Add lexer and input classes

This commit is contained in:
Louis Rubet 2022-02-21 15:22:37 +01:00
parent 8542890b61
commit 1faf101dd8
21 changed files with 524 additions and 613 deletions

View file

@ -55,7 +55,8 @@ add_executable(
${PROJECT_SOURCE_DIR}/src/object.cpp
${PROJECT_SOURCE_DIR}/src/mpreal-out.cpp
${PROJECT_SOURCE_DIR}/src/program.cpp
${PROJECT_SOURCE_DIR}/src/parse.cpp
${PROJECT_SOURCE_DIR}/src/lexer.cpp
${PROJECT_SOURCE_DIR}/src/input.cpp
${PROJECT_SOURCE_DIR}/src/rpn-branch.cpp
${PROJECT_SOURCE_DIR}/src/rpn-complex.cpp
${PROJECT_SOURCE_DIR}/src/rpn-general.cpp

View file

@ -10,7 +10,6 @@
// default mode and number of printed digits
//
#define DEFAULT_MODE number::std
#define MPFR_DEFAULT_FORMAT "%.xxRg"
/* directly calculated from 128 bits precision
ceil(128 * log10(2)) - 1 = 38 */
@ -19,34 +18,9 @@
// MPFR related defaults
//
// rounding method
#define MPFR_DEFAULT_RND MPFR_RNDN
#define MPFR_ROUND_STRINGS \
{"nearest (even)", MPFR_RNDN}, {"toward zero", MPFR_RNDZ}, {"toward +inf", MPFR_RNDU}, {"toward -inf", MPFR_RNDD}, \
{"away from zero", MPFR_RNDA}, {"faithful rounding", MPFR_RNDF}, { \
"nearest (away from zero)", MPFR_RNDNA \
}
// 128 bits significand precision
#define MPFR_DEFAULT_PREC_BITS 128
// constants
//
// commands and entry related constants
#define MAX_COMMAND_LENGTH 24
#define AUTOCOMPLETE_KEY '\t'
#define SHOW_STACK_SEPARATOR "> "
#define PROMPT "rpn> "
#define MULTILINE_PROMPT "> "
// show formats
#define MPFR_FORMAT_BEG "%."
#define MPFR_FORMAT_STD "Rg"
#define MPFR_FORMAT_FIX "Rf"
#define MPFR_FORMAT_SCI "Re"
#define MPFR_FORMAT_HEX "%Ra"
// return values, used by all classes
//
typedef enum {
@ -70,20 +44,6 @@ typedef enum {
ret_max
} ret_value;
#define RET_VALUE_STRINGS \
{ \
"ok", "unknown command", "missing operand", "bad operand type", "out of range", "unknown variable", \
"internal error, aborting", "deadly", "goodbye", "not implemented", "no operation", "syntax error", \
"division by zero", "runtime error", "aborted current entry", "out of memory", "bad value", \
}
// command types
//
// history
#define HISTORY_FILE ".rpn_history"
#define HISTORY_FILE_MAX_LINES (100)
// base min and max
#define BASE_MIN 2
#define BASE_MAX 62

View file

@ -1,299 +1,10 @@
#include <regex>
using namespace std;
#include "program.hpp"
#include "lexer.hpp"
/// @brief completion callback as asked by linenoise-ng
/// this is called by linenoise-ng whenever the user enters TAB
///
/// @param text the text after wich the user entered TAB
/// @param lc the completion object to add strings with linenoiseAddCompletion()
///
void program::entry_completion_generator(const char* text, linenoiseCompletions* lc) {
int i = 0;
int text_len = strnlen(text, 6);
// propose all keywords
if (text_len == 0) {
while (program::s_keywords[i].type != cmd_max) {
if (program::s_keywords[i].fn != NULL)
// add all keywords
linenoiseAddCompletion(lc, program::s_keywords[i].name);
i++;
}
}
// propose keywords matching to text begining
else {
while (program::s_keywords[i].type != cmd_max) {
if (program::s_keywords[i].fn != NULL) {
// compare list entry with text, return if match
if (strncmp(program::s_keywords[i].name, text, text_len) == 0)
linenoiseAddCompletion(lc, program::s_keywords[i].name);
}
i++;
}
}
}
/// @brief interactive entry and decoding
///
/// @param prog the program to add entered objects
/// @return ret_value see this type
///
ret_value program::entry(program& prog) {
string entry_str;
char* entry;
int entry_len;
int total_entry_len;
ret_value ret = ret_max;
bool multiline = false;
// linenoise for entry
linenoiseSetCompletionCallback(entry_completion_generator);
while (ret == ret_max) {
// get user entry
if (multiline)
entry = linenoise(MULTILINE_PROMPT, &entry_len);
else
entry = linenoise(PROMPT, &entry_len);
// Ctrl-C
if (linenoiseKeyType() == 1) {
if (entry_len > 0 || multiline)
ret = ret_abort_current_entry;
else
ret = ret_good_bye;
} else if (linenoiseKeyType() == 3) {
multiline = true;
if (entry != NULL) entry_str += entry;
entry_str += " ";
} else {
if (entry != NULL) {
entry_str += entry;
// parse it
ret = parse(entry_str, prog);
// keep history
if (entry[0] != 0) (void)linenoiseHistoryAdd(entry_str.c_str());
} else
ret = ret_internal;
}
free(entry);
}
return ret;
}
struct SynElement {
cmd_type_t type;
string value;
mpreal re;
mpreal im;
int reBase;
int imBase;
program_fn_t fn;
bool autoEval;
};
struct SynError {
size_t indx;
string err;
};
struct ReservedWord {
cmd_type_t type;
program_fn_t fn;
};
static map<string, ReservedWord> _keywordsMap;
static void trim(string& s) {
s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](unsigned char ch) { return !std::isspace(ch); }));
s.erase(std::find_if(s.rbegin(), s.rend(), [](unsigned char ch) { return !std::isspace(ch); }).base(), s.end());
}
static bool parseString(string& entry, size_t idx, size_t& nextIdx, vector<SynError>& errors,
vector<SynElement>& elements) {
// here we are sure that entry[0] is at least '"'
for (size_t i = idx + 1; i < entry.size(); i++) {
if (entry[i] == '"') {
if (entry[i] - 1 != '\\') {
elements.push_back({cmd_string, .value = entry.substr(idx + 1, i - idx - 1)});
nextIdx = i + 1;
return true;
}
}
}
elements.push_back({cmd_string, .value = entry.substr(idx + 1, entry.size() - idx - 1)});
nextIdx = entry.size();
return true;
}
static bool parseSymbol(string& entry, size_t idx, size_t& nextIdx, vector<SynError>& errors,
vector<SynElement>& elements) {
// here we are sure that entry[0] is at least '\''
for (size_t i = idx + 1; i < entry.size(); i++) {
if (entry[i] == '\'') {
elements.push_back({cmd_symbol, .value = entry.substr(idx + 1, i - idx - 1), .autoEval = false});
nextIdx = i + 1;
return true;
}
}
elements.push_back({cmd_symbol, .value = entry.substr(idx + 1, entry.size() - idx - 1)});
nextIdx = entry.size();
return true;
}
static bool parseProgram(string& entry, size_t idx, size_t& nextIdx, vector<SynError>& errors,
vector<SynElement>& elements) {
// here we are sure that entry is at least "<<"
// find last ">>" or "»"
int countNested = 0;
for (size_t i = idx + 2; i < entry.size() - 1; i++) {
if ((entry[i] == '<' && entry[i + 1] == '<') || (entry.substr(i, 2) == "«"))
countNested++;
else if ((entry[i] == '>' && entry[i + 1] == '>') || (entry.substr(i, 2) == "»")) {
if (countNested == 0) {
string prg = entry.substr(idx + 2, i - idx - 2);
trim(prg);
elements.push_back({cmd_program, .value = prg});
nextIdx = i + 2;
return true;
} else
countNested--;
}
}
string prg = entry.substr(idx + 2, entry.size() - idx - 2);
trim(prg);
elements.push_back({cmd_program, .value = prg});
nextIdx = entry.size();
return true;
}
static int getBase(string& entry, int idxStart, bool& positive) {
regex baseRegex("([+-])?((0[xX])|([0-9][0-9]?[bB]))");
smatch match;
if (regex_search(entry, match, baseRegex) && match.size() >= 5) {
string sign = match[1].str();
string base = match[2].str();
// sign out, permits expressions like -0xAB3F
positive = sign.size() > 0 && sign[0] == '-' ? false : true;
// base
entry = entry.substr(base.size() + sign.size());
if (base[1] == 'X' || base[1] == 'x') return 16;
if (base.size() > 0) {
int b = stoi(base.substr(0, base.size() - 1));
if (b == 0) b = 2; // admit "0b" as binary suffix
return b;
}
}
return 10;
}
static bool getNumberAt(string& entry, size_t idx, size_t& nextIdx, int& base, mpreal& r, char delim = ' ') {
stringstream ss;
int idxNumber = 0;
string token;
bool positive = true;
ss.str(entry.substr(idx));
if (getline(ss, token, delim)) {
nextIdx = token.size() + idx + 1;
base = getBase(token, idx, positive);
if (base < BASE_MIN || base > BASE_MAX) return false;
trim(token);
if (mpfr_set_str(r.mpfr_ptr(), token.c_str(), base, mpreal::get_default_rnd()) == 0) {
if (!positive) r = -r;
return true;
} else
return false;
}
nextIdx = token.size() + idx + 1;
return false;
}
static bool parseNumber(string& entry, size_t idx, size_t& nextIdx, vector<SynError>& errors,
vector<SynElement>& elements) {
mpreal r;
int base = 10;
if (getNumberAt(entry, idx, nextIdx, base, r)) {
elements.push_back({cmd_number, .re = r, .reBase = base});
return true;
} else {
errors.push_back({entry.size(), "unterminated number"});
return false;
}
}
static bool parseComplex(string& entry, size_t idx, size_t& nextIdx, vector<SynError>& errors,
vector<SynElement>& elements) {
mpreal re, im;
int reBase, imBase = 10;
if (idx + 1 == entry.size()) {
elements.push_back({cmd_symbol, .value = entry.substr(idx, entry.size() - idx)});
nextIdx = entry.size();
return true; // complex format error, return a symbol
}
if (!getNumberAt(entry, idx + 1, nextIdx, reBase, re, ',')) {
elements.push_back({cmd_symbol, .value = entry.substr(idx, entry.size() - idx)});
nextIdx = entry.size();
return true; // complex format error, return a symbol
}
size_t i = nextIdx;
//while (i < entry.size() && entry[i] != ',') i++;
if (i >= entry.size()) {
elements.push_back({cmd_symbol, .value = entry.substr(idx, entry.size() - idx)});
nextIdx = entry.size();
return true; // complex format error, return a symbol
}
if (!getNumberAt(entry, i, nextIdx, imBase, im, ')')) {
elements.push_back({cmd_symbol, .value = entry.substr(idx, entry.size() - idx)});
nextIdx = entry.size();
return true; // complex format error, return a symbol
}
elements.push_back({cmd_complex, .re = re, .im = im, .reBase = reBase, .imBase = imBase});
nextIdx++;
return true;
}
static bool parseReserved(string& entry, size_t idx, size_t& nextIdx, vector<SynElement>& elements,
map<string, ReservedWord>& keywords) {
stringstream ss(entry.substr(idx));
string token;
ss >> token;
auto resa = keywords.find(token);
if (resa != keywords.end()) {
elements.push_back({resa->second.type, .value = token, .fn = resa->second.fn});
nextIdx = token.size() + idx;
return true;
}
return false;
}
static bool parseUnknown(string& entry, size_t idx, size_t& nextIdx, vector<SynElement>& elements) {
stringstream ss(entry.substr(idx));
string token;
ss >> token;
elements.push_back({cmd_symbol, .value = token, .autoEval = true});
nextIdx = token.size() + idx;
return true;
}
/// @brief lexical analysis of an entry string
///
/// @param entry the entry string
/// @param elements syntax elements vector
/// @param errors errors vector
/// @param keywords reserved keywords
/// @return false=errors, the lexer must stop
///
static bool lexer(string& entry, vector<SynElement>& elements, vector<SynError>& errors,
map<string, ReservedWord>& keywords) {
bool Lexer::lexer(string& entry, map<string, ReservedWord>& keywords, vector<SynElement>& elements,
vector<SynError>& errors) {
size_t jump;
for (size_t i = 0; i < entry.size(); i++) {
if (isspace(entry[i])) continue;
@ -332,60 +43,177 @@ static bool lexer(string& entry, vector<SynElement>& elements, vector<SynError>&
return true;
}
static bool progFromElements(vector<SynElement>& elements, program& prog) {
// TODO control elements creation
for (SynElement& element : elements) {
switch (element.type) {
case cmd_number:
prog.push_back(new number(element.re, element.reBase));
break;
case cmd_complex:
prog.push_back(new ocomplex(element.re, element.im, element.reBase, element.imBase));
break;
case cmd_string:
prog.push_back(new ostring(element.value));
break;
case cmd_symbol:
prog.push_back(new symbol(element.value, element.autoEval));
break;
case cmd_program:
prog.push_back(new oprogram(element.value));
break;
case cmd_keyword:
prog.push_back(new keyword(element.fn, element.value));
break;
case cmd_branch:
prog.push_back(new branch((branch_fn_t)element.fn, element.value));
break;
default:
return false;
void Lexer::trim(string& s) {
s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](unsigned char ch) { return !std::isspace(ch); }));
s.erase(std::find_if(s.rbegin(), s.rend(), [](unsigned char ch) { return !std::isspace(ch); }).base(), s.end());
}
bool Lexer::parseString(string& entry, size_t idx, size_t& nextIdx, vector<SynError>& errors,
vector<SynElement>& elements) {
// here we are sure that entry[0] is at least '"'
for (size_t i = idx + 1; i < entry.size(); i++) {
if (entry[i] == '"') {
if (entry[i] - 1 != '\\') {
elements.push_back({cmd_string, .value = entry.substr(idx + 1, i - idx - 1)});
nextIdx = i + 1;
return true;
}
}
}
elements.push_back({cmd_string, .value = entry.substr(idx + 1, entry.size() - idx - 1)});
nextIdx = entry.size();
return true;
}
/// @brief parse an entry string: cut it into objects chunks and add them to a program
///
/// @param entry the entry string
/// @param prog the program to be filled
/// @return ret_value see this type
///
ret_value program::parse(string& entry, program& prog) {
vector<SynElement> elements;
vector<SynError> errors;
ret_value ret = ret_ok;
// prepare map for finding reserved keywords
if (_keywordsMap.size() == 0)
for (int i = 0; s_keywords[i].type != cmd_max; i++)
_keywordsMap[s_keywords[i].name] = {s_keywords[i].type, s_keywords[i].fn};
// separate the entry string
if (lexer(entry, elements, errors, _keywordsMap)) {
// make objects from parsed elements
if (!progFromElements(elements, prog)) prog.show_error(ret_unknown_err, "error creating program from entry");
} else
for (SynError& err : errors) prog.show_syntax_error(err.err.c_str());
return ret;
bool Lexer::parseSymbol(string& entry, size_t idx, size_t& nextIdx, vector<SynError>& errors,
vector<SynElement>& elements) {
// here we are sure that entry[0] is at least '\''
for (size_t i = idx + 1; i < entry.size(); i++) {
if (entry[i] == '\'') {
elements.push_back({cmd_symbol, .value = entry.substr(idx + 1, i - idx - 1), .autoEval = false});
nextIdx = i + 1;
return true;
}
}
elements.push_back({cmd_symbol, .value = entry.substr(idx + 1, entry.size() - idx - 1)});
nextIdx = entry.size();
return true;
}
bool Lexer::parseProgram(string& entry, size_t idx, size_t& nextIdx, vector<SynError>& errors,
vector<SynElement>& elements) {
// here we are sure that entry is at least "<<"
// find last ">>" or "»"
int countNested = 0;
for (size_t i = idx + 2; i < entry.size() - 1; i++) {
if ((entry[i] == '<' && entry[i + 1] == '<') || (entry.substr(i, 2) == "«"))
countNested++;
else if ((entry[i] == '>' && entry[i + 1] == '>') || (entry.substr(i, 2) == "»")) {
if (countNested == 0) {
string prg = entry.substr(idx + 2, i - idx - 2);
trim(prg);
elements.push_back({cmd_program, .value = prg});
nextIdx = i + 2;
return true;
} else
countNested--;
}
}
string prg = entry.substr(idx + 2, entry.size() - idx - 2);
trim(prg);
elements.push_back({cmd_program, .value = prg});
nextIdx = entry.size();
return true;
}
int Lexer::getBaseAt(string& entry, int idxStart, bool& positive) {
regex baseRegex("([+-])?((0[xX])|([0-9][0-9]?[bB]))");
smatch match;
if (regex_search(entry, match, baseRegex) && match.size() >= 5) {
string sign = match[1].str();
string base = match[2].str();
// sign out, permits expressions like -0xAB3F
positive = sign.size() > 0 && sign[0] == '-' ? false : true;
// base
entry = entry.substr(base.size() + sign.size());
if (base[1] == 'X' || base[1] == 'x') return 16;
if (base.size() > 0) {
int b = stoi(base.substr(0, base.size() - 1));
if (b == 0) b = 2; // admit "0b" as binary suffix
return b;
}
}
return 10;
}
bool Lexer::getNumberAt(string& entry, size_t idx, size_t& nextIdx, int& base, mpreal& r, char delim) {
stringstream ss;
int idxNumber = 0;
string token;
bool positive = true;
ss.str(entry.substr(idx));
if (getline(ss, token, delim)) {
nextIdx = token.size() + idx + 1;
base = getBaseAt(token, idx, positive);
if (base < BASE_MIN || base > BASE_MAX) return false;
trim(token);
if (mpfr_set_str(r.mpfr_ptr(), token.c_str(), base, mpreal::get_default_rnd()) == 0) {
if (!positive) r = -r;
return true;
} else
return false;
}
nextIdx = token.size() + idx + 1;
return false;
}
bool Lexer::parseNumber(string& entry, size_t idx, size_t& nextIdx, vector<SynError>& errors,
vector<SynElement>& elements) {
mpreal r;
int base = 10;
if (getNumberAt(entry, idx, nextIdx, base, r)) {
elements.push_back({cmd_number, .re = r, .reBase = base});
return true;
} else {
errors.push_back({entry.size(), "unterminated number"});
return false;
}
}
bool Lexer::parseComplex(string& entry, size_t idx, size_t& nextIdx, vector<SynError>& errors,
vector<SynElement>& elements) {
mpreal re, im;
int reBase, imBase = 10;
if (idx + 1 == entry.size()) {
elements.push_back({cmd_symbol, .value = entry.substr(idx, entry.size() - idx)});
nextIdx = entry.size();
return true; // complex format error, return a symbol
}
if (!getNumberAt(entry, idx + 1, nextIdx, reBase, re, ',')) {
elements.push_back({cmd_symbol, .value = entry.substr(idx, entry.size() - idx)});
nextIdx = entry.size();
return true; // complex format error, return a symbol
}
size_t i = nextIdx;
// while (i < entry.size() && entry[i] != ',') i++;
if (i >= entry.size()) {
elements.push_back({cmd_symbol, .value = entry.substr(idx, entry.size() - idx)});
nextIdx = entry.size();
return true; // complex format error, return a symbol
}
if (!getNumberAt(entry, i, nextIdx, imBase, im, ')')) {
elements.push_back({cmd_symbol, .value = entry.substr(idx, entry.size() - idx)});
nextIdx = entry.size();
return true; // complex format error, return a symbol
}
elements.push_back({cmd_complex, .re = re, .im = im, .reBase = reBase, .imBase = imBase});
nextIdx++;
return true;
}
bool Lexer::parseReserved(string& entry, size_t idx, size_t& nextIdx, vector<SynElement>& elements,
map<string, ReservedWord>& keywords) {
stringstream ss(entry.substr(idx));
string token;
ss >> token;
auto resa = keywords.find(token);
if (resa != keywords.end()) {
elements.push_back({resa->second.type, .value = token, .fn = resa->second.fn});
nextIdx = token.size() + idx;
return true;
}
return false;
}
bool Lexer::parseUnknown(string& entry, size_t idx, size_t& nextIdx, vector<SynElement>& elements) {
stringstream ss(entry.substr(idx));
string token;
ss >> token;
elements.push_back({cmd_symbol, .value = token, .autoEval = true});
nextIdx = token.size() + idx;
return true;
}

73
src/lexer.hpp Normal file
View file

@ -0,0 +1,73 @@
#ifndef _PARSER_HPP_
#define _PARSER_HPP_
#include <map>
#include <vector>
using namespace std;
#define MPFR_USE_NO_MACRO
#include <mpfr.h>
#include <mpreal.h>
using namespace mpfr;
#include "constant.h"
#include "object.hpp"
class Lexer {
public:
// a structure to describe a syntaxical element and its value
struct SynElement {
cmd_type_t type;
string value;
mpreal re;
mpreal im;
int reBase;
int imBase;
program_fn_t fn;
bool autoEval;
};
struct SynError {
size_t indx;
string err;
};
struct ReservedWord {
cmd_type_t type;
program_fn_t fn;
};
Lexer() {}
/// @brief lexical analysis of an entry string
///
/// @param[in] entry the entry to lex
/// @param[out] elements syntax elements vector
/// @param[out] errors errors vector
/// @param[in] keywords reserved keywords
/// @return false=errors, the lexer must stop
///
bool lexer(string& entry, map<string, ReservedWord>& keywords, vector<SynElement>& elements,
vector<SynError>& errors);
private:
bool parseString(string& entry, size_t idx, size_t& nextIdx, vector<SynError>& errors,
vector<SynElement>& elements);
bool parseSymbol(string& entry, size_t idx, size_t& nextIdx, vector<SynError>& errors,
vector<SynElement>& elements);
bool parseProgram(string& entry, size_t idx, size_t& nextIdx, vector<SynError>& errors,
vector<SynElement>& elements);
bool parseNumber(string& entry, size_t idx, size_t& nextIdx, vector<SynError>& errors,
vector<SynElement>& elements);
bool parseComplex(string& entry, size_t idx, size_t& nextIdx, vector<SynError>& errors,
vector<SynElement>& elements);
bool parseReserved(string& entry, size_t idx, size_t& nextIdx, vector<SynElement>& elements,
map<string, ReservedWord>& keywords);
bool parseUnknown(string& entry, size_t idx, size_t& nextIdx, vector<SynElement>& elements);
void trim(string& s);
int getBaseAt(string& entry, int idxStart, bool& positive);
bool getNumberAt(string& entry, size_t idx, size_t& nextIdx, int& base, mpreal& r, char delim = ' ');
};
#endif

View file

@ -1,4 +1,3 @@
#include <linux/limits.h>
#include <pwd.h>
#include <signal.h>
@ -7,19 +6,20 @@
using namespace std;
// internal includes
#include "input.hpp"
#include "program.hpp"
static heap _global_heap;
static rpnstack _global_stack;
static program* _prog_to_interrupt = NULL;
static program* _prog_to_interrupt = nullptr;
/// @brief actions to be done at rpn exit
///
static void exit_interactive_rpn() {
struct passwd* pw = getpwuid(getuid());
if (pw != NULL) {
if (pw != nullptr) {
stringstream history_path;
history_path << pw->pw_dir << '/' << HISTORY_FILE;
history_path << pw->pw_dir << "/.rpn_history";
// trunc current history
ofstream history(history_path.str(), ios_base::out | ios_base::trunc);
@ -37,12 +37,12 @@ static void exit_interactive_rpn() {
///
static void init_interactive_rpn() {
struct passwd* pw = getpwuid(getuid());
if (pw != NULL) {
if (pw != nullptr) {
stringstream history_path;
history_path << pw->pw_dir << '/' << HISTORY_FILE;
history_path << pw->pw_dir << "/.rpn_history";
// don't care about errors
linenoiseHistorySetMaxLen(HISTORY_FILE_MAX_LINES);
linenoiseHistorySetMaxLen(100);
linenoiseHistoryLoad(history_path.str().c_str());
}
}
@ -54,9 +54,9 @@ static void init_interactive_rpn() {
/// @param context see POSIX sigaction
///
static void ctrlc_handler(int sig, siginfo_t* siginfo, void* context) {
if (_prog_to_interrupt != NULL) {
if (_prog_to_interrupt != nullptr) {
_prog_to_interrupt->stop();
_prog_to_interrupt = NULL;
_prog_to_interrupt = nullptr;
}
exit_interactive_rpn();
@ -71,12 +71,12 @@ static void ctrlc_handler(int sig, siginfo_t* siginfo, void* context) {
static void segv_handler(int sig, siginfo_t* siginfo, void* context) {
cerr << "Internal error" << endl;
_prog_to_interrupt->stop();
_prog_to_interrupt = NULL;
_prog_to_interrupt = nullptr;
}
/// @brief setup signals handlers to stop with honours
///
/// @param prog the prog to catch the signals to, must be checked not NULL by user
/// @param prog the prog to catch the signals to, must be checked not nullptr by user
///
static void catch_signals(program* prog) {
struct sigaction act = {0};
@ -85,12 +85,12 @@ static void catch_signals(program* prog) {
act.sa_sigaction = &ctrlc_handler;
act.sa_flags = SA_SIGINFO;
if (sigaction(SIGINT, &act, NULL) < 0)
if (sigaction(SIGINT, &act, nullptr) < 0)
cerr << "Warning, Ctrl-C cannot be caught [errno=" << errno << ' ' << strerror(errno) << "']" << endl;
act.sa_sigaction = &segv_handler;
act.sa_flags = SA_SIGINFO;
if (sigaction(SIGSEGV, &act, NULL) < 0)
if (sigaction(SIGSEGV, &act, nullptr) < 0)
cerr << "Warning, SIGSEGV cannot be caught [errno=" << errno << ' ' << strerror(errno) << "']" << endl;
}
@ -116,30 +116,28 @@ int main(int argc, char* argv[]) {
while (go_on) {
// make program from interactive entry
program prog(_global_stack, _global_heap);
switch (program::entry(prog)) {
case ret_good_bye:
go_on = false;
break;
case ret_abort_current_entry:
break;
default:
string entry;
switch (Input(entry, program::getAutocompletionWords()).status) {
case Input::ok:
// user could stop prog with CtrlC
catch_signals(&prog);
// run it
if (prog.run() == ret_good_bye)
if (prog.parse(entry) == ret_ok && prog.run() == ret_good_bye)
go_on = false;
else
program::show_stack(_global_stack);
break;
case Input::ctrlc:
go_on = false;
break;
default:
break;
}
}
// manage history and exit
exit_interactive_rpn();
}
// run with cmd line arguments
else {
} else { // run with cmd line arguments
program prog(_global_stack, _global_heap);
string entry;
int i;
@ -151,7 +149,7 @@ int main(int argc, char* argv[]) {
}
// make program
ret = program::parse(entry, prog);
ret = prog.parse(entry);
if (ret == ret_ok) {
// user could stop prog with CtrlC
catch_signals(&prog);

View file

@ -1,10 +1,4 @@
#include <math.h>
#include <string>
using namespace std;
#include "constant.h"
#define MPFR_USE_NO_MACRO
#include <mpfr.h>
#include "object.hpp"
// number statics

View file

@ -1,13 +1,14 @@
#ifndef OBJECT_HPP
#define OBJECT_HPP
#define MPFR_USE_NO_MACRO
#include <mpfr.h>
#include <mpreal.h>
using namespace mpfr;
#include <bitset>
#include <complex>
#include <iomanip>
#include <ostream>
#include <string>
#include <sstream>
using namespace std;
#include "mpreal-out.hpp"
@ -127,7 +128,6 @@ struct ocomplex : object {
struct ostring : object {
ostring() : object(cmd_string) {}
ostring(const string& value_) : object(cmd_string), value(value_) {}
ostring(const char* value_) : object(cmd_string) { value = string(value_); }
virtual object* clone() { return new ostring(value); }
virtual string name() { return string("string"); }
virtual ostream& show(ostream& out) { return out << "\"" << value << "\""; }

View file

@ -1,12 +1,9 @@
#include "program.hpp"
//< return type strings
const char* program::s_ret_value_string[ret_max] = RET_VALUE_STRINGS;
//< language reserved keywords (allowed types are cmd_keyword, cmd_branch or cmd_undef)
program::keyword_t program::s_keywords[] = {
vector<program::keyword_t> program::_keywords{
// GENERAL
{cmd_undef, "", NULL, "\nGENERAL"},
{cmd_undef, "", nullptr, "\nGENERAL"},
{cmd_keyword, "nop", &program::rpn_nop, "no operation"},
{cmd_keyword, "help", &program::rpn_help, "this help message"},
{cmd_keyword, "h", &program::rpn_help, ""},
@ -20,7 +17,7 @@ program::keyword_t program::s_keywords[] = {
{cmd_keyword, "history", &program::rpn_history, "see commands history"},
// USUAL OPERATIONS ON REALS AND COMPLEXES
{cmd_undef, "", NULL, "\nUSUAL OPERATIONS ON REALS AND COMPLEXES"},
{cmd_undef, "", nullptr, "\nUSUAL OPERATIONS ON REALS AND COMPLEXES"},
{cmd_keyword, "+", &program::rpn_plus, "addition"},
{cmd_keyword, "-", &program::rpn_minus, "substraction"},
{cmd_keyword, "*", &program::rpn_mul, "multiplication"},
@ -36,7 +33,7 @@ program::keyword_t program::s_keywords[] = {
{cmd_keyword, "sign", &program::rpn_sign, "sign of a number or z/|z| for a complex"},
// OPERATIONS ON REALS
{cmd_undef, "", NULL, "\nOPERATIONS ON REALS"},
{cmd_undef, "", nullptr, "\nOPERATIONS ON REALS"},
{cmd_keyword, "%", &program::rpn_purcent, "purcent"},
{cmd_keyword, "%CH", &program::rpn_purcentCH, "inverse purcent"},
{cmd_keyword, "mod", &program::rpn_modulo, "modulo"},
@ -51,7 +48,7 @@ program::keyword_t program::s_keywords[] = {
{cmd_keyword, "max", &program::rpn_max, "max of 2 real numbers"},
// OPERATIONS ON COMPLEXES
{cmd_undef, "", NULL, "\nOPERATIONS ON COMPLEXES"},
{cmd_undef, "", nullptr, "\nOPERATIONS ON COMPLEXES"},
{cmd_keyword, "re", &program::rpn_re, "complex real part"},
{cmd_keyword, "im", &program::rpn_im, "complex imaginary part"},
{cmd_keyword, "conj", &program::rpn_conj, "complex conjugate"},
@ -62,7 +59,7 @@ program::keyword_t program::s_keywords[] = {
{cmd_keyword, "r->p", &program::rpn_r2p, "polar to cartesian"},
// MODE
{cmd_undef, "", NULL, "\nMODE"},
{cmd_undef, "", nullptr, "\nMODE"},
{cmd_keyword, "std", &program::rpn_std, "standard floating numbers representation. ex: std"},
{cmd_keyword, "fix", &program::rpn_fix, "fixed point representation. ex: 6 fix"},
{cmd_keyword, "sci", &program::rpn_sci, "scientific floating point representation. ex: 20 sci"},
@ -79,7 +76,7 @@ program::keyword_t program::s_keywords[] = {
{cmd_keyword, "base", &program::rpn_base, "arbitrary base representation, applies on stack level 0 only"},
// TESTS
{cmd_undef, "", NULL, "\nTEST"},
{cmd_undef, "", nullptr, "\nTEST"},
{cmd_keyword, ">", &program::rpn_sup, "binary operator >"},
{cmd_keyword, ">=", &program::rpn_sup_eq, "binary operator >="},
{cmd_keyword, "<", &program::rpn_inf, "binary operator <"},
@ -93,7 +90,7 @@ program::keyword_t program::s_keywords[] = {
{cmd_keyword, "same", &program::rpn_same, "boolean operator same (equal)"},
// STACK
{cmd_undef, "", NULL, "\nSTACK"},
{cmd_undef, "", nullptr, "\nSTACK"},
{cmd_keyword, "swap", &program::rpn_swap, "swap 2 first stack entries"},
{cmd_keyword, "drop", &program::rpn_drop, "drop first stack entry"},
{cmd_keyword, "drop2", &program::rpn_drop2, "drop 2 first stack entries"},
@ -111,7 +108,7 @@ program::keyword_t program::s_keywords[] = {
{cmd_keyword, "over", &program::rpn_over, "push a copy of the element in stack level 2 onto the stack"},
// STRING
{cmd_undef, "", NULL, "\nSTRING"},
{cmd_undef, "", nullptr, "\nSTRING"},
{cmd_keyword, "->str", &program::rpn_instr, "convert an object into a string"},
{cmd_keyword, "str->", &program::rpn_strout, "convert a string into an object"},
{cmd_keyword, "chr", &program::rpn_chr, "convert ASCII character code in stack level 1 into a string"},
@ -122,7 +119,7 @@ program::keyword_t program::s_keywords[] = {
{cmd_keyword, "sub", &program::rpn_strsub, "return a substring of the string in level 3"},
// BRANCH
{cmd_undef, "", NULL, "\nBRANCH"},
{cmd_undef, "", nullptr, "\nBRANCH"},
{cmd_branch, "if", (program_fn_t)&program::rpn_if,
"if <test-instruction> then <true-instructions> else <false-instructions> "
"end"},
@ -144,7 +141,7 @@ program::keyword_t program::s_keywords[] = {
{cmd_branch, "repeat", (program_fn_t)&program::rpn_repeat, "used with while"},
// STORE
{cmd_undef, "", NULL, "\nSTORE"},
{cmd_undef, "", nullptr, "\nSTORE"},
{cmd_keyword, "sto", &program::rpn_sto, "store a variable. ex: 1 'name' sto"},
{cmd_keyword, "rcl", &program::rpn_rcl, "recall a variable. ex: 'name' rcl"},
{cmd_keyword, "purge", &program::rpn_purge, "delete a variable. ex: 'name' purge"},
@ -158,14 +155,14 @@ program::keyword_t program::s_keywords[] = {
{cmd_keyword, "sneg", &program::rpn_stoneg, "negate a variable. ex: 'name' sneg"},
{cmd_keyword, "sinv", &program::rpn_stoinv, "inverse a variable. ex: 1 'name' sinv"},
// PROGRAM
{cmd_undef, "", NULL, "\nPROGRAM"},
{cmd_undef, "", nullptr, "\nPROGRAM"},
{cmd_keyword, "eval", &program::rpn_eval, "evaluate (run) a program, or recall a variable. ex: 'my_prog' eval"},
{cmd_branch, "->", (program_fn_t)&program::rpn_inprog,
"load program local variables. ex: << -> n m << 0 n m for i i + next >> "
">>"},
// TRIG ON REALS AND COMPLEXES
{cmd_undef, "", NULL, "\nTRIG ON REALS AND COMPLEXES"},
{cmd_undef, "", nullptr, "\nTRIG ON REALS AND COMPLEXES"},
{cmd_keyword, "pi", &program::rpn_pi, "pi constant"},
{cmd_keyword, "sin", &program::rpn_sin, "sinus"},
{cmd_keyword, "asin", &program::rpn_asin, "arg sinus"},
@ -177,7 +174,7 @@ program::keyword_t program::s_keywords[] = {
{cmd_keyword, "r->d", &program::rpn_r2d, "convert radians to degrees"},
// LOGS ON REALS AND COMPLEXES
{cmd_undef, "", NULL, "\nLOGS ON REALS AND COMPLEXES"},
{cmd_undef, "", nullptr, "\nLOGS ON REALS AND COMPLEXES"},
{cmd_keyword, "e", &program::rpn_e, "Euler constant"},
{cmd_keyword, "ln", &program::rpn_ln, "logarithm base e"},
{cmd_keyword, "log", &program::rpn_ln, ""},
@ -198,15 +195,25 @@ program::keyword_t program::s_keywords[] = {
{cmd_keyword, "atanh", &program::rpn_atanh, "inverse hyperbolic tangent"},
// TIME AND DATE
{cmd_undef, "", NULL, "\nTIME AND DATE"},
{cmd_undef, "", nullptr, "\nTIME AND DATE"},
{cmd_keyword, "time", &program::rpn_time, "time in local format"},
{cmd_keyword, "date", &program::rpn_date, "date in local format"},
{cmd_keyword, "ticks", &program::rpn_ticks, "system tick in µs"},
// end
{cmd_max, "", NULL, ""},
};
/// keywords map for lexer
map<string, Lexer::ReservedWord> program::_keywordsMap;
/// autocompletion vector for linenoise autocompletion
vector<string> program::_autocompletionWords;
vector<string>& program::getAutocompletionWords() {
if (_autocompletionWords.empty())
for (auto& kw : _keywords)
if (!kw.name.empty()) _autocompletionWords.push_back(kw.name);
return _autocompletionWords;
}
/// @brief run a program on a stack and a heap
///
/// @return ret_value see this type
@ -302,7 +309,7 @@ ret_value program::run() {
_local_heap.clear();
if (interrupt_now) {
fprintf(stderr, "\nInterrupted\n");
cerr << endl << "Interrupted" << endl;
interrupt_now = false;
}
@ -561,15 +568,75 @@ ret_value program::preprocess(void) {
return ret_ok;
}
/// @brief parse an entry string: cut it into objects chunks and add them to a program
///
/// @param entry the entry string
/// @param prog the program to be filled
/// @return ret_value see this type
///
ret_value program::parse(string& entry) {
vector<Lexer::SynElement> elements;
vector<Lexer::SynError> errors;
ret_value ret = ret_ok;
// prepare map for finding reserved keywords
if (_keywordsMap.empty())
for (auto& kw : _keywords)
if (!kw.name.empty()) _keywordsMap[kw.name] = {kw.type, kw.fn};
// separate the entry string
if (lexer(entry, _keywordsMap, elements, errors)) {
// make objects from parsed elements
for (Lexer::SynElement& element : elements) {
switch (element.type) {
case cmd_number:
push_back(new number(element.re, element.reBase));
break;
case cmd_complex:
push_back(new ocomplex(element.re, element.im, element.reBase, element.imBase));
break;
case cmd_string:
push_back(new ostring(element.value));
break;
case cmd_symbol:
push_back(new symbol(element.value, element.autoEval));
break;
case cmd_program:
push_back(new oprogram(element.value));
break;
case cmd_keyword:
push_back(new keyword(element.fn, element.value));
break;
case cmd_branch:
push_back(new branch((branch_fn_t)element.fn, element.value));
break;
default:
show_error(ret_unknown_err, "error creating program from entry");
}
}
} else
for (SynError& err : errors) show_syntax_error(err.err.c_str());
return ret;
}
/// @brief show the last error set
///
/// @return ret_value see this type
///
ret_value program::show_error() {
ret_value ret;
// clang-format off
vector<string> errorStrings {"ok", "unknown command", "missing operand", "bad operand type",
"out of range", "unknown variable", "internal error, aborting", "deadly", "goodbye", "not implemented",
"no operation", "syntax error", "division by zero", "runtime error", "aborted current entry", "out of memory",
"bad value"};
// clang-format on
// show last recorded error
cerr << _err_context << ": error " << _err << ": " << s_ret_value_string[_err] << endl;
if ((size_t)_err < errorStrings.size())
cerr << _err_context << ": error " << _err << ": " << errorStrings[_err] << endl;
else
cerr << _err_context << " (unknown error code)" << endl;
switch (_err) {
case ret_internal:
case ret_deadly:
@ -637,7 +704,7 @@ void program::show_stack(rpnstack& st, bool show_separator) {
cout << st[0] << endl;
else
for (int i = st.size() - 1; i >= 0; i--) {
if (show_separator) cout << i + 1 << SHOW_STACK_SEPARATOR;
if (show_separator) cout << i + 1 << "> ";
cout << st[i] << endl;
}
}

View file

@ -1,33 +1,21 @@
#ifndef PROGRAM_HPP
#define PROGRAM_HPP
// std c
#include <math.h>
#include <stdint.h>
#include <stdlib.h>
#include <sys/mman.h>
// std c++
#include <fstream>
#include <iomanip>
#include <iostream>
#include <map>
#include <sstream>
#include <vector>
using namespace std;
// external libs
#define MPFR_USE_NO_MACRO
#include <mpfr.h>
#include "linenoise.h"
#include <mpreal.h>
using namespace mpfr;
// internal includes
#include "constant.h"
#include "escape.h"
#include "object.hpp"
#include "stack.hpp"
#include "version.h"
#include "lexer.hpp"
//< convinient structure to preprocess a program
struct if_layout_t {
@ -42,7 +30,7 @@ struct if_layout_t {
};
//< program class: the class containing a string parser, all the programs keywords, a stack for running the program
class program : public deque<object*> {
class program : public deque<object*>, public Lexer {
public:
program(rpnstack& stk, heap& hp, program* parent = nullptr):_stack(stk),_heap(hp),_parent(parent) {
interrupt_now = false;
@ -53,9 +41,7 @@ class program : public deque<object*> {
}
// parser
static ret_value parse(string& entry, program& prog);
static ret_value entry(program& prog);
static void entry_completion_generator(const char* text, linenoiseCompletions* lc);
ret_value parse(string& entry);
static ret_value get_fn(const char* fn_name, program_fn_t& fn, cmd_type_t& type);
// running
@ -74,6 +60,8 @@ class program : public deque<object*> {
static void apply_default();
static vector<string>& getAutocompletionWords();
private:
bool interrupt_now;
@ -96,16 +84,16 @@ class program : public deque<object*> {
int stack_size() { return _stack.size(); }
private:
static const char* s_ret_value_string[ret_max];
// keywords
struct keyword_t {
cmd_type_t type;
char name[MAX_COMMAND_LENGTH];
string name;
program_fn_t fn;
string comment;
};
static keyword_t s_keywords[];
static vector<keyword_t> _keywords;
static map<string, Lexer::ReservedWord> _keywordsMap;
static vector<string> _autocompletionWords;
// keywords implementation
////
@ -285,26 +273,22 @@ class program : public deque<object*> {
// convenience macros for rpn_xx function
// carefull : some of these macros modify program flow
#define ERR_CONTEXT(err) do { _err = (err); _err_context = __FUNCTION__; } while (0)
#define setErrorContext(err) do { _err = (err); _err_context = __FUNCTION__; } while (0)
#define MIN_ARGUMENTS(num) do { \
if (stack_size() < (num)) { ERR_CONTEXT(ret_missing_operand); return; } \
if (stack_size() < (num)) { setErrorContext(ret_missing_operand); return; } \
} while (0)
#define MIN_ARGUMENTS_RET(num, ret) do { \
if (stack_size() < (num)) { ERR_CONTEXT(ret_missing_operand); return (ret); } \
if (stack_size() < (num)) { setErrorContext(ret_missing_operand); return (ret); } \
} while (0)
#define ARG_MUST_BE_OF_TYPE(num, type) do { \
if (_stack.at(num)->_type != (type)) { ERR_CONTEXT(ret_bad_operand_type); return; } \
if (_stack.at(num)->_type != (type)) { setErrorContext(ret_bad_operand_type); return; } \
} while (0)
#define ARG_MUST_BE_OF_TYPE_RET(num, type, ret) do { \
if (_stack.at(num)->_type != (type)) { ERR_CONTEXT(ret_bad_operand_type); return (ret); } \
if (_stack.at(num)->_type != (type)) { setErrorContext(ret_bad_operand_type); return (ret); } \
} while (0)
#define IS_ARG_TYPE(num, type) (_stack.at(num)->_type == (type))
#define CHECK_MPFR(op) do { (void)(op); } while (0)
#endif

View file

@ -1,4 +1,34 @@
#include <stdio.h>
#include "escape.h"
#include "linenoise.h"
#include "program.hpp"
#include "version.h"
// description
#define XSTR(a) STR(a)
#define STR(a) #a
static const string _description{
ATTR_BOLD "R" ATTR_OFF "everse " ATTR_BOLD "P" ATTR_OFF "olish " ATTR_BOLD "N" ATTR_OFF
"otation language\n\n"
"using " ATTR_BOLD "GMP" ATTR_OFF " v" XSTR(__GNU_MP_VERSION) "." XSTR(__GNU_MP_VERSION_MINOR) "." XSTR(
__GNU_MP_VERSION_PATCHLEVEL) " under GNU LGPL\n" ATTR_BOLD "MPFR" ATTR_OFF " v" MPFR_VERSION_STRING
" under GNU LGPL\n"
"and " ATTR_BOLD "linenoise-ng" ATTR_OFF " v" LINENOISE_VERSION
" under BSD\n"};
// syntax
static const string _syntax{ATTR_BOLD "Syntax" ATTR_OFF
": rpn [command]\n"
"with optional command = list of commands"};
static const map<string, mpfr_rnd_t> _mpfr_round{{"nearest (even)", MPFR_RNDN},
{"toward zero", MPFR_RNDZ},
{"toward +inf", MPFR_RNDU},
{"toward -inf", MPFR_RNDD},
{"away from zero", MPFR_RNDA},
{"faithful rounding", MPFR_RNDF},
{"nearest (away from zero)", MPFR_RNDNA}};
/// @brief nop keyword implementation
///
@ -8,32 +38,30 @@ void program::rpn_nop() {
/// @brief quit keyword implementation
///
void program::rpn_good_bye() { ERR_CONTEXT(ret_good_bye); }
void program::rpn_good_bye() { setErrorContext(ret_good_bye); }
/// @brief nop keyword implementation
/// the result is written on stdout
///
void program::rpn_help() {
// software name
cout << endl << ATTR_BOLD << uname << ATTR_OFF << endl;
cout << endl << ATTR_BOLD << RPN_UNAME << ATTR_OFF << endl;
// description
cout << description << endl << endl;
// _description
cout << _description << endl << endl;
// syntax
cout << syntax << endl;
// _syntax
cout << _syntax << endl;
// keywords
unsigned int i = 0;
while (s_keywords[i].type != cmd_max) {
if (s_keywords[i].comment.size() != 0) {
for (auto& kw : _keywords)
if (!kw.comment.empty()) {
// titles in bold
if (s_keywords[i].type == cmd_undef) cout << ATTR_BOLD;
if (kw.type == cmd_undef) cout << ATTR_BOLD;
// show title or keyword + comment
cout << s_keywords[i].name << '\t' << s_keywords[i].comment << endl;
if (s_keywords[i].type == cmd_undef) cout << ATTR_OFF;
}
i++;
cout << kw.name << '\t' << kw.comment << endl;
if (kw.type == cmd_undef) cout << ATTR_OFF;
}
cout << endl;
@ -57,14 +85,9 @@ void program::rpn_help() {
// bits precision, decimal digits and rounding mode
cout << " with " << number::s_digits << " digits after the decimal point" << endl;
cout << "Current floating point precision is " << (int)mpreal::get_default_prec() << " bits" << endl;
struct RndStrings {
string name;
mp_rnd_t rnd;
};
vector<RndStrings> rndStrings{MPFR_ROUND_STRINGS};
for (RndStrings r : rndStrings)
if (r.rnd == mpreal::get_default_rnd()) {
cout << "Current rounding mode is " << r.name << endl;
for (auto& rn : _mpfr_round)
if (rn.second == mpreal::get_default_rnd()) {
cout << "Current rounding mode is '" << rn.first << '\'' << endl;
break;
}
cout << endl << endl;
@ -103,7 +126,7 @@ void program::rpn_std() {
number::s_digits = (int)digits;
_stack.pop();
} else
ERR_CONTEXT(ret_out_of_range);
setErrorContext(ret_out_of_range);
}
/// @brief fix keyword implementation
@ -120,7 +143,7 @@ void program::rpn_fix() {
number::s_digits = (int)digits;
_stack.pop();
} else
ERR_CONTEXT(ret_out_of_range);
setErrorContext(ret_out_of_range);
}
/// @brief sci keyword implementation
@ -137,16 +160,16 @@ void program::rpn_sci() {
number::s_digits = (int)digits;
_stack.pop();
} else
ERR_CONTEXT(ret_out_of_range);
setErrorContext(ret_out_of_range);
}
/// @brief version keyword implementation
/// @brief _version keyword implementation
///
void program::rpn_version() { _stack.push_front(new ostring(version)); }
void program::rpn_version() { _stack.push_front(new ostring(RPN_VERSION)); }
/// @brief uname keyword implementation
/// @brief _uname keyword implementation
///
void program::rpn_uname() { _stack.push_front(new ostring(uname)); }
void program::rpn_uname() { _stack.push_front(new ostring(RPN_UNAME)); }
/// @brief history keyword implementation
///
@ -154,7 +177,7 @@ void program::rpn_history() {
// see command history on stdout
int index = 0;
char* line = linenoiseHistoryLine(index);
while (line != NULL) {
while (line != nullptr) {
cout << line << endl;
free(line);
line = linenoiseHistoryLine(++index);
@ -191,7 +214,7 @@ void program::rpn_precision() {
}
_stack.pop();
} else
ERR_CONTEXT(ret_out_of_range);
setErrorContext(ret_out_of_range);
}
/// @brief round keyword implementation
@ -200,12 +223,12 @@ void program::rpn_round() {
MIN_ARGUMENTS(1);
ARG_MUST_BE_OF_TYPE(0, cmd_string);
map<string, mpfr_rnd_t> matchRound{MPFR_ROUND_STRINGS};
map<string, mpfr_rnd_t> matchRound{_mpfr_round};
auto found = matchRound.find(_stack.value<ostring>(0));
if (found != matchRound.end())
mpreal::set_default_rnd(found->second);
else
ERR_CONTEXT(ret_out_of_range);
setErrorContext(ret_out_of_range);
_stack.pop();
}

View file

@ -13,7 +13,7 @@ void program::rpn_log10() {
else if (_stack.type(0) == cmd_complex)
_stack.value<ocomplex>(0) = log10(_stack.value<ocomplex>(0));
else
ERR_CONTEXT(ret_bad_operand_type);
setErrorContext(ret_bad_operand_type);
}
/// @brief alog10 keyword implementation
@ -25,7 +25,7 @@ void program::rpn_alog10() {
else if (_stack.type(0) == cmd_complex)
_stack.value<ocomplex>(0) = exp(log(mpreal(10)) * _stack.value<ocomplex>(0));
else
ERR_CONTEXT(ret_bad_operand_type);
setErrorContext(ret_bad_operand_type);
}
/// @brief log2 keyword implementation
@ -37,7 +37,7 @@ void program::rpn_log2() {
else if (_stack.type(0) == cmd_complex)
_stack.value<ocomplex>(0) = log(_stack.value<ocomplex>(0)) / const_log2();
else
ERR_CONTEXT(ret_bad_operand_type);
setErrorContext(ret_bad_operand_type);
}
/// @brief alog2 keyword implementation
@ -49,7 +49,7 @@ void program::rpn_alog2() {
else if (_stack.type(0) == cmd_complex)
_stack.value<ocomplex>(0) = exp(const_log2() * _stack.value<ocomplex>(0));
else
ERR_CONTEXT(ret_bad_operand_type);
setErrorContext(ret_bad_operand_type);
}
/// @brief ln keyword implementation
@ -61,7 +61,7 @@ void program::rpn_ln() {
else if (_stack.type(0) == cmd_complex)
_stack.value<ocomplex>(0) = log(_stack.value<ocomplex>(0));
else
ERR_CONTEXT(ret_bad_operand_type);
setErrorContext(ret_bad_operand_type);
}
/// @brief exp keyword implementation
@ -73,7 +73,7 @@ void program::rpn_exp() {
else if (_stack.type(0) == cmd_complex)
_stack.value<ocomplex>(0) = exp(_stack.value<ocomplex>(0));
else
ERR_CONTEXT(ret_bad_operand_type);
setErrorContext(ret_bad_operand_type);
}
/// @brief expm keyword implementation
@ -85,7 +85,7 @@ void program::rpn_expm() {
else if (_stack.type(0) == cmd_complex)
_stack.value<ocomplex>(0) = exp(_stack.value<ocomplex>(0)) - mpreal(1);
else
ERR_CONTEXT(ret_bad_operand_type);
setErrorContext(ret_bad_operand_type);
}
/// @brief lnp1 keyword implementation
@ -97,7 +97,7 @@ void program::rpn_lnp1() {
else if (_stack.type(0) == cmd_complex)
_stack.value<ocomplex>(0) = log(_stack.value<ocomplex>(0) + mpreal(1));
else
ERR_CONTEXT(ret_bad_operand_type);
setErrorContext(ret_bad_operand_type);
}
/// @brief sinh keyword implementation
@ -109,7 +109,7 @@ void program::rpn_sinh() {
else if (_stack.type(0) == cmd_complex)
_stack.value<ocomplex>(0) = sinh(_stack.value<ocomplex>(0));
else
ERR_CONTEXT(ret_bad_operand_type);
setErrorContext(ret_bad_operand_type);
}
/// @brief asinh keyword implementation
@ -121,7 +121,7 @@ void program::rpn_asinh() {
else if (_stack.type(0) == cmd_complex)
_stack.value<ocomplex>(0) = asinh(_stack.value<ocomplex>(0));
else
ERR_CONTEXT(ret_bad_operand_type);
setErrorContext(ret_bad_operand_type);
}
/// @brief cosh keyword implementation
@ -133,7 +133,7 @@ void program::rpn_cosh() {
else if (_stack.type(0) == cmd_complex)
_stack.value<ocomplex>(0) = cosh(_stack.value<ocomplex>(0));
else
ERR_CONTEXT(ret_bad_operand_type);
setErrorContext(ret_bad_operand_type);
}
/// @brief acosh keyword implementation
@ -145,7 +145,7 @@ void program::rpn_acosh() {
else if (_stack.type(0) == cmd_complex)
_stack.value<ocomplex>(0) = acosh(_stack.value<ocomplex>(0));
else
ERR_CONTEXT(ret_bad_operand_type);
setErrorContext(ret_bad_operand_type);
}
/// @brief tanh keyword implementation
@ -157,7 +157,7 @@ void program::rpn_tanh() {
else if (_stack.type(0) == cmd_complex)
_stack.value<ocomplex>(0) = tanh(_stack.value<ocomplex>(0));
else
ERR_CONTEXT(ret_bad_operand_type);
setErrorContext(ret_bad_operand_type);
}
/// @brief atanh keyword implementation
@ -169,5 +169,5 @@ void program::rpn_atanh() {
else if (_stack.type(0) == cmd_complex)
_stack.value<ocomplex>(0) = atanh(_stack.value<ocomplex>(0));
else
ERR_CONTEXT(ret_bad_operand_type);
setErrorContext(ret_bad_operand_type);
}

View file

@ -14,7 +14,7 @@ bool program::find_variable(string& variable, object*& obj) {
if (_local_heap.get(variable, obj))
found = true;
else {
while (parent != NULL) {
while (parent != nullptr) {
if (parent->_local_heap.get(variable, obj)) {
found = true;
break;
@ -36,7 +36,7 @@ void program::rpn_eval(void) {
string prog_text;
MIN_ARGUMENTS(1);
if (IS_ARG_TYPE(0, cmd_symbol)) {
if (_stack.type(0) == cmd_symbol) {
// recall a variable
object* obj;
string variable(_stack.value<symbol>(0));
@ -53,21 +53,21 @@ void program::rpn_eval(void) {
_stack.push_front(obj);
}
} else
ERR_CONTEXT(ret_unknown_variable);
} else if (IS_ARG_TYPE(0, cmd_program)) {
setErrorContext(ret_unknown_variable);
} else if (_stack.type(0) == cmd_program) {
// eval a program
prog_text = _stack.value<oprogram>(0);
_stack.pop();
run_prog = true;
} else
ERR_CONTEXT(ret_bad_operand_type);
setErrorContext(ret_bad_operand_type);
// run prog if any
if (run_prog) {
program prog(_stack, _heap, this);
// make program from entry
if (program::parse(prog_text, prog) == ret_ok) {
if (prog.parse(prog_text) == ret_ok) {
// run it
prog.run();
}
@ -82,7 +82,7 @@ int program::rpn_inprog(branch& myobj) {
bool prog_found = false;
if (myobj.arg1 == -1) {
ERR_CONTEXT(ret_unknown_err);
setErrorContext(ret_unknown_err);
return -1;
}
@ -92,15 +92,15 @@ int program::rpn_inprog(branch& myobj) {
// find next oprogram object
for (unsigned int i = myobj.arg1 + 1; i < size(); i++) {
// count symbol
if ((*this)[i]->_type == cmd_symbol) count_symbols++;
if (at(i)->_type == cmd_symbol) count_symbols++;
// stop if prog
else if ((*this)[i]->_type == cmd_program) {
else if (at(i)->_type == cmd_program) {
prog_found = true;
break;
}
// found something other than symbol
else {
ERR_CONTEXT(ret_bad_operand_type);
setErrorContext(ret_bad_operand_type);
show_error(_err, context);
return -1;
}
@ -108,37 +108,37 @@ int program::rpn_inprog(branch& myobj) {
// found 0 symbols
if (count_symbols == 0) {
ERR_CONTEXT(ret_syntax);
setErrorContext(ret_syntax);
show_error(_err, context);
return -1;
}
// <oprogram> is missing
if (!prog_found) {
ERR_CONTEXT(ret_syntax);
setErrorContext(ret_syntax);
show_error(_err, context);
return -1;
}
// check symbols number vs stack size
if (stack_size() < count_symbols) {
ERR_CONTEXT(ret_missing_operand);
setErrorContext(ret_missing_operand);
show_error(_err, context);
return -1;
}
// load variables
for (unsigned int i = myobj.arg1 + count_symbols; i > myobj.arg1; i--) {
_local_heap[string(((symbol*)(*this)[i])->value)] = _stack.at(0)->clone();
_local_heap[string(((symbol*)at(i))->value)] = _stack.at(0)->clone();
_stack.pop_front();
}
// run the program
string entry(((oprogram*)(*this)[myobj.arg1 + count_symbols + 1])->value);
string entry(((oprogram*)at(myobj.arg1 + count_symbols + 1))->value);
program prog(_stack, _heap, this);
// make the program from entry
if (program::parse(entry, prog) == ret_ok) {
if (prog.parse(entry) == ret_ok) {
// run it
prog.run();
}

View file

@ -31,7 +31,7 @@ void program::rpn_plus() {
_stack.value<ocomplex>(1) += _stack.value<number>(0);
_stack.pop();
} else
ERR_CONTEXT(ret_bad_operand_type);
setErrorContext(ret_bad_operand_type);
}
/// @brief - keyword implementation
@ -60,7 +60,7 @@ void program::rpn_minus() {
_stack.value<ocomplex>(1) = _stack.value<number>(0) - _stack.value<ocomplex>(1);
_stack.pop();
} else
ERR_CONTEXT(ret_bad_operand_type);
setErrorContext(ret_bad_operand_type);
}
/// @brief * keyword implementation
@ -89,7 +89,7 @@ void program::rpn_mul() {
_stack.value<ocomplex>(1) *= _stack.value<number>(0);
_stack.pop();
} else
ERR_CONTEXT(ret_bad_operand_type);
setErrorContext(ret_bad_operand_type);
}
/// @brief / keyword implementation
@ -118,7 +118,7 @@ void program::rpn_div() {
_stack.value<ocomplex>(1) = _stack.value<number>(0) / _stack.value<ocomplex>(1);
_stack.pop();
} else
ERR_CONTEXT(ret_bad_operand_type);
setErrorContext(ret_bad_operand_type);
}
/// @brief neg keyword implementation
@ -131,7 +131,7 @@ void program::rpn_neg() {
else if (_stack.type(0) == cmd_complex)
_stack.value<ocomplex>(0) = -_stack.value<ocomplex>(0);
else
ERR_CONTEXT(ret_bad_operand_type);
setErrorContext(ret_bad_operand_type);
}
/// @brief inv keyword implementation
@ -144,7 +144,7 @@ void program::rpn_inv() {
else if (_stack.type(0) == cmd_complex)
_stack.value<ocomplex>(0) = mpreal{1} / _stack.value<ocomplex>(0);
else
ERR_CONTEXT(ret_bad_operand_type);
setErrorContext(ret_bad_operand_type);
}
/// @brief power keyword implementation
@ -172,7 +172,7 @@ void program::rpn_power() {
_stack.value<ocomplex>(1) = pow(_stack.value<number>(0), _stack.value<ocomplex>(1));
_stack.pop();
} else
ERR_CONTEXT(ret_bad_operand_type);
setErrorContext(ret_bad_operand_type);
}
/// @brief sqrt keyword implementation
@ -192,7 +192,7 @@ void program::rpn_squareroot() {
} else if (_stack.type(0) == cmd_complex)
_stack.value<ocomplex>(0) = sqrt(_stack.value<ocomplex>(0));
else
ERR_CONTEXT(ret_bad_operand_type);
setErrorContext(ret_bad_operand_type);
}
/// @brief hex keyword implementation
@ -205,7 +205,7 @@ void program::rpn_hex() {
_stack.obj<ocomplex>(0).reBase = 16;
_stack.obj<ocomplex>(0).imBase = 16;
} else
ERR_CONTEXT(ret_bad_operand_type);
setErrorContext(ret_bad_operand_type);
}
/// @brief bin keyword implementation
@ -218,7 +218,7 @@ void program::rpn_bin() {
_stack.obj<ocomplex>(0).reBase = 2;
_stack.obj<ocomplex>(0).imBase = 2;
} else
ERR_CONTEXT(ret_bad_operand_type);
setErrorContext(ret_bad_operand_type);
}
/// @brief dec keyword implementation
@ -231,7 +231,7 @@ void program::rpn_dec() {
_stack.obj<ocomplex>(0).reBase = 10;
_stack.obj<ocomplex>(0).imBase = 10;
} else
ERR_CONTEXT(ret_bad_operand_type);
setErrorContext(ret_bad_operand_type);
}
/// @brief base keyword implementation
@ -249,9 +249,9 @@ void program::rpn_base() {
_stack.obj<ocomplex>(0).imBase = base;
}
} else
ERR_CONTEXT(ret_out_of_range);
setErrorContext(ret_out_of_range);
} else
ERR_CONTEXT(ret_bad_operand_type);
setErrorContext(ret_bad_operand_type);
}
/// @brief % (purcent) keyword implementation
@ -283,7 +283,7 @@ void program::rpn_square() {
else if (_stack.at(0)->_type == cmd_complex)
_stack.value<ocomplex>(0) *= _stack.value<ocomplex>(0);
else
ERR_CONTEXT(ret_bad_operand_type);
setErrorContext(ret_bad_operand_type);
}
/// @brief mod keyword implementation
@ -306,7 +306,7 @@ void program::rpn_abs() {
_stack.push(new number(abs(_stack.value<ocomplex>(0))));
_stack.erase(1);
} else
ERR_CONTEXT(ret_bad_operand_type);
setErrorContext(ret_bad_operand_type);
}
/// @brief fact (factorial) keyword implementation
@ -327,7 +327,7 @@ void program::rpn_sign() {
else if (_stack.at(0)->_type == cmd_complex)
_stack.value<ocomplex>(0) = _stack.value<ocomplex>(0) / abs(_stack.value<ocomplex>(0));
else
ERR_CONTEXT(ret_bad_operand_type);
setErrorContext(ret_bad_operand_type);
}
/// @brief mant keyword implementation
@ -336,7 +336,7 @@ void program::rpn_mant() {
MIN_ARGUMENTS(1);
ARG_MUST_BE_OF_TYPE(0, cmd_number);
if (!isfinite(_stack.value<number>(0))) {
ERR_CONTEXT(ret_out_of_range);
setErrorContext(ret_out_of_range);
return;
}
mp_exp_t exp;
@ -349,7 +349,7 @@ void program::rpn_xpon() {
MIN_ARGUMENTS(1);
ARG_MUST_BE_OF_TYPE(0, cmd_number);
if (!isfinite(_stack.value<number>(0))) {
ERR_CONTEXT(ret_out_of_range);
setErrorContext(ret_out_of_range);
return;
}
mp_exp_t exp;

View file

@ -77,7 +77,7 @@ void program::rpn_pick(void) {
// treat stack depth errors
if ((to_pick == 0) || (to_pick > _stack.size())) {
ERR_CONTEXT(ret_out_of_range);
setErrorContext(ret_out_of_range);
return;
}

View file

@ -1,4 +1,5 @@
#include "program.hpp"
#include "input.hpp"
/// @brief sto keyword implementation
///
@ -22,7 +23,7 @@ void program::rpn_stoadd(void) {
MIN_ARGUMENTS(2);
ARG_MUST_BE_OF_TYPE(0, cmd_symbol);
if (_heap.find(_stack.value<ostring>(0)) == _heap.end()) {
ERR_CONTEXT(ret_unknown_variable);
setErrorContext(ret_unknown_variable);
return;
}
rpn_dup();
@ -39,7 +40,7 @@ void program::rpn_stosub(void) {
MIN_ARGUMENTS(2);
ARG_MUST_BE_OF_TYPE(0, cmd_symbol);
if (_heap.find(_stack.value<ostring>(0)) == _heap.end()) {
ERR_CONTEXT(ret_unknown_variable);
setErrorContext(ret_unknown_variable);
return;
}
rpn_dup();
@ -56,7 +57,7 @@ void program::rpn_stomul(void) {
MIN_ARGUMENTS(2);
ARG_MUST_BE_OF_TYPE(0, cmd_symbol);
if (_heap.find(_stack.value<ostring>(0)) == _heap.end()) {
ERR_CONTEXT(ret_unknown_variable);
setErrorContext(ret_unknown_variable);
return;
}
rpn_dup();
@ -73,7 +74,7 @@ void program::rpn_stodiv(void) {
MIN_ARGUMENTS(2);
ARG_MUST_BE_OF_TYPE(0, cmd_symbol);
if (_heap.find(_stack.value<ostring>(0)) == _heap.end()) {
ERR_CONTEXT(ret_unknown_variable);
setErrorContext(ret_unknown_variable);
return;
}
rpn_dup();
@ -90,7 +91,7 @@ void program::rpn_stoneg(void) {
MIN_ARGUMENTS(1);
ARG_MUST_BE_OF_TYPE(0, cmd_symbol);
if (_heap.find(_stack.value<ostring>(0)) == _heap.end()) {
ERR_CONTEXT(ret_unknown_variable);
setErrorContext(ret_unknown_variable);
return;
}
rpn_dup();
@ -106,7 +107,7 @@ void program::rpn_stoinv(void) {
MIN_ARGUMENTS(1);
ARG_MUST_BE_OF_TYPE(0, cmd_symbol);
if (_heap.find(_stack.value<ostring>(0)) == _heap.end()) {
ERR_CONTEXT(ret_unknown_variable);
setErrorContext(ret_unknown_variable);
return;
}
rpn_dup();
@ -131,7 +132,7 @@ void program::rpn_rcl(void) {
(void)_stack.pop_front();
_stack.push_front(obj->clone());
} else
ERR_CONTEXT(ret_unknown_variable);
setErrorContext(ret_unknown_variable);
}
/// @brief edit keyword implementation
@ -146,7 +147,7 @@ void program::rpn_edit(void) {
_stack.pop();
// set it as the linenoise line entry
linenoisePreloadBuffer((const char*)st.str().c_str());
Input::preload(st.str().c_str());
}
/// @brief recall then eval a symbol variable if it is auto-evaluable
@ -179,7 +180,7 @@ void program::rpn_purge(void) {
delete i->second;
_heap.erase(i);
} else
ERR_CONTEXT(ret_unknown_variable);
setErrorContext(ret_unknown_variable);
_stack.pop();
}
@ -199,7 +200,7 @@ void program::rpn_vars(void) {
}
// parents local variables
while (parent != NULL) {
while (parent != nullptr) {
for (int i = 0; i < (int)parent->_local_heap.size(); i++) {
(void)parent->_local_heap.get_by_index(i, name, obj);
cout<<"var "<<i+1<<": name '"<<name<<"', type "<<obj->name()<<", value ";

View file

@ -27,7 +27,7 @@ void program::rpn_strout() {
_stack.pop();
// make program from string in stack level 1
if (program::parse(entry, prog) == ret_ok)
if (prog.parse(entry) == ret_ok)
// run it
prog.run();
}

View file

@ -1,5 +1,8 @@
#include <regex>
using namespace std;
#include "version.h"
#include "escape.h"
#include "program.hpp"
/// @brief write stack in a string, each entry separated between commas
@ -59,9 +62,9 @@ void program::rpn_test() {
string test_filename = _stack.value<ostring>(0);
_stack.pop();
cout << endl << "rpn version is " << version << endl;
cout << endl << "rpn version is " << RPN_VERSION << endl;
test(test_filename, total_tests, total_tests_failed, total_steps, total_steps_failed);
test_show_result("Total", total_tests, total_tests_failed, total_steps, total_steps_failed);
test_show_result("\nTotal", total_tests, total_tests_failed, total_steps, total_steps_failed);
}
/// @brief load a test file and run its tests
@ -228,7 +231,7 @@ void program::test(string test_filename, int& total_tests, int& total_tests_fail
entry = regex_replace(entry, regex("`"), "");
if (!entry.empty()) {
program prog(stk, hp);
ret = program::parse(entry, prog);
ret = prog.parse(entry);
if (ret == ret_ok) {
// run it
(void)prog.run();

View file

@ -28,7 +28,7 @@ void program::rpn_sup(void) {
_stack.push_front(new number(cmp_strings_on_stack_top() == 1));
_stack.erase(1, 2);
} else
ERR_CONTEXT(ret_bad_operand_type);
setErrorContext(ret_bad_operand_type);
}
/// @brief >= keyword implementation
@ -46,7 +46,7 @@ void program::rpn_sup_eq(void) {
_stack.push_front(new number(cmp_strings_on_stack_top() != -1));
_stack.erase(1, 2);
} else
ERR_CONTEXT(ret_bad_operand_type);
setErrorContext(ret_bad_operand_type);
}
/// @brief < keyword implementation
@ -64,7 +64,7 @@ void program::rpn_inf(void) {
_stack.push_front(new number(cmp_strings_on_stack_top() == -1));
_stack.erase(1, 2);
} else
ERR_CONTEXT(ret_bad_operand_type);
setErrorContext(ret_bad_operand_type);
}
/// @brief <= keyword implementation
@ -80,7 +80,7 @@ void program::rpn_inf_eq(void) {
_stack.push_front(new number(cmp_strings_on_stack_top() != 1));
_stack.erase(1, 2);
} else
ERR_CONTEXT(ret_bad_operand_type);
setErrorContext(ret_bad_operand_type);
}
/// @brief != keyword implementation
@ -103,7 +103,7 @@ void program::rpn_diff(void) {
_stack.push_front(new number(cmp_strings_on_stack_top() != 0));
_stack.erase(1, 2);
} else
ERR_CONTEXT(ret_bad_operand_type);
setErrorContext(ret_bad_operand_type);
}
/// @brief == keyword implementation
@ -126,7 +126,7 @@ void program::rpn_eq(void) {
_stack.push_front(new number(cmp_strings_on_stack_top() == 0));
_stack.erase(1, 2);
} else
ERR_CONTEXT(ret_bad_operand_type);
setErrorContext(ret_bad_operand_type);
}
/// @brief and keyword implementation

View file

@ -13,7 +13,7 @@ void program::rpn_time() {
clock_gettime(CLOCK_REALTIME, &ts);
time_t time = (time_t)ts.tv_sec;
tm = localtime(&time);
if (tm != NULL) {
if (tm != nullptr) {
// date format = HH.MMSSssssss
date = ((double)tm->tm_hour) * 10000000000.0 + ((double)tm->tm_min) * 100000000.0 +
((double)tm->tm_sec) * 1000000.0 + (double)(ts.tv_nsec / 1000);
@ -23,7 +23,7 @@ void program::rpn_time() {
_stack.push(new number(date));
_stack.value<number>(0) /= 10000000000.0;
} else
ERR_CONTEXT(ret_internal);
setErrorContext(ret_internal);
}
/// @brief date keyword implementation
@ -37,7 +37,7 @@ void program::rpn_date() {
clock_gettime(CLOCK_REALTIME, &ts);
time_t time = (time_t)ts.tv_sec;
tm = localtime(&time);
if (tm != NULL) {
if (tm != nullptr) {
// date format = (M)M.DDYYYY
date = (double)(tm->tm_mon + 1) * 1000000.0 + (double)(tm->tm_mday) * 10000.0 + (double)(tm->tm_year + 1900);
@ -47,7 +47,7 @@ void program::rpn_date() {
_stack.push(new number(date));
_stack.value<number>(0) /= 1000000.0;
} else
ERR_CONTEXT(ret_internal);
setErrorContext(ret_internal);
}
/// @brief ticks keyword implementation
@ -61,10 +61,10 @@ void program::rpn_ticks() {
clock_gettime(CLOCK_REALTIME, &ts);
time_t time = (time_t)ts.tv_sec;
tm = localtime(&time);
if (tm != NULL) {
if (tm != nullptr) {
// date in µs
date = 1000000.0 * (double)ts.tv_sec + (double)(ts.tv_nsec / 1000);
_stack.push(new number(date));
} else
ERR_CONTEXT(ret_internal);
setErrorContext(ret_internal);
}

View file

@ -34,7 +34,7 @@ void program::rpn_sin(void) {
else if (_stack.type(0) == cmd_complex)
_stack.value<ocomplex>(0) = sin(_stack.value<ocomplex>(0));
else
ERR_CONTEXT(ret_bad_operand_type);
setErrorContext(ret_bad_operand_type);
}
/// @brief asin keyword implementation
@ -47,7 +47,7 @@ void program::rpn_asin(void) {
else if (_stack.type(0) == cmd_complex)
_stack.value<ocomplex>(0) = asin(_stack.value<ocomplex>(0));
else
ERR_CONTEXT(ret_bad_operand_type);
setErrorContext(ret_bad_operand_type);
}
/// @brief cos keyword implementation
@ -60,7 +60,7 @@ void program::rpn_cos(void) {
else if (_stack.type(0) == cmd_complex)
_stack.value<ocomplex>(0) = cos(_stack.value<ocomplex>(0));
else
ERR_CONTEXT(ret_bad_operand_type);
setErrorContext(ret_bad_operand_type);
}
/// @brief acos keyword implementation
@ -73,7 +73,7 @@ void program::rpn_acos(void) {
else if (_stack.type(0) == cmd_complex)
_stack.value<ocomplex>(0) = acos(_stack.value<ocomplex>(0));
else
ERR_CONTEXT(ret_bad_operand_type);
setErrorContext(ret_bad_operand_type);
}
/// @brief tan keyword implementation
@ -86,7 +86,7 @@ void program::rpn_tan(void) {
else if (_stack.type(0) == cmd_complex)
_stack.value<ocomplex>(0) = tan(_stack.value<ocomplex>(0));
else
ERR_CONTEXT(ret_bad_operand_type);
setErrorContext(ret_bad_operand_type);
}
/// @brief atan keyword implementation
@ -99,5 +99,5 @@ void program::rpn_atan(void) {
else if (_stack.type(0) == cmd_complex)
_stack.value<ocomplex>(0) = atan(_stack.value<ocomplex>(0));
else
ERR_CONTEXT(ret_bad_operand_type);
setErrorContext(ret_bad_operand_type);
}

View file

@ -1,23 +1,2 @@
// version and soft name
#define RPN_VERSION "2.4"
static const string version{RPN_VERSION};
static const string uname{"rpn v" RPN_VERSION ", (c) 2017 <louis@rubet.fr>, GNU LGPL v3"};
#define XSTR(a) STR(a)
#define STR(a) #a
// description
static const string description{
ATTR_BOLD "R" ATTR_OFF "everse " ATTR_BOLD "P" ATTR_OFF "olish " ATTR_BOLD "N" ATTR_OFF
"otation language\n\n"
"using " ATTR_BOLD "GMP" ATTR_OFF
" v" XSTR(__GNU_MP_VERSION) "." XSTR(__GNU_MP_VERSION_MINOR) "." XSTR(
__GNU_MP_VERSION_PATCHLEVEL) " under GNU LGPL\n" ATTR_BOLD "MPFR" ATTR_OFF " v" MPFR_VERSION_STRING
" under GNU LGPL\n"
"and " ATTR_BOLD "linenoise-ng" ATTR_OFF " v" LINENOISE_VERSION
" under BSD\n"};
// syntax
static const string syntax{ATTR_BOLD "Syntax" ATTR_OFF
": rpn [command]\n"
"with optional command = list of commands"};
#define RPN_UNAME "rpn v" RPN_VERSION ", (c) 2017 <louis@rubet.fr>, GNU LGPL v3"