diff --git a/CMakeLists.txt b/CMakeLists.txt index 801a9d0..2a75c5f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -51,25 +51,25 @@ include_directories(${PROJECT_SOURCE_DIR}/src ${PROJECT_SOURCE_DIR}/linenoise-ng # build add_executable( rpn - ${PROJECT_SOURCE_DIR}/src/main.cpp - ${PROJECT_SOURCE_DIR}/src/object.cpp - ${PROJECT_SOURCE_DIR}/src/mpreal-out.cpp - ${PROJECT_SOURCE_DIR}/src/program.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 - ${PROJECT_SOURCE_DIR}/src/rpn-logs.cpp - ${PROJECT_SOURCE_DIR}/src/rpn-program.cpp - ${PROJECT_SOURCE_DIR}/src/rpn-real.cpp - ${PROJECT_SOURCE_DIR}/src/rpn-stack.cpp - ${PROJECT_SOURCE_DIR}/src/rpn-store.cpp - ${PROJECT_SOURCE_DIR}/src/rpn-string.cpp - ${PROJECT_SOURCE_DIR}/src/rpn-test.cpp - ${PROJECT_SOURCE_DIR}/src/rpn-test-framework.cpp - ${PROJECT_SOURCE_DIR}/src/rpn-time.cpp - ${PROJECT_SOURCE_DIR}/src/rpn-trig.cpp + ${PROJECT_SOURCE_DIR}/src/main.cc + ${PROJECT_SOURCE_DIR}/src/object.cc + ${PROJECT_SOURCE_DIR}/src/mpreal-out.cc + ${PROJECT_SOURCE_DIR}/src/program.cc + ${PROJECT_SOURCE_DIR}/src/lexer.cc + ${PROJECT_SOURCE_DIR}/src/input.cc + ${PROJECT_SOURCE_DIR}/src/rpn-branch.cc + ${PROJECT_SOURCE_DIR}/src/rpn-complex.cc + ${PROJECT_SOURCE_DIR}/src/rpn-general.cc + ${PROJECT_SOURCE_DIR}/src/rpn-logs.cc + ${PROJECT_SOURCE_DIR}/src/rpn-program.cc + ${PROJECT_SOURCE_DIR}/src/rpn-real.cc + ${PROJECT_SOURCE_DIR}/src/rpn-stack.cc + ${PROJECT_SOURCE_DIR}/src/rpn-store.cc + ${PROJECT_SOURCE_DIR}/src/rpn-string.cc + ${PROJECT_SOURCE_DIR}/src/rpn-test.cc + ${PROJECT_SOURCE_DIR}/src/rpn-test-framework.cc + ${PROJECT_SOURCE_DIR}/src/rpn-time.cc + ${PROJECT_SOURCE_DIR}/src/rpn-trig.cc ${PROJECT_SOURCE_DIR}/linenoise-ng/src/ConvertUTF.cpp ${PROJECT_SOURCE_DIR}/linenoise-ng/src/linenoise.cpp ${PROJECT_SOURCE_DIR}/linenoise-ng/src/wcwidth.cpp diff --git a/Changelog.md b/Changelog.md index 9b89648..18bc9a7 100644 --- a/Changelog.md +++ b/Changelog.md @@ -15,17 +15,13 @@ Changelog - static and global variables - common patterns (ex: no static maps or vectors) - classes (explicit) + - naming: + - file names (.cc .h), types (PascalCase), variables (snake_case), members (trailing _), static const (camelCase begining with k), enum (enum class, values like static const + - consistent comments (//), class comments, functions comments - cpplint used with a CPPLINT.cfg removing some warnings - Test files are now markdown (.md) files, tests result are slightly changed - Delivery as flatpak and snap -- it seems cosh was giving sinh (!) - -grosse perte en performances (!) -- v2.3.2 fibo: 0,01s user 0,01s system 97% cpu 0,017 total -- v3.0.0 fibo: 2,60s user 0,00s system 99% cpu 2,611 total -- facteur 150 environ -cf https://gmplib.org/manual/Custom-Allocation -cf https://www.geeksforgeeks.org/overloading-new-delete-operator-c/ +- error string are slightly different, althought error codes are still the same New - `«` and `»` are now valid as program delimiters. `<<` and `>>` are still valid diff --git a/src/CPPLINT.cfg b/src/CPPLINT.cfg index d771828..1910e8d 100644 --- a/src/CPPLINT.cfg +++ b/src/CPPLINT.cfg @@ -2,6 +2,10 @@ set noparent filter=-whitespace/line_length -// avoid "Is this a non-const reference? If so, make const or use a pointer" -// as it seems to be allowed by google now , cf https://github.com/cpplint/cpplint/issues/148 +# avoid "Is this a non-const reference? If so, make const or use a pointer" +# as it seems to be allowed by google now , cf https://github.com/cpplint/cpplint/issues/148 filter=-runtime/references + +# avoid "Include the directory when naming header files" +# for files not being in a subdirectory +filter=-build/include_subdir diff --git a/src/input.cpp b/src/input.cc similarity index 62% rename from src/input.cpp rename to src/input.cc index c41f1bf..c9c50bf 100644 --- a/src/input.cpp +++ b/src/input.cc @@ -1,33 +1,34 @@ // Copyright (c) 2014-2022 Louis Rubet -#include "input.hpp" +#include "input.h" #include -vector* Input::_acWords = nullptr; +vector* Input::ac_list_ = nullptr; -Input::Input(string& entry, vector& autocompletionWords, string prompt, string mlPrompt) : status(cont) { +Input::Input(string& entry, vector& autocompletion_list, string prompt, string multiline_prompt) + : status(InputStatus::kContinue) { char* c_entry = nullptr; bool multiline = false; int entry_len; - _acWords = &autocompletionWords; + ac_list_ = &autocompletion_list; // linenoise for entry - linenoiseSetCompletionCallback(entry_completion_generator); - while (status == cont) { + linenoiseSetCompletionCallback(EntryCompletionGenerator); + while (status == InputStatus::kContinue) { // get user entry if (multiline) - c_entry = linenoise(mlPrompt.c_str(), &entry_len); + c_entry = linenoise(multiline_prompt.c_str(), &entry_len); else c_entry = linenoise(prompt.c_str(), &entry_len); // Ctrl-C if (linenoiseKeyType() == 1) { if (entry_len > 0 || multiline) - status = abort; + status = InputStatus::kAbort; else - status = ctrlc; + status = InputStatus::kCtrlc; } else if (linenoiseKeyType() == 3) { multiline = true; if (c_entry != nullptr) entry += c_entry; @@ -37,9 +38,9 @@ Input::Input(string& entry, vector& autocompletionWords, string prompt, entry += c_entry; // keep history if (c_entry[0] != 0) (void)linenoiseHistoryAdd(entry.c_str()); - status = ok; + status = InputStatus::kOk; } else { - status = error; + status = InputStatus::kError; } } } @@ -52,21 +53,21 @@ Input::Input(string& entry, vector& autocompletionWords, string prompt, /// @param text the text after wich the user entered TAB /// @param lc the completion object to add strings with linenoiseAddCompletion() /// -void Input::entry_completion_generator(const char* text, linenoiseCompletions* lc) { - if (Input::_acWords == nullptr || text == nullptr) return; +void Input::EntryCompletionGenerator(const char* text, linenoiseCompletions* lc) { + if (Input::ac_list_ == nullptr || text == nullptr) return; int text_len = strnlen(text, 6); if (text_len == 0) // propose all keywords - for (string& ac : *Input::_acWords) linenoiseAddCompletion(lc, ac.c_str()); + for (string& ac : *Input::ac_list_) linenoiseAddCompletion(lc, ac.c_str()); else // propose only keywords matching to text begining - for (string& ac : *Input::_acWords) + for (string& ac : *Input::ac_list_) // compare list entry with text, return if match if (ac.compare(0, text_len, text) == 0) linenoiseAddCompletion(lc, ac.c_str()); } -void Input::preload(const char* preloadText) { +void Input::Preload(const char* preloadText) { if (preloadText != nullptr) linenoisePreloadBuffer(preloadText); } diff --git a/src/input.h b/src/input.h new file mode 100644 index 0000000..171775b --- /dev/null +++ b/src/input.h @@ -0,0 +1,25 @@ +// Copyright (c) 2014-2022 Louis Rubet + +#ifndef SRC_INPUT_HPP_ +#define SRC_INPUT_HPP_ + +#include +#include +#include +using std::string, std::vector; + +#include "linenoise.h" + +class Input { + public: + Input(string& entry, vector& autocompletion_list, string prompt = "rpn> ", string multiline_prompt = ">"); + enum class InputStatus { kOk, kContinue, kAbort, kCtrlc, kError } status; + + static void Preload(const char* preloadText); + + private: + static void EntryCompletionGenerator(const char* text, linenoiseCompletions* lc); + static vector* ac_list_; +}; + +#endif // SRC_INPUT_HPP_ diff --git a/src/input.hpp b/src/input.hpp deleted file mode 100644 index cec0079..0000000 --- a/src/input.hpp +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (c) 2014-2022 Louis Rubet - -#ifndef SRC_INPUT_HPP_ -#define SRC_INPUT_HPP_ - -#include -#include -#include -using namespace std; - -#include "linenoise.h" - -class Input { - public: - Input(string& entry, vector& autocompletionWords, string prompt = "rpn> ", string mlPrompt = ">"); - enum { ok, cont, abort, ctrlc, error } status; - - static void preload(const char* preloadText); - - private: - static void entry_completion_generator(const char* text, linenoiseCompletions* lc); - static vector* _acWords; -}; - -#endif // SRC_INPUT_HPP_ diff --git a/src/lexer.cpp b/src/lexer.cc similarity index 60% rename from src/lexer.cpp rename to src/lexer.cc index e4428f3..79cee56 100644 --- a/src/lexer.cpp +++ b/src/lexer.cc @@ -1,8 +1,8 @@ // Copyright (c) 2014-2022 Louis Rubet -#include "lexer.hpp" +#include "lexer.h" -bool Lexer::lexer(string& entry, map& keywords, vector& elements, +bool Lexer::Analyse(string& entry, map& keywords, vector& elements, vector& errors) { size_t jump; for (size_t i = 0; i < entry.size(); i++) { @@ -10,31 +10,31 @@ bool Lexer::lexer(string& entry, map& keywords, vector& keywords, vector& errors, +bool Lexer::ParseString(string& entry, size_t idx, size_t& next_idx, vector& errors, vector& 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; + elements.push_back({kString, .value = entry.substr(idx + 1, i - idx - 1)}); + next_idx = i + 1; return true; } } } - elements.push_back({cmd_string, .value = entry.substr(idx + 1, entry.size() - idx - 1)}); - nextIdx = entry.size(); + elements.push_back({kString, .value = entry.substr(idx + 1, entry.size() - idx - 1)}); + next_idx = entry.size(); return true; } -bool Lexer::parseSymbol(string& entry, size_t idx, size_t& nextIdx, vector& errors, +bool Lexer::ParseSymbol(string& entry, size_t idx, size_t& next_idx, vector& errors, vector& 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; + elements.push_back({kSymbol, .value = entry.substr(idx + 1, i - idx - 1), .auto_eval = false}); + next_idx = i + 1; return true; } } - elements.push_back({cmd_symbol, .value = entry.substr(idx + 1, entry.size() - idx - 1)}); - nextIdx = entry.size(); + elements.push_back({kSymbol, .value = entry.substr(idx + 1, entry.size() - idx - 1)}); + next_idx = entry.size(); return true; } -bool Lexer::parseProgram(string& entry, size_t idx, size_t& nextIdx, vector& errors, +bool Lexer::ParseProgram(string& entry, size_t idx, size_t& next_idx, vector& errors, vector& elements) { // here we are sure that entry is at least "<<" // find last ">>" or "»" @@ -92,9 +92,9 @@ bool Lexer::parseProgram(string& entry, size_t idx, size_t& nextIdx, vector' && 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; + Trim(prg); + elements.push_back({kProgram, .value = prg}); + next_idx = i + 2; return true; } else { countNested--; @@ -102,26 +102,26 @@ bool Lexer::parseProgram(string& entry, size_t idx, size_t& nextIdx, vector= entry.size()) return 10; if (entry[scan] == '+') { scan++; - nextIdx = scan; + next_idx = scan; } else if (entry[scan] == '-') { scan++; - nextIdx = scan; + next_idx = scan; positive = false; } if (scan + 2 >= entry.size()) return 10; @@ -131,40 +131,40 @@ int Lexer::getBaseAt(string& entry, size_t& nextIdx, bool& positive) { if (scan + 2 < entry.size()) c = entry[scan + 2]; if (a == '0') { if (b == 'x' || b == 'X') { - nextIdx = scan + 2; + next_idx = scan + 2; return 16; } if (b == 'b' || b == 'B') { - nextIdx = scan + 2; + next_idx = scan + 2; return 2; } } else if (isdigit(a)) { if (b == 'b' || b == 'B') { - nextIdx = scan + 2; + next_idx = scan + 2; return static_cast(a - '0'); } if (isdigit(b) && (c == 'b' || c == 'B')) { - nextIdx = scan + 3; + next_idx = scan + 3; return 10 * static_cast(a - '0') + static_cast(b - '0'); } } return 10; } -bool Lexer::getNumberAt(string& entry, size_t idx, size_t& nextIdx, int& base, mpreal** r, char delim) { +bool Lexer::GetNUmberAt(string& entry, size_t idx, size_t& next_idx, int& base, mpreal** r, char delim) { stringstream ss; int idxNumber = 0; string token; bool positive = true; - nextIdx = idx; + next_idx = idx; ss.str(entry.substr(idx)); if (getline(ss, token, delim)) { size_t numberIdx; - nextIdx = token.size() + idx + 1; - trim(token); - base = getBaseAt(token, numberIdx, positive); + next_idx = token.size() + idx + 1; + Trim(token); + base = GetBaseAt(token, numberIdx, positive); if (base < 2 || base > 62) return false; if (numberIdx != 0) token = token.substr(numberIdx); *r = new mpreal; @@ -176,16 +176,16 @@ bool Lexer::getNumberAt(string& entry, size_t idx, size_t& nextIdx, int& base, m return false; } } - nextIdx = token.size() + idx + 1; + next_idx = token.size() + idx + 1; return false; } -bool Lexer::parseNumber(string& entry, size_t idx, size_t& nextIdx, vector& errors, +bool Lexer::ParseNumber(string& entry, size_t idx, size_t& next_idx, vector& errors, vector& elements) { mpreal* r = nullptr; int base = 10; - if (getNumberAt(entry, idx, nextIdx, base, &r)) { - elements.push_back({cmd_number, .re = r, .reBase = base}); + if (GetNUmberAt(entry, idx, next_idx, base, &r)) { + elements.push_back({kNumber, .re = r, .re_base = base}); return true; } else { errors.push_back({entry.size(), "unterminated number"}); @@ -193,44 +193,44 @@ bool Lexer::parseNumber(string& entry, size_t idx, size_t& nextIdx, vector& errors, +bool Lexer::ParseComplex(string& entry, size_t idx, size_t& next_idx, vector& errors, vector& elements) { mpreal* re = nullptr; mpreal* im = nullptr; - int reBase, imBase = 10; + int re_base, im_base = 10; if (idx + 1 == entry.size()) { - elements.push_back({cmd_symbol, .value = entry.substr(idx, entry.size() - idx)}); - nextIdx = entry.size(); + elements.push_back({kSymbol, .value = entry.substr(idx, entry.size() - idx)}); + next_idx = 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(); + if (!GetNUmberAt(entry, idx + 1, next_idx, re_base, &re, ',')) { + elements.push_back({kSymbol, .value = entry.substr(idx, entry.size() - idx)}); + next_idx = entry.size(); return true; // complex format error, return a symbol } - size_t i = nextIdx; + size_t i = next_idx; if (i >= entry.size()) { - elements.push_back({cmd_symbol, .value = entry.substr(idx, entry.size() - idx)}); - nextIdx = entry.size(); + elements.push_back({kSymbol, .value = entry.substr(idx, entry.size() - idx)}); + next_idx = entry.size(); if (re != nullptr) delete re; if (im != nullptr) delete im; 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(); + if (!GetNUmberAt(entry, i, next_idx, im_base, &im, ')')) { + elements.push_back({kSymbol, .value = entry.substr(idx, entry.size() - idx)}); + next_idx = entry.size(); if (re != nullptr) delete re; if (im != nullptr) delete im; return true; // complex format error, return a symbol } - elements.push_back({cmd_complex, .re = re, .im = im, .reBase = reBase, .imBase = imBase}); - nextIdx++; + elements.push_back({kComplex, .re = re, .im = im, .re_base = re_base, .im_base = im_base}); + next_idx++; return true; } -bool Lexer::parseReserved(string& entry, size_t idx, size_t& nextIdx, vector& elements, +bool Lexer::ParseReserved(string& entry, size_t idx, size_t& next_idx, vector& elements, map& keywords) { stringstream ss(entry.substr(idx)); string token; @@ -239,17 +239,17 @@ bool Lexer::parseReserved(string& entry, size_t idx, size_t& nextIdx, vectorsecond.type, .value = token, .fn = resa->second.fn}); - nextIdx = token.size() + idx; + next_idx = token.size() + idx; return true; } return false; } -bool Lexer::parseUnknown(string& entry, size_t idx, size_t& nextIdx, vector& elements) { +bool Lexer::ParseUnknown(string& entry, size_t idx, size_t& next_idx, vector& elements) { stringstream ss(entry.substr(idx)); string token; ss >> token; - elements.push_back({cmd_symbol, .value = token, .autoEval = true}); - nextIdx = token.size() + idx; + elements.push_back({kSymbol, .value = token, .auto_eval = true}); + next_idx = token.size() + idx; return true; } diff --git a/src/lexer.hpp b/src/lexer.h similarity index 51% rename from src/lexer.hpp rename to src/lexer.h index 08fc468..e31ba0a 100644 --- a/src/lexer.hpp +++ b/src/lexer.h @@ -4,27 +4,27 @@ #define SRC_LEXER_HPP_ #include -using namespace mpfr; +using mpfr::mpreal; #include -#include #include -using namespace std; +#include +using std::map, std::string, std::vector; -#include "object.hpp" +#include "object.h" class Lexer { public: // a structure to describe a syntaxical element and its value struct SynElement { - cmd_type_t type; + ObjectType type; string value; mpreal* re; mpreal* im; - int reBase; - int imBase; + int re_base; + int im_base; program_fn_t fn; - bool autoEval; + bool auto_eval; }; struct SynError { @@ -33,7 +33,7 @@ class Lexer { }; struct ReservedWord { - cmd_type_t type; + ObjectType type; program_fn_t fn; }; @@ -47,27 +47,27 @@ class Lexer { /// @param[in] keywords reserved keywords /// @return false=errors, the lexer must stop /// - bool lexer(string& entry, map& keywords, vector& elements, - vector& errors); + bool Analyse(string& entry, map& keywords, vector& elements, + vector& errors); private: - bool parseString(string& entry, size_t idx, size_t& nextIdx, vector& errors, + bool ParseString(string& entry, size_t idx, size_t& next_idx, vector& errors, vector& elements); - bool parseSymbol(string& entry, size_t idx, size_t& nextIdx, vector& errors, + bool ParseSymbol(string& entry, size_t idx, size_t& next_idx, vector& errors, vector& elements); - bool parseProgram(string& entry, size_t idx, size_t& nextIdx, vector& errors, + bool ParseProgram(string& entry, size_t idx, size_t& next_idx, vector& errors, vector& elements); - bool parseNumber(string& entry, size_t idx, size_t& nextIdx, vector& errors, + bool ParseNumber(string& entry, size_t idx, size_t& next_idx, vector& errors, vector& elements); - bool parseComplex(string& entry, size_t idx, size_t& nextIdx, vector& errors, + bool ParseComplex(string& entry, size_t idx, size_t& next_idx, vector& errors, vector& elements); - bool parseReserved(string& entry, size_t idx, size_t& nextIdx, vector& elements, + bool ParseReserved(string& entry, size_t idx, size_t& next_idx, vector& elements, map& keywords); - bool parseUnknown(string& entry, size_t idx, size_t& nextIdx, vector& elements); + bool ParseUnknown(string& entry, size_t idx, size_t& next_idx, vector& elements); - void trim(string& s); - int getBaseAt(string& entry, size_t& nextIdx, bool& positive); - bool getNumberAt(string& entry, size_t idx, size_t& nextIdx, int& base, mpreal** r, char delim = ' '); + void Trim(string& s); + int GetBaseAt(string& entry, size_t& next_idx, bool& positive); + bool GetNUmberAt(string& entry, size_t idx, size_t& next_idx, int& base, mpreal** r, char delim = ' '); }; #endif // SRC_LEXER_HPP_ diff --git a/src/main.cpp b/src/main.cc similarity index 74% rename from src/main.cpp rename to src/main.cc index a88ee26..374b0f9 100644 --- a/src/main.cpp +++ b/src/main.cc @@ -6,19 +6,19 @@ #include #include #include -using namespace std; +using std::cerr; // internal includes -#include "input.hpp" -#include "program.hpp" +#include "input.h" +#include "program.h" /// @brief actions to be done at rpn exit /// -static void exit_interactive_rpn() { +static void ExitInteractive() { struct passwd* pw = getpwuid(getuid()); if (pw != nullptr) { stringstream history_path; - history_path << pw->pw_dir << "/.rpn_history"; + history_path << pw->pw_dir << "/.RpnHistory"; // trunc current history ofstream history(history_path.str(), ios_base::out | ios_base::trunc); @@ -34,11 +34,11 @@ static void exit_interactive_rpn() { /// @brief actions to be done at rpn exit /// -static void init_interactive_rpn() { +static void EnterInteractive() { struct passwd* pw = getpwuid(getuid()); if (pw != nullptr) { stringstream history_path; - history_path << pw->pw_dir << "/.rpn_history"; + history_path << pw->pw_dir << "/.RpnHistory"; // don't care about errors linenoiseHistorySetMaxLen(100); @@ -52,16 +52,16 @@ static void init_interactive_rpn() { /// @param siginfo signal info, see POSIX sigaction /// @param context see POSIX sigaction /// -static void ctrlc_handler(int sig, siginfo_t* siginfo, void* context) { exit_interactive_rpn(); } +static void CtrlHandler(int sig, siginfo_t* siginfo, void* context) { ExitInteractive(); } /// @brief setup signals handlers to stop with honours /// /// @param prog the prog to catch the signals to, must be checked not nullptr by user /// -static void catch_signals(program* prog) { +static void CatchSignals(program* prog) { struct sigaction act = {0}; - act.sa_sigaction = &ctrlc_handler; + act.sa_sigaction = &CtrlHandler; act.sa_flags = SA_SIGINFO; if (sigaction(SIGINT, &act, nullptr) < 0) cerr << "Warning, Ctrl-C cannot be caught [errno=" << errno << ' ' << strerror(errno) << "']" << endl; @@ -78,12 +78,12 @@ int main(int argc, char* argv[]) { bool go_on = true; // apply default configuration - program::apply_default(); + program::ApplyDefault(); // run with interactive prompt if (argc == 1) { // init history - init_interactive_rpn(); + EnterInteractive(); // entry loop heap heap; @@ -93,17 +93,17 @@ int main(int argc, char* argv[]) { // make program from interactive entry program prog(stack, heap); string entry; - switch (Input(entry, program::getAutocompletionWords()).status) { - case Input::ok: + switch (Input(entry, program::GetAutocompletionWords()).status) { + case Input::InputStatus::kOk: // user could stop prog with CtrlC - catch_signals(&prog); + CatchSignals(&prog); // run it - if (prog.parse(entry) == ret_ok && prog.run() == ret_good_bye) + if (prog.Parse(entry) == kOk && prog.Run() == kGoodbye) go_on = false; else - prog.show_stack(); + prog.ShowStack(); break; - case Input::ctrlc: + case Input::InputStatus::kCtrlc: go_on = false; break; default: @@ -112,7 +112,7 @@ int main(int argc, char* argv[]) { } // manage history and exit - exit_interactive_rpn(); + ExitInteractive(); } else { // run with cmd line arguments heap heap; rpnstack stack; @@ -124,19 +124,19 @@ int main(int argc, char* argv[]) { for (i = 1; i < argc; i++) entry += string(argv[i]) + ' '; // make program - ret = prog.parse(entry); - if (ret == ret_ok) { + ret = prog.Parse(entry); + if (ret == kOk) { // user could stop prog with CtrlC - catch_signals(&prog); + CatchSignals(&prog); // run it - ret = prog.run(); - prog.show_stack(false); + ret = prog.Run(); + prog.ShowStack(false); } } mpfr_free_cache(); - if (ret != ret_ok) return EXIT_FAILURE; + if (ret != kOk) return EXIT_FAILURE; return EXIT_SUCCESS; } diff --git a/src/mpreal-out.cpp b/src/mpreal-out.cc similarity index 97% rename from src/mpreal-out.cpp rename to src/mpreal-out.cc index 276852d..dfb5d4b 100644 --- a/src/mpreal-out.cpp +++ b/src/mpreal-out.cc @@ -1,6 +1,6 @@ // Copyright (c) 2014-2022 Louis Rubet -#include "mpreal-out.hpp" +#include "mpreal-out.h" #define MIN(a, b) (((a) < (b)) ? (a) : (b)) #define MAX(h, i) ((h) > (i) ? (h) : (i)) @@ -33,7 +33,7 @@ #define MPFR_IS_POS(x) (MPFR_SIGN(x) > 0) #define MPFR_PREC(x) ((x)->_mpfr_prec) -ostream& mpreal_output10base(ostream& out, const string& fmt, const mpreal& value) { +ostream& MprealOutput10Base(ostream& out, const string& fmt, const mpreal& value) { // cf std::ostream& mpreal::output(std::ostream& os) const char* s = NULL; if (!(mpfr_asprintf(&s, fmt.c_str(), mpreal::get_default_rnd(), value.mpfr_srcptr()) < 0)) { @@ -195,7 +195,7 @@ ostream& _out_number(ostream& out, int base, const mpreal& value) { return out; } -ostream& mpreal_outputNbase(ostream& out, int base, const mpreal& value) { +ostream& MprealOutputNBase(ostream& out, int base, const mpreal& value) { // see mpfr_vasprintf code int digits = 0; // forced 0 digits after separator diff --git a/src/mpreal-out.h b/src/mpreal-out.h new file mode 100644 index 0000000..a830da6 --- /dev/null +++ b/src/mpreal-out.h @@ -0,0 +1,16 @@ +// Copyright (c) 2014-2022 Louis Rubet + +#ifndef SRC_MPREAL_OUT_HPP_ +#define SRC_MPREAL_OUT_HPP_ + +#include +using mpfr::mpreal; + +#include +#include +using std::string, std::ostream; + +ostream& MprealOutput10Base(ostream& out, const string& fmt, const mpreal& value); +ostream& MprealOutputNBase(ostream& out, int base, const mpreal& value); + +#endif // SRC_MPREAL_OUT_HPP_ diff --git a/src/mpreal-out.hpp b/src/mpreal-out.hpp deleted file mode 100644 index cac8f10..0000000 --- a/src/mpreal-out.hpp +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright (c) 2014-2022 Louis Rubet - -#ifndef SRC_MPREAL_OUT_HPP_ -#define SRC_MPREAL_OUT_HPP_ - -#include -using namespace mpfr; - -#include -#include -using namespace std; - -ostream& mpreal_output10base(ostream& out, const string& fmt, const mpreal& value); -ostream& mpreal_outputNbase(ostream& out, int base, const mpreal& value); - -#endif // SRC_MPREAL_OUT_HPP_ diff --git a/src/object.cc b/src/object.cc new file mode 100644 index 0000000..36deefd --- /dev/null +++ b/src/object.cc @@ -0,0 +1,7 @@ +// Copyright (c) 2014-2022 Louis Rubet + +#include "object.h" + +// number statics +Number::mode_enum Number::mode = Number::kDefaultMode; +int Number::digits = kDefaultDecimalDigits; diff --git a/src/object.cpp b/src/object.cpp deleted file mode 100644 index 43b8d49..0000000 --- a/src/object.cpp +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright (c) 2014-2022 Louis Rubet - -#include "object.hpp" - -// number statics -number::mode_enum number::s_mode = number::DEFAULT_MODE; -int number::s_digits = DEFAULT_DECIMAL_DIGITS; diff --git a/src/object.h b/src/object.h new file mode 100644 index 0000000..bf56f39 --- /dev/null +++ b/src/object.h @@ -0,0 +1,215 @@ +// Copyright (c) 2014-2022 Louis Rubet + +#ifndef SRC_OBJECT_H_ +#define SRC_OBJECT_H_ + +#include +using mpfr::mpreal; + +#include +#include +#include +#include +using std::complex, std::ostream, std::string, std::stringstream; + +#include "mpreal-out.h" + +// definitions for objects +/// +typedef enum { + kOk, + kUnknownError, + kMissingOperand, + kBadOperandType, + kOutOfRange, + kUnknownVariable, + kInternalError, + kDeadlyError, + kGoodbye, + kNotImplemented, + kNop, + kSyntaxError, + kDivByZero, + kRuntimeError, + kAbortCurrentEntry, + kOutOfMemory, + kBadValue, + kTestFailed +} RetValue; + +typedef enum { + kUndef, + kNumber, // 3.1416, 1e-1234, 0x12ab, 2b110, 50ba12 + kComplex, // (1,2) + kString, // "string" + kSymbol, // 'symbol' + kProgram, // << instructions >> «instructions» + kKeyword, // langage (reserved) keyword (rot, dup, swap ..) + kBranch // langage (reserved) branch keyword (for, if, then ..) +} ObjectType; + +class program; +class Branch; + +typedef void (program::*program_fn_t)(void); +typedef size_t (program::*branch_fn_t)(Branch&); + +/// @brief Object - a generic stack object +/// +struct Object { + explicit Object(ObjectType type = kUndef) : _type(type) {} + virtual ~Object() {} + ObjectType _type; + virtual Object* Clone() { + Object* o = new Object(); + if (o != nullptr) *o = *this; + return o; + } + + virtual string Name() { return string("Object"); } + virtual ostream& Show(ostream& out) { + out << "(" << Name() << " - unknown representation)"; + return out; + } + friend ostream& operator<<(ostream& os, Object* o) { return o->Show(os); } +}; + +/// @brief stack objects derived from Object +/// +struct Number : Object { + Number() : Object(kNumber), base(10) {} + explicit Number(const mpreal& value__, int base__ = 10) : Object(kNumber), base(base__), value(value__) {} + explicit Number(int value__, int base__ = 10) : Object(kNumber), base(base__), value(value__) {} + + int base; + mpreal value; + + virtual Object* Clone() { return new Number(value, base); } + virtual string Name() { return string("number"); } + virtual ostream& Show(ostream& out) { return ShowValue(out, value, mode, digits, base); } + + // representation mode + typedef enum { kStd, kFix, kSci } mode_enum; + static mode_enum mode; + static constexpr mode_enum kDefaultMode = Number::kStd; + + // precision + static constexpr mpfr_prec_t kMpfrDefaultPrecBits = 128; + static constexpr int kDefaultDecimalDigits = 38; + static int digits; + + // clang-format off + static string MakeNumberFormat(mode_enum mode, int digits) { + stringstream format; + format << "%." << digits; + switch ( mode ) { + case kStd: format << "R*g"; break; + case kFix: format << "R*f"; break; + case kSci: format << "R*e"; break; + } + return format.str(); + } + // clang-format on + + static ostream& ShowValue(ostream& out, const mpreal& value, mode_enum mode, int digits, int base) { + if (base == 10) + return MprealOutput10Base(out, MakeNumberFormat(mode, digits), value); + else + return MprealOutputNBase(out, base, value); + } +}; + +/// @brief stack objects inheriting Object +/// +struct Complex : Object { + Complex() : Object(kComplex), re_base(10), im_base(10) {} + explicit Complex(complex& value__, int re_base__ = 10, int im_base__ = 10) + : Object(kComplex), re_base(re_base__), im_base(im_base__) { + value = value__; + } + explicit Complex(mpreal& re__, mpreal& im__, int re_base__ = 10, int im_base__ = 10) + : Object(kComplex), re_base(re_base__), im_base(im_base__) { + value.real(re__); + value.imag(im__); + } + + int re_base, im_base; + complex value; + + virtual Object* Clone() { return new Complex(value, re_base, im_base); } + virtual string Name() { return string("complex"); } + virtual ostream& Show(ostream& out) { + out << '('; + Number::ShowValue(out, value.real(), Number::mode, Number::digits, re_base); + out << ','; + Number::ShowValue(out, value.imag(), Number::mode, Number::digits, im_base); + return out << ')'; + } +}; + +struct String : Object { + String() : Object(kString) {} + explicit String(const string& value__) : Object(kString), value(value__) {} + virtual Object* Clone() { return new String(value); } + virtual string Name() { return string("string"); } + virtual ostream& Show(ostream& out) { return out << "\"" << value << "\""; } + string value; +}; + +struct Program : Object { + Program() : Object(kProgram) {} + explicit Program(const string& value__) : Object(kProgram), value(value__) {} + virtual Object* Clone() { return new Program(value); } + virtual string Name() { return string("program"); } + virtual ostream& Show(ostream& out) { return out << "«" << value << "»"; } + string value; +}; + +struct Symbol : Object { + explicit Symbol(bool auto_eval__ = true) : Object(kSymbol), auto_eval(auto_eval__) {} + explicit Symbol(const string& value__, bool auto_eval__ = true) + : Object(kSymbol), value(value__), auto_eval(auto_eval__) {} + virtual Object* Clone() { return new Symbol(value, auto_eval); } + virtual string Name() { return string("symbol"); } + virtual ostream& Show(ostream& out) { return out << "'" << value << "'"; } + bool auto_eval; + string value; +}; + +struct Keyword : Object { + Keyword() : Object(kKeyword) {} + explicit Keyword(program_fn_t fn__, const string& value__) : Object(kKeyword), fn(fn__), value(value__) {} + virtual Object* Clone() { return new Keyword(fn, value); } + virtual string Name() { return string("keyword"); } + program_fn_t fn; + string value; +}; + +struct Branch : Object { + Branch() : Object(kBranch) {} + explicit Branch(branch_fn_t fn__, const string& value__) : Object(kBranch) { + fn = fn__; + arg1 = static_cast(-1); + arg2 = static_cast(-1); + arg3 = static_cast(-1); + arg_bool = 0; + value = value__; + } + explicit Branch(Branch& other) : Object(kBranch) { + fn = other.fn; + arg1 = other.arg1; + arg2 = other.arg2; + arg3 = other.arg3; + arg_bool = other.arg_bool; + value = other.value; + } + virtual Object* Clone() { return new Branch(*this); } + virtual string Name() { return string("branch"); } + branch_fn_t fn; + size_t arg1, arg2, arg3; + mpreal first_index, last_index; + bool arg_bool; + string value; +}; + +#endif // SRC_OBJECT_H_ diff --git a/src/object.hpp b/src/object.hpp deleted file mode 100644 index 0c52665..0000000 --- a/src/object.hpp +++ /dev/null @@ -1,227 +0,0 @@ -// Copyright (c) 2014-2022 Louis Rubet - -#ifndef SRC_OBJECT_HPP_ -#define SRC_OBJECT_HPP_ - -#include - -#include "mpreal-out.hpp" -using namespace mpfr; - -#include -#include -#include -using namespace std; - -// definitions for objects -/// -typedef enum { - ret_ok, - ret_unknown_err, - ret_missing_operand, - ret_bad_operand_type, - ret_out_of_range, - ret_unknown_variable, - ret_internal, - ret_deadly, - ret_good_bye, - ret_not_impl, - ret_nop, - ret_syntax, - ret_div_by_zero, - ret_runtime_error, - ret_abort_current_entry, - ret_out_of_memory, - ret_bad_value, - ret_test_failed -} ret_value; - -typedef enum { - cmd_undef, - cmd_number, // floating point number - cmd_complex, // complex, couple of floating point numbers - cmd_string, // string like "string" - cmd_symbol, // symbol like 'symbol' - cmd_program, // program like << instructions >> - cmd_keyword, // langage keyword - cmd_branch, // langage branch keyword - cmd_max -} cmd_type_t; - -class program; -class branch; - -typedef void (program::*program_fn_t)(void); -typedef size_t (program::*branch_fn_t)(branch&); - -/// @brief object - a generic stack object -/// -struct object { - explicit object(cmd_type_t type = cmd_undef) : _type(type) {} - virtual ~object() {} - cmd_type_t _type; - virtual object* clone() { - object* o = new object(); - if (o != nullptr) *o = *this; - return o; - } - - virtual string name() { return string("object"); } - virtual ostream& show(ostream& out) { - out << "(" << name() << " - unknown representation)"; - return out; - } - friend ostream& operator<<(ostream& os, object* o) { return o->show(os); } - - unsigned int size() { return sizeof(*this); } -}; - -/// @brief stack objects derived from object -/// -struct number : object { - number() : object(cmd_number), base(10) {} - explicit number(const mpreal& value_, int base_ = 10) : object(cmd_number), base(base_), value(value_) {} - explicit number(int value_, int base_ = 10) : object(cmd_number), base(base_), value(value_) {} - - int base; - mpreal value; - - virtual object* clone() { return new number(value, base); } - virtual string name() { return string("number"); } - virtual ostream& show(ostream& out) { return showValue(out, value, s_mode, s_digits, base); } - - // representation mode - typedef enum { std, fix, sci } mode_enum; - static mode_enum s_mode; - static constexpr mode_enum DEFAULT_MODE = number::std; - - // precision - static constexpr mpfr_prec_t MPFR_DEFAULT_PREC_BITS = 128; - static constexpr int DEFAULT_DECIMAL_DIGITS = 38; - static int s_digits; - - // clang-format off - static string _makeNumberFormat(mode_enum mode, int digits) { - stringstream format; - format << "%." << digits; - switch ( mode ) { - case std: format << "R*g"; break; - case fix: format << "R*f"; break; - case sci: format << "R*e"; break; - } - return format.str(); - } - // clang-format on - - static ostream& showValue(ostream& out, const mpreal& value, mode_enum mode, int digits, int base) { - if (base == 10) - return mpreal_output10base(out, _makeNumberFormat(s_mode, s_digits), value); - else - return mpreal_outputNbase(out, base, value); - } -}; - -/// @brief stack objects derived from object -/// -struct ocomplex : object { - ocomplex() : object(cmd_complex), reBase(10), imBase(10) {} - explicit ocomplex(complex& value_, int reb = 10, int imb = 10) - : object(cmd_complex), reBase(reb), imBase(imb) { - value = value_; - } - explicit ocomplex(mpreal& re_, mpreal& im_, int reb = 10, int imb = 10) - : object(cmd_complex), reBase(reb), imBase(imb) { - value.real(re_); - value.imag(im_); - } - - int reBase, imBase; - complex value; - - virtual object* clone() { return new ocomplex(value, reBase, imBase); } - virtual string name() { return string("complex"); } - virtual ostream& show(ostream& out) { - out << '('; - number::showValue(out, value.real(), number::s_mode, number::s_digits, reBase); - out << ','; - number::showValue(out, value.imag(), number::s_mode, number::s_digits, imBase); - return out << ')'; - } -}; - -/// @brief object string -/// -struct ostring : object { - ostring() : object(cmd_string) {} - explicit ostring(const string& value_) : object(cmd_string), value(value_) {} - virtual object* clone() { return new ostring(value); } - virtual string name() { return string("string"); } - virtual ostream& show(ostream& out) { return out << "\"" << value << "\""; } - string value; -}; - -/// @brief object program -/// -struct oprogram : object { - oprogram() : object(cmd_program) {} - explicit oprogram(const string& value_) : object(cmd_program), value(value_) {} - virtual object* clone() { return new oprogram(value); } - virtual string name() { return string("program"); } - virtual ostream& show(ostream& out) { return out << "«" << value << "»"; } - string value; -}; - -/// @brief object symbol -/// -struct symbol : object { - explicit symbol(bool autoEval_ = true) : object(cmd_symbol), autoEval(autoEval_) {} - explicit symbol(const string& value_, bool autoEval_ = true) - : object(cmd_symbol), value(value_), autoEval(autoEval_) {} - virtual object* clone() { return new symbol(value, autoEval); } - virtual string name() { return string("symbol"); } - virtual ostream& show(ostream& out) { return out << "'" << value << "'"; } - bool autoEval; - string value; -}; - -/// @brief object keyword -/// -struct keyword : object { - keyword() : object(cmd_keyword) {} - explicit keyword(program_fn_t fn_, const string& value_) : object(cmd_keyword), fn(fn_), value(value_) {} - virtual object* clone() { return new keyword(fn, value); } - virtual string name() { return string("keyword"); } - program_fn_t fn; - string value; -}; - -/// @brief object branch -/// -struct branch : object { - branch() : object(cmd_branch) {} - explicit branch(branch_fn_t fn_, const string& value_) : object(cmd_branch) { - fn = fn_; - arg1 = static_cast(-1); - arg2 = static_cast(-1); - arg3 = static_cast(-1); - arg_bool = 0; - value = value_; - } - explicit branch(branch& other) : object(cmd_branch) { - fn = other.fn; - arg1 = other.arg1; - arg2 = other.arg2; - arg3 = other.arg3; - arg_bool = other.arg_bool; - value = other.value; - } - virtual object* clone() { return new branch(*this); } - virtual string name() { return string("branch"); } - branch_fn_t fn; - size_t arg1, arg2, arg3; - mpreal firstIndex, lastIndex; - bool arg_bool; - string value; -}; - -#endif // SRC_OBJECT_HPP_ diff --git a/src/program.cc b/src/program.cc new file mode 100644 index 0000000..12e5ef4 --- /dev/null +++ b/src/program.cc @@ -0,0 +1,718 @@ +// Copyright (c) 2014-2022 Louis Rubet + +#include "program.h" + +//< language reserved keywords (allowed types are kKeyword, kBranch or kUndef) +vector program::keywords_{ + // GENERAL + {kUndef, "", nullptr, "\nGENERAL"}, + {kKeyword, "nop", &program::RpnNop, "no operation"}, + {kKeyword, "help", &program::RpnHelp, "this help message"}, + {kKeyword, "h", &program::RpnHelp, ""}, + {kKeyword, "?", &program::RpnHelp, ""}, + {kKeyword, "quit", &program::RpnQuit, "quit software"}, + {kKeyword, "q", &program::RpnQuit, ""}, + {kKeyword, "exit", &program::RpnQuit, ""}, + {kKeyword, "test", &program::RpnTest, ""}, // not seen by user + {kKeyword, "version", &program::RpnVersion, "show rpn version"}, + {kKeyword, "uname", &program::RpnUname, "show rpn complete identification string"}, + {kKeyword, "history", &program::RpnHistory, "see commands history"}, + + // USUAL OPERATIONS ON REALS AND COMPLEXES + {kUndef, "", nullptr, "\nUSUAL OPERATIONS ON REALS AND COMPLEXES"}, + {kKeyword, "+", &program::RpnPlus, "addition"}, + {kKeyword, "-", &program::RpnMinus, "substraction"}, + {kKeyword, "*", &program::RpnMul, "multiplication"}, + {kKeyword, "/", &program::RpnDiv, "division"}, + {kKeyword, "inv", &program::RpnInv, "inverse"}, + {kKeyword, "chs", &program::RpnNeg, "negation"}, + {kKeyword, "neg", &program::RpnNeg, ""}, + {kKeyword, "^", &program::RpnPower, "power"}, + {kKeyword, "pow", &program::RpnPower, ""}, + {kKeyword, "sqrt", &program::RpnSquareroot, "RpnSquare root"}, + {kKeyword, "sq", &program::RpnSquare, "RpnSquare"}, + {kKeyword, "abs", &program::RpnAbs, "absolute value (norm for a complex)"}, + {kKeyword, "sign", &program::RpnSign, "sign of a number or z/|z| for a complex"}, + + // OPERATIONS ON REALS + {kUndef, "", nullptr, "\nOPERATIONS ON REALS"}, + {kKeyword, "%", &program::RpnPurcent, "purcent"}, + {kKeyword, "%CH", &program::RpnPurcentCH, "inverse purcent"}, + {kKeyword, "mod", &program::RpnModulo, "modulo"}, + {kKeyword, "fact", &program::RpnFact, "n! for integer n or Gamma(x+1) for fractional x"}, + {kKeyword, "mant", &program::RpnMant, "mantissa of a real number"}, + {kKeyword, "xpon", &program::RpnXpon, "exponant of a real number"}, + {kKeyword, "floor", &program::RpnFloor, "largest number <="}, + {kKeyword, "ceil", &program::RpnCeil, "smallest number >="}, + {kKeyword, "ip", &program::RpnIp, "integer part"}, + {kKeyword, "fp", &program::RpnFp, "fractional part"}, + {kKeyword, "min", &program::RpnMin, "min of 2 real numbers"}, + {kKeyword, "max", &program::RpnMax, "max of 2 real numbers"}, + + // OPERATIONS ON COMPLEXES + {kUndef, "", nullptr, "\nOPERATIONS ON COMPLEXES"}, + {kKeyword, "re", &program::RpnReal, "complex real part"}, + {kKeyword, "im", &program::RpnImag, "complex imaginary part"}, + {kKeyword, "conj", &program::RpnConj, "complex conjugate"}, + {kKeyword, "arg", &program::RpnArg, "complex argument in radians"}, + {kKeyword, "c->r", &program::RpnC2r, "transform a complex in 2 reals"}, + {kKeyword, "r->c", &program::RpnR2c, "transform 2 reals in a complex"}, + {kKeyword, "p->r", &program::RpnP2r, "cartesian to polar"}, + {kKeyword, "r->p", &program::RpnR2p, "polar to cartesian"}, + + // MODE + {kUndef, "", nullptr, "\nMODE"}, + {kKeyword, "std", &program::RpnStd, "standard floating numbers representation. ex: std"}, + {kKeyword, "fix", &program::RpnFix, "fixed point representation. ex: 6 fix"}, + {kKeyword, "sci", &program::RpnSci, "scientific floating point representation. ex: 20 sci"}, + {kKeyword, "prec", &program::RpnPrecision, "set float precision in bits. ex: 256 prec"}, + {kKeyword, "round", &program::RpnRound, + "set float rounding mode.\n\tex: [\"nearest (even)\", \"toward zero\", \"toward " + "+inf\", \"toward -inf\", \"away from zero\", \"faithful rounding\", \"nearest (away from zero)\"] round"}, + + {kKeyword, "default", &program::RpnDefault, "set float representation and precision to default"}, + {kKeyword, "type", &program::RpnType, "show type of stack first entry"}, + {kKeyword, "hex", &program::RpnHex, "hexadecimal representation, applies on stack level 0 only"}, + {kKeyword, "dec", &program::RpnDec, "decimal representation, applies on stack level 0 only"}, + {kKeyword, "bin", &program::RpnBin, "binary representation, applies on stack level 0 only"}, + {kKeyword, "base", &program::RpnBase, "arbitrary base representation, applies on stack level 0 only"}, + + // TESTS + {kUndef, "", nullptr, "\nTEST"}, + {kKeyword, ">", &program::RpnSup, "binary operator >"}, + {kKeyword, ">=", &program::RpnSupEq, "binary operator >="}, + {kKeyword, "<", &program::RpnInf, "binary operator <"}, + {kKeyword, "<=", &program::RpnInfEq, "binary operator <="}, + {kKeyword, "!=", &program::RpnDiff, "binary operator != (different)"}, + {kKeyword, "==", &program::RpnEq, "binary operator == (equal)"}, + {kKeyword, "and", &program::RpnTestAnd, "boolean operator and"}, + {kKeyword, "or", &program::RpnTestOr, "boolean operator or"}, + {kKeyword, "xor", &program::RpnTestXor, "boolean operator xor"}, + {kKeyword, "not", &program::RpnTestNot, "boolean operator not"}, + {kKeyword, "same", &program::RpnSame, "boolean operator same (equal)"}, + + // STACK + {kUndef, "", nullptr, "\nSTACK"}, + {kKeyword, "swap", &program::RpnSwap, "swap 2 first stack entries"}, + {kKeyword, "drop", &program::RpnDrop, "drop first stack entry"}, + {kKeyword, "drop2", &program::RpnDrop2, "drop 2 first stack entries"}, + {kKeyword, "dropn", &program::RpnDropn, "drop n first stack entries"}, + {kKeyword, "del", &program::RpnErase, "drop all stack entries"}, + {kKeyword, "erase", &program::RpnErase, ""}, + {kKeyword, "rot", &program::RpnRot, "rotate 3 first stack entries"}, + {kKeyword, "dup", &program::RpnDup, "duplicate first stack entry"}, + {kKeyword, "dup2", &program::RpnDup2, "duplicate 2 first stack entries"}, + {kKeyword, "dupn", &program::RpnDupn, "duplicate n first stack entries"}, + {kKeyword, "pick", &program::RpnPick, "push a copy of the given stack level onto the stack"}, + {kKeyword, "depth", &program::RpnDepth, "give stack depth"}, + {kKeyword, "roll", &program::RpnRoll, "move a stack entry to the top of the stack"}, + {kKeyword, "rolld", &program::RpnRolld, "move the element on top of the stack to a higher stack position"}, + {kKeyword, "over", &program::RpnOver, "push a copy of the element in stack level 2 onto the stack"}, + + // STRING + {kUndef, "", nullptr, "\nSTRING"}, + {kKeyword, "->str", &program::RpnInstr, "convert an object into a string"}, + {kKeyword, "str->", &program::RpnStrout, "convert a string into an object"}, + {kKeyword, "chr", &program::RpnChr, "convert ASCII character code in stack level 1 into a string"}, + {kKeyword, "num", &program::RpnNum, + "return ASCII code of the first character of the string in stack level 1 as a real number"}, + {kKeyword, "size", &program::RpnStrsize, "return the length of the string"}, + {kKeyword, "pos", &program::RpnStrpos, "seach for the string in level 1 within the string in level 2"}, + {kKeyword, "sub", &program::RpnStrsub, "return a substring of the string in level 3"}, + + // BRANCH + {kUndef, "", nullptr, "\nBRANCH"}, + {kBranch, "if", (program_fn_t)&program::RpnIf, + "if then else " + "end"}, + {kBranch, "then", (program_fn_t)&program::RpnThen, "used with if"}, + {kBranch, "else", (program_fn_t)&program::RpnElse, "used with if"}, + {kBranch, "end", (program_fn_t)&program::RpnEnd, "used with various branch instructions"}, + {kBranch, "start", (program_fn_t)&program::RpnStart, " start next| step"}, + {kBranch, "for", (program_fn_t)&program::RpnFor, + " for next| step"}, + {kBranch, "next", (program_fn_t)&program::RpnNext, "used with start and for"}, + {kBranch, "step", (program_fn_t)&program::RpnStep, "used with start and for"}, + {kKeyword, "ift", &program::RpnIft, "similar to if-then-end, ift"}, + {kKeyword, "ifte", &program::RpnIfte, + "similar to if-then-else-end, " + " ifte"}, + {kBranch, "do", (program_fn_t)&program::RpnDo, "do until end"}, + {kBranch, "until", (program_fn_t)&program::RpnUntil, "used with do"}, + {kBranch, "while", (program_fn_t)&program::RpnWhile, "while repeat end"}, + {kBranch, "repeat", (program_fn_t)&program::RpnRepeat, "used with while"}, + + // STORE + {kUndef, "", nullptr, "\nSTORE"}, + {kKeyword, "sto", &program::RpnSto, "store a variable. ex: 1 'name' sto"}, + {kKeyword, "rcl", &program::RpnRcl, "recall a variable. ex: 'name' rcl"}, + {kKeyword, "purge", &program::RpnPurge, "delete a variable. ex: 'name' purge"}, + {kKeyword, "vars", &program::RpnVars, "list all variables"}, + {kKeyword, "clusr", &program::RpnClusr, "erase all variables"}, + {kKeyword, "edit", &program::RpnEdit, "edit a variable content"}, + {kKeyword, "sto+", &program::RpnStoadd, "add to a stored variable. ex: 1 'name' sto+ 'name' 2 sto+"}, + {kKeyword, "sto-", &program::RpnStosub, "substract to a stored variable. ex: 1 'name' sto- 'name' 2 sto-"}, + {kKeyword, "sto*", &program::RpnStomul, "multiply a stored variable. ex: 3 'name' sto* 'name' 2 sto*"}, + {kKeyword, "sto/", &program::RpnStodiv, "divide a stored variable. ex: 3 'name' sto/ 'name' 2 sto/"}, + {kKeyword, "sneg", &program::RpnStoneg, "negate a variable. ex: 'name' sneg"}, + {kKeyword, "sinv", &program::RpnStoinv, "inverse a variable. ex: 1 'name' sinv"}, + // PROGRAM + {kUndef, "", nullptr, "\nPROGRAM"}, + {kKeyword, "eval", &program::RpnEval, "evaluate (run) a program, or recall a variable. ex: 'my_prog' eval"}, + {kBranch, "->", (program_fn_t)&program::RpnInprog, + "load program local variables. ex: << -> n m << 0 n m for i i + next >> " + ">>"}, + + // TRIG ON REALS AND COMPLEXES + {kUndef, "", nullptr, "\nTRIG ON REALS AND COMPLEXES"}, + {kKeyword, "pi", &program::RpnPi, "pi constant"}, + {kKeyword, "sin", &program::RpnSin, "sinus"}, + {kKeyword, "asin", &program::RpnAsin, "arg sinus"}, + {kKeyword, "cos", &program::RpnCos, "cosinus"}, + {kKeyword, "acos", &program::RpnAcos, "arg cosinus"}, + {kKeyword, "tan", &program::RpnTan, "tangent"}, + {kKeyword, "atan", &program::RpnAtan, "arg tangent"}, + {kKeyword, "d->r", &program::RpnD2r, "convert degrees to radians"}, + {kKeyword, "r->d", &program::RpnR2d, "convert radians to degrees"}, + + // LOGS ON REALS AND COMPLEXES + {kUndef, "", nullptr, "\nLOGS ON REALS AND COMPLEXES"}, + {kKeyword, "e", &program::RpnE, "Euler constant"}, + {kKeyword, "ln", &program::RpnLn, "logarithm base e"}, + {kKeyword, "log", &program::RpnLn, ""}, + {kKeyword, "lnp1", &program::RpnLnp1, "ln(1+x) which is useful when x is close to 0"}, + {kKeyword, "exp", &program::RpnExp, "exponential"}, + {kKeyword, "expm", &program::RpnExpm, "exp(x)-1 which is useful when x is close to 0"}, + {kKeyword, "log10", &program::RpnLog10, "logarithm base 10"}, + {kKeyword, "alog10", &program::RpnAlog10, "exponential base 10"}, + {kKeyword, "exp10", &program::RpnAlog10, ""}, + {kKeyword, "log2", &program::RpnLog2, "logarithm base 2"}, + {kKeyword, "alog2", &program::RpnAlog2, "exponential base 2"}, + {kKeyword, "exp2", &program::RpnAlog2, ""}, + {kKeyword, "sinh", &program::RpnSinh, "hyperbolic sine"}, + {kKeyword, "asinh", &program::RpnAsinh, "inverse hyperbolic sine"}, + {kKeyword, "cosh", &program::RpnCosh, "hyperbolic cosine"}, + {kKeyword, "acosh", &program::RpnAcosh, "inverse hyperbolic cosine"}, + {kKeyword, "tanh", &program::RpnTanh, "hyperbolic tangent"}, + {kKeyword, "atanh", &program::RpnAtanh, "inverse hyperbolic tangent"}, + + // TIME AND DATE + {kUndef, "", nullptr, "\nTIME AND DATE"}, + {kKeyword, "time", &program::RpnTime, "time in local format"}, + {kKeyword, "date", &program::RpnDate, "date in local format"}, + {kKeyword, "ticks", &program::RpnTicks, "system tick in µs"}, +}; + +/// autocompletion vector for linenoise autocompletion +vector& program::GetAutocompletionWords() { + static vector autocompletion_words; + if (autocompletion_words.empty()) + for (auto& kw : keywords_) + if (!kw.name.empty()) autocompletion_words.push_back(kw.name); + return autocompletion_words; +} + +/// @brief run a program on a stack and a heap +/// +/// @return RetValue see this type +/// +RetValue program::Run() { + bool go_out = false; + RetValue ret = kOk; + ObjectType type; + + err_ = kOk; + err_context_ = ""; + + // branches for 'if' + ret = Preprocess(); + if (ret != kOk) { + // free allocated + for (Object* o : *this) delete o; + local_heap_.clear(); + return ret; + } + + // iterate commands + for (size_t i = 0; (go_out == false) && (i < size());) { + Object* o = at(i); + switch (o->_type) { + // could be an auto-evaluated symbol + case kSymbol: + AutoRcl(reinterpret_cast(o)); + i++; + break; + + // a keyword + case kKeyword: { + Keyword* k = reinterpret_cast(o); + // call the matching function + (this->*(k->fn))(); + switch (err_) { + // no pb -> go on + case kOk: + break; + // explicit go out software + case kGoodbye: + go_out = true; + ret = kGoodbye; + break; + default: + // error: abort prog + go_out = true; + + // test error: make rpn return EXIT_FAILURE + if (err_ == kTestFailed) ret = kTestFailed; + + // error: show it + if (ShowError(err_, err_context_) == kDeadlyError) + // pb showing error -> go out software + ret = kGoodbye; + break; + } + i++; + break; + } + + // a branch keyword + case kBranch: { + // call matching function + Branch* b = reinterpret_cast(o); + size_t next_cmd = (this->*(b->fn))(*b); + switch (next_cmd) { + case kStepOut: // step out + i++; // meaning 'next command' + break; + case kRtError: // runtime error + (void)ShowError(err_, err_context_); + go_out = true; + break; + default: + i = next_cmd; + break; + } + break; + } + + default: + // not a command, but a stack entry, manage it + // copy the program stack entry to the running stack + stack_.push_front(o->Clone()); + i++; + break; + } + } + + // free allocated + for (Object* o : *this) delete o; + local_heap_.clear(); + + return ret; +} + +/// @brief prepare a program for execution +/// this is needed before a program can be run +/// inner members of branch or keyword objects are filled by this function +/// these inner members store for example the index of the next keyword to execute etc. +/// +/// @return RetValue see this type +/// +RetValue program::Preprocess() { + struct if_layout_t { + if_layout_t() + : index_then_or_unti_or_repeat(-1), + index_else(-1), + index_end(-1), + is_do_unti(false), + is_while_repeat(false) {} + int index_if_or_do_or_while; + int index_then_or_unti_or_repeat; + int index_else; + int index_end; + bool is_do_unti; + bool is_while_repeat; + }; + // for if-then-else-end + vector vlayout; + int layout_index = -1; + // for start-end-step + vector vstart_index; + + // analyse if-then-else-end branches + // analyse start-{next, step} branches + for (size_t i = 0; i < size(); i++) { + if (at(i)->_type == kBranch) { + Branch* k = reinterpret_cast(at(i)); + if (k->value == "if") { + if_layout_t layout; + layout.index_if_or_do_or_while = i; + vlayout.push_back(layout); + layout_index++; + } else if (k->value == "then") { + size_t next = i + 1; + if (next >= size()) next = kStepOut; + + // nothing after 'then' -> error + if (next == kStepOut) { + // error: show it + ShowSyntaxError("missing end after then"); + return kSyntaxError; + } + if (layout_index < 0) { + // error: show it + ShowSyntaxError("missing if before then"); + return kSyntaxError; + } + if (vlayout[layout_index].index_then_or_unti_or_repeat != -1) { + // error: show it + ShowSyntaxError("duplicate then"); + return kSyntaxError; + } + vlayout[layout_index].index_then_or_unti_or_repeat = i; + k->arg1 = next; + k->arg3 = vlayout[layout_index].index_if_or_do_or_while; + } else if (k->value == "else") { + size_t next = i + 1; + if (next >= size()) next = kStepOut; + + // nothing after 'else' -> error + if (next == kStepOut) { + // error: show it + ShowSyntaxError("missing end after else"); + return kSyntaxError; + } + if (layout_index < 0) { + // error: show it + ShowSyntaxError("missing if before else"); + return kSyntaxError; + } + if (vlayout[layout_index].index_then_or_unti_or_repeat == -1) { + // error: show it + ShowSyntaxError("missing then before else"); + return kSyntaxError; + } + if (vlayout[layout_index].index_else != -1) { + // error: show it + ShowSyntaxError("duplicate else"); + return kSyntaxError; + } + vlayout[layout_index].index_else = i; + k->arg1 = next; // fill branch1 (if was false) of 'else' + k->arg3 = vlayout[layout_index].index_if_or_do_or_while; + reinterpret_cast(at(vlayout[layout_index].index_then_or_unti_or_repeat))->arg2 = + next; // fill branch2 (if was false) of 'then' + } else if (k->value == "start") { + vstart_index.push_back(i); + } else if (k->value == "for") { + vstart_index.push_back(i); + k->arg1 = i + 1; // arg1 points on symbol variable + } else if (k->value == "next") { + if (vstart_index.size() == 0) { + // error: show it + ShowSyntaxError("missing start or for before next"); + return kSyntaxError; + } + k->arg1 = vstart_index[vstart_index.size() - 1]; // 'next' arg1 = 'start' index + reinterpret_cast(at(vstart_index[vstart_index.size() - 1]))->arg2 = + i; // 'for' or 'start' arg2 = 'next' index + vstart_index.pop_back(); + } else if (k->value == "step") { + if (vstart_index.size() == 0) { + // error: show it + ShowSyntaxError("missing start or for before step"); + return kSyntaxError; + } + k->arg1 = vstart_index[vstart_index.size() - 1]; // fill 'step' branch1 = 'start' index + reinterpret_cast(at(vstart_index[vstart_index.size() - 1]))->arg2 = + i; // 'for' or 'start' arg2 = 'next' index + vstart_index.pop_back(); + } else if (k->value == "->") { + k->arg1 = i; // arg1 is '->' command index in program + } else if (k->value == "do") { + if_layout_t layout; + layout.index_if_or_do_or_while = i; + layout.is_do_unti = true; + vlayout.push_back(layout); + layout_index++; + } else if (k->value == "until") { + size_t next = i + 1; + if (next >= size()) next = kStepOut; + + // nothing after 'unti' -> error + if (next == kStepOut) { + // error: show it + ShowSyntaxError("missing end"); + return kSyntaxError; + } + if (layout_index < 0 || !vlayout[layout_index].is_do_unti) { + // error: show it + ShowSyntaxError("missing do"); + return kSyntaxError; + } + if (vlayout[layout_index].index_then_or_unti_or_repeat != -1) { + // error: show it + ShowSyntaxError("duplicate until"); + return kSyntaxError; + } + vlayout[layout_index].index_then_or_unti_or_repeat = i; + } else if (k->value == "while") { + if_layout_t layout; + layout.index_if_or_do_or_while = i; + layout.is_while_repeat = true; + vlayout.push_back(layout); + layout_index++; + } else if (k->value == "repeat") { + if (layout_index < 0 || !vlayout[layout_index].is_while_repeat) { + // error: show it + ShowSyntaxError("missing while"); + return kSyntaxError; + } + if (vlayout[layout_index].index_then_or_unti_or_repeat != -1) { + // error: show it + ShowSyntaxError("duplicate repeat"); + return kSyntaxError; + } + vlayout[layout_index].index_then_or_unti_or_repeat = i; + } else if (k->value == "end") { + size_t next = i + 1; + if (next >= size()) next = kStepOut; + + if (layout_index < 0) { + // error: show it + ShowSyntaxError("missing branch instruction before end"); + return kSyntaxError; + } else { + if (vlayout[layout_index].is_do_unti) { + // this end closes a do..unti + if (vlayout[layout_index].index_end != -1) { + // error: show it + ShowSyntaxError("duplicate end"); + return kSyntaxError; + } + + if (vlayout[layout_index].index_then_or_unti_or_repeat == -1) { + // error: show it + ShowSyntaxError("missing until"); + return kSyntaxError; + } + + k->arg1 = vlayout[layout_index].index_if_or_do_or_while + 1; + layout_index--; + } else if (vlayout[layout_index].is_while_repeat) { + // this end closes a while..repeat + if (vlayout[layout_index].index_end != -1) { + // error: show it + ShowSyntaxError("duplicate end"); + return kSyntaxError; + } + + k->arg2 = vlayout[layout_index].index_if_or_do_or_while + 1; + + if (vlayout[layout_index].index_then_or_unti_or_repeat == -1) { + // error: show it + ShowSyntaxError("missing repeat"); + return kSyntaxError; + } + + // fill 'repeat' arg1 with 'end+1' + reinterpret_cast(at(vlayout[layout_index].index_then_or_unti_or_repeat))->arg1 = i + 1; + layout_index--; + } else { + // this end closes an if..then..(else) + if (vlayout[layout_index].index_end != -1) { + // error: show it + ShowSyntaxError("duplicate end"); + return kSyntaxError; + } + if (vlayout[layout_index].index_else != -1) { + // fill 'end' branch of 'else' + reinterpret_cast(at(vlayout[layout_index].index_else))->arg2 = i; + } else { + // fill 'end' branch of 'then' + if (vlayout[layout_index].index_then_or_unti_or_repeat != -1) { + reinterpret_cast(at(vlayout[layout_index].index_then_or_unti_or_repeat)) + ->arg2 = i; + } else { + // error: show it + ShowSyntaxError("missing then"); + return kSyntaxError; + } + } + layout_index--; + } + } + } + } + } + if (layout_index >= 0) { + // error: show it + ShowSyntaxError("missing end"); + return kSyntaxError; + } + if (vstart_index.size() > 0) { + // error: show it + ShowSyntaxError("missing next or step after for or start"); + return kSyntaxError; + } + return kOk; +} + +/// @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 RetValue see this type +/// +RetValue program::Parse(string& entry) { + static map keywords_map; + vector elements; + vector errors; + RetValue ret = kOk; + + // prepare map for finding reserved keywords + if (keywords_map.empty()) + for (auto& kw : keywords_) + if (!kw.name.empty()) keywords_map[kw.name] = {kw.type, kw.fn}; + + // separate the entry string + if (Analyse(entry, keywords_map, elements, errors)) { + // make objects from parsed elements + for (Lexer::SynElement& element : elements) { + switch (element.type) { + case kNumber: + push_back(new Number(*element.re, element.re_base)); + break; + case kComplex: + push_back(new Complex(*element.re, *element.im, element.re_base, element.im_base)); + break; + case kString: + push_back(new String(element.value)); + break; + case kSymbol: + push_back(new Symbol(element.value, element.auto_eval)); + break; + case kProgram: + push_back(new Program(element.value)); + break; + case kKeyword: + push_back(new Keyword(element.fn, element.value)); + break; + case kBranch: + push_back(new Branch((branch_fn_t)element.fn, element.value)); + break; + default: + ShowError(kUnknownError, "error creating program from entry"); + break; + } + if (element.re != nullptr) delete element.re; + if (element.im != nullptr) delete element.im; + } + } else { + for (SynError& err : errors) ShowSyntaxError(err.err.c_str()); + } + + return ret; +} + +/// @brief show the last error set +/// +/// @return RetValue see this type +/// +RetValue program::ShowError() { + RetValue ret; + // clang-format off + map errorStrings{{kOk, "ok"}, {kUnknownError, "unknown command"}, + {kMissingOperand, "missing operand"}, {kBadOperandType, "bad operand type"}, + {kOutOfRange, "out of range"}, {kUnknownVariable, "unknown variable"}, + {kInternalError, "internal error, aborting"}, {kDeadlyError, "deadly"}, + {kGoodbye, "goodbye"}, {kNotImplemented, "not implemented"}, + {kNop, "no operation"}, {kSyntaxError, "syntax error"}, + {kDivByZero, "division by zero"}, {kRuntimeError, "runtime error"}, + {kAbortCurrentEntry, "aborted current entry"}, {kOutOfMemory, "out of memory"}, + {kBadValue, "bad value"}, {kTestFailed, "test failed"} + }; + // clang-format on + cerr << err_context_ << ": error " << err_ << ": " << errorStrings[err_] << endl; + switch (err_) { + case kInternalError: + case kDeadlyError: + ret = kDeadlyError; + default: + ret = kOk; + } + + return ret; +} + +/// @brief record an error as the last error set and show it +/// +/// @param err the error to record +/// @param context a context string +/// @return RetValue see this type +/// +RetValue program::ShowError(RetValue err, string& context) { + // record error + err_ = err; + err_context_ = context; + return ShowError(); +} + +/// @brief record an error as the last error set and show it +/// +/// @param err the error to record +/// @param context a context string +/// @return RetValue see this type +/// +RetValue program::ShowError(RetValue err, const char* context) { + // record error + err_ = err; + err_context_ = context; + return ShowError(); +} + +/// @brief set the last error as being a syntax error and show it +/// +/// @param err the error to record +/// @param context a context string +/// @return RetValue see this type +/// +void program::ShowSyntaxError(const char* context) { + // record error + err_ = kSyntaxError; + err_context_ = context; + (void)ShowError(); +} + +/// @brief return the last error set +/// +/// @return RetValue see this type +/// +RetValue program::GetLastError(void) { return err_; } + +/// @brief show a stack (show its different objects) +/// generally a stack is associated to a running program +/// +/// @param show_separator whether to show a stack level prefix or not +/// +void program::ShowStack(bool show_separator) { + if (stack_.size() == 1) { + cout << stack_[0] << endl; + } else { + for (int i = stack_.size() - 1; i >= 0; i--) { + if (show_separator) cout << i + 1 << "> "; + cout << stack_[i] << endl; + } + } +} + +/// @brief apply default precision mode and digits +/// +void program::ApplyDefault() { + // default float precision, float mode + Number::mode = Number::kDefaultMode; + Number::digits = Number::kDefaultDecimalDigits; + mpreal::set_default_prec(Number::kMpfrDefaultPrecBits); + + static mp_rnd_t def_rnd = mpreal::get_default_rnd(); + mpreal::set_default_rnd(def_rnd); +} diff --git a/src/program.cpp b/src/program.cpp deleted file mode 100644 index 918b2ab..0000000 --- a/src/program.cpp +++ /dev/null @@ -1,717 +0,0 @@ -// Copyright (c) 2014-2022 Louis Rubet - -#include "program.hpp" - -//< language reserved keywords (allowed types are cmd_keyword, cmd_branch or cmd_undef) -vector program::_keywords{ - // GENERAL - {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, ""}, - {cmd_keyword, "?", &program::rpn_help, ""}, - {cmd_keyword, "quit", &program::rpn_good_bye, "quit software"}, - {cmd_keyword, "q", &program::rpn_good_bye, ""}, - {cmd_keyword, "exit", &program::rpn_good_bye, ""}, - {cmd_keyword, "test", &program::rpn_test, ""}, // not seen by user - {cmd_keyword, "version", &program::rpn_version, "show rpn version"}, - {cmd_keyword, "uname", &program::rpn_uname, "show rpn complete identification string"}, - {cmd_keyword, "history", &program::rpn_history, "see commands history"}, - - // USUAL 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"}, - {cmd_keyword, "/", &program::rpn_div, "division"}, - {cmd_keyword, "inv", &program::rpn_inv, "inverse"}, - {cmd_keyword, "chs", &program::rpn_neg, "negation"}, - {cmd_keyword, "neg", &program::rpn_neg, ""}, - {cmd_keyword, "^", &program::rpn_power, "power"}, - {cmd_keyword, "pow", &program::rpn_power, ""}, - {cmd_keyword, "sqrt", &program::rpn_squareroot, "rpn_square root"}, - {cmd_keyword, "sq", &program::rpn_square, "rpn_square"}, - {cmd_keyword, "abs", &program::rpn_abs, "absolute value (norm for a complex)"}, - {cmd_keyword, "sign", &program::rpn_sign, "sign of a number or z/|z| for a complex"}, - - // OPERATIONS 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"}, - {cmd_keyword, "fact", &program::rpn_fact, "n! for integer n or Gamma(x+1) for fractional x"}, - {cmd_keyword, "mant", &program::rpn_mant, "mantissa of a real number"}, - {cmd_keyword, "xpon", &program::rpn_xpon, "exponant of a real number"}, - {cmd_keyword, "floor", &program::rpn_floor, "largest number <="}, - {cmd_keyword, "ceil", &program::rpn_ceil, "smallest number >="}, - {cmd_keyword, "ip", &program::rpn_ip, "integer part"}, - {cmd_keyword, "fp", &program::rpn_fp, "fractional part"}, - {cmd_keyword, "min", &program::rpn_min, "min of 2 real numbers"}, - {cmd_keyword, "max", &program::rpn_max, "max of 2 real numbers"}, - - // OPERATIONS 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"}, - {cmd_keyword, "arg", &program::rpn_arg, "complex argument in radians"}, - {cmd_keyword, "c->r", &program::rpn_c2r, "transform a complex in 2 reals"}, - {cmd_keyword, "r->c", &program::rpn_r2c, "transform 2 reals in a complex"}, - {cmd_keyword, "p->r", &program::rpn_p2r, "cartesian to polar"}, - {cmd_keyword, "r->p", &program::rpn_r2p, "polar to cartesian"}, - - // MODE - {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"}, - {cmd_keyword, "prec", &program::rpn_precision, "set float precision in bits. ex: 256 prec"}, - {cmd_keyword, "round", &program::rpn_round, - "set float rounding mode.\n\tex: [\"nearest (even)\", \"toward zero\", \"toward " - "+inf\", \"toward -inf\", \"away from zero\", \"faithful rounding\", \"nearest (away from zero)\"] round"}, - - {cmd_keyword, "default", &program::rpn_default, "set float representation and precision to default"}, - {cmd_keyword, "type", &program::rpn_type, "show type of stack first entry"}, - {cmd_keyword, "hex", &program::rpn_hex, "hexadecimal representation, applies on stack level 0 only"}, - {cmd_keyword, "dec", &program::rpn_dec, "decimal representation, applies on stack level 0 only"}, - {cmd_keyword, "bin", &program::rpn_bin, "binary representation, applies on stack level 0 only"}, - {cmd_keyword, "base", &program::rpn_base, "arbitrary base representation, applies on stack level 0 only"}, - - // TESTS - {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 <"}, - {cmd_keyword, "<=", &program::rpn_inf_eq, "binary operator <="}, - {cmd_keyword, "!=", &program::rpn_diff, "binary operator != (different)"}, - {cmd_keyword, "==", &program::rpn_eq, "binary operator == (equal)"}, - {cmd_keyword, "and", &program::rpn_test_and, "boolean operator and"}, - {cmd_keyword, "or", &program::rpn_test_or, "boolean operator or"}, - {cmd_keyword, "xor", &program::rpn_test_xor, "boolean operator xor"}, - {cmd_keyword, "not", &program::rpn_test_not, "boolean operator not"}, - {cmd_keyword, "same", &program::rpn_same, "boolean operator same (equal)"}, - - // STACK - {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"}, - {cmd_keyword, "dropn", &program::rpn_dropn, "drop n first stack entries"}, - {cmd_keyword, "del", &program::rpn_erase, "drop all stack entries"}, - {cmd_keyword, "erase", &program::rpn_erase, ""}, - {cmd_keyword, "rot", &program::rpn_rot, "rotate 3 first stack entries"}, - {cmd_keyword, "dup", &program::rpn_dup, "duplicate first stack entry"}, - {cmd_keyword, "dup2", &program::rpn_dup2, "duplicate 2 first stack entries"}, - {cmd_keyword, "dupn", &program::rpn_dupn, "duplicate n first stack entries"}, - {cmd_keyword, "pick", &program::rpn_pick, "push a copy of the given stack level onto the stack"}, - {cmd_keyword, "depth", &program::rpn_depth, "give stack depth"}, - {cmd_keyword, "roll", &program::rpn_roll, "move a stack entry to the top of the stack"}, - {cmd_keyword, "rolld", &program::rpn_rolld, "move the element on top of the stack to a higher stack position"}, - {cmd_keyword, "over", &program::rpn_over, "push a copy of the element in stack level 2 onto the stack"}, - - // STRING - {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"}, - {cmd_keyword, "num", &program::rpn_num, - "return ASCII code of the first character of the string in stack level 1 as a real number"}, - {cmd_keyword, "size", &program::rpn_strsize, "return the length of the string"}, - {cmd_keyword, "pos", &program::rpn_strpos, "seach for the string in level 1 within the string in level 2"}, - {cmd_keyword, "sub", &program::rpn_strsub, "return a substring of the string in level 3"}, - - // BRANCH - {cmd_undef, "", nullptr, "\nBRANCH"}, - {cmd_branch, "if", (program_fn_t)&program::rpn_if, - "if then else " - "end"}, - {cmd_branch, "then", (program_fn_t)&program::rpn_then, "used with if"}, - {cmd_branch, "else", (program_fn_t)&program::rpn_else, "used with if"}, - {cmd_branch, "end", (program_fn_t)&program::rpn_end, "used with various branch instructions"}, - {cmd_branch, "start", (program_fn_t)&program::rpn_start, " start next| step"}, - {cmd_branch, "for", (program_fn_t)&program::rpn_for, - " for next| step"}, - {cmd_branch, "next", (program_fn_t)&program::rpn_next, "used with start and for"}, - {cmd_branch, "step", (program_fn_t)&program::rpn_step, "used with start and for"}, - {cmd_keyword, "ift", &program::rpn_ift, "similar to if-then-end, ift"}, - {cmd_keyword, "ifte", &program::rpn_ifte, - "similar to if-then-else-end, " - " ifte"}, - {cmd_branch, "do", (program_fn_t)&program::rpn_do, "do until end"}, - {cmd_branch, "until", (program_fn_t)&program::rpn_until, "used with do"}, - {cmd_branch, "while", (program_fn_t)&program::rpn_while, "while repeat end"}, - {cmd_branch, "repeat", (program_fn_t)&program::rpn_repeat, "used with while"}, - - // STORE - {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"}, - {cmd_keyword, "vars", &program::rpn_vars, "list all variables"}, - {cmd_keyword, "clusr", &program::rpn_clusr, "erase all variables"}, - {cmd_keyword, "edit", &program::rpn_edit, "edit a variable content"}, - {cmd_keyword, "sto+", &program::rpn_stoadd, "add to a stored variable. ex: 1 'name' sto+ 'name' 2 sto+"}, - {cmd_keyword, "sto-", &program::rpn_stosub, "substract to a stored variable. ex: 1 'name' sto- 'name' 2 sto-"}, - {cmd_keyword, "sto*", &program::rpn_stomul, "multiply a stored variable. ex: 3 'name' sto* 'name' 2 sto*"}, - {cmd_keyword, "sto/", &program::rpn_stodiv, "divide a stored variable. ex: 3 'name' sto/ 'name' 2 sto/"}, - {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, "", 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, "", 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"}, - {cmd_keyword, "cos", &program::rpn_cos, "cosinus"}, - {cmd_keyword, "acos", &program::rpn_acos, "arg cosinus"}, - {cmd_keyword, "tan", &program::rpn_tan, "tangent"}, - {cmd_keyword, "atan", &program::rpn_atan, "arg tangent"}, - {cmd_keyword, "d->r", &program::rpn_d2r, "convert degrees to radians"}, - {cmd_keyword, "r->d", &program::rpn_r2d, "convert radians to degrees"}, - - // LOGS 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, ""}, - {cmd_keyword, "lnp1", &program::rpn_lnp1, "ln(1+x) which is useful when x is close to 0"}, - {cmd_keyword, "exp", &program::rpn_exp, "exponential"}, - {cmd_keyword, "expm", &program::rpn_expm, "exp(x)-1 which is useful when x is close to 0"}, - {cmd_keyword, "log10", &program::rpn_log10, "logarithm base 10"}, - {cmd_keyword, "alog10", &program::rpn_alog10, "exponential base 10"}, - {cmd_keyword, "exp10", &program::rpn_alog10, ""}, - {cmd_keyword, "log2", &program::rpn_log2, "logarithm base 2"}, - {cmd_keyword, "alog2", &program::rpn_alog2, "exponential base 2"}, - {cmd_keyword, "exp2", &program::rpn_alog2, ""}, - {cmd_keyword, "sinh", &program::rpn_sinh, "hyperbolic sine"}, - {cmd_keyword, "asinh", &program::rpn_asinh, "inverse hyperbolic sine"}, - {cmd_keyword, "cosh", &program::rpn_cosh, "hyperbolic cosine"}, - {cmd_keyword, "acosh", &program::rpn_acosh, "inverse hyperbolic cosine"}, - {cmd_keyword, "tanh", &program::rpn_tanh, "hyperbolic tangent"}, - {cmd_keyword, "atanh", &program::rpn_atanh, "inverse hyperbolic tangent"}, - - // TIME 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"}, -}; - -/// autocompletion vector for linenoise autocompletion -vector& program::getAutocompletionWords() { - static vector autocompletionWords; - 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 -/// -ret_value program::run() { - bool go_out = false; - ret_value ret = ret_ok; - cmd_type_t type; - - _err = ret_ok; - _err_context = ""; - - // branches for 'if' - ret = preprocess(); - if (ret != ret_ok) { - // free allocated - for (object* o : *this) delete o; - _local_heap.clear(); - return ret; - } - - // iterate commands - for (size_t i = 0; (go_out == false) && (i < size());) { - object* o = at(i); - switch (o->_type) { - // could be an auto-evaluated symbol - case cmd_symbol: - auto_rcl(reinterpret_cast(o)); - i++; - break; - - // a keyword - case cmd_keyword: { - keyword* k = reinterpret_cast(o); - // call the matching function - (this->*(k->fn))(); - switch (_err) { - // no pb -> go on - case ret_ok: - break; - // explicit go out software - case ret_good_bye: - go_out = true; - ret = ret_good_bye; - break; - default: - // error: abort prog - go_out = true; - - // test error: make rpn return EXIT_FAILURE - if (_err == ret_test_failed) ret = ret_test_failed; - - // error: show it - if (show_error(_err, _err_context) == ret_deadly) - // pb showing error -> go out software - ret = ret_good_bye; - break; - } - i++; - break; - } - - // a branch keyword - case cmd_branch: { - // call matching function - branch* b = reinterpret_cast(o); - size_t next_cmd = (this->*(b->fn))(*b); - switch (next_cmd) { - case step_out: // step out - i++; // meaning 'next command' - break; - case runtime_error: // runtime error - (void)show_error(_err, _err_context); - go_out = true; - break; - default: - i = next_cmd; - break; - } - break; - } - - default: - // not a command, but a stack entry, manage it - // copy the program stack entry to the running stack - _stack.push_front(o->clone()); - i++; - break; - } - } - - // free allocated - for (object* o : *this) delete o; - _local_heap.clear(); - - return ret; -} - -/// @brief prepare a program for execution -/// this is needed before a program can be run -/// inner members of branch or keyword objects are filled by this function -/// these inner members store for example the index of the next keyword to execute etc. -/// -/// @return ret_value see this type -/// -ret_value program::preprocess(void) { - struct if_layout_t { - if_layout_t() - : index_then_or_unti_or_repeat(-1), - index_else(-1), - index_end(-1), - is_do_unti(false), - is_while_repeat(false) {} - int index_if_or_do_or_while; - int index_then_or_unti_or_repeat; - int index_else; - int index_end; - bool is_do_unti; - bool is_while_repeat; - }; - // for if-then-else-end - vector vlayout; - int layout_index = -1; - // for start-end-step - vector vstartindex; - - // analyse if-then-else-end branches - // analyse start-{next, step} branches - for (size_t i = 0; i < size(); i++) { - if (at(i)->_type == cmd_branch) { - branch* k = reinterpret_cast(at(i)); - if (k->value == "if") { - if_layout_t layout; - layout.index_if_or_do_or_while = i; - vlayout.push_back(layout); - layout_index++; - } else if (k->value == "then") { - size_t next = i + 1; - if (next >= size()) next = step_out; - - // nothing after 'then' -> error - if (next == step_out) { - // error: show it - show_syntax_error("missing end after then"); - return ret_syntax; - } - if (layout_index < 0) { - // error: show it - show_syntax_error("missing if before then"); - return ret_syntax; - } - if (vlayout[layout_index].index_then_or_unti_or_repeat != -1) { - // error: show it - show_syntax_error("duplicate then"); - return ret_syntax; - } - vlayout[layout_index].index_then_or_unti_or_repeat = i; - k->arg1 = next; - k->arg3 = vlayout[layout_index].index_if_or_do_or_while; - } else if (k->value == "else") { - size_t next = i + 1; - if (next >= size()) next = step_out; - - // nothing after 'else' -> error - if (next == step_out) { - // error: show it - show_syntax_error("missing end after else"); - return ret_syntax; - } - if (layout_index < 0) { - // error: show it - show_syntax_error("missing if before else"); - return ret_syntax; - } - if (vlayout[layout_index].index_then_or_unti_or_repeat == -1) { - // error: show it - show_syntax_error("missing then before else"); - return ret_syntax; - } - if (vlayout[layout_index].index_else != -1) { - // error: show it - show_syntax_error("duplicate else"); - return ret_syntax; - } - vlayout[layout_index].index_else = i; - k->arg1 = next; // fill branch1 (if was false) of 'else' - k->arg3 = vlayout[layout_index].index_if_or_do_or_while; - reinterpret_cast(at(vlayout[layout_index].index_then_or_unti_or_repeat))->arg2 = - next; // fill branch2 (if was false) of 'then' - } else if (k->value == "start") { - vstartindex.push_back(i); - } else if (k->value == "for") { - vstartindex.push_back(i); - k->arg1 = i + 1; // arg1 points on symbol variable - } else if (k->value == "next") { - if (vstartindex.size() == 0) { - // error: show it - show_syntax_error("missing start or for before next"); - return ret_syntax; - } - k->arg1 = vstartindex[vstartindex.size() - 1]; // 'next' arg1 = 'start' index - reinterpret_cast(at(vstartindex[vstartindex.size() - 1]))->arg2 = - i; // 'for' or 'start' arg2 = 'next' index - vstartindex.pop_back(); - } else if (k->value == "step") { - if (vstartindex.size() == 0) { - // error: show it - show_syntax_error("missing start or for before step"); - return ret_syntax; - } - k->arg1 = vstartindex[vstartindex.size() - 1]; // fill 'step' branch1 = 'start' index - reinterpret_cast(at(vstartindex[vstartindex.size() - 1]))->arg2 = - i; // 'for' or 'start' arg2 = 'next' index - vstartindex.pop_back(); - } else if (k->value == "->") { - k->arg1 = i; // arg1 is '->' command index in program - } else if (k->value == "do") { - if_layout_t layout; - layout.index_if_or_do_or_while = i; - layout.is_do_unti = true; - vlayout.push_back(layout); - layout_index++; - } else if (k->value == "until") { - size_t next = i + 1; - if (next >= size()) next = step_out; - - // nothing after 'unti' -> error - if (next == step_out) { - // error: show it - show_syntax_error("missing end"); - return ret_syntax; - } - if (layout_index < 0 || !vlayout[layout_index].is_do_unti) { - // error: show it - show_syntax_error("missing do"); - return ret_syntax; - } - if (vlayout[layout_index].index_then_or_unti_or_repeat != -1) { - // error: show it - show_syntax_error("duplicate until"); - return ret_syntax; - } - vlayout[layout_index].index_then_or_unti_or_repeat = i; - } else if (k->value == "while") { - if_layout_t layout; - layout.index_if_or_do_or_while = i; - layout.is_while_repeat = true; - vlayout.push_back(layout); - layout_index++; - } else if (k->value == "repeat") { - if (layout_index < 0 || !vlayout[layout_index].is_while_repeat) { - // error: show it - show_syntax_error("missing while"); - return ret_syntax; - } - if (vlayout[layout_index].index_then_or_unti_or_repeat != -1) { - // error: show it - show_syntax_error("duplicate repeat"); - return ret_syntax; - } - vlayout[layout_index].index_then_or_unti_or_repeat = i; - } else if (k->value == "end") { - size_t next = i + 1; - if (next >= size()) next = step_out; - - if (layout_index < 0) { - // error: show it - show_syntax_error("missing branch instruction before end"); - return ret_syntax; - } else { - if (vlayout[layout_index].is_do_unti) { - // this end closes a do..unti - if (vlayout[layout_index].index_end != -1) { - // error: show it - show_syntax_error("duplicate end"); - return ret_syntax; - } - - if (vlayout[layout_index].index_then_or_unti_or_repeat == -1) { - // error: show it - show_syntax_error("missing until"); - return ret_syntax; - } - - k->arg1 = vlayout[layout_index].index_if_or_do_or_while + 1; - layout_index--; - } else if (vlayout[layout_index].is_while_repeat) { - // this end closes a while..repeat - if (vlayout[layout_index].index_end != -1) { - // error: show it - show_syntax_error("duplicate end"); - return ret_syntax; - } - - k->arg2 = vlayout[layout_index].index_if_or_do_or_while + 1; - - if (vlayout[layout_index].index_then_or_unti_or_repeat == -1) { - // error: show it - show_syntax_error("missing repeat"); - return ret_syntax; - } - - // fill 'repeat' arg1 with 'end+1' - reinterpret_cast(at(vlayout[layout_index].index_then_or_unti_or_repeat))->arg1 = - i + 1; - layout_index--; - } else { - // this end closes an if..then..(else) - if (vlayout[layout_index].index_end != -1) { - // error: show it - show_syntax_error("duplicate end"); - return ret_syntax; - } - if (vlayout[layout_index].index_else != -1) { - // fill 'end' branch of 'else' - reinterpret_cast(at(vlayout[layout_index].index_else))->arg2 = i; - } else { - // fill 'end' branch of 'then' - if (vlayout[layout_index].index_then_or_unti_or_repeat != -1) { - reinterpret_cast(at(vlayout[layout_index].index_then_or_unti_or_repeat)) - ->arg2 = i; - } else { - // error: show it - show_syntax_error("missing then"); - return ret_syntax; - } - } - layout_index--; - } - } - } - } - } - if (layout_index >= 0) { - // error: show it - show_syntax_error("missing end"); - return ret_syntax; - } - if (vstartindex.size() > 0) { - // error: show it - show_syntax_error("missing next or step after for or start"); - return ret_syntax; - } - 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) { - static map keywordsMap; - vector elements; - vector 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"); - break; - } - if (element.re != nullptr) delete element.re; - if (element.im != nullptr) delete element.im; - } - } 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 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", "test failed"}; - // clang-format on - // show last recorded error - if (static_cast(_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: - ret = ret_deadly; - default: - ret = ret_ok; - } - - return ret; -} - -/// @brief record an error as the last error set and show it -/// -/// @param err the error to record -/// @param context a context string -/// @return ret_value see this type -/// -ret_value program::show_error(ret_value err, string& context) { - // record error - _err = err; - _err_context = context; - return show_error(); -} - -/// @brief record an error as the last error set and show it -/// -/// @param err the error to record -/// @param context a context string -/// @return ret_value see this type -/// -ret_value program::show_error(ret_value err, const char* context) { - // record error - _err = err; - _err_context = context; - return show_error(); -} - -/// @brief set the last error as being a syntax error and show it -/// -/// @param err the error to record -/// @param context a context string -/// @return ret_value see this type -/// -void program::show_syntax_error(const char* context) { - // record error - _err = ret_syntax; - _err_context = context; - (void)show_error(); -} - -/// @brief return the last error set -/// -/// @return ret_value see this type -/// -ret_value program::get_err(void) { return _err; } - -/// @brief show a stack (show its different objects) -/// generally a stack is associated to a running program -/// -/// @param show_separator whether to show a stack level prefix or not -/// -void program::show_stack(bool show_separator) { - if (_stack.size() == 1) { - cout << _stack[0] << endl; - } else { - for (int i = _stack.size() - 1; i >= 0; i--) { - if (show_separator) cout << i + 1 << "> "; - cout << _stack[i] << endl; - } - } -} - -/// @brief apply default precision mode and digits -/// -void program::apply_default() { - // default float precision, float mode - number::s_mode = number::DEFAULT_MODE; - number::s_digits = number::DEFAULT_DECIMAL_DIGITS; - mpreal::set_default_prec(number::MPFR_DEFAULT_PREC_BITS); - - static mp_rnd_t def_rnd = mpreal::get_default_rnd(); - mpreal::set_default_rnd(def_rnd); -} diff --git a/src/program.h b/src/program.h new file mode 100644 index 0000000..cfe5827 --- /dev/null +++ b/src/program.h @@ -0,0 +1,289 @@ +// Copyright (c) 2014-2022 Louis Rubet + +#ifndef SRC_PROGRAM_HPP_ +#define SRC_PROGRAM_HPP_ + +// std c++ +#include +#include +#include +#include +using namespace std; + +#include +using mpfr::mpreal; + +// internal includes +#include "lexer.h" +#include "object.h" +#include "stack.h" + +//< program class: the class containing a string parser, all the programs keywords, a stack for running the program +class program : public deque, public Lexer { + public: + program(rpnstack& stack__, heap& heap__, program* parent__ = nullptr) + : stack_(stack__), heap_(heap__), parent_(parent__) {} + virtual ~program() { + local_heap_.clear(); + clear(); + } + + // parser + RetValue Parse(string& entry); + + // running + RetValue Run(); + RetValue Preprocess(); + + RetValue ShowError(); + RetValue ShowError(RetValue err, string& context); + RetValue ShowError(RetValue err, const char* context); + void ShowSyntaxError(const char* context); + RetValue GetLastError(void); + + void ShowStack(bool show_separator = true); + + static void ApplyDefault(); + + static vector& GetAutocompletionWords(); + + private: + // current error and its context + RetValue err_; + string err_context_; + + // global stack holding results for user + rpnstack& stack_; + + // global heap (sto, rcl) + heap& heap_; + + // local heap for local loop variables (for..next) + heap local_heap_; + + // parent prog for inheriting heaps + program* parent_; + + private: + // keywords + struct keyword_t { + ObjectType type; + string name; + program_fn_t fn; + string comment; + }; + static vector keywords_; + + // keywords implementation + //// + + // branch + size_t RpnIf(Branch& myobj); + size_t RpnThen(Branch& myobj); + size_t RpnElse(Branch& myobj); + size_t RpnEnd(Branch& myobj); + size_t RpnDo(Branch& myobj); + size_t RpnUntil(Branch& myobj); + void RpnIft(void); + void RpnIfte(void); + size_t RpnWhile(Branch& myobj); + size_t RpnRepeat(Branch& myobj); + size_t RpnStart(Branch& myobj); + size_t RpnFor(Branch& myobj); + size_t RpnNext(Branch& myobj); + size_t RpnStep(Branch& myobj); + enum { kStepOut = static_cast(-1), kRtError = static_cast(-2) }; + + // complex + void RpnReal(); + void RpnImag(); + void RpnArg(); + void RpnConj(); + void RpnR2c(); + void RpnC2r(); + void RpnR2p(); + void RpnP2r(); + + // general + void RpnNop(); + void RpnQuit(); + void RpnHelp(); + void RpnStd(); + void RpnFix(); + void RpnSci(); + void RpnVersion(); + void RpnUname(); + void RpnHistory(); + void RpnType(); + void RpnDefault(); + void RpnPrecision(); + void RpnRound(); + + // logs + void RpnE(void); + void RpnLog10(); + void RpnAlog10(); + void RpnLog2(); + void RpnAlog2(); + void RpnLn(); + void RpnExp(); + void RpnExpm(); + void RpnLnp1(); + void RpnSinh(); + void RpnAsinh(); + void RpnCosh(); + void RpnAcosh(); + void RpnTanh(); + void RpnAtanh(); + + // program + bool FindVariable(string& variable, Object*& obj); + void RpnEval(void); + int RpnInprog(Branch& inprog_obj); + + // real + void RpnPlus(); + void RpnMinus(); + void RpnMul(); + void RpnDiv(); + void RpnNeg(); + void RpnInv(); + void RpnPurcent(); + void RpnPurcentCH(); + void RpnPower(); + void RpnSquareroot(); + void RpnSquare(); + void RpnModulo(); + void RpnAbs(); + void RpnHex(); + void RpnBin(); + void RpnDec(); + void RpnBase(); + void RpnFact(); + void RpnSign(); + void RpnMant(); + void RpnXpon(); + void RpnFloor(); + void RpnCeil(); + void RpnFp(); + void RpnIp(); + void RpnMin(); + void RpnMax(); + + // stack + void RpnSwap(void); + void RpnDrop(void); + void RpnDrop2(void); + void RpnDropn(void); + void RpnErase(void); + void RpnDup(void); + void RpnDup2(void); + void RpnDupn(void); + void RpnPick(void); + void RpnRot(void); + void RpnDepth(void); + void RpnRoll(void); + void RpnRolld(void); + void RpnOver(void); + + // store + void RpnSto(void); + void RpnStoadd(void); + void RpnStosub(void); + void RpnStomul(void); + void RpnStodiv(void); + void RpnStoneg(void); + void RpnStoinv(void); + void RpnRcl(void); + void RpnEdit(void); + void AutoRcl(Symbol* symb); + void RpnPurge(void); + void RpnVars(void); + void RpnClusr(void); + + // string + void RpnInstr(); + void RpnStrout(); + void RpnChr(); + void RpnNum(); + void RpnStrsize(); + void RpnStrpos(); + void RpnStrsub(); + + // test-core + void RpnTest(); + void RunTestFile(string test_filename, int& total_tests, int& total_tests_failed, int& total_steps, + int& total_steps_failed); + + // test + void RpnSup(void); + void RpnSupEq(void); + void RpnInf(void); + void RpnInfEq(void); + void RpnDiff(void); + void RpnEq(void); + void RpnTestAnd(void); + void RpnTestOr(void); + void RpnTestXor(void); + void RpnTestNot(void); + void RpnSame(void); + + // trig + void RpnPi(void); + void RpnD2r(void); + void RpnR2d(void); + void RpnSin(void); + void RpnAsin(void); + void RpnCos(void); + void RpnAcos(void); + void RpnTan(void); + void RpnAtan(void); + + // time + void RpnTime(); + void RpnDate(); + void RpnTicks(); +}; + +// convenience macros for rpn_xx function +// carefull : some of these macros modify program flow + +#define ERROR_CONTEXT(err) \ + do { \ + err_ = (err); \ + err_context_ = __FUNCTION__; \ + } while (0) + +#define MIN_ARGUMENTS(num) \ + do { \ + if ((num) >= 0 && stack_.size() < (num)) { \ + ERROR_CONTEXT(kMissingOperand); \ + return; \ + } \ + } while (0) + +#define MIN_ARGUMENTS_RET(num, ret) \ + do { \ + if ((num) >= 0 && stack_.size() < (num)) { \ + ERROR_CONTEXT(kMissingOperand); \ + return (ret); \ + } \ + } while (0) + +#define ARG_MUST_BE_OF_TYPE(num, type) \ + do { \ + if ((num) >= 0 && stack_.at(num)->_type != (type)) { \ + ERROR_CONTEXT(kBadOperandType); \ + return; \ + } \ + } while (0) + +#define ARG_MUST_BE_OF_TYPE_RET(num, type, ret) \ + do { \ + if ((num) >= 0 && stack_.at(num)->_type != (type)) { \ + ERROR_CONTEXT(kBadOperandType); \ + return (ret); \ + } \ + } while (0) + +#endif // SRC_PROGRAM_HPP_ diff --git a/src/program.hpp b/src/program.hpp deleted file mode 100644 index 3571bbf..0000000 --- a/src/program.hpp +++ /dev/null @@ -1,291 +0,0 @@ -// Copyright (c) 2014-2022 Louis Rubet - -#ifndef SRC_PROGRAM_HPP_ -#define SRC_PROGRAM_HPP_ - -// std c++ -#include -#include -#include -#include -using namespace std; - -#include -using namespace mpfr; - -// internal includes -#include "lexer.hpp" -#include "object.hpp" -#include "stack.hpp" - -//< program class: the class containing a string parser, all the programs keywords, a stack for running the program -class program : public deque, public Lexer { - public: - program(rpnstack& stk, heap& hp, program* parent = nullptr) : _stack(stk), _heap(hp), _parent(parent) {} - virtual ~program() { - _local_heap.clear(); - clear(); - } - - // parser - ret_value parse(string& entry); - static ret_value get_fn(const char* fn_name, program_fn_t& fn, cmd_type_t& type); - - // running - ret_value run(); - void stop(); - ret_value preprocess(void); - - ret_value show_error(); - ret_value show_error(ret_value err, string& context); - ret_value show_error(ret_value err, const char* context); - void show_syntax_error(const char* context); - ret_value get_err(void); - - void show_stack(bool show_separator = true); - - static void apply_default(); - - static vector& getAutocompletionWords(); - - private: - // current error and its context - ret_value _err; - string _err_context; - - // global stack holding results for user - rpnstack& _stack; - - // global heap (sto, rcl) - heap& _heap; - - // local heap for local loop variables (for..next) - heap _local_heap; - - // parent prog for inheriting heaps - program* _parent; - - private: - // keywords - struct keyword_t { - cmd_type_t type; - string name; - program_fn_t fn; - string comment; - }; - static vector _keywords; - - // keywords implementation - //// - - // branch - size_t rpn_if(branch& myobj); - size_t rpn_then(branch& myobj); - size_t rpn_else(branch& myobj); - size_t rpn_end(branch& myobj); - size_t rpn_do(branch& myobj); - size_t rpn_until(branch& myobj); - void rpn_ift(void); - void rpn_ifte(void); - size_t rpn_while(branch& myobj); - size_t rpn_repeat(branch& myobj); - size_t rpn_start(branch& myobj); - size_t rpn_for(branch& myobj); - size_t rpn_next(branch& myobj); - size_t rpn_step(branch& myobj); - enum { step_out = static_cast(-1), runtime_error = static_cast(-2) }; - - // complex - void rpn_re(); - void rpn_im(); - void rpn_arg(); - void rpn_conj(); - void rpn_r2c(); - void rpn_c2r(); - void rpn_r2p(); - void rpn_p2r(); - - // general - void rpn_nop(); - void rpn_good_bye(); - void rpn_help(); - void rpn_std(); - void rpn_fix(); - void rpn_sci(); - void rpn_version(); - void rpn_uname(); - void rpn_history(); - void rpn_type(); - void rpn_default(); - void rpn_precision(); - void rpn_round(); - - // logs - void rpn_e(void); - void rpn_log10(); - void rpn_alog10(); - void rpn_log2(); - void rpn_alog2(); - void rpn_ln(); - void rpn_exp(); - void rpn_expm(); - void rpn_lnp1(); - void rpn_sinh(); - void rpn_asinh(); - void rpn_cosh(); - void rpn_acosh(); - void rpn_tanh(); - void rpn_atanh(); - - // program - bool find_variable(string& variable, object*& obj); - void rpn_eval(void); - int rpn_inprog(branch& myobj); - - // real - void rpn_plus(); - void rpn_minus(); - void rpn_mul(); - void do_divide_complexes(); - void rpn_div(); - void rpn_neg(); - void rpn_inv(); - void rpn_purcent(); - void rpn_purcentCH(); - void rpn_power(); - void rpn_squareroot(); - void rpn_square(); - void rpn_modulo(); - void rpn_abs(); - void rpn_hex(); - void rpn_bin(); - void rpn_dec(); - void rpn_base(); - void rpn_fact(); - void rpn_sign(); - void rpn_mant(); - void rpn_xpon(); - void rpn_floor(); - void rpn_ceil(); - void rpn_fp(); - void rpn_ip(); - void rpn_min(); - void rpn_max(); - - // stack - void rpn_swap(void); - void rpn_drop(void); - void rpn_drop2(void); - void rpn_dropn(void); - void rpn_erase(void); - void rpn_dup(void); - void rpn_dup2(void); - void rpn_dupn(void); - void rpn_pick(void); - void rpn_rot(void); - void rpn_depth(void); - void rpn_roll(void); - void rpn_rolld(void); - void rpn_over(void); - - // store - void rpn_sto(void); - void rpn_stoadd(void); - void rpn_stosub(void); - void rpn_stomul(void); - void rpn_stodiv(void); - void rpn_stoneg(void); - void rpn_stoinv(void); - void rpn_rcl(void); - void rpn_edit(void); - void auto_rcl(symbol* symb); - void rpn_purge(void); - void rpn_vars(void); - void rpn_clusr(void); - - // string - void rpn_instr(); - void rpn_strout(); - void rpn_chr(); - void rpn_num(); - void rpn_strsize(); - void rpn_strpos(); - void rpn_strsub(); - - // test-core - void rpn_test(); - void test(string test_filename, int& total_tests, int& total_tests_failed, int& total_steps, - int& total_steps_failed); - - // test - void rpn_sup(void); - void rpn_sup_eq(void); - void rpn_inf(void); - void rpn_inf_eq(void); - void rpn_diff(void); - void rpn_eq(void); - void rpn_test_and(void); - void rpn_test_or(void); - void rpn_test_xor(void); - void rpn_test_not(void); - void rpn_same(void); - - // trig - void rpn_pi(void); - void rpn_d2r(void); - void rpn_r2d(void); - void rpn_sin(void); - void rpn_asin(void); - void rpn_cos(void); - void rpn_acos(void); - void rpn_tan(void); - void rpn_atan(void); - - // time - void rpn_time(); - void rpn_date(); - void rpn_ticks(); -}; - -// convenience macros for rpn_xx function -// carefull : some of these macros modify program flow - -#define setErrorContext(err) \ - do { \ - _err = (err); \ - _err_context = __FUNCTION__; \ - } while (0) - -#define MIN_ARGUMENTS(num) \ - do { \ - if ((num) >= 0 && _stack.size() < (num)) { \ - setErrorContext(ret_missing_operand); \ - return; \ - } \ - } while (0) - -#define MIN_ARGUMENTS_RET(num, ret) \ - do { \ - if ((num) >= 0 && _stack.size() < (num)) { \ - setErrorContext(ret_missing_operand); \ - return (ret); \ - } \ - } while (0) - -#define ARG_MUST_BE_OF_TYPE(num, type) \ - do { \ - if ((num) >= 0 && _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 ((num) >= 0 && _stack.at(num)->_type != (type)) { \ - setErrorContext(ret_bad_operand_type); \ - return (ret); \ - } \ - } while (0) - -#endif // SRC_PROGRAM_HPP_ diff --git a/src/rpn-branch.cc b/src/rpn-branch.cc new file mode 100644 index 0000000..e72d0cf --- /dev/null +++ b/src/rpn-branch.cc @@ -0,0 +1,393 @@ +// Copyright (c) 2014-2022 Louis Rubet + +#include "program.h" + +/// @brief if keyword (branch) implementation +/// +/// @param myobj the current branch object +/// @return size_t index of the next object to run in the current program +/// @return kStepOut next object to run in the current program is current + 1 +/// @return kRtError something went wrong with preprocess, abort branch +/// +size_t program::RpnIf(Branch& myobj) { + // myobj.arg1 = 'if' condition evaluation value + MIN_ARGUMENTS_RET(1, kRtError); + ARG_MUST_BE_OF_TYPE_RET(0, kNumber, kRtError); + + if (stack_.value(0) != 0) + myobj.arg1 = 1; + else + myobj.arg1 = 0; + stack_.pop(); + return kStepOut; +} + +/// @brief then keyword (branch) implementation +/// +/// @param myobj the current branch object +/// @return size_t index of the next object to run in the current program +/// @return kStepOut next object to run in the current program is current + 1 +/// @return kRtError something went wrong with preprocess, abort Branch +/// +size_t program::RpnThen(Branch& myobj) { + // myobj.arg1 = index of then + 1 + // myobj.arg2 = index of else + 1 or end + 1 + // myobj.arg3 = index of if + // if condition is true -> arg1 (= jump to then + 1) + // else -> arg2 (= jump to else + 1 or end + 1) + Branch* if_cmd; + if (myobj.arg3 >= size() || at(myobj.arg3)->_type != kBranch) { + ERROR_CONTEXT(kMissingOperand); + return kRtError; + } + if_cmd = reinterpret_cast(at(myobj.arg3)); + if (if_cmd->arg1 == 1) + return myobj.arg1; + else + return myobj.arg2; +} + +/// @brief else keyword (branch) implementation +/// +/// @param myobj the current branch object +/// @return size_t index of the next object to run in the current program +/// @return kStepOut next object to run in the current program is current + 1 +/// @return kRtError something went wrong with preprocess, abort branch +/// +size_t program::RpnElse(Branch& myobj) { + // myobj.arg1 = index of else + 1 + // myobj.arg2 = index of end + 1 + // myobj.arg3 = index of if + // if condition was false -> arg1 (= jump to else + 1) + // if condition was true -> arg2 (= jump to end + 1) + Branch* if_cmd; + if (myobj.arg3 >= size() || at(myobj.arg3)->_type != kBranch) { + ERROR_CONTEXT(kMissingOperand); + return kRtError; + } + if_cmd = reinterpret_cast(at(myobj.arg3)); + if (if_cmd->arg1 == 1) + return myobj.arg2; + else + return myobj.arg1; +} + +/// @brief end keyword (branch) implementation +/// +/// @param myobj the current branch object +/// @return size_t index of the next object to run in the current program +/// @return kStepOut next object to run in the current program is current + 1 +/// @return kRtError something went wrong with preprocess, abort branch +/// +size_t program::RpnEnd(Branch& myobj) { + size_t ret = kStepOut; + + // arg1 = index of do+1 in case of do..unti..end + if (myobj.arg1 != -1) { + // in a template do..until..end + MIN_ARGUMENTS_RET(1, kRtError); + ARG_MUST_BE_OF_TYPE_RET(0, kNumber, kRtError); + + // check arg + if (stack_.value(0) == 0) ret = myobj.arg1; + stack_.pop(); + } else if (myobj.arg2 != kStepOut) { + // arg2 = index of while+1 in case of while..repeat..end + ret = myobj.arg2; + } + + return ret; +} + +/// @brief do keyword (branch) implementation +/// +/// @param myobj the current branch object +/// @return size_t index of the next object to run in the current program +/// @return kStepOut next object to run in the current program is current + 1 +/// @return kRtError something went wrong with preprocess, abort branch +/// +size_t program::RpnDo(Branch& myobj) { + // nothing + return kStepOut; +} + +/// @brief until keyword (branch) implementation +/// +/// @param myobj the current branch object +/// @return size_t index of the next object to run in the current program +/// @return kStepOut next object to run in the current program is current + 1 +/// @return kRtError something went wrong with preprocess, abort Branch +/// +size_t program::RpnUntil(Branch& myobj) { + // nothing + return kStepOut; +} + +/// @brief ift keyword (branch) implementation +/// +/// @param myobj the current branch object +/// @return size_t index of the next object to run in the current program +/// @return kStepOut next object to run in the current program is current + 1 +/// @return kRtError something went wrong with preprocess, abort branch +/// +void program::RpnIft(void) { + MIN_ARGUMENTS(2); + ARG_MUST_BE_OF_TYPE(1, kNumber); + + // check ift arg + // arg is true if Number != 0 or if is nan or +/-inf + if (stack_.value(1) != 0) + stack_.erase(1); + else + stack_.erase(0, 2); +} + +/// @brief ifte keyword (branch) implementation +/// +/// @param myobj the current branch object +/// @return size_t index of the next object to run in the current program +/// @return kStepOut next object to run in the current program is current + 1 +/// @return kRtError something went wrong with preprocess, abort branch +/// +void program::RpnIfte(void) { + MIN_ARGUMENTS(3); + ARG_MUST_BE_OF_TYPE(2, kNumber); + + // check ifte arg + if (stack_.value(2) != 0) { + stack_.erase(2); + stack_.pop(); + } else { + stack_.erase(2); + stack_.erase(1); + } +} + +/// @brief while keyword (branch) implementation +/// +/// @param myobj the current branch object +/// @return size_t index of the next object to run in the current program +/// @return kStepOut next object to run in the current program is current + 1 +/// @return kRtError something went wrong with preprocess, abort branch +/// +size_t program::RpnWhile(Branch& myobj) { + // nothing + return kStepOut; +} + +/// @brief repeat keyword (branch) implementation +/// +/// @param myobj the current branch object +/// @return size_t index of the next object to run in the current program +/// @return kStepOut next object to run in the current program is current + 1 +/// @return kRtError something went wrong with preprocess, abort branch +/// +size_t program::RpnRepeat(Branch& myobj) { + size_t ret = kStepOut; + + MIN_ARGUMENTS_RET(1, kRtError); + ARG_MUST_BE_OF_TYPE_RET(0, kNumber, kRtError); + + // check arg + // myobj.arg1 is end+1 + if (stack_.value(0) == 0) ret = myobj.arg1; + stack_.pop(); + + return ret; +} + +/// @brief start keyword (branch) implementation +/// +/// @param myobj the current branch object +/// @return size_t index of the next object to run in the current program +/// @return kStepOut next object to run in the current program is current + 1 +/// @return kRtError something went wrong with preprocess, abort branch +/// +size_t program::RpnStart(Branch& myobj) { + size_t ret = kStepOut; + + MIN_ARGUMENTS_RET(2, kRtError); + ARG_MUST_BE_OF_TYPE_RET(0, kNumber, kRtError); + ARG_MUST_BE_OF_TYPE_RET(1, kNumber, kRtError); + + // loop boundaries + myobj.first_index = stack_.value(1); + myobj.last_index = stack_.value(0); + stack_.erase(0, 2); + + // test value + if (myobj.first_index > myobj.last_index) + // last boundary lower than first boundary + // -> next command shall be after 'next' + // arg2 holds index of 'next' + ret = myobj.arg2 + 1; + + return ret; +} + +/// @brief for keyword (branch) implementation +/// +/// @param myobj the current branch object +/// @return size_t index of the next object to run in the current program +/// @return kStepOut next object to run in the current program is current + 1 +/// @return kRtError something went wrong with preprocess, abort branch +/// +size_t program::RpnFor(Branch& myobj) { + size_t ret; + + MIN_ARGUMENTS_RET(2, kRtError); + ARG_MUST_BE_OF_TYPE_RET(0, kNumber, kRtError); + ARG_MUST_BE_OF_TYPE_RET(1, kNumber, kRtError); + + Symbol* sym; + if (myobj.arg1 >= size() || at(myobj.arg1)->_type != kSymbol) { + ERROR_CONTEXT(kMissingOperand); + return kRtError; + } + sym = reinterpret_cast(at(myobj.arg1)); // arg1 = loop variable index + + // loop boundaries + myobj.first_index = stack_.value(1); + myobj.last_index = stack_.value(0); + + // test value + if (myobj.first_index > myobj.last_index) { + // last boundary lower than first boundary + // -> next command shall be after 'next' + // arg2 holds index of 'next' + ret = myobj.arg2 + 1; + } else { + // store symbol with first value + auto it = local_heap_.find(sym->value); + if (it != local_heap_.end()) { + delete it->second; + local_heap_.erase(it); + } + local_heap_[sym->value] = stack_.obj(1).Clone(); + ret = myobj.arg1 + 1; + } + + stack_.erase(0, 2); + + return ret; +} + +/// @brief next keyword (branch) implementation +/// +/// @param myobj the current branch object +/// @return size_t index of the next object to run in the current program +/// @return kStepOut next object to run in the current program is current + 1 +/// @return kRtError something went wrong with preprocess, abort branch +/// +size_t program::RpnNext(Branch& myobj) { + // arg1 = loop variable index + // first_index = current point in the loop + Branch* start_or_for; + if (myobj.arg1 >= size() || at(myobj.arg1)->_type != kBranch) { + ERROR_CONTEXT(kMissingOperand); + return kRtError; + } + start_or_for = reinterpret_cast(at(myobj.arg1)); + if (!myobj.arg_bool) { + myobj.arg_bool = true; + myobj.first_index = start_or_for->first_index; + } + + // increment then test + // carefull: round toward minus infinity to avoid missing last boundary (because growing step) + mpfr_add(myobj.first_index.mpfr_ptr(), myobj.first_index.mpfr_srcptr(), mpreal(1).mpfr_srcptr(), MPFR_RNDD); + + // for command: increment symbol too + if (start_or_for->arg1 != -1) { + Object* obj; + Symbol* var; + if (start_or_for->arg1 >= size() || at(start_or_for->arg1)->_type != kSymbol) { + ERROR_CONTEXT(kMissingOperand); + return kRtError; + } + var = reinterpret_cast(at(start_or_for->arg1)); + + // store symbol variable (asserted existing in the local heap) + reinterpret_cast(local_heap_[var->value])->value = myobj.first_index; + } + + // test value + if (myobj.first_index > start_or_for->last_index) { + // end of loop + myobj.arg_bool = false; // init again next time + return kStepOut; + } else { + // for command: next instruction will be after symbol variable + if (start_or_for->arg1 != -1) return start_or_for->arg1 + 1; + // start command: next instruction will be after start command + else + return myobj.arg1 + 1; + } +} + +/// @brief step keyword (branch) implementation +/// +/// @param myobj the current branch object +/// @return size_t index of the next object to run in the current program +/// @return kStepOut next object to run in the current program is current + 1 +/// @return kRtError something went wrong with preprocess, abort branch +/// +size_t program::RpnStep(Branch& myobj) { + size_t ret; + MIN_ARGUMENTS_RET(1, kRtError); + ARG_MUST_BE_OF_TYPE_RET(0, kNumber, kRtError); + + mpreal step = stack_.value(0); + stack_.pop(); + + // end of loop if step is negative or zero + if (step <= 0) { + ret = kStepOut; + } else { + // arg1 = loop variable index + // first_index = current count + Branch* start_or_for; + if (myobj.arg1 >= size() || at(myobj.arg1)->_type != kBranch) { + ERROR_CONTEXT(kMissingOperand); + return kRtError; + } + start_or_for = reinterpret_cast(at(myobj.arg1)); + if (!myobj.arg_bool) { + myobj.arg_bool = true; + myobj.first_index = start_or_for->first_index; + } + + // increment then test + // carefull: round toward minus infinity to avoid missing last boundary (because growing step) + mpfr_add(myobj.first_index.mpfr_ptr(), myobj.first_index.mpfr_srcptr(), step.mpfr_srcptr(), MPFR_RNDD); + + if (start_or_for->arg1 != -1) { + Object* obj; + Symbol* var; + + // for command: increment symbol too + if (start_or_for->arg1 >= size() || at(start_or_for->arg1)->_type != kSymbol) { + ERROR_CONTEXT(kMissingOperand); + return kRtError; + } + var = reinterpret_cast(at(start_or_for->arg1)); + // increase symbol variable + reinterpret_cast(local_heap_[var->value])->value = myobj.first_index; + } + + // test loop value is out of range + if (myobj.first_index > start_or_for->last_index) { + // end of loop + myobj.arg_bool = false; // init again next time + ret = kStepOut; + } else { + // for command: next instruction will be after symbol variable + if (start_or_for->arg1 != -1) ret = start_or_for->arg1 + 1; + // start command: next instruction will be after start command + else + ret = myobj.arg1 + 1; + } + } + + return ret; +} diff --git a/src/rpn-branch.cpp b/src/rpn-branch.cpp deleted file mode 100644 index 182cc03..0000000 --- a/src/rpn-branch.cpp +++ /dev/null @@ -1,393 +0,0 @@ -// Copyright (c) 2014-2022 Louis Rubet - -#include "program.hpp" - -/// @brief if keyword (branch) implementation -/// -/// @param myobj the current branch object -/// @return size_t index of the next object to run in the current program -/// @return step_out next object to run in the current program is current + 1 -/// @return runtime_error something went wrong with preprocess, abort branch -/// -size_t program::rpn_if(branch& myobj) { - // myobj.arg1 = 'if' condition evaluation value - MIN_ARGUMENTS_RET(1, runtime_error); - ARG_MUST_BE_OF_TYPE_RET(0, cmd_number, runtime_error); - - if (_stack.value(0) != 0) - myobj.arg1 = 1; - else - myobj.arg1 = 0; - _stack.pop(); - return step_out; -} - -/// @brief then keyword (branch) implementation -/// -/// @param myobj the current branch object -/// @return size_t index of the next object to run in the current program -/// @return step_out next object to run in the current program is current + 1 -/// @return runtime_error something went wrong with preprocess, abort branch -/// -size_t program::rpn_then(branch& myobj) { - // myobj.arg1 = index of then + 1 - // myobj.arg2 = index of else + 1 or end + 1 - // myobj.arg3 = index of if - // if condition is true -> arg1 (= jump to then + 1) - // else -> arg2 (= jump to else + 1 or end + 1) - branch* if_cmd; - if (myobj.arg3 >= size() || at(myobj.arg3)->_type != cmd_branch) { - setErrorContext(ret_missing_operand); - return runtime_error; - } - if_cmd = reinterpret_cast(at(myobj.arg3)); - if (if_cmd->arg1 == 1) - return myobj.arg1; - else - return myobj.arg2; -} - -/// @brief else keyword (branch) implementation -/// -/// @param myobj the current branch object -/// @return size_t index of the next object to run in the current program -/// @return step_out next object to run in the current program is current + 1 -/// @return runtime_error something went wrong with preprocess, abort branch -/// -size_t program::rpn_else(branch& myobj) { - // myobj.arg1 = index of else + 1 - // myobj.arg2 = index of end + 1 - // myobj.arg3 = index of if - // if condition was false -> arg1 (= jump to else + 1) - // if condition was true -> arg2 (= jump to end + 1) - branch* if_cmd; - if (myobj.arg3 >= size() || at(myobj.arg3)->_type != cmd_branch) { - setErrorContext(ret_missing_operand); - return runtime_error; - } - if_cmd = reinterpret_cast(at(myobj.arg3)); - if (if_cmd->arg1 == 1) - return myobj.arg2; - else - return myobj.arg1; -} - -/// @brief end keyword (branch) implementation -/// -/// @param myobj the current branch object -/// @return size_t index of the next object to run in the current program -/// @return step_out next object to run in the current program is current + 1 -/// @return runtime_error something went wrong with preprocess, abort branch -/// -size_t program::rpn_end(branch& myobj) { - size_t ret = step_out; - - // arg1 = index of do+1 in case of do..unti..end - if (myobj.arg1 != -1) { - // in a template do..until..end - MIN_ARGUMENTS_RET(1, runtime_error); - ARG_MUST_BE_OF_TYPE_RET(0, cmd_number, runtime_error); - - // check arg - if (_stack.value(0) == 0) ret = myobj.arg1; - _stack.pop(); - } else if (myobj.arg2 != step_out) { - // arg2 = index of while+1 in case of while..repeat..end - ret = myobj.arg2; - } - - return ret; -} - -/// @brief do keyword (branch) implementation -/// -/// @param myobj the current branch object -/// @return size_t index of the next object to run in the current program -/// @return step_out next object to run in the current program is current + 1 -/// @return runtime_error something went wrong with preprocess, abort branch -/// -size_t program::rpn_do(branch& myobj) { - // nothing - return step_out; -} - -/// @brief until keyword (branch) implementation -/// -/// @param myobj the current branch object -/// @return size_t index of the next object to run in the current program -/// @return step_out next object to run in the current program is current + 1 -/// @return runtime_error something went wrong with preprocess, abort branch -/// -size_t program::rpn_until(branch& myobj) { - // nothing - return step_out; -} - -/// @brief ift keyword (branch) implementation -/// -/// @param myobj the current branch object -/// @return size_t index of the next object to run in the current program -/// @return step_out next object to run in the current program is current + 1 -/// @return runtime_error something went wrong with preprocess, abort branch -/// -void program::rpn_ift(void) { - MIN_ARGUMENTS(2); - ARG_MUST_BE_OF_TYPE(1, cmd_number); - - // check ift arg - // arg is true if number != 0 or if is nan or +/-inf - if (_stack.value(1) != 0) - _stack.erase(1); - else - _stack.erase(0, 2); -} - -/// @brief ifte keyword (branch) implementation -/// -/// @param myobj the current branch object -/// @return size_t index of the next object to run in the current program -/// @return step_out next object to run in the current program is current + 1 -/// @return runtime_error something went wrong with preprocess, abort branch -/// -void program::rpn_ifte(void) { - MIN_ARGUMENTS(3); - ARG_MUST_BE_OF_TYPE(2, cmd_number); - - // check ifte arg - if (_stack.value(2) != 0) { - _stack.erase(2); - _stack.pop(); - } else { - _stack.erase(2); - _stack.erase(1); - } -} - -/// @brief while keyword (branch) implementation -/// -/// @param myobj the current branch object -/// @return size_t index of the next object to run in the current program -/// @return step_out next object to run in the current program is current + 1 -/// @return runtime_error something went wrong with preprocess, abort branch -/// -size_t program::rpn_while(branch& myobj) { - // nothing - return step_out; -} - -/// @brief repeat keyword (branch) implementation -/// -/// @param myobj the current branch object -/// @return size_t index of the next object to run in the current program -/// @return step_out next object to run in the current program is current + 1 -/// @return runtime_error something went wrong with preprocess, abort branch -/// -size_t program::rpn_repeat(branch& myobj) { - size_t ret = step_out; - - MIN_ARGUMENTS_RET(1, runtime_error); - ARG_MUST_BE_OF_TYPE_RET(0, cmd_number, runtime_error); - - // check arg - // myobj.arg1 is end+1 - if (_stack.value(0) == 0) ret = myobj.arg1; - _stack.pop(); - - return ret; -} - -/// @brief start keyword (branch) implementation -/// -/// @param myobj the current branch object -/// @return size_t index of the next object to run in the current program -/// @return step_out next object to run in the current program is current + 1 -/// @return runtime_error something went wrong with preprocess, abort branch -/// -size_t program::rpn_start(branch& myobj) { - size_t ret = step_out; - - MIN_ARGUMENTS_RET(2, runtime_error); - ARG_MUST_BE_OF_TYPE_RET(0, cmd_number, runtime_error); - ARG_MUST_BE_OF_TYPE_RET(1, cmd_number, runtime_error); - - // loop boundaries - myobj.firstIndex = _stack.value(1); - myobj.lastIndex = _stack.value(0); - _stack.erase(0, 2); - - // test value - if (myobj.firstIndex > myobj.lastIndex) - // last boundary lower than first boundary - // -> next command shall be after 'next' - // arg2 holds index of 'next' - ret = myobj.arg2 + 1; - - return ret; -} - -/// @brief for keyword (branch) implementation -/// -/// @param myobj the current branch object -/// @return size_t index of the next object to run in the current program -/// @return step_out next object to run in the current program is current + 1 -/// @return runtime_error something went wrong with preprocess, abort branch -/// -size_t program::rpn_for(branch& myobj) { - size_t ret; - - MIN_ARGUMENTS_RET(2, runtime_error); - ARG_MUST_BE_OF_TYPE_RET(0, cmd_number, runtime_error); - ARG_MUST_BE_OF_TYPE_RET(1, cmd_number, runtime_error); - - symbol* sym; - if (myobj.arg1 >= size() || at(myobj.arg1)->_type != cmd_symbol) { - setErrorContext(ret_missing_operand); - return runtime_error; - } - sym = reinterpret_cast(at(myobj.arg1)); // arg1 = loop variable index - - // loop boundaries - myobj.firstIndex = _stack.value(1); - myobj.lastIndex = _stack.value(0); - - // test value - if (myobj.firstIndex > myobj.lastIndex) { - // last boundary lower than first boundary - // -> next command shall be after 'next' - // arg2 holds index of 'next' - ret = myobj.arg2 + 1; - } else { - // store symbol with first value - auto it = _local_heap.find(sym->value); - if (it != _local_heap.end()) { - delete it->second; - _local_heap.erase(it); - } - _local_heap[sym->value] = _stack.obj(1).clone(); - ret = myobj.arg1 + 1; - } - - _stack.erase(0, 2); - - return ret; -} - -/// @brief next keyword (branch) implementation -/// -/// @param myobj the current branch object -/// @return size_t index of the next object to run in the current program -/// @return step_out next object to run in the current program is current + 1 -/// @return runtime_error something went wrong with preprocess, abort branch -/// -size_t program::rpn_next(branch& myobj) { - // arg1 = loop variable index - // firstIndex = current point in the loop - branch* start_or_for; - if (myobj.arg1 >= size() || at(myobj.arg1)->_type != cmd_branch) { - setErrorContext(ret_missing_operand); - return runtime_error; - } - start_or_for = reinterpret_cast(at(myobj.arg1)); - if (!myobj.arg_bool) { - myobj.arg_bool = true; - myobj.firstIndex = start_or_for->firstIndex; - } - - // increment then test - // carefull: round toward minus infinity to avoid missing last boundary (because growing step) - mpfr_add(myobj.firstIndex.mpfr_ptr(), myobj.firstIndex.mpfr_srcptr(), mpreal(1).mpfr_srcptr(), MPFR_RNDD); - - // for command: increment symbol too - if (start_or_for->arg1 != -1) { - object* obj; - symbol* var; - if (start_or_for->arg1 >= size() || at(start_or_for->arg1)->_type != cmd_symbol) { - setErrorContext(ret_missing_operand); - return runtime_error; - } - var = reinterpret_cast(at(start_or_for->arg1)); - - // store symbol variable (asserted existing in the local heap) - reinterpret_cast(_local_heap[var->value])->value = myobj.firstIndex; - } - - // test value - if (myobj.firstIndex > start_or_for->lastIndex) { - // end of loop - myobj.arg_bool = false; // init again next time - return step_out; - } else { - // for command: next instruction will be after symbol variable - if (start_or_for->arg1 != -1) return start_or_for->arg1 + 1; - // start command: next instruction will be after start command - else - return myobj.arg1 + 1; - } -} - -/// @brief step keyword (branch) implementation -/// -/// @param myobj the current branch object -/// @return size_t index of the next object to run in the current program -/// @return step_out next object to run in the current program is current + 1 -/// @return runtime_error something went wrong with preprocess, abort branch -/// -size_t program::rpn_step(branch& myobj) { - size_t ret; - MIN_ARGUMENTS_RET(1, runtime_error); - ARG_MUST_BE_OF_TYPE_RET(0, cmd_number, runtime_error); - - mpreal step = _stack.value(0); - _stack.pop(); - - // end of loop if step is negative or zero - if (step <= 0) { - ret = step_out; - } else { - // arg1 = loop variable index - // firstIndex = current count - branch* start_or_for; - if (myobj.arg1 >= size() || at(myobj.arg1)->_type != cmd_branch) { - setErrorContext(ret_missing_operand); - return runtime_error; - } - start_or_for = reinterpret_cast(at(myobj.arg1)); - if (!myobj.arg_bool) { - myobj.arg_bool = true; - myobj.firstIndex = start_or_for->firstIndex; - } - - // increment then test - // carefull: round toward minus infinity to avoid missing last boundary (because growing step) - mpfr_add(myobj.firstIndex.mpfr_ptr(), myobj.firstIndex.mpfr_srcptr(), step.mpfr_srcptr(), MPFR_RNDD); - - if (start_or_for->arg1 != -1) { - object* obj; - symbol* var; - - // for command: increment symbol too - if (start_or_for->arg1 >= size() || at(start_or_for->arg1)->_type != cmd_symbol) { - setErrorContext(ret_missing_operand); - return runtime_error; - } - var = reinterpret_cast(at(start_or_for->arg1)); - // increase symbol variable - reinterpret_cast(_local_heap[var->value])->value = myobj.firstIndex; - } - - // test loop value is out of range - if (myobj.firstIndex > start_or_for->lastIndex) { - // end of loop - myobj.arg_bool = false; // init again next time - ret = step_out; - } else { - // for command: next instruction will be after symbol variable - if (start_or_for->arg1 != -1) ret = start_or_for->arg1 + 1; - // start command: next instruction will be after start command - else - ret = myobj.arg1 + 1; - } - } - - return ret; -} diff --git a/src/rpn-complex.cc b/src/rpn-complex.cc new file mode 100644 index 0000000..55d3aed --- /dev/null +++ b/src/rpn-complex.cc @@ -0,0 +1,85 @@ +// Copyright (c) 2014-2022 Louis Rubet + +#include "program.h" + +/// @brief re keyword implementation +/// the result is stacked on current program stack +/// +void program::RpnReal() { + MIN_ARGUMENTS(1); + ARG_MUST_BE_OF_TYPE(0, kComplex); + stack_.push_front(new Number(real(stack_.value(0)))); + stack_.erase(1); +} + +/// @brief im keyword implementation +/// the result is stacked on current program stack +/// +void program::RpnImag() { + MIN_ARGUMENTS(1); + ARG_MUST_BE_OF_TYPE(0, kComplex); + stack_.push_front(new Number(imag(stack_.value(0)))); + stack_.erase(1); +} + +/// @brief arg keyword implementation +/// the result is stacked on current program stack +/// +void program::RpnArg() { + MIN_ARGUMENTS(1); + ARG_MUST_BE_OF_TYPE(0, kComplex); + stack_.push_front(new Number(arg(stack_.value(0)))); + stack_.erase(1); +} + +/// @brief conj keyword implementation +/// the result is stacked on current program stack +/// +void program::RpnConj() { + MIN_ARGUMENTS(1); + ARG_MUST_BE_OF_TYPE(0, kComplex); + stack_.value(0) = conj(stack_.value(0)); +} + +/// @brief r2c keyword implementation +/// the result is stacked on current program stack +/// +void program::RpnR2c() { + MIN_ARGUMENTS(2); + ARG_MUST_BE_OF_TYPE(0, kNumber); + ARG_MUST_BE_OF_TYPE(1, kNumber); + stack_.push(new Complex(stack_.value(1), stack_.value(0), stack_.obj(1).re_base, stack_.obj(0).re_base)); + stack_.erase(1, 2); +} + +/// @brief c2r keyword implementation +/// the result is stacked on current program stack +/// +void program::RpnC2r() { + MIN_ARGUMENTS(1); + ARG_MUST_BE_OF_TYPE(0, kComplex); + stack_.push(new Number(real(stack_.value(0)), stack_.obj(0).re_base)); + stack_.push(new Number(imag(stack_.value(1)), stack_.obj(1).im_base)); + stack_.erase(2); +} + +/// @brief r2p keyword implementation +/// the result is stacked on current program stack +/// +void program::RpnR2p() { + MIN_ARGUMENTS(1); + ARG_MUST_BE_OF_TYPE(0, kComplex); + mpreal rho = abs(stack_.value(0)); + mpreal theta = arg(stack_.value(0)); + stack_.value(0).real(rho); + stack_.value(0).imag(theta); +} + +/// @brief p2r keyword implementation +/// the result is stacked on current program stack +/// +void program::RpnP2r() { + MIN_ARGUMENTS(1); + ARG_MUST_BE_OF_TYPE(0, kComplex); + stack_.value(0) = polar(abs(stack_.value(0)), arg(stack_.value(0))); +} diff --git a/src/rpn-complex.cpp b/src/rpn-complex.cpp deleted file mode 100644 index ca94145..0000000 --- a/src/rpn-complex.cpp +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright (c) 2014-2022 Louis Rubet - -#include "program.hpp" - -/// @brief re keyword implementation -/// the result is stacked on current program stack -/// -void program::rpn_re() { - MIN_ARGUMENTS(1); - ARG_MUST_BE_OF_TYPE(0, cmd_complex); - _stack.push_front(new number(real(_stack.value(0)))); - _stack.erase(1); -} - -/// @brief im keyword implementation -/// the result is stacked on current program stack -/// -void program::rpn_im() { - MIN_ARGUMENTS(1); - ARG_MUST_BE_OF_TYPE(0, cmd_complex); - _stack.push_front(new number(imag(_stack.value(0)))); - _stack.erase(1); -} - -/// @brief arg keyword implementation -/// the result is stacked on current program stack -/// -void program::rpn_arg() { - MIN_ARGUMENTS(1); - ARG_MUST_BE_OF_TYPE(0, cmd_complex); - _stack.push_front(new number(arg(_stack.value(0)))); - _stack.erase(1); -} - -/// @brief conj keyword implementation -/// the result is stacked on current program stack -/// -void program::rpn_conj() { - MIN_ARGUMENTS(1); - ARG_MUST_BE_OF_TYPE(0, cmd_complex); - _stack.value(0) = conj(_stack.value(0)); -} - -/// @brief r2c keyword implementation -/// the result is stacked on current program stack -/// -void program::rpn_r2c() { - MIN_ARGUMENTS(2); - ARG_MUST_BE_OF_TYPE(0, cmd_number); - ARG_MUST_BE_OF_TYPE(1, cmd_number); - _stack.push(new ocomplex(_stack.value(1), _stack.value(0), _stack.obj(1).reBase, _stack.obj(0).reBase)); - _stack.erase(1, 2); -} - -/// @brief c2r keyword implementation -/// the result is stacked on current program stack -/// -void program::rpn_c2r() { - MIN_ARGUMENTS(1); - ARG_MUST_BE_OF_TYPE(0, cmd_complex); - _stack.push(new number(real(_stack.value(0)), _stack.obj(0).reBase)); - _stack.push(new number(imag(_stack.value(1)), _stack.obj(1).imBase)); - _stack.erase(2); -} - -/// @brief r2p keyword implementation -/// the result is stacked on current program stack -/// -void program::rpn_r2p() { - MIN_ARGUMENTS(1); - ARG_MUST_BE_OF_TYPE(0, cmd_complex); - mpreal rho = abs(_stack.value(0)); - mpreal theta = arg(_stack.value(0)); - _stack.value(0).real(rho); - _stack.value(0).imag(theta); -} - -/// @brief p2r keyword implementation -/// the result is stacked on current program stack -/// -void program::rpn_p2r() { - MIN_ARGUMENTS(1); - ARG_MUST_BE_OF_TYPE(0, cmd_complex); - _stack.value(0) = polar(abs(_stack.value(0)), arg(_stack.value(0))); -} diff --git a/src/rpn-general.cc b/src/rpn-general.cc new file mode 100644 index 0000000..7437f8f --- /dev/null +++ b/src/rpn-general.cc @@ -0,0 +1,228 @@ +// Copyright (c) 2014-2022 Louis Rubet + +#include +#include +#include +using std::cout, std::string, std::pair; + +#include "linenoise.h" +#include "program.h" +#include "version.h" + +// description +#define ATTR_BOLD "\33[1m" +#define ATTR_OFF "\33[0m" + +#define XSTR(a) STR(a) +#define STR(a) #a +static const char _description[] = ATTR_BOLD + "R" ATTR_OFF "everse " ATTR_BOLD "P" ATTR_OFF "olish " ATTR_BOLD "N" ATTR_OFF "otation language\n\nusing " 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\nand " ATTR_BOLD "linenoise-ng" ATTR_OFF " v" LINENOISE_VERSION + " under BSD\n"; + +static const char _syntax[] = ATTR_BOLD "Syntax" ATTR_OFF ": rpn [command]\nwith optional command = list of commands"; + +#define 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 +/// +void program::RpnNop() { + // nop +} + +/// @brief quit keyword implementation +/// +void program::RpnQuit() { ERROR_CONTEXT(kGoodbye); } + +/// @brief nop keyword implementation +/// the result is written on stdout +/// +void program::RpnHelp() { + // software name + cout << endl << ATTR_BOLD << RPN_UNAME << ATTR_OFF << endl; + + // _description + cout << _description << endl << endl; + + // _syntax + cout << _syntax << endl; + + // keywords + unsigned int i = 0; + for (auto& kw : keywords_) + if (!kw.comment.empty()) { + // titles in bold + if (kw.type == kUndef) cout << ATTR_BOLD; + // show title or keyword + comment + cout << kw.name << '\t' << kw.comment << endl; + if (kw.type == kUndef) cout << ATTR_OFF; + } + cout << endl; + + // show mode + cout << "Current float mode is "; + switch (Number::mode) { + case Number::kStd: + cout << "'std'"; + break; + case Number::kFix: + cout << "'fix'"; + break; + case Number::kSci: + cout << "'sci'"; + break; + default: + cout << "unknown"; + break; + } + + // bits precision, decimal digits and rounding mode + cout << " with " << Number::digits << " digits after the decimal point" << endl; + cout << "Current floating point precision is " << static_cast(mpreal::get_default_prec()) << " bits" << endl; + vector> rnd{MPFR_ROUND}; + for (auto& rn : rnd) + if (rn.second == mpreal::get_default_rnd()) { + cout << "Current rounding mode is '" << rn.first << '\'' << endl; + break; + } + cout << endl << endl; +} + +/// @brief whether a printed precision is in the precision min/max +/// +/// @param precision the precision in digits +/// @return true the precision is good +/// @return false the precision is not good +/// +static bool check_decimal_digits(int precision) { return precision >= 0; } + +/// @brief std keyword implementation +/// +void program::RpnStd() { + MIN_ARGUMENTS(1); + ARG_MUST_BE_OF_TYPE(0, kNumber); + + int digits = static_cast(stack_.value(0).toLong()); + + if (check_decimal_digits(digits)) { + // set mode, decimal digits and print format + Number::mode = Number::kStd; + Number::digits = digits; + stack_.pop(); + } else { + ERROR_CONTEXT(kOutOfRange); + } +} + +/// @brief fix keyword implementation +/// +void program::RpnFix() { + MIN_ARGUMENTS(1); + ARG_MUST_BE_OF_TYPE(0, kNumber); + + int digits = static_cast(stack_.value(0).toLong()); + + if (check_decimal_digits(digits)) { + // set mode, decimal digits and print format + Number::mode = Number::kFix; + Number::digits = digits; + stack_.pop(); + } else { + ERROR_CONTEXT(kOutOfRange); + } +} + +/// @brief sci keyword implementation +/// +void program::RpnSci() { + MIN_ARGUMENTS(1); + ARG_MUST_BE_OF_TYPE(0, kNumber); + + int digits = static_cast(stack_.value(0).toLong()); + + if (check_decimal_digits(digits)) { + // set mode, decimal digits and print format + Number::mode = Number::kSci; + Number::digits = digits; + stack_.pop(); + } else { + ERROR_CONTEXT(kOutOfRange); + } +} + +/// @brief _version keyword implementation +/// +void program::RpnVersion() { stack_.push_front(new String(RPN_VERSION)); } + +/// @brief _uname keyword implementation +/// +void program::RpnUname() { stack_.push_front(new String(RPN_UNAME)); } + +/// @brief history keyword implementation +/// +void program::RpnHistory() { + // see command history on stdout + int index = 0; + char* line = linenoiseHistoryLine(index); + while (line != nullptr) { + cout << line << endl; + free(line); + line = linenoiseHistoryLine(++index); + } +} + +/// @brief type keyword implementation +/// +void program::RpnType() { + MIN_ARGUMENTS(1); + stack_.push(new String(stack_.at(0)->Name())); + stack_.erase(1); +} + +/// @brief default keyword implementation +/// +void program::RpnDefault() { program::ApplyDefault(); } + +/// @brief prec keyword implementation +/// +void program::RpnPrecision() { + MIN_ARGUMENTS(1); + ARG_MUST_BE_OF_TYPE(0, kNumber); + + // set precision + int prec = static_cast(stack_.value(0).toLong()); + if (prec >= MPFR_PREC_MIN && prec <= MPFR_PREC_MAX) { + mpreal::set_default_prec(prec); + + // modify digits seen by user if std mode + if (Number::mode == Number::kStd) { + // calc max nb of digits user can see with the current bit precision + Number::digits = mpfr::bits2digits(mpreal::get_default_prec()); + } + stack_.pop(); + } else { + ERROR_CONTEXT(kOutOfRange); + } +} + +/// @brief round keyword implementation +/// +void program::RpnRound() { + MIN_ARGUMENTS(1); + ARG_MUST_BE_OF_TYPE(0, kString); + + map matchRound{MPFR_ROUND}; + + auto found = matchRound.find(stack_.value(0)); + if (found != matchRound.end()) + mpreal::set_default_rnd(found->second); + else + ERROR_CONTEXT(kOutOfRange); + stack_.pop(); +} diff --git a/src/rpn-general.cpp b/src/rpn-general.cpp deleted file mode 100644 index cd04390..0000000 --- a/src/rpn-general.cpp +++ /dev/null @@ -1,234 +0,0 @@ -// Copyright (c) 2014-2022 Louis Rubet - -#include -#include - -#include "linenoise.h" -using namespace std; - -#include "program.hpp" -#include "version.h" - -// description -static const string ATTR_BOLD = "\33[1m"; -static const string ATTR_OFF = "\33[0m"; - -#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\nusing " + 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\nand " + - ATTR_BOLD + "linenoise-ng" + ATTR_OFF + " v" LINENOISE_VERSION " under BSD\n"}; - -// syntax -static const string _syntax{ATTR_BOLD + "Syntax" + ATTR_OFF + - ": rpn [command]\nwith optional command = list of commands"}; - -static const map _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 -/// -void program::rpn_nop() { - // nop -} - -/// @brief quit keyword implementation -/// -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 << RPN_UNAME << ATTR_OFF << endl; - - // _description - cout << _description << endl << endl; - - // _syntax - cout << _syntax << endl; - - // keywords - unsigned int i = 0; - for (auto& kw : _keywords) - if (!kw.comment.empty()) { - // titles in bold - if (kw.type == cmd_undef) cout << ATTR_BOLD; - // show title or keyword + comment - cout << kw.name << '\t' << kw.comment << endl; - if (kw.type == cmd_undef) cout << ATTR_OFF; - } - cout << endl; - - // show mode - cout << "Current float mode is "; - switch (number::s_mode) { - case number::std: - cout << "'std'"; - break; - case number::fix: - cout << "'fix'"; - break; - case number::sci: - cout << "'sci'"; - break; - default: - cout << "unknown"; - break; - } - - // bits precision, decimal digits and rounding mode - cout << " with " << number::s_digits << " digits after the decimal point" << endl; - cout << "Current floating point precision is " << static_cast(mpreal::get_default_prec()) << " bits" << 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; -} - -/// @brief whether a printed precision is in the precision min/max -/// -/// @param precision the precision in digits -/// @return true the precision is good -/// @return false the precision is not good -/// -static bool check_decimal_digits(int precision) { - return precision >= 0; -} - -/// @brief std keyword implementation -/// -void program::rpn_std() { - MIN_ARGUMENTS(1); - ARG_MUST_BE_OF_TYPE(0, cmd_number); - - int digits = static_cast(_stack.value(0).toLong()); - - if (check_decimal_digits(digits)) { - // set mode, decimal digits and print format - number::s_mode = number::std; - number::s_digits = digits; - _stack.pop(); - } else { - setErrorContext(ret_out_of_range); - } -} - -/// @brief fix keyword implementation -/// -void program::rpn_fix() { - MIN_ARGUMENTS(1); - ARG_MUST_BE_OF_TYPE(0, cmd_number); - - int digits = static_cast(_stack.value(0).toLong()); - - if (check_decimal_digits(digits)) { - // set mode, decimal digits and print format - number::s_mode = number::fix; - number::s_digits = digits; - _stack.pop(); - } else { - setErrorContext(ret_out_of_range); - } -} - -/// @brief sci keyword implementation -/// -void program::rpn_sci() { - MIN_ARGUMENTS(1); - ARG_MUST_BE_OF_TYPE(0, cmd_number); - - int digits = static_cast(_stack.value(0).toLong()); - - if (check_decimal_digits(digits)) { - // set mode, decimal digits and print format - number::s_mode = number::sci; - number::s_digits = digits; - _stack.pop(); - } else { - setErrorContext(ret_out_of_range); - } -} - -/// @brief _version keyword implementation -/// -void program::rpn_version() { _stack.push_front(new ostring(RPN_VERSION)); } - -/// @brief _uname keyword implementation -/// -void program::rpn_uname() { _stack.push_front(new ostring(RPN_UNAME)); } - -/// @brief history keyword implementation -/// -void program::rpn_history() { - // see command history on stdout - int index = 0; - char* line = linenoiseHistoryLine(index); - while (line != nullptr) { - cout << line << endl; - free(line); - line = linenoiseHistoryLine(++index); - } -} - -/// @brief type keyword implementation -/// -void program::rpn_type() { - MIN_ARGUMENTS(1); - _stack.push(new ostring(_stack.at(0)->name())); - _stack.erase(1); -} - -/// @brief default keyword implementation -/// -void program::rpn_default() { program::apply_default(); } - -/// @brief prec keyword implementation -/// -void program::rpn_precision() { - MIN_ARGUMENTS(1); - ARG_MUST_BE_OF_TYPE(0, cmd_number); - - // set precision - int prec = static_cast(_stack.value(0).toLong()); - if (prec >= MPFR_PREC_MIN && prec <= MPFR_PREC_MAX) { - mpreal::set_default_prec(prec); - - // modify digits seen by user if std mode - if (number::s_mode == number::std) { - // calc max nb of digits user can see with the current bit precision - number::s_digits = bits2digits(mpreal::get_default_prec()); - } - _stack.pop(); - } else { - setErrorContext(ret_out_of_range); - } -} - -/// @brief round keyword implementation -/// -void program::rpn_round() { - MIN_ARGUMENTS(1); - ARG_MUST_BE_OF_TYPE(0, cmd_string); - - map matchRound{_mpfr_round}; - - auto found = matchRound.find(_stack.value(0)); - if (found != matchRound.end()) - mpreal::set_default_rnd(found->second); - else - setErrorContext(ret_out_of_range); - _stack.pop(); -} diff --git a/src/rpn-logs.cc b/src/rpn-logs.cc new file mode 100644 index 0000000..379ad79 --- /dev/null +++ b/src/rpn-logs.cc @@ -0,0 +1,175 @@ +// Copyright (c) 2014-2022 Louis Rubet + +#include "program.h" + +/// @brief e keyword implementation +/// +void program::RpnE(void) { stack_.push(new Number(mpfr::const_euler())); } + +/// @brief log10 keyword implementation +/// +void program::RpnLog10() { + MIN_ARGUMENTS(1); + if (stack_.type(0) == kNumber) + stack_.value(0) = log10(stack_.value(0)); + else if (stack_.type(0) == kComplex) + stack_.value(0) = log10(stack_.value(0)); + else + ERROR_CONTEXT(kBadOperandType); +} + +/// @brief alog10 keyword implementation +/// +void program::RpnAlog10() { + MIN_ARGUMENTS(1); + if (stack_.type(0) == kNumber) + stack_.value(0) = exp(log(mpreal(10)) * stack_.value(0)); + else if (stack_.type(0) == kComplex) + stack_.value(0) = exp(log(mpreal(10)) * stack_.value(0)); + else + ERROR_CONTEXT(kBadOperandType); +} + +/// @brief log2 keyword implementation +/// +void program::RpnLog2() { + MIN_ARGUMENTS(1); + if (stack_.type(0) == kNumber) + stack_.value(0) = log(stack_.value(0)) / mpfr::const_log2(); + else if (stack_.type(0) == kComplex) + stack_.value(0) = log(stack_.value(0)) / mpfr::const_log2(); + else + ERROR_CONTEXT(kBadOperandType); +} + +/// @brief alog2 keyword implementation +/// +void program::RpnAlog2() { + MIN_ARGUMENTS(1); + if (stack_.type(0) == kNumber) + stack_.value(0) = exp(mpfr::const_log2() * stack_.value(0)); + else if (stack_.type(0) == kComplex) + stack_.value(0) = exp(mpfr::const_log2() * stack_.value(0)); + else + ERROR_CONTEXT(kBadOperandType); +} + +/// @brief ln keyword implementation +/// +void program::RpnLn() { + MIN_ARGUMENTS(1); + if (stack_.type(0) == kNumber) + stack_.value(0) = log(stack_.value(0)); + else if (stack_.type(0) == kComplex) + stack_.value(0) = log(stack_.value(0)); + else + ERROR_CONTEXT(kBadOperandType); +} + +/// @brief exp keyword implementation +/// +void program::RpnExp() { + MIN_ARGUMENTS(1); + if (stack_.type(0) == kNumber) + stack_.value(0) = exp(stack_.value(0)); + else if (stack_.type(0) == kComplex) + stack_.value(0) = exp(stack_.value(0)); + else + ERROR_CONTEXT(kBadOperandType); +} + +/// @brief expm keyword implementation +/// +void program::RpnExpm() { + MIN_ARGUMENTS(1); + if (stack_.type(0) == kNumber) + stack_.value(0) = exp(stack_.value(0)) - mpreal(1); + else if (stack_.type(0) == kComplex) + stack_.value(0) = exp(stack_.value(0)) - mpreal(1); + else + ERROR_CONTEXT(kBadOperandType); +} + +/// @brief lnp1 keyword implementation +/// +void program::RpnLnp1() { + MIN_ARGUMENTS(1); + if (stack_.type(0) == kNumber) + stack_.value(0) = log(stack_.value(0) + 1); + else if (stack_.type(0) == kComplex) + stack_.value(0) = log(stack_.value(0) + mpreal(1)); + else + ERROR_CONTEXT(kBadOperandType); +} + +/// @brief sinh keyword implementation +/// +void program::RpnSinh() { + MIN_ARGUMENTS(1); + if (stack_.type(0) == kNumber) + stack_.value(0) = sinh(stack_.value(0)); + else if (stack_.type(0) == kComplex) + stack_.value(0) = sinh(stack_.value(0)); + else + ERROR_CONTEXT(kBadOperandType); +} + +/// @brief asinh keyword implementation +/// +void program::RpnAsinh() { + MIN_ARGUMENTS(1); + if (stack_.type(0) == kNumber) + stack_.value(0) = asinh(stack_.value(0)); + else if (stack_.type(0) == kComplex) + stack_.value(0) = asinh(stack_.value(0)); + else + ERROR_CONTEXT(kBadOperandType); +} + +/// @brief cosh keyword implementation +/// +void program::RpnCosh() { + MIN_ARGUMENTS(1); + if (stack_.type(0) == kNumber) + stack_.value(0) = cosh(stack_.value(0)); + else if (stack_.type(0) == kComplex) + stack_.value(0) = cosh(stack_.value(0)); + else + ERROR_CONTEXT(kBadOperandType); +} + +/// @brief acosh keyword implementation +/// +void program::RpnAcosh() { + MIN_ARGUMENTS(1); + if (stack_.type(0) == kNumber) + stack_.value(0) = acosh(stack_.value(0)); + else if (stack_.type(0) == kComplex) + stack_.value(0) = acosh(stack_.value(0)); + else + ERROR_CONTEXT(kBadOperandType); +} + +/// @brief tanh keyword implementation +/// +void program::RpnTanh() { + MIN_ARGUMENTS(1); + if (stack_.type(0) == kNumber) + stack_.value(0) = tanh(stack_.value(0)); + else if (stack_.type(0) == kComplex) + stack_.value(0) = tanh(stack_.value(0)); + else + ERROR_CONTEXT(kBadOperandType); +} + +/// @brief atanh keyword implementation +/// +void program::RpnAtanh() { + MIN_ARGUMENTS(1); + if (stack_.type(0) == kNumber) + stack_.value(0) = atanh(stack_.value(0)); + else if (stack_.type(0) == kComplex) + stack_.value(0) = atanh(stack_.value(0)); + else + ERROR_CONTEXT(kBadOperandType); +} diff --git a/src/rpn-logs.cpp b/src/rpn-logs.cpp deleted file mode 100644 index 0ee199b..0000000 --- a/src/rpn-logs.cpp +++ /dev/null @@ -1,175 +0,0 @@ -// Copyright (c) 2014-2022 Louis Rubet - -#include "program.hpp" - -/// @brief e keyword implementation -/// -void program::rpn_e(void) { _stack.push(new number(const_euler())); } - -/// @brief log10 keyword implementation -/// -void program::rpn_log10() { - MIN_ARGUMENTS(1); - if (_stack.type(0) == cmd_number) - _stack.value(0) = log10(_stack.value(0)); - else if (_stack.type(0) == cmd_complex) - _stack.value(0) = log10(_stack.value(0)); - else - setErrorContext(ret_bad_operand_type); -} - -/// @brief alog10 keyword implementation -/// -void program::rpn_alog10() { - MIN_ARGUMENTS(1); - if (_stack.type(0) == cmd_number) - _stack.value(0) = exp(log(mpreal(10)) * _stack.value(0)); - else if (_stack.type(0) == cmd_complex) - _stack.value(0) = exp(log(mpreal(10)) * _stack.value(0)); - else - setErrorContext(ret_bad_operand_type); -} - -/// @brief log2 keyword implementation -/// -void program::rpn_log2() { - MIN_ARGUMENTS(1); - if (_stack.type(0) == cmd_number) - _stack.value(0) = log(_stack.value(0)) / const_log2(); - else if (_stack.type(0) == cmd_complex) - _stack.value(0) = log(_stack.value(0)) / const_log2(); - else - setErrorContext(ret_bad_operand_type); -} - -/// @brief alog2 keyword implementation -/// -void program::rpn_alog2() { - MIN_ARGUMENTS(1); - if (_stack.type(0) == cmd_number) - _stack.value(0) = exp(const_log2() * _stack.value(0)); - else if (_stack.type(0) == cmd_complex) - _stack.value(0) = exp(const_log2() * _stack.value(0)); - else - setErrorContext(ret_bad_operand_type); -} - -/// @brief ln keyword implementation -/// -void program::rpn_ln() { - MIN_ARGUMENTS(1); - if (_stack.type(0) == cmd_number) - _stack.value(0) = log(_stack.value(0)); - else if (_stack.type(0) == cmd_complex) - _stack.value(0) = log(_stack.value(0)); - else - setErrorContext(ret_bad_operand_type); -} - -/// @brief exp keyword implementation -/// -void program::rpn_exp() { - MIN_ARGUMENTS(1); - if (_stack.type(0) == cmd_number) - _stack.value(0) = exp(_stack.value(0)); - else if (_stack.type(0) == cmd_complex) - _stack.value(0) = exp(_stack.value(0)); - else - setErrorContext(ret_bad_operand_type); -} - -/// @brief expm keyword implementation -/// -void program::rpn_expm() { - MIN_ARGUMENTS(1); - if (_stack.type(0) == cmd_number) - _stack.value(0) = exp(_stack.value(0)) - mpreal(1); - else if (_stack.type(0) == cmd_complex) - _stack.value(0) = exp(_stack.value(0)) - mpreal(1); - else - setErrorContext(ret_bad_operand_type); -} - -/// @brief lnp1 keyword implementation -/// -void program::rpn_lnp1() { - MIN_ARGUMENTS(1); - if (_stack.type(0) == cmd_number) - _stack.value(0) = log(_stack.value(0) + 1); - else if (_stack.type(0) == cmd_complex) - _stack.value(0) = log(_stack.value(0) + mpreal(1)); - else - setErrorContext(ret_bad_operand_type); -} - -/// @brief sinh keyword implementation -/// -void program::rpn_sinh() { - MIN_ARGUMENTS(1); - if (_stack.type(0) == cmd_number) - _stack.value(0) = sinh(_stack.value(0)); - else if (_stack.type(0) == cmd_complex) - _stack.value(0) = sinh(_stack.value(0)); - else - setErrorContext(ret_bad_operand_type); -} - -/// @brief asinh keyword implementation -/// -void program::rpn_asinh() { - MIN_ARGUMENTS(1); - if (_stack.type(0) == cmd_number) - _stack.value(0) = asinh(_stack.value(0)); - else if (_stack.type(0) == cmd_complex) - _stack.value(0) = asinh(_stack.value(0)); - else - setErrorContext(ret_bad_operand_type); -} - -/// @brief cosh keyword implementation -/// -void program::rpn_cosh() { - MIN_ARGUMENTS(1); - if (_stack.type(0) == cmd_number) - _stack.value(0) = cosh(_stack.value(0)); - else if (_stack.type(0) == cmd_complex) - _stack.value(0) = cosh(_stack.value(0)); - else - setErrorContext(ret_bad_operand_type); -} - -/// @brief acosh keyword implementation -/// -void program::rpn_acosh() { - MIN_ARGUMENTS(1); - if (_stack.type(0) == cmd_number) - _stack.value(0) = acosh(_stack.value(0)); - else if (_stack.type(0) == cmd_complex) - _stack.value(0) = acosh(_stack.value(0)); - else - setErrorContext(ret_bad_operand_type); -} - -/// @brief tanh keyword implementation -/// -void program::rpn_tanh() { - MIN_ARGUMENTS(1); - if (_stack.type(0) == cmd_number) - _stack.value(0) = tanh(_stack.value(0)); - else if (_stack.type(0) == cmd_complex) - _stack.value(0) = tanh(_stack.value(0)); - else - setErrorContext(ret_bad_operand_type); -} - -/// @brief atanh keyword implementation -/// -void program::rpn_atanh() { - MIN_ARGUMENTS(1); - if (_stack.type(0) == cmd_number) - _stack.value(0) = atanh(_stack.value(0)); - else if (_stack.type(0) == cmd_complex) - _stack.value(0) = atanh(_stack.value(0)); - else - setErrorContext(ret_bad_operand_type); -} diff --git a/src/rpn-program.cc b/src/rpn-program.cc new file mode 100644 index 0000000..c4d0199 --- /dev/null +++ b/src/rpn-program.cc @@ -0,0 +1,152 @@ +// Copyright (c) 2014-2022 Louis Rubet + +#include "program.h" + +/// @brief find variable by its name in local heap, successive parents heaps, global heap +/// +/// @param variable the variable name to find +/// @param obj the variable object found +/// @return true variable was found +/// @return false variable was not found +/// +bool program::FindVariable(string& variable, Object*& obj) { + bool found = false; + program* parent = parent_; + + if (local_heap_.get(variable, obj)) { + found = true; + } else { + while (parent != nullptr) { + if (parent->local_heap_.get(variable, obj)) { + found = true; + break; + } + parent = parent->parent_; + } + if (!found) { + if (heap_.get(variable, obj)) found = true; + } + } + + return found; +} + +/// @brief eval keyword implementation +/// +void program::RpnEval(void) { + bool run_prog = false; + string prog_text; + + MIN_ARGUMENTS(1); + if (stack_.type(0) == kSymbol) { + // recall a variable + Object* obj; + string variable(stack_.value(0)); + stack_.pop(); + + // if variable holds a program, run this program + if (FindVariable(variable, obj)) { + if (obj->_type == kProgram) { + prog_text = stack_.value(0); + stack_.pop(); + run_prog = true; + } else { + // else recall this variable (i.e. stack_ its content) + stack_.push_front(obj); + } + } else { + ERROR_CONTEXT(kUnknownVariable); + } + } else if (stack_.type(0) == kProgram) { + // eval a program + prog_text = stack_.value(0); + stack_.pop(); + run_prog = true; + } else { + ERROR_CONTEXT(kBadOperandType); + } + + // run prog if any + if (run_prog) { + program prog(stack_, heap_, this); + + // make program from entry + if (prog.Parse(prog_text) == kOk) { + // run it + prog.Run(); + } + } +} + +/// @brief -> keyword (Branch) implementation +/// +int program::RpnInprog(Branch& inprog_obj) { + string context("->"); // for showing errors + int count_symbols = 0; + bool prog_found = false; + + if (inprog_obj.arg1 == -1) { + ERROR_CONTEXT(kUnknownError); + return -1; + } + + // syntax must be + // -> ... + + // find next Program object + for (unsigned int i = inprog_obj.arg1 + 1; i < size(); i++) { + // count symbol + if (at(i)->_type == kSymbol) { + count_symbols++; + } else if (at(i)->_type == kProgram) { + // stop if prog + prog_found = true; + break; + } else { + // found something other than symbol + ERROR_CONTEXT(kBadOperandType); + ShowError(err_, context); + return -1; + } + } + + // found 0 symbols + if (count_symbols == 0) { + ERROR_CONTEXT(kSyntaxError); + ShowError(err_, context); + return -1; + } + + // is missing + if (!prog_found) { + ERROR_CONTEXT(kSyntaxError); + ShowError(err_, context); + return -1; + } + + // check symbols Number vs stack_ size + if (stack_.size() < count_symbols) { + ERROR_CONTEXT(kMissingOperand); + ShowError(err_, context); + return -1; + } + + // load variables + for (unsigned int i = inprog_obj.arg1 + count_symbols; i > inprog_obj.arg1; i--) { + local_heap_[reinterpret_cast(at(i))->value] = stack_.at(0)->Clone(); + stack_.pop(); + } + + // run the program + string& entry = reinterpret_cast(at(inprog_obj.arg1 + count_symbols + 1))->value; + program prog(stack_, heap_, this); + + // make the program from entry + if (prog.Parse(entry) == kOk) { + // run it + prog.Run(); + } + + // point on next command + return inprog_obj.arg1 + count_symbols + 2; +} diff --git a/src/rpn-program.cpp b/src/rpn-program.cpp deleted file mode 100644 index d6ea074..0000000 --- a/src/rpn-program.cpp +++ /dev/null @@ -1,152 +0,0 @@ -// Copyright (c) 2014-2022 Louis Rubet - -#include "program.hpp" - -/// @brief find variable by its name in local heap, successive parents heaps, global heap -/// -/// @param variable the variable name to find -/// @param obj the variable object found -/// @return true variable was found -/// @return false variable was not found -/// -bool program::find_variable(string& variable, object*& obj) { - bool found = false; - program* parent = _parent; - - if (_local_heap.get(variable, obj)) { - found = true; - } else { - while (parent != nullptr) { - if (parent->_local_heap.get(variable, obj)) { - found = true; - break; - } - parent = parent->_parent; - } - if (!found) { - if (_heap.get(variable, obj)) found = true; - } - } - - return found; -} - -/// @brief eval keyword implementation -/// -void program::rpn_eval(void) { - bool run_prog = false; - string prog_text; - - MIN_ARGUMENTS(1); - if (_stack.type(0) == cmd_symbol) { - // recall a variable - object* obj; - string variable(_stack.value(0)); - _stack.pop(); - - // if variable holds a program, run this program - if (find_variable(variable, obj)) { - if (obj->_type == cmd_program) { - prog_text = _stack.value(0); - _stack.pop(); - run_prog = true; - } else { - // else recall this variable (i.e. stack its content) - _stack.push_front(obj); - } - } else { - setErrorContext(ret_unknown_variable); - } - } else if (_stack.type(0) == cmd_program) { - // eval a program - prog_text = _stack.value(0); - _stack.pop(); - run_prog = true; - } else { - setErrorContext(ret_bad_operand_type); - } - - // run prog if any - if (run_prog) { - program prog(_stack, _heap, this); - - // make program from entry - if (prog.parse(prog_text) == ret_ok) { - // run it - prog.run(); - } - } -} - -/// @brief -> keyword (branch) implementation -/// -int program::rpn_inprog(branch& myobj) { - string context("->"); // for showing errors - int count_symbols = 0; - bool prog_found = false; - - if (myobj.arg1 == -1) { - setErrorContext(ret_unknown_err); - return -1; - } - - // syntax must be - // -> ... - - // find next oprogram object - for (unsigned int i = myobj.arg1 + 1; i < size(); i++) { - // count symbol - if (at(i)->_type == cmd_symbol) { - count_symbols++; - } else if (at(i)->_type == cmd_program) { - // stop if prog - prog_found = true; - break; - } else { - // found something other than symbol - setErrorContext(ret_bad_operand_type); - show_error(_err, context); - return -1; - } - } - - // found 0 symbols - if (count_symbols == 0) { - setErrorContext(ret_syntax); - show_error(_err, context); - return -1; - } - - // is missing - if (!prog_found) { - setErrorContext(ret_syntax); - show_error(_err, context); - return -1; - } - - // check symbols number vs stack size - if (_stack.size() < count_symbols) { - 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[reinterpret_cast(at(i))->value] = _stack.at(0)->clone(); - _stack.pop(); - } - - // run the program - string& entry = reinterpret_cast(at(myobj.arg1 + count_symbols + 1))->value; - program prog(_stack, _heap, this); - - // make the program from entry - if (prog.parse(entry) == ret_ok) { - // run it - prog.run(); - } - - // point on next command - return myobj.arg1 + count_symbols + 2; -} diff --git a/src/rpn-real.cc b/src/rpn-real.cc new file mode 100644 index 0000000..93f9901 --- /dev/null +++ b/src/rpn-real.cc @@ -0,0 +1,389 @@ +// Copyright (c) 2014-2022 Louis Rubet + +#include "program.h" + +/// @brief + keyword implementation +/// +void program::RpnPlus() { + MIN_ARGUMENTS(2); + if (stack_.type(0) == kString && stack_.type(1) == kString) { + stack_.value(1) += stack_.value(0); + stack_.pop(); + } else if (stack_.type(0) == kNumber && stack_.type(1) == kNumber) { + stack_.value(1) += stack_.value(0); + stack_.pop(); + } else if (stack_.type(0) == kComplex && stack_.type(1) == kComplex) { + stack_.value(1) += stack_.value(0); + stack_.pop(); + } else if (stack_.type(0) == kNumber && stack_.type(1) == kComplex) { + stack_.value(1) += stack_.value(0); + stack_.pop(); + } else if (stack_.type(0) == kComplex && stack_.type(1) == kNumber) { + RpnSwap(); + stack_.value(1) += stack_.value(0); + stack_.pop(); + } else { + ERROR_CONTEXT(kBadOperandType); + } +} + +/// @brief - keyword implementation +/// +void program::RpnMinus() { + MIN_ARGUMENTS(2); + if (stack_.type(0) == kNumber && stack_.type(1) == kNumber) { + stack_.value(1) -= stack_.value(0); + stack_.pop(); + } else if (stack_.type(0) == kComplex && stack_.type(1) == kComplex) { + stack_.value(1) -= stack_.value(0); + stack_.pop(); + } else if (stack_.type(0) == kNumber && stack_.type(1) == kComplex) { + stack_.value(1) -= stack_.value(0); + stack_.pop(); + } else if (stack_.type(0) == kComplex && stack_.type(1) == kNumber) { + RpnSwap(); + stack_.value(1) = stack_.value(0) - stack_.value(1); + stack_.pop(); + } else { + ERROR_CONTEXT(kBadOperandType); + } +} + +/// @brief * keyword implementation +/// +void program::RpnMul() { + MIN_ARGUMENTS(2); + if (stack_.type(0) == kNumber && stack_.type(1) == kNumber) { + stack_.value(1) *= stack_.value(0); + stack_.pop(); + } else if (stack_.type(0) == kComplex && stack_.type(1) == kComplex) { + stack_.value(1) *= stack_.value(0); + stack_.pop(); + } else if (stack_.type(0) == kNumber && stack_.type(1) == kComplex) { + stack_.value(1) *= stack_.value(0); + stack_.pop(); + } else if (stack_.type(0) == kComplex && stack_.type(1) == kNumber) { + RpnSwap(); + stack_.value(1) *= stack_.value(0); + stack_.pop(); + } else { + ERROR_CONTEXT(kBadOperandType); + } +} + +/// @brief / keyword implementation +/// +void program::RpnDiv() { + MIN_ARGUMENTS(2); + if (stack_.type(0) == kNumber && stack_.type(1) == kNumber) { + stack_.value(1) /= stack_.value(0); + stack_.pop(); + } else if (stack_.type(0) == kComplex && stack_.type(1) == kComplex) { + stack_.value(1) /= stack_.value(0); + stack_.pop(); + } else if (stack_.type(0) == kNumber && stack_.type(1) == kComplex) { + stack_.value(1) /= stack_.value(0); + stack_.pop(); + } else if (stack_.type(0) == kComplex && stack_.type(1) == kNumber) { + RpnSwap(); + stack_.value(1) = stack_.value(0) / stack_.value(1); + stack_.pop(); + } else { + ERROR_CONTEXT(kBadOperandType); + } +} + +/// @brief neg keyword implementation +/// +void program::RpnNeg() { + MIN_ARGUMENTS(1); + if (stack_.type(0) == kNumber) + stack_.value(0) = -stack_.value(0); + else if (stack_.type(0) == kComplex) + stack_.value(0) = -stack_.value(0); + else + ERROR_CONTEXT(kBadOperandType); +} + +/// @brief inv keyword implementation +/// +void program::RpnInv() { + MIN_ARGUMENTS(1); + if (stack_.type(0) == kNumber) + stack_.value(0) = 1 / stack_.value(0); + else if (stack_.type(0) == kComplex) + stack_.value(0) = mpreal(1) / stack_.value(0); + else + ERROR_CONTEXT(kBadOperandType); +} + +/// @brief power keyword implementation +/// +void program::RpnPower() { + MIN_ARGUMENTS(2); + if (stack_.type(0) == kNumber && stack_.type(1) == kNumber) { + if (stack_.value(1) >= 0) { + stack_.value(1) = pow(stack_.value(1), stack_.value(0)); + stack_.pop(); + } else { + mpreal zero; + stack_.push(new Complex(stack_.value(1), zero, stack_.obj(1).base)); + stack_.value(0) = pow(stack_.value(0), stack_.value(1)); + stack_.erase(1, 2); + } + } else if (stack_.type(0) == kComplex && stack_.type(1) == kComplex) { + stack_.value(1) = pow(stack_.value(1), stack_.value(0)); + stack_.pop(); + } else if (stack_.type(0) == kNumber && stack_.type(1) == kComplex) { + stack_.value(1) = pow(stack_.value(1), stack_.value(0)); + stack_.pop(); + } else if (stack_.type(0) == kComplex && stack_.type(1) == kNumber) { + RpnSwap(); + stack_.value(1) = pow(stack_.value(0), stack_.value(1)); + stack_.pop(); + } else { + ERROR_CONTEXT(kBadOperandType); + } +} + +/// @brief sqrt keyword implementation +/// +void program::RpnSquareroot() { + MIN_ARGUMENTS(1); + if (stack_.type(0) == kNumber) { + if (stack_.value(0) >= 0) { + stack_.value(0) = sqrt(stack_.value(0)); + } else { + // negative number -> square root is complex + mpreal zero; + stack_.push(new Complex(stack_.value(0), zero, + stack_.obj(0).base)); // TODO(louis) manage new errors + stack_.value(0) = sqrt(stack_.value(0)); + stack_.erase(1); + } + } else if (stack_.type(0) == kComplex) { + stack_.value(0) = sqrt(stack_.value(0)); + } else { + ERROR_CONTEXT(kBadOperandType); + } +} + +/// @brief hex keyword implementation +/// +void program::RpnHex() { + MIN_ARGUMENTS(1); + if (stack_.type(0) == kNumber) { + stack_.obj(0).base = 16; + } else if (stack_.type(0) == kComplex) { + stack_.obj(0).re_base = 16; + stack_.obj(0).im_base = 16; + } else { + ERROR_CONTEXT(kBadOperandType); + } +} + +/// @brief bin keyword implementation +/// +void program::RpnBin() { + MIN_ARGUMENTS(1); + if (stack_.type(0) == kNumber) { + stack_.obj(0).base = 2; + } else if (stack_.type(0) == kComplex) { + stack_.obj(0).re_base = 2; + stack_.obj(0).im_base = 2; + } else { + ERROR_CONTEXT(kBadOperandType); + } +} + +/// @brief dec keyword implementation +/// +void program::RpnDec() { + MIN_ARGUMENTS(1); + if (stack_.type(0) == kNumber) { + stack_.obj(0).base = 10; + } else if (stack_.type(0) == kComplex) { + stack_.obj(0).re_base = 10; + stack_.obj(0).im_base = 10; + } else { + ERROR_CONTEXT(kBadOperandType); + } +} + +/// @brief base keyword implementation +/// +void program::RpnBase() { + MIN_ARGUMENTS(2); + if (stack_.type(1) == kNumber || stack_.type(1) == kComplex) { + int base = static_cast(stack_.value(0).toLong()); + stack_.pop(); + if (base >= 2 && base <= 62) { + if (stack_.type(0) == kNumber) { + stack_.obj(0).base = base; + } else { + stack_.obj(0).re_base = base; + stack_.obj(0).im_base = base; + } + } else { + ERROR_CONTEXT(kOutOfRange); + } + } else { + ERROR_CONTEXT(kBadOperandType); + } +} + +/// @brief % (purcent) keyword implementation +/// +void program::RpnPurcent() { + MIN_ARGUMENTS(2); + ARG_MUST_BE_OF_TYPE(0, kNumber); + ARG_MUST_BE_OF_TYPE(1, kNumber); + stack_.value(1) *= stack_.value(0) / 100; + stack_.pop(); +} + +/// @brief %CH keyword implementation +/// +void program::RpnPurcentCH() { + MIN_ARGUMENTS(2); + ARG_MUST_BE_OF_TYPE(0, kNumber); + ARG_MUST_BE_OF_TYPE(1, kNumber); + stack_.value(1) = (stack_.value(0) * 100) / stack_.value(1); + stack_.pop(); +} + +/// @brief sq keyword implementation +/// +void program::RpnSquare() { + MIN_ARGUMENTS(1); + if (stack_.type(0) == kNumber) + stack_.value(0) *= stack_.value(0); + else if (stack_.at(0)->_type == kComplex) + stack_.value(0) *= stack_.value(0); + else + ERROR_CONTEXT(kBadOperandType); +} + +/// @brief mod keyword implementation +/// +void program::RpnModulo() { + MIN_ARGUMENTS(2); + ARG_MUST_BE_OF_TYPE(0, kNumber); + ARG_MUST_BE_OF_TYPE(1, kNumber); + stack_.value(1) = fmod(stack_.value(1), stack_.value(0)); + stack_.pop(); +} + +/// @brief abs keyword implementation +/// +void program::RpnAbs() { + MIN_ARGUMENTS(1); + if (stack_.type(0) == kNumber) { + stack_.value(0) = abs(stack_.value(0)); + } else if (stack_.type(0) == kComplex) { + stack_.push(new Number(abs(stack_.value(0)))); + stack_.erase(1); + } else { + ERROR_CONTEXT(kBadOperandType); + } +} + +/// @brief fact (factorial) keyword implementation +/// +void program::RpnFact() { + MIN_ARGUMENTS(1); + ARG_MUST_BE_OF_TYPE(0, kNumber); + // fact(n) = gamma(n+1) + stack_.value(0) = gamma(stack_.value(0) + 1); +} + +/// @brief sign keyword implementation +/// +void program::RpnSign() { + MIN_ARGUMENTS(1); + if (stack_.type(0) == kNumber) + stack_.value(0) = sgn(stack_.value(0)); + else if (stack_.at(0)->_type == kComplex) + stack_.value(0) = stack_.value(0) / abs(stack_.value(0)); + else + ERROR_CONTEXT(kBadOperandType); +} + +/// @brief mant keyword implementation +/// +void program::RpnMant() { + MIN_ARGUMENTS(1); + ARG_MUST_BE_OF_TYPE(0, kNumber); + if (!isfinite(stack_.value(0))) { + ERROR_CONTEXT(kOutOfRange); + return; + } + mp_exp_t exp; + stack_.value(0) = frexp(stack_.value(0), &exp); +} + +/// @brief xpon keyword implementation +/// +void program::RpnXpon() { + MIN_ARGUMENTS(1); + ARG_MUST_BE_OF_TYPE(0, kNumber); + if (!isfinite(stack_.value(0))) { + ERROR_CONTEXT(kOutOfRange); + return; + } + mp_exp_t exp; + (void)frexp(stack_.value(0), &exp); + stack_.value(0) = exp; +} + +/// @brief floor keyword implementation +/// +void program::RpnFloor() { + MIN_ARGUMENTS(1); + ARG_MUST_BE_OF_TYPE(0, kNumber); + stack_.value(0) = floor(stack_.value(0)); +} + +/// @brief ceil keyword implementation +/// +void program::RpnCeil() { + MIN_ARGUMENTS(1); + ARG_MUST_BE_OF_TYPE(0, kNumber); + stack_.value(0) = ceil(stack_.value(0)); +} + +/// @brief fp keyword implementation +/// +void program::RpnFp() { + MIN_ARGUMENTS(1); + ARG_MUST_BE_OF_TYPE(0, kNumber); + stack_.value(0) = frac(stack_.value(0)); +} + +/// @brief ip keyword implementation +/// +void program::RpnIp() { + MIN_ARGUMENTS(1); + ARG_MUST_BE_OF_TYPE(0, kNumber); + stack_.value(0) = trunc(stack_.value(0)); +} + +/// @brief min keyword implementation +/// +void program::RpnMin() { + MIN_ARGUMENTS(2); + ARG_MUST_BE_OF_TYPE(0, kNumber); + ARG_MUST_BE_OF_TYPE(1, kNumber); + stack_.value(0) = min(stack_.value(0), stack_.value(1)); + stack_.erase(1); +} + +/// @brief max keyword implementation +/// +void program::RpnMax() { + MIN_ARGUMENTS(2); + ARG_MUST_BE_OF_TYPE(0, kNumber); + ARG_MUST_BE_OF_TYPE(1, kNumber); + stack_.value(0) = max(stack_.value(0), stack_.value(1)); + stack_.erase(1); +} diff --git a/src/rpn-real.cpp b/src/rpn-real.cpp deleted file mode 100644 index 471046e..0000000 --- a/src/rpn-real.cpp +++ /dev/null @@ -1,389 +0,0 @@ -// Copyright (c) 2014-2022 Louis Rubet - -#include "program.hpp" - -/// @brief + keyword implementation -/// -void program::rpn_plus() { - MIN_ARGUMENTS(2); - if (_stack.type(0) == cmd_string && _stack.type(1) == cmd_string) { - _stack.value(1) += _stack.value(0); - _stack.pop(); - } else if (_stack.type(0) == cmd_number && _stack.type(1) == cmd_number) { - _stack.value(1) += _stack.value(0); - _stack.pop(); - } else if (_stack.type(0) == cmd_complex && _stack.type(1) == cmd_complex) { - _stack.value(1) += _stack.value(0); - _stack.pop(); - } else if (_stack.type(0) == cmd_number && _stack.type(1) == cmd_complex) { - _stack.value(1) += _stack.value(0); - _stack.pop(); - } else if (_stack.type(0) == cmd_complex && _stack.type(1) == cmd_number) { - rpn_swap(); - _stack.value(1) += _stack.value(0); - _stack.pop(); - } else { - setErrorContext(ret_bad_operand_type); - } -} - -/// @brief - keyword implementation -/// -void program::rpn_minus() { - MIN_ARGUMENTS(2); - if (_stack.type(0) == cmd_number && _stack.type(1) == cmd_number) { - _stack.value(1) -= _stack.value(0); - _stack.pop(); - } else if (_stack.type(0) == cmd_complex && _stack.type(1) == cmd_complex) { - _stack.value(1) -= _stack.value(0); - _stack.pop(); - } else if (_stack.type(0) == cmd_number && _stack.type(1) == cmd_complex) { - _stack.value(1) -= _stack.value(0); - _stack.pop(); - } else if (_stack.type(0) == cmd_complex && _stack.type(1) == cmd_number) { - rpn_swap(); - _stack.value(1) = _stack.value(0) - _stack.value(1); - _stack.pop(); - } else { - setErrorContext(ret_bad_operand_type); - } -} - -/// @brief * keyword implementation -/// -void program::rpn_mul() { - MIN_ARGUMENTS(2); - if (_stack.type(0) == cmd_number && _stack.type(1) == cmd_number) { - _stack.value(1) *= _stack.value(0); - _stack.pop(); - } else if (_stack.type(0) == cmd_complex && _stack.type(1) == cmd_complex) { - _stack.value(1) *= _stack.value(0); - _stack.pop(); - } else if (_stack.type(0) == cmd_number && _stack.type(1) == cmd_complex) { - _stack.value(1) *= _stack.value(0); - _stack.pop(); - } else if (_stack.type(0) == cmd_complex && _stack.type(1) == cmd_number) { - rpn_swap(); - _stack.value(1) *= _stack.value(0); - _stack.pop(); - } else { - setErrorContext(ret_bad_operand_type); - } -} - -/// @brief / keyword implementation -/// -void program::rpn_div() { - MIN_ARGUMENTS(2); - if (_stack.type(0) == cmd_number && _stack.type(1) == cmd_number) { - _stack.value(1) /= _stack.value(0); - _stack.pop(); - } else if (_stack.type(0) == cmd_complex && _stack.type(1) == cmd_complex) { - _stack.value(1) /= _stack.value(0); - _stack.pop(); - } else if (_stack.type(0) == cmd_number && _stack.type(1) == cmd_complex) { - _stack.value(1) /= _stack.value(0); - _stack.pop(); - } else if (_stack.type(0) == cmd_complex && _stack.type(1) == cmd_number) { - rpn_swap(); - _stack.value(1) = _stack.value(0) / _stack.value(1); - _stack.pop(); - } else { - setErrorContext(ret_bad_operand_type); - } -} - -/// @brief neg keyword implementation -/// -void program::rpn_neg() { - MIN_ARGUMENTS(1); - if (_stack.type(0) == cmd_number) - _stack.value(0) = -_stack.value(0); - else if (_stack.type(0) == cmd_complex) - _stack.value(0) = -_stack.value(0); - else - setErrorContext(ret_bad_operand_type); -} - -/// @brief inv keyword implementation -/// -void program::rpn_inv() { - MIN_ARGUMENTS(1); - if (_stack.type(0) == cmd_number) - _stack.value(0) = 1 / _stack.value(0); - else if (_stack.type(0) == cmd_complex) - _stack.value(0) = mpreal(1) / _stack.value(0); - else - setErrorContext(ret_bad_operand_type); -} - -/// @brief power keyword implementation -/// -void program::rpn_power() { - MIN_ARGUMENTS(2); - if (_stack.type(0) == cmd_number && _stack.type(1) == cmd_number) { - if (_stack.value(1) >= 0) { - _stack.value(1) = pow(_stack.value(1), _stack.value(0)); - _stack.pop(); - } else { - mpreal zero; - _stack.push(new ocomplex(_stack.value(1), zero, _stack.obj(1).base)); - _stack.value(0) = pow(_stack.value(0), _stack.value(1)); - _stack.erase(1, 2); - } - } else if (_stack.type(0) == cmd_complex && _stack.type(1) == cmd_complex) { - _stack.value(1) = pow(_stack.value(1), _stack.value(0)); - _stack.pop(); - } else if (_stack.type(0) == cmd_number && _stack.type(1) == cmd_complex) { - _stack.value(1) = pow(_stack.value(1), _stack.value(0)); - _stack.pop(); - } else if (_stack.type(0) == cmd_complex && _stack.type(1) == cmd_number) { - rpn_swap(); - _stack.value(1) = pow(_stack.value(0), _stack.value(1)); - _stack.pop(); - } else { - setErrorContext(ret_bad_operand_type); - } -} - -/// @brief sqrt keyword implementation -/// -void program::rpn_squareroot() { - MIN_ARGUMENTS(1); - if (_stack.type(0) == cmd_number) { - if (_stack.value(0) >= 0) { - _stack.value(0) = sqrt(_stack.value(0)); - } else { - // negative number -> square root is complex - mpreal zero; - _stack.push(new ocomplex(_stack.value(0), zero, - _stack.obj(0).base)); // TODO(louis) manage new errors - _stack.value(0) = sqrt(_stack.value(0)); - _stack.erase(1); - } - } else if (_stack.type(0) == cmd_complex) { - _stack.value(0) = sqrt(_stack.value(0)); - } else { - setErrorContext(ret_bad_operand_type); - } -} - -/// @brief hex keyword implementation -/// -void program::rpn_hex() { - MIN_ARGUMENTS(1); - if (_stack.type(0) == cmd_number) { - _stack.obj(0).base = 16; - } else if (_stack.type(0) == cmd_complex) { - _stack.obj(0).reBase = 16; - _stack.obj(0).imBase = 16; - } else { - setErrorContext(ret_bad_operand_type); - } -} - -/// @brief bin keyword implementation -/// -void program::rpn_bin() { - MIN_ARGUMENTS(1); - if (_stack.type(0) == cmd_number) { - _stack.obj(0).base = 2; - } else if (_stack.type(0) == cmd_complex) { - _stack.obj(0).reBase = 2; - _stack.obj(0).imBase = 2; - } else { - setErrorContext(ret_bad_operand_type); - } -} - -/// @brief dec keyword implementation -/// -void program::rpn_dec() { - MIN_ARGUMENTS(1); - if (_stack.type(0) == cmd_number) { - _stack.obj(0).base = 10; - } else if (_stack.type(0) == cmd_complex) { - _stack.obj(0).reBase = 10; - _stack.obj(0).imBase = 10; - } else { - setErrorContext(ret_bad_operand_type); - } -} - -/// @brief base keyword implementation -/// -void program::rpn_base() { - MIN_ARGUMENTS(2); - if (_stack.type(1) == cmd_number || _stack.type(1) == cmd_complex) { - int base = static_cast(_stack.value(0).toLong()); - _stack.pop(); - if (base >= 2 && base <= 62) { - if (_stack.type(0) == cmd_number) { - _stack.obj(0).base = base; - } else { - _stack.obj(0).reBase = base; - _stack.obj(0).imBase = base; - } - } else { - setErrorContext(ret_out_of_range); - } - } else { - setErrorContext(ret_bad_operand_type); - } -} - -/// @brief % (purcent) keyword implementation -/// -void program::rpn_purcent() { - MIN_ARGUMENTS(2); - ARG_MUST_BE_OF_TYPE(0, cmd_number); - ARG_MUST_BE_OF_TYPE(1, cmd_number); - _stack.value(1) *= _stack.value(0) / 100; - _stack.pop(); -} - -/// @brief %CH keyword implementation -/// -void program::rpn_purcentCH() { - MIN_ARGUMENTS(2); - ARG_MUST_BE_OF_TYPE(0, cmd_number); - ARG_MUST_BE_OF_TYPE(1, cmd_number); - _stack.value(1) = (_stack.value(0) * 100) / _stack.value(1); - _stack.pop(); -} - -/// @brief sq keyword implementation -/// -void program::rpn_square() { - MIN_ARGUMENTS(1); - if (_stack.type(0) == cmd_number) - _stack.value(0) *= _stack.value(0); - else if (_stack.at(0)->_type == cmd_complex) - _stack.value(0) *= _stack.value(0); - else - setErrorContext(ret_bad_operand_type); -} - -/// @brief mod keyword implementation -/// -void program::rpn_modulo() { - MIN_ARGUMENTS(2); - ARG_MUST_BE_OF_TYPE(0, cmd_number); - ARG_MUST_BE_OF_TYPE(1, cmd_number); - _stack.value(1) = fmod(_stack.value(1), _stack.value(0)); - _stack.pop(); -} - -/// @brief abs keyword implementation -/// -void program::rpn_abs() { - MIN_ARGUMENTS(1); - if (_stack.type(0) == cmd_number) { - _stack.value(0) = abs(_stack.value(0)); - } else if (_stack.type(0) == cmd_complex) { - _stack.push(new number(abs(_stack.value(0)))); - _stack.erase(1); - } else { - setErrorContext(ret_bad_operand_type); - } -} - -/// @brief fact (factorial) keyword implementation -/// -void program::rpn_fact() { - MIN_ARGUMENTS(1); - ARG_MUST_BE_OF_TYPE(0, cmd_number); - // fact(n) = gamma(n+1) - _stack.value(0) = gamma(_stack.value(0) + 1); -} - -/// @brief sign keyword implementation -/// -void program::rpn_sign() { - MIN_ARGUMENTS(1); - if (_stack.type(0) == cmd_number) - _stack.value(0) = sgn(_stack.value(0)); - else if (_stack.at(0)->_type == cmd_complex) - _stack.value(0) = _stack.value(0) / abs(_stack.value(0)); - else - setErrorContext(ret_bad_operand_type); -} - -/// @brief mant keyword implementation -/// -void program::rpn_mant() { - MIN_ARGUMENTS(1); - ARG_MUST_BE_OF_TYPE(0, cmd_number); - if (!isfinite(_stack.value(0))) { - setErrorContext(ret_out_of_range); - return; - } - mp_exp_t exp; - _stack.value(0) = frexp(_stack.value(0), &exp); -} - -/// @brief xpon keyword implementation -/// -void program::rpn_xpon() { - MIN_ARGUMENTS(1); - ARG_MUST_BE_OF_TYPE(0, cmd_number); - if (!isfinite(_stack.value(0))) { - setErrorContext(ret_out_of_range); - return; - } - mp_exp_t exp; - (void)frexp(_stack.value(0), &exp); - _stack.value(0) = exp; -} - -/// @brief floor keyword implementation -/// -void program::rpn_floor() { - MIN_ARGUMENTS(1); - ARG_MUST_BE_OF_TYPE(0, cmd_number); - _stack.value(0) = floor(_stack.value(0)); -} - -/// @brief ceil keyword implementation -/// -void program::rpn_ceil() { - MIN_ARGUMENTS(1); - ARG_MUST_BE_OF_TYPE(0, cmd_number); - _stack.value(0) = ceil(_stack.value(0)); -} - -/// @brief fp keyword implementation -/// -void program::rpn_fp() { - MIN_ARGUMENTS(1); - ARG_MUST_BE_OF_TYPE(0, cmd_number); - _stack.value(0) = frac(_stack.value(0)); -} - -/// @brief ip keyword implementation -/// -void program::rpn_ip() { - MIN_ARGUMENTS(1); - ARG_MUST_BE_OF_TYPE(0, cmd_number); - _stack.value(0) = trunc(_stack.value(0)); -} - -/// @brief min keyword implementation -/// -void program::rpn_min() { - MIN_ARGUMENTS(2); - ARG_MUST_BE_OF_TYPE(0, cmd_number); - ARG_MUST_BE_OF_TYPE(1, cmd_number); - _stack.value(0) = min(_stack.value(0), _stack.value(1)); - _stack.erase(1); -} - -/// @brief max keyword implementation -/// -void program::rpn_max() { - MIN_ARGUMENTS(2); - ARG_MUST_BE_OF_TYPE(0, cmd_number); - ARG_MUST_BE_OF_TYPE(1, cmd_number); - _stack.value(0) = max(_stack.value(0), _stack.value(1)); - _stack.erase(1); -} diff --git a/src/rpn-stack.cc b/src/rpn-stack.cc new file mode 100644 index 0000000..92b2892 --- /dev/null +++ b/src/rpn-stack.cc @@ -0,0 +1,137 @@ +// Copyright (c) 2014-2022 Louis Rubet + +#include "program.h" + +/// @brief swap keyword implementation +/// +void program::RpnSwap(void) { + MIN_ARGUMENTS(2); + Object* tmp = stack_.front(); + stack_.erase(0, 1, false); + stack_.insert(stack_.begin() + 1, tmp); +} + +/// @brief drop keyword implementation +/// +void program::RpnDrop(void) { + MIN_ARGUMENTS(1); + stack_.pop(); +} + +/// @brief drop2 keyword implementation +/// +void program::RpnDrop2(void) { + MIN_ARGUMENTS(2); + stack_.erase(0, 2); +} + +/// @brief dropn keyword implementation +/// +void program::RpnDropn(void) { + MIN_ARGUMENTS(1); + ARG_MUST_BE_OF_TYPE(0, kNumber); + + int args = static_cast(stack_.value(0).toLong()); + MIN_ARGUMENTS(args + 1); + stack_.erase(0, args + 1); +} + +/// @brief erase / del keyword implementation +/// +void program::RpnErase(void) { stack_.erase(0, stack_.size()); } + +/// @brief dup keyword implementation +/// +void program::RpnDup(void) { + MIN_ARGUMENTS(1); + stack_.push_front(stack_.at(0)->Clone()); +} + +/// @brief dupn keyword implementation +/// +void program::RpnDupn(void) { + MIN_ARGUMENTS(1); + ARG_MUST_BE_OF_TYPE(0, kNumber); + + int args = static_cast(stack_.value(0).toLong()); + stack_.pop(); + + MIN_ARGUMENTS(args); + for (int i = 0; i < args; i++) stack_.push_front(stack_.at(args - 1)->Clone()); +} + +/// @brief dup2 keyword implementation +/// +void program::RpnDup2(void) { + MIN_ARGUMENTS(2); + stack_.push_front(stack_.at(1)->Clone()); + stack_.push_front(stack_.at(1)->Clone()); +} + +/// @brief pick keyword implementation +/// +void program::RpnPick(void) { + MIN_ARGUMENTS(1); + ARG_MUST_BE_OF_TYPE(0, kNumber); + + int to_pick = static_cast(stack_.value(0).toLong()); + stack_.pop(); + + // treat stack_ depth errors + if ((to_pick == 0) || (to_pick > stack_.size())) { + ERROR_CONTEXT(kOutOfRange); + return; + } + + stack_.push_front(stack_.at(to_pick - 1)->Clone()); +} + +/// @brief rot keyword implementation +/// +void program::RpnRot(void) { + MIN_ARGUMENTS(3); + Object* tmp = stack_.at(2); + stack_.erase(2, 1, false); + stack_.insert(stack_.begin(), tmp); +} + +/// @brief depth keyword implementation +/// +void program::RpnDepth(void) { stack_.push_front(new Number(stack_.size())); } + +/// @brief roll keyword implementation +/// +void program::RpnRoll(void) { + MIN_ARGUMENTS(1); + ARG_MUST_BE_OF_TYPE(0, kNumber); + + int args = static_cast(stack_.value(0).toLong()); + stack_.pop(); + MIN_ARGUMENTS(args); + + Object* tmp = stack_.at(args - 1); + stack_.erase(args - 1, 1, false); + stack_.insert(stack_.begin(), tmp); +} + +/// @brief rolld keyword implementation +/// +void program::RpnRolld(void) { + MIN_ARGUMENTS(2); + ARG_MUST_BE_OF_TYPE(0, kNumber); + + int args = static_cast(stack_.value(0).toLong()); + stack_.pop(); + MIN_ARGUMENTS(args); + + Object* tmp = stack_.at(0); + stack_.erase(0, 1, false); + stack_.insert(stack_.begin() + args - 1, tmp); +} + +/// @brief over keyword implementation +/// +void program::RpnOver(void) { + MIN_ARGUMENTS(2); + stack_.push_front(stack_.at(1)->Clone()); +} diff --git a/src/rpn-stack.cpp b/src/rpn-stack.cpp deleted file mode 100644 index 646a845..0000000 --- a/src/rpn-stack.cpp +++ /dev/null @@ -1,137 +0,0 @@ -// Copyright (c) 2014-2022 Louis Rubet - -#include "program.hpp" - -/// @brief swap keyword implementation -/// -void program::rpn_swap(void) { - MIN_ARGUMENTS(2); - object* tmp = _stack.front(); - _stack.erase(0, 1, false); - _stack.insert(_stack.begin() + 1, tmp); -} - -/// @brief drop keyword implementation -/// -void program::rpn_drop(void) { - MIN_ARGUMENTS(1); - _stack.pop(); -} - -/// @brief drop2 keyword implementation -/// -void program::rpn_drop2(void) { - MIN_ARGUMENTS(2); - _stack.erase(0, 2); -} - -/// @brief dropn keyword implementation -/// -void program::rpn_dropn(void) { - MIN_ARGUMENTS(1); - ARG_MUST_BE_OF_TYPE(0, cmd_number); - - int args = static_cast(_stack.value(0).toLong()); - MIN_ARGUMENTS(args + 1); - _stack.erase(0, args + 1); -} - -/// @brief erase / del keyword implementation -/// -void program::rpn_erase(void) { _stack.erase(0, _stack.size()); } - -/// @brief dup keyword implementation -/// -void program::rpn_dup(void) { - MIN_ARGUMENTS(1); - _stack.push_front(_stack.at(0)->clone()); -} - -/// @brief dupn keyword implementation -/// -void program::rpn_dupn(void) { - MIN_ARGUMENTS(1); - ARG_MUST_BE_OF_TYPE(0, cmd_number); - - int args = static_cast(_stack.value(0).toLong()); - _stack.pop(); - - MIN_ARGUMENTS(args); - for (int i = 0; i < args; i++) _stack.push_front(_stack.at(args - 1)->clone()); -} - -/// @brief dup2 keyword implementation -/// -void program::rpn_dup2(void) { - MIN_ARGUMENTS(2); - _stack.push_front(_stack.at(1)->clone()); - _stack.push_front(_stack.at(1)->clone()); -} - -/// @brief pick keyword implementation -/// -void program::rpn_pick(void) { - MIN_ARGUMENTS(1); - ARG_MUST_BE_OF_TYPE(0, cmd_number); - - int to_pick = static_cast(_stack.value(0).toLong()); - _stack.pop(); - - // treat stack depth errors - if ((to_pick == 0) || (to_pick > _stack.size())) { - setErrorContext(ret_out_of_range); - return; - } - - _stack.push_front(_stack.at(to_pick - 1)->clone()); -} - -/// @brief rot keyword implementation -/// -void program::rpn_rot(void) { - MIN_ARGUMENTS(3); - object* tmp = _stack.at(2); - _stack.erase(2, 1, false); - _stack.insert(_stack.begin(), tmp); -} - -/// @brief depth keyword implementation -/// -void program::rpn_depth(void) { _stack.push_front(new number(_stack.size())); } - -/// @brief roll keyword implementation -/// -void program::rpn_roll(void) { - MIN_ARGUMENTS(1); - ARG_MUST_BE_OF_TYPE(0, cmd_number); - - int args = static_cast(_stack.value(0).toLong()); - _stack.pop(); - MIN_ARGUMENTS(args); - - object* tmp = _stack.at(args - 1); - _stack.erase(args - 1, 1, false); - _stack.insert(_stack.begin(), tmp); -} - -/// @brief rolld keyword implementation -/// -void program::rpn_rolld(void) { - MIN_ARGUMENTS(2); - ARG_MUST_BE_OF_TYPE(0, cmd_number); - - int args = static_cast(_stack.value(0).toLong()); - _stack.pop(); - MIN_ARGUMENTS(args); - - object* tmp = _stack.at(0); - _stack.erase(0, 1, false); - _stack.insert(_stack.begin() + args - 1, tmp); -} - -/// @brief over keyword implementation -/// -void program::rpn_over(void) { - MIN_ARGUMENTS(2); - _stack.push_front(_stack.at(1)->clone()); -} diff --git a/src/rpn-store.cc b/src/rpn-store.cc new file mode 100644 index 0000000..3e9c316 --- /dev/null +++ b/src/rpn-store.cc @@ -0,0 +1,225 @@ +// Copyright (c) 2014-2022 Louis Rubet + +#include "input.h" +#include "program.h" + +/// @brief sto keyword implementation +/// +void program::RpnSto(void) { + MIN_ARGUMENTS(2); + ARG_MUST_BE_OF_TYPE(0, kSymbol); + + // store symbol with first value + const auto it = heap_.find(stack_.value(0)); + if (it != heap_.end()) { + delete it->second; + heap_.erase(it); + } + heap_[stack_.value(0)] = stack_.at(1)->Clone(); + stack_.erase(0, 2); +} + +/// @brief sto+ keyword implementation +/// +void program::RpnStoadd(void) { + MIN_ARGUMENTS(2); + ARG_MUST_BE_OF_TYPE(0, kSymbol); + if (heap_.find(stack_.value(0)) == heap_.end()) { + ERROR_CONTEXT(kUnknownVariable); + return; + } + RpnDup(); + RpnRcl(); // TODO(louis) is rcl the good one? it will recall local variables too + RpnRot(); + RpnPlus(); + RpnSwap(); + RpnSto(); +} + +/// @brief sto- keyword implementation +/// +void program::RpnStosub(void) { + MIN_ARGUMENTS(2); + ARG_MUST_BE_OF_TYPE(0, kSymbol); + if (heap_.find(stack_.value(0)) == heap_.end()) { + ERROR_CONTEXT(kUnknownVariable); + return; + } + RpnDup(); + RpnRcl(); + RpnRot(); + RpnMinus(); + RpnSwap(); + RpnSto(); +} + +/// @brief sto* keyword implementation +/// +void program::RpnStomul(void) { + MIN_ARGUMENTS(2); + ARG_MUST_BE_OF_TYPE(0, kSymbol); + if (heap_.find(stack_.value(0)) == heap_.end()) { + ERROR_CONTEXT(kUnknownVariable); + return; + } + RpnDup(); + RpnRcl(); + RpnRot(); + RpnMul(); + RpnSwap(); + RpnSto(); +} + +/// @brief sto/ keyword implementation +/// +void program::RpnStodiv(void) { + MIN_ARGUMENTS(2); + ARG_MUST_BE_OF_TYPE(0, kSymbol); + if (heap_.find(stack_.value(0)) == heap_.end()) { + ERROR_CONTEXT(kUnknownVariable); + return; + } + RpnDup(); + RpnRcl(); + RpnRot(); + RpnDiv(); + RpnSwap(); + RpnSto(); +} + +/// @brief stosneg keyword implementation +/// +void program::RpnStoneg(void) { + MIN_ARGUMENTS(1); + ARG_MUST_BE_OF_TYPE(0, kSymbol); + if (heap_.find(stack_.value(0)) == heap_.end()) { + ERROR_CONTEXT(kUnknownVariable); + return; + } + RpnDup(); + RpnRcl(); + RpnNeg(); + RpnSwap(); + RpnSto(); +} + +/// @brief sinv keyword implementation +/// +void program::RpnStoinv(void) { + MIN_ARGUMENTS(1); + ARG_MUST_BE_OF_TYPE(0, kSymbol); + if (heap_.find(stack_.value(0)) == heap_.end()) { + ERROR_CONTEXT(kUnknownVariable); + return; + } + RpnDup(); + RpnRcl(); + RpnInv(); + RpnSwap(); + RpnSto(); +} + +/// @brief rcl keyword implementation +/// +void program::RpnRcl(void) { + MIN_ARGUMENTS(1); + ARG_MUST_BE_OF_TYPE(0, kSymbol); + + // recall a variable + Object* obj; + string variable(stack_.value(0)); + + // mind the order of heaps + if (FindVariable(variable, obj)) { + (void)stack_.pop(); + stack_.push_front(obj->Clone()); + } else { + ERROR_CONTEXT(kUnknownVariable); + } +} + +/// @brief edit keyword implementation +/// +void program::RpnEdit(void) { + MIN_ARGUMENTS(1); + + ostringstream st; + + // re-write stack_ objet in a stream + stack_.at(0)->Show(st); + stack_.pop(); + + // set it as the linenoise line entry + Input::Preload(st.str().c_str()); +} + +/// @brief recall then eval a symbol variable if it is auto-evaluable +/// +/// @param symb the smlbol to recall and autoeval +/// +void program::AutoRcl(Symbol* symb) { + if (symb->auto_eval) { + Object* obj; + string variable(symb->value); + + // mind the order of heaps + if (FindVariable(variable, obj)) { + stack_.push_front(obj->Clone()); + if (obj->_type == kProgram) RpnEval(); + } else { + stack_.push_front(symb->Clone()); + } + } else { + stack_.push_front(symb->Clone()); + } +} + +/// @brief purge keyword implementation +/// +void program::RpnPurge(void) { + MIN_ARGUMENTS(1); + ARG_MUST_BE_OF_TYPE(0, kSymbol); + + const auto i = heap_.find(stack_.value(0)); + if (i != heap_.end()) { + delete i->second; + heap_.erase(i); + } else { + ERROR_CONTEXT(kUnknownVariable); + } + stack_.pop(); +} + +/// @brief vars keyword implementation +/// +void program::RpnVars(void) { + Object* obj; + program* parent = parent_; + string name; + int index = 1; + + // heap variables + for (auto i : heap_) { + cout << "var " << index++ << ": name '" << i.first << "', type " << i.second->Name() << ", value "; + i.second->Show(cout) << endl; + } + + // local variables + for (auto i : local_heap_) { + cout << "var " << index++ << ": name '" << i.first << "', type " << i.second->Name() << ", value "; + i.second->Show(cout) << endl; + } + + // parents local variables + while (parent != nullptr) { + for (auto i : parent->local_heap_) { + cout << "parent var " << index++ << ": name '" << i.first << "', type " << i.second->Name() << ", value "; + obj->Show(cout) << endl; + } + parent = parent->parent_; + } +} + +/// @brief clusr keyword implementation +/// +void program::RpnClusr(void) { heap_.clear(); } diff --git a/src/rpn-store.cpp b/src/rpn-store.cpp deleted file mode 100644 index 4e9f195..0000000 --- a/src/rpn-store.cpp +++ /dev/null @@ -1,225 +0,0 @@ -// Copyright (c) 2014-2022 Louis Rubet - -#include "input.hpp" -#include "program.hpp" - -/// @brief sto keyword implementation -/// -void program::rpn_sto(void) { - MIN_ARGUMENTS(2); - ARG_MUST_BE_OF_TYPE(0, cmd_symbol); - - // store symbol with first value - const auto it = _heap.find(_stack.value(0)); - if (it != _heap.end()) { - delete it->second; - _heap.erase(it); - } - _heap[_stack.value(0)] = _stack.at(1)->clone(); - _stack.erase(0, 2); -} - -/// @brief sto+ keyword implementation -/// -void program::rpn_stoadd(void) { - MIN_ARGUMENTS(2); - ARG_MUST_BE_OF_TYPE(0, cmd_symbol); - if (_heap.find(_stack.value(0)) == _heap.end()) { - setErrorContext(ret_unknown_variable); - return; - } - rpn_dup(); - rpn_rcl(); // TODO(louis) is rcl the good one? it will recall local variables too - rpn_rot(); - rpn_plus(); - rpn_swap(); - rpn_sto(); -} - -/// @brief sto- keyword implementation -/// -void program::rpn_stosub(void) { - MIN_ARGUMENTS(2); - ARG_MUST_BE_OF_TYPE(0, cmd_symbol); - if (_heap.find(_stack.value(0)) == _heap.end()) { - setErrorContext(ret_unknown_variable); - return; - } - rpn_dup(); - rpn_rcl(); - rpn_rot(); - rpn_minus(); - rpn_swap(); - rpn_sto(); -} - -/// @brief sto* keyword implementation -/// -void program::rpn_stomul(void) { - MIN_ARGUMENTS(2); - ARG_MUST_BE_OF_TYPE(0, cmd_symbol); - if (_heap.find(_stack.value(0)) == _heap.end()) { - setErrorContext(ret_unknown_variable); - return; - } - rpn_dup(); - rpn_rcl(); - rpn_rot(); - rpn_mul(); - rpn_swap(); - rpn_sto(); -} - -/// @brief sto/ keyword implementation -/// -void program::rpn_stodiv(void) { - MIN_ARGUMENTS(2); - ARG_MUST_BE_OF_TYPE(0, cmd_symbol); - if (_heap.find(_stack.value(0)) == _heap.end()) { - setErrorContext(ret_unknown_variable); - return; - } - rpn_dup(); - rpn_rcl(); - rpn_rot(); - rpn_div(); - rpn_swap(); - rpn_sto(); -} - -/// @brief stosneg keyword implementation -/// -void program::rpn_stoneg(void) { - MIN_ARGUMENTS(1); - ARG_MUST_BE_OF_TYPE(0, cmd_symbol); - if (_heap.find(_stack.value(0)) == _heap.end()) { - setErrorContext(ret_unknown_variable); - return; - } - rpn_dup(); - rpn_rcl(); - rpn_neg(); - rpn_swap(); - rpn_sto(); -} - -/// @brief sinv keyword implementation -/// -void program::rpn_stoinv(void) { - MIN_ARGUMENTS(1); - ARG_MUST_BE_OF_TYPE(0, cmd_symbol); - if (_heap.find(_stack.value(0)) == _heap.end()) { - setErrorContext(ret_unknown_variable); - return; - } - rpn_dup(); - rpn_rcl(); - rpn_inv(); - rpn_swap(); - rpn_sto(); -} - -/// @brief rcl keyword implementation -/// -void program::rpn_rcl(void) { - MIN_ARGUMENTS(1); - ARG_MUST_BE_OF_TYPE(0, cmd_symbol); - - // recall a variable - object* obj; - string variable(_stack.value(0)); - - // mind the order of heaps - if (find_variable(variable, obj)) { - (void)_stack.pop(); - _stack.push_front(obj->clone()); - } else { - setErrorContext(ret_unknown_variable); - } -} - -/// @brief edit keyword implementation -/// -void program::rpn_edit(void) { - MIN_ARGUMENTS(1); - - ostringstream st; - - // re-write stack objet in a stream - _stack.at(0)->show(st); - _stack.pop(); - - // set it as the linenoise line entry - Input::preload(st.str().c_str()); -} - -/// @brief recall then eval a symbol variable if it is auto-evaluable -/// -/// @param symb the smlbol to recall and autoeval -/// -void program::auto_rcl(symbol* symb) { - if (symb->autoEval) { - object* obj; - string variable(symb->value); - - // mind the order of heaps - if (find_variable(variable, obj)) { - _stack.push_front(obj->clone()); - if (obj->_type == cmd_program) rpn_eval(); - } else { - _stack.push_front(symb->clone()); - } - } else { - _stack.push_front(symb->clone()); - } -} - -/// @brief purge keyword implementation -/// -void program::rpn_purge(void) { - MIN_ARGUMENTS(1); - ARG_MUST_BE_OF_TYPE(0, cmd_symbol); - - const auto i = _heap.find(_stack.value(0)); - if (i != _heap.end()) { - delete i->second; - _heap.erase(i); - } else { - setErrorContext(ret_unknown_variable); - } - _stack.pop(); -} - -/// @brief vars keyword implementation -/// -void program::rpn_vars(void) { - object* obj; - program* parent = _parent; - string name; - int index = 1; - - // heap variables - for (auto i : _heap) { - cout << "var " << index++ << ": name '" << i.first << "', type " << i.second->name() << ", value "; - i.second->show(cout) << endl; - } - - // local variables - for (auto i : _local_heap) { - cout << "var " << index++ << ": name '" << i.first << "', type " << i.second->name() << ", value "; - i.second->show(cout) << endl; - } - - // parents local variables - while (parent != nullptr) { - for (auto i : parent->_local_heap) { - cout << "parent var " << index++ << ": name '" << i.first << "', type " << i.second->name() << ", value "; - obj->show(cout) << endl; - } - parent = parent->_parent; - } -} - -/// @brief clusr keyword implementation -/// -void program::rpn_clusr(void) { _heap.clear(); } diff --git a/src/rpn-string.cc b/src/rpn-string.cc new file mode 100644 index 0000000..83b3340 --- /dev/null +++ b/src/rpn-string.cc @@ -0,0 +1,96 @@ +// Copyright (c) 2014-2022 Louis Rubet + +#include + +#include "program.h" + +/// @brief ->str keyword implementation +/// +void program::RpnInstr() { + MIN_ARGUMENTS(1); + + // stringify only if not already a string + if (stack_.type(0) != kString) { + stringstream ss; + ss << stack_.at(0); + stack_.pop(); + stack_.push(new String(ss.str())); + } +} + +/// @brief str-> keyword implementation +/// +void program::RpnStrout() { + MIN_ARGUMENTS(1); + ARG_MUST_BE_OF_TYPE(0, kString); + + string entry(stack_.value(0)); + program prog(stack_, heap_); + stack_.pop(); + + // make program from string in stack_ level 1 + if (prog.Parse(entry) == kOk) + // run it + prog.Run(); +} + +/// @brief chr keyword implementation +/// +void program::RpnChr() { + MIN_ARGUMENTS(1); + ARG_MUST_BE_OF_TYPE(0, kNumber); + char the_chr = static_cast(stack_.value(0).toLong()); + stack_.pop(); + if (the_chr < 32 || the_chr > 126) the_chr = '.'; + stack_.push_front(new String(string(1, the_chr))); +} + +/// @brief num keyword implementation +/// +void program::RpnNum() { + MIN_ARGUMENTS(1); + ARG_MUST_BE_OF_TYPE(0, kString); + if (stack_.value(0).size() > 0) + stack_.push_front(new Number(stack_.value(0)[0])); + else + stack_.push_front(new Number(0)); + stack_.erase(1); +} + +/// @brief size keyword implementation +/// +void program::RpnStrsize() { + MIN_ARGUMENTS(1); + ARG_MUST_BE_OF_TYPE(0, kString); + stack_.push_front(new Number(stack_.value(0).size())); + stack_.erase(1); +} + +/// @brief pos keyword implementation +/// +void program::RpnStrpos() { + MIN_ARGUMENTS(2); + ARG_MUST_BE_OF_TYPE(0, kString); + ARG_MUST_BE_OF_TYPE(1, kString); + + size_t pos = stack_.value(1).find(stack_.value(0)) + 1; + stack_.erase(0, 2); + stack_.push_front(new Number(pos)); +} + +/// @brief sub keyword implementation +/// +void program::RpnStrsub() { + MIN_ARGUMENTS(3); + ARG_MUST_BE_OF_TYPE(0, kNumber); + ARG_MUST_BE_OF_TYPE(1, kNumber); + ARG_MUST_BE_OF_TYPE(2, kString); + + size_t first = stack_.value(1).toULong(); + size_t len = stack_.value(0).toULong() - first + 1; + first--; + + if (first > stack_.value(2).size()) first = len = 0; + stack_.push(new String(stack_.value(2).substr(first, len))); + stack_.erase(1, 3); +} diff --git a/src/rpn-string.cpp b/src/rpn-string.cpp deleted file mode 100644 index 18b5b1d..0000000 --- a/src/rpn-string.cpp +++ /dev/null @@ -1,96 +0,0 @@ -// Copyright (c) 2014-2022 Louis Rubet - -#include - -#include "program.hpp" - -/// @brief ->str keyword implementation -/// -void program::rpn_instr() { - MIN_ARGUMENTS(1); - - // stringify only if not already a string - if (_stack.type(0) != cmd_string) { - stringstream ss; - ss << _stack.at(0); - _stack.pop(); - _stack.push(new ostring(ss.str())); - } -} - -/// @brief str-> keyword implementation -/// -void program::rpn_strout() { - MIN_ARGUMENTS(1); - ARG_MUST_BE_OF_TYPE(0, cmd_string); - - string entry(_stack.value(0)); - program prog(_stack, _heap); - _stack.pop(); - - // make program from string in stack level 1 - if (prog.parse(entry) == ret_ok) - // run it - prog.run(); -} - -/// @brief chr keyword implementation -/// -void program::rpn_chr() { - MIN_ARGUMENTS(1); - ARG_MUST_BE_OF_TYPE(0, cmd_number); - char the_chr = static_cast(_stack.value(0).toLong()); - _stack.pop(); - if (the_chr < 32 || the_chr > 126) the_chr = '.'; - _stack.push_front(new ostring(string(1, the_chr))); -} - -/// @brief num keyword implementation -/// -void program::rpn_num() { - MIN_ARGUMENTS(1); - ARG_MUST_BE_OF_TYPE(0, cmd_string); - if (_stack.value(0).size() > 0) - _stack.push_front(new number(_stack.value(0)[0])); - else - _stack.push_front(new number(0)); - _stack.erase(1); -} - -/// @brief size keyword implementation -/// -void program::rpn_strsize() { - MIN_ARGUMENTS(1); - ARG_MUST_BE_OF_TYPE(0, cmd_string); - _stack.push_front(new number(_stack.value(0).size())); - _stack.erase(1); -} - -/// @brief pos keyword implementation -/// -void program::rpn_strpos() { - MIN_ARGUMENTS(2); - ARG_MUST_BE_OF_TYPE(0, cmd_string); - ARG_MUST_BE_OF_TYPE(1, cmd_string); - - size_t pos = _stack.value(1).find(_stack.value(0)) + 1; - _stack.erase(0, 2); - _stack.push_front(new number(pos)); -} - -/// @brief sub keyword implementation -/// -void program::rpn_strsub() { - MIN_ARGUMENTS(3); - ARG_MUST_BE_OF_TYPE(0, cmd_number); - ARG_MUST_BE_OF_TYPE(1, cmd_number); - ARG_MUST_BE_OF_TYPE(2, cmd_string); - - size_t first = _stack.value(1).toULong(); - size_t len = _stack.value(0).toULong() - first + 1; - first--; - - if (first > _stack.value(2).size()) first = len = 0; - _stack.push(new ostring(_stack.value(2).substr(first, len))); - _stack.erase(1, 3); -} diff --git a/src/rpn-test-framework.cpp b/src/rpn-test-framework.cc similarity index 85% rename from src/rpn-test-framework.cpp rename to src/rpn-test-framework.cc index 60d26b1..f0e4742 100644 --- a/src/rpn-test-framework.cpp +++ b/src/rpn-test-framework.cc @@ -1,21 +1,21 @@ // Copyright (c) 2014-2022 Louis Rubet #include -using namespace std; +using std::string; -#include "program.hpp" +#include "program.h" #include "version.h" // foreground colors -static const string FG_RED = "\33[31m"; -static const string FG_GREEN = "\33[32m"; -static const string COLOR_OFF = "\33[m"; +static const char FG_RED[] = "\33[31m"; +static const char FG_GREEN[] = "\33[32m"; +static const char COLOR_OFF[] = "\33[m"; -static void _findAndReplaceAll(std::string& data, std::string toSearch, std::string replaceStr) { +static void FindAndReplaceAll(string& data, string toSearch, string replaceStr) { // Get the first occurrence size_t pos = data.find(toSearch); // Repeat till end is reached - while (pos != std::string::npos) { + while (pos != string::npos) { // Replace this occurrence of Sub String data.replace(pos, toSearch.size(), replaceStr); // Get the next occurrence from the current position @@ -28,17 +28,17 @@ static void _findAndReplaceAll(std::string& data, std::string toSearch, std::str /// @param stack_is the output string /// @param stk the stack /// -static void getStackAsString(string& stack_is, rpnstack& stk) { +static void GetStackAsString(string& stack_is, rpnstack& stk) { ostringstream st; if (stk.empty()) { stack_is.clear(); return; } - stk[stk.size() - 1]->show(st); + stk[stk.size() - 1]->Show(st); stack_is += st.str(); for (int i = static_cast(stk.size()) - 2; i >= 0; i--) { ostringstream st; - stk[i]->show(st); + stk[i]->Show(st); stack_is += ", " + st.str(); } } @@ -51,7 +51,7 @@ static void getStackAsString(string& stack_is, rpnstack& stk) { /// @param steps steps nb /// @param steps_failed failed steps nb /// -static void testShowResult(string title, int tests, int tests_failed, int steps, int steps_failed) { +static void ShowTestResult(string title, int tests, int tests_failed, int steps, int steps_failed) { if (!title.empty()) cout << title << ": "; cout << "run " << tests << " tests: " << tests - tests_failed << " passed, "; if (tests_failed > 0) cout << FG_RED; @@ -67,25 +67,25 @@ static void testShowResult(string title, int tests, int tests_failed, int steps, /// @brief test keyword implementation /// -void program::rpn_test() { +void program::RpnTest() { MIN_ARGUMENTS(1); - ARG_MUST_BE_OF_TYPE(0, cmd_string); + ARG_MUST_BE_OF_TYPE(0, kString); int total_tests = 0; int total_tests_failed = 0; int total_steps = 0; int total_steps_failed = 0; - string test_filename = _stack.value(0); - _stack.pop(); + string test_filename = stack_.value(0); + stack_.pop(); cout << endl << "rpn version is " << RPN_VERSION << endl; - test(test_filename, total_tests, total_tests_failed, total_steps, total_steps_failed); - testShowResult("\nTotal", total_tests, total_tests_failed, total_steps, total_steps_failed); + RunTestFile(test_filename, total_tests, total_tests_failed, total_steps, total_steps_failed); + ShowTestResult("\nTotal", total_tests, total_tests_failed, total_steps, total_steps_failed); // notify to caller that test succeeded or not if (total_tests_failed > 0) { - _err = ret_test_failed; - _err_context = string("rpn version ") + RPN_VERSION + ", test file " + test_filename; + err_ = kTestFailed; + err_context_ = string("rpn version ") + RPN_VERSION + ", test file " + test_filename; } } @@ -97,7 +97,7 @@ void program::rpn_test() { /// @param total_steps the total steps nb /// @param total_steps_failed the total failed steps nb /// -void program::test(string test_filename, int& total_tests, int& total_tests_failed, int& total_steps, +void program::RunTestFile(string test_filename, int& total_tests, int& total_tests_failed, int& total_steps, int& total_steps_failed) { const string stack_size("-> stack size should be "); const string stack_value("-> stack should be "); @@ -113,7 +113,7 @@ void program::test(string test_filename, int& total_tests, int& total_tests_fail if (test_file.is_open()) { string test_title; string entry; - ret_value ret; + RetValue ret; rpnstack stk; heap hp; bool failed = false; @@ -131,7 +131,7 @@ void program::test(string test_filename, int& total_tests, int& total_tests_fail if (entry.empty()) continue; if (entry.substr(0, 8) == "@include") { - test(entry.substr(9), total_tests, total_tests_failed, total_steps, total_steps_failed); + RunTestFile(entry.substr(9), total_tests, total_tests_failed, total_steps, total_steps_failed); } else if (entry.substr(0, 2) == "# ") { cout << endl << test_filename << ": " << entry.substr(2) << endl; } else if (entry.substr(0, 3) == "## ") { @@ -179,7 +179,7 @@ void program::test(string test_filename, int& total_tests, int& total_tests_fail string stack_should_be = entry.substr(stack_value.size()); string stack_is; - getStackAsString(stack_is, stk); + GetStackAsString(stack_is, stk); if (stack_is != stack_should_be) { // count fail test and step @@ -242,14 +242,14 @@ void program::test(string test_filename, int& total_tests, int& total_tests_fail failed = true; } else { // parse entry and run line - _findAndReplaceAll(entry, "`", ""); + FindAndReplaceAll(entry, "`", ""); if (!entry.empty()) { program prog(stk, hp); - ret = prog.parse(entry); - if (ret == ret_ok) { + ret = prog.Parse(entry); + if (ret == kOk) { // run it - (void)prog.run(); - last_err = static_cast(prog.get_err()); + (void)prog.Run(); + last_err = static_cast(prog.GetLastError()); } } } @@ -264,7 +264,7 @@ void program::test(string test_filename, int& total_tests, int& total_tests_fail // conclusion: show and keep for total if (tests != 0) { - testShowResult("", tests, tests_failed, steps, steps_failed); + ShowTestResult("", tests, tests_failed, steps, steps_failed); total_tests += tests; total_tests_failed += tests_failed; diff --git a/src/rpn-test.cc b/src/rpn-test.cc new file mode 100644 index 0000000..fe610b0 --- /dev/null +++ b/src/rpn-test.cc @@ -0,0 +1,166 @@ +// Copyright (c) 2014-2022 Louis Rubet + +#include "program.h" + +/// @brief compared 2 strings on top of the stack_ +/// +/// @return 0 strings are equal +/// @return !0 strings are not equal (see strcmp output) +/// +static int CmpStringOnStackTop(rpnstack& stk) { + // stack_ should have 2 strings at level 1 and 2 + // this function removes these 2 entries + int res = stk.value(1).compare(stk.value(0)); + stk.erase(0, 2); + return res; +} + +/// @brief > keyword implementation +/// +void program::RpnSup(void) { + MIN_ARGUMENTS(2); + if (stack_.type(0) == kNumber && stack_.type(1) == kNumber) { + stack_.push_front(new Number(stack_.value(1) > stack_.value(0))); + stack_.erase(1, 2); + } else if (stack_.type(0) == kString && stack_.type(1) == kString) { + stack_.push_front(new Number(CmpStringOnStackTop(stack_) == 1)); + stack_.erase(1, 2); + } else { + ERROR_CONTEXT(kBadOperandType); + } +} + +/// @brief >= keyword implementation +/// +void program::RpnSupEq(void) { + MIN_ARGUMENTS(2); + if (stack_.type(0) == kNumber && stack_.type(1) == kNumber) { + stack_.push_front(new Number(stack_.value(1) >= stack_.value(0))); + stack_.erase(1, 2); + } else if (stack_.type(0) == kString && stack_.type(1) == kString) { + stack_.push_front(new Number(CmpStringOnStackTop(stack_) != -1)); + stack_.erase(1, 2); + } else { + ERROR_CONTEXT(kBadOperandType); + } +} + +/// @brief < keyword implementation +/// +void program::RpnInf(void) { + MIN_ARGUMENTS(2); + + if (stack_.type(0) == kNumber && stack_.type(1) == kNumber) { + stack_.push_front(new Number(stack_.value(1) < stack_.value(0))); + stack_.erase(1, 2); + } else if (stack_.type(0) == kString && stack_.type(1) == kString) { + stack_.push_front(new Number(CmpStringOnStackTop(stack_) == -1)); + stack_.erase(1, 2); + } else { + ERROR_CONTEXT(kBadOperandType); + } +} + +/// @brief <= keyword implementation +/// +void program::RpnInfEq(void) { + MIN_ARGUMENTS(2); + if (stack_.type(0) == kNumber && stack_.type(1) == kNumber) { + stack_.push_front(new Number(stack_.value(1) <= stack_.value(0))); + stack_.erase(1, 2); + } else if (stack_.type(0) == kString && stack_.type(1) == kString) { + stack_.push_front(new Number(CmpStringOnStackTop(stack_) != 1)); + stack_.erase(1, 2); + } else { + ERROR_CONTEXT(kBadOperandType); + } +} + +/// @brief != keyword implementation +/// +void program::RpnDiff(void) { + MIN_ARGUMENTS(2); + if (stack_.type(0) == kNumber && stack_.type(1) == kNumber) { + stack_.push_front(new Number(stack_.value(1) != stack_.value(0))); + stack_.erase(1, 2); + } else if (stack_.type(0) == kComplex && stack_.type(1) == kComplex) { + stack_.push_front(new Number(stack_.value(1) != stack_.value(0))); + stack_.erase(1, 2); + } else if (stack_.type(0) == kString && stack_.type(1) == kString) { + stack_.push_front(new Number(CmpStringOnStackTop(stack_) != 0)); + stack_.erase(1, 2); + } else { + ERROR_CONTEXT(kBadOperandType); + } +} + +/// @brief == keyword implementation +/// +void program::RpnEq(void) { + MIN_ARGUMENTS(2); + if (stack_.type(0) == kNumber && stack_.type(1) == kNumber) { + stack_.push_front(new Number(stack_.value(1) == stack_.value(0))); + stack_.erase(1, 2); + } else if (stack_.type(0) == kComplex && stack_.type(1) == kComplex) { + stack_.push_front(new Number(stack_.value(1) == stack_.value(0))); + stack_.erase(1, 2); + } else if (stack_.type(0) == kString && stack_.type(1) == kString) { + stack_.push_front(new Number(CmpStringOnStackTop(stack_) == 0)); + stack_.erase(1, 2); + } else { + ERROR_CONTEXT(kBadOperandType); + } +} + +/// @brief and keyword implementation +/// +void program::RpnTestAnd(void) { + MIN_ARGUMENTS(2); + ARG_MUST_BE_OF_TYPE(0, kNumber); + ARG_MUST_BE_OF_TYPE(1, kNumber); + if (stack_.value(0) != 0 && stack_.value(1) != 0) + stack_.push(new Number(1)); + else + stack_.push(new Number(0)); + stack_.erase(1, 2); +} + +/// @brief or keyword implementation +/// +void program::RpnTestOr(void) { + MIN_ARGUMENTS(2); + ARG_MUST_BE_OF_TYPE(0, kNumber); + ARG_MUST_BE_OF_TYPE(1, kNumber); + if (stack_.value(0) != 0 || stack_.value(1) != 0) + stack_.push(new Number(1)); + else + stack_.push(new Number(0)); + stack_.erase(1, 2); +} + +/// @brief xor keyword implementation +/// +void program::RpnTestXor(void) { + MIN_ARGUMENTS(2); + ARG_MUST_BE_OF_TYPE(0, kNumber); + ARG_MUST_BE_OF_TYPE(1, kNumber); + if (stack_.value(0) != 0 ^ stack_.value(1) != 0) + stack_.push(new Number(1)); + else + stack_.push(new Number(0)); + stack_.erase(1, 2); +} + +/// @brief not keyword implementation +/// +void program::RpnTestNot(void) { + MIN_ARGUMENTS(1); + ARG_MUST_BE_OF_TYPE(0, kNumber); + + stack_.push(new Number(stack_.value(0) == 0 ? 1 : 0)); + stack_.erase(1, 1); +} + +/// @brief test same implementation +/// +void program::RpnSame(void) { RpnEq(); } diff --git a/src/rpn-test.cpp b/src/rpn-test.cpp deleted file mode 100644 index 8b80d52..0000000 --- a/src/rpn-test.cpp +++ /dev/null @@ -1,166 +0,0 @@ -// Copyright (c) 2014-2022 Louis Rubet - -#include "program.hpp" - -/// @brief compared 2 strings on top of the stack -/// -/// @return 0 strings are equal -/// @return !0 strings are not equal (see strcmp output) -/// -static int cmpStringsOnStackTop(rpnstack& stk) { - // _stack should have 2 strings at level 1 and 2 - // this function removes these 2 entries - int res = stk.value(1).compare(stk.value(0)); - stk.erase(0, 2); - return res; -} - -/// @brief > keyword implementation -/// -void program::rpn_sup(void) { - MIN_ARGUMENTS(2); - if (_stack.type(0) == cmd_number && _stack.type(1) == cmd_number) { - _stack.push_front(new number(_stack.value(1) > _stack.value(0))); - _stack.erase(1, 2); - } else if (_stack.type(0) == cmd_string && _stack.type(1) == cmd_string) { - _stack.push_front(new number(cmpStringsOnStackTop(_stack) == 1)); - _stack.erase(1, 2); - } else { - setErrorContext(ret_bad_operand_type); - } -} - -/// @brief >= keyword implementation -/// -void program::rpn_sup_eq(void) { - MIN_ARGUMENTS(2); - if (_stack.type(0) == cmd_number && _stack.type(1) == cmd_number) { - _stack.push_front(new number(_stack.value(1) >= _stack.value(0))); - _stack.erase(1, 2); - } else if (_stack.type(0) == cmd_string && _stack.type(1) == cmd_string) { - _stack.push_front(new number(cmpStringsOnStackTop(_stack) != -1)); - _stack.erase(1, 2); - } else { - setErrorContext(ret_bad_operand_type); - } -} - -/// @brief < keyword implementation -/// -void program::rpn_inf(void) { - MIN_ARGUMENTS(2); - - if (_stack.type(0) == cmd_number && _stack.type(1) == cmd_number) { - _stack.push_front(new number(_stack.value(1) < _stack.value(0))); - _stack.erase(1, 2); - } else if (_stack.type(0) == cmd_string && _stack.type(1) == cmd_string) { - _stack.push_front(new number(cmpStringsOnStackTop(_stack) == -1)); - _stack.erase(1, 2); - } else { - setErrorContext(ret_bad_operand_type); - } -} - -/// @brief <= keyword implementation -/// -void program::rpn_inf_eq(void) { - MIN_ARGUMENTS(2); - if (_stack.type(0) == cmd_number && _stack.type(1) == cmd_number) { - _stack.push_front(new number(_stack.value(1) <= _stack.value(0))); - _stack.erase(1, 2); - } else if (_stack.type(0) == cmd_string && _stack.type(1) == cmd_string) { - _stack.push_front(new number(cmpStringsOnStackTop(_stack) != 1)); - _stack.erase(1, 2); - } else { - setErrorContext(ret_bad_operand_type); - } -} - -/// @brief != keyword implementation -/// -void program::rpn_diff(void) { - MIN_ARGUMENTS(2); - if (_stack.type(0) == cmd_number && _stack.type(1) == cmd_number) { - _stack.push_front(new number(_stack.value(1) != _stack.value(0))); - _stack.erase(1, 2); - } else if (_stack.type(0) == cmd_complex && _stack.type(1) == cmd_complex) { - _stack.push_front(new number(_stack.value(1) != _stack.value(0))); - _stack.erase(1, 2); - } else if (_stack.type(0) == cmd_string && _stack.type(1) == cmd_string) { - _stack.push_front(new number(cmpStringsOnStackTop(_stack) != 0)); - _stack.erase(1, 2); - } else { - setErrorContext(ret_bad_operand_type); - } -} - -/// @brief == keyword implementation -/// -void program::rpn_eq(void) { - MIN_ARGUMENTS(2); - if (_stack.type(0) == cmd_number && _stack.type(1) == cmd_number) { - _stack.push_front(new number(_stack.value(1) == _stack.value(0))); - _stack.erase(1, 2); - } else if (_stack.type(0) == cmd_complex && _stack.type(1) == cmd_complex) { - _stack.push_front(new number(_stack.value(1) == _stack.value(0))); - _stack.erase(1, 2); - } else if (_stack.type(0) == cmd_string && _stack.type(1) == cmd_string) { - _stack.push_front(new number(cmpStringsOnStackTop(_stack) == 0)); - _stack.erase(1, 2); - } else { - setErrorContext(ret_bad_operand_type); - } -} - -/// @brief and keyword implementation -/// -void program::rpn_test_and(void) { - MIN_ARGUMENTS(2); - ARG_MUST_BE_OF_TYPE(0, cmd_number); - ARG_MUST_BE_OF_TYPE(1, cmd_number); - if (_stack.value(0) != 0 && _stack.value(1) != 0) - _stack.push(new number(1)); - else - _stack.push(new number(0)); - _stack.erase(1, 2); -} - -/// @brief or keyword implementation -/// -void program::rpn_test_or(void) { - MIN_ARGUMENTS(2); - ARG_MUST_BE_OF_TYPE(0, cmd_number); - ARG_MUST_BE_OF_TYPE(1, cmd_number); - if (_stack.value(0) != 0 || _stack.value(1) != 0) - _stack.push(new number(1)); - else - _stack.push(new number(0)); - _stack.erase(1, 2); -} - -/// @brief xor keyword implementation -/// -void program::rpn_test_xor(void) { - MIN_ARGUMENTS(2); - ARG_MUST_BE_OF_TYPE(0, cmd_number); - ARG_MUST_BE_OF_TYPE(1, cmd_number); - if (_stack.value(0) != 0 ^ _stack.value(1) != 0) - _stack.push(new number(1)); - else - _stack.push(new number(0)); - _stack.erase(1, 2); -} - -/// @brief not keyword implementation -/// -void program::rpn_test_not(void) { - MIN_ARGUMENTS(1); - ARG_MUST_BE_OF_TYPE(0, cmd_number); - - _stack.push(new number(_stack.value(0) == 0 ? 1 : 0)); - _stack.erase(1, 1); -} - -/// @brief test same implementation -/// -void program::rpn_same(void) { rpn_eq(); } diff --git a/src/rpn-time.cpp b/src/rpn-time.cc similarity index 77% rename from src/rpn-time.cpp rename to src/rpn-time.cc index ad37b3f..f763dba 100644 --- a/src/rpn-time.cpp +++ b/src/rpn-time.cc @@ -2,11 +2,11 @@ #include -#include "program.hpp" +#include "program.h" /// @brief time keyword implementation /// -void program::rpn_time() { +void program::RpnTime() { struct timespec ts; struct tm* tm; double date; @@ -22,16 +22,16 @@ void program::rpn_time() { // push it // division after push for real precision - _stack.push(new number(date)); - _stack.value(0) /= 10000000000.0; + stack_.push(new Number(date)); + stack_.value(0) /= 10000000000.0; } else { - setErrorContext(ret_internal); + ERROR_CONTEXT(kInternalError); } } /// @brief date keyword implementation /// -void program::rpn_date() { +void program::RpnDate() { struct timespec ts; struct tm* tm; double date; @@ -46,18 +46,18 @@ void program::rpn_date() { static_cast(tm->tm_year + 1900); // push it - number* num; + Number* num; // division after push for real precision - _stack.push(new number(date)); - _stack.value(0) /= 1000000.0; + stack_.push(new Number(date)); + stack_.value(0) /= 1000000.0; } else { - setErrorContext(ret_internal); + ERROR_CONTEXT(kInternalError); } } /// @brief ticks keyword implementation /// -void program::rpn_ticks() { +void program::RpnTicks() { struct timespec ts; struct tm* tm; double date; @@ -69,8 +69,8 @@ void program::rpn_ticks() { if (tm != nullptr) { // date in µs date = 1000000.0 * static_cast(ts.tv_sec) + static_cast(ts.tv_nsec / 1000); - _stack.push(new number(date)); + stack_.push(new Number(date)); } else { - setErrorContext(ret_internal); + ERROR_CONTEXT(kInternalError); } } diff --git a/src/rpn-trig.cc b/src/rpn-trig.cc new file mode 100644 index 0000000..d31ceb3 --- /dev/null +++ b/src/rpn-trig.cc @@ -0,0 +1,99 @@ +// Copyright (c) 2014-2022 Louis Rubet + +#include "program.h" + +/// @brief pi keyword implementation +/// +void program::RpnPi(void) { + stack_.push_front(new Number(mpfr::const_pi())); +} + +/// @brief d->r keyword implementation +/// +void program::RpnD2r(void) { + MIN_ARGUMENTS(1); + ARG_MUST_BE_OF_TYPE(0, kNumber); + stack_.value(0) *= mpfr::const_pi(); + stack_.value(0) /= 180; +} + +/// @brief r->d keyword implementation +/// +void program::RpnR2d(void) { + MIN_ARGUMENTS(1); + ARG_MUST_BE_OF_TYPE(0, kNumber); + stack_.value(0) /= mpfr::const_pi(); + stack_.value(0) *= 180; +} + +/// @brief sin keyword implementation +/// +void program::RpnSin(void) { + MIN_ARGUMENTS(1); + if (stack_.type(0) == kNumber) + stack_.value(0) = sin(stack_.value(0)); + else if (stack_.type(0) == kComplex) + stack_.value(0) = sin(stack_.value(0)); + else + ERROR_CONTEXT(kBadOperandType); +} + +/// @brief asin keyword implementation +/// +void program::RpnAsin(void) { + MIN_ARGUMENTS(1); + if (stack_.type(0) == kNumber) + stack_.value(0) = asin(stack_.value(0)); + else if (stack_.type(0) == kComplex) + stack_.value(0) = asin(stack_.value(0)); + else + ERROR_CONTEXT(kBadOperandType); +} + +/// @brief cos keyword implementation +/// +void program::RpnCos(void) { + MIN_ARGUMENTS(1); + if (stack_.type(0) == kNumber) + stack_.value(0) = cos(stack_.value(0)); + else if (stack_.type(0) == kComplex) + stack_.value(0) = cos(stack_.value(0)); + else + ERROR_CONTEXT(kBadOperandType); +} + +/// @brief acos keyword implementation +/// +void program::RpnAcos(void) { + MIN_ARGUMENTS(1); + if (stack_.type(0) == kNumber) + stack_.value(0) = acos(stack_.value(0)); + else if (stack_.type(0) == kComplex) + stack_.value(0) = acos(stack_.value(0)); + else + ERROR_CONTEXT(kBadOperandType); +} + +/// @brief tan keyword implementation +/// +void program::RpnTan(void) { + MIN_ARGUMENTS(1); + if (stack_.type(0) == kNumber) + stack_.value(0) = tan(stack_.value(0)); + else if (stack_.type(0) == kComplex) + stack_.value(0) = tan(stack_.value(0)); + else + ERROR_CONTEXT(kBadOperandType); +} + +/// @brief atan keyword implementation +/// +void program::RpnAtan(void) { + MIN_ARGUMENTS(1); + if (stack_.type(0) == kNumber) + stack_.value(0) = atan(stack_.value(0)); + else if (stack_.type(0) == kComplex) + stack_.value(0) = atan(stack_.value(0)); + else + ERROR_CONTEXT(kBadOperandType); +} diff --git a/src/rpn-trig.cpp b/src/rpn-trig.cpp deleted file mode 100644 index 678c42d..0000000 --- a/src/rpn-trig.cpp +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright (c) 2014-2022 Louis Rubet - -#include "program.hpp" - -/// @brief pi keyword implementation -/// -void program::rpn_pi(void) { - _stack.push_front(new number(const_pi())); -} - -/// @brief d->r keyword implementation -/// -void program::rpn_d2r(void) { - MIN_ARGUMENTS(1); - ARG_MUST_BE_OF_TYPE(0, cmd_number); - _stack.value(0) *= const_pi(); - _stack.value(0) /= 180; -} - -/// @brief r->d keyword implementation -/// -void program::rpn_r2d(void) { - MIN_ARGUMENTS(1); - ARG_MUST_BE_OF_TYPE(0, cmd_number); - _stack.value(0) /= const_pi(); - _stack.value(0) *= 180; -} - -/// @brief sin keyword implementation -/// -void program::rpn_sin(void) { - MIN_ARGUMENTS(1); - if (_stack.type(0) == cmd_number) - _stack.value(0) = sin(_stack.value(0)); - else if (_stack.type(0) == cmd_complex) - _stack.value(0) = sin(_stack.value(0)); - else - setErrorContext(ret_bad_operand_type); -} - -/// @brief asin keyword implementation -/// -void program::rpn_asin(void) { - MIN_ARGUMENTS(1); - if (_stack.type(0) == cmd_number) - _stack.value(0) = asin(_stack.value(0)); - else if (_stack.type(0) == cmd_complex) - _stack.value(0) = asin(_stack.value(0)); - else - setErrorContext(ret_bad_operand_type); -} - -/// @brief cos keyword implementation -/// -void program::rpn_cos(void) { - MIN_ARGUMENTS(1); - if (_stack.type(0) == cmd_number) - _stack.value(0) = cos(_stack.value(0)); - else if (_stack.type(0) == cmd_complex) - _stack.value(0) = cos(_stack.value(0)); - else - setErrorContext(ret_bad_operand_type); -} - -/// @brief acos keyword implementation -/// -void program::rpn_acos(void) { - MIN_ARGUMENTS(1); - if (_stack.type(0) == cmd_number) - _stack.value(0) = acos(_stack.value(0)); - else if (_stack.type(0) == cmd_complex) - _stack.value(0) = acos(_stack.value(0)); - else - setErrorContext(ret_bad_operand_type); -} - -/// @brief tan keyword implementation -/// -void program::rpn_tan(void) { - MIN_ARGUMENTS(1); - if (_stack.type(0) == cmd_number) - _stack.value(0) = tan(_stack.value(0)); - else if (_stack.type(0) == cmd_complex) - _stack.value(0) = tan(_stack.value(0)); - else - setErrorContext(ret_bad_operand_type); -} - -/// @brief atan keyword implementation -/// -void program::rpn_atan(void) { - MIN_ARGUMENTS(1); - if (_stack.type(0) == cmd_number) - _stack.value(0) = atan(_stack.value(0)); - else if (_stack.type(0) == cmd_complex) - _stack.value(0) = atan(_stack.value(0)); - else - setErrorContext(ret_bad_operand_type); -} diff --git a/src/stack.hpp b/src/stack.h similarity index 78% rename from src/stack.hpp rename to src/stack.h index 1e55024..96cf050 100644 --- a/src/stack.hpp +++ b/src/stack.h @@ -3,28 +3,28 @@ #ifndef SRC_STACK_HPP_ #define SRC_STACK_HPP_ -#include #include #include #include -using namespace std; +#include +using std::deque, std::map, std::string; -#include "object.hpp" +#include "object.h" /// @brief stack object, parens of program, storing execution stack values or programs /// -class rpnstack : public deque { +class rpnstack : public deque { public: rpnstack() {} virtual ~rpnstack() { - for_each(begin(), end(), [](object* o) { delete o; }); + for_each(begin(), end(), [](Object* o) { delete o; }); deque::erase(begin(), end()); } // stack manipulation void erase(size_t first = 0, size_t nb = 1, bool del = true) { size_t last = std::min(first + nb, size()); - if (del) for_each(begin() + first, begin() + last, [](object* o) { delete o; }); + if (del) for_each(begin() + first, begin() + last, [](Object* o) { delete o; }); deque::erase(begin() + first, begin() + last); } @@ -32,7 +32,7 @@ class rpnstack : public deque { // access helpers // - cmd_type_t type(int level) { + ObjectType type(int level) { // carefull: caller must ensure that level is correct return at(level)->_type; } @@ -49,12 +49,12 @@ class rpnstack : public deque { return static_cast(at(level))->value; } - void push(object* o) { deque::push_front(o); } + void push(Object* o) { deque::push_front(o); } }; /// @brief heap object, storing variables (=named object) /// -class heap : public map { +class heap : public map { public: heap() {} virtual ~heap() { clear(); } @@ -64,7 +64,7 @@ class heap : public map { map::erase(begin(), end()); } - bool get(const string name, object*& obj) { + bool get(const string name, Object*& obj) { auto i = find(name); if (i != end()) { obj = i->second;