mirror of
https://github.com/louisrubet/rpn
synced 2025-01-30 20:34:30 +01:00
Merge pull request #237 from louisrubet/#218/google_style_naming
Apply google style naming conventions
This commit is contained in:
commit
9c7ad2f563
45 changed files with 3626 additions and 3644 deletions
|
@ -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
|
||||
|
|
12
Changelog.md
12
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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1,33 +1,34 @@
|
|||
// Copyright (c) 2014-2022 Louis Rubet
|
||||
|
||||
#include "input.hpp"
|
||||
#include "input.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
vector<string>* Input::_acWords = nullptr;
|
||||
vector<string>* Input::ac_list_ = nullptr;
|
||||
|
||||
Input::Input(string& entry, vector<string>& autocompletionWords, string prompt, string mlPrompt) : status(cont) {
|
||||
Input::Input(string& entry, vector<string>& 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<string>& 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<string>& 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);
|
||||
}
|
25
src/input.h
Normal file
25
src/input.h
Normal file
|
@ -0,0 +1,25 @@
|
|||
// Copyright (c) 2014-2022 Louis Rubet
|
||||
|
||||
#ifndef SRC_INPUT_HPP_
|
||||
#define SRC_INPUT_HPP_
|
||||
|
||||
#include <cstdio>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
using std::string, std::vector;
|
||||
|
||||
#include "linenoise.h"
|
||||
|
||||
class Input {
|
||||
public:
|
||||
Input(string& entry, vector<string>& 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<string>* ac_list_;
|
||||
};
|
||||
|
||||
#endif // SRC_INPUT_HPP_
|
|
@ -1,25 +0,0 @@
|
|||
// Copyright (c) 2014-2022 Louis Rubet
|
||||
|
||||
#ifndef SRC_INPUT_HPP_
|
||||
#define SRC_INPUT_HPP_
|
||||
|
||||
#include <cstdio>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
using namespace std;
|
||||
|
||||
#include "linenoise.h"
|
||||
|
||||
class Input {
|
||||
public:
|
||||
Input(string& entry, vector<string>& 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<string>* _acWords;
|
||||
};
|
||||
|
||||
#endif // SRC_INPUT_HPP_
|
|
@ -1,8 +1,8 @@
|
|||
// Copyright (c) 2014-2022 Louis Rubet
|
||||
|
||||
#include "lexer.hpp"
|
||||
#include "lexer.h"
|
||||
|
||||
bool Lexer::lexer(string& entry, map<string, ReservedWord>& keywords, vector<SynElement>& elements,
|
||||
bool Lexer::Analyse(string& entry, map<string, ReservedWord>& keywords, vector<SynElement>& elements,
|
||||
vector<SynError>& errors) {
|
||||
size_t jump;
|
||||
for (size_t i = 0; i < entry.size(); i++) {
|
||||
|
@ -10,31 +10,31 @@ bool Lexer::lexer(string& entry, map<string, ReservedWord>& keywords, vector<Syn
|
|||
SynElement element{.re = nullptr, .im = nullptr};
|
||||
switch (entry[i]) {
|
||||
case '"':
|
||||
if (!parseString(entry, i, jump, errors, elements)) return false;
|
||||
if (!ParseString(entry, i, jump, errors, elements)) return false;
|
||||
i = jump - 1;
|
||||
continue;
|
||||
case '\'':
|
||||
if (!parseSymbol(entry, i, jump, errors, elements)) return false;
|
||||
if (!ParseSymbol(entry, i, jump, errors, elements)) return false;
|
||||
i = jump - 1;
|
||||
continue;
|
||||
case '(':
|
||||
if (!parseComplex(entry, i, jump, errors, elements)) return false;
|
||||
if (!ParseComplex(entry, i, jump, errors, elements)) return false;
|
||||
i = jump - 1;
|
||||
continue;
|
||||
}
|
||||
if (i < entry.size() - 1 && (entry.substr(i, 2) == "<<" || entry.substr(i, 2) == "«")) {
|
||||
if (!parseProgram(entry, i, jump, errors, elements)) return false;
|
||||
if (!ParseProgram(entry, i, jump, errors, elements)) return false;
|
||||
i = jump - 1;
|
||||
continue;
|
||||
} else if (parseReserved(entry, i, jump, elements, keywords)) {
|
||||
} else if (ParseReserved(entry, i, jump, elements, keywords)) {
|
||||
// found a keywords word, add it with its correct type
|
||||
i = jump - 1;
|
||||
continue;
|
||||
} else if (parseNumber(entry, i, jump, errors, elements)) {
|
||||
} else if (ParseNumber(entry, i, jump, errors, elements)) {
|
||||
i = jump - 1;
|
||||
continue;
|
||||
}
|
||||
if (parseUnknown(entry, i, jump, elements))
|
||||
if (ParseUnknown(entry, i, jump, elements))
|
||||
// last chance, this unknown entry is treated as a symbol
|
||||
i = jump - 1;
|
||||
else
|
||||
|
@ -44,44 +44,44 @@ bool Lexer::lexer(string& entry, map<string, ReservedWord>& keywords, vector<Syn
|
|||
return true;
|
||||
}
|
||||
|
||||
void Lexer::trim(string& s) {
|
||||
void Lexer::Trim(string& s) {
|
||||
s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](unsigned char ch) { return !std::isspace(ch); }));
|
||||
s.erase(std::find_if(s.rbegin(), s.rend(), [](unsigned char ch) { return !std::isspace(ch); }).base(), s.end());
|
||||
}
|
||||
|
||||
bool Lexer::parseString(string& entry, size_t idx, size_t& nextIdx, vector<SynError>& errors,
|
||||
bool Lexer::ParseString(string& entry, size_t idx, size_t& next_idx, vector<SynError>& errors,
|
||||
vector<SynElement>& elements) {
|
||||
// here we are sure that entry[0] is at least '"'
|
||||
for (size_t i = idx + 1; i < entry.size(); i++) {
|
||||
if (entry[i] == '"') {
|
||||
if (entry[i] - 1 != '\\') {
|
||||
elements.push_back({cmd_string, .value = entry.substr(idx + 1, i - idx - 1)});
|
||||
nextIdx = i + 1;
|
||||
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<SynError>& errors,
|
||||
bool Lexer::ParseSymbol(string& entry, size_t idx, size_t& next_idx, vector<SynError>& errors,
|
||||
vector<SynElement>& elements) {
|
||||
// here we are sure that entry[0] is at least '\''
|
||||
for (size_t i = idx + 1; i < entry.size(); i++) {
|
||||
if (entry[i] == '\'') {
|
||||
elements.push_back({cmd_symbol, .value = entry.substr(idx + 1, i - idx - 1), .autoEval = false});
|
||||
nextIdx = i + 1;
|
||||
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<SynError>& errors,
|
||||
bool Lexer::ParseProgram(string& entry, size_t idx, size_t& next_idx, vector<SynError>& errors,
|
||||
vector<SynElement>& 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<SynE
|
|||
} else if ((entry[i] == '>' && entry[i + 1] == '>') || (entry.substr(i, 2) == "»")) {
|
||||
if (countNested == 0) {
|
||||
string prg = entry.substr(idx + 2, i - idx - 2);
|
||||
trim(prg);
|
||||
elements.push_back({cmd_program, .value = prg});
|
||||
nextIdx = i + 2;
|
||||
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<SynE
|
|||
}
|
||||
}
|
||||
string prg = entry.substr(idx + 2, entry.size() - idx - 2);
|
||||
trim(prg);
|
||||
elements.push_back({cmd_program, .value = prg});
|
||||
nextIdx = entry.size();
|
||||
Trim(prg);
|
||||
elements.push_back({kProgram, .value = prg});
|
||||
next_idx = entry.size();
|
||||
return true;
|
||||
}
|
||||
|
||||
int Lexer::getBaseAt(string& entry, size_t& nextIdx, bool& positive) {
|
||||
int Lexer::GetBaseAt(string& entry, size_t& next_idx, bool& positive) {
|
||||
// a regex could be "([+-])?((0[xX])|([0-9][0-9]?[bB]))"
|
||||
// regex is not use because dramatically slow
|
||||
// entry is scanned from idxStart, searching for [s]abc (sign and 3 first chars)
|
||||
size_t scan = 0;
|
||||
nextIdx = 0;
|
||||
next_idx = 0;
|
||||
positive = true;
|
||||
if (scan >= 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<int>(a - '0');
|
||||
}
|
||||
if (isdigit(b) && (c == 'b' || c == 'B')) {
|
||||
nextIdx = scan + 3;
|
||||
next_idx = scan + 3;
|
||||
return 10 * static_cast<int>(a - '0') + static_cast<int>(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<SynError>& errors,
|
||||
bool Lexer::ParseNumber(string& entry, size_t idx, size_t& next_idx, vector<SynError>& errors,
|
||||
vector<SynElement>& 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<SynEr
|
|||
}
|
||||
}
|
||||
|
||||
bool Lexer::parseComplex(string& entry, size_t idx, size_t& nextIdx, vector<SynError>& errors,
|
||||
bool Lexer::ParseComplex(string& entry, size_t idx, size_t& next_idx, vector<SynError>& errors,
|
||||
vector<SynElement>& 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<SynElement>& elements,
|
||||
bool Lexer::ParseReserved(string& entry, size_t idx, size_t& next_idx, vector<SynElement>& elements,
|
||||
map<string, ReservedWord>& keywords) {
|
||||
stringstream ss(entry.substr(idx));
|
||||
string token;
|
||||
|
@ -239,17 +239,17 @@ bool Lexer::parseReserved(string& entry, size_t idx, size_t& nextIdx, vector<Syn
|
|||
auto resa = keywords.find(token);
|
||||
if (resa != keywords.end()) {
|
||||
elements.push_back({resa->second.type, .value = token, .fn = resa->second.fn});
|
||||
nextIdx = token.size() + idx;
|
||||
next_idx = token.size() + idx;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Lexer::parseUnknown(string& entry, size_t idx, size_t& nextIdx, vector<SynElement>& elements) {
|
||||
bool Lexer::ParseUnknown(string& entry, size_t idx, size_t& next_idx, vector<SynElement>& elements) {
|
||||
stringstream ss(entry.substr(idx));
|
||||
string token;
|
||||
ss >> token;
|
||||
elements.push_back({cmd_symbol, .value = token, .autoEval = true});
|
||||
nextIdx = token.size() + idx;
|
||||
elements.push_back({kSymbol, .value = token, .auto_eval = true});
|
||||
next_idx = token.size() + idx;
|
||||
return true;
|
||||
}
|
|
@ -4,27 +4,27 @@
|
|||
#define SRC_LEXER_HPP_
|
||||
|
||||
#include <mpreal.h>
|
||||
using namespace mpfr;
|
||||
using mpfr::mpreal;
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
using namespace std;
|
||||
#include <vector>
|
||||
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<string, ReservedWord>& keywords, vector<SynElement>& elements,
|
||||
vector<SynError>& errors);
|
||||
bool Analyse(string& entry, map<string, ReservedWord>& keywords, vector<SynElement>& elements,
|
||||
vector<SynError>& errors);
|
||||
|
||||
private:
|
||||
bool parseString(string& entry, size_t idx, size_t& nextIdx, vector<SynError>& errors,
|
||||
bool ParseString(string& entry, size_t idx, size_t& next_idx, vector<SynError>& errors,
|
||||
vector<SynElement>& elements);
|
||||
bool parseSymbol(string& entry, size_t idx, size_t& nextIdx, vector<SynError>& errors,
|
||||
bool ParseSymbol(string& entry, size_t idx, size_t& next_idx, vector<SynError>& errors,
|
||||
vector<SynElement>& elements);
|
||||
bool parseProgram(string& entry, size_t idx, size_t& nextIdx, vector<SynError>& errors,
|
||||
bool ParseProgram(string& entry, size_t idx, size_t& next_idx, vector<SynError>& errors,
|
||||
vector<SynElement>& elements);
|
||||
bool parseNumber(string& entry, size_t idx, size_t& nextIdx, vector<SynError>& errors,
|
||||
bool ParseNumber(string& entry, size_t idx, size_t& next_idx, vector<SynError>& errors,
|
||||
vector<SynElement>& elements);
|
||||
bool parseComplex(string& entry, size_t idx, size_t& nextIdx, vector<SynError>& errors,
|
||||
bool ParseComplex(string& entry, size_t idx, size_t& next_idx, vector<SynError>& errors,
|
||||
vector<SynElement>& elements);
|
||||
bool parseReserved(string& entry, size_t idx, size_t& nextIdx, vector<SynElement>& elements,
|
||||
bool ParseReserved(string& entry, size_t idx, size_t& next_idx, vector<SynElement>& elements,
|
||||
map<string, ReservedWord>& keywords);
|
||||
bool parseUnknown(string& entry, size_t idx, size_t& nextIdx, vector<SynElement>& elements);
|
||||
bool ParseUnknown(string& entry, size_t idx, size_t& next_idx, vector<SynElement>& 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_
|
|
@ -6,19 +6,19 @@
|
|||
#include <cerrno>
|
||||
#include <csignal>
|
||||
#include <iostream>
|
||||
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;
|
||||
}
|
|
@ -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
|
||||
|
16
src/mpreal-out.h
Normal file
16
src/mpreal-out.h
Normal file
|
@ -0,0 +1,16 @@
|
|||
// Copyright (c) 2014-2022 Louis Rubet
|
||||
|
||||
#ifndef SRC_MPREAL_OUT_HPP_
|
||||
#define SRC_MPREAL_OUT_HPP_
|
||||
|
||||
#include <mpreal.h>
|
||||
using mpfr::mpreal;
|
||||
|
||||
#include <string>
|
||||
#include <ostream>
|
||||
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_
|
|
@ -1,16 +0,0 @@
|
|||
// Copyright (c) 2014-2022 Louis Rubet
|
||||
|
||||
#ifndef SRC_MPREAL_OUT_HPP_
|
||||
#define SRC_MPREAL_OUT_HPP_
|
||||
|
||||
#include <mpreal.h>
|
||||
using namespace mpfr;
|
||||
|
||||
#include <string>
|
||||
#include <ostream>
|
||||
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_
|
7
src/object.cc
Normal file
7
src/object.cc
Normal file
|
@ -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;
|
|
@ -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;
|
215
src/object.h
Normal file
215
src/object.h
Normal file
|
@ -0,0 +1,215 @@
|
|||
// Copyright (c) 2014-2022 Louis Rubet
|
||||
|
||||
#ifndef SRC_OBJECT_H_
|
||||
#define SRC_OBJECT_H_
|
||||
|
||||
#include <mpreal.h>
|
||||
using mpfr::mpreal;
|
||||
|
||||
#include <complex>
|
||||
#include <ostream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
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<mpreal>& 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<mpreal> 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<size_t>(-1);
|
||||
arg2 = static_cast<size_t>(-1);
|
||||
arg3 = static_cast<size_t>(-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_
|
227
src/object.hpp
227
src/object.hpp
|
@ -1,227 +0,0 @@
|
|||
// Copyright (c) 2014-2022 Louis Rubet
|
||||
|
||||
#ifndef SRC_OBJECT_HPP_
|
||||
#define SRC_OBJECT_HPP_
|
||||
|
||||
#include <mpreal.h>
|
||||
|
||||
#include "mpreal-out.hpp"
|
||||
using namespace mpfr;
|
||||
|
||||
#include <ostream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
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<mpreal>& 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<mpreal> 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<size_t>(-1);
|
||||
arg2 = static_cast<size_t>(-1);
|
||||
arg3 = static_cast<size_t>(-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_
|
718
src/program.cc
Normal file
718
src/program.cc
Normal file
|
@ -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::keyword_t> 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 <test-instruction> then <true-instructions> else <false-instructions> "
|
||||
"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> <end> start <instructions> next|<step> step"},
|
||||
{kBranch, "for", (program_fn_t)&program::RpnFor,
|
||||
"<start> <end> for <variable> <instructions> next|<step> 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, <test-instruction> <true-instruction> ift"},
|
||||
{kKeyword, "ifte", &program::RpnIfte,
|
||||
"similar to if-then-else-end, <test-instruction> <true-instruction> "
|
||||
"<false-instruction> ifte"},
|
||||
{kBranch, "do", (program_fn_t)&program::RpnDo, "do <instructions> until <condition> end"},
|
||||
{kBranch, "until", (program_fn_t)&program::RpnUntil, "used with do"},
|
||||
{kBranch, "while", (program_fn_t)&program::RpnWhile, "while <test-instruction> repeat <loop-instructions> 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<string>& program::GetAutocompletionWords() {
|
||||
static vector<string> 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<Symbol*>(o));
|
||||
i++;
|
||||
break;
|
||||
|
||||
// a keyword
|
||||
case kKeyword: {
|
||||
Keyword* k = reinterpret_cast<Keyword*>(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<Branch*>(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<struct if_layout_t> vlayout;
|
||||
int layout_index = -1;
|
||||
// for start-end-step
|
||||
vector<int> 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<Branch*>(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<Branch*>(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<Branch*>(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<Branch*>(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<Branch*>(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<Branch*>(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<Branch*>(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<string, Lexer::ReservedWord> keywords_map;
|
||||
vector<Lexer::SynElement> elements;
|
||||
vector<Lexer::SynError> 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<RetValue, string> 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);
|
||||
}
|
717
src/program.cpp
717
src/program.cpp
|
@ -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::keyword_t> 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 <test-instruction> then <true-instructions> else <false-instructions> "
|
||||
"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> <end> start <instructions> next|<step> step"},
|
||||
{cmd_branch, "for", (program_fn_t)&program::rpn_for,
|
||||
"<start> <end> for <variable> <instructions> next|<step> 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, <test-instruction> <true-instruction> ift"},
|
||||
{cmd_keyword, "ifte", &program::rpn_ifte,
|
||||
"similar to if-then-else-end, <test-instruction> <true-instruction> "
|
||||
"<false-instruction> ifte"},
|
||||
{cmd_branch, "do", (program_fn_t)&program::rpn_do, "do <instructions> until <condition> end"},
|
||||
{cmd_branch, "until", (program_fn_t)&program::rpn_until, "used with do"},
|
||||
{cmd_branch, "while", (program_fn_t)&program::rpn_while, "while <test-instruction> repeat <loop-instructions> 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<string>& program::getAutocompletionWords() {
|
||||
static vector<string> 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<symbol*>(o));
|
||||
i++;
|
||||
break;
|
||||
|
||||
// a keyword
|
||||
case cmd_keyword: {
|
||||
keyword* k = reinterpret_cast<keyword*>(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<branch*>(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<struct if_layout_t> vlayout;
|
||||
int layout_index = -1;
|
||||
// for start-end-step
|
||||
vector<int> 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<branch*>(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<branch*>(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<branch*>(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<branch*>(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<branch*>(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<branch*>(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<branch*>(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<string, Lexer::ReservedWord> keywordsMap;
|
||||
vector<Lexer::SynElement> elements;
|
||||
vector<Lexer::SynError> errors;
|
||||
ret_value ret = ret_ok;
|
||||
|
||||
// prepare map for finding reserved keywords
|
||||
if (keywordsMap.empty())
|
||||
for (auto& kw : _keywords)
|
||||
if (!kw.name.empty()) keywordsMap[kw.name] = {kw.type, kw.fn};
|
||||
|
||||
// separate the entry string
|
||||
if (lexer(entry, keywordsMap, elements, errors)) {
|
||||
// make objects from parsed elements
|
||||
for (Lexer::SynElement& element : elements) {
|
||||
switch (element.type) {
|
||||
case cmd_number:
|
||||
push_back(new number(*element.re, element.reBase));
|
||||
break;
|
||||
case cmd_complex:
|
||||
push_back(new ocomplex(*element.re, *element.im, element.reBase, element.imBase));
|
||||
break;
|
||||
case cmd_string:
|
||||
push_back(new ostring(element.value));
|
||||
break;
|
||||
case cmd_symbol:
|
||||
push_back(new symbol(element.value, element.autoEval));
|
||||
break;
|
||||
case cmd_program:
|
||||
push_back(new oprogram(element.value));
|
||||
break;
|
||||
case cmd_keyword:
|
||||
push_back(new keyword(element.fn, element.value));
|
||||
break;
|
||||
case cmd_branch:
|
||||
push_back(new branch((branch_fn_t)element.fn, element.value));
|
||||
break;
|
||||
default:
|
||||
show_error(ret_unknown_err, "error creating program from entry");
|
||||
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<string> errorStrings {"ok", "unknown command", "missing operand", "bad operand type",
|
||||
"out of range", "unknown variable", "internal error, aborting", "deadly", "goodbye", "not implemented",
|
||||
"no operation", "syntax error", "division by zero", "runtime error", "aborted current entry", "out of memory",
|
||||
"bad value", "test failed"};
|
||||
// clang-format on
|
||||
// show last recorded error
|
||||
if (static_cast<size_t>(_err) < errorStrings.size())
|
||||
cerr << _err_context << ": error " << _err << ": " << errorStrings[_err] << endl;
|
||||
else
|
||||
cerr << _err_context << " (unknown error code)" << endl;
|
||||
switch (_err) {
|
||||
case ret_internal:
|
||||
case ret_deadly:
|
||||
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);
|
||||
}
|
289
src/program.h
Normal file
289
src/program.h
Normal file
|
@ -0,0 +1,289 @@
|
|||
// Copyright (c) 2014-2022 Louis Rubet
|
||||
|
||||
#ifndef SRC_PROGRAM_HPP_
|
||||
#define SRC_PROGRAM_HPP_
|
||||
|
||||
// std c++
|
||||
#include <deque>
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
using namespace std;
|
||||
|
||||
#include <mpreal.h>
|
||||
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<Object*>, 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<string>& 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<keyword_t> 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<size_t>(-1), kRtError = static_cast<size_t>(-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_
|
291
src/program.hpp
291
src/program.hpp
|
@ -1,291 +0,0 @@
|
|||
// Copyright (c) 2014-2022 Louis Rubet
|
||||
|
||||
#ifndef SRC_PROGRAM_HPP_
|
||||
#define SRC_PROGRAM_HPP_
|
||||
|
||||
// std c++
|
||||
#include <deque>
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
using namespace std;
|
||||
|
||||
#include <mpreal.h>
|
||||
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<object*>, 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<string>& 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<keyword_t> _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<size_t>(-1), runtime_error = static_cast<size_t>(-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_
|
393
src/rpn-branch.cc
Normal file
393
src/rpn-branch.cc
Normal file
|
@ -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<Number>(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<Branch*>(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<Branch*>(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<Number>(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<Number>(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<Number>(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<Number>(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<Number>(1);
|
||||
myobj.last_index = stack_.value<Number>(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<Symbol*>(at(myobj.arg1)); // arg1 = loop variable index
|
||||
|
||||
// loop boundaries
|
||||
myobj.first_index = stack_.value<Number>(1);
|
||||
myobj.last_index = stack_.value<Number>(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<Number>(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<Branch*>(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<Symbol*>(at(start_or_for->arg1));
|
||||
|
||||
// store symbol variable (asserted existing in the local heap)
|
||||
reinterpret_cast<Number*>(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<Number>(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<Branch*>(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<Symbol*>(at(start_or_for->arg1));
|
||||
// increase symbol variable
|
||||
reinterpret_cast<Number*>(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;
|
||||
}
|
|
@ -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<number>(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<branch*>(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<branch*>(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<number>(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<number>(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<number>(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<number>(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<number>(1);
|
||||
myobj.lastIndex = _stack.value<number>(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<symbol*>(at(myobj.arg1)); // arg1 = loop variable index
|
||||
|
||||
// loop boundaries
|
||||
myobj.firstIndex = _stack.value<number>(1);
|
||||
myobj.lastIndex = _stack.value<number>(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<number>(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<branch*>(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<symbol*>(at(start_or_for->arg1));
|
||||
|
||||
// store symbol variable (asserted existing in the local heap)
|
||||
reinterpret_cast<number*>(_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<number>(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<branch*>(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<symbol*>(at(start_or_for->arg1));
|
||||
// increase symbol variable
|
||||
reinterpret_cast<number*>(_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;
|
||||
}
|
85
src/rpn-complex.cc
Normal file
85
src/rpn-complex.cc
Normal file
|
@ -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<Complex>(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<Complex>(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<Complex>(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<Complex>(0) = conj(stack_.value<Complex>(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<Number>(1), stack_.value<Number>(0), stack_.obj<Complex>(1).re_base, stack_.obj<Complex>(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<Complex>(0)), stack_.obj<Complex>(0).re_base));
|
||||
stack_.push(new Number(imag(stack_.value<Complex>(1)), stack_.obj<Complex>(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<Complex>(0));
|
||||
mpreal theta = arg(stack_.value<Complex>(0));
|
||||
stack_.value<Complex>(0).real(rho);
|
||||
stack_.value<Complex>(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<Complex>(0) = polar(abs(stack_.value<Complex>(0)), arg(stack_.value<Complex>(0)));
|
||||
}
|
|
@ -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<ocomplex>(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<ocomplex>(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<ocomplex>(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<ocomplex>(0) = conj(_stack.value<ocomplex>(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<number>(1), _stack.value<number>(0), _stack.obj<ocomplex>(1).reBase, _stack.obj<ocomplex>(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<ocomplex>(0)), _stack.obj<ocomplex>(0).reBase));
|
||||
_stack.push(new number(imag(_stack.value<ocomplex>(1)), _stack.obj<ocomplex>(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<ocomplex>(0));
|
||||
mpreal theta = arg(_stack.value<ocomplex>(0));
|
||||
_stack.value<ocomplex>(0).real(rho);
|
||||
_stack.value<ocomplex>(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<ocomplex>(0) = polar(abs(_stack.value<ocomplex>(0)), arg(_stack.value<ocomplex>(0)));
|
||||
}
|
228
src/rpn-general.cc
Normal file
228
src/rpn-general.cc
Normal file
|
@ -0,0 +1,228 @@
|
|||
// Copyright (c) 2014-2022 Louis Rubet
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
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<int>(mpreal::get_default_prec()) << " bits" << endl;
|
||||
vector<pair<string, mpfr_rnd_t>> 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<int>(stack_.value<Number>(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<int>(stack_.value<Number>(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<int>(stack_.value<Number>(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<int>(stack_.value<Number>(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<string, mpfr_rnd_t> matchRound{MPFR_ROUND};
|
||||
|
||||
auto found = matchRound.find(stack_.value<String>(0));
|
||||
if (found != matchRound.end())
|
||||
mpreal::set_default_rnd(found->second);
|
||||
else
|
||||
ERROR_CONTEXT(kOutOfRange);
|
||||
stack_.pop();
|
||||
}
|
|
@ -1,234 +0,0 @@
|
|||
// Copyright (c) 2014-2022 Louis Rubet
|
||||
|
||||
#include <cstdio>
|
||||
#include <string>
|
||||
|
||||
#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<string, mpfr_rnd_t> _mpfr_round{{"nearest (even)", MPFR_RNDN},
|
||||
{"toward zero", MPFR_RNDZ},
|
||||
{"toward +inf", MPFR_RNDU},
|
||||
{"toward -inf", MPFR_RNDD},
|
||||
{"away from zero", MPFR_RNDA},
|
||||
{"faithful rounding", MPFR_RNDF},
|
||||
{"nearest (away from zero)", MPFR_RNDNA}};
|
||||
|
||||
/// @brief nop keyword implementation
|
||||
///
|
||||
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<int>(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<int>(_stack.value<number>(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<int>(_stack.value<number>(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<int>(_stack.value<number>(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<int>(_stack.value<number>(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<string, mpfr_rnd_t> matchRound{_mpfr_round};
|
||||
|
||||
auto found = matchRound.find(_stack.value<ostring>(0));
|
||||
if (found != matchRound.end())
|
||||
mpreal::set_default_rnd(found->second);
|
||||
else
|
||||
setErrorContext(ret_out_of_range);
|
||||
_stack.pop();
|
||||
}
|
175
src/rpn-logs.cc
Normal file
175
src/rpn-logs.cc
Normal file
|
@ -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<Number>(0) = log10(stack_.value<Number>(0));
|
||||
else if (stack_.type(0) == kComplex)
|
||||
stack_.value<Complex>(0) = log10(stack_.value<Complex>(0));
|
||||
else
|
||||
ERROR_CONTEXT(kBadOperandType);
|
||||
}
|
||||
|
||||
/// @brief alog10 keyword implementation
|
||||
///
|
||||
void program::RpnAlog10() {
|
||||
MIN_ARGUMENTS(1);
|
||||
if (stack_.type(0) == kNumber)
|
||||
stack_.value<Number>(0) = exp(log(mpreal(10)) * stack_.value<Number>(0));
|
||||
else if (stack_.type(0) == kComplex)
|
||||
stack_.value<Complex>(0) = exp(log(mpreal(10)) * stack_.value<Complex>(0));
|
||||
else
|
||||
ERROR_CONTEXT(kBadOperandType);
|
||||
}
|
||||
|
||||
/// @brief log2 keyword implementation
|
||||
///
|
||||
void program::RpnLog2() {
|
||||
MIN_ARGUMENTS(1);
|
||||
if (stack_.type(0) == kNumber)
|
||||
stack_.value<Number>(0) = log(stack_.value<Number>(0)) / mpfr::const_log2();
|
||||
else if (stack_.type(0) == kComplex)
|
||||
stack_.value<Complex>(0) = log(stack_.value<Complex>(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<Number>(0) = exp(mpfr::const_log2() * stack_.value<Number>(0));
|
||||
else if (stack_.type(0) == kComplex)
|
||||
stack_.value<Complex>(0) = exp(mpfr::const_log2() * stack_.value<Complex>(0));
|
||||
else
|
||||
ERROR_CONTEXT(kBadOperandType);
|
||||
}
|
||||
|
||||
/// @brief ln keyword implementation
|
||||
///
|
||||
void program::RpnLn() {
|
||||
MIN_ARGUMENTS(1);
|
||||
if (stack_.type(0) == kNumber)
|
||||
stack_.value<Number>(0) = log(stack_.value<Number>(0));
|
||||
else if (stack_.type(0) == kComplex)
|
||||
stack_.value<Complex>(0) = log(stack_.value<Complex>(0));
|
||||
else
|
||||
ERROR_CONTEXT(kBadOperandType);
|
||||
}
|
||||
|
||||
/// @brief exp keyword implementation
|
||||
///
|
||||
void program::RpnExp() {
|
||||
MIN_ARGUMENTS(1);
|
||||
if (stack_.type(0) == kNumber)
|
||||
stack_.value<Number>(0) = exp(stack_.value<Number>(0));
|
||||
else if (stack_.type(0) == kComplex)
|
||||
stack_.value<Complex>(0) = exp(stack_.value<Complex>(0));
|
||||
else
|
||||
ERROR_CONTEXT(kBadOperandType);
|
||||
}
|
||||
|
||||
/// @brief expm keyword implementation
|
||||
///
|
||||
void program::RpnExpm() {
|
||||
MIN_ARGUMENTS(1);
|
||||
if (stack_.type(0) == kNumber)
|
||||
stack_.value<Number>(0) = exp(stack_.value<Number>(0)) - mpreal(1);
|
||||
else if (stack_.type(0) == kComplex)
|
||||
stack_.value<Complex>(0) = exp(stack_.value<Complex>(0)) - mpreal(1);
|
||||
else
|
||||
ERROR_CONTEXT(kBadOperandType);
|
||||
}
|
||||
|
||||
/// @brief lnp1 keyword implementation
|
||||
///
|
||||
void program::RpnLnp1() {
|
||||
MIN_ARGUMENTS(1);
|
||||
if (stack_.type(0) == kNumber)
|
||||
stack_.value<Number>(0) = log(stack_.value<Number>(0) + 1);
|
||||
else if (stack_.type(0) == kComplex)
|
||||
stack_.value<Complex>(0) = log(stack_.value<Complex>(0) + mpreal(1));
|
||||
else
|
||||
ERROR_CONTEXT(kBadOperandType);
|
||||
}
|
||||
|
||||
/// @brief sinh keyword implementation
|
||||
///
|
||||
void program::RpnSinh() {
|
||||
MIN_ARGUMENTS(1);
|
||||
if (stack_.type(0) == kNumber)
|
||||
stack_.value<Number>(0) = sinh(stack_.value<Number>(0));
|
||||
else if (stack_.type(0) == kComplex)
|
||||
stack_.value<Complex>(0) = sinh(stack_.value<Complex>(0));
|
||||
else
|
||||
ERROR_CONTEXT(kBadOperandType);
|
||||
}
|
||||
|
||||
/// @brief asinh keyword implementation
|
||||
///
|
||||
void program::RpnAsinh() {
|
||||
MIN_ARGUMENTS(1);
|
||||
if (stack_.type(0) == kNumber)
|
||||
stack_.value<Number>(0) = asinh(stack_.value<Number>(0));
|
||||
else if (stack_.type(0) == kComplex)
|
||||
stack_.value<Complex>(0) = asinh(stack_.value<Complex>(0));
|
||||
else
|
||||
ERROR_CONTEXT(kBadOperandType);
|
||||
}
|
||||
|
||||
/// @brief cosh keyword implementation
|
||||
///
|
||||
void program::RpnCosh() {
|
||||
MIN_ARGUMENTS(1);
|
||||
if (stack_.type(0) == kNumber)
|
||||
stack_.value<Number>(0) = cosh(stack_.value<Number>(0));
|
||||
else if (stack_.type(0) == kComplex)
|
||||
stack_.value<Complex>(0) = cosh(stack_.value<Complex>(0));
|
||||
else
|
||||
ERROR_CONTEXT(kBadOperandType);
|
||||
}
|
||||
|
||||
/// @brief acosh keyword implementation
|
||||
///
|
||||
void program::RpnAcosh() {
|
||||
MIN_ARGUMENTS(1);
|
||||
if (stack_.type(0) == kNumber)
|
||||
stack_.value<Number>(0) = acosh(stack_.value<Number>(0));
|
||||
else if (stack_.type(0) == kComplex)
|
||||
stack_.value<Complex>(0) = acosh(stack_.value<Complex>(0));
|
||||
else
|
||||
ERROR_CONTEXT(kBadOperandType);
|
||||
}
|
||||
|
||||
/// @brief tanh keyword implementation
|
||||
///
|
||||
void program::RpnTanh() {
|
||||
MIN_ARGUMENTS(1);
|
||||
if (stack_.type(0) == kNumber)
|
||||
stack_.value<Number>(0) = tanh(stack_.value<Number>(0));
|
||||
else if (stack_.type(0) == kComplex)
|
||||
stack_.value<Complex>(0) = tanh(stack_.value<Complex>(0));
|
||||
else
|
||||
ERROR_CONTEXT(kBadOperandType);
|
||||
}
|
||||
|
||||
/// @brief atanh keyword implementation
|
||||
///
|
||||
void program::RpnAtanh() {
|
||||
MIN_ARGUMENTS(1);
|
||||
if (stack_.type(0) == kNumber)
|
||||
stack_.value<Number>(0) = atanh(stack_.value<Number>(0));
|
||||
else if (stack_.type(0) == kComplex)
|
||||
stack_.value<Complex>(0) = atanh(stack_.value<Complex>(0));
|
||||
else
|
||||
ERROR_CONTEXT(kBadOperandType);
|
||||
}
|
175
src/rpn-logs.cpp
175
src/rpn-logs.cpp
|
@ -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<number>(0) = log10(_stack.value<number>(0));
|
||||
else if (_stack.type(0) == cmd_complex)
|
||||
_stack.value<ocomplex>(0) = log10(_stack.value<ocomplex>(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<number>(0) = exp(log(mpreal(10)) * _stack.value<number>(0));
|
||||
else if (_stack.type(0) == cmd_complex)
|
||||
_stack.value<ocomplex>(0) = exp(log(mpreal(10)) * _stack.value<ocomplex>(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<number>(0) = log(_stack.value<number>(0)) / const_log2();
|
||||
else if (_stack.type(0) == cmd_complex)
|
||||
_stack.value<ocomplex>(0) = log(_stack.value<ocomplex>(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<number>(0) = exp(const_log2() * _stack.value<number>(0));
|
||||
else if (_stack.type(0) == cmd_complex)
|
||||
_stack.value<ocomplex>(0) = exp(const_log2() * _stack.value<ocomplex>(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<number>(0) = log(_stack.value<number>(0));
|
||||
else if (_stack.type(0) == cmd_complex)
|
||||
_stack.value<ocomplex>(0) = log(_stack.value<ocomplex>(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<number>(0) = exp(_stack.value<number>(0));
|
||||
else if (_stack.type(0) == cmd_complex)
|
||||
_stack.value<ocomplex>(0) = exp(_stack.value<ocomplex>(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<number>(0) = exp(_stack.value<number>(0)) - mpreal(1);
|
||||
else if (_stack.type(0) == cmd_complex)
|
||||
_stack.value<ocomplex>(0) = exp(_stack.value<ocomplex>(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<number>(0) = log(_stack.value<number>(0) + 1);
|
||||
else if (_stack.type(0) == cmd_complex)
|
||||
_stack.value<ocomplex>(0) = log(_stack.value<ocomplex>(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<number>(0) = sinh(_stack.value<number>(0));
|
||||
else if (_stack.type(0) == cmd_complex)
|
||||
_stack.value<ocomplex>(0) = sinh(_stack.value<ocomplex>(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<number>(0) = asinh(_stack.value<number>(0));
|
||||
else if (_stack.type(0) == cmd_complex)
|
||||
_stack.value<ocomplex>(0) = asinh(_stack.value<ocomplex>(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<number>(0) = cosh(_stack.value<number>(0));
|
||||
else if (_stack.type(0) == cmd_complex)
|
||||
_stack.value<ocomplex>(0) = cosh(_stack.value<ocomplex>(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<number>(0) = acosh(_stack.value<number>(0));
|
||||
else if (_stack.type(0) == cmd_complex)
|
||||
_stack.value<ocomplex>(0) = acosh(_stack.value<ocomplex>(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<number>(0) = tanh(_stack.value<number>(0));
|
||||
else if (_stack.type(0) == cmd_complex)
|
||||
_stack.value<ocomplex>(0) = tanh(_stack.value<ocomplex>(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<number>(0) = atanh(_stack.value<number>(0));
|
||||
else if (_stack.type(0) == cmd_complex)
|
||||
_stack.value<ocomplex>(0) = atanh(_stack.value<ocomplex>(0));
|
||||
else
|
||||
setErrorContext(ret_bad_operand_type);
|
||||
}
|
152
src/rpn-program.cc
Normal file
152
src/rpn-program.cc
Normal file
|
@ -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<Symbol>(0));
|
||||
stack_.pop();
|
||||
|
||||
// if variable holds a program, run this program
|
||||
if (FindVariable(variable, obj)) {
|
||||
if (obj->_type == kProgram) {
|
||||
prog_text = stack_.value<Program>(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<Program>(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
|
||||
// -> <auto evaluated symbol #1> ... <auto evaluated symbol #n> <program>
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
// <program> 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<Symbol*>(at(i))->value] = stack_.at(0)->Clone();
|
||||
stack_.pop();
|
||||
}
|
||||
|
||||
// run the program
|
||||
string& entry = reinterpret_cast<Program*>(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;
|
||||
}
|
|
@ -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<symbol>(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<oprogram>(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<oprogram>(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
|
||||
// -> <auto evaluated symbol #1> ... <auto evaluated symbol #n> <oprogram>
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
// <oprogram> 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<symbol*>(at(i))->value] = _stack.at(0)->clone();
|
||||
_stack.pop();
|
||||
}
|
||||
|
||||
// run the program
|
||||
string& entry = reinterpret_cast<oprogram*>(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;
|
||||
}
|
389
src/rpn-real.cc
Normal file
389
src/rpn-real.cc
Normal file
|
@ -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<String>(1) += stack_.value<String>(0);
|
||||
stack_.pop();
|
||||
} else if (stack_.type(0) == kNumber && stack_.type(1) == kNumber) {
|
||||
stack_.value<Number>(1) += stack_.value<Number>(0);
|
||||
stack_.pop();
|
||||
} else if (stack_.type(0) == kComplex && stack_.type(1) == kComplex) {
|
||||
stack_.value<Complex>(1) += stack_.value<Complex>(0);
|
||||
stack_.pop();
|
||||
} else if (stack_.type(0) == kNumber && stack_.type(1) == kComplex) {
|
||||
stack_.value<Complex>(1) += stack_.value<Number>(0);
|
||||
stack_.pop();
|
||||
} else if (stack_.type(0) == kComplex && stack_.type(1) == kNumber) {
|
||||
RpnSwap();
|
||||
stack_.value<Complex>(1) += stack_.value<Number>(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<Number>(1) -= stack_.value<Number>(0);
|
||||
stack_.pop();
|
||||
} else if (stack_.type(0) == kComplex && stack_.type(1) == kComplex) {
|
||||
stack_.value<Complex>(1) -= stack_.value<Complex>(0);
|
||||
stack_.pop();
|
||||
} else if (stack_.type(0) == kNumber && stack_.type(1) == kComplex) {
|
||||
stack_.value<Complex>(1) -= stack_.value<Number>(0);
|
||||
stack_.pop();
|
||||
} else if (stack_.type(0) == kComplex && stack_.type(1) == kNumber) {
|
||||
RpnSwap();
|
||||
stack_.value<Complex>(1) = stack_.value<Number>(0) - stack_.value<Complex>(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<Number>(1) *= stack_.value<Number>(0);
|
||||
stack_.pop();
|
||||
} else if (stack_.type(0) == kComplex && stack_.type(1) == kComplex) {
|
||||
stack_.value<Complex>(1) *= stack_.value<Complex>(0);
|
||||
stack_.pop();
|
||||
} else if (stack_.type(0) == kNumber && stack_.type(1) == kComplex) {
|
||||
stack_.value<Complex>(1) *= stack_.value<Number>(0);
|
||||
stack_.pop();
|
||||
} else if (stack_.type(0) == kComplex && stack_.type(1) == kNumber) {
|
||||
RpnSwap();
|
||||
stack_.value<Complex>(1) *= stack_.value<Number>(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<Number>(1) /= stack_.value<Number>(0);
|
||||
stack_.pop();
|
||||
} else if (stack_.type(0) == kComplex && stack_.type(1) == kComplex) {
|
||||
stack_.value<Complex>(1) /= stack_.value<Complex>(0);
|
||||
stack_.pop();
|
||||
} else if (stack_.type(0) == kNumber && stack_.type(1) == kComplex) {
|
||||
stack_.value<Complex>(1) /= stack_.value<Number>(0);
|
||||
stack_.pop();
|
||||
} else if (stack_.type(0) == kComplex && stack_.type(1) == kNumber) {
|
||||
RpnSwap();
|
||||
stack_.value<Complex>(1) = stack_.value<Number>(0) / stack_.value<Complex>(1);
|
||||
stack_.pop();
|
||||
} else {
|
||||
ERROR_CONTEXT(kBadOperandType);
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief neg keyword implementation
|
||||
///
|
||||
void program::RpnNeg() {
|
||||
MIN_ARGUMENTS(1);
|
||||
if (stack_.type(0) == kNumber)
|
||||
stack_.value<Number>(0) = -stack_.value<Number>(0);
|
||||
else if (stack_.type(0) == kComplex)
|
||||
stack_.value<Complex>(0) = -stack_.value<Complex>(0);
|
||||
else
|
||||
ERROR_CONTEXT(kBadOperandType);
|
||||
}
|
||||
|
||||
/// @brief inv keyword implementation
|
||||
///
|
||||
void program::RpnInv() {
|
||||
MIN_ARGUMENTS(1);
|
||||
if (stack_.type(0) == kNumber)
|
||||
stack_.value<Number>(0) = 1 / stack_.value<Number>(0);
|
||||
else if (stack_.type(0) == kComplex)
|
||||
stack_.value<Complex>(0) = mpreal(1) / stack_.value<Complex>(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<Number>(1) >= 0) {
|
||||
stack_.value<Number>(1) = pow(stack_.value<Number>(1), stack_.value<Number>(0));
|
||||
stack_.pop();
|
||||
} else {
|
||||
mpreal zero;
|
||||
stack_.push(new Complex(stack_.value<Number>(1), zero, stack_.obj<Number>(1).base));
|
||||
stack_.value<Complex>(0) = pow(stack_.value<Complex>(0), stack_.value<Number>(1));
|
||||
stack_.erase(1, 2);
|
||||
}
|
||||
} else if (stack_.type(0) == kComplex && stack_.type(1) == kComplex) {
|
||||
stack_.value<Complex>(1) = pow(stack_.value<Complex>(1), stack_.value<Complex>(0));
|
||||
stack_.pop();
|
||||
} else if (stack_.type(0) == kNumber && stack_.type(1) == kComplex) {
|
||||
stack_.value<Complex>(1) = pow(stack_.value<Complex>(1), stack_.value<Number>(0));
|
||||
stack_.pop();
|
||||
} else if (stack_.type(0) == kComplex && stack_.type(1) == kNumber) {
|
||||
RpnSwap();
|
||||
stack_.value<Complex>(1) = pow(stack_.value<Number>(0), stack_.value<Complex>(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<Number>(0) >= 0) {
|
||||
stack_.value<Number>(0) = sqrt(stack_.value<Number>(0));
|
||||
} else {
|
||||
// negative number -> square root is complex
|
||||
mpreal zero;
|
||||
stack_.push(new Complex(stack_.value<Number>(0), zero,
|
||||
stack_.obj<Number>(0).base)); // TODO(louis) manage new errors
|
||||
stack_.value<Complex>(0) = sqrt(stack_.value<Complex>(0));
|
||||
stack_.erase(1);
|
||||
}
|
||||
} else if (stack_.type(0) == kComplex) {
|
||||
stack_.value<Complex>(0) = sqrt(stack_.value<Complex>(0));
|
||||
} else {
|
||||
ERROR_CONTEXT(kBadOperandType);
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief hex keyword implementation
|
||||
///
|
||||
void program::RpnHex() {
|
||||
MIN_ARGUMENTS(1);
|
||||
if (stack_.type(0) == kNumber) {
|
||||
stack_.obj<Number>(0).base = 16;
|
||||
} else if (stack_.type(0) == kComplex) {
|
||||
stack_.obj<Complex>(0).re_base = 16;
|
||||
stack_.obj<Complex>(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<Number>(0).base = 2;
|
||||
} else if (stack_.type(0) == kComplex) {
|
||||
stack_.obj<Complex>(0).re_base = 2;
|
||||
stack_.obj<Complex>(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<Number>(0).base = 10;
|
||||
} else if (stack_.type(0) == kComplex) {
|
||||
stack_.obj<Complex>(0).re_base = 10;
|
||||
stack_.obj<Complex>(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<int>(stack_.value<Number>(0).toLong());
|
||||
stack_.pop();
|
||||
if (base >= 2 && base <= 62) {
|
||||
if (stack_.type(0) == kNumber) {
|
||||
stack_.obj<Number>(0).base = base;
|
||||
} else {
|
||||
stack_.obj<Complex>(0).re_base = base;
|
||||
stack_.obj<Complex>(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<Number>(1) *= stack_.value<Number>(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<Number>(1) = (stack_.value<Number>(0) * 100) / stack_.value<Number>(1);
|
||||
stack_.pop();
|
||||
}
|
||||
|
||||
/// @brief sq keyword implementation
|
||||
///
|
||||
void program::RpnSquare() {
|
||||
MIN_ARGUMENTS(1);
|
||||
if (stack_.type(0) == kNumber)
|
||||
stack_.value<Number>(0) *= stack_.value<Number>(0);
|
||||
else if (stack_.at(0)->_type == kComplex)
|
||||
stack_.value<Complex>(0) *= stack_.value<Complex>(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<Number>(1) = fmod(stack_.value<Number>(1), stack_.value<Number>(0));
|
||||
stack_.pop();
|
||||
}
|
||||
|
||||
/// @brief abs keyword implementation
|
||||
///
|
||||
void program::RpnAbs() {
|
||||
MIN_ARGUMENTS(1);
|
||||
if (stack_.type(0) == kNumber) {
|
||||
stack_.value<Number>(0) = abs(stack_.value<Number>(0));
|
||||
} else if (stack_.type(0) == kComplex) {
|
||||
stack_.push(new Number(abs(stack_.value<Complex>(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<Number>(0) = gamma(stack_.value<Number>(0) + 1);
|
||||
}
|
||||
|
||||
/// @brief sign keyword implementation
|
||||
///
|
||||
void program::RpnSign() {
|
||||
MIN_ARGUMENTS(1);
|
||||
if (stack_.type(0) == kNumber)
|
||||
stack_.value<Number>(0) = sgn(stack_.value<Number>(0));
|
||||
else if (stack_.at(0)->_type == kComplex)
|
||||
stack_.value<Complex>(0) = stack_.value<Complex>(0) / abs(stack_.value<Complex>(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<Number>(0))) {
|
||||
ERROR_CONTEXT(kOutOfRange);
|
||||
return;
|
||||
}
|
||||
mp_exp_t exp;
|
||||
stack_.value<Number>(0) = frexp(stack_.value<Number>(0), &exp);
|
||||
}
|
||||
|
||||
/// @brief xpon keyword implementation
|
||||
///
|
||||
void program::RpnXpon() {
|
||||
MIN_ARGUMENTS(1);
|
||||
ARG_MUST_BE_OF_TYPE(0, kNumber);
|
||||
if (!isfinite(stack_.value<Number>(0))) {
|
||||
ERROR_CONTEXT(kOutOfRange);
|
||||
return;
|
||||
}
|
||||
mp_exp_t exp;
|
||||
(void)frexp(stack_.value<Number>(0), &exp);
|
||||
stack_.value<Number>(0) = exp;
|
||||
}
|
||||
|
||||
/// @brief floor keyword implementation
|
||||
///
|
||||
void program::RpnFloor() {
|
||||
MIN_ARGUMENTS(1);
|
||||
ARG_MUST_BE_OF_TYPE(0, kNumber);
|
||||
stack_.value<Number>(0) = floor(stack_.value<Number>(0));
|
||||
}
|
||||
|
||||
/// @brief ceil keyword implementation
|
||||
///
|
||||
void program::RpnCeil() {
|
||||
MIN_ARGUMENTS(1);
|
||||
ARG_MUST_BE_OF_TYPE(0, kNumber);
|
||||
stack_.value<Number>(0) = ceil(stack_.value<Number>(0));
|
||||
}
|
||||
|
||||
/// @brief fp keyword implementation
|
||||
///
|
||||
void program::RpnFp() {
|
||||
MIN_ARGUMENTS(1);
|
||||
ARG_MUST_BE_OF_TYPE(0, kNumber);
|
||||
stack_.value<Number>(0) = frac(stack_.value<Number>(0));
|
||||
}
|
||||
|
||||
/// @brief ip keyword implementation
|
||||
///
|
||||
void program::RpnIp() {
|
||||
MIN_ARGUMENTS(1);
|
||||
ARG_MUST_BE_OF_TYPE(0, kNumber);
|
||||
stack_.value<Number>(0) = trunc(stack_.value<Number>(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<Number>(0) = min(stack_.value<Number>(0), stack_.value<Number>(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<Number>(0) = max(stack_.value<Number>(0), stack_.value<Number>(1));
|
||||
stack_.erase(1);
|
||||
}
|
389
src/rpn-real.cpp
389
src/rpn-real.cpp
|
@ -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<ostring>(1) += _stack.value<ostring>(0);
|
||||
_stack.pop();
|
||||
} else if (_stack.type(0) == cmd_number && _stack.type(1) == cmd_number) {
|
||||
_stack.value<number>(1) += _stack.value<number>(0);
|
||||
_stack.pop();
|
||||
} else if (_stack.type(0) == cmd_complex && _stack.type(1) == cmd_complex) {
|
||||
_stack.value<ocomplex>(1) += _stack.value<ocomplex>(0);
|
||||
_stack.pop();
|
||||
} else if (_stack.type(0) == cmd_number && _stack.type(1) == cmd_complex) {
|
||||
_stack.value<ocomplex>(1) += _stack.value<number>(0);
|
||||
_stack.pop();
|
||||
} else if (_stack.type(0) == cmd_complex && _stack.type(1) == cmd_number) {
|
||||
rpn_swap();
|
||||
_stack.value<ocomplex>(1) += _stack.value<number>(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<number>(1) -= _stack.value<number>(0);
|
||||
_stack.pop();
|
||||
} else if (_stack.type(0) == cmd_complex && _stack.type(1) == cmd_complex) {
|
||||
_stack.value<ocomplex>(1) -= _stack.value<ocomplex>(0);
|
||||
_stack.pop();
|
||||
} else if (_stack.type(0) == cmd_number && _stack.type(1) == cmd_complex) {
|
||||
_stack.value<ocomplex>(1) -= _stack.value<number>(0);
|
||||
_stack.pop();
|
||||
} else if (_stack.type(0) == cmd_complex && _stack.type(1) == cmd_number) {
|
||||
rpn_swap();
|
||||
_stack.value<ocomplex>(1) = _stack.value<number>(0) - _stack.value<ocomplex>(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<number>(1) *= _stack.value<number>(0);
|
||||
_stack.pop();
|
||||
} else if (_stack.type(0) == cmd_complex && _stack.type(1) == cmd_complex) {
|
||||
_stack.value<ocomplex>(1) *= _stack.value<ocomplex>(0);
|
||||
_stack.pop();
|
||||
} else if (_stack.type(0) == cmd_number && _stack.type(1) == cmd_complex) {
|
||||
_stack.value<ocomplex>(1) *= _stack.value<number>(0);
|
||||
_stack.pop();
|
||||
} else if (_stack.type(0) == cmd_complex && _stack.type(1) == cmd_number) {
|
||||
rpn_swap();
|
||||
_stack.value<ocomplex>(1) *= _stack.value<number>(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<number>(1) /= _stack.value<number>(0);
|
||||
_stack.pop();
|
||||
} else if (_stack.type(0) == cmd_complex && _stack.type(1) == cmd_complex) {
|
||||
_stack.value<ocomplex>(1) /= _stack.value<ocomplex>(0);
|
||||
_stack.pop();
|
||||
} else if (_stack.type(0) == cmd_number && _stack.type(1) == cmd_complex) {
|
||||
_stack.value<ocomplex>(1) /= _stack.value<number>(0);
|
||||
_stack.pop();
|
||||
} else if (_stack.type(0) == cmd_complex && _stack.type(1) == cmd_number) {
|
||||
rpn_swap();
|
||||
_stack.value<ocomplex>(1) = _stack.value<number>(0) / _stack.value<ocomplex>(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<number>(0) = -_stack.value<number>(0);
|
||||
else if (_stack.type(0) == cmd_complex)
|
||||
_stack.value<ocomplex>(0) = -_stack.value<ocomplex>(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<number>(0) = 1 / _stack.value<number>(0);
|
||||
else if (_stack.type(0) == cmd_complex)
|
||||
_stack.value<ocomplex>(0) = mpreal(1) / _stack.value<ocomplex>(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<number>(1) >= 0) {
|
||||
_stack.value<number>(1) = pow(_stack.value<number>(1), _stack.value<number>(0));
|
||||
_stack.pop();
|
||||
} else {
|
||||
mpreal zero;
|
||||
_stack.push(new ocomplex(_stack.value<number>(1), zero, _stack.obj<number>(1).base));
|
||||
_stack.value<ocomplex>(0) = pow(_stack.value<ocomplex>(0), _stack.value<number>(1));
|
||||
_stack.erase(1, 2);
|
||||
}
|
||||
} else if (_stack.type(0) == cmd_complex && _stack.type(1) == cmd_complex) {
|
||||
_stack.value<ocomplex>(1) = pow(_stack.value<ocomplex>(1), _stack.value<ocomplex>(0));
|
||||
_stack.pop();
|
||||
} else if (_stack.type(0) == cmd_number && _stack.type(1) == cmd_complex) {
|
||||
_stack.value<ocomplex>(1) = pow(_stack.value<ocomplex>(1), _stack.value<number>(0));
|
||||
_stack.pop();
|
||||
} else if (_stack.type(0) == cmd_complex && _stack.type(1) == cmd_number) {
|
||||
rpn_swap();
|
||||
_stack.value<ocomplex>(1) = pow(_stack.value<number>(0), _stack.value<ocomplex>(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<number>(0) >= 0) {
|
||||
_stack.value<number>(0) = sqrt(_stack.value<number>(0));
|
||||
} else {
|
||||
// negative number -> square root is complex
|
||||
mpreal zero;
|
||||
_stack.push(new ocomplex(_stack.value<number>(0), zero,
|
||||
_stack.obj<number>(0).base)); // TODO(louis) manage new errors
|
||||
_stack.value<ocomplex>(0) = sqrt(_stack.value<ocomplex>(0));
|
||||
_stack.erase(1);
|
||||
}
|
||||
} else if (_stack.type(0) == cmd_complex) {
|
||||
_stack.value<ocomplex>(0) = sqrt(_stack.value<ocomplex>(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<number>(0).base = 16;
|
||||
} else if (_stack.type(0) == cmd_complex) {
|
||||
_stack.obj<ocomplex>(0).reBase = 16;
|
||||
_stack.obj<ocomplex>(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<number>(0).base = 2;
|
||||
} else if (_stack.type(0) == cmd_complex) {
|
||||
_stack.obj<ocomplex>(0).reBase = 2;
|
||||
_stack.obj<ocomplex>(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<number>(0).base = 10;
|
||||
} else if (_stack.type(0) == cmd_complex) {
|
||||
_stack.obj<ocomplex>(0).reBase = 10;
|
||||
_stack.obj<ocomplex>(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<int>(_stack.value<number>(0).toLong());
|
||||
_stack.pop();
|
||||
if (base >= 2 && base <= 62) {
|
||||
if (_stack.type(0) == cmd_number) {
|
||||
_stack.obj<number>(0).base = base;
|
||||
} else {
|
||||
_stack.obj<ocomplex>(0).reBase = base;
|
||||
_stack.obj<ocomplex>(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<number>(1) *= _stack.value<number>(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<number>(1) = (_stack.value<number>(0) * 100) / _stack.value<number>(1);
|
||||
_stack.pop();
|
||||
}
|
||||
|
||||
/// @brief sq keyword implementation
|
||||
///
|
||||
void program::rpn_square() {
|
||||
MIN_ARGUMENTS(1);
|
||||
if (_stack.type(0) == cmd_number)
|
||||
_stack.value<number>(0) *= _stack.value<number>(0);
|
||||
else if (_stack.at(0)->_type == cmd_complex)
|
||||
_stack.value<ocomplex>(0) *= _stack.value<ocomplex>(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<number>(1) = fmod(_stack.value<number>(1), _stack.value<number>(0));
|
||||
_stack.pop();
|
||||
}
|
||||
|
||||
/// @brief abs keyword implementation
|
||||
///
|
||||
void program::rpn_abs() {
|
||||
MIN_ARGUMENTS(1);
|
||||
if (_stack.type(0) == cmd_number) {
|
||||
_stack.value<number>(0) = abs(_stack.value<number>(0));
|
||||
} else if (_stack.type(0) == cmd_complex) {
|
||||
_stack.push(new number(abs(_stack.value<ocomplex>(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<number>(0) = gamma(_stack.value<number>(0) + 1);
|
||||
}
|
||||
|
||||
/// @brief sign keyword implementation
|
||||
///
|
||||
void program::rpn_sign() {
|
||||
MIN_ARGUMENTS(1);
|
||||
if (_stack.type(0) == cmd_number)
|
||||
_stack.value<number>(0) = sgn(_stack.value<number>(0));
|
||||
else if (_stack.at(0)->_type == cmd_complex)
|
||||
_stack.value<ocomplex>(0) = _stack.value<ocomplex>(0) / abs(_stack.value<ocomplex>(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<number>(0))) {
|
||||
setErrorContext(ret_out_of_range);
|
||||
return;
|
||||
}
|
||||
mp_exp_t exp;
|
||||
_stack.value<number>(0) = frexp(_stack.value<number>(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<number>(0))) {
|
||||
setErrorContext(ret_out_of_range);
|
||||
return;
|
||||
}
|
||||
mp_exp_t exp;
|
||||
(void)frexp(_stack.value<number>(0), &exp);
|
||||
_stack.value<number>(0) = exp;
|
||||
}
|
||||
|
||||
/// @brief floor keyword implementation
|
||||
///
|
||||
void program::rpn_floor() {
|
||||
MIN_ARGUMENTS(1);
|
||||
ARG_MUST_BE_OF_TYPE(0, cmd_number);
|
||||
_stack.value<number>(0) = floor(_stack.value<number>(0));
|
||||
}
|
||||
|
||||
/// @brief ceil keyword implementation
|
||||
///
|
||||
void program::rpn_ceil() {
|
||||
MIN_ARGUMENTS(1);
|
||||
ARG_MUST_BE_OF_TYPE(0, cmd_number);
|
||||
_stack.value<number>(0) = ceil(_stack.value<number>(0));
|
||||
}
|
||||
|
||||
/// @brief fp keyword implementation
|
||||
///
|
||||
void program::rpn_fp() {
|
||||
MIN_ARGUMENTS(1);
|
||||
ARG_MUST_BE_OF_TYPE(0, cmd_number);
|
||||
_stack.value<number>(0) = frac(_stack.value<number>(0));
|
||||
}
|
||||
|
||||
/// @brief ip keyword implementation
|
||||
///
|
||||
void program::rpn_ip() {
|
||||
MIN_ARGUMENTS(1);
|
||||
ARG_MUST_BE_OF_TYPE(0, cmd_number);
|
||||
_stack.value<number>(0) = trunc(_stack.value<number>(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<number>(0) = min(_stack.value<number>(0), _stack.value<number>(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<number>(0) = max(_stack.value<number>(0), _stack.value<number>(1));
|
||||
_stack.erase(1);
|
||||
}
|
137
src/rpn-stack.cc
Normal file
137
src/rpn-stack.cc
Normal file
|
@ -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<int>(stack_.value<Number>(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<int>(stack_.value<Number>(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<int>(stack_.value<Number>(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<int>(stack_.value<Number>(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<int>(stack_.value<Number>(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());
|
||||
}
|
|
@ -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<int>(_stack.value<number>(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<int>(_stack.value<number>(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<int>(_stack.value<number>(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<int>(_stack.value<number>(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<int>(_stack.value<number>(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());
|
||||
}
|
225
src/rpn-store.cc
Normal file
225
src/rpn-store.cc
Normal file
|
@ -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<String>(0));
|
||||
if (it != heap_.end()) {
|
||||
delete it->second;
|
||||
heap_.erase(it);
|
||||
}
|
||||
heap_[stack_.value<String>(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<String>(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<String>(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<String>(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<String>(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<String>(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<String>(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<Symbol>(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<Symbol>(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(); }
|
|
@ -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<ostring>(0));
|
||||
if (it != _heap.end()) {
|
||||
delete it->second;
|
||||
_heap.erase(it);
|
||||
}
|
||||
_heap[_stack.value<ostring>(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<ostring>(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<ostring>(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<ostring>(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<ostring>(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<ostring>(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<ostring>(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<symbol>(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<symbol>(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(); }
|
96
src/rpn-string.cc
Normal file
96
src/rpn-string.cc
Normal file
|
@ -0,0 +1,96 @@
|
|||
// Copyright (c) 2014-2022 Louis Rubet
|
||||
|
||||
#include <fcntl.h>
|
||||
|
||||
#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<String>(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<char>(stack_.value<Number>(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<String>(0).size() > 0)
|
||||
stack_.push_front(new Number(stack_.value<String>(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<String>(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<String>(1).find(stack_.value<String>(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<Number>(1).toULong();
|
||||
size_t len = stack_.value<Number>(0).toULong() - first + 1;
|
||||
first--;
|
||||
|
||||
if (first > stack_.value<String>(2).size()) first = len = 0;
|
||||
stack_.push(new String(stack_.value<String>(2).substr(first, len)));
|
||||
stack_.erase(1, 3);
|
||||
}
|
|
@ -1,96 +0,0 @@
|
|||
// Copyright (c) 2014-2022 Louis Rubet
|
||||
|
||||
#include <fcntl.h>
|
||||
|
||||
#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<ostring>(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<char>(_stack.value<number>(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<ostring>(0).size() > 0)
|
||||
_stack.push_front(new number(_stack.value<ostring>(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<ostring>(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<ostring>(1).find(_stack.value<ostring>(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<number>(1).toULong();
|
||||
size_t len = _stack.value<number>(0).toULong() - first + 1;
|
||||
first--;
|
||||
|
||||
if (first > _stack.value<ostring>(2).size()) first = len = 0;
|
||||
_stack.push(new ostring(_stack.value<ostring>(2).substr(first, len)));
|
||||
_stack.erase(1, 3);
|
||||
}
|
|
@ -1,21 +1,21 @@
|
|||
// Copyright (c) 2014-2022 Louis Rubet
|
||||
|
||||
#include <string>
|
||||
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<int>(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<ostring>(0);
|
||||
_stack.pop();
|
||||
string test_filename = stack_.value<String>(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<int>(prog.get_err());
|
||||
(void)prog.Run();
|
||||
last_err = static_cast<int>(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;
|
166
src/rpn-test.cc
Normal file
166
src/rpn-test.cc
Normal file
|
@ -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<String>(1).compare(stk.value<String>(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<Number>(1) > stack_.value<Number>(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<Number>(1) >= stack_.value<Number>(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<Number>(1) < stack_.value<Number>(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<Number>(1) <= stack_.value<Number>(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<Number>(1) != stack_.value<Number>(0)));
|
||||
stack_.erase(1, 2);
|
||||
} else if (stack_.type(0) == kComplex && stack_.type(1) == kComplex) {
|
||||
stack_.push_front(new Number(stack_.value<Complex>(1) != stack_.value<Complex>(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<Number>(1) == stack_.value<Number>(0)));
|
||||
stack_.erase(1, 2);
|
||||
} else if (stack_.type(0) == kComplex && stack_.type(1) == kComplex) {
|
||||
stack_.push_front(new Number(stack_.value<Complex>(1) == stack_.value<Complex>(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<Number>(0) != 0 && stack_.value<Number>(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<Number>(0) != 0 || stack_.value<Number>(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<Number>(0) != 0 ^ stack_.value<Number>(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<Number>(0) == 0 ? 1 : 0));
|
||||
stack_.erase(1, 1);
|
||||
}
|
||||
|
||||
/// @brief test same implementation
|
||||
///
|
||||
void program::RpnSame(void) { RpnEq(); }
|
166
src/rpn-test.cpp
166
src/rpn-test.cpp
|
@ -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<ostring>(1).compare(stk.value<ostring>(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<number>(1) > _stack.value<number>(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<number>(1) >= _stack.value<number>(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<number>(1) < _stack.value<number>(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<number>(1) <= _stack.value<number>(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<number>(1) != _stack.value<number>(0)));
|
||||
_stack.erase(1, 2);
|
||||
} else if (_stack.type(0) == cmd_complex && _stack.type(1) == cmd_complex) {
|
||||
_stack.push_front(new number(_stack.value<ocomplex>(1) != _stack.value<ocomplex>(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<number>(1) == _stack.value<number>(0)));
|
||||
_stack.erase(1, 2);
|
||||
} else if (_stack.type(0) == cmd_complex && _stack.type(1) == cmd_complex) {
|
||||
_stack.push_front(new number(_stack.value<ocomplex>(1) == _stack.value<ocomplex>(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<number>(0) != 0 && _stack.value<number>(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<number>(0) != 0 || _stack.value<number>(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<number>(0) != 0 ^ _stack.value<number>(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<number>(0) == 0 ? 1 : 0));
|
||||
_stack.erase(1, 1);
|
||||
}
|
||||
|
||||
/// @brief test same implementation
|
||||
///
|
||||
void program::rpn_same(void) { rpn_eq(); }
|
|
@ -2,11 +2,11 @@
|
|||
|
||||
#include <ctime>
|
||||
|
||||
#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<number>(0) /= 10000000000.0;
|
||||
stack_.push(new Number(date));
|
||||
stack_.value<Number>(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<double>(tm->tm_year + 1900);
|
||||
|
||||
// push it
|
||||
number* num;
|
||||
Number* num;
|
||||
// division after push for real precision
|
||||
_stack.push(new number(date));
|
||||
_stack.value<number>(0) /= 1000000.0;
|
||||
stack_.push(new Number(date));
|
||||
stack_.value<Number>(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<double>(ts.tv_sec) + static_cast<double>(ts.tv_nsec / 1000);
|
||||
_stack.push(new number(date));
|
||||
stack_.push(new Number(date));
|
||||
} else {
|
||||
setErrorContext(ret_internal);
|
||||
ERROR_CONTEXT(kInternalError);
|
||||
}
|
||||
}
|
99
src/rpn-trig.cc
Normal file
99
src/rpn-trig.cc
Normal file
|
@ -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<Number>(0) *= mpfr::const_pi();
|
||||
stack_.value<Number>(0) /= 180;
|
||||
}
|
||||
|
||||
/// @brief r->d keyword implementation
|
||||
///
|
||||
void program::RpnR2d(void) {
|
||||
MIN_ARGUMENTS(1);
|
||||
ARG_MUST_BE_OF_TYPE(0, kNumber);
|
||||
stack_.value<Number>(0) /= mpfr::const_pi();
|
||||
stack_.value<Number>(0) *= 180;
|
||||
}
|
||||
|
||||
/// @brief sin keyword implementation
|
||||
///
|
||||
void program::RpnSin(void) {
|
||||
MIN_ARGUMENTS(1);
|
||||
if (stack_.type(0) == kNumber)
|
||||
stack_.value<Number>(0) = sin(stack_.value<Number>(0));
|
||||
else if (stack_.type(0) == kComplex)
|
||||
stack_.value<Complex>(0) = sin(stack_.value<Complex>(0));
|
||||
else
|
||||
ERROR_CONTEXT(kBadOperandType);
|
||||
}
|
||||
|
||||
/// @brief asin keyword implementation
|
||||
///
|
||||
void program::RpnAsin(void) {
|
||||
MIN_ARGUMENTS(1);
|
||||
if (stack_.type(0) == kNumber)
|
||||
stack_.value<Number>(0) = asin(stack_.value<Number>(0));
|
||||
else if (stack_.type(0) == kComplex)
|
||||
stack_.value<Complex>(0) = asin(stack_.value<Complex>(0));
|
||||
else
|
||||
ERROR_CONTEXT(kBadOperandType);
|
||||
}
|
||||
|
||||
/// @brief cos keyword implementation
|
||||
///
|
||||
void program::RpnCos(void) {
|
||||
MIN_ARGUMENTS(1);
|
||||
if (stack_.type(0) == kNumber)
|
||||
stack_.value<Number>(0) = cos(stack_.value<Number>(0));
|
||||
else if (stack_.type(0) == kComplex)
|
||||
stack_.value<Complex>(0) = cos(stack_.value<Complex>(0));
|
||||
else
|
||||
ERROR_CONTEXT(kBadOperandType);
|
||||
}
|
||||
|
||||
/// @brief acos keyword implementation
|
||||
///
|
||||
void program::RpnAcos(void) {
|
||||
MIN_ARGUMENTS(1);
|
||||
if (stack_.type(0) == kNumber)
|
||||
stack_.value<Number>(0) = acos(stack_.value<Number>(0));
|
||||
else if (stack_.type(0) == kComplex)
|
||||
stack_.value<Complex>(0) = acos(stack_.value<Complex>(0));
|
||||
else
|
||||
ERROR_CONTEXT(kBadOperandType);
|
||||
}
|
||||
|
||||
/// @brief tan keyword implementation
|
||||
///
|
||||
void program::RpnTan(void) {
|
||||
MIN_ARGUMENTS(1);
|
||||
if (stack_.type(0) == kNumber)
|
||||
stack_.value<Number>(0) = tan(stack_.value<Number>(0));
|
||||
else if (stack_.type(0) == kComplex)
|
||||
stack_.value<Complex>(0) = tan(stack_.value<Complex>(0));
|
||||
else
|
||||
ERROR_CONTEXT(kBadOperandType);
|
||||
}
|
||||
|
||||
/// @brief atan keyword implementation
|
||||
///
|
||||
void program::RpnAtan(void) {
|
||||
MIN_ARGUMENTS(1);
|
||||
if (stack_.type(0) == kNumber)
|
||||
stack_.value<Number>(0) = atan(stack_.value<Number>(0));
|
||||
else if (stack_.type(0) == kComplex)
|
||||
stack_.value<Complex>(0) = atan(stack_.value<Complex>(0));
|
||||
else
|
||||
ERROR_CONTEXT(kBadOperandType);
|
||||
}
|
|
@ -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<number>(0) *= const_pi();
|
||||
_stack.value<number>(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<number>(0) /= const_pi();
|
||||
_stack.value<number>(0) *= 180;
|
||||
}
|
||||
|
||||
/// @brief sin keyword implementation
|
||||
///
|
||||
void program::rpn_sin(void) {
|
||||
MIN_ARGUMENTS(1);
|
||||
if (_stack.type(0) == cmd_number)
|
||||
_stack.value<number>(0) = sin(_stack.value<number>(0));
|
||||
else if (_stack.type(0) == cmd_complex)
|
||||
_stack.value<ocomplex>(0) = sin(_stack.value<ocomplex>(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<number>(0) = asin(_stack.value<number>(0));
|
||||
else if (_stack.type(0) == cmd_complex)
|
||||
_stack.value<ocomplex>(0) = asin(_stack.value<ocomplex>(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<number>(0) = cos(_stack.value<number>(0));
|
||||
else if (_stack.type(0) == cmd_complex)
|
||||
_stack.value<ocomplex>(0) = cos(_stack.value<ocomplex>(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<number>(0) = acos(_stack.value<number>(0));
|
||||
else if (_stack.type(0) == cmd_complex)
|
||||
_stack.value<ocomplex>(0) = acos(_stack.value<ocomplex>(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<number>(0) = tan(_stack.value<number>(0));
|
||||
else if (_stack.type(0) == cmd_complex)
|
||||
_stack.value<ocomplex>(0) = tan(_stack.value<ocomplex>(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<number>(0) = atan(_stack.value<number>(0));
|
||||
else if (_stack.type(0) == cmd_complex)
|
||||
_stack.value<ocomplex>(0) = atan(_stack.value<ocomplex>(0));
|
||||
else
|
||||
setErrorContext(ret_bad_operand_type);
|
||||
}
|
|
@ -3,28 +3,28 @@
|
|||
#ifndef SRC_STACK_HPP_
|
||||
#define SRC_STACK_HPP_
|
||||
|
||||
#include <algorithm>
|
||||
#include <deque>
|
||||
#include <map>
|
||||
#include <string>
|
||||
using namespace std;
|
||||
#include <algorithm>
|
||||
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<object*> {
|
||||
class rpnstack : public deque<Object*> {
|
||||
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<object*> {
|
|||
|
||||
// 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<object*> {
|
|||
return static_cast<objectType*>(at(level))->value;
|
||||
}
|
||||
|
||||
void push(object* o) { deque<object*>::push_front(o); }
|
||||
void push(Object* o) { deque<Object*>::push_front(o); }
|
||||
};
|
||||
|
||||
/// @brief heap object, storing variables (=named object)
|
||||
///
|
||||
class heap : public map<string, object*> {
|
||||
class heap : public map<string, Object*> {
|
||||
public:
|
||||
heap() {}
|
||||
virtual ~heap() { clear(); }
|
||||
|
@ -64,7 +64,7 @@ class heap : public map<string, object*> {
|
|||
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;
|
Loading…
Add table
Reference in a new issue