Merge pull request #237 from louisrubet/#218/google_style_naming

Apply google style naming conventions
This commit is contained in:
Louis Rubet 2022-02-25 16:19:56 +01:00 committed by GitHub
commit 9c7ad2f563
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
45 changed files with 3626 additions and 3644 deletions

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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
View 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_

View file

@ -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_

View file

@ -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;
}

View file

@ -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_

View file

@ -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;
}

View file

@ -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
View 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_

View file

@ -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
View 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;

View file

@ -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
View 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_

View file

@ -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
View 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);
}

View file

@ -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
View 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_

View file

@ -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
View 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;
}

View file

@ -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
View 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)));
}

View file

@ -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
View 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();
}

View file

@ -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
View 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);
}

View file

@ -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
View 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;
}

View file

@ -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
View 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);
}

View file

@ -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
View 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());
}

View file

@ -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
View 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(); }

View file

@ -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
View 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);
}

View file

@ -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);
}

View file

@ -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
View 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(); }

View file

@ -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(); }

View file

@ -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
View 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);
}

View file

@ -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);
}

View file

@ -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;