mirror of
https://github.com/louisrubet/rpn
synced 2025-01-30 20:34:30 +01:00
Add lexer and input classes
This commit is contained in:
parent
8542890b61
commit
1faf101dd8
21 changed files with 524 additions and 613 deletions
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
73
src/lexer.hpp
Normal 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
|
50
src/main.cpp
50
src/main.cpp
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 << "\""; }
|
||||
|
|
117
src/program.cpp
117
src/program.cpp
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,33 +38,31 @@ 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;
|
||||
cout << kw.name << '\t' << kw.comment << endl;
|
||||
if (kw.type == cmd_undef) cout << ATTR_OFF;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
cout << endl;
|
||||
|
||||
// show mode
|
||||
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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 ";
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
|
|
Loading…
Add table
Reference in a new issue