Merge pull request #226 from louisrubet/#218/stack_heap_refactoring

Code refactoring - mpreal, md tests, std allocation
This commit is contained in:
Louis Rubet 2022-02-21 16:04:48 +01:00 committed by GitHub
commit d590642a6c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
71 changed files with 6960 additions and 6541 deletions

6
.gitignore vendored
View file

@ -22,5 +22,9 @@
*.x86_64
*.hex
# editors garbage
# Tools workfiles
*~
.scannerwork/*
*bw-output*/*
.vscode/*
build/*

3
.gitmodules vendored
View file

@ -4,3 +4,6 @@
[submodule "linenoise-ng"]
path = linenoise-ng
url = git@github.com:louisrubet/linenoise-ng.git
[submodule "mpreal"]
path = mpreal
url = https://github.com/advanpix/mpreal

View file

@ -24,7 +24,8 @@ set(RPN_DESCRIPTION_FILE "${PROJECT_SOURCE_DIR}/README.md")
# compiler options
if(CMAKE_COMPILER_IS_GNUCXX)
message(STATUS "Compiler type GNU: ${CMAKE_CXX_COMPILER}")
set(BASE_COMPILER_OPTIONS "-std=c++0x -Wl,--no-as-needed")
# TODO still up to date?
set(BASE_COMPILER_OPTIONS "-std=c++14 -Wl,--no-as-needed")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${BASE_COMPILER_OPTIONS}")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${BASE_COMPILER_OPTIONS} -O0 -g")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${BASE_COMPILER_OPTIONS} -O3 -fomit-frame-pointer -s")
@ -37,16 +38,25 @@ if(NOT EXISTS "${PROJECT_SOURCE_DIR}/linenoise-ng/.git")
execute_process(COMMAND git checkout v1.1.1-rpn WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/linenoise-ng)
endif()
# custom mpreal
if(NOT EXISTS "${PROJECT_SOURCE_DIR}/mpreal/.git")
execute_process(COMMAND git submodule init ${PROJECT_SOURCE_DIR}/mpreal)
execute_process(COMMAND git submodule update ${PROJECT_SOURCE_DIR}/mpreal)
execute_process(COMMAND git checkout mpfrc++-3.6.9 WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/mpreal)
endif()
# includes
include_directories(${PROJECT_SOURCE_DIR}/src ${PROJECT_SOURCE_DIR}/linenoise-ng/include)
include_directories(${PROJECT_SOURCE_DIR}/src ${PROJECT_SOURCE_DIR}/linenoise-ng/include ${PROJECT_SOURCE_DIR}/mpreal)
# 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/parse.cpp
${PROJECT_SOURCE_DIR}/src/lexer.cpp
${PROJECT_SOURCE_DIR}/src/input.cpp
${PROJECT_SOURCE_DIR}/src/rpn-branch.cpp
${PROJECT_SOURCE_DIR}/src/rpn-complex.cpp
${PROJECT_SOURCE_DIR}/src/rpn-general.cpp
@ -57,7 +67,7 @@ add_executable(
${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-core.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}/linenoise-ng/src/ConvertUTF.cpp

45
Changelog.md Normal file
View file

@ -0,0 +1,45 @@
Changelog
- Better parser (now called lexer)
- Use of mpreal instead of raw mpfr for calc on reals
- Use of C++ complex class
- Removing old memory management, efficient but poorly maintainable
- Enhanced code quality and memory usage checks
- Added CircleCI checks: passing functional tests and valgrind mem checks at each pull request
- SonarCloud integration, Sonar way profile
- clang-format now based on google style
- [google c++ style guide](https://google.github.io/styleguide/cppguide.html) applied
- 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/
New
- `«` and `»` are now valid as program delimiters. `<<` and `>>` are still valid
- entering the sign after the base (ex: 0x-1e2) is allowed
- rpn is delivered as flatpak and snap packages to be compatible with a maximum of Linux distribs. rpm and deb are no longer generated
Compatibility is broken on these points
- `<< <<` input doesn't lead to `««»»` but to `«<< »`, preventing to eval the real program content
- `1 2+` not allowed anymore, keep `1 2 +`, this corrects bad behaviors like `3b114` pushing `3b11` and `4`
- complexes are written in the form `(1,2)` instead of `(1, 2)` (space is removed)
- removed useless `unti`, `repea`, `whil` (prev.existing for HP28S compatibility)
- removed `sqr` function, please use `sq` instead (prev.existing for HP28S compatibility)
- the binary prefix is always 0b on display, but still can be 0b, 0B, 2b or 2B at input
- the hex prefix is always 0x on display, but still can be 0x, 0X or 16B at input
- `mant` and `xpon` now give binary (and not decimal) significand and exponent, as it is the norm in standard libs (libC, standard C++, mpfr, gmp)
- `dupn`, `roll`, `rolld` are not leaving anymore their argument in front of the stack in case of error
- `sto+` `sto-` `sto*` `sto/` don't accept anymore the syntax `'varname' value stoX`, but only `value 'varname' stoX`, ex: `3 'a' sto*`
- incomplete entry `(1,` is not available anymore
- signed zero is the sign of zero is subject to change compared to previous version, for example `-3 sqrt` now equals `(0.000000,1.732051)` instead of `(-0.000000,1.732051)`
Debug
- `sub` now only accepts boundaries between 1 and the string length
- `sto/` behavior: sto/ now correctly stores variable / constant and not constant / variable
- `cosh` now returns the hyp cosinus instead of the hyp sinus (!)

6
TODO.md Normal file
View file

@ -0,0 +1,6 @@
TODO
missing tests / problems
- les arguments d'une fonction en erreur doivent ils être consommés ?
ex embettant : sto+
- `1 'i' sto while i <= 2 repeat 0 'j' sto while j <= 1 repeat i (1,0) * j (0,1) * + 1 'j' sto+ end 1 'i' sto+ end` plante

1
mpreal Submodule

@ -0,0 +1 @@
Subproject commit c45d0d522c9bd0dd16d7aac25fa0862dd074ddb0

View file

@ -1,3 +1,9 @@
BasedOnStyle: Google
IndentWidth: '4'
ColumnLimit: 120
---
BasedOnStyle: google
IndentWidth: 4
---
Language: Cpp
# Force pointers to the type for C++.
DerivePointerAlignment: false
PointerAlignment: Left
ColumnLimit: 120

View file

@ -1,6 +1,7 @@
#ifndef CONSTANT_H
#define CONSTANT_H
#define MPFR_USE_NO_MACRO
#include <mpfr.h>
// default values
@ -9,7 +10,6 @@
// default mode and number of printed digits
//
#define DEFAULT_MODE number::std
#define MPFR_DEFAULT_FORMAT "%.xxRg"
/* directly calculated from 128 bits precision
ceil(128 * log10(2)) - 1 = 38 */
@ -18,36 +18,9 @@
// MPFR related defaults
//
// rounding method
#define MPFR_DEFAULT_RND MPFR_RNDN
// 128 bits significand precision
#define MPFR_DEFAULT_PREC_BITS 128
// 128 bits significand storing length in bytes, result of
// mpfr_custom_get_size(128)
#define MPFR_DEFAULT_STORING_LENGTH_BYTES 16
// constants
//
// commands and entry related constants
#define MAX_COMMAND_LENGTH 24
#define AUTOCOMPLETE_KEY '\t'
#define SHOW_STACK_SEPARATOR "> "
#define PROMPT "rpn> "
#define MULTILINE_PROMPT "> "
// show formats
#define MPFR_FORMAT_BEG "%."
#define MPFR_FORMAT_STD "Rg"
#define MPFR_FORMAT_FIX "Rf"
#define MPFR_FORMAT_SCI "Re"
#define MPFR_FORMAT_HEX "%Ra"
#define MPFR_RND_STRINGS \
{ "nearest", "toward zero", "toward +inf", "toward -inf", "away from zero" }
// return values, used by all classes
//
typedef enum {
@ -66,58 +39,13 @@ typedef enum {
ret_div_by_zero,
ret_runtime_error,
ret_abort_current_entry,
ret_out_of_memory,
ret_bad_value,
ret_max
} ret_value;
#define RET_VALUE_STRINGS \
{ \
"ok", "unknown command", "missing operand", "bad operand type", "out of range", "unknown variable", \
"internal error, aborting", "deadly", "goodbye", "not implemented", "no operation", "syntax error", \
"division by zero", "runtime error", "aborted current entry" \
}
// command types
//
#define CMD_TYPE_STRINGS \
{ "undef", "number", "complex", "string", "symbol", "program", "keyword", "keyword" }
// history
#define HISTORY_FILE ".rpn_history"
#define HISTORY_FILE_MAX_LINES (100)
// base min and max
#define BASE_MIN 2
#define BASE_MAX 62
// some defs for mpfr
#if _MPFR_EXP_FORMAT == 1
#define MPFR_EXP_MAX (SHRT_MAX)
#define MPFR_EXP_MIN (SHRT_MIN)
#elif _MPFR_EXP_FORMAT == 2
#define MPFR_EXP_MAX (INT_MAX)
#define MPFR_EXP_MIN (INT_MIN)
#elif _MPFR_EXP_FORMAT == 3
#define MPFR_EXP_MAX (LONG_MAX)
#define MPFR_EXP_MIN (LONG_MIN)
#elif _MPFR_EXP_FORMAT == 4
#define MPFR_EXP_MAX (MPFR_INTMAX_MAX)
#define MPFR_EXP_MIN (MPFR_INTMAX_MIN)
#else
#error "Invalid MPFR Exp format"
#endif
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
#define MAX(h, i) ((h) > (i) ? (h) : (i))
#define MPFR_EXP_INF (MPFR_EXP_MIN + 3)
#define MPFR_EXP_NAN (MPFR_EXP_MIN + 2)
#define MPFR_EXP(x) ((x)->_mpfr_exp)
#define MPFR_IS_SINGULAR(x) (MPFR_EXP(x) <= MPFR_EXP_INF)
#define MPFR_UNLIKELY(x) (__builtin_expect(!!(x), 0))
#define MPFR_IS_NAN(x) (MPFR_EXP(x) == MPFR_EXP_NAN)
#define MPFR_IS_INF(x) (MPFR_EXP(x) == MPFR_EXP_INF)
#define MPFR_IS_NEG(x) (MPFR_SIGN(x) < 0)
#define MPFR_IS_POS(x) (MPFR_SIGN(x) > 0)
#define MPFR_PREC(x) ((x)->_mpfr_prec)
#endif

View file

@ -1,88 +0,0 @@
#ifndef DEBUG_H
#define DEBUG_H
#include <string.h>
static void dump8(unsigned char* to_dump, unsigned long offset, unsigned long size) {
const int block_size = 1;
const int block_per_line = 16;
int max_line = size / block_size;
unsigned char mychar;
int i;
int j;
for (i = 0; i < max_line; i++) {
if ((i % block_per_line) == 0) {
if (i > 0) {
printf(" ");
for (j = i - block_per_line; j < i; j++) {
mychar = *(to_dump + j);
if ((mychar < 32) || (mychar >= 127)) mychar = '.';
printf("%c", mychar);
}
}
printf("\n%08lX:", offset + i * block_size);
}
printf(" %02hhX", *(to_dump + i));
}
if (i > 0) {
printf(" ");
for (j = (i >= block_per_line) ? (i - block_per_line) : 0; j < i; j++) {
mychar = *(to_dump + j);
if ((mychar < 32) || (mychar >= 127)) mychar = '.';
printf("%c", mychar);
}
}
printf("\n");
}
//
#define TRACE(...) \
do { \
printf(__VA_ARGS__); \
} while (0)
// chrono
#include <time.h>
static int chrono_next = 0;
static struct {
struct timespec ts_point;
char comment[256];
} chrono_point[24];
static void chrono_start() {
clock_gettime(CLOCK_REALTIME, &chrono_point[0].ts_point);
strcpy(chrono_point[0].comment, "START");
chrono_next = 1;
}
static void chrono_add(const char* comment) {
clock_gettime(CLOCK_REALTIME, &chrono_point[chrono_next].ts_point);
strcpy(chrono_point[chrono_next].comment, comment);
chrono_next++;
}
static uint64_t chrono_diff_us(struct timespec* ts_from, struct timespec* ts_to) {
uint64_t from = (uint64_t)ts_from->tv_sec * 1000000UL + ((uint64_t)ts_from->tv_nsec) / 1000UL;
uint64_t to = (uint64_t)ts_to->tv_sec * 1000000UL + ((uint64_t)ts_to->tv_nsec) / 1000UL;
return to - from;
}
static void chrono_all_print(void) {
for (int i = 1; i < chrono_next; i++) {
printf("CHRONO [%lu us] %s\n", chrono_diff_us(&chrono_point[i - 1].ts_point, &chrono_point[i].ts_point),
chrono_point[i].comment);
}
}
static void chrono_print(int chrono) {
if (chrono >= 1) {
printf("CHRONO [%lu us] %s\n",
chrono_diff_us(&chrono_point[chrono - 1].ts_point, &chrono_point[chrono].ts_point),
chrono_point[chrono].comment);
}
}
#define max(a, b) (((a) > (b)) ? (a) : (b))
#endif

View file

@ -7,31 +7,12 @@
#define FG_BLACK "\33[30m"
#define FG_RED "\33[31m"
#define FG_GREEN "\33[32m"
#define FG_YELLOW "\33[33m"
#define FG_BLUE "\33[34m"
#define FG_MAJENTA "\33[35m"
#define FG_CYAN "\33[36m"
#define FG_WHITE "\33[37m"
// background colors
#define BG_BLACK "\33[40m"
#define BG_RED "\33[41m"
#define BG_GREEN "\33[42m"
#define BG_YELLOW "\33[43m"
#define BG_BLUE "\33[44m"
#define BG_MAJENTA "\33[45m"
#define BG_CYAN "\33[46m"
#define BG_WHITE "\33[47m"
#define COLOR_OFF "\33[m"
// attributes
#define ATTR_BOLD "\33[1m"
#define ATTR_UNDERSCORE "\33[4m"
#define ATTR_BLINK "\33[5m"
#define ATTR_REVERSE "\33[7m"
#define ATTR_CONCEALED "\33[8m"
#define ATTR_OFF "\33[0m"
#endif

219
src/lexer.cpp Normal file
View file

@ -0,0 +1,219 @@
#include <regex>
using namespace std;
#include "lexer.hpp"
bool Lexer::lexer(string& entry, map<string, ReservedWord>& keywords, vector<SynElement>& elements,
vector<SynError>& errors) {
size_t jump;
for (size_t i = 0; i < entry.size(); i++) {
if (isspace(entry[i])) continue;
SynElement element;
switch (entry[i]) {
case '"':
if (!parseString(entry, i, jump, errors, elements)) return false;
i = jump - 1;
continue;
case '\'':
if (!parseSymbol(entry, i, jump, errors, elements)) return false;
i = jump - 1;
continue;
case '(':
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;
i = jump - 1;
continue;
} 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)) {
i = jump - 1;
continue;
}
if (parseUnknown(entry, i, jump, elements))
// last chance, this unknown entry is treated as a symbol
i = jump - 1;
}
return true;
}
void Lexer::trim(string& s) {
s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](unsigned char ch) { return !std::isspace(ch); }));
s.erase(std::find_if(s.rbegin(), s.rend(), [](unsigned char ch) { return !std::isspace(ch); }).base(), s.end());
}
bool Lexer::parseString(string& entry, size_t idx, size_t& nextIdx, vector<SynError>& errors,
vector<SynElement>& elements) {
// here we are sure that entry[0] is at least '"'
for (size_t i = idx + 1; i < entry.size(); i++) {
if (entry[i] == '"') {
if (entry[i] - 1 != '\\') {
elements.push_back({cmd_string, .value = entry.substr(idx + 1, i - idx - 1)});
nextIdx = i + 1;
return true;
}
}
}
elements.push_back({cmd_string, .value = entry.substr(idx + 1, entry.size() - idx - 1)});
nextIdx = entry.size();
return true;
}
bool Lexer::parseSymbol(string& entry, size_t idx, size_t& nextIdx, vector<SynError>& errors,
vector<SynElement>& elements) {
// here we are sure that entry[0] is at least '\''
for (size_t i = idx + 1; i < entry.size(); i++) {
if (entry[i] == '\'') {
elements.push_back({cmd_symbol, .value = entry.substr(idx + 1, i - idx - 1), .autoEval = false});
nextIdx = i + 1;
return true;
}
}
elements.push_back({cmd_symbol, .value = entry.substr(idx + 1, entry.size() - idx - 1)});
nextIdx = entry.size();
return true;
}
bool Lexer::parseProgram(string& entry, size_t idx, size_t& nextIdx, vector<SynError>& errors,
vector<SynElement>& elements) {
// here we are sure that entry is at least "<<"
// find last ">>" or "»"
int countNested = 0;
for (size_t i = idx + 2; i < entry.size() - 1; i++) {
if ((entry[i] == '<' && entry[i + 1] == '<') || (entry.substr(i, 2) == "«"))
countNested++;
else if ((entry[i] == '>' && entry[i + 1] == '>') || (entry.substr(i, 2) == "»")) {
if (countNested == 0) {
string prg = entry.substr(idx + 2, i - idx - 2);
trim(prg);
elements.push_back({cmd_program, .value = prg});
nextIdx = i + 2;
return true;
} else
countNested--;
}
}
string prg = entry.substr(idx + 2, entry.size() - idx - 2);
trim(prg);
elements.push_back({cmd_program, .value = prg});
nextIdx = entry.size();
return true;
}
int Lexer::getBaseAt(string& entry, int idxStart, bool& positive) {
regex baseRegex("([+-])?((0[xX])|([0-9][0-9]?[bB]))");
smatch match;
if (regex_search(entry, match, baseRegex) && match.size() >= 5) {
string sign = match[1].str();
string base = match[2].str();
// sign out, permits expressions like -0xAB3F
positive = sign.size() > 0 && sign[0] == '-' ? false : true;
// base
entry = entry.substr(base.size() + sign.size());
if (base[1] == 'X' || base[1] == 'x') return 16;
if (base.size() > 0) {
int b = stoi(base.substr(0, base.size() - 1));
if (b == 0) b = 2; // admit "0b" as binary suffix
return b;
}
}
return 10;
}
bool Lexer::getNumberAt(string& entry, size_t idx, size_t& nextIdx, int& base, mpreal& r, char delim) {
stringstream ss;
int idxNumber = 0;
string token;
bool positive = true;
ss.str(entry.substr(idx));
if (getline(ss, token, delim)) {
nextIdx = token.size() + idx + 1;
base = getBaseAt(token, idx, positive);
if (base < BASE_MIN || base > BASE_MAX) return false;
trim(token);
if (mpfr_set_str(r.mpfr_ptr(), token.c_str(), base, mpreal::get_default_rnd()) == 0) {
if (!positive) r = -r;
return true;
} else
return false;
}
nextIdx = token.size() + idx + 1;
return false;
}
bool Lexer::parseNumber(string& entry, size_t idx, size_t& nextIdx, vector<SynError>& errors,
vector<SynElement>& elements) {
mpreal r;
int base = 10;
if (getNumberAt(entry, idx, nextIdx, base, r)) {
elements.push_back({cmd_number, .re = r, .reBase = base});
return true;
} else {
errors.push_back({entry.size(), "unterminated number"});
return false;
}
}
bool Lexer::parseComplex(string& entry, size_t idx, size_t& nextIdx, vector<SynError>& errors,
vector<SynElement>& elements) {
mpreal re, im;
int reBase, imBase = 10;
if (idx + 1 == entry.size()) {
elements.push_back({cmd_symbol, .value = entry.substr(idx, entry.size() - idx)});
nextIdx = entry.size();
return true; // complex format error, return a symbol
}
if (!getNumberAt(entry, idx + 1, nextIdx, reBase, re, ',')) {
elements.push_back({cmd_symbol, .value = entry.substr(idx, entry.size() - idx)});
nextIdx = entry.size();
return true; // complex format error, return a symbol
}
size_t i = nextIdx;
// while (i < entry.size() && entry[i] != ',') i++;
if (i >= entry.size()) {
elements.push_back({cmd_symbol, .value = entry.substr(idx, entry.size() - idx)});
nextIdx = entry.size();
return true; // complex format error, return a symbol
}
if (!getNumberAt(entry, i, nextIdx, imBase, im, ')')) {
elements.push_back({cmd_symbol, .value = entry.substr(idx, entry.size() - idx)});
nextIdx = entry.size();
return true; // complex format error, return a symbol
}
elements.push_back({cmd_complex, .re = re, .im = im, .reBase = reBase, .imBase = imBase});
nextIdx++;
return true;
}
bool Lexer::parseReserved(string& entry, size_t idx, size_t& nextIdx, vector<SynElement>& elements,
map<string, ReservedWord>& keywords) {
stringstream ss(entry.substr(idx));
string token;
ss >> token;
auto resa = keywords.find(token);
if (resa != keywords.end()) {
elements.push_back({resa->second.type, .value = token, .fn = resa->second.fn});
nextIdx = token.size() + idx;
return true;
}
return false;
}
bool Lexer::parseUnknown(string& entry, size_t idx, size_t& nextIdx, vector<SynElement>& elements) {
stringstream ss(entry.substr(idx));
string token;
ss >> token;
elements.push_back({cmd_symbol, .value = token, .autoEval = true});
nextIdx = token.size() + idx;
return true;
}

73
src/lexer.hpp Normal file
View file

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

View file

@ -1,101 +1,101 @@
// std c
#include <errno.h>
#include <linux/limits.h>
#include <pwd.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <cerrno>
#include <iostream>
using namespace std;
// internal includes
#include "input.hpp"
#include "program.hpp"
static heap s_global_heap;
static stack s_global_stack;
static program* s_prog_to_interrupt = NULL;
static heap _global_heap;
static rpnstack _global_stack;
static program* _prog_to_interrupt = nullptr;
/// @brief actions to be done at rpn exit
///
void exit_interactive_rpn() {
static void exit_interactive_rpn() {
struct passwd* pw = getpwuid(getuid());
if (pw != NULL) {
char history_path[PATH_MAX];
sprintf(history_path, "%s/%s", pw->pw_dir, HISTORY_FILE);
if (pw != nullptr) {
stringstream history_path;
history_path << pw->pw_dir << "/.rpn_history";
// trunc current history
ofstream history(history_path, ios_base::out | ios_base::trunc);
ofstream history(history_path.str(), ios_base::out | ios_base::trunc);
history.close();
// save it
if (linenoiseHistorySave(history_path) != 0)
fprintf(stderr, "warning, could not save %s (errno=%d, '%s')\n", history_path, errno, strerror(errno));
if (linenoiseHistorySave(history_path.str().c_str()) != 0)
cerr << "warning, could not save " << history_path.str() << " [errno=" << errno << ' ' << strerror(errno)
<< "']" << endl;
linenoiseHistoryFree();
}
}
/// @brief actions to be done at rpn exit
///
void init_interactive_rpn() {
static void init_interactive_rpn() {
struct passwd* pw = getpwuid(getuid());
if (pw != NULL) {
char history_path[PATH_MAX];
sprintf(history_path, "%s/%s", pw->pw_dir, HISTORY_FILE);
if (pw != nullptr) {
stringstream history_path;
history_path << pw->pw_dir << "/.rpn_history";
// don't care about errors
linenoiseHistorySetMaxLen(HISTORY_FILE_MAX_LINES);
linenoiseHistoryLoad(history_path);
linenoiseHistorySetMaxLen(100);
linenoiseHistoryLoad(history_path.str().c_str());
}
}
/// @brief handle CtrlC signal (sigaction handler): exit rpn
///
///
/// @param sig signal, see POSIX sigaction
/// @param siginfo signal info, see POSIX sigaction
/// @param context see POSIX sigaction
///
static void ctrlc_handler(int sig, siginfo_t* siginfo, void* context) {
if (s_prog_to_interrupt != NULL) {
s_prog_to_interrupt->stop();
s_prog_to_interrupt = NULL;
if (_prog_to_interrupt != nullptr) {
_prog_to_interrupt->stop();
_prog_to_interrupt = nullptr;
}
exit_interactive_rpn();
}
/// @brief handle SIGSEGV signal (sigaction handler): stop and exit rpn
///
///
/// @param sig signal, see POSIX sigaction
/// @param siginfo signal info, see POSIX sigaction
/// @param context see POSIX sigaction
///
static void segv_handler(int sig, siginfo_t* siginfo, void* context) {
fprintf(stderr, "Internal error\n");
s_prog_to_interrupt->stop();
s_prog_to_interrupt = NULL;
cerr << "Internal error" << endl;
_prog_to_interrupt->stop();
_prog_to_interrupt = nullptr;
}
/// @brief setup signals handlers to stop with honours
///
/// @param prog the prog to catch the signals to, must be checked not NULL by user
///
/// @param prog the prog to catch the signals to, must be checked not nullptr by user
///
static void catch_signals(program* prog) {
struct sigaction act;
struct sigaction act = {0};
s_prog_to_interrupt = prog;
_prog_to_interrupt = prog;
act.sa_sigaction = &ctrlc_handler;
act.sa_flags = SA_SIGINFO;
if (sigaction(SIGINT, &act, NULL) < 0)
(void)fprintf(stderr, "Warning, Ctrl-C cannot be catched [errno=%d '%s']", errno, strerror(errno));
if (sigaction(SIGINT, &act, nullptr) < 0)
cerr << "Warning, Ctrl-C cannot be caught [errno=" << errno << ' ' << strerror(errno) << "']" << endl;
act.sa_sigaction = &segv_handler;
act.sa_flags = SA_SIGINFO;
if (sigaction(SIGSEGV, &act, NULL) < 0)
(void)fprintf(stderr, "Warning, SIGSEGV cannot be catched [errno=%d '%s']", errno, strerror(errno));
if (sigaction(SIGSEGV, &act, nullptr) < 0)
cerr << "Warning, SIGSEGV cannot be caught [errno=" << errno << ' ' << strerror(errno) << "']" << endl;
}
/// @brief rpn entry point
///
///
/// @param argc command line args nb
/// @param argv command line args nb
/// @return int 0=ok
@ -115,32 +115,30 @@ int main(int argc, char* argv[]) {
// entry loop
while (go_on) {
// make program from interactive entry
program prog;
switch (program::entry(prog)) {
case ret_good_bye:
go_on = false;
break;
case ret_abort_current_entry:
break;
default:
program prog(_global_stack, _global_heap);
string entry;
switch (Input(entry, program::getAutocompletionWords()).status) {
case Input::ok:
// user could stop prog with CtrlC
catch_signals(&prog);
// run it
if (prog.run(s_global_stack, s_global_heap) == ret_good_bye)
if (prog.parse(entry) == ret_ok && prog.run() == ret_good_bye)
go_on = false;
else
program::show_stack(s_global_stack);
program::show_stack(_global_stack);
break;
case Input::ctrlc:
go_on = false;
break;
default:
break;
}
}
// manage history and exit
exit_interactive_rpn();
}
// run with cmd line arguments
else {
program prog;
} else { // run with cmd line arguments
program prog(_global_stack, _global_heap);
string entry;
int i;
@ -151,16 +149,14 @@ int main(int argc, char* argv[]) {
}
// make program
ret = program::parse(entry.c_str(), prog);
ret = prog.parse(entry);
if (ret == ret_ok) {
string separator = "";
// user could stop prog with CtrlC
catch_signals(&prog);
// run it
ret = prog.run(s_global_stack, s_global_heap);
program::show_stack(s_global_stack, false);
ret = prog.run();
program::show_stack(_global_stack, false);
}
}

203
src/mpreal-out.cpp Normal file
View file

@ -0,0 +1,203 @@
#include "mpreal-out.hpp"
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
#define MAX(h, i) ((h) > (i) ? (h) : (i))
// some defs for mpfr
#if _MPFR_EXP_FORMAT == 1
#define MPFR_EXP_MAX (SHRT_MAX)
#define MPFR_EXP_MIN (SHRT_MIN)
#elif _MPFR_EXP_FORMAT == 2
#define MPFR_EXP_MAX (INT_MAX)
#define MPFR_EXP_MIN (INT_MIN)
#elif _MPFR_EXP_FORMAT == 3
#define MPFR_EXP_MAX (LONG_MAX)
#define MPFR_EXP_MIN (LONG_MIN)
#elif _MPFR_EXP_FORMAT == 4
#define MPFR_EXP_MAX (MPFR_INTMAX_MAX)
#define MPFR_EXP_MIN (MPFR_INTMAX_MIN)
#else
#error "Invalid MPFR Exp format"
#endif
#define MPFR_EXP_INF (MPFR_EXP_MIN + 3)
#define MPFR_EXP_NAN (MPFR_EXP_MIN + 2)
#define MPFR_EXP(x) ((x)->_mpfr_exp)
#define MPFR_IS_SINGULAR(x) (MPFR_EXP(x) <= MPFR_EXP_INF)
#define MPFR_UNLIKELY(x) (__builtin_expect(!!(x), 0))
#define MPFR_IS_NAN(x) (MPFR_EXP(x) == MPFR_EXP_NAN)
#define MPFR_IS_INF(x) (MPFR_EXP(x) == MPFR_EXP_INF)
#define MPFR_IS_NEG(x) (MPFR_SIGN(x) < 0)
#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) {
// 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)) {
out << string(s);
mpfr_free_str(s);
}
return out;
}
static bool is_min(const mpreal& p, mpfr_prec_t prec) {
// see mpfr_vasprintf code
// TODO here use mpreal functions like <=0, isinf etc
bool ret;
int round_away;
switch (mpreal::get_default_rnd()) {
case MPFR_RNDA:
round_away = 1;
break;
case MPFR_RNDD:
round_away = MPFR_IS_NEG(p.mpfr_srcptr());
break;
case MPFR_RNDU:
round_away = MPFR_IS_POS(p.mpfr_srcptr());
break;
case MPFR_RNDN: {
/* compare |p| to y = 0.5*10^(-prec) */
mpfr_t y;
mpfr_exp_t e = MAX(MPFR_PREC(p.mpfr_srcptr()), 56);
mpfr_init2(y, e + 8);
do {
/* find a lower approximation of
0.5*10^(-prec) different from |p| */
e += 8;
mpfr_set_prec(y, e);
mpfr_set_si(y, -prec, MPFR_RNDN);
mpfr_exp10(y, y, MPFR_RNDD);
mpfr_div_2ui(y, y, 1, MPFR_RNDN);
} while (mpfr_cmpabs(y, p.mpfr_srcptr()) == 0);
round_away = mpfr_cmpabs(y, p.mpfr_srcptr()) < 0;
mpfr_clear(y);
} break;
default:
round_away = 0;
}
if (round_away) /* round away from zero: the last output digit is '1' */
ret = true;
else
/* only zeros in fractional part */
ret = false;
return ret;
}
static void _out_base(ostream& out, int base) {
if (base == 16)
out << "0x";
else if (base == 2)
out << "0b";
else
out << base << "b";
}
ostream& _out_singular(ostream& out, int base, const mpreal& value) {
const char* write_after_sign = NULL; // unused for now
int digits = 0; // forced 0 digits after separator
if (MPFR_IS_NAN(value.mpfr_srcptr()))
out << "nan";
else if (MPFR_IS_INF(value.mpfr_srcptr())) {
if (MPFR_IS_NEG(value.mpfr_srcptr())) out << '-';
out << "inf";
} else {
// zero
if (MPFR_IS_NEG(value.mpfr_srcptr())) out << '-'; // signed zero is allowed
if (write_after_sign != NULL) out << write_after_sign;
_out_base(out, base);
out << '0';
if (digits > 0) {
out << '.';
for (int i = 0; i < digits; i++) out << '0';
}
}
return out;
}
ostream& _out_little_number(ostream& out, int base, const mpreal& value) {
const char* write_after_sign = NULL; // unused for now
int digits = 0; // forced 0 digits after separator
if (MPFR_IS_NEG(value.mpfr_srcptr())) out << '-';
if (write_after_sign != NULL) out << write_after_sign;
_out_base(out, base);
out << '0';
if (digits > 0) {
out << '.';
for (int i = 0; i < digits - 1; i++) out << '0';
if (is_min(value.mpfr_srcptr(), digits))
out << '1';
else
out << '0';
}
return out;
}
ostream& _out_number(ostream& out, int base, const mpreal& value) {
const char* write_after_sign = NULL; // unused for now
int digits = 0; // forced 0 digits after separator
mpfr_exp_t exp = mpfr_get_exp(value.mpfr_srcptr());
char* str = mpfr_get_str(NULL, &exp, base, digits + exp + 1, value.mpfr_srcptr(), mpreal::get_default_rnd());
char* print_from;
if (str != NULL) {
int len = strlen(str);
print_from = str;
if (len > 0) {
if (print_from[0] == '-') {
out << print_from[0];
len--;
print_from++;
} else if (print_from[0] == '+') {
len--;
print_from++;
}
if (write_after_sign != NULL) out << write_after_sign;
if (base == 16)
out << "0x";
else if (base == 2)
out << "0b";
else
out << base << "b";
if (exp < 0) {
out << '0';
if (digits > 0) {
out << '.';
for (int i = 0; i < -(int)exp; i++) out << '0';
out << str;
for (int i = 0; i < (int)(digits - len + exp); i++) out << '0';
}
} else {
if (exp == 0)
out << '0';
else
for (int i = 0; i < (int)exp; i++) out << print_from[i];
if (digits > 0) {
out << '.';
int remaining = (int)MIN(strlen(print_from) - exp - 1, digits) + 1;
for (int i = (int)exp; i < remaining + (int)exp; i++) out << print_from[i];
for (int i = 0; i < (int)(exp + digits - len); i++) out << '0';
}
}
}
mpfr_free_str(str);
}
return out;
}
ostream& mpreal_outputNbase(ostream& out, int base, const mpreal& value) {
// see mpfr_vasprintf code
int digits = 0; // forced 0 digits after separator
if (MPFR_UNLIKELY(MPFR_IS_SINGULAR(value.mpfr_srcptr()))) return _out_singular(out, base, value);
mpfr_exp_t exp = mpfr_get_exp(value.mpfr_srcptr());
if (exp < -digits) return _out_little_number(out, base, value);
return _out_number(out, base, value);
}

11
src/mpreal-out.hpp Normal file
View file

@ -0,0 +1,11 @@
#include <string>
#include <ostream>
using namespace std;
#define MPFR_USE_NO_MACRO
#include <mpfr.h>
#include <mpreal.h>
using namespace mpfr;
ostream& mpreal_output10base(ostream& out, const string& fmt, const mpreal& value);
ostream& mpreal_outputNbase(ostream& out, int base, const mpreal& value);

View file

@ -1,228 +1,6 @@
#include <math.h>
#include <string>
using namespace std;
#include "constant.h"
#include "mpfr.h"
#include "object.hpp"
// floating_t statics
mpfr_prec_t floating_t::s_mpfr_prec = MPFR_DEFAULT_PREC_BITS;
mpfr_rnd_t floating_t::s_mpfr_rnd = MPFR_DEFAULT_RND;
unsigned int floating_t::s_mpfr_prec_bytes = MPFR_DEFAULT_STORING_LENGTH_BYTES;
const char* floating_t::s_mpfr_rnd_str[5] = MPFR_RND_STRINGS;
// number statics
number::mode_enum number::s_mode = DEFAULT_MODE;
int number::s_decimal_digits = DEFAULT_DECIMAL_DIGITS;
string number::s_mpfr_printf_format = string(MPFR_DEFAULT_FORMAT);
//
const char* object::s_cmd_type_string[cmd_max] = CMD_TYPE_STRINGS;
/// @brief return if a mpfr is higher to a given precision
/// this function is directly copied from mpfr
///
/// @param p the mpfr to test
/// @param prec the precision
/// @return true p is higher than 10^prec
/// @return false p is lower than 10^prec
///
static bool is_min(mpfr_t p, mpfr_prec_t prec) {
// see mpfr_vasprintf code
bool ret;
int round_away;
switch (floating_t::s_mpfr_rnd) {
case MPFR_RNDA:
round_away = 1;
break;
case MPFR_RNDD:
round_away = MPFR_IS_NEG(p);
break;
case MPFR_RNDU:
round_away = MPFR_IS_POS(p);
break;
case MPFR_RNDN: {
/* compare |p| to y = 0.5*10^(-prec) */
mpfr_t y;
mpfr_exp_t e = MAX(MPFR_PREC(p), 56);
mpfr_init2(y, e + 8);
do {
/* find a lower approximation of
0.5*10^(-prec) different from |p| */
e += 8;
mpfr_set_prec(y, e);
mpfr_set_si(y, -prec, MPFR_RNDN);
mpfr_exp10(y, y, MPFR_RNDD);
mpfr_div_2ui(y, y, 1, MPFR_RNDN);
} while (mpfr_cmpabs(y, p) == 0);
round_away = mpfr_cmpabs(y, p) < 0;
mpfr_clear(y);
} break;
default:
round_away = 0;
}
if (round_away) /* round away from zero: the last output digit is '1' */
ret = true;
else
/* only zeros in fractional part */
ret = false;
return ret;
}
/// @brief print a mpfr in fixed format according to a base
/// this function is based copied on mpfr library
///
/// @param stream the stream to write to
/// @param real the real to print
/// @param base the base to print the real
/// @param write_after_sign substring to write between the sign and the real
///
static void print_fix(FILE* stream, mpfr_t real, int base, const char* write_after_sign = NULL) {
// see mpfr_vasprintf code
mpfr_exp_t exp = mpfr_get_exp(real);
int digits = 0; // forced 0 digits after separator
int i;
if (MPFR_UNLIKELY(MPFR_IS_SINGULAR(real))) {
if (MPFR_IS_NAN(real))
fputs("nan", stream);
else if (MPFR_IS_INF(real)) {
if (MPFR_IS_NEG(real)) fputc('-', stream);
fputs("inf", stream);
} else {
// zero
if (MPFR_IS_NEG(real)) fputc('-', stream); // signed zero is allowed
if (write_after_sign != NULL) fputs(write_after_sign, stream);
fputc('0', stream);
if (digits > 0) {
fputc('.', stream);
for (i = 0; i < digits; i++) fputc('0', stream);
}
}
} else if (exp < -digits) {
if (MPFR_IS_NEG(real)) fputc('-', stream);
if (write_after_sign != NULL) fputs(write_after_sign, stream);
fputc('0', stream);
if (digits > 0) {
fputc('.', stream);
for (i = 0; i < digits - 1; i++) fputc('0', stream);
if (is_min(real, digits))
fputc('1', stream);
else
fputc('0', stream);
}
} else {
char* str = mpfr_get_str(NULL, &exp, base, digits + exp + 1, real, floating_t::s_mpfr_rnd);
char* print_from;
if (str != NULL) {
int len = strlen(str);
print_from = str;
if (len > 0) {
if (print_from[0] == '-') {
fputc(print_from[0], stream);
len--;
print_from++;
} else if (print_from[0] == '+') {
len--;
print_from++;
}
if (write_after_sign != NULL) fputs(write_after_sign, stream);
if (exp < 0) {
fputc('0', stream);
if (digits > 0) {
fputc('.', stream);
for (i = 0; i < -(int)exp; i++) fputc('0', stream);
fputs(str, stream);
for (i = 0; i < (int)(digits - len + exp); i++) fputc('0', stream);
}
} else {
if (exp == 0)
fputc('0', stream);
else
for (i = 0; i < (int)exp; i++) fputc(print_from[i], stream);
if (digits > 0) {
fputc('.', stream);
int remaining = (int)MIN(strlen(print_from) - exp - 1, digits) + 1;
for (i = (int)exp; i < remaining + (int)exp; i++) fputc(print_from[i], stream);
for (i = 0; i < (int)(exp + digits - len); i++) fputc('0', stream);
}
}
}
mpfr_free_str(str);
}
}
}
/// @brief show an object representation according to its type
///
/// @param stream the stream to write to
///
void object::show(FILE* stream) {
int digits;
char* str;
char base[32];
switch (_type) {
case cmd_number:
switch (((number*)this)->_representation) {
case number::dec:
mpfr_fprintf(stream, number::s_mpfr_printf_format.c_str(), ((number*)this)->_value.mpfr);
break;
case number::hex:
print_fix(stream, ((number*)this)->_value.mpfr, 16, "0x");
break;
case number::bin:
print_fix(stream, ((number*)this)->_value.mpfr, 2, "0b");
break;
case number::base:
sprintf(base, "%db", ((number*)this)->_base);
print_fix(stream, ((number*)this)->_value.mpfr, ((number*)this)->_base, base);
break;
default:
fprintf(stream, "<unknown number representation>");
break;
}
break;
case cmd_complex:
switch (((complex*)this)->_representation) {
case number::dec:
fprintf(stream, "(");
mpfr_fprintf(stream, number::s_mpfr_printf_format.c_str(), ((complex*)this)->re()->mpfr);
fprintf(stream, ",");
mpfr_fprintf(stream, number::s_mpfr_printf_format.c_str(), ((complex*)this)->im()->mpfr);
fprintf(stream, ")");
break;
case number::hex:
fprintf(stream, "(");
mpfr_fprintf(stream, string(MPFR_FORMAT_HEX).c_str(), ((complex*)this)->re()->mpfr);
fprintf(stream, ",");
mpfr_fprintf(stream, string(MPFR_FORMAT_HEX).c_str(), ((complex*)this)->im()->mpfr);
fprintf(stream, ")");
break;
default:
fprintf(stream, "<unknown complex representation>");
}
break;
case cmd_string:
fprintf(stream, "\"%s\"", ((ostring*)this)->_value);
break;
case cmd_program:
fprintf(stream, "<<%s>>", ((oprogram*)this)->_value);
break;
case cmd_symbol:
fprintf(stream, "'%s'", ((symbol*)this)->_value);
break;
case cmd_keyword:
case cmd_branch:
fprintf(stream, "%s", ((keyword*)this)->_value);
break;
default:
fprintf(stream, "< unknown object representation >");
break;
}
}
int number::s_digits = DEFAULT_DECIMAL_DIGITS;

View file

@ -1,11 +1,20 @@
#ifndef OBJECT_HPP
#define OBJECT_HPP
#define MPFR_USE_NO_MACRO
#include <mpfr.h>
#include <string.h>
#include <mpreal.h>
using namespace mpfr;
#include <ostream>
#include <string>
#include <sstream>
using namespace std;
#include "mpreal-out.hpp"
// definitions for objects
////
///
typedef enum {
cmd_undef,
cmd_number, // floating point number
@ -24,259 +33,168 @@ class branch;
typedef void (program::*program_fn_t)(void);
typedef int (program::*branch_fn_t)(branch&);
/// @brief MPFR (floating point) object
///
struct floating_t {
mpfr_prec_t mpfr_prec; // precision in bits
unsigned int mpfr_prec_bytes; // significand storing length in bytes
mpfr_t mpfr; // mpfr object
void init() {
void* significand = (void*)(this + 1);
mpfr_prec = s_mpfr_prec;
mpfr_prec_bytes = s_mpfr_prec_bytes;
mpfr_custom_init(significand, MPFR_DEFAULT_PREC_BITS);
mpfr_custom_init_set(mpfr, MPFR_ZERO_KIND, 0, mpfr_prec, significand);
}
void move() {
void* significand = (void*)(this + 1);
mpfr_custom_move(mpfr, significand);
}
floating_t& operator=(const double val) { mpfr_set_d(mpfr, val, s_mpfr_rnd); }
floating_t& operator=(const long int val) { mpfr_set_si(mpfr, val, s_mpfr_rnd); }
floating_t& operator=(const unsigned long val) { mpfr_set_ui(mpfr, val, s_mpfr_rnd); }
operator double() { return mpfr_get_d(mpfr, s_mpfr_rnd); }
operator int() { return (int)mpfr_get_si(mpfr, s_mpfr_rnd); }
operator long() { return mpfr_get_si(mpfr, s_mpfr_rnd); }
bool operator>(const floating_t right) { return mpfr_cmp(mpfr, right.mpfr) > 0; }
bool operator<(const floating_t right) { return mpfr_cmp(mpfr, right.mpfr) < 0; }
// default precision in bits, precision length in bytes, rounding mode
static mpfr_prec_t s_mpfr_prec;
static unsigned int s_mpfr_prec_bytes;
static mpfr_rnd_t s_mpfr_rnd;
static const char* s_mpfr_rnd_str[5];
};
/// @brief object - a generic stack object
///
struct object {
// object type
object(cmd_type_t type = cmd_undef) : _type(type) {}
virtual ~object() {}
cmd_type_t _type;
unsigned int _size;
virtual object* clone() {
object* o = new object();
if (o != nullptr) *o = *this;
return o;
}
//
unsigned int size() { return _size; }
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); }
void show(FILE* stream = stdout);
//
static const char* s_cmd_type_string[cmd_max];
unsigned int size() { return sizeof(*this); }
};
/// @brief stack objects derived from object
///
struct number : public object {
// members
enum { dec, hex, bin, base } _representation;
// base
// carefull: _base is used only if _representation = base
int _base;
// mind that float value is at the end of the object
// because its mantissa is just after the obj in memory
floating_t _value;
struct number : object {
number() : object(cmd_number), base(10) {}
number(const mpreal& value_, int base_ = 10) : object(cmd_number), base(base_), value(value_) {}
number(long value_, int base_ = 10) : object(cmd_number), base(base_), value(value_) {}
// publics
number() { _type = cmd_number; }
int base;
mpreal value;
void init() {
_type = cmd_number;
_representation = dec;
_value.init();
}
void move() { _value.move(); }
void set(unsigned long value) {
_type = cmd_number;
_value = value;
}
static unsigned int calc_size() { return (unsigned int)(sizeof(number) + floating_t::s_mpfr_prec_bytes); }
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;
// precision
static int s_decimal_digits;
static string s_mpfr_printf_format;
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 complex : public object {
enum { dec, hex } _representation;
// mind that re float value is at the end of the object
// because its mantissa is just after the obj in memory
floating_t _re;
complex() { _type = cmd_complex; }
// re and im float values are at the end of the object
floating_t* re() { return &_re; }
floating_t* im() { return (floating_t*)((char*)&_re + sizeof(floating_t) + _re.mpfr_prec_bytes); }
void init() {
_type = cmd_complex;
_representation = dec;
re()->init();
im()->init();
struct ocomplex : object {
ocomplex() : object(cmd_complex), reBase(10), imBase(10) {}
ocomplex(complex<mpreal>& value_, int reb = 10, int imb = 10) : object(cmd_complex), reBase(reb), imBase(imb) {
value = value_;
}
ocomplex(mpreal& re_, mpreal& im_, int reb = 10, int imb = 10) : object(cmd_complex), reBase(reb), imBase(imb) {
value.real(re_);
value.imag(im_);
}
void move() {
re()->move();
im()->move();
}
int reBase, imBase;
complex<mpreal> value;
static unsigned int calc_size() {
return (unsigned int)(sizeof(complex) + 2 * (sizeof(floating_t) + floating_t::s_mpfr_prec_bytes));
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 : public object {
// ostring may first have been allocated with len+1 bytes
void set(const char* value, unsigned int len) {
_type = cmd_string;
if (value != NULL) {
if (len > 0) (void)memcpy(_value, value, len);
_value[len] = 0;
_len = len;
} else {
_value[_len] = 0;
_len = 0;
}
}
// length of _value, not including the terminal '\0'
unsigned int _len;
char _value[0];
///
struct ostring : object {
ostring() : object(cmd_string) {}
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 : public object {
// oprogram may first have been allocated with len+1 bytes
void set(const char* value, unsigned int len) {
_type = cmd_program;
if (value != NULL) {
if (len > 0) (void)memcpy(_value, value, len);
_value[len] = 0;
_len = len;
} else {
_value[0] = 0;
_len = 0;
}
}
// length of _value, not includiong the terminal '\0'
unsigned int _len;
char _value[0];
///
struct oprogram : object {
oprogram() : object(cmd_program) {}
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 : public object {
// symbol may first have been allocated with len+1 bytes
void set(const char* value, unsigned int len, bool auto_eval) {
_type = cmd_symbol;
_auto_eval = auto_eval;
if (value != NULL) {
if (len > 0) (void)memcpy(_value, value, len);
_value[len] = 0;
_len = len;
} else {
_value[0] = 0;
_len = 0;
}
}
//
bool _auto_eval;
// length of _value, not includiong the terminal '\0'
unsigned int _len;
char _value[0];
///
struct symbol : object {
symbol(bool autoEval_ = true) : object(cmd_symbol), autoEval(autoEval_) {}
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 : public object {
// keyword may first have been allocated with len+1 bytes
void set(program_fn_t fn, const char* value, unsigned int len) {
_type = cmd_keyword;
_fn = fn;
if (value != NULL) {
if (len > 0) (void)memcpy(_value, value, len);
_value[len] = 0;
_len = len;
} else {
_value[0] = 0;
_len = 0;
}
}
//
program_fn_t _fn;
// length of _value, not includiong the terminal '\0'
unsigned int _len;
char _value[0];
///
struct keyword : object {
keyword() : object(cmd_keyword) {}
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 : public object {
//
void set(branch_fn_t fn, const char* value, unsigned int len) {
_type = cmd_branch;
_fn = fn;
///
struct branch : object {
branch() : object(cmd_branch) {}
branch(branch_fn_t fn_, const string& value_) : object(cmd_branch) {
fn = fn_;
arg1 = -1;
arg2 = -1;
arg3 = -1;
farg1 = NULL;
farg2 = NULL;
arg_bool = 0;
if (value != NULL) {
if (len > 0) (void)memcpy(_value, value, len);
_value[len] = 0;
_len = len;
} else {
_value[0] = 0;
_len = 0;
}
value = value_;
}
// branch function
branch_fn_t _fn;
// args used by cmd_branch cmds
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;
int arg1, arg2, arg3;
number *farg1, *farg2;
mpreal firstIndex, lastIndex;
bool arg_bool;
// length of _value, not includiong the terminal '\0'
unsigned int _len;
char _value[0];
string value;
};
#endif

View file

@ -1,627 +0,0 @@
#include "program.hpp"
/// @brief completion callback as asked by linenoise-ng
/// this is called by linenoise-ng whenever the user enters TAB
///
/// @param text the text after wich the user entered TAB
/// @param lc the completion object to add strings with linenoiseAddCompletion()
///
void program::entry_completion_generator(const char* text, linenoiseCompletions* lc) {
int i = 0;
int text_len = strnlen(text, 6);
// propose all keywords
if (text_len == 0) {
while (program::s_keywords[i].type != cmd_max) {
if (program::s_keywords[i].fn != NULL)
// add all keywords
linenoiseAddCompletion(lc, program::s_keywords[i].name);
i++;
}
}
// propose keywords matching to text begining
else {
while (program::s_keywords[i].type != cmd_max) {
if (program::s_keywords[i].fn != NULL) {
// compare list entry with text, return if match
if (strncmp(program::s_keywords[i].name, text, text_len) == 0)
linenoiseAddCompletion(lc, program::s_keywords[i].name);
}
i++;
}
}
}
/// @brief interactive entry and decoding
///
/// @param prog the program to add entered objects
/// @return ret_value see this type
///
ret_value program::entry(program& prog) {
string entry_str;
char* entry;
int entry_len;
int total_entry_len;
ret_value ret = ret_max;
bool multiline = false;
// linenoise for entry
linenoiseSetCompletionCallback(entry_completion_generator);
while (ret == ret_max) {
// get user entry
if (multiline)
entry = linenoise(MULTILINE_PROMPT, &entry_len);
else
entry = linenoise(PROMPT, &entry_len);
// Ctrl-C
if (linenoiseKeyType() == 1) {
if (entry_len > 0 || multiline)
ret = ret_abort_current_entry;
else
ret = ret_good_bye;
} else if (linenoiseKeyType() == 3) {
multiline = true;
if (entry != NULL) entry_str += entry;
entry_str += " ";
} else {
if (entry != NULL) {
entry_str += entry;
// parse it
ret = parse(entry_str.c_str(), prog);
// keep history
if (entry[0] != 0) (void)linenoiseHistoryAdd(entry_str.c_str());
} else
ret = ret_internal;
}
free(entry);
}
return ret;
}
/// @brief return function pointer from function name
///
/// @param fn_name function name
/// @param fn function pointer
/// @param type the function type (cmd_keyword or cmd_branch)
/// @return ret_value see this type
///
ret_value program::get_fn(const char* fn_name, program_fn_t& fn, cmd_type_t& type) {
unsigned int i = 0;
while (s_keywords[i].type != cmd_max) {
if (strncasecmp(fn_name, s_keywords[i].name, sizeof(s_keywords[i].name)) == 0) {
fn = s_keywords[i].fn;
type = s_keywords[i].type;
return ret_ok;
}
i++;
}
return ret_unknown_err;
}
/// @brief get a keyword object from entry and add it to a program
///
/// @param entry the entry
/// @param prog the program
/// @param remaining_entry the remaining entry after the object was added
/// @return true an object was added
/// @return false no object was added
///
static bool get_keyword(const string& entry, program& prog, string& remaining_entry) {
program_fn_t fn;
unsigned int obj_len;
cmd_type_t type;
bool ret = false;
// could be a command
if (program::get_fn(entry.c_str(), fn, type) == ret_ok) {
if (type == cmd_keyword) {
// allocate keyword object
obj_len = sizeof(keyword) + entry.size() + 1;
keyword* new_obj = (keyword*)prog.allocate_back(obj_len, cmd_keyword);
new_obj->set(fn, entry.c_str(), entry.size());
ret = true;
} else if (type == cmd_branch) {
// allocate branch object
obj_len = sizeof(branch) + entry.size() + 1;
branch* new_obj = (branch*)prog.allocate_back(obj_len, cmd_branch);
new_obj->set((branch_fn_t)fn, entry.c_str(), entry.size());
ret = true;
}
}
return ret;
}
/// @brief get a symbol object from entry and add it to a program
///
/// @param entry the entry
/// @param prog the program
/// @param remaining_entry the remaining entry after the object was added
/// @return true an object was added
/// @return false no object was added
///
static bool get_symbol(const string& entry, program& prog, string& remaining_entry) {
bool ret = false;
int entry_len = entry.size();
unsigned int obj_len;
if (entry_len >= 1 && entry[0] == '\'') {
if (entry_len == 1) {
// void symbol entry, like '
// total object length
obj_len = sizeof(symbol) + 1;
// allocate and set object
// symbol beginning with ' is not autoevaluated
symbol* new_obj = (symbol*)prog.allocate_back(obj_len, cmd_symbol);
new_obj->set("", 0, false);
} else {
// symbol entry, like 'toto' or 'toto
int naked_entry_len;
// entry length without prefix / postfix
naked_entry_len = entry[entry_len - 1] == '\'' ? (entry_len - 2) : (entry_len - 1);
// total object length
obj_len = sizeof(symbol) + naked_entry_len + 1;
// allocate and set object
// symbol beginning with ' is not autoevaluated
symbol* new_obj = (symbol*)prog.allocate_back(obj_len, cmd_symbol);
new_obj->set(entry.substr(1, naked_entry_len).c_str(), naked_entry_len, false);
}
ret = true;
}
return ret;
}
/// @brief get an object other from known ones from entry and add it to a program
///
/// @param entry the entry
/// @param prog the program
/// @param remaining_entry the remaining entry after the object was added
/// @return true an object was added
/// @return false no object was added
///
static bool get_other(const string& entry, program& prog, string& remaining_entry) {
bool ret = false;
int entry_len = entry.size();
unsigned int obj_len;
if (entry_len >= 1) {
// entry which is nothing is considered as an auto-evaluated symbol
int naked_entry_len;
// entry length without prefix / postfix
naked_entry_len = entry[entry_len - 1] == '\'' ? (entry_len - 1) : (entry_len);
// total object length
obj_len = sizeof(symbol) + naked_entry_len + 1;
// allocate and set object
// symbol not beginning with ' is autoevaluated (ie is evaluated when pushed
// on stack)
symbol* new_obj = (symbol*)prog.allocate_back(obj_len, cmd_symbol);
new_obj->set(entry.c_str(), naked_entry_len, true);
ret = true;
}
return ret;
}
/// @brief get a string object from entry and add it to a program
///
/// @param entry the entry
/// @param prog the program
/// @param remaining_entry the remaining entry after the object was added
/// @return true an object was added
/// @return false no object was added
///
static bool get_string(const string& entry, program& prog, string& remaining_entry) {
bool ret = false;
unsigned int obj_len;
int entry_len = entry.size();
if (entry_len >= 1 && entry[0] == '"') {
if (entry_len == 1) {
// total object length
obj_len = sizeof(ostring) + 1;
// allocate and set object
ostring* new_obj = (ostring*)prog.allocate_back(obj_len, cmd_string);
new_obj->set("", 0);
} else {
int naked_entry_len;
// entry length without prefix / postfix
naked_entry_len = entry[entry_len - 1] == '"' ? (entry_len - 2) : (entry_len - 1);
// total object length
obj_len = sizeof(ostring) + naked_entry_len + 1;
// allocate and set object
ostring* new_obj = (ostring*)prog.allocate_back(obj_len, cmd_string);
new_obj->set(entry.substr(1, naked_entry_len).c_str(), naked_entry_len);
}
ret = true;
}
return ret;
}
/// @brief get a program object from entry and add it to a program
///
/// @param entry the entry
/// @param prog the program
/// @param remaining_entry the remaining entry after the object was added
/// @return true an object was added
/// @return false no object was added
///
static bool get_program(string& entry, program& prog, string& remaining_entry) {
bool ret = false;
unsigned int obj_len;
int entry_len = entry.size();
if (entry_len >= 2 && entry[0] == '<' && entry[1] == '<') {
int naked_entry_len;
// entry length without prefix / postfix
if (entry_len >= 4 && entry[entry_len - 1] == '>' && entry[entry_len - 2] == '>')
naked_entry_len = entry_len - 4;
else
naked_entry_len = entry_len - 2;
// total object length
obj_len = sizeof(oprogram) + naked_entry_len + 1;
// allocate and set object
oprogram* new_obj = (oprogram*)prog.allocate_back(obj_len, cmd_program);
new_obj->set(&entry[2], naked_entry_len);
ret = true;
}
return ret;
}
/// @brief get a number object from entry and add it to a program
///
/// @param entry the entry
/// @param prog the program
/// @param remaining_entry the remaining entry after the object was added
/// @return true an object was added
/// @return false no object was added
///
static bool get_number(string& entry, program& prog, string& remaining_entry) {
char* endptr;
bool ret = false;
if (entry.size() > 0) {
// pre parse to avoid doing a useless allocation
// detect the begining of a number including nan, inf, @nan@, @inf@
if (entry.find_first_of("+-0123456789.ni@", 0) == 0) {
// detect an arbitrary base entry like 3bXXX or 27bYYY
int base = 0;
size_t base_detect = entry.find_first_of("b", 0);
if (base_detect == 1 || base_detect == 2)
if (sscanf(entry.c_str(), "%db", &base) == 1 && base >= 2 && base <= 62)
entry = entry.substr(base_detect + 1);
else
base = 0;
number* num = (number*)prog.allocate_back(number::calc_size(), cmd_number);
int mpfr_ret = mpfr_strtofr(num->_value.mpfr, entry.c_str(), &endptr, base, MPFR_DEFAULT_RND);
if (endptr != NULL && endptr != entry.c_str()) {
// determine representation
if (base != 0) {
num->_representation = number::base;
num->_base = base;
} else {
string beg = entry.substr(0, 2);
if (beg == "0x" || beg == "0X")
num->_representation = number::hex;
else if (beg == "0b" || beg == "0B")
num->_representation = number::bin;
else
num->_representation = number::dec;
}
ret = true;
// remaining string if any
remaining_entry = endptr;
} else
(void)prog.pop_back();
}
}
return ret;
}
/// @brief get a complex object from entry and add it to a program
///
/// @param entry the entry
/// @param prog the program
/// @param remaining_entry the remaining entry after the object was added
/// @return true an object was added
/// @return false no object was added
///
static bool get_complex(const string& entry, program& prog, string& remaining_entry) {
char* endptr;
bool ret = false;
if (entry.size() > 0) {
size_t comma = entry.find(',');
if (comma != string::npos) {
complex* cplx;
// pre parse RE to avoid doing a useless allocation
// detect the begining of a number, including nan, inf, @nan@, @inf@
string re_str = entry.substr(1, comma - 1).c_str();
if (re_str.find_first_of(" +-0123456789.ni@", 0) == 0) {
cplx = (complex*)prog.allocate_back(complex::calc_size(), cmd_complex);
int mpfr_ret = mpfr_strtofr(cplx->re()->mpfr, re_str.c_str(), &endptr, 0, MPFR_DEFAULT_RND);
if (endptr != NULL && endptr != re_str.c_str()) {
// determine representation
string beg = re_str.substr(0, 2);
if (beg == "0x" || beg == "0X")
cplx->_representation = complex::hex;
else
cplx->_representation = complex::dec;
ret = true;
} else
(void)prog.pop_back();
}
// pre parse IM to avoid doing a useless allocation
// detect the begining of a number, including nan, inf, @nan@, @inf@
string im_str = entry.substr(comma + 1).c_str();
if (ret == true && im_str.find_first_of(" +-0123456789.ni@", 0) == 0) {
ret = false;
int mpfr_ret = mpfr_strtofr(cplx->im()->mpfr, im_str.c_str(), &endptr, 0, MPFR_DEFAULT_RND);
if (endptr != NULL && endptr != im_str.c_str()) {
// determine representation
string beg = im_str.substr(0, 2);
if (beg == "0x" || beg == "0X")
cplx->_representation = complex::hex;
else
cplx->_representation = complex::dec;
ret = true;
} else
(void)prog.pop_back();
}
}
}
return ret;
}
/// @brief recognize a comment object from entry
///
/// @param entry the entry
/// @param remaining_entry the remaining entry after the comment was found
/// @return true a comment was found
/// @return false no comment was found
///
static bool get_comment(string& entry, string& remaining_entry) {
bool ret = false;
unsigned int obj_len;
int entry_len = entry.size();
if (entry_len >= 1 && entry[0] == '#') {
// entry (complete line) is ignored
ret = true;
}
return ret;
}
/// @brief get an object from an entry string and add it to a program
///
/// @param entry the entry string
/// @param prog the program
/// @param remaining_entry the remaining entry after the object was added
/// @return true an object was added to the prog
/// @return false no object was added to the prog
///
static bool _obj_from_string(string& entry, program& prog, string& remaining_entry) {
bool ret = false;
remaining_entry.erase();
if (get_number(entry, prog, remaining_entry))
ret = true;
else if (get_symbol(entry, prog, remaining_entry))
ret = true;
else if (get_string(entry, prog, remaining_entry))
ret = true;
else if (get_program(entry, prog, remaining_entry))
ret = true;
else if (get_keyword(entry, prog, remaining_entry))
ret = true;
else if (get_complex(entry, prog, remaining_entry))
ret = true;
else if (get_comment(entry, remaining_entry))
ret = true;
else
// nothing, considered as an auto-evaluated symbol
if (get_other(entry, prog, remaining_entry))
ret = true;
return ret;
}
/// @brief cut an entry string into entry chunks with respect of types separators
///
/// @param entry the entry
/// @param entries the cut entriy vector
/// @return true entries not vempty
/// @return false entries empty
///
static bool _cut(const char* entry, vector<string>& entries) {
string tmp;
int len = strlen(entry);
for (int i = 0; i < len; i++) {
switch (entry[i]) {
// symbol
case '\'':
// push prec entry if exists
if (tmp.size() > 0) {
entries.push_back(tmp);
tmp.clear();
}
// get symbol
tmp = '\'';
i++;
while ((i < len) && entry[i] != '\'') tmp += entry[i++];
if ((i < len) && entry[i] != '\'') tmp += '\'';
entries.push_back(tmp);
tmp.clear();
break;
// string
case '"':
// push prec entry if exists
if (tmp.size() > 0) {
entries.push_back(tmp);
tmp.clear();
}
// get expression
tmp = '"';
i++;
while (i < len && entry[i] != '"') tmp += entry[i++];
if ((i < len) && entry[i] != '"') tmp += '"';
entries.push_back(tmp);
tmp.clear();
break;
// program
case '<':
// push prec entry if exists
if (tmp.size() > 0) {
entries.push_back(tmp);
tmp.clear();
}
if (strncmp(&entry[i], "<<", 2) == 0) {
int up = 1;
// found a program begin
i += 2;
tmp = "<< ";
// trim leading spaces
while (i < len && isspace(entry[i])) i++;
while (i < len) {
if (strncmp(&entry[i], "<<", 2) == 0) {
up++;
i += 2;
tmp += "<< ";
// trim leading spaces
while (i < len && isspace(entry[i])) i++;
} else if (strncmp(&entry[i], ">>", 2) == 0) {
if (isspace(entry[i - 1]) && entry[i - 2] != '>')
tmp += ">>";
else
tmp += " >>";
up--;
i += 2;
// trim trailing spaces
while (i < len && isspace(entry[i])) i++;
// found end
if (up == 0) break;
} else {
tmp += entry[i];
i++;
}
}
while ((up--) > 0) tmp += " >>";
if (tmp.size() > 0) {
entries.push_back(tmp);
tmp.clear();
}
i--; // i has move 1 too far
} else
// reinject '<'' which is not a prog begin
tmp = "<";
break;
// complex
case '(':
// push prec entry if exists
if (tmp.size() > 0) {
entries.push_back(tmp);
tmp.clear();
}
// get complex
while ((i < len) && entry[i] != ')') tmp += entry[i++];
if ((i < len) && entry[i] != ')') tmp += ')';
if (tmp.size() > 0) {
entries.push_back(tmp);
tmp.clear();
}
break;
// other
default:
if (!isspace(entry[i]))
tmp += entry[i];
else {
if (tmp.size() > 0) {
entries.push_back(tmp);
tmp.clear();
}
}
break;
}
}
if (tmp.size() > 0) {
entries.push_back(tmp);
tmp.clear();
}
return entries.size() > 0;
}
/// @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
/// @return ret_value see this type
///
ret_value program::parse(const char* entry, program& prog) {
vector<string> entries;
ret_value ret = ret_ok;
// 1. cut global entry string into shorter strings
if (_cut(entry, entries)) {
// 2. make an object from each entry, and add it to the program
for (vector<string>::iterator it = entries.begin(); it != entries.end(); it++) {
string remaining_entry;
string main_entry = (*it);
while (main_entry.size() > 0) {
// remaining_entry is used only in case of concatenated entry
// ex: entry="1 2+" -> vector<string> = {"1", "2+"} -> first "1",
// second "2" and remaining_entry="+" this remaining entry is treated as
// an entry
// TODO errors ?
_obj_from_string(main_entry, prog, remaining_entry);
main_entry = remaining_entry;
}
}
}
return ret;
}

View file

@ -1,12 +1,9 @@
#include "program.hpp"
//< return type strings
const char* program::s_ret_value_string[ret_max] = RET_VALUE_STRINGS;
//< kanguage reserved keywords (allowed types are cmd_keyword, cmd_branch or cmd_undef)
program::keyword_t program::s_keywords[] = {
//< language reserved keywords (allowed types are cmd_keyword, cmd_branch or cmd_undef)
vector<program::keyword_t> program::_keywords{
// GENERAL
{cmd_undef, "", NULL, "\nGENERAL"},
{cmd_undef, "", nullptr, "\nGENERAL"},
{cmd_keyword, "nop", &program::rpn_nop, "no operation"},
{cmd_keyword, "help", &program::rpn_help, "this help message"},
{cmd_keyword, "h", &program::rpn_help, ""},
@ -20,28 +17,23 @@ program::keyword_t program::s_keywords[] = {
{cmd_keyword, "history", &program::rpn_history, "see commands history"},
// USUAL OPERATIONS ON REALS AND COMPLEXES
{cmd_undef, "", NULL, "\nUSUAL OPERATIONS ON REALS AND COMPLEXES"},
{cmd_undef, "", nullptr, "\nUSUAL OPERATIONS ON REALS AND COMPLEXES"},
{cmd_keyword, "+", &program::rpn_plus, "addition"},
{cmd_keyword, "-", &program::rpn_minus, "substraction"},
{cmd_keyword, "chs", &program::rpn_neg, "negation"},
{cmd_keyword, "neg", &program::rpn_neg, ""},
{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, "sqr", &program::rpn_square, ""},
{cmd_keyword, "abs", &program::rpn_abs, "absolute value"},
{cmd_keyword, "dec", &program::rpn_dec, "decimal representation"},
{cmd_keyword, "hex", &program::rpn_hex, "hexadecimal representation"},
{cmd_keyword, "bin", &program::rpn_bin, "decimal representation"},
{cmd_keyword, "base", &program::rpn_base, "arbitrary base representation"},
{cmd_keyword, "sign", &program::rpn_sign, "1 if number at stack level 1 is > 0, 0 if == 0, -1 if <= 0"},
{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, "", NULL, "\nOPERATIONS ON REALS"},
{cmd_undef, "", nullptr, "\nOPERATIONS ON REALS"},
{cmd_keyword, "%", &program::rpn_purcent, "purcent"},
{cmd_keyword, "%CH", &program::rpn_purcentCH, "inverse purcent"},
{cmd_keyword, "mod", &program::rpn_modulo, "modulo"},
@ -55,8 +47,8 @@ program::keyword_t program::s_keywords[] = {
{cmd_keyword, "min", &program::rpn_min, "min of 2 real numbers"},
{cmd_keyword, "max", &program::rpn_max, "max of 2 real numbers"},
// nOPERATIONS ON COMPLEXES
{cmd_undef, "", NULL, "\nOPERATIONS ON COMPLEXES"},
// 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"},
@ -67,19 +59,24 @@ program::keyword_t program::s_keywords[] = {
{cmd_keyword, "r->p", &program::rpn_r2p, "polar to cartesian"},
// MODE
{cmd_undef, "", NULL, "\nMODE"},
{cmd_undef, "", nullptr, "\nMODE"},
{cmd_keyword, "std", &program::rpn_std, "standard floating numbers representation. ex: std"},
{cmd_keyword, "fix", &program::rpn_fix, "fixed point representation. ex: 6 fix"},
{cmd_keyword, "sci", &program::rpn_sci, "scientific floating point representation. ex: 20 sci"},
{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\", \"toward zero\", \"toward "
"+inf\", \"toward -inf\", \"away from zero\"] 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, "", NULL, "\nTEST"},
{cmd_undef, "", nullptr, "\nTEST"},
{cmd_keyword, ">", &program::rpn_sup, "binary operator >"},
{cmd_keyword, ">=", &program::rpn_sup_eq, "binary operator >="},
{cmd_keyword, "<", &program::rpn_inf, "binary operator <"},
@ -93,7 +90,7 @@ program::keyword_t program::s_keywords[] = {
{cmd_keyword, "same", &program::rpn_same, "boolean operator same (equal)"},
// STACK
{cmd_undef, "", NULL, "\nSTACK"},
{cmd_undef, "", nullptr, "\nSTACK"},
{cmd_keyword, "swap", &program::rpn_swap, "swap 2 first stack entries"},
{cmd_keyword, "drop", &program::rpn_drop, "drop first stack entry"},
{cmd_keyword, "drop2", &program::rpn_drop2, "drop 2 first stack entries"},
@ -111,19 +108,18 @@ program::keyword_t program::s_keywords[] = {
{cmd_keyword, "over", &program::rpn_over, "push a copy of the element in stack level 2 onto the stack"},
// STRING
{cmd_undef, "", NULL, "\nSTRING"},
{cmd_undef, "", nullptr, "\nSTRING"},
{cmd_keyword, "->str", &program::rpn_instr, "convert an object into a string"},
{cmd_keyword, "str->", &program::rpn_strout, "convert a string into an object"},
{cmd_keyword, "chr", &program::rpn_chr, "convert ASCII character code in stack level 1 into a string"},
{cmd_keyword, "num", &program::rpn_num,
"return ASCII code of the first character of the string in stack level 1 "
"as a real number"},
"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, "", NULL, "\nBRANCH"},
{cmd_undef, "", nullptr, "\nBRANCH"},
{cmd_branch, "if", (program_fn_t)&program::rpn_if,
"if <test-instruction> then <true-instructions> else <false-instructions> "
"end"},
@ -141,14 +137,11 @@ program::keyword_t program::s_keywords[] = {
"<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, "unti", (program_fn_t)&program::rpn_until, ""},
{cmd_branch, "while", (program_fn_t)&program::rpn_while, "while <test-instruction> repeat <loop-instructions> end"},
{cmd_branch, "whil", (program_fn_t)&program::rpn_while, ""},
{cmd_branch, "repeat", (program_fn_t)&program::rpn_repeat, "used with while"},
{cmd_branch, "repea", (program_fn_t)&program::rpn_repeat, ""},
// STORE
{cmd_undef, "", NULL, "\nSTORE"},
{cmd_undef, "", nullptr, "\nSTORE"},
{cmd_keyword, "sto", &program::rpn_sto, "store a variable. ex: 1 'name' sto"},
{cmd_keyword, "rcl", &program::rpn_rcl, "recall a variable. ex: 'name' rcl"},
{cmd_keyword, "purge", &program::rpn_purge, "delete a variable. ex: 'name' purge"},
@ -161,16 +154,15 @@ program::keyword_t program::s_keywords[] = {
{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, "", NULL, "\nPROGRAM"},
{cmd_undef, "", nullptr, "\nPROGRAM"},
{cmd_keyword, "eval", &program::rpn_eval, "evaluate (run) a program, or recall a variable. ex: 'my_prog' eval"},
{cmd_branch, "->", (program_fn_t)&program::rpn_inprog,
"load program local variables. ex: << -> n m << 0 n m for i i + next >> "
">>"},
// TRIG ON REALS AND COMPLEXES
{cmd_undef, "", NULL, "\nTRIG ON REALS AND COMPLEXES"},
{cmd_undef, "", nullptr, "\nTRIG ON REALS AND COMPLEXES"},
{cmd_keyword, "pi", &program::rpn_pi, "pi constant"},
{cmd_keyword, "sin", &program::rpn_sin, "sinus"},
{cmd_keyword, "asin", &program::rpn_asin, "arg sinus"},
@ -182,7 +174,7 @@ program::keyword_t program::s_keywords[] = {
{cmd_keyword, "r->d", &program::rpn_r2d, "convert radians to degrees"},
// LOGS ON REALS AND COMPLEXES
{cmd_undef, "", NULL, "\nLOGS ON REALS AND COMPLEXES"},
{cmd_undef, "", nullptr, "\nLOGS ON REALS AND COMPLEXES"},
{cmd_keyword, "e", &program::rpn_e, "Euler constant"},
{cmd_keyword, "ln", &program::rpn_ln, "logarithm base e"},
{cmd_keyword, "log", &program::rpn_ln, ""},
@ -197,113 +189,127 @@ program::keyword_t program::s_keywords[] = {
{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_sinh, "hyperbolic cosine"},
{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, "", NULL, "\nTIME AND DATE"},
{cmd_undef, "", nullptr, "\nTIME AND DATE"},
{cmd_keyword, "time", &program::rpn_time, "time in local format"},
{cmd_keyword, "date", &program::rpn_date, "date in local format"},
{cmd_keyword, "ticks", &program::rpn_ticks, "system tick in µs"},
// end
{cmd_max, "", NULL, ""},
};
/// keywords map for lexer
map<string, Lexer::ReservedWord> program::_keywordsMap;
/// autocompletion vector for linenoise autocompletion
vector<string> program::_autocompletionWords;
vector<string>& program::getAutocompletionWords() {
if (_autocompletionWords.empty())
for (auto& kw : _keywords)
if (!kw.name.empty()) _autocompletionWords.push_back(kw.name);
return _autocompletionWords;
}
/// @brief run a program on a stack and a heap
///
/// @param stk the stack, storing prog result
/// @param hp the heap, storing variables
/// @return ret_value see this type
///
ret_value program::run(stack& stk, heap& hp) {
ret_value program::run() {
bool go_out = false;
ret_value ret = ret_ok;
cmd_type_t type;
// stack comes from outside
_stack = &stk;
// global heap comes from outside
_heap = &hp;
_err = ret_ok;
_err_context = "";
// branches for 'if'
ret = preprocess();
if (ret != ret_ok) return ret;
if (ret != ret_ok) {
// free allocated
for (object* o : *this) delete o;
_local_heap.clear();
return ret;
}
// iterate commands
for (int i = 0; (go_out == false) && (interrupt_now == false) && (i < (int)size());) {
type = (cmd_type_t)seq_type(i);
object* o = (*this)[i];
switch (o->_type) {
// could be an auto-evaluated symbol
case cmd_symbol:
auto_rcl((symbol*)o);
i++;
break;
// could be an auto-evaluated symbol
if (type == cmd_symbol) {
auto_rcl((symbol*)seq_obj(i));
i++;
}
// a keyword
else if (type == cmd_keyword) {
keyword* k = (keyword*)seq_obj(i);
// call 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;
// error: show it
if (show_error(_err, _err_context) == ret_deadly) {
// pb showing error -> go out software
// a keyword
case cmd_keyword: {
keyword* k = (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;
}
i++;
}
break;
default:
// error: abort prog
go_out = true;
// a branch keyword
else if (type == cmd_branch) {
// call matching function
branch* b = (branch*)seq_obj(i);
int next_cmd = (this->*(b->_fn))(*b);
switch (next_cmd) {
case -1:
i++; // meaning 'next command'
break;
case -(int)ret_runtime_error:
// error: show it
(void)show_error(_err, _err_context);
go_out = true; // end of run
break;
default:
i = next_cmd; // new direction
break;
// error: show it
if (show_error(_err, _err_context) == ret_deadly) {
// pb showing error -> go out software
ret = ret_good_bye;
}
break;
}
i++;
break;
}
}
// not a command, but a stack entry, manage it
else {
// copy the program stack entry to the running stack
stack::copy_and_push_back(*this, i, stk);
i++;
// a branch keyword
case cmd_branch: {
// call matching function
branch* b = (branch*)o;
int next_cmd = (this->*(b->fn))(*b);
switch (next_cmd) {
case -1:
i++; // meaning 'next command'
break;
case -(int)ret_runtime_error:
// error: show it
(void)show_error(_err, _err_context);
go_out = true; // end of run
break;
default:
i = next_cmd; // new direction
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();
if (interrupt_now) {
fprintf(stderr, "\nInterrupted\n");
cerr << endl << "Interrupted" << endl;
interrupt_now = false;
}
@ -324,8 +330,8 @@ void program::stop() { interrupt_now = true; }
/// @return false the branch name is NOT str_to_compare
///
bool program::compare_branch(branch* b, const char* str_to_compare, int len) {
if (b->_len >= len)
return strncasecmp(b->_value, str_to_compare, len) == 0;
if (str_to_compare != nullptr)
return b->value == str_to_compare;
else
return false;
}
@ -347,9 +353,8 @@ ret_value program::preprocess(void) {
// analyse if-then-else-end branches
// analyse start-{next, step} branches
for (int i = 0; i < (int)size(); i++) {
int type = seq_type(i);
if (type == cmd_branch) {
branch* k = (branch*)seq_obj(i);
if ((*this)[i]->_type == cmd_branch) {
branch* k = (branch*)(*this)[i];
if (compare_branch(k, "if", 2)) {
if_layout_t layout;
layout.index_if_or_do_or_while = i;
@ -406,7 +411,7 @@ ret_value program::preprocess(void) {
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;
((branch*)seq_obj(vlayout[layout_index].index_then_or_unti_or_repeat))->arg2 =
((branch*)(*this)[vlayout[layout_index].index_then_or_unti_or_repeat])->arg2 =
next; // fill branch2 (if was false) of 'then'
} else if (compare_branch(k, "start", 5))
vstartindex.push_back(i);
@ -420,7 +425,7 @@ ret_value program::preprocess(void) {
return ret_syntax;
}
k->arg1 = vstartindex[vstartindex.size() - 1]; // 'next' arg1 = 'start' index
((branch*)seq_obj(vstartindex[vstartindex.size() - 1]))->arg2 =
((branch*)(*this)[vstartindex[vstartindex.size() - 1]])->arg2 =
i; // 'for' or 'start' arg2 = 'next' index
vstartindex.pop_back();
} else if (compare_branch(k, "step", 4)) {
@ -430,7 +435,7 @@ ret_value program::preprocess(void) {
return ret_syntax;
}
k->arg1 = vstartindex[vstartindex.size() - 1]; // fill 'step' branch1 = 'start' index
((branch*)seq_obj(vstartindex[vstartindex.size() - 1]))->arg2 =
((branch*)(*this)[vstartindex[vstartindex.size() - 1]])->arg2 =
i; // 'for' or 'start' arg2 = 'next' index
vstartindex.pop_back();
} else if (compare_branch(k, "->", 2)) {
@ -522,7 +527,7 @@ ret_value program::preprocess(void) {
}
// fill 'repeat' arg1 with 'end+1'
((branch*)seq_obj(vlayout[layout_index].index_then_or_unti_or_repeat))->arg1 = i + 1;
((branch*)(*this)[vlayout[layout_index].index_then_or_unti_or_repeat])->arg1 = i + 1;
layout_index--;
} else {
// this end closes an if..then..(else)
@ -533,11 +538,11 @@ ret_value program::preprocess(void) {
}
if (vlayout[layout_index].index_else != -1)
// fill 'end' branch of 'else'
((branch*)seq_obj(vlayout[layout_index].index_else))->arg2 = i;
((branch*)(*this)[vlayout[layout_index].index_else])->arg2 = i;
else {
// fill 'end' branch of 'then'
if (vlayout[layout_index].index_then_or_unti_or_repeat != -1)
((branch*)seq_obj(vlayout[layout_index].index_then_or_unti_or_repeat))->arg2 = i;
((branch*)(*this)[vlayout[layout_index].index_then_or_unti_or_repeat])->arg2 = i;
else {
// error: show it
show_syntax_error("missing then");
@ -563,15 +568,75 @@ ret_value program::preprocess(void) {
return ret_ok;
}
/// @brief parse an entry string: cut it into objects chunks and add them to a program
///
/// @param entry the entry string
/// @param prog the program to be filled
/// @return ret_value see this type
///
ret_value program::parse(string& entry) {
vector<Lexer::SynElement> elements;
vector<Lexer::SynError> errors;
ret_value ret = ret_ok;
// prepare map for finding reserved keywords
if (_keywordsMap.empty())
for (auto& kw : _keywords)
if (!kw.name.empty()) _keywordsMap[kw.name] = {kw.type, kw.fn};
// separate the entry string
if (lexer(entry, _keywordsMap, elements, errors)) {
// make objects from parsed elements
for (Lexer::SynElement& element : elements) {
switch (element.type) {
case cmd_number:
push_back(new number(element.re, element.reBase));
break;
case cmd_complex:
push_back(new ocomplex(element.re, element.im, element.reBase, element.imBase));
break;
case cmd_string:
push_back(new ostring(element.value));
break;
case cmd_symbol:
push_back(new symbol(element.value, element.autoEval));
break;
case cmd_program:
push_back(new oprogram(element.value));
break;
case cmd_keyword:
push_back(new keyword(element.fn, element.value));
break;
case cmd_branch:
push_back(new branch((branch_fn_t)element.fn, element.value));
break;
default:
show_error(ret_unknown_err, "error creating program from entry");
}
}
} else
for (SynError& err : errors) show_syntax_error(err.err.c_str());
return ret;
}
/// @brief show the last error set
///
/// @return ret_value see this type
///
ret_value program::show_error() {
ret_value ret;
// clang-format off
vector<string> errorStrings {"ok", "unknown command", "missing operand", "bad operand type",
"out of range", "unknown variable", "internal error, aborting", "deadly", "goodbye", "not implemented",
"no operation", "syntax error", "division by zero", "runtime error", "aborted current entry", "out of memory",
"bad value"};
// clang-format on
// show last recorded error
cerr << _err_context << ": error " << _err << ": " << s_ret_value_string[_err] << endl;
if ((size_t)_err < errorStrings.size())
cerr << _err_context << ": error " << _err << ": " << errorStrings[_err] << endl;
else
cerr << _err_context << " (unknown error code)" << endl;
switch (_err) {
case ret_internal:
case ret_deadly:
@ -634,17 +699,14 @@ ret_value program::get_err(void) { return _err; }
/// @param st the stack to show
/// @param show_separator whether to show a stack level prefix or not
///
void program::show_stack(stack& st, bool show_separator) {
if (st.size() == 1) {
((object*)st.back())->show();
printf("\n");
} else {
void program::show_stack(rpnstack& st, bool show_separator) {
if (st.size() == 1)
cout << st[0] << endl;
else
for (int i = st.size() - 1; i >= 0; i--) {
if (show_separator) printf("%d%s", i + 1, SHOW_STACK_SEPARATOR);
((object*)st[i])->show();
printf("\n");
if (show_separator) cout << i + 1 << "> ";
cout << st[i] << endl;
}
}
}
/// @brief apply default precision mode and digits
@ -652,14 +714,9 @@ void program::show_stack(stack& st, bool show_separator) {
void program::apply_default() {
// default float precision, float mode
number::s_mode = DEFAULT_MODE;
number::s_decimal_digits = DEFAULT_DECIMAL_DIGITS;
number::s_digits = DEFAULT_DECIMAL_DIGITS;
mpreal::set_default_prec(MPFR_DEFAULT_PREC_BITS);
// format for mpfr_printf
stringstream ss;
ss << number::s_decimal_digits;
number::s_mpfr_printf_format = string(MPFR_FORMAT_BEG) + ss.str() + string(MPFR_FORMAT_STD);
// default calc precision for MPFR
floating_t::s_mpfr_prec = (mpfr_prec_t)MPFR_DEFAULT_PREC_BITS;
floating_t::s_mpfr_prec_bytes = MPFR_DEFAULT_STORING_LENGTH_BYTES;
static mp_rnd_t def_rnd = mpreal::get_default_rnd();
mpreal::set_default_rnd(def_rnd);
}

View file

@ -1,31 +1,21 @@
#ifndef PROGRAM_HPP
#define PROGRAM_HPP
// std c
#include <math.h>
#include <stdint.h>
#include <stdlib.h>
#include <sys/mman.h>
// std c++
#include <fstream>
#include <iomanip>
#include <iostream>
#include <sstream>
#include <vector>
using namespace std;
// external libs
#define MPFR_USE_NO_MACRO
#include <mpfr.h>
#include "linenoise.h"
#include <mpreal.h>
using namespace mpfr;
// internal includes
#include "constant.h"
#include "debug.h"
#include "escape.h"
#include "object.hpp"
#include "stack.hpp"
#include "version.h"
#include "lexer.hpp"
//< convinient structure to preprocess a program
struct if_layout_t {
@ -40,23 +30,23 @@ struct if_layout_t {
};
//< program class: the class containing a string parser, all the programs keywords, a stack for running the program
class program : public stack {
class program : public deque<object*>, public Lexer {
public:
program(program* parent_prog = NULL) {
_parent_prog = parent_prog;
program(rpnstack& stk, heap& hp, program* parent = nullptr):_stack(stk),_heap(hp),_parent(parent) {
interrupt_now = false;
}
virtual ~program() {
_local_heap.clear();
clear();
}
// parser
static ret_value parse(const char* entry, program& prog);
static ret_value entry(program& prog);
static void entry_completion_generator(const char* text, linenoiseCompletions* lc);
ret_value parse(string& entry);
static ret_value get_fn(const char* fn_name, program_fn_t& fn, cmd_type_t& type);
// running
ret_value run(stack& stk, heap& hp);
ret_value run();
void stop();
bool compare_keyword(keyword* k, const char* str_to_compare, int len);
bool compare_branch(branch* b, const char* str_to_compare, int len);
ret_value preprocess(void);
@ -66,10 +56,12 @@ class program : public stack {
void show_syntax_error(const char* context);
ret_value get_err(void);
static void show_stack(stack& st, bool show_separator = true);
static void show_stack(rpnstack& st, bool show_separator = true);
static void apply_default();
static vector<string>& getAutocompletionWords();
private:
bool interrupt_now;
@ -78,33 +70,30 @@ class program : public stack {
string _err_context;
// global stack holding results for user
stack* _stack;
rpnstack& _stack;
// global heap (sto, rcl)
heap* _heap;
heap& _heap;
// local heap for local loop variables (for..next)
heap _local_heap;
// calc stack internally used by branch and calc commands
stack _calc_stack;
// parent prog for inheriting heaps
program* _parent_prog;
program* _parent;
int stack_size() { return _stack->size(); }
int stack_size() { return _stack.size(); }
private:
static const char* s_ret_value_string[ret_max];
// keywords
struct keyword_t {
cmd_type_t type;
char name[MAX_COMMAND_LENGTH];
string name;
program_fn_t fn;
string comment;
};
static keyword_t s_keywords[];
static vector<keyword_t> _keywords;
static map<string, Lexer::ReservedWord> _keywordsMap;
static vector<string> _autocompletionWords;
// keywords implementation
////
@ -168,7 +157,7 @@ class program : public stack {
void rpn_atanh();
// program
bool find_variable(string& variable, object*& obj, unsigned int& size);
bool find_variable(string& variable, object*& obj);
void rpn_eval(void);
int rpn_inprog(branch& myobj);
@ -243,14 +232,14 @@ class program : public stack {
void rpn_strsub();
// test-core
void test_get_stack(string& stack_is, stack& stk);
void test_get_stack(string& stack_is, rpnstack& stk);
void test_show_result(string title, int tests, int tests_failed, int steps, int steps_failed);
void rpn_test();
void test(string test_filename, int& total_tests, int& total_tests_failed, int& total_steps,
int& total_steps_failed);
// test
int cmp_strings_on_stack_top();
long cmp_strings_on_stack_top();
void rpn_sup(void);
void rpn_sup_eq(void);
void rpn_inf(void);
@ -280,51 +269,26 @@ class program : public stack {
void rpn_ticks();
};
// convinience macros for rpn_xx function
// clang-format off
// convenience macros for rpn_xx function
// carefull : some of these macros modify program flow
#define ERR_CONTEXT(err) \
do { \
_err = (err); \
_err_context = __FUNCTION__; \
#define setErrorContext(err) do { _err = (err); _err_context = __FUNCTION__; } while (0)
#define MIN_ARGUMENTS(num) do { \
if (stack_size() < (num)) { setErrorContext(ret_missing_operand); return; } \
} while (0)
#define MIN_ARGUMENTS(num) \
do { \
if (stack_size() < (num)) { \
ERR_CONTEXT(ret_missing_operand); \
return; \
} \
#define MIN_ARGUMENTS_RET(num, ret) do { \
if (stack_size() < (num)) { setErrorContext(ret_missing_operand); return (ret); } \
} while (0)
#define MIN_ARGUMENTS_RET(num, ret) \
do { \
if (stack_size() < (num)) { \
ERR_CONTEXT(ret_missing_operand); \
return (ret); \
} \
#define ARG_MUST_BE_OF_TYPE(num, type) do { \
if (_stack.at(num)->_type != (type)) { setErrorContext(ret_bad_operand_type); return; } \
} while (0)
#define ARG_MUST_BE_OF_TYPE(num, type) \
do { \
if (_stack->get_type(num) != (type)) { \
ERR_CONTEXT(ret_bad_operand_type); \
return; \
} \
} while (0)
#define ARG_MUST_BE_OF_TYPE_RET(num, type, ret) \
do { \
if (_stack->get_type(num) != (type)) { \
ERR_CONTEXT(ret_bad_operand_type); \
return (ret); \
} \
} while (0)
#define IS_ARG_TYPE(num, type) (_stack->get_type(num) == (type))
#define CHECK_MPFR(op) \
do { \
(void)(op); \
#define ARG_MUST_BE_OF_TYPE_RET(num, type, ret) do { \
if (_stack.at(num)->_type != (type)) { setErrorContext(ret_bad_operand_type); return (ret); } \
} while (0)
#endif

View file

@ -1,7 +1,7 @@
#include "program.hpp"
/// @brief if keyword (branch) implementation
///
///
/// @param myobj the current branch object
/// @return int index of the next object to run in the current program
/// @return -1 the next object index to run in the current program is the current+1
@ -11,16 +11,16 @@ int program::rpn_if(branch& myobj) {
MIN_ARGUMENTS_RET(1, -(int)ret_runtime_error);
ARG_MUST_BE_OF_TYPE_RET(0, cmd_number, -(int)ret_runtime_error);
if (mpfr_cmp_si(((number*)_stack->get_obj(0))->_value.mpfr, 0UL) != 0)
if (_stack.value<number>(0) != 0)
myobj.arg1 = 1;
else
myobj.arg1 = 0;
(void)_stack->pop_back();
_stack.pop();
return -1;
}
/// @brief then keyword (branch) implementation
///
///
/// @param myobj the current branch object
/// @return int index of the next object to run in the current program
///
@ -30,7 +30,7 @@ int program::rpn_then(branch& myobj) {
// 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 = (branch*)seq_obj(myobj.arg3);
branch* if_cmd = (branch*)at(myobj.arg3);
if (if_cmd->arg1 == 1)
return myobj.arg1;
else
@ -38,7 +38,7 @@ int program::rpn_then(branch& myobj) {
}
/// @brief else keyword (branch) implementation
///
///
/// @param myobj the current branch object
/// @return int index of the next object to run in the current program
/// @return -1 the next object index to run in the current program is the current+1
@ -49,7 +49,7 @@ int program::rpn_else(branch& myobj) {
// 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 = (branch*)seq_obj(myobj.arg3);
branch* if_cmd = (branch*)at(myobj.arg3);
if (if_cmd->arg1 == 1)
return myobj.arg2;
else
@ -57,7 +57,7 @@ int program::rpn_else(branch& myobj) {
}
/// @brief end keyword (branch) implementation
///
///
/// @param myobj the current branch object
/// @return int index of the next object to run in the current program
/// @return -1 the next object index to run in the current program is the current+1
@ -72,8 +72,8 @@ int program::rpn_end(branch& myobj) {
ARG_MUST_BE_OF_TYPE_RET(0, cmd_number, -(int)ret_runtime_error);
// check arg
number* arg = (number*)_stack->pop_back();
if (mpfr_cmp_si(arg->_value.mpfr, 0UL) == 0) ret = myobj.arg1;
if (_stack.value<number>(0) == 0) ret = myobj.arg1;
_stack.pop();
}
// arg2 = index of while+1 in case of while..repeat..end
else if (myobj.arg2 != -1)
@ -83,7 +83,7 @@ int program::rpn_end(branch& myobj) {
}
/// @brief do keyword (branch) implementation
///
///
/// @param myobj the current branch object
/// @return int index of the next object to run in the current program
/// @return -1 the next object index to run in the current program is the current+1
@ -94,7 +94,7 @@ int program::rpn_do(branch& myobj) {
}
/// @brief until keyword (branch) implementation
///
///
/// @param myobj the current branch object
/// @return int index of the next object to run in the current program
/// @return -1 the next object index to run in the current program is the current+1
@ -105,7 +105,7 @@ int program::rpn_until(branch& myobj) {
}
/// @brief ift keyword (branch) implementation
///
///
/// @param myobj the current branch object
/// @return int index of the next object to run in the current program
/// @return -1 the next object index to run in the current program is the current+1
@ -116,20 +116,15 @@ void program::rpn_ift(void) {
// check ift arg
// arg is true if number != 0 or if is nan or +/-inf
number* testee = ((number*)_stack->get_obj(1));
if (!mpfr_zero_p(testee->_value.mpfr)) {
CHECK_MPFR(stack::copy_and_push_back(*_stack, _stack->size() - 1, _calc_stack));
(void)_stack->pop_back(2);
CHECK_MPFR(stack::copy_and_push_back(_calc_stack, _calc_stack.size() - 1, *_stack));
(void)_calc_stack.pop_back();
} else
(void)_stack->pop_back(2);
if (_stack.value<number>(1) != 0) {
_stack.erase(1);
} else {
_stack.pop_front(2);
}
}
/// @brief ifte keyword (branch) implementation
///
///
/// @param myobj the current branch object
/// @return int index of the next object to run in the current program
/// @return -1 the next object index to run in the current program is the current+1
@ -139,22 +134,17 @@ void program::rpn_ifte(void) {
ARG_MUST_BE_OF_TYPE(2, cmd_number);
// check ifte arg
// arg is true if number != 0 or if is nan or +/-inf
number* testee = ((number*)_stack->get_obj(2));
if (!mpfr_zero_p(testee->_value.mpfr))
CHECK_MPFR(stack::copy_and_push_back(*_stack, _stack->size() - 2, _calc_stack));
else
CHECK_MPFR(stack::copy_and_push_back(*_stack, _stack->size() - 1, _calc_stack));
(void)_stack->pop_back(3);
CHECK_MPFR(stack::copy_and_push_back(_calc_stack, _calc_stack.size() - 1, *_stack));
(void)_calc_stack.pop_back();
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 int index of the next object to run in the current program
/// @return -1 the next object index to run in the current program is the current+1
@ -165,7 +155,7 @@ int program::rpn_while(branch& myobj) {
}
/// @brief repeat keyword (branch) implementation
///
///
/// @param myobj the current branch object
/// @return int index of the next object to run in the current program
/// @return -1 the next object index to run in the current program is the current+1
@ -178,14 +168,14 @@ int program::rpn_repeat(branch& myobj) {
// check arg
// myobj.arg1 is end+1
number* arg = (number*)_stack->pop_back();
if (mpfr_cmp_si(arg->_value.mpfr, 0UL) == 0) ret = myobj.arg1;
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 int index of the next object to run in the current program
/// @return -1 the next object index to run in the current program is the current+1
@ -197,18 +187,13 @@ int program::rpn_start(branch& myobj) {
ARG_MUST_BE_OF_TYPE_RET(0, cmd_number, -(int)ret_runtime_error);
ARG_MUST_BE_OF_TYPE_RET(1, cmd_number, -(int)ret_runtime_error);
// farg2 = last value of start command
stack::copy_and_push_back(*_stack, _stack->size() - 1, _calc_stack);
myobj.farg2 = (number*)_calc_stack.back();
_stack->pop_back();
// farg1 = first value of start command
stack::copy_and_push_back(*_stack, _stack->size() - 1, _calc_stack);
myobj.farg1 = (number*)_calc_stack.back();
_stack->pop_back();
// loop boundaries
myobj.firstIndex = _stack.value<number>(1);
myobj.lastIndex = _stack.value<number>(0);
_stack.pop_front(2);
// test value
if (myobj.farg1->_value > myobj.farg2->_value)
if (myobj.firstIndex > myobj.lastIndex)
// last boundary lower than first boundary
// -> next command shall be after 'next'
// arg2 holds index of 'next'
@ -218,7 +203,7 @@ int program::rpn_start(branch& myobj) {
}
/// @brief for keyword (branch) implementation
///
///
/// @param myobj the current branch object
/// @return int index of the next object to run in the current program
/// @return -1 the next object index to run in the current program is the current+1
@ -230,68 +215,67 @@ int program::rpn_for(branch& myobj) {
ARG_MUST_BE_OF_TYPE_RET(0, cmd_number, -(int)ret_runtime_error);
ARG_MUST_BE_OF_TYPE_RET(1, cmd_number, -(int)ret_runtime_error);
symbol* sym = ((symbol*)seq_obj(myobj.arg1));
symbol* sym = (symbol*)at(myobj.arg1); // arg1 = loop variable index
// farg2 = last value of for command
// arg1 = index of symbol to increase
stack::copy_and_push_back(*_stack, _stack->size() - 1, _calc_stack);
myobj.farg2 = (number*)_calc_stack.back();
_stack->pop_back();
// farg1 = first value of for command
stack::copy_and_push_back(*_stack, _stack->size() - 1, _calc_stack);
myobj.farg1 = (number*)_calc_stack.back();
_stack->pop_back();
// loop boundaries
myobj.firstIndex = _stack.value<number>(1);
myobj.lastIndex = _stack.value<number>(0);
// test value
if (myobj.farg1->_value > myobj.farg2->_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
_local_heap.add(sym->_value, (object*)myobj.farg1, myobj.farg1->size());
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.pop_front(2);
return ret;
}
/// @brief next keyword (branch) implementation
///
///
/// @param myobj the current branch object
/// @return int index of the next object to run in the current program
/// @return -1 the next object index to run in the current program is the current+1
///
int program::rpn_next(branch& myobj) {
// arg1 = index of start or for command in program
// farg1 = current count
branch* start_or_for = (branch*)seq_obj(myobj.arg1);
// arg1 = loop variable index
// firstIndex = current point in the loop
branch* start_or_for = (branch*)at(myobj.arg1);
if (!myobj.arg_bool) {
myobj.arg_bool = true;
myobj.farg1 = start_or_for->farg1;
myobj.firstIndex = start_or_for->firstIndex;
}
// increment then test
// carefull: round toward minus infinity to avoid missing last boundary (because growing step)
mpfr_add_si(myobj.farg1->_value.mpfr, myobj.farg1->_value.mpfr, 1UL, MPFR_RNDD);
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;
unsigned int size;
symbol* var = (symbol*)seq_obj(start_or_for->arg1);
symbol* var = (symbol*)at(start_or_for->arg1);
// increase symbol variable
_local_heap.replace_value(string(var->_value), myobj.farg1, myobj.farg1->size());
// store symbol variable (asserted existing in the local heap)
((number*)_local_heap[var->value])->value = myobj.firstIndex;
}
// test value
if (myobj.farg1->_value > start_or_for->farg2->_value) {
if (myobj.firstIndex > start_or_for->lastIndex) {
// end of loop
myobj.arg_bool = false; // init again next time
_calc_stack.pop_back(2);
return -1;
} else {
// for command: next instruction will be after symbol variable
@ -303,7 +287,7 @@ int program::rpn_next(branch& myobj) {
}
/// @brief step keyword (branch) implementation
///
///
/// @param myobj the current branch object
/// @return int index of the next object to run in the current program
/// @return -1 the next object index to run in the current program is the current+1
@ -313,39 +297,39 @@ int program::rpn_step(branch& myobj) {
MIN_ARGUMENTS_RET(1, -(int)ret_runtime_error);
ARG_MUST_BE_OF_TYPE_RET(0, cmd_number, -(int)ret_runtime_error);
number* step = (number*)_stack->pop_back();
mpreal step = _stack.value<number>(0);
_stack.pop();
// end of loop if step is negative or zero
if (mpfr_cmp_d(step->_value.mpfr, 0.0) <= 0)
if (step <= 0)
ret = -1;
else {
// arg1 = index of start or for command in program
// farg1 = current count
branch* start_or_for = (branch*)seq_obj(myobj.arg1);
// arg1 = loop variable index
// firstIndex = current count
branch* start_or_for = (branch*)at(myobj.arg1);
if (!myobj.arg_bool) {
myobj.arg_bool = true;
myobj.farg1 = start_or_for->farg1;
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.farg1->_value.mpfr, myobj.farg1->_value.mpfr, step->_value.mpfr, MPFR_RNDD);
mpfr_add(myobj.firstIndex.mpfr_ptr(), myobj.firstIndex.mpfr_srcptr(), step.mpfr_srcptr(), MPFR_RNDD);
// for command: increment symbol too
if (start_or_for->arg1 != -1) {
object* obj;
unsigned int size;
symbol* var = (symbol*)seq_obj(start_or_for->arg1);
symbol* var = (symbol*)at(start_or_for->arg1);
// increase symbol variable
_local_heap.replace_value(string(var->_value), myobj.farg1, myobj.farg1->size());
}
((number*)_local_heap[var->value])->value = myobj.firstIndex;
}
// test loop value is out of range
if (myobj.farg1->_value > start_or_for->farg2->_value) {
if (myobj.firstIndex > start_or_for->lastIndex) {
// end of loop
myobj.arg_bool = false; // init again next time
_calc_stack.pop_back(2);
ret = -1;
} else {
// for command: next instruction will be after symbol variable

View file

@ -6,13 +6,8 @@
void program::rpn_re() {
MIN_ARGUMENTS(1);
ARG_MUST_BE_OF_TYPE(0, cmd_complex);
stack::copy_and_push_back(*_stack, _stack->size() - 1, _calc_stack);
_stack->pop_back();
number* re = (number*)_stack->allocate_back(number::calc_size(), cmd_number);
CHECK_MPFR(mpfr_set(re->_value.mpfr, ((complex*)_calc_stack.get_obj(0))->re()->mpfr, floating_t::s_mpfr_rnd));
_calc_stack.pop_back();
_stack.push_front(new number(real(_stack.value<ocomplex>(0))));
_stack.erase(1);
}
/// @brief im keyword implementation
@ -21,13 +16,8 @@ void program::rpn_re() {
void program::rpn_im() {
MIN_ARGUMENTS(1);
ARG_MUST_BE_OF_TYPE(0, cmd_complex);
stack::copy_and_push_back(*_stack, _stack->size() - 1, _calc_stack);
_stack->pop_back();
number* im = (number*)_stack->allocate_back(number::calc_size(), cmd_number);
CHECK_MPFR(mpfr_set(im->_value.mpfr, ((complex*)_calc_stack.get_obj(0))->im()->mpfr, floating_t::s_mpfr_rnd));
_calc_stack.pop_back();
_stack.push_front(new number(imag(_stack.value<ocomplex>(0))));
_stack.erase(1);
}
/// @brief arg keyword implementation
@ -36,15 +26,8 @@ void program::rpn_im() {
void program::rpn_arg() {
MIN_ARGUMENTS(1);
ARG_MUST_BE_OF_TYPE(0, cmd_complex);
// calc atan2(x/y)
complex* cplx = (complex*)_stack->pop_back();
number* num = (number*)_calc_stack.allocate_back(number::calc_size(), cmd_number);
CHECK_MPFR(mpfr_atan2(num->_value.mpfr, cplx->im()->mpfr, cplx->re()->mpfr, floating_t::s_mpfr_rnd));
stack::copy_and_push_back(_calc_stack, _calc_stack.size() - 1, *_stack);
_calc_stack.pop_back();
_stack.push_front(new number(arg(_stack.value<ocomplex>(0))));
_stack.erase(1);
}
/// @brief conj keyword implementation
@ -53,9 +36,7 @@ void program::rpn_arg() {
void program::rpn_conj() {
MIN_ARGUMENTS(1);
ARG_MUST_BE_OF_TYPE(0, cmd_complex);
complex* cplx = (complex*)_stack->back();
CHECK_MPFR(mpfr_neg(cplx->im()->mpfr, cplx->im()->mpfr, floating_t::s_mpfr_rnd));
_stack.value<ocomplex>(0) = conj(_stack.value<ocomplex>(0));
}
/// @brief r2c keyword implementation
@ -65,16 +46,8 @@ void program::rpn_r2c() {
MIN_ARGUMENTS(2);
ARG_MUST_BE_OF_TYPE(0, cmd_number);
ARG_MUST_BE_OF_TYPE(1, cmd_number);
stack::copy_and_push_back(*_stack, _stack->size() - 2, _calc_stack);
stack::copy_and_push_back(*_stack, _stack->size() - 1, _calc_stack);
_stack->pop_back();
_stack->pop_back();
complex* cplx = (complex*)_stack->allocate_back(complex::calc_size(), cmd_complex);
CHECK_MPFR(mpfr_set(cplx->re()->mpfr, ((number*)_calc_stack.get_obj(1))->_value.mpfr, floating_t::s_mpfr_rnd));
CHECK_MPFR(mpfr_set(cplx->im()->mpfr, ((number*)_calc_stack.get_obj(0))->_value.mpfr, floating_t::s_mpfr_rnd));
_calc_stack.pop_back(2);
_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
@ -83,16 +56,9 @@ void program::rpn_r2c() {
void program::rpn_c2r() {
MIN_ARGUMENTS(1);
ARG_MUST_BE_OF_TYPE(0, cmd_complex);
stack::copy_and_push_back(*_stack, _stack->size() - 1, _calc_stack);
_stack->pop_back();
number* re = (number*)_stack->allocate_back(number::calc_size(), cmd_number);
number* im = (number*)_stack->allocate_back(number::calc_size(), cmd_number);
CHECK_MPFR(mpfr_set(re->_value.mpfr, ((complex*)_calc_stack.back())->re()->mpfr, floating_t::s_mpfr_rnd));
CHECK_MPFR(mpfr_set(im->_value.mpfr, ((complex*)_calc_stack.back())->im()->mpfr, floating_t::s_mpfr_rnd));
_calc_stack.pop_back();
_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
@ -101,20 +67,10 @@ void program::rpn_c2r() {
void program::rpn_r2p() {
MIN_ARGUMENTS(1);
ARG_MUST_BE_OF_TYPE(0, cmd_complex);
rpn_dup();
rpn_dup();
rpn_arg();
complex* cplx = (complex*)_stack->get_obj(1);
CHECK_MPFR(mpfr_set(cplx->im()->mpfr, ((number*)_stack->back())->_value.mpfr, floating_t::s_mpfr_rnd));
_stack->pop_back();
rpn_swap();
rpn_abs();
cplx = (complex*)_stack->get_obj(1);
CHECK_MPFR(mpfr_set(cplx->re()->mpfr, ((number*)_stack->back())->_value.mpfr, floating_t::s_mpfr_rnd));
_stack->pop_back();
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
@ -123,26 +79,5 @@ void program::rpn_r2p() {
void program::rpn_p2r() {
MIN_ARGUMENTS(1);
ARG_MUST_BE_OF_TYPE(0, cmd_complex);
stack::copy_and_push_back(*_stack, _stack->size() - 1, _calc_stack);
_calc_stack.allocate_back(number::calc_size(), cmd_number);
// assert complex is polar
complex* rhotheta = (complex*)_calc_stack.get_obj(1);
number* tmp = (number*)_calc_stack.get_obj(0);
complex* result = (complex*)_stack->back();
// calc cos(theta)
CHECK_MPFR(mpfr_set(tmp->_value.mpfr, rhotheta->im()->mpfr, floating_t::s_mpfr_rnd));
CHECK_MPFR(mpfr_cos(tmp->_value.mpfr, tmp->_value.mpfr, floating_t::s_mpfr_rnd));
// calc rcos(theta)
CHECK_MPFR(mpfr_mul(result->re()->mpfr, rhotheta->re()->mpfr, tmp->_value.mpfr, floating_t::s_mpfr_rnd));
// calc sin(theta)
CHECK_MPFR(mpfr_set(tmp->_value.mpfr, rhotheta->im()->mpfr, floating_t::s_mpfr_rnd));
CHECK_MPFR(mpfr_sin(tmp->_value.mpfr, tmp->_value.mpfr, floating_t::s_mpfr_rnd));
// calc rsin(theta)
CHECK_MPFR(mpfr_mul(result->im()->mpfr, rhotheta->re()->mpfr, tmp->_value.mpfr, floating_t::s_mpfr_rnd));
_stack.value<ocomplex>(0) = polar(abs(_stack.value<ocomplex>(0)), arg(_stack.value<ocomplex>(0)));
}

View file

@ -1,4 +1,34 @@
#include <stdio.h>
#include "escape.h"
#include "linenoise.h"
#include "program.hpp"
#include "version.h"
// description
#define XSTR(a) STR(a)
#define STR(a) #a
static const string _description{
ATTR_BOLD "R" ATTR_OFF "everse " ATTR_BOLD "P" ATTR_OFF "olish " ATTR_BOLD "N" ATTR_OFF
"otation language\n\n"
"using " ATTR_BOLD "GMP" ATTR_OFF " v" XSTR(__GNU_MP_VERSION) "." XSTR(__GNU_MP_VERSION_MINOR) "." XSTR(
__GNU_MP_VERSION_PATCHLEVEL) " under GNU LGPL\n" ATTR_BOLD "MPFR" ATTR_OFF " v" MPFR_VERSION_STRING
" under GNU LGPL\n"
"and " ATTR_BOLD "linenoise-ng" ATTR_OFF " v" LINENOISE_VERSION
" under BSD\n"};
// syntax
static const string _syntax{ATTR_BOLD "Syntax" ATTR_OFF
": rpn [command]\n"
"with optional command = list of commands"};
static const map<string, mpfr_rnd_t> _mpfr_round{{"nearest (even)", MPFR_RNDN},
{"toward zero", MPFR_RNDZ},
{"toward +inf", MPFR_RNDU},
{"toward -inf", MPFR_RNDD},
{"away from zero", MPFR_RNDA},
{"faithful rounding", MPFR_RNDF},
{"nearest (away from zero)", MPFR_RNDNA}};
/// @brief nop keyword implementation
///
@ -8,85 +38,63 @@ void program::rpn_nop() {
/// @brief quit keyword implementation
///
void program::rpn_good_bye() { ERR_CONTEXT(ret_good_bye); }
void program::rpn_good_bye() { setErrorContext(ret_good_bye); }
/// @brief nop keyword implementation
/// the result is written on stdout
///
void program::rpn_help() {
// software name
printf("\n" ATTR_BOLD "%s" ATTR_OFF "\n", uname);
cout << endl << ATTR_BOLD << RPN_UNAME << ATTR_OFF << endl;
// description
printf("%s\n\n", description);
// _description
cout << _description << endl << endl;
// syntax
printf("%s\n", syntax);
// _syntax
cout << _syntax << endl;
// keywords
unsigned int i = 0;
while (s_keywords[i].type != cmd_max) {
if (s_keywords[i].comment.size() != 0) {
for (auto& kw : _keywords)
if (!kw.comment.empty()) {
// titles in bold
if (s_keywords[i].type == cmd_undef) printf(ATTR_BOLD);
if (kw.type == cmd_undef) cout << ATTR_BOLD;
// show title or keyword + comment
printf("%s\t%s\n", s_keywords[i].name, s_keywords[i].comment.c_str());
if (s_keywords[i].type == cmd_undef) printf(ATTR_OFF);
cout << kw.name << '\t' << kw.comment << endl;
if (kw.type == cmd_undef) cout << ATTR_OFF;
}
i++;
}
printf("\n");
cout << endl;
// show mode
printf("Current float mode is ");
cout << "Current float mode is ";
switch (number::s_mode) {
case number::std:
printf("'std'");
cout << "'std'";
break;
case number::fix:
printf("'fix'");
cout << "'fix'";
break;
case number::sci:
printf("'sci'");
cout << "'sci'";
break;
default:
printf("unknown");
cout << "unknown";
break;
}
printf(" with %d digits after the decimal point\n", number::s_decimal_digits);
// bits precision, decimal digits and rounding mode
printf("Current floating point precision is %d bits\n", (int)floating_t::s_mpfr_prec);
printf("Current rounding mode is \"%s\"\n", floating_t::s_mpfr_rnd_str[floating_t::s_mpfr_rnd]);
printf("\n\n");
}
/// @brief calculate a number of digits for a given base from a precision in bits
///
/// @param base the base
/// @param bit_precision the precision in bits
/// @return int the number of digits
///
static int base_digits_from_bit_precision(int base, int bit_precision) {
return (int)ceil(bit_precision * log(2.0) / log((double)base)) - 1;
}
/// @brief print a decimal digit in a given MPFR format
///
/// @param decimal_digits the number
/// @param printf_format the format
/// @return string the result string
///
static string make_digit_format(int decimal_digits, const char* printf_format) {
stringstream ss;
ss << MPFR_FORMAT_BEG;
ss << number::s_decimal_digits;
ss << printf_format;
return ss.str();
cout << " with " << number::s_digits << " digits after the decimal point" << endl;
cout << "Current floating point precision is " << (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 precision is in the precision min/max
///
///
/// @param precision the precision in bits
/// @return true the precision is good
/// @return false the precision is not good
@ -110,15 +118,15 @@ void program::rpn_std() {
MIN_ARGUMENTS(1);
ARG_MUST_BE_OF_TYPE(0, cmd_number);
double digits = double(((number*)_stack->pop_back())->_value);
double digits = double(_stack.value<number>(0));
if (check_decimal_digits(digits)) {
// set mode, decimal digits and print format
number::s_mode = number::std;
number::s_decimal_digits = (int)digits;
number::s_mpfr_printf_format = make_digit_format(number::s_decimal_digits, MPFR_FORMAT_STD);
number::s_digits = (int)digits;
_stack.pop();
} else
ERR_CONTEXT(ret_out_of_range);
setErrorContext(ret_out_of_range);
}
/// @brief fix keyword implementation
@ -127,15 +135,15 @@ void program::rpn_fix() {
MIN_ARGUMENTS(1);
ARG_MUST_BE_OF_TYPE(0, cmd_number);
double digits = double(((number*)_stack->pop_back())->_value);
double digits = double(_stack.value<number>(0));
if (check_decimal_digits(digits)) {
// set mode, decimal digits and print format
number::s_mode = number::fix;
number::s_decimal_digits = (int)digits;
number::s_mpfr_printf_format = make_digit_format(number::s_decimal_digits, MPFR_FORMAT_FIX);
number::s_digits = (int)digits;
_stack.pop();
} else
ERR_CONTEXT(ret_out_of_range);
setErrorContext(ret_out_of_range);
}
/// @brief sci keyword implementation
@ -144,34 +152,24 @@ void program::rpn_sci() {
MIN_ARGUMENTS(1);
ARG_MUST_BE_OF_TYPE(0, cmd_number);
double digits = double(((number*)_stack->pop_back())->_value);
double digits = double(_stack.value<number>(0));
if (check_decimal_digits(digits)) {
// set mode, decimal digits and print format
number::s_mode = number::sci;
number::s_decimal_digits = (int)digits;
number::s_mpfr_printf_format = make_digit_format(number::s_decimal_digits, MPFR_FORMAT_SCI);
number::s_digits = (int)digits;
_stack.pop();
} else
ERR_CONTEXT(ret_out_of_range);
setErrorContext(ret_out_of_range);
}
/// @brief version keyword implementation
/// @brief _version keyword implementation
///
void program::rpn_version() {
// allocate and set object
unsigned int naked_entry_len = strlen(version);
ostring* str = (ostring*)_stack->allocate_back(sizeof(ostring) + naked_entry_len + 1, cmd_string);
str->set(version, naked_entry_len);
}
void program::rpn_version() { _stack.push_front(new ostring(RPN_VERSION)); }
/// @brief uname keyword implementation
/// @brief _uname keyword implementation
///
void program::rpn_uname() {
// allocate and set object
unsigned int naked_entry_len = strlen(uname);
ostring* str = (ostring*)_stack->allocate_back(sizeof(ostring) + naked_entry_len + 1, cmd_string);
str->set(uname, naked_entry_len);
}
void program::rpn_uname() { _stack.push_front(new ostring(RPN_UNAME)); }
/// @brief history keyword implementation
///
@ -179,7 +177,7 @@ void program::rpn_history() {
// see command history on stdout
int index = 0;
char* line = linenoiseHistoryLine(index);
while (line != NULL) {
while (line != nullptr) {
cout << line << endl;
free(line);
line = linenoiseHistoryLine(++index);
@ -190,14 +188,8 @@ void program::rpn_history() {
///
void program::rpn_type() {
MIN_ARGUMENTS(1);
int type = _stack->pop_back()->_type;
if (type < 0 || type >= (int)cmd_max) type = (int)cmd_undef;
unsigned int string_size = strlen(object::s_cmd_type_string[type]);
unsigned int size = sizeof(symbol) + string_size + 1;
ostring* typ = (ostring*)_stack->allocate_back(size, cmd_string);
typ->set(object::s_cmd_type_string[type], string_size);
_stack.push(new ostring(_stack.at(0)->name()));
_stack.erase(1);
}
/// @brief default keyword implementation
@ -211,19 +203,18 @@ void program::rpn_precision() {
ARG_MUST_BE_OF_TYPE(0, cmd_number);
// set precision
unsigned long prec = mpfr_get_ui(((number*)_stack->pop_back())->_value.mpfr, floating_t::s_mpfr_rnd);
unsigned long prec = _stack.value<number>(0).toULong();
if (prec >= (unsigned long)MPFR_PREC_MIN && prec <= (unsigned long)MPFR_PREC_MAX) {
floating_t::s_mpfr_prec = (mpfr_prec_t)prec;
floating_t::s_mpfr_prec_bytes = mpfr_custom_get_size(prec);
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_decimal_digits = base_digits_from_bit_precision(10, floating_t::s_mpfr_prec);
number::s_mpfr_printf_format = make_digit_format(number::s_decimal_digits, MPFR_FORMAT_STD);
number::s_digits = bits2digits(mpreal::get_default_prec());
}
_stack.pop();
} else
ERR_CONTEXT(ret_out_of_range);
setErrorContext(ret_out_of_range);
}
/// @brief round keyword implementation
@ -232,13 +223,12 @@ void program::rpn_round() {
MIN_ARGUMENTS(1);
ARG_MUST_BE_OF_TYPE(0, cmd_string);
ostring* str = (ostring*)_stack->pop_back();
bool done = false;
for (int rnd = (int)MPFR_DEFAULT_RND; rnd <= (int)MPFR_RNDA; rnd++) {
if (string(floating_t::s_mpfr_rnd_str[rnd]) == str->_value) {
floating_t::s_mpfr_rnd = (mpfr_rnd_t)rnd;
done = true;
}
}
if (!done) ERR_CONTEXT(ret_out_of_range);
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();
}

View file

@ -2,349 +2,172 @@
/// @brief e keyword implementation
///
void program::rpn_e(void) {
number* euler = (number*)_stack->allocate_back(number::calc_size(), cmd_number);
euler->_value = 1L;
CHECK_MPFR(mpfr_exp(euler->_value.mpfr, euler->_value.mpfr, floating_t::s_mpfr_rnd));
}
void program::rpn_e(void) { _stack.push(new number(const_euler())); }
/// @brief log10 keyword implementation
///
void program::rpn_log10() {
MIN_ARGUMENTS(1);
if (_stack->get_type(0) == cmd_number || _stack->get_type(0) == cmd_complex) {
// log10(z)=ln(z)/ln(10)
rpn_ln();
number* ten = (number*)_stack->allocate_back(number::calc_size(), cmd_number);
CHECK_MPFR(mpfr_set_d(ten->_value.mpfr, 10.0, floating_t::s_mpfr_rnd));
rpn_ln();
rpn_div();
} else
ERR_CONTEXT(ret_bad_operand_type);
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->get_type(0) == cmd_number || _stack->get_type(0) == cmd_complex) {
floating_t* left = &((number*)_stack->get_obj(0))->_value;
number* ten = (number*)_stack->allocate_back(number::calc_size(), cmd_number);
CHECK_MPFR(mpfr_set_d(ten->_value.mpfr, 10.0, floating_t::s_mpfr_rnd));
rpn_ln();
rpn_mul();
rpn_exp();
}
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->get_type(0) == cmd_number || _stack->get_type(0) == cmd_complex) {
// log2(z)=ln(z)/ln(2)
rpn_ln();
number* two = (number*)_stack->allocate_back(number::calc_size(), cmd_number);
CHECK_MPFR(mpfr_set_d(two->_value.mpfr, 2.0, floating_t::s_mpfr_rnd));
rpn_ln();
rpn_div();
} else
ERR_CONTEXT(ret_bad_operand_type);
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->get_type(0) == cmd_number || _stack->get_type(0) == cmd_complex) {
floating_t* left = &((number*)_stack->get_obj(0))->_value;
number* two = (number*)_stack->allocate_back(number::calc_size(), cmd_number);
CHECK_MPFR(mpfr_set_d(two->_value.mpfr, 2.0, floating_t::s_mpfr_rnd));
rpn_ln();
rpn_mul();
rpn_exp();
}
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->get_type(0) == cmd_number) {
number* left = (number*)_stack->back();
// x<0 -> ln(x) = ln(-x)+i*pi
if (mpfr_cmp_si(left->_value.mpfr, 0) < 0) {
stack::copy_and_push_back(*_stack, _stack->size() - 1, _calc_stack);
_stack->pop_back();
left = (number*)_calc_stack.back();
complex* cplx = (complex*)_stack->allocate_back(complex::calc_size(), cmd_complex);
CHECK_MPFR(mpfr_neg(cplx->re()->mpfr, left->_value.mpfr, floating_t::s_mpfr_rnd));
CHECK_MPFR(mpfr_log(cplx->re()->mpfr, cplx->re()->mpfr, floating_t::s_mpfr_rnd));
CHECK_MPFR(mpfr_const_pi(cplx->im()->mpfr, floating_t::s_mpfr_rnd));
_calc_stack.pop_back();
} else
CHECK_MPFR(mpfr_log(left->_value.mpfr, left->_value.mpfr, floating_t::s_mpfr_rnd));
} else if (_stack->get_type(0) == cmd_complex) {
// ln(x+iy) = 0.5*ln(x*x+y*y) + i atan(x/y)
stack::copy_and_push_back(*_stack, _stack->size() - 1, _calc_stack);
floating_t* x = ((complex*)_calc_stack.get_obj(0))->re();
floating_t* y = ((complex*)_calc_stack.get_obj(0))->im();
floating_t* re = ((complex*)_stack->get_obj(0))->re();
floating_t* im = ((complex*)_stack->get_obj(0))->im();
// 1. atan(x/y)
CHECK_MPFR(mpfr_atan2(im->mpfr, y->mpfr, x->mpfr, floating_t::s_mpfr_rnd));
// 2. 0.5*ln(x*x+y*y)
CHECK_MPFR(mpfr_mul(x->mpfr, x->mpfr, x->mpfr, floating_t::s_mpfr_rnd));
CHECK_MPFR(mpfr_mul(y->mpfr, y->mpfr, y->mpfr, floating_t::s_mpfr_rnd));
CHECK_MPFR(mpfr_add(re->mpfr, x->mpfr, y->mpfr, floating_t::s_mpfr_rnd));
CHECK_MPFR(mpfr_log(re->mpfr, re->mpfr, floating_t::s_mpfr_rnd));
CHECK_MPFR(mpfr_mul_d(re->mpfr, re->mpfr, 0.5, floating_t::s_mpfr_rnd));
_calc_stack.pop_back();
} else
ERR_CONTEXT(ret_bad_operand_type);
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->get_type(0) == cmd_number) {
floating_t* left = &((number*)_stack->get_obj(0))->_value;
CHECK_MPFR(mpfr_exp(left->mpfr, left->mpfr, floating_t::s_mpfr_rnd));
} else if (_stack->get_type(0) == cmd_complex) {
// exp(x)*(cos(y)+i sin(y))
stack::copy_and_push_back(*_stack, _stack->size() - 1, _calc_stack);
floating_t* x = ((complex*)_calc_stack.get_obj(0))->re();
floating_t* y = ((complex*)_calc_stack.get_obj(0))->im();
floating_t* re = ((complex*)_stack->get_obj(0))->re();
floating_t* im = ((complex*)_stack->get_obj(0))->im();
CHECK_MPFR(mpfr_cos(re->mpfr, y->mpfr, floating_t::s_mpfr_rnd));
CHECK_MPFR(mpfr_sin(im->mpfr, y->mpfr, floating_t::s_mpfr_rnd));
CHECK_MPFR(mpfr_exp(x->mpfr, x->mpfr, floating_t::s_mpfr_rnd));
CHECK_MPFR(mpfr_mul(re->mpfr, re->mpfr, x->mpfr, floating_t::s_mpfr_rnd));
CHECK_MPFR(mpfr_mul(im->mpfr, im->mpfr, x->mpfr, floating_t::s_mpfr_rnd));
_calc_stack.pop_back();
} else
ERR_CONTEXT(ret_bad_operand_type);
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->get_type(0) == cmd_number || _stack->get_type(0) == cmd_complex) {
// exp(x)-1
rpn_exp();
number* one = (number*)_stack->allocate_back(number::calc_size(), cmd_number);
CHECK_MPFR(mpfr_set_d(one->_value.mpfr, 1.0, floating_t::s_mpfr_rnd));
rpn_minus();
} else
ERR_CONTEXT(ret_bad_operand_type);
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->get_type(0) == cmd_number || _stack->get_type(0) == cmd_complex) {
// ln(x+1)
number* one = (number*)_stack->allocate_back(number::calc_size(), cmd_number);
CHECK_MPFR(mpfr_set_d(one->_value.mpfr, 1.0, floating_t::s_mpfr_rnd));
rpn_plus();
rpn_ln();
} else
ERR_CONTEXT(ret_bad_operand_type);
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->get_type(0) == cmd_number) {
floating_t* left = &((number*)_stack->get_obj(0))->_value;
CHECK_MPFR(mpfr_sinh(left->mpfr, left->mpfr, floating_t::s_mpfr_rnd));
} else if (_stack->get_type(0) == cmd_complex) {
// sinh(x+iy)=sinh(x)cos(y)+icosh(x)sin(y)
stack::copy_and_push_back(*_stack, _stack->size() - 1, _calc_stack);
floating_t* tmp = &((number*)_calc_stack.allocate_back(number::calc_size(), cmd_number))->_value;
floating_t* x = ((complex*)_calc_stack.get_obj(1))->re();
floating_t* y = ((complex*)_calc_stack.get_obj(1))->im();
floating_t* re = ((complex*)_stack->get_obj(0))->re();
floating_t* im = ((complex*)_stack->get_obj(0))->im();
CHECK_MPFR(mpfr_sinh(re->mpfr, x->mpfr, floating_t::s_mpfr_rnd));
CHECK_MPFR(mpfr_cos(tmp->mpfr, y->mpfr, floating_t::s_mpfr_rnd));
CHECK_MPFR(mpfr_mul(re->mpfr, re->mpfr, tmp->mpfr, floating_t::s_mpfr_rnd));
CHECK_MPFR(mpfr_cosh(im->mpfr, x->mpfr, floating_t::s_mpfr_rnd));
CHECK_MPFR(mpfr_sin(tmp->mpfr, y->mpfr, floating_t::s_mpfr_rnd));
CHECK_MPFR(mpfr_mul(im->mpfr, im->mpfr, tmp->mpfr, floating_t::s_mpfr_rnd));
_calc_stack.pop_back(2);
} else
ERR_CONTEXT(ret_bad_operand_type);
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->get_type(0) == cmd_number) {
floating_t* left = &((number*)_stack->get_obj(0))->_value;
CHECK_MPFR(mpfr_asinh(left->mpfr, left->mpfr, floating_t::s_mpfr_rnd));
} else if (_stack->get_type(0) == cmd_complex) {
// asinh(z)=ln(z+sqrt(1+z*z))
rpn_dup();
rpn_square();
number* num = (number*)_stack->allocate_back(number::calc_size(), cmd_number);
CHECK_MPFR(mpfr_set_d(num->_value.mpfr, 1.0, floating_t::s_mpfr_rnd));
rpn_plus();
rpn_squareroot();
rpn_plus();
rpn_ln();
} else
ERR_CONTEXT(ret_bad_operand_type);
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->get_type(0) == cmd_number) {
floating_t* left = &((number*)_stack->get_obj(0))->_value;
CHECK_MPFR(mpfr_cosh(left->mpfr, left->mpfr, floating_t::s_mpfr_rnd));
} else if (_stack->get_type(0) == cmd_complex) {
// acosh(x+iy)=cosh(x)cos(y)+isinh(x)sin(y)
stack::copy_and_push_back(*_stack, _stack->size() - 1, _calc_stack);
floating_t* tmp = &((number*)_calc_stack.allocate_back(number::calc_size(), cmd_number))->_value;
floating_t* x = ((complex*)_calc_stack.get_obj(1))->re();
floating_t* y = ((complex*)_calc_stack.get_obj(1))->im();
floating_t* re = ((complex*)_stack->get_obj(0))->re();
floating_t* im = ((complex*)_stack->get_obj(0))->im();
CHECK_MPFR(mpfr_cosh(re->mpfr, x->mpfr, floating_t::s_mpfr_rnd));
CHECK_MPFR(mpfr_cos(tmp->mpfr, y->mpfr, floating_t::s_mpfr_rnd));
CHECK_MPFR(mpfr_mul(re->mpfr, re->mpfr, tmp->mpfr, floating_t::s_mpfr_rnd));
CHECK_MPFR(mpfr_sinh(im->mpfr, x->mpfr, floating_t::s_mpfr_rnd));
CHECK_MPFR(mpfr_sin(tmp->mpfr, y->mpfr, floating_t::s_mpfr_rnd));
CHECK_MPFR(mpfr_mul(im->mpfr, im->mpfr, tmp->mpfr, floating_t::s_mpfr_rnd));
_calc_stack.pop_back(2);
} else
ERR_CONTEXT(ret_bad_operand_type);
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->get_type(0) == cmd_number) {
floating_t* left = &((number*)_stack->get_obj(0))->_value;
CHECK_MPFR(mpfr_acosh(left->mpfr, left->mpfr, floating_t::s_mpfr_rnd));
} else if (_stack->get_type(0) == cmd_complex) {
// acosh(z)=ln(z+sqrt(z+1)sqrt(z-1))
rpn_dup();
number* num = (number*)_stack->allocate_back(number::calc_size(), cmd_number);
CHECK_MPFR(mpfr_set_d(num->_value.mpfr, 1.0, floating_t::s_mpfr_rnd));
rpn_plus();
rpn_dup();
num = (number*)_stack->allocate_back(number::calc_size(), cmd_number);
CHECK_MPFR(mpfr_set_d(num->_value.mpfr, 2.0, floating_t::s_mpfr_rnd));
rpn_minus();
rpn_mul();
rpn_squareroot();
rpn_plus();
rpn_ln();
} else
ERR_CONTEXT(ret_bad_operand_type);
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->get_type(0) == cmd_number) {
floating_t* left = &((number*)_stack->get_obj(0))->_value;
CHECK_MPFR(mpfr_tanh(left->mpfr, left->mpfr, floating_t::s_mpfr_rnd));
} else if (_stack->get_type(0) == cmd_complex) {
// tanh(x+iy)=(tanh(x)+itan(y)) / (1 + itanh(x)tan(y))
rpn_dup();
floating_t* x = ((complex*)_stack->get_obj(1))->re();
floating_t* y = ((complex*)_stack->get_obj(1))->im();
floating_t* re = ((complex*)_stack->get_obj(1))->re();
floating_t* im = ((complex*)_stack->get_obj(1))->im();
CHECK_MPFR(mpfr_tanh(re->mpfr, x->mpfr, floating_t::s_mpfr_rnd));
CHECK_MPFR(mpfr_tan(im->mpfr, y->mpfr, floating_t::s_mpfr_rnd));
CHECK_MPFR(mpfr_tanh(x->mpfr, x->mpfr, floating_t::s_mpfr_rnd));
CHECK_MPFR(mpfr_tan(y->mpfr, y->mpfr, floating_t::s_mpfr_rnd));
CHECK_MPFR(mpfr_mul(y->mpfr, y->mpfr, x->mpfr, floating_t::s_mpfr_rnd));
CHECK_MPFR(mpfr_set_d(x->mpfr, 1.0, floating_t::s_mpfr_rnd));
rpn_div();
} else
ERR_CONTEXT(ret_bad_operand_type);
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->get_type(0) == cmd_number) {
floating_t* left = &((number*)_stack->get_obj(0))->_value;
CHECK_MPFR(mpfr_atanh(left->mpfr, left->mpfr, floating_t::s_mpfr_rnd));
} else if (_stack->get_type(0) == cmd_complex) {
// atanh(z)=0.5*ln((1+z)/(1-z))
rpn_dup();
number* num = (number*)_stack->allocate_back(number::calc_size(), cmd_number);
CHECK_MPFR(mpfr_set_d(num->_value.mpfr, 1.0, floating_t::s_mpfr_rnd));
rpn_plus();
rpn_swap();
num = (number*)_stack->allocate_back(number::calc_size(), cmd_number);
CHECK_MPFR(mpfr_set_d(num->_value.mpfr, 1.0, floating_t::s_mpfr_rnd));
rpn_minus();
rpn_neg();
rpn_div();
rpn_ln();
num = (number*)_stack->allocate_back(number::calc_size(), cmd_number);
CHECK_MPFR(mpfr_set_d(num->_value.mpfr, 0.5, floating_t::s_mpfr_rnd));
rpn_mul();
} else
ERR_CONTEXT(ret_bad_operand_type);
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);
}

View file

@ -1,29 +1,28 @@
#include "program.hpp"
/// @brief find variable by its name in local heap, parens heaps, global heap
///
/// @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
/// @param size its size
/// @return true variable was found
/// @return false variable was not found
///
bool program::find_variable(string& variable, object*& obj, unsigned int& size) {
bool program::find_variable(string& variable, object*& obj) {
bool found = false;
program* parent = _parent_prog;
program* parent = _parent;
if (_local_heap.get(variable, obj, size))
if (_local_heap.get(variable, obj))
found = true;
else {
while (parent != NULL) {
if (parent->_local_heap.get(variable, obj, size)) {
while (parent != nullptr) {
if (parent->_local_heap.get(variable, obj)) {
found = true;
break;
}
parent = parent->_parent_prog;
parent = parent->_parent;
}
if (!found) {
if (_heap->get(variable, obj, size)) found = true;
if (_heap.get(variable, obj)) found = true;
}
}
@ -37,40 +36,40 @@ void program::rpn_eval(void) {
string prog_text;
MIN_ARGUMENTS(1);
if (IS_ARG_TYPE(0, cmd_symbol)) {
if (_stack.type(0) == cmd_symbol) {
// recall a variable
object* obj;
unsigned int size;
string variable(((symbol*)_stack->back())->_value);
string variable(_stack.value<symbol>(0));
_stack.pop();
// if variable holds a program, run this program
if (find_variable(variable, obj, size)) {
if (find_variable(variable, obj)) {
if (obj->_type == cmd_program) {
prog_text = ((oprogram*)obj)->_value;
(void)_stack->pop_back();
prog_text = _stack.value<oprogram>(0);
_stack.pop();
run_prog = true;
} else {
// else recall this variable (i.e. stack its content)
(void)_stack->pop_back();
stack::copy_and_push_back(obj, *_stack, size);
_stack.push_front(obj);
}
} else
ERR_CONTEXT(ret_unknown_variable);
} else if (IS_ARG_TYPE(0, cmd_program)) {
setErrorContext(ret_unknown_variable);
} else if (_stack.type(0) == cmd_program) {
// eval a program
prog_text = ((oprogram*)_stack->pop_back())->_value;
prog_text = _stack.value<oprogram>(0);
_stack.pop();
run_prog = true;
} else
ERR_CONTEXT(ret_bad_operand_type);
setErrorContext(ret_bad_operand_type);
// run prog if any
if (run_prog) {
program prog(this);
program prog(_stack, _heap, this);
// make program from entry
if (program::parse(prog_text.c_str(), prog) == ret_ok) {
if (prog.parse(prog_text) == ret_ok) {
// run it
prog.run(*_stack, *_heap);
prog.run();
}
}
}
@ -83,7 +82,7 @@ int program::rpn_inprog(branch& myobj) {
bool prog_found = false;
if (myobj.arg1 == -1) {
ERR_CONTEXT(ret_unknown_err);
setErrorContext(ret_unknown_err);
return -1;
}
@ -93,15 +92,15 @@ int program::rpn_inprog(branch& myobj) {
// find next oprogram object
for (unsigned int i = myobj.arg1 + 1; i < size(); i++) {
// count symbol
if (seq_type(i) == cmd_symbol) count_symbols++;
if (at(i)->_type == cmd_symbol) count_symbols++;
// stop if prog
else if (seq_type(i) == cmd_program) {
else if (at(i)->_type == cmd_program) {
prog_found = true;
break;
}
// found something other than symbol
else {
ERR_CONTEXT(ret_bad_operand_type);
setErrorContext(ret_bad_operand_type);
show_error(_err, context);
return -1;
}
@ -109,39 +108,39 @@ int program::rpn_inprog(branch& myobj) {
// found 0 symbols
if (count_symbols == 0) {
ERR_CONTEXT(ret_syntax);
setErrorContext(ret_syntax);
show_error(_err, context);
return -1;
}
// <oprogram> is missing
if (!prog_found) {
ERR_CONTEXT(ret_syntax);
setErrorContext(ret_syntax);
show_error(_err, context);
return -1;
}
// check symbols number vs stack size
if (stack_size() < count_symbols) {
ERR_CONTEXT(ret_missing_operand);
setErrorContext(ret_missing_operand);
show_error(_err, context);
return -1;
}
// load variables
for (unsigned int i = myobj.arg1 + count_symbols; i > myobj.arg1; i--) {
_local_heap.add(string(((symbol*)seq_obj(i))->_value), _stack->get_obj(0), _stack->get_len(0));
(void)_stack->pop_back();
_local_heap[string(((symbol*)at(i))->value)] = _stack.at(0)->clone();
_stack.pop_front();
}
// run the program
string entry(((oprogram*)seq_obj(myobj.arg1 + count_symbols + 1))->_value);
program prog(this);
string entry(((oprogram*)at(myobj.arg1 + count_symbols + 1))->value);
program prog(_stack, _heap, this);
// make the program from entry
if (program::parse(entry.c_str(), prog) == ret_ok) {
if (prog.parse(entry) == ret_ok) {
// run it
prog.run(*_stack, *_heap);
prog.run();
}
// point on next command

View file

@ -5,52 +5,33 @@
void program::rpn_plus() {
MIN_ARGUMENTS(2);
// adding strings
if (_stack->get_type(0) == cmd_string && _stack->get_type(1) == cmd_string) {
unsigned int left_str_size = ((ostring*)_stack->get_obj(1))->_len;
unsigned int right_str_size = ((ostring*)_stack->get_obj(0))->_len;
stack::copy_and_push_back(*_stack, _stack->size() - 2, _calc_stack);
stack::copy_and_push_back(*_stack, _stack->size() - 1, _calc_stack);
(void)_stack->pop_back();
(void)_stack->pop_back();
ostring* str =
(ostring*)_stack->allocate_back(left_str_size + right_str_size + 1 + sizeof(ostring), cmd_string);
str->_len = left_str_size + right_str_size;
strncpy(str->_value, ((ostring*)_calc_stack.get_obj(1))->_value, left_str_size);
strncat(str->_value, ((ostring*)_calc_stack.get_obj(0))->_value, right_str_size);
_calc_stack.pop_back();
_calc_stack.pop_back();
// strings
if (_stack.type(0) == cmd_string && _stack.type(1) == cmd_string) {
_stack.value<ostring>(1) += _stack.value<ostring>(0);
_stack.pop();
}
// adding numbers
else if (_stack->get_type(0) == cmd_number && _stack->get_type(1) == cmd_number) {
number* right = (number*)_stack->pop_back();
number* left = (number*)_stack->back();
CHECK_MPFR(mpfr_add(left->_value.mpfr, left->_value.mpfr, right->_value.mpfr, floating_t::s_mpfr_rnd));
// numbers
else if (_stack.type(0) == cmd_number && _stack.type(1) == cmd_number) {
_stack.value<number>(1) += _stack.value<number>(0);
_stack.pop();
}
// adding complexes
else if (_stack->get_type(0) == cmd_complex && _stack->get_type(1) == cmd_complex) {
complex* right = (complex*)_stack->pop_back();
complex* left = (complex*)_stack->back();
CHECK_MPFR(mpfr_add(left->re()->mpfr, left->re()->mpfr, right->re()->mpfr, floating_t::s_mpfr_rnd));
CHECK_MPFR(mpfr_add(left->im()->mpfr, left->im()->mpfr, right->im()->mpfr, floating_t::s_mpfr_rnd));
// complexes
else if (_stack.type(0) == cmd_complex && _stack.type(1) == cmd_complex) {
_stack.value<ocomplex>(1) += _stack.value<ocomplex>(0);
_stack.pop();
}
// adding complex+number
else if (_stack->get_type(0) == cmd_number && _stack->get_type(1) == cmd_complex) {
number* right = (number*)_stack->pop_back();
complex* left = (complex*)_stack->back();
CHECK_MPFR(mpfr_add(left->re()->mpfr, left->re()->mpfr, right->_value.mpfr, floating_t::s_mpfr_rnd));
// complex+number
else if (_stack.type(0) == cmd_number && _stack.type(1) == cmd_complex) {
_stack.value<ocomplex>(1) += _stack.value<number>(0);
_stack.pop();
}
// adding number+complex
else if (_stack->get_type(0) == cmd_complex && _stack->get_type(1) == cmd_number) {
// number+complex
else if (_stack.type(0) == cmd_complex && _stack.type(1) == cmd_number) {
rpn_swap();
number* right = (number*)_stack->pop_back();
complex* left = (complex*)_stack->back();
CHECK_MPFR(mpfr_add(left->re()->mpfr, left->re()->mpfr, right->_value.mpfr, floating_t::s_mpfr_rnd));
_stack.value<ocomplex>(1) += _stack.value<number>(0);
_stack.pop();
} else
ERR_CONTEXT(ret_bad_operand_type);
setErrorContext(ret_bad_operand_type);
}
/// @brief - keyword implementation
@ -58,33 +39,28 @@ void program::rpn_plus() {
void program::rpn_minus() {
MIN_ARGUMENTS(2);
// substracting numbers
if (_stack->get_type(0) == cmd_number && _stack->get_type(1) == cmd_number) {
number* right = (number*)_stack->pop_back();
number* left = (number*)_stack->back();
CHECK_MPFR(mpfr_sub(left->_value.mpfr, left->_value.mpfr, right->_value.mpfr, floating_t::s_mpfr_rnd));
// numbers
if (_stack.type(0) == cmd_number && _stack.type(1) == cmd_number) {
_stack.value<number>(1) -= _stack.value<number>(0);
_stack.pop();
}
// substracting complexes
else if (_stack->get_type(0) == cmd_complex && _stack->get_type(1) == cmd_complex) {
complex* right = (complex*)_stack->pop_back();
complex* left = (complex*)_stack->back();
CHECK_MPFR(mpfr_sub(left->re()->mpfr, left->re()->mpfr, right->re()->mpfr, floating_t::s_mpfr_rnd));
CHECK_MPFR(mpfr_sub(left->im()->mpfr, left->im()->mpfr, right->im()->mpfr, floating_t::s_mpfr_rnd));
// complexes
else if (_stack.type(0) == cmd_complex && _stack.type(1) == cmd_complex) {
_stack.value<ocomplex>(1) -= _stack.value<ocomplex>(0);
_stack.pop();
}
// substracting complex-number
else if (_stack->get_type(0) == cmd_number && _stack->get_type(1) == cmd_complex) {
number* right = (number*)_stack->pop_back();
complex* left = (complex*)_stack->back();
CHECK_MPFR(mpfr_sub(left->re()->mpfr, left->re()->mpfr, right->_value.mpfr, floating_t::s_mpfr_rnd));
// subbing complex-number
else if (_stack.type(0) == cmd_number && _stack.type(1) == cmd_complex) {
_stack.value<ocomplex>(1) -= _stack.value<number>(0);
_stack.pop();
}
// substracting number-complex
else if (_stack->get_type(0) == cmd_complex && _stack->get_type(1) == cmd_number) {
// number-complex
else if (_stack.type(0) == cmd_complex && _stack.type(1) == cmd_number) {
rpn_swap();
number* right = (number*)_stack->pop_back();
complex* left = (complex*)_stack->back();
CHECK_MPFR(mpfr_sub(left->re()->mpfr, right->_value.mpfr, left->re()->mpfr, floating_t::s_mpfr_rnd));
_stack.value<ocomplex>(1) = _stack.value<number>(0) - _stack.value<ocomplex>(1);
_stack.pop();
} else
ERR_CONTEXT(ret_bad_operand_type);
setErrorContext(ret_bad_operand_type);
}
/// @brief * keyword implementation
@ -92,98 +68,28 @@ void program::rpn_minus() {
void program::rpn_mul() {
MIN_ARGUMENTS(2);
// multiplying numbers
if (_stack->get_type(0) == cmd_number && _stack->get_type(1) == cmd_number) {
number* right = (number*)_stack->pop_back();
number* left = (number*)_stack->back();
CHECK_MPFR(mpfr_mul(left->_value.mpfr, left->_value.mpfr, right->_value.mpfr, floating_t::s_mpfr_rnd));
// mutiplying numbers
if (_stack.type(0) == cmd_number && _stack.type(1) == cmd_number) {
_stack.value<number>(1) *= _stack.value<number>(0);
_stack.pop();
}
// multiplying complexes (a+ib)*(x+iy)=(ax-by)+i(ay+bx)=a(x+iy)+b(-y+ix)
else if (_stack->get_type(0) == cmd_complex && _stack->get_type(1) == cmd_complex) {
complex* right = (complex*)_stack->pop_back(); // x+iy
complex* left = (complex*)_stack->back(); // a+ib
stack::copy_and_push_back(*_stack, _stack->size() - 1, _calc_stack);
complex* left_sav = (complex*)_calc_stack.back(); // a+ib
// left: (a+ib)->(ax+iay)
CHECK_MPFR(mpfr_mul(left->re()->mpfr, left->re()->mpfr, right->re()->mpfr, floating_t::s_mpfr_rnd));
CHECK_MPFR(mpfr_mul(left->im()->mpfr, left_sav->re()->mpfr, right->im()->mpfr, floating_t::s_mpfr_rnd));
// right: (x+iy)-> (bx-iby)
CHECK_MPFR(mpfr_mul(right->im()->mpfr, left_sav->im()->mpfr, right->im()->mpfr, floating_t::s_mpfr_rnd));
CHECK_MPFR(mpfr_neg(right->im()->mpfr, right->im()->mpfr, floating_t::s_mpfr_rnd));
CHECK_MPFR(mpfr_mul(right->re()->mpfr, left_sav->im()->mpfr, right->re()->mpfr, floating_t::s_mpfr_rnd));
// left=left+transpose(right)
CHECK_MPFR(mpfr_add(left->re()->mpfr, left->re()->mpfr, right->im()->mpfr, floating_t::s_mpfr_rnd));
CHECK_MPFR(mpfr_add(left->im()->mpfr, left->im()->mpfr, right->re()->mpfr, floating_t::s_mpfr_rnd));
_calc_stack.pop_back();
// mutiplying complexes
else if (_stack.type(0) == cmd_complex && _stack.type(1) == cmd_complex) {
_stack.value<ocomplex>(1) *= _stack.value<ocomplex>(0);
_stack.pop();
}
// multiplying complex*number
else if (_stack->get_type(0) == cmd_number && _stack->get_type(1) == cmd_complex) {
number* right = (number*)_stack->pop_back();
complex* left = (complex*)_stack->back();
CHECK_MPFR(mpfr_mul(left->re()->mpfr, left->re()->mpfr, right->_value.mpfr, floating_t::s_mpfr_rnd));
CHECK_MPFR(mpfr_mul(left->im()->mpfr, left->im()->mpfr, right->_value.mpfr, floating_t::s_mpfr_rnd));
// mutiplying complex*number
else if (_stack.type(0) == cmd_number && _stack.type(1) == cmd_complex) {
_stack.value<ocomplex>(1) *= _stack.value<number>(0);
_stack.pop();
}
// multiplying number*complex
else if (_stack->get_type(0) == cmd_complex && _stack->get_type(1) == cmd_number) {
// mutiplying number*complex
else if (_stack.type(0) == cmd_complex && _stack.type(1) == cmd_number) {
rpn_swap();
number* right = (number*)_stack->pop_back();
complex* left = (complex*)_stack->back();
CHECK_MPFR(mpfr_mul(left->re()->mpfr, left->re()->mpfr, right->_value.mpfr, floating_t::s_mpfr_rnd));
CHECK_MPFR(mpfr_mul(left->im()->mpfr, left->im()->mpfr, right->_value.mpfr, floating_t::s_mpfr_rnd));
_stack.value<ocomplex>(1) *= _stack.value<number>(0);
_stack.pop();
} else
ERR_CONTEXT(ret_bad_operand_type);
}
/// @brief divide the 2 complexes on stack
/// result on the prog stack
///
void program::do_divide_complexes() {
//(a+ib)/(x+iy)=(a+ib)(x-iy)/(x^2+y^2)=(ax+by+i(bx-ay))/(x^2+y^2)
complex* right = (complex*)_stack->get_obj(0); // x+iy
complex* left = (complex*)_stack->get_obj(1); // a+ib
// 1. calc (x^2-y^2) in _calc_stack
number* ex = (number*)_calc_stack.allocate_back(number::calc_size(), cmd_number);
CHECK_MPFR(mpfr_mul(ex->_value.mpfr, right->re()->mpfr, right->re()->mpfr,
floating_t::s_mpfr_rnd)); // x2
number* wy = (number*)_calc_stack.allocate_back(number::calc_size(), cmd_number);
CHECK_MPFR(mpfr_mul(wy->_value.mpfr, right->im()->mpfr, right->im()->mpfr,
floating_t::s_mpfr_rnd)); // y2
CHECK_MPFR(mpfr_add(ex->_value.mpfr, ex->_value.mpfr, wy->_value.mpfr,
floating_t::s_mpfr_rnd)); // ex=x2+y2
stack::copy_and_push_back(*_stack, _stack->size() - 2, _calc_stack); // x+iy
stack::copy_and_push_back(*_stack, _stack->size() - 1, _calc_stack);
complex* left_sav = (complex*)_calc_stack.get_obj(1); // a+ib
complex* right_sav = (complex*)_calc_stack.get_obj(0); // x+iy
// 2. left.re=ax+by
CHECK_MPFR(mpfr_mul(left->re()->mpfr, left_sav->re()->mpfr, right_sav->re()->mpfr,
floating_t::s_mpfr_rnd)); // left.re=ax
CHECK_MPFR(mpfr_mul(right->re()->mpfr, left_sav->im()->mpfr, right_sav->im()->mpfr,
floating_t::s_mpfr_rnd)); // right.re=by
CHECK_MPFR(mpfr_add(left->re()->mpfr, left->re()->mpfr, right->re()->mpfr,
floating_t::s_mpfr_rnd)); // left.re=ax+by
// 3. left.im=bx-ay
CHECK_MPFR(mpfr_mul(left->im()->mpfr, left_sav->im()->mpfr, right_sav->re()->mpfr,
floating_t::s_mpfr_rnd)); // left.im=bx
CHECK_MPFR(mpfr_mul(right->im()->mpfr, left_sav->re()->mpfr, right_sav->im()->mpfr,
floating_t::s_mpfr_rnd)); // right.im=ay
CHECK_MPFR(mpfr_sub(left->im()->mpfr, left->im()->mpfr, right->im()->mpfr,
floating_t::s_mpfr_rnd)); // left.im=bx-ay
// 4. left.re/=(x^2-y^2), left.im/=(x^2+y^2)
CHECK_MPFR(mpfr_div(left->re()->mpfr, left->re()->mpfr, ex->_value.mpfr, floating_t::s_mpfr_rnd));
CHECK_MPFR(mpfr_div(left->im()->mpfr, left->im()->mpfr, ex->_value.mpfr, floating_t::s_mpfr_rnd));
_stack->pop_back();
_calc_stack.pop_back(4);
setErrorContext(ret_bad_operand_type);
}
/// @brief / keyword implementation
@ -192,47 +98,27 @@ void program::rpn_div() {
MIN_ARGUMENTS(2);
// dividing numbers
if (_stack->get_type(0) == cmd_number && _stack->get_type(1) == cmd_number) {
number* right = (number*)_stack->pop_back();
number* left = (number*)_stack->back();
CHECK_MPFR(mpfr_div(left->_value.mpfr, left->_value.mpfr, right->_value.mpfr, floating_t::s_mpfr_rnd));
if (_stack.type(0) == cmd_number && _stack.type(1) == cmd_number) {
_stack.value<number>(1) /= _stack.value<number>(0);
_stack.pop();
}
// dividing complexes
else if (_stack->get_type(0) == cmd_complex && _stack->get_type(1) == cmd_complex)
do_divide_complexes();
else if (_stack.type(0) == cmd_complex && _stack.type(1) == cmd_complex) {
_stack.value<ocomplex>(1) /= _stack.value<ocomplex>(0);
_stack.pop();
}
// dividing complex/number
else if (_stack->get_type(0) == cmd_number && _stack->get_type(1) == cmd_complex) {
number* right = (number*)_stack->pop_back();
complex* left = (complex*)_stack->back();
CHECK_MPFR(mpfr_div(left->re()->mpfr, left->re()->mpfr, right->_value.mpfr, floating_t::s_mpfr_rnd));
CHECK_MPFR(mpfr_div(left->im()->mpfr, left->im()->mpfr, right->_value.mpfr, floating_t::s_mpfr_rnd));
else if (_stack.type(0) == cmd_number && _stack.type(1) == cmd_complex) {
_stack.value<ocomplex>(1) /= _stack.value<number>(0);
_stack.pop();
}
// dividing number/complex
else if (_stack->get_type(0) == cmd_complex && _stack->get_type(1) == cmd_number) {
// 1. copy out
stack::copy_and_push_back(*_stack, _stack->size() - 1,
_calc_stack); // complex
stack::copy_and_push_back(*_stack, _stack->size() - 2,
_calc_stack); // number
_stack->pop_back(2);
// 2. copy back (2 complexes on stack)
stack::copy_and_push_back(_calc_stack, _calc_stack.size() - 2,
*_stack); // complex back to stack
stack::copy_and_push_back(_calc_stack, _calc_stack.size() - 2,
*_stack); // complex back to stack
// 3. set complex level 2 to (number,0)
complex* new_cplx = (complex*)_stack->get_obj(1);
CHECK_MPFR(
mpfr_set(new_cplx->re()->mpfr, ((number*)_calc_stack.get_obj(0))->_value.mpfr, floating_t::s_mpfr_rnd));
CHECK_MPFR(mpfr_set_ui(new_cplx->im()->mpfr, 0UL, floating_t::s_mpfr_rnd));
_calc_stack.pop_back(2);
// 4. divide
do_divide_complexes();
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
ERR_CONTEXT(ret_bad_operand_type);
setErrorContext(ret_bad_operand_type);
}
/// @brief neg keyword implementation
@ -240,15 +126,12 @@ void program::rpn_div() {
void program::rpn_neg() {
MIN_ARGUMENTS(1);
if (_stack->get_type(0) == cmd_number) {
number* left = (number*)_stack->back();
CHECK_MPFR(mpfr_neg(left->_value.mpfr, left->_value.mpfr, floating_t::s_mpfr_rnd));
} else if (_stack->get_type(0) == cmd_complex) {
complex* left = (complex*)_stack->back();
CHECK_MPFR(mpfr_neg(left->re()->mpfr, left->re()->mpfr, floating_t::s_mpfr_rnd));
CHECK_MPFR(mpfr_neg(left->im()->mpfr, left->im()->mpfr, floating_t::s_mpfr_rnd));
} else
ERR_CONTEXT(ret_bad_operand_type);
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
@ -256,20 +139,119 @@ void program::rpn_neg() {
void program::rpn_inv() {
MIN_ARGUMENTS(1);
if (_stack->get_type(0) == cmd_number) {
number* left = (number*)_stack->back();
CHECK_MPFR(mpfr_si_div(left->_value.mpfr, 1L, left->_value.mpfr, floating_t::s_mpfr_rnd));
} else if (_stack->get_type(0) == cmd_complex) {
// 1. duplicate
rpn_dup();
// 2. set complex level 2 to (1,0)
complex* cplx = (complex*)_stack->get_obj(1);
CHECK_MPFR(mpfr_set_ui(cplx->re()->mpfr, 1UL, floating_t::s_mpfr_rnd));
CHECK_MPFR(mpfr_set_ui(cplx->im()->mpfr, 0UL, floating_t::s_mpfr_rnd));
// 3. divide
do_divide_complexes();
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
ERR_CONTEXT(ret_bad_operand_type);
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 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 = (int)_stack.value<number>(0).toLong();
_stack.pop();
if (base >= BASE_MIN && base <= BASE_MAX) {
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
@ -278,12 +260,8 @@ void program::rpn_purcent() {
MIN_ARGUMENTS(2);
ARG_MUST_BE_OF_TYPE(0, cmd_number);
ARG_MUST_BE_OF_TYPE(1, cmd_number);
number* right = (number*)_stack->pop_back();
number* left = (number*)_stack->back();
CHECK_MPFR(mpfr_mul(left->_value.mpfr, left->_value.mpfr, right->_value.mpfr, floating_t::s_mpfr_rnd));
CHECK_MPFR(mpfr_div_si(left->_value.mpfr, left->_value.mpfr, 100L, floating_t::s_mpfr_rnd));
_stack.value<number>(1) *= _stack.value<number>(0) / 100;
_stack.pop();
}
/// @brief %CH keyword implementation
@ -292,112 +270,20 @@ void program::rpn_purcentCH() {
MIN_ARGUMENTS(2);
ARG_MUST_BE_OF_TYPE(0, cmd_number);
ARG_MUST_BE_OF_TYPE(1, cmd_number);
number* right = (number*)_stack->pop_back();
number* left = (number*)_stack->back();
CHECK_MPFR(mpfr_mul_si(right->_value.mpfr, right->_value.mpfr, 100L, floating_t::s_mpfr_rnd));
CHECK_MPFR(mpfr_div(left->_value.mpfr, right->_value.mpfr, left->_value.mpfr, floating_t::s_mpfr_rnd));
}
/// @brief power keyword implementation
///
void program::rpn_power() {
MIN_ARGUMENTS(2);
bool done_on_real = false;
if (_stack->get_type(1) == cmd_number) {
ARG_MUST_BE_OF_TYPE(0, cmd_number);
number* right = (number*)_stack->get_obj(0);
number* left = (number*)_stack->get_obj(1);
if (mpfr_cmp_d(left->_value.mpfr, 0.0) >= 0) {
CHECK_MPFR(mpfr_pow(left->_value.mpfr, left->_value.mpfr, right->_value.mpfr, floating_t::s_mpfr_rnd));
_stack->pop_back();
done_on_real = true;
} else {
// copy power out
stack::copy_and_push_back(*_stack, _stack->size() - 1, _calc_stack);
_stack->pop_back();
// negative number -> complex number
_stack->allocate_back(number::calc_size(), cmd_number);
CHECK_MPFR(mpfr_set_d(((number*)_stack->back())->_value.mpfr, 0.0, floating_t::s_mpfr_rnd));
rpn_r2c();
// copy power back
stack::copy_and_push_back(_calc_stack, _calc_stack.size() - 1, *_stack);
_calc_stack.pop_back();
}
}
// carrefull, no 'else' here
if (!done_on_real) {
if (_stack->get_type(1) == cmd_complex) {
ARG_MUST_BE_OF_TYPE(0, cmd_number);
// power on tmp stack
stack::copy_and_push_back(*_stack, _stack->size() - 1, _calc_stack);
_stack->pop_back();
// switch complex to polar
complex* cplx = (complex*)_stack->back();
rpn_r2p();
// new abs=abs^exponent
number* exponent = (number*)_calc_stack.back();
CHECK_MPFR(mpfr_pow(cplx->re()->mpfr, cplx->re()->mpfr, exponent->_value.mpfr, floating_t::s_mpfr_rnd));
// new arg=arg*exponent
CHECK_MPFR(mpfr_mul(cplx->im()->mpfr, cplx->im()->mpfr, exponent->_value.mpfr, floating_t::s_mpfr_rnd));
// back to cartesian
rpn_p2r();
_calc_stack.pop_back();
} else
ERR_CONTEXT(ret_bad_operand_type);
}
}
/// @brief sqrt keyword implementation
///
void program::rpn_squareroot() {
if (_stack->get_type(0) == cmd_number) {
number* left = (number*)_stack->back();
if (mpfr_cmp_d(left->_value.mpfr, 0.0) >= 0)
CHECK_MPFR(mpfr_sqrt(left->_value.mpfr, left->_value.mpfr, floating_t::s_mpfr_rnd));
else {
// negative number -> complex square root
_stack->allocate_back(number::calc_size(), cmd_number);
CHECK_MPFR(mpfr_set_d(((number*)_stack->back())->_value.mpfr, 0.0, floating_t::s_mpfr_rnd));
rpn_r2c();
_stack->allocate_back(number::calc_size(), cmd_number);
CHECK_MPFR(mpfr_set_d(((number*)_stack->back())->_value.mpfr, 0.5, floating_t::s_mpfr_rnd));
rpn_power();
}
} else if (_stack->get_type(0) == cmd_complex) {
// calc cplx^0.5
_stack->allocate_back(number::calc_size(), cmd_number);
CHECK_MPFR(mpfr_set_d(((number*)_stack->back())->_value.mpfr, 0.5, floating_t::s_mpfr_rnd));
rpn_power();
} else
ERR_CONTEXT(ret_bad_operand_type);
_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->get_type(0) == cmd_number) {
number* left = (number*)_stack->back();
CHECK_MPFR(mpfr_sqr(left->_value.mpfr, left->_value.mpfr, floating_t::s_mpfr_rnd));
} else if (_stack->get_type(0) == cmd_complex) {
rpn_dup();
rpn_mul();
} else
ERR_CONTEXT(ret_bad_operand_type);
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
@ -406,83 +292,21 @@ void program::rpn_modulo() {
MIN_ARGUMENTS(2);
ARG_MUST_BE_OF_TYPE(0, cmd_number);
ARG_MUST_BE_OF_TYPE(1, cmd_number);
number* right = (number*)_stack->pop_back();
number* left = (number*)_stack->back();
CHECK_MPFR(mpfr_fmod(left->_value.mpfr, left->_value.mpfr, right->_value.mpfr, floating_t::s_mpfr_rnd));
_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->get_type(0) == cmd_number) {
number* left = (number*)_stack->back();
CHECK_MPFR(mpfr_abs(left->_value.mpfr, left->_value.mpfr, floating_t::s_mpfr_rnd));
} else if (_stack->get_type(0) == cmd_complex) {
// 1. copy out -> calc x2+iy2
stack::copy_and_push_back(*_stack, _stack->size() - 1, _calc_stack);
_stack->pop_back();
// 2. calc x2+iy2
complex* cplx = (complex*)_calc_stack.back();
CHECK_MPFR(mpfr_mul(cplx->re()->mpfr, cplx->re()->mpfr, cplx->re()->mpfr, floating_t::s_mpfr_rnd));
CHECK_MPFR(mpfr_mul(cplx->im()->mpfr, cplx->im()->mpfr, cplx->im()->mpfr, floating_t::s_mpfr_rnd));
// 3. new real on stack
_stack->allocate_back(number::calc_size(), cmd_number);
number* module = (number*)_stack->back();
// 4. set it to |x2+y2| then take sqrt
CHECK_MPFR(mpfr_set(module->_value.mpfr, cplx->re()->mpfr, floating_t::s_mpfr_rnd));
CHECK_MPFR(mpfr_add(module->_value.mpfr, module->_value.mpfr, cplx->im()->mpfr, floating_t::s_mpfr_rnd));
CHECK_MPFR(mpfr_sqrt(module->_value.mpfr, module->_value.mpfr, floating_t::s_mpfr_rnd));
_calc_stack.pop_back();
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
ERR_CONTEXT(ret_bad_operand_type);
}
/// @brief hex keyword implementation
///
void program::rpn_hex() {
MIN_ARGUMENTS(1);
ARG_MUST_BE_OF_TYPE(0, cmd_number);
((number*)_stack->back())->_representation = number::hex;
number::s_decimal_digits = 0;
}
/// @brief bin keyword implementation
///
void program::rpn_bin() {
MIN_ARGUMENTS(1);
ARG_MUST_BE_OF_TYPE(0, cmd_number);
((number*)_stack->back())->_representation = number::bin;
}
/// @brief dec keyword implementation
///
void program::rpn_dec() {
MIN_ARGUMENTS(1);
ARG_MUST_BE_OF_TYPE(0, cmd_number);
((number*)_stack->back())->_representation = number::dec;
}
/// @brief base keyword implementation
///
void program::rpn_base() {
MIN_ARGUMENTS(2);
ARG_MUST_BE_OF_TYPE(0, cmd_number);
ARG_MUST_BE_OF_TYPE(1, cmd_number);
if (mpfr_cmp_d(((number*)_stack->back())->_value.mpfr, (double)BASE_MIN) >= 0 &&
mpfr_cmp_d(((number*)_stack->back())->_value.mpfr, (double)BASE_MAX) <= 0) {
int base = (int)mpfr_get_d(((number*)_stack->pop_back())->_value.mpfr, floating_t::s_mpfr_rnd);
((number*)_stack->get_obj(0))->_base = base;
((number*)_stack->get_obj(0))->_representation = number::base;
} else
ERR_CONTEXT(ret_out_of_range);
setErrorContext(ret_bad_operand_type);
}
/// @brief fact (factorial) keyword implementation
@ -490,33 +314,20 @@ void program::rpn_base() {
void program::rpn_fact() {
MIN_ARGUMENTS(1);
ARG_MUST_BE_OF_TYPE(0, cmd_number);
// fact(n) = gamma(n+1)
number* left = (number*)_stack->back();
number* right = (number*)_stack->allocate_back(number::calc_size(), cmd_number);
right->_value = 1L;
rpn_plus();
CHECK_MPFR(mpfr_gamma(left->_value.mpfr, left->_value.mpfr, floating_t::s_mpfr_rnd));
_stack.value<number>(0) = gamma(_stack.value<number>(0) + 1);
}
/// @brief sign keyword implementation
///
void program::rpn_sign() {
MIN_ARGUMENTS(1);
if (_stack->get_type(0) == cmd_number) {
// fact(n) = gamma(n+1)
number* left = (number*)_stack->back();
int result = mpfr_sgn(left->_value.mpfr);
left->_value = (long)result;
} else if (_stack->get_type(0) == cmd_complex) {
// calc x/sqrt(x*x+y*y) +iy/sqrt(x*x+y*y)
rpn_dup();
rpn_abs();
rpn_div();
} else
ERR_CONTEXT(ret_bad_operand_type);
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
@ -524,31 +335,12 @@ void program::rpn_sign() {
void program::rpn_mant() {
MIN_ARGUMENTS(1);
ARG_MUST_BE_OF_TYPE(0, cmd_number);
number* left = (number*)_stack->back();
if (mpfr_number_p(left->_value.mpfr)) {
if (mpfr_zero_p(left->_value.mpfr))
left->_value = 0.0;
else {
mpfr_abs(left->_value.mpfr, left->_value.mpfr, floating_t::s_mpfr_rnd);
number* one = (number*)_calc_stack.allocate_back(number::calc_size(), cmd_number);
number* ten = (number*)_calc_stack.allocate_back(number::calc_size(), cmd_number);
ten->_value = 10L;
one->_value = 1L;
while (mpfr_greaterequal_p(left->_value.mpfr, one->_value.mpfr))
mpfr_div(left->_value.mpfr, left->_value.mpfr, ten->_value.mpfr, floating_t::s_mpfr_rnd);
one->_value = 0.1;
while (mpfr_less_p(left->_value.mpfr, one->_value.mpfr))
mpfr_mul(left->_value.mpfr, left->_value.mpfr, ten->_value.mpfr, floating_t::s_mpfr_rnd);
_calc_stack.pop_back(2);
}
} else
ERR_CONTEXT(ret_out_of_range);
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
@ -556,39 +348,13 @@ void program::rpn_mant() {
void program::rpn_xpon() {
MIN_ARGUMENTS(1);
ARG_MUST_BE_OF_TYPE(0, cmd_number);
number* left = (number*)_stack->back();
if (mpfr_number_p(left->_value.mpfr)) {
if (mpfr_zero_p(left->_value.mpfr))
left->_value = 0.0;
else {
double exponant = 0.0;
mpfr_abs(left->_value.mpfr, left->_value.mpfr, floating_t::s_mpfr_rnd);
number* one = (number*)_calc_stack.allocate_back(number::calc_size(), cmd_number);
number* ten = (number*)_calc_stack.allocate_back(number::calc_size(), cmd_number);
ten->_value = 10L;
one->_value = 1L;
while (mpfr_greaterequal_p(left->_value.mpfr, one->_value.mpfr)) {
mpfr_div(left->_value.mpfr, left->_value.mpfr, ten->_value.mpfr, floating_t::s_mpfr_rnd);
exponant += 1.0;
}
one->_value = 0.1;
while (mpfr_less_p(left->_value.mpfr, one->_value.mpfr)) {
mpfr_mul(left->_value.mpfr, left->_value.mpfr, ten->_value.mpfr, floating_t::s_mpfr_rnd);
exponant -= 1.0;
}
left->_value = exponant;
_calc_stack.pop_back();
_calc_stack.pop_back();
}
} else
ERR_CONTEXT(ret_out_of_range);
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
@ -596,10 +362,7 @@ void program::rpn_xpon() {
void program::rpn_floor() {
MIN_ARGUMENTS(1);
ARG_MUST_BE_OF_TYPE(0, cmd_number);
number* left = (number*)_stack->back();
CHECK_MPFR(mpfr_floor(left->_value.mpfr, left->_value.mpfr));
_stack.value<number>(0) = floor(_stack.value<number>(0));
}
/// @brief ceil keyword implementation
@ -607,10 +370,7 @@ void program::rpn_floor() {
void program::rpn_ceil() {
MIN_ARGUMENTS(1);
ARG_MUST_BE_OF_TYPE(0, cmd_number);
number* left = (number*)_stack->back();
CHECK_MPFR(mpfr_ceil(left->_value.mpfr, left->_value.mpfr));
_stack.value<number>(0) = ceil(_stack.value<number>(0));
}
/// @brief fp keyword implementation
@ -618,10 +378,7 @@ void program::rpn_ceil() {
void program::rpn_fp() {
MIN_ARGUMENTS(1);
ARG_MUST_BE_OF_TYPE(0, cmd_number);
number* left = (number*)_stack->back();
CHECK_MPFR(mpfr_frac(left->_value.mpfr, left->_value.mpfr, floating_t::s_mpfr_rnd));
_stack.value<number>(0) = frac(_stack.value<number>(0));
}
/// @brief ip keyword implementation
@ -629,10 +386,7 @@ void program::rpn_fp() {
void program::rpn_ip() {
MIN_ARGUMENTS(1);
ARG_MUST_BE_OF_TYPE(0, cmd_number);
number* left = (number*)_stack->back();
CHECK_MPFR(mpfr_trunc(left->_value.mpfr, left->_value.mpfr));
_stack.value<number>(0) = trunc(_stack.value<number>(0));
}
/// @brief min keyword implementation
@ -641,11 +395,8 @@ void program::rpn_min() {
MIN_ARGUMENTS(2);
ARG_MUST_BE_OF_TYPE(0, cmd_number);
ARG_MUST_BE_OF_TYPE(1, cmd_number);
number* right = (number*)_stack->pop_back();
number* left = (number*)_stack->back();
CHECK_MPFR(mpfr_min(left->_value.mpfr, left->_value.mpfr, right->_value.mpfr, floating_t::s_mpfr_rnd));
_stack.value<number>(0) = min(_stack.value<number>(0), _stack.value<number>(1));
_stack.erase(1);
}
/// @brief max keyword implementation
@ -654,9 +405,6 @@ void program::rpn_max() {
MIN_ARGUMENTS(2);
ARG_MUST_BE_OF_TYPE(0, cmd_number);
ARG_MUST_BE_OF_TYPE(1, cmd_number);
number* right = (number*)_stack->pop_back();
number* left = (number*)_stack->back();
CHECK_MPFR(mpfr_max(left->_value.mpfr, left->_value.mpfr, right->_value.mpfr, floating_t::s_mpfr_rnd));
_stack.value<number>(0) = max(_stack.value<number>(0), _stack.value<number>(1));
_stack.erase(1);
}

View file

@ -4,26 +4,23 @@
///
void program::rpn_swap(void) {
MIN_ARGUMENTS(2);
stack::copy_and_push_back(*_stack, _stack->size() - 1, _calc_stack);
stack::copy_and_push_back(*_stack, _stack->size() - 2, _calc_stack);
(void)_stack->pop_back(2);
stack::copy_and_push_back(_calc_stack, _calc_stack.size() - 2, *_stack);
stack::copy_and_push_back(_calc_stack, _calc_stack.size() - 1, *_stack);
_calc_stack.pop_back(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);
(void)_stack->pop_back();
_stack.pop();
}
/// @brief drop2 keyword implementation
///
void program::rpn_drop2(void) {
MIN_ARGUMENTS(2);
(void)_stack->pop_back(2);
_stack.pop_front(2);
}
/// @brief dropn keyword implementation
@ -32,29 +29,20 @@ void program::rpn_dropn(void) {
MIN_ARGUMENTS(1);
ARG_MUST_BE_OF_TYPE(0, cmd_number);
int args = (int)mpfr_get_si(((number*)_stack->back())->_value.mpfr, floating_t::s_mpfr_rnd);
int args = (int)_stack.value<number>(0).toLong();
MIN_ARGUMENTS(args + 1);
(void)_stack->pop_back(args + 1);
_stack.erase(0, args + 1);
}
/// @brief erase / del keyword implementation
///
void program::rpn_erase(void) { (void)_stack->pop_back(_stack->size()); }
void program::rpn_erase(void) { _stack.erase(0, _stack.size()); }
/// @brief dup keyword implementation
///
void program::rpn_dup(void) {
MIN_ARGUMENTS(1);
stack::copy_and_push_back(*_stack, _stack->size() - 1, *_stack);
}
/// @brief dup2 keyword implementation
///
void program::rpn_dup2(void) {
MIN_ARGUMENTS(2);
stack::copy_and_push_back(*_stack, _stack->size() - 2, *_stack);
stack::copy_and_push_back(*_stack, _stack->size() - 2, *_stack);
rpnstack::copy_and_push_front(_stack, 0, _stack);
}
/// @brief dupn keyword implementation
@ -63,11 +51,19 @@ void program::rpn_dupn(void) {
MIN_ARGUMENTS(1);
ARG_MUST_BE_OF_TYPE(0, cmd_number);
int args = (int)mpfr_get_si(((number*)_stack->back())->_value.mpfr, floating_t::s_mpfr_rnd);
MIN_ARGUMENTS(args + 1);
_stack->pop_back();
int args = (int)((number*)_stack.front())->value.toLong();
_stack.pop();
for (int i = 0; i < args; i++) stack::copy_and_push_back(*_stack, _stack->size() - args, *_stack);
MIN_ARGUMENTS(args);
for (int i = 0; i < args; i++) rpnstack::copy_and_push_front(_stack, args - 1, _stack);
}
/// @brief dup2 keyword implementation
///
void program::rpn_dup2(void) {
MIN_ARGUMENTS(2);
rpnstack::copy_and_push_front(_stack, 1, _stack);
rpnstack::copy_and_push_front(_stack, 1, _stack);
}
/// @brief pick keyword implementation
@ -76,39 +72,30 @@ void program::rpn_pick(void) {
MIN_ARGUMENTS(1);
ARG_MUST_BE_OF_TYPE(0, cmd_number);
unsigned int to_pick = (unsigned int)int(((number*)_stack->pop_back())->_value);
unsigned int to_pick = (unsigned int)_stack.value<number>(0);
_stack.pop();
// treat stack depth errors
if ((to_pick == 0) || (to_pick > _stack->size())) {
ERR_CONTEXT(ret_out_of_range);
if ((to_pick == 0) || (to_pick > _stack.size())) {
setErrorContext(ret_out_of_range);
return;
}
stack::copy_and_push_back(*_stack, _stack->size() - to_pick, *_stack);
rpnstack::copy_and_push_front(_stack, to_pick - 1, _stack);
}
/// @brief rot keyword implementation
///
void program::rpn_rot(void) {
MIN_ARGUMENTS(3);
stack::copy_and_push_back(*_stack, _stack->size() - 3, _calc_stack);
stack::copy_and_push_back(*_stack, _stack->size() - 2, _calc_stack);
stack::copy_and_push_back(*_stack, _stack->size() - 1, _calc_stack);
(void)_stack->pop_back(3);
stack::copy_and_push_back(_calc_stack, _calc_stack.size() - 2, *_stack);
stack::copy_and_push_back(_calc_stack, _calc_stack.size() - 1, *_stack);
stack::copy_and_push_back(_calc_stack, _calc_stack.size() - 3, *_stack);
_calc_stack.pop_back(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) {
unsigned long depth = (unsigned long)_stack->size();
number* num = (number*)_stack->allocate_back(number::calc_size(), cmd_number);
num->set(depth);
}
void program::rpn_depth(void) { _stack.push_front(new number(_stack.size())); }
/// @brief roll keyword implementation
///
@ -116,19 +103,13 @@ void program::rpn_roll(void) {
MIN_ARGUMENTS(1);
ARG_MUST_BE_OF_TYPE(0, cmd_number);
int args = (int)mpfr_get_si(((number*)_stack->back())->_value.mpfr, floating_t::s_mpfr_rnd);
MIN_ARGUMENTS(args + 1);
_stack->pop_back();
size_t args = (int)((number*)_stack.front())->value;
_stack.pop();
MIN_ARGUMENTS(args);
for (int i = 0; i < args; i++) {
stack::copy_and_push_back(*_stack, _stack->size() - 1, _calc_stack);
(void)_stack->pop_back();
}
for (int i = 1; i < args; i++) stack::copy_and_push_back(_calc_stack, args - 1 - i, *_stack);
stack::copy_and_push_back(_calc_stack, args - 1, *_stack);
_calc_stack.pop_back(args);
object* tmp = _stack.at(args - 1);
_stack.erase(args - 1, 1, false);
_stack.insert(_stack.begin(), tmp);
}
/// @brief rolld keyword implementation
@ -137,20 +118,13 @@ void program::rpn_rolld(void) {
MIN_ARGUMENTS(2);
ARG_MUST_BE_OF_TYPE(0, cmd_number);
int args = (int)mpfr_get_si(((number*)_stack->back())->_value.mpfr, floating_t::s_mpfr_rnd);
MIN_ARGUMENTS(args + 1);
_stack->pop_back();
int args = (int)((number*)_stack.front())->value.toLong();
_stack.pop();
MIN_ARGUMENTS(args);
for (int i = 0; i < args; i++) {
stack::copy_and_push_back(*_stack, _stack->size() - 1, _calc_stack);
(void)_stack->pop_back();
}
stack::copy_and_push_back(_calc_stack, _calc_stack.size() - args, *_stack);
for (int i = 1; i < args; i++) stack::copy_and_push_back(_calc_stack, _calc_stack.size() - i, *_stack);
_calc_stack.pop_back(args);
object* tmp = _stack.at(0);
_stack.erase(0, 1, false);
_stack.insert(_stack.begin() + args - 1, tmp);
}
/// @brief over keyword implementation
@ -158,5 +132,6 @@ void program::rpn_rolld(void) {
void program::rpn_over(void) {
MIN_ARGUMENTS(2);
stack::copy_and_push_back(*_stack, _stack->size() - 2, *_stack);
rpnstack::copy_and_push_front(_stack, 1, _stack);
}

View file

@ -1,4 +1,5 @@
#include "program.hpp"
#include "input.hpp"
/// @brief sto keyword implementation
///
@ -6,173 +7,114 @@ void program::rpn_sto(void) {
MIN_ARGUMENTS(2);
ARG_MUST_BE_OF_TYPE(0, cmd_symbol);
string name(((symbol*)_stack->pop_back())->_value);
_heap->add(name, _stack->get_obj(0), _stack->get_len(0));
(void)_stack->pop_back();
// 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.pop_front(2);
}
/// @brief sto+ keyword implementation
///
void program::rpn_stoadd(void) {
MIN_ARGUMENTS(2);
if (_stack->get_type(0) == cmd_symbol && _stack->get_type(1) == cmd_number) {
// get variable value on stack level 1, make op then modify variable
string variable(((symbol*)_stack->back())->_value);
rpn_rcl();
if (_err == ret_ok) {
rpn_plus();
_heap->add(variable, _stack->get_obj(0), _stack->get_len(0));
_stack->pop_back();
}
} else if (_stack->get_type(1) == cmd_symbol && _stack->get_type(0) == cmd_number) {
// copy value, get variable value on stack level 1,
// put back value on stack level 1, make op then modify variable
stack::copy_and_push_back(*_stack, _stack->size() - 1, _calc_stack);
_stack->pop_back();
string variable(((symbol*)_stack->back())->_value);
rpn_rcl();
if (_err == ret_ok) {
stack::copy_and_push_back(_calc_stack, _calc_stack.size() - 1, *_stack);
rpn_plus();
_heap->add(variable, _stack->get_obj(0), _stack->get_len(0));
_stack->pop_back();
}
} else
ERR_CONTEXT(ret_bad_operand_type);
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 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);
if (_stack->get_type(0) == cmd_symbol && _stack->get_type(1) == cmd_number) {
// get variable value on stack level 1, make op then modify variable
string variable(((symbol*)_stack->back())->_value);
rpn_rcl();
if (_err == ret_ok) {
rpn_minus();
_heap->add(variable, _stack->get_obj(0), _stack->get_len(0));
_stack->pop_back();
}
} else if (_stack->get_type(1) == cmd_symbol && _stack->get_type(0) == cmd_number) {
// copy value, get variable value on stack level 1,
// put back value on stack level 1, make op then modify variable
stack::copy_and_push_back(*_stack, _stack->size() - 1, _calc_stack);
_stack->pop_back();
string variable(((symbol*)_stack->back())->_value);
rpn_rcl();
if (_err == ret_ok) {
stack::copy_and_push_back(_calc_stack, _calc_stack.size() - 1, *_stack);
rpn_minus();
_heap->add(variable, _stack->get_obj(0), _stack->get_len(0));
_stack->pop_back();
}
} else
ERR_CONTEXT(ret_bad_operand_type);
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);
if (_stack->get_type(0) == cmd_symbol && _stack->get_type(1) == cmd_number) {
// get variable value on stack level 1, make op then modify variable
string variable(((symbol*)_stack->back())->_value);
rpn_rcl();
if (_err == ret_ok) {
rpn_mul();
_heap->add(variable, _stack->get_obj(0), _stack->get_len(0));
_stack->pop_back();
}
} else if (_stack->get_type(1) == cmd_symbol && _stack->get_type(0) == cmd_number) {
// copy value, get variable value on stack level 1,
// put back value on stack level 1, make op then modify variable
stack::copy_and_push_back(*_stack, _stack->size() - 1, _calc_stack);
_stack->pop_back();
string variable(((symbol*)_stack->back())->_value);
rpn_rcl();
if (_err == ret_ok) {
stack::copy_and_push_back(_calc_stack, _calc_stack.size() - 1, *_stack);
rpn_mul();
_heap->add(variable, _stack->get_obj(0), _stack->get_len(0));
_stack->pop_back();
}
} else
ERR_CONTEXT(ret_bad_operand_type);
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);
if (_stack->get_type(0) == cmd_symbol && _stack->get_type(1) == cmd_number) {
// get variable value on stack level 1, make op then modify variable
string variable(((symbol*)_stack->back())->_value);
rpn_rcl();
if (_err == ret_ok) {
rpn_div();
_heap->add(variable, _stack->get_obj(0), _stack->get_len(0));
_stack->pop_back();
}
} else if (_stack->get_type(1) == cmd_symbol && _stack->get_type(0) == cmd_number) {
// copy value, get variable value on stack level 1,
// put back value on stack level 1, make op then modify variable
stack::copy_and_push_back(*_stack, _stack->size() - 1, _calc_stack);
_stack->pop_back();
string variable(((symbol*)_stack->back())->_value);
rpn_rcl();
if (_err == ret_ok) {
stack::copy_and_push_back(_calc_stack, _calc_stack.size() - 1, *_stack);
rpn_div();
_heap->add(variable, _stack->get_obj(0), _stack->get_len(0));
_stack->pop_back();
}
} else
ERR_CONTEXT(ret_bad_operand_type);
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);
if (_stack->get_type(0) == cmd_symbol) {
// get variable value on stack level 1, make op then modify variable
string variable(((symbol*)_stack->back())->_value);
rpn_rcl();
if (_err == ret_ok) {
rpn_neg();
_heap->add(variable, _stack->get_obj(0), _stack->get_len(0));
_stack->pop_back();
}
} else
ERR_CONTEXT(ret_bad_operand_type);
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);
if (_stack->get_type(0) == cmd_symbol) {
// get variable value on stack level 1, make op then modify variable
string variable(((symbol*)_stack->back())->_value);
rpn_rcl();
if (_err == ret_ok) {
rpn_inv();
_heap->add(variable, _stack->get_obj(0), _stack->get_len(0));
_stack->pop_back();
}
} else
ERR_CONTEXT(ret_bad_operand_type);
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
@ -183,15 +125,14 @@ void program::rpn_rcl(void) {
// recall a variable
object* obj;
unsigned int size;
string variable(((symbol*)_stack->back())->_value);
string variable(_stack.value<symbol>(0));
// mind the order of heaps
if (find_variable(variable, obj, size)) {
(void)_stack->pop_back();
stack::copy_and_push_back(obj, *_stack, size);
if (find_variable(variable, obj)) {
(void)_stack.pop_front();
_stack.push_front(obj->clone());
} else
ERR_CONTEXT(ret_unknown_variable);
setErrorContext(ret_unknown_variable);
}
/// @brief edit keyword implementation
@ -199,47 +140,33 @@ void program::rpn_rcl(void) {
void program::rpn_edit(void) {
MIN_ARGUMENTS(1);
FILE* tmp = tmpfile();
if (tmp != NULL) {
// re-write stack objet in a stream
((object*)_stack->pop_back())->show(tmp);
ostringstream st;
// edit: stuff chars using linenoise facility
int len = (int)ftell(tmp);
rewind(tmp);
// re-write stack objet in a stream
_stack.at(0)->show(st);
_stack.pop();
// get stream data
void* file_data = mmap(NULL, len, PROT_READ, MAP_PRIVATE, fileno(tmp), 0);
if (file_data != MAP_FAILED) {
// set it as the linenoise line entry
linenoisePreloadBuffer((const char*)file_data);
munmap(file_data, len);
} else
ERR_CONTEXT(ret_runtime_error);
fclose(tmp);
} else
ERR_CONTEXT(ret_runtime_error);
// 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->_auto_eval) {
if (symb->autoEval) {
object* obj;
unsigned int size;
string variable(symb->_value);
string variable(symb->value);
// mind the order of heaps
if (find_variable(variable, obj, size)) {
stack::copy_and_push_back(obj, *_stack, size);
if (find_variable(variable, obj)) {
_stack.push_front(obj->clone());
if (obj->_type == cmd_program) rpn_eval();
} else
stack::copy_and_push_back(symb, *_stack, symb->size());
_stack.push_front(symb->clone());
} else
stack::copy_and_push_back(symb, *_stack, symb->size());
_stack.push_front(symb->clone());
}
/// @brief purge keyword implementation
@ -248,47 +175,50 @@ void program::rpn_purge(void) {
MIN_ARGUMENTS(1);
ARG_MUST_BE_OF_TYPE(0, cmd_symbol);
string name(((symbol*)_stack->pop_back())->_value);
if (!_heap->erase(name)) ERR_CONTEXT(ret_unknown_variable);
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;
unsigned int size;
program* parent = _parent_prog;
program* parent = _parent;
string name;
// heap variables
for (int i = 0; i < (int)_heap->count_vars(); i++) {
(void)_heap->get_by_index(i, name, obj, size);
printf("var %d: name '%s', type %s, value ", i + 1, name.c_str(), object::s_cmd_type_string[obj->_type]);
obj->show();
printf("\n");
for (int i = 0; i < (int)_heap.size(); i++) {
(void)_heap.get_by_index(i, name, obj);
cout<<"var "<<i+1<<": name '"<<name<<"', type "<<obj->name()<<", value ";
obj->show(cout);
cout<<endl;
}
// parents local variables
while (parent != NULL) {
while (parent != nullptr) {
for (int i = 0; i < (int)parent->_local_heap.size(); i++) {
(void)parent->_local_heap.get_by_index(i, name, obj, size);
printf("local var %d: name '%s', type %s, value ", i + 1, name.c_str(),
object::s_cmd_type_string[obj->_type]);
obj->show();
printf("\n");
(void)parent->_local_heap.get_by_index(i, name, obj);
cout<<"var "<<i+1<<": name '"<<name<<"', type "<<obj->name()<<", value ";
obj->show(cout);
cout<<endl;
}
parent = parent->_parent_prog;
parent = parent->_parent;
}
// local variables
for (int i = 0; i < (int)_local_heap.size(); i++) {
(void)_local_heap.get_by_index(i, name, obj, size);
printf("local var %d: name '%s', type %s, value ", i + 1, name.c_str(), object::s_cmd_type_string[obj->_type]);
obj->show();
printf("\n");
(void)_local_heap.get_by_index(i, name, obj);
cout<<"var "<<i+1<<": name '"<<name<<"', type "<<obj->name()<<", value ";
obj->show(cout);
cout<<endl;
}
}
/// @brief clusr keyword implementation
///
void program::rpn_clusr(void) { _heap->erase_all(); }
void program::rpn_clusr(void) { _heap.clear(); }

View file

@ -1,3 +1,5 @@
#include <fcntl.h>
#include "program.hpp"
/// @brief ->str keyword implementation
@ -6,24 +8,11 @@ void program::rpn_instr() {
MIN_ARGUMENTS(1);
// stringify only if not already a string
if (_stack->get_type(0) != cmd_string) {
// write the object in stack(0) in a string and remove this obj
FILE* tmp = tmpfile();
if (tmp != NULL) {
((object*)_stack->pop_back())->show(tmp);
// reserve the correct size on stack
unsigned int str_size = (unsigned int)ftell(tmp);
ostring* str = (ostring*)_stack->allocate_back(str_size + 1 + sizeof(ostring), cmd_string);
str->_len = str_size;
// fill the obj
rewind(tmp);
if (fread(str->_value, str_size, 1, tmp) != 1) ERR_CONTEXT(ret_runtime_error);
str->_value[str_size] = 0;
fclose(tmp);
} else
ERR_CONTEXT(ret_runtime_error);
if (_stack.type(0) != cmd_string) {
stringstream ss;
ss << _stack.at(0);
_stack.pop();
_stack.push(new ostring(ss.str()));
}
}
@ -33,14 +22,14 @@ void program::rpn_strout() {
MIN_ARGUMENTS(1);
ARG_MUST_BE_OF_TYPE(0, cmd_string);
string entry(((ostring*)_stack->pop_back())->_value);
program prog;
string entry(_stack.value<ostring>(0));
program prog(_stack, _heap);
_stack.pop();
// make program from string in stack level 1
if (program::parse(entry.c_str(), prog) == ret_ok)
if (prog.parse(entry) == ret_ok)
// run it
prog.run(*_stack, *_heap);
prog.run();
}
/// @brief chr keyword implementation
@ -48,17 +37,10 @@ void program::rpn_strout() {
void program::rpn_chr() {
MIN_ARGUMENTS(1);
ARG_MUST_BE_OF_TYPE(0, cmd_number);
// get arg as number % 256
char the_chr = (char)mpfr_get_d(((number*)_stack->pop_back())->_value.mpfr, floating_t::s_mpfr_rnd);
char the_chr = (char)(int)_stack.value<number>(0);
_stack.pop();
if (the_chr < 32 || the_chr > 126) the_chr = '.';
// reserve the correct size on stack (1 char)
unsigned int str_size = 1;
ostring* str = (ostring*)_stack->allocate_back(str_size + 1 + sizeof(ostring), cmd_string);
str->_len = str_size;
str->_value[0] = the_chr;
str->_value[1] = 0;
_stack.push_front(new ostring(string(1, the_chr)));
}
/// @brief num keyword implementation
@ -66,10 +48,11 @@ void program::rpn_chr() {
void program::rpn_num() {
MIN_ARGUMENTS(1);
ARG_MUST_BE_OF_TYPE(0, cmd_string);
double the_chr = (double)((ostring*)_stack->pop_back())->_value[0];
number* numb = (number*)_stack->allocate_back(number::calc_size(), cmd_number);
numb->_value = the_chr;
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
@ -77,10 +60,8 @@ void program::rpn_num() {
void program::rpn_strsize() {
MIN_ARGUMENTS(1);
ARG_MUST_BE_OF_TYPE(0, cmd_string);
double len = ((ostring*)_stack->pop_back())->_len;
number* numb = (number*)_stack->allocate_back(number::calc_size(), cmd_number);
numb->_value = len;
_stack.push_front(new number(_stack.value<ostring>(0).size()));
_stack.erase(1);
}
/// @brief pos keyword implementation
@ -90,15 +71,9 @@ void program::rpn_strpos() {
ARG_MUST_BE_OF_TYPE(0, cmd_string);
ARG_MUST_BE_OF_TYPE(1, cmd_string);
long pos = 0;
char* src = ((ostring*)_stack->get_obj(1))->_value;
char* found = strstr(src, ((ostring*)_stack->get_obj(0))->_value);
if (found != NULL) pos = (long)(found - src) + 1L;
_stack->pop_back(2);
number* numb = (number*)_stack->allocate_back(number::calc_size(), cmd_number);
numb->_value = pos;
size_t pos = _stack.value<ostring>(1).find(_stack.value<ostring>(0)) + 1;
_stack.pop_front(2);
_stack.push_front(new number(pos));
}
/// @brief sub keyword implementation
@ -109,38 +84,11 @@ void program::rpn_strsub() {
ARG_MUST_BE_OF_TYPE(1, cmd_number);
ARG_MUST_BE_OF_TYPE(2, cmd_string);
long first = long(((number*)_stack->get_obj(1))->_value) - 1;
long last = long(((number*)_stack->get_obj(0))->_value) - 1;
long len = ((ostring*)_stack->get_obj(2))->_len;
bool result_is_void = false;
size_t first = (size_t)_stack.value<number>(1);
size_t len = (size_t)_stack.value<number>(0) - first + 1;
first--;
_stack->pop_back(2);
if (first < 0) first = 0;
if (last < 0) last = 0;
if (first > len && last > len)
result_is_void = true;
else {
if (first > len) first = len - 1;
if (last > len) last = len - 1;
if (first > last) result_is_void = true;
}
if (!result_is_void) {
unsigned int str_size = last - first + 1;
ostring* str = (ostring*)_calc_stack.allocate_back(str_size + 1 + sizeof(ostring), cmd_string);
str->_len = str_size;
memcpy(((ostring*)_calc_stack.back())->_value, ((ostring*)_stack->get_obj(0))->_value + first, str_size);
((ostring*)_calc_stack.back())->_value[str_size] = 0;
_stack->pop_back();
stack::copy_and_push_back(_calc_stack, _calc_stack.size() - 1, *_stack);
_calc_stack.pop_back();
} else {
_stack->pop_back();
ostring* str = (ostring*)_stack->allocate_back(1 + sizeof(ostring), cmd_string);
str->_len = 0;
str->_value[0] = 0;
}
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,35 +1,32 @@
#include <regex>
using namespace std;
#include "version.h"
#include "escape.h"
#include "program.hpp"
/// @brief write stack in a string, each entry separated between commas
///
///
/// @param stack_is the output string
/// @param stk the stack
///
void program::test_get_stack(string& stack_is, stack& stk) {
for (int i = 0; i < (int)stk.size(); i++) {
FILE* tmp_file = tmpfile();
char* line = NULL;
size_t len;
if (i > 0) stack_is += ", ";
if (tmp_file != NULL) {
((object*)stk.seq_obj(i))->show(tmp_file);
// write stack in a tmp file
(void)rewind(tmp_file);
if (getline(&line, &len, tmp_file) >= 0) {
stack_is += line;
free(line);
}
(void)fclose(tmp_file);
} else
ERR_CONTEXT(ret_runtime_error);
void program::test_get_stack(string& stack_is, rpnstack& stk) {
ostringstream st;
if (stk.empty()) {
stack_is.clear();
return;
}
stk[stk.size() - 1]->show(st);
stack_is += st.str();
for (int i = (int)stk.size() - 2; i >= 0; i--) {
ostringstream st;
stk[i]->show(st);
stack_is += ", " + st.str();
}
}
/// @brief show the tests results
///
///
/// @param title test title
/// @param tests tests nb
/// @param tests_failed failed tests nb
@ -37,16 +34,19 @@ void program::test_get_stack(string& stack_is, stack& stk) {
/// @param steps_failed failed steps nb
///
void program::test_show_result(string title, int tests, int tests_failed, int steps, int steps_failed) {
printf("%s: run %d tests: %d passed, ", title.c_str(), tests, tests - tests_failed);
if (tests_failed > 0) printf(FG_RED);
printf("%d failed", tests_failed);
if (tests_failed > 0) printf(COLOR_OFF);
//cout << title << ": run " << tests << " tests: " << tests - tests_failed << " passed, ";
if (!title.empty())
cout << title << ": ";
cout <<"run " << tests << " tests: " << tests - tests_failed << " passed, ";
if (tests_failed > 0) cout << FG_RED;
cout << tests_failed << " failed";
if (tests_failed > 0) cout << COLOR_OFF;
printf(" (%d steps: %d passed, ", steps, steps - steps_failed);
if (steps_failed > 0) printf(FG_RED);
printf("%d failed", steps_failed);
if (steps_failed > 0) printf(COLOR_OFF);
printf(")\n");
cout << " (" << steps << " steps: " << steps - steps_failed << " passed, ";
if (steps_failed > 0) cout << FG_RED;
cout << steps_failed << " failed";
if (steps_failed > 0) cout << COLOR_OFF;
cout << ")" << endl;
}
/// @brief test keyword implementation
@ -60,14 +60,15 @@ void program::rpn_test() {
int total_steps = 0;
int total_steps_failed = 0;
string test_filename = ((ostring*)_stack->pop_back())->_value;
printf("\nrpn version is %s\n", version);
string test_filename = _stack.value<ostring>(0);
_stack.pop();
cout << endl << "rpn version is " << RPN_VERSION << endl;
test(test_filename, total_tests, total_tests_failed, total_steps, total_steps_failed);
test_show_result("Total", total_tests, total_tests_failed, total_steps, total_steps_failed);
test_show_result("\nTotal", total_tests, total_tests_failed, total_steps, total_steps_failed);
}
/// @brief load a test file and run its tests
///
///
/// @param test_filename the test file filename
/// @param total_tests the total tests nb
/// @param total_tests_failed the total failed tests nb
@ -91,7 +92,7 @@ void program::test(string test_filename, int& total_tests, int& total_tests_fail
string test_title;
string entry;
ret_value ret;
stack stk;
rpnstack stk;
heap hp;
bool failed = false;
bool is_first_step;
@ -105,21 +106,22 @@ void program::test(string test_filename, int& total_tests, int& total_tests_fail
while (!test_file.eof()) {
getline(test_file, entry);
if (entry.empty()) continue;
if (entry.substr(0, 8) == "#include")
if (entry.substr(0, 8) == "@include")
test(entry.substr(9), total_tests, total_tests_failed, total_steps, total_steps_failed);
else if (entry.substr(0, 2) == "##")
printf("\n%s: %s\n", test_filename.c_str(), entry.substr(3).c_str());
else if (entry.substr(0, 2) == "# ") {
else if (entry.substr(0, 2) == "# ")
cout << endl << test_filename << ": " << entry.substr(2) << endl;
else if (entry.substr(0, 3) == "## ") {
// indicates the status of previous test
if (failed == false && tests > 0) printf(FG_GREEN " PASSED" COLOR_OFF "\n");
if (failed == false && tests > 0) cout << FG_GREEN << " PASSED" << COLOR_OFF << endl;
failed = false;
// read a test title
test_title = entry;
is_first_step = true;
is_test_error_shown = false;
printf("%s", test_title.c_str());
cout << test_title;
}
// treat "-> stack size should be "
else if (entry.find(stack_size, 0) == 0) {
@ -136,15 +138,15 @@ void program::test(string test_filename, int& total_tests, int& total_tests_fail
if (size != (int)stk.size()) {
// count fail test and step
if (!is_test_error_shown) {
printf(FG_RED " FAIL" COLOR_OFF "\n");
cout << FG_RED << " FAIL" << COLOR_OFF << endl;
tests_failed++;
is_test_error_shown = true;
}
steps_failed++;
// show failure
printf("\t%s\n", entry.c_str());
printf("\tbut real stack size is " FG_RED "%d" COLOR_OFF "\n", stk.size());
cout << '\t' << entry << endl;
cout << "\t but real stack size is " << FG_RED << stk.size() << COLOR_OFF << endl;
failed = true;
}
is_first_step = false;
@ -164,15 +166,15 @@ void program::test(string test_filename, int& total_tests, int& total_tests_fail
if (stack_is != stack_should_be) {
// count fail test and step
if (!is_test_error_shown) {
printf(FG_RED " FAIL" COLOR_OFF "\n");
cout << FG_RED << " FAIL" << COLOR_OFF << endl;
tests_failed++;
is_test_error_shown = true;
}
steps_failed++;
// show failure
printf("\t%s\n", entry.c_str());
printf("\tbut real stack is " FG_RED "%s" COLOR_OFF "\n", stack_is.c_str());
cout << '\t' << entry << endl;
cout << "\t but real stack is " << FG_RED << stack_is << COLOR_OFF << endl;
failed = true;
}
is_first_step = false;
@ -191,43 +193,64 @@ void program::test(string test_filename, int& total_tests, int& total_tests_fail
if (err_should_be != last_err) {
// count fail test and step
if (!is_test_error_shown) {
printf(FG_RED " FAIL" COLOR_OFF "\n");
cout << FG_RED << " FAIL" << COLOR_OFF << endl;
tests_failed++;
is_test_error_shown = true;
}
steps_failed++;
// show failure
printf("\t%s\n", entry.c_str());
printf("\tbut last error is " FG_RED "%d" COLOR_OFF "\n", last_err);
cout << '\t' << entry << endl;
cout << "\t but last error is " << FG_RED << last_err << COLOR_OFF << endl;
failed = true;
}
is_first_step = false;
} else if (entry.find(cmd_exit, 0) == 0) {
// forced test end
break;
} else if (entry.size() > 0) {
}
// treat unknown "->"
else if (entry.find("->", 0) == 0) {
// count test
if (is_first_step) tests++;
steps++;
// count fail test and step
if (!is_test_error_shown) {
cout << FG_RED << " FAIL" << COLOR_OFF << endl;
tests_failed++;
is_test_error_shown = true;
}
steps_failed++;
// show failure
cout << FG_RED << "\tthis test '" << entry << "' is unknown" << COLOR_OFF << endl;
failed = true;
} else {
// parse entry and run line
program prog;
ret = program::parse(entry.c_str(), prog);
if (ret == ret_ok) {
// run it
(void)prog.run(stk, hp);
last_err = (int)prog.get_err();
entry = regex_replace(entry, regex("`"), "");
if (!entry.empty()) {
program prog(stk, hp);
ret = prog.parse(entry);
if (ret == ret_ok) {
// run it
(void)prog.run();
last_err = (int)prog.get_err();
}
}
}
}
// last test
// indicates the status of previous test
if (failed == false && tests > 0) printf(FG_GREEN " PASSED" COLOR_OFF "\n");
if (failed == false && tests > 0) cout << FG_GREEN << " PASSED" << COLOR_OFF << endl;
// cerr back
cerr.rdbuf(cerr_old_buffer);
// conclusion: show and keep for total
if (tests != 0) {
test_show_result(test_filename, tests, tests_failed, steps, steps_failed);
test_show_result("", tests, tests_failed, steps, steps_failed);
total_tests += tests;
total_tests_failed += tests_failed;
@ -235,5 +258,5 @@ void program::test(string test_filename, int& total_tests, int& total_tests_fail
total_steps_failed += steps_failed;
}
} else
fprintf(stderr, "test file '%s' not found\n", test_filename.c_str());
cerr << "test file '" << test_filename << "' not found" << endl;
}

View file

@ -1,16 +1,16 @@
#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)
///
int program::cmp_strings_on_stack_top() {
// _stack sould have 2 strings at level 1 and 2
long program::cmp_strings_on_stack_top() {
// _stack should have 2 strings at level 1 and 2
// this function removes these 2 entries
ostring* right = (ostring*)_stack->pop_back();
ostring* left = (ostring*)_stack->pop_back();
return strncmp(left->_value, right->_value, min(left->_len, right->_len));
long res = (long)_stack.value<ostring>(1).compare(_stack.value<ostring>(0));
(void)_stack.pop_front(2);
return res;
}
/// @brief > keyword implementation
@ -19,22 +19,16 @@ void program::rpn_sup(void) {
MIN_ARGUMENTS(2);
// numbers
if (_stack->get_type(0) == cmd_number && _stack->get_type(1) == cmd_number) {
number* right = (number*)_stack->pop_back();
number* left = (number*)_stack->back();
if (mpfr_cmp(left->_value.mpfr, right->_value.mpfr) > 0)
mpfr_set_si(left->_value.mpfr, 1, floating_t::s_mpfr_rnd);
else
mpfr_set_si(left->_value.mpfr, 0, floating_t::s_mpfr_rnd);
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);
}
// strings
else if (_stack->get_type(0) == cmd_string && _stack->get_type(1) == cmd_string) {
int res_cmp = cmp_strings_on_stack_top();
number* res = (number*)_stack->allocate_back(number::calc_size(), cmd_number);
res->_value = (res_cmp > 0) ? 1L : 0L;
else if (_stack.type(0) == cmd_string && _stack.type(1) == cmd_string) {
_stack.push_front(new number(cmp_strings_on_stack_top() == 1));
_stack.erase(1, 2);
} else
ERR_CONTEXT(ret_bad_operand_type);
setErrorContext(ret_bad_operand_type);
}
/// @brief >= keyword implementation
@ -43,22 +37,16 @@ void program::rpn_sup_eq(void) {
MIN_ARGUMENTS(2);
// numbers
if (_stack->get_type(0) == cmd_number && _stack->get_type(1) == cmd_number) {
number* right = (number*)_stack->pop_back();
number* left = (number*)_stack->back();
if (mpfr_cmp(left->_value.mpfr, right->_value.mpfr) >= 0)
mpfr_set_si(left->_value.mpfr, 1, floating_t::s_mpfr_rnd);
else
mpfr_set_si(left->_value.mpfr, 0, floating_t::s_mpfr_rnd);
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);
}
// strings
else if (_stack->get_type(0) == cmd_string && _stack->get_type(1) == cmd_string) {
int res_cmp = cmp_strings_on_stack_top();
number* res = (number*)_stack->allocate_back(number::calc_size(), cmd_number);
res->_value = (res_cmp >= 0) ? 1L : 0L;
else if (_stack.type(0) == cmd_string && _stack.type(1) == cmd_string) {
_stack.push_front(new number(cmp_strings_on_stack_top() != -1));
_stack.erase(1, 2);
} else
ERR_CONTEXT(ret_bad_operand_type);
setErrorContext(ret_bad_operand_type);
}
/// @brief < keyword implementation
@ -67,46 +55,32 @@ void program::rpn_inf(void) {
MIN_ARGUMENTS(2);
// numbers
if (_stack->get_type(0) == cmd_number && _stack->get_type(1) == cmd_number) {
number* right = (number*)_stack->pop_back();
number* left = (number*)_stack->back();
if (mpfr_cmp(left->_value.mpfr, right->_value.mpfr) < 0)
mpfr_set_si(left->_value.mpfr, 1, floating_t::s_mpfr_rnd);
else
mpfr_set_si(left->_value.mpfr, 0, floating_t::s_mpfr_rnd);
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);
}
// strings
else if (_stack->get_type(0) == cmd_string && _stack->get_type(1) == cmd_string) {
int res_cmp = cmp_strings_on_stack_top();
number* res = (number*)_stack->allocate_back(number::calc_size(), cmd_number);
res->_value = (res_cmp < 0) ? 1L : 0L;
else if (_stack.type(0) == cmd_string && _stack.type(1) == cmd_string) {
_stack.push_front(new number(cmp_strings_on_stack_top() == -1));
_stack.erase(1, 2);
} else
ERR_CONTEXT(ret_bad_operand_type);
setErrorContext(ret_bad_operand_type);
}
/// @brief <= keyword implementation
///
void program::rpn_inf_eq(void) {
MIN_ARGUMENTS(2);
// numbers
if (_stack->get_type(0) == cmd_number && _stack->get_type(1) == cmd_number) {
number* right = (number*)_stack->pop_back();
number* left = (number*)_stack->back();
if (mpfr_cmp(left->_value.mpfr, right->_value.mpfr) <= 0)
mpfr_set_si(left->_value.mpfr, 1, floating_t::s_mpfr_rnd);
else
mpfr_set_si(left->_value.mpfr, 0, floating_t::s_mpfr_rnd);
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);
}
// strings
else if (_stack->get_type(0) == cmd_string && _stack->get_type(1) == cmd_string) {
int res_cmp = cmp_strings_on_stack_top();
number* res = (number*)_stack->allocate_back(number::calc_size(), cmd_number);
res->_value = (res_cmp <= 0) ? 1L : 0L;
else if (_stack.type(0) == cmd_string && _stack.type(1) == cmd_string) {
_stack.push_front(new number(cmp_strings_on_stack_top() != 1));
_stack.erase(1, 2);
} else
ERR_CONTEXT(ret_bad_operand_type);
setErrorContext(ret_bad_operand_type);
}
/// @brief != keyword implementation
@ -115,37 +89,21 @@ void program::rpn_diff(void) {
MIN_ARGUMENTS(2);
// numbers
if (_stack->get_type(0) == cmd_number && _stack->get_type(1) == cmd_number) {
number* right = (number*)_stack->pop_back();
number* left = (number*)_stack->back();
if (mpfr_cmp(left->_value.mpfr, right->_value.mpfr) != 0)
mpfr_set_si(left->_value.mpfr, 1, floating_t::s_mpfr_rnd);
else
mpfr_set_si(left->_value.mpfr, 0, floating_t::s_mpfr_rnd);
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);
}
// complexes
else if (_stack->get_type(0) == cmd_complex && _stack->get_type(1) == cmd_complex) {
bool diff = false;
complex* right = (complex*)_stack->pop_back();
complex* left = (complex*)_stack->pop_back();
if (mpfr_cmp(left->re()->mpfr, right->re()->mpfr) != 0 || mpfr_cmp(left->im()->mpfr, right->im()->mpfr) != 0)
diff = true;
number* num = (number*)_stack->allocate_back(number::calc_size(), cmd_number);
if (diff)
mpfr_set_si(num->_value.mpfr, 1, floating_t::s_mpfr_rnd);
else
mpfr_set_si(num->_value.mpfr, 0, floating_t::s_mpfr_rnd);
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);
}
// strings
else if (_stack->get_type(0) == cmd_string && _stack->get_type(1) == cmd_string) {
int res_cmp = cmp_strings_on_stack_top();
number* res = (number*)_stack->allocate_back(number::calc_size(), cmd_number);
res->_value = (res_cmp != 0) ? 1L : 0L;
else if (_stack.type(0) == cmd_string && _stack.type(1) == cmd_string) {
_stack.push_front(new number(cmp_strings_on_stack_top() != 0));
_stack.erase(1, 2);
} else
ERR_CONTEXT(ret_bad_operand_type);
setErrorContext(ret_bad_operand_type);
}
/// @brief == keyword implementation
@ -154,37 +112,21 @@ void program::rpn_eq(void) {
MIN_ARGUMENTS(2);
// numbers
if (_stack->get_type(0) == cmd_number && _stack->get_type(1) == cmd_number) {
number* right = (number*)_stack->pop_back();
number* left = (number*)_stack->back();
if (mpfr_cmp(left->_value.mpfr, right->_value.mpfr) == 0)
mpfr_set_si(left->_value.mpfr, 1, floating_t::s_mpfr_rnd);
else
mpfr_set_si(left->_value.mpfr, 0, floating_t::s_mpfr_rnd);
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);
}
// complexes
else if (_stack->get_type(0) == cmd_complex && _stack->get_type(1) == cmd_complex) {
bool diff = false;
complex* right = (complex*)_stack->pop_back();
complex* left = (complex*)_stack->pop_back();
if (mpfr_cmp(left->re()->mpfr, right->re()->mpfr) != 0 || mpfr_cmp(left->im()->mpfr, right->im()->mpfr) != 0)
diff = true;
number* num = (number*)_stack->allocate_back(number::calc_size(), cmd_number);
if (diff)
mpfr_set_si(num->_value.mpfr, 0, floating_t::s_mpfr_rnd);
else
mpfr_set_si(num->_value.mpfr, 1, floating_t::s_mpfr_rnd);
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);
}
// strings
else if (_stack->get_type(0) == cmd_string && _stack->get_type(1) == cmd_string) {
int res_cmp = cmp_strings_on_stack_top();
number* res = (number*)_stack->allocate_back(number::calc_size(), cmd_number);
res->_value = (res_cmp == 0) ? 1L : 0L;
else if (_stack.type(0) == cmd_string && _stack.type(1) == cmd_string) {
_stack.push_front(new number(cmp_strings_on_stack_top() == 0));
_stack.erase(1, 2);
} else
ERR_CONTEXT(ret_bad_operand_type);
setErrorContext(ret_bad_operand_type);
}
/// @brief and keyword implementation
@ -194,13 +136,11 @@ void program::rpn_test_and(void) {
ARG_MUST_BE_OF_TYPE(0, cmd_number);
ARG_MUST_BE_OF_TYPE(1, cmd_number);
number* right = (number*)_stack->pop_back();
number* left = (number*)_stack->back();
if ((mpfr_cmp_si(left->_value.mpfr, 0) != 0) && (mpfr_cmp_si(right->_value.mpfr, 0) != 0))
mpfr_set_si(left->_value.mpfr, 1, floating_t::s_mpfr_rnd);
if (_stack.value<number>(0) != 0 && _stack.value<number>(1) != 0)
_stack.push(new number(1));
else
mpfr_set_si(left->_value.mpfr, 0, floating_t::s_mpfr_rnd);
_stack.push(new number(0));
_stack.erase(1, 2);
}
/// @brief or keyword implementation
@ -210,13 +150,11 @@ void program::rpn_test_or(void) {
ARG_MUST_BE_OF_TYPE(0, cmd_number);
ARG_MUST_BE_OF_TYPE(1, cmd_number);
number* right = (number*)_stack->pop_back();
number* left = (number*)_stack->back();
if ((mpfr_cmp_si(left->_value.mpfr, 0) != 0) || (mpfr_cmp_si(right->_value.mpfr, 0) != 0))
mpfr_set_si(left->_value.mpfr, 1, floating_t::s_mpfr_rnd);
if (_stack.value<number>(0) != 0 || _stack.value<number>(1) != 0)
_stack.push(new number(1));
else
mpfr_set_si(left->_value.mpfr, 0, floating_t::s_mpfr_rnd);
_stack.push(new number(0));
_stack.erase(1, 2);
}
/// @brief xor keyword implementation
@ -226,20 +164,11 @@ void program::rpn_test_xor(void) {
ARG_MUST_BE_OF_TYPE(0, cmd_number);
ARG_MUST_BE_OF_TYPE(1, cmd_number);
number* right = (number*)_stack->pop_back();
number* left = (number*)_stack->back();
if (mpfr_cmp_si(left->_value.mpfr, 0) == 0) {
if (mpfr_cmp_si(right->_value.mpfr, 0) != 0)
mpfr_set_si(left->_value.mpfr, 1, floating_t::s_mpfr_rnd);
else
mpfr_set_si(left->_value.mpfr, 0, floating_t::s_mpfr_rnd);
} else {
if (mpfr_cmp_si(right->_value.mpfr, 0) == 0)
mpfr_set_si(left->_value.mpfr, 1, floating_t::s_mpfr_rnd);
else
mpfr_set_si(left->_value.mpfr, 0, floating_t::s_mpfr_rnd);
}
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
@ -248,11 +177,8 @@ void program::rpn_test_not(void) {
MIN_ARGUMENTS(1);
ARG_MUST_BE_OF_TYPE(0, cmd_number);
number* left = (number*)_stack->back();
if (mpfr_cmp_si(left->_value.mpfr, 0) == 0)
mpfr_set_si(left->_value.mpfr, 1, floating_t::s_mpfr_rnd);
else
mpfr_set_si(left->_value.mpfr, 0, floating_t::s_mpfr_rnd);
_stack.push(new number(_stack.value<number>(0) == 0?1:0));
_stack.erase(1, 1);
}
/// @brief test same implementation

View file

@ -1,4 +1,5 @@
#include <time.h>
#include "program.hpp"
/// @brief time keyword implementation
@ -12,18 +13,17 @@ void program::rpn_time() {
clock_gettime(CLOCK_REALTIME, &ts);
time_t time = (time_t)ts.tv_sec;
tm = localtime(&time);
if (tm != NULL) {
if (tm != nullptr) {
// date format = HH.MMSSssssss
date = ((double)tm->tm_hour) * 10000000000.0 + ((double)tm->tm_min) * 100000000.0 +
((double)tm->tm_sec) * 1000000.0 + (double)(ts.tv_nsec / 1000);
// push it
number* num = (number*)_stack->allocate_back(number::calc_size(), cmd_number);
CHECK_MPFR(mpfr_set_d(num->_value.mpfr, date, floating_t::s_mpfr_rnd));
// division is done here because of real precision)
CHECK_MPFR(mpfr_div_d(num->_value.mpfr, num->_value.mpfr, 10000000000.0, floating_t::s_mpfr_rnd));
// division after push for real precision
_stack.push(new number(date));
_stack.value<number>(0) /= 10000000000.0;
} else
ERR_CONTEXT(ret_internal);
setErrorContext(ret_internal);
}
/// @brief date keyword implementation
@ -37,17 +37,17 @@ void program::rpn_date() {
clock_gettime(CLOCK_REALTIME, &ts);
time_t time = (time_t)ts.tv_sec;
tm = localtime(&time);
if (tm != NULL) {
if (tm != nullptr) {
// date format = (M)M.DDYYYY
date = (double)(tm->tm_mon + 1) * 1000000.0 + (double)(tm->tm_mday) * 10000.0 + (double)(tm->tm_year + 1900);
// push it
number* num = (number*)_stack->allocate_back(number::calc_size(), cmd_number);
CHECK_MPFR(mpfr_set_d(num->_value.mpfr, date, floating_t::s_mpfr_rnd));
// division is done here because of real precision)
CHECK_MPFR(mpfr_div_d(num->_value.mpfr, num->_value.mpfr, 1000000.0, floating_t::s_mpfr_rnd));
number* num;
// division after push for real precision
_stack.push(new number(date));
_stack.value<number>(0) /= 1000000.0;
} else
ERR_CONTEXT(ret_internal);
setErrorContext(ret_internal);
}
/// @brief ticks keyword implementation
@ -61,13 +61,10 @@ void program::rpn_ticks() {
clock_gettime(CLOCK_REALTIME, &ts);
time_t time = (time_t)ts.tv_sec;
tm = localtime(&time);
if (tm != NULL) {
if (tm != nullptr) {
// date in µs
date = 1000000.0 * (double)ts.tv_sec + (double)(ts.tv_nsec / 1000);
// push it
number* num = (number*)_stack->allocate_back(number::calc_size(), cmd_number);
CHECK_MPFR(mpfr_set_d(num->_value.mpfr, date, floating_t::s_mpfr_rnd));
_stack.push(new number(date));
} else
ERR_CONTEXT(ret_internal);
setErrorContext(ret_internal);
}

View file

@ -3,8 +3,7 @@
/// @brief pi keyword implementation
///
void program::rpn_pi(void) {
number* pi = (number*)_stack->allocate_back(number::calc_size(), cmd_number);
CHECK_MPFR(mpfr_const_pi(pi->_value.mpfr, floating_t::s_mpfr_rnd));
_stack.push_front(new number(const_pi()));
}
/// @brief d->r keyword implementation
@ -12,15 +11,8 @@ void program::rpn_pi(void) {
void program::rpn_d2r(void) {
MIN_ARGUMENTS(1);
ARG_MUST_BE_OF_TYPE(0, cmd_number);
// add pi on stack
rpn_pi();
floating_t* pi = &((number*)_stack->pop_back())->_value;
floating_t* left = &((number*)_stack->get_obj(0))->_value;
CHECK_MPFR(mpfr_mul(left->mpfr, left->mpfr, pi->mpfr, floating_t::s_mpfr_rnd));
CHECK_MPFR(mpfr_div_si(left->mpfr, left->mpfr, 180, floating_t::s_mpfr_rnd));
_stack.value<number>(0) *= const_pi();
_stack.value<number>(0) /= 180;
}
/// @brief r->d keyword implementation
@ -28,15 +20,8 @@ void program::rpn_d2r(void) {
void program::rpn_r2d(void) {
MIN_ARGUMENTS(1);
ARG_MUST_BE_OF_TYPE(0, cmd_number);
// add pi on stack
rpn_pi();
floating_t* pi = &((number*)_stack->pop_back())->_value;
floating_t* left = &((number*)_stack->get_obj(0))->_value;
CHECK_MPFR(mpfr_div(left->mpfr, left->mpfr, pi->mpfr, floating_t::s_mpfr_rnd));
CHECK_MPFR(mpfr_mul_si(left->mpfr, left->mpfr, 180, floating_t::s_mpfr_rnd));
_stack.value<number>(0) /= const_pi();
_stack.value<number>(0) *= 180;
}
/// @brief sin keyword implementation
@ -44,31 +29,12 @@ void program::rpn_r2d(void) {
void program::rpn_sin(void) {
MIN_ARGUMENTS(1);
if (_stack->get_type(0) == cmd_number) {
floating_t* left = &((number*)_stack->get_obj(0))->_value;
CHECK_MPFR(mpfr_sin(left->mpfr, left->mpfr, floating_t::s_mpfr_rnd));
} else if (_stack->get_type(0) == cmd_complex) {
// sin(x+iy)=sin(x)cosh(y)+icos(x)sinh(y)
stack::copy_and_push_back(*_stack, _stack->size() - 1, _calc_stack);
floating_t* tmp = &((number*)_calc_stack.allocate_back(number::calc_size(), cmd_number))->_value;
floating_t* x = ((complex*)_calc_stack.get_obj(1))->re();
floating_t* y = ((complex*)_calc_stack.get_obj(1))->im();
floating_t* re = ((complex*)_stack->get_obj(0))->re();
floating_t* im = ((complex*)_stack->get_obj(0))->im();
CHECK_MPFR(mpfr_sin(re->mpfr, x->mpfr, floating_t::s_mpfr_rnd));
CHECK_MPFR(mpfr_cosh(tmp->mpfr, y->mpfr, floating_t::s_mpfr_rnd));
CHECK_MPFR(mpfr_mul(re->mpfr, re->mpfr, tmp->mpfr, floating_t::s_mpfr_rnd));
CHECK_MPFR(mpfr_cos(im->mpfr, x->mpfr, floating_t::s_mpfr_rnd));
CHECK_MPFR(mpfr_sinh(tmp->mpfr, y->mpfr, floating_t::s_mpfr_rnd));
CHECK_MPFR(mpfr_mul(im->mpfr, im->mpfr, tmp->mpfr, floating_t::s_mpfr_rnd));
_calc_stack.pop_back(2);
} else
ERR_CONTEXT(ret_bad_operand_type);
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
@ -76,38 +42,12 @@ void program::rpn_sin(void) {
void program::rpn_asin(void) {
MIN_ARGUMENTS(1);
if (_stack->get_type(0) == cmd_number) {
floating_t* left = &((number*)_stack->get_obj(0))->_value;
CHECK_MPFR(mpfr_asin(left->mpfr, left->mpfr, floating_t::s_mpfr_rnd));
} else if (_stack->get_type(0) == cmd_complex) {
number* num;
complex* i;
// asin(z)=-iln(iz+sqrt(1-z*z))
stack::copy_and_push_back(*_stack, _stack->size() - 1, _calc_stack);
i = (complex*)_calc_stack.get_obj(0);
CHECK_MPFR(mpfr_set_d(i->re()->mpfr, 0.0, floating_t::s_mpfr_rnd));
CHECK_MPFR(mpfr_set_d(i->im()->mpfr, 1.0, floating_t::s_mpfr_rnd));
rpn_dup();
rpn_square();
num = (number*)_stack->allocate_back(number::calc_size(), cmd_number);
CHECK_MPFR(mpfr_set_d(num->_value.mpfr, 1.0, floating_t::s_mpfr_rnd));
rpn_minus();
rpn_neg();
rpn_squareroot();
rpn_swap();
stack::copy_and_push_back(_calc_stack, 0, *_stack);
rpn_mul();
rpn_plus();
rpn_ln();
stack::copy_and_push_back(_calc_stack, 0, *_stack);
rpn_mul();
rpn_neg();
_calc_stack.pop_back();
} else
ERR_CONTEXT(ret_bad_operand_type);
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
@ -115,32 +55,12 @@ void program::rpn_asin(void) {
void program::rpn_cos(void) {
MIN_ARGUMENTS(1);
if (_stack->get_type(0) == cmd_number) {
floating_t* left = &((number*)_stack->get_obj(0))->_value;
CHECK_MPFR(mpfr_cos(left->mpfr, left->mpfr, floating_t::s_mpfr_rnd));
} else if (_stack->get_type(0) == cmd_complex) {
// cos(x+iy) = cos(x)cosh(y) - isin(x)sinh(y)
stack::copy_and_push_back(*_stack, _stack->size() - 1, _calc_stack);
floating_t* tmp = &((number*)_calc_stack.allocate_back(number::calc_size(), cmd_number))->_value;
floating_t* x = ((complex*)_calc_stack.get_obj(1))->re();
floating_t* y = ((complex*)_calc_stack.get_obj(1))->im();
floating_t* re = ((complex*)_stack->get_obj(0))->re();
floating_t* im = ((complex*)_stack->get_obj(0))->im();
CHECK_MPFR(mpfr_cos(re->mpfr, x->mpfr, floating_t::s_mpfr_rnd));
CHECK_MPFR(mpfr_cosh(tmp->mpfr, y->mpfr, floating_t::s_mpfr_rnd));
CHECK_MPFR(mpfr_mul(re->mpfr, re->mpfr, tmp->mpfr, floating_t::s_mpfr_rnd));
CHECK_MPFR(mpfr_sin(im->mpfr, x->mpfr, floating_t::s_mpfr_rnd));
CHECK_MPFR(mpfr_sinh(tmp->mpfr, y->mpfr, floating_t::s_mpfr_rnd));
CHECK_MPFR(mpfr_mul(im->mpfr, im->mpfr, tmp->mpfr, floating_t::s_mpfr_rnd));
CHECK_MPFR(mpfr_neg(im->mpfr, im->mpfr, floating_t::s_mpfr_rnd));
_calc_stack.pop_back(2);
} else
ERR_CONTEXT(ret_bad_operand_type);
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
@ -148,20 +68,12 @@ void program::rpn_cos(void) {
void program::rpn_acos(void) {
MIN_ARGUMENTS(1);
if (_stack->get_type(0) == cmd_number) {
floating_t* left = &((number*)_stack->get_obj(0))->_value;
CHECK_MPFR(mpfr_acos(left->mpfr, left->mpfr, floating_t::s_mpfr_rnd));
} else if (_stack->get_type(0) == cmd_complex) {
// acos(z)=pi/2-asin(z)
rpn_asin();
rpn_pi();
number* num = (number*)_stack->allocate_back(number::calc_size(), cmd_number);
CHECK_MPFR(mpfr_set_d(num->_value.mpfr, 2.0, floating_t::s_mpfr_rnd));
rpn_div();
rpn_minus();
rpn_neg();
} else
ERR_CONTEXT(ret_bad_operand_type);
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
@ -169,41 +81,12 @@ void program::rpn_acos(void) {
void program::rpn_tan(void) {
MIN_ARGUMENTS(1);
if (_stack->get_type(0) == cmd_number) {
floating_t* left = &((number*)_stack->get_obj(0))->_value;
CHECK_MPFR(mpfr_tan(left->mpfr, left->mpfr, floating_t::s_mpfr_rnd));
} else if (_stack->get_type(0) == cmd_complex) {
// tan(x+iy) = (sin(2x)+isinh(2y)) / cosh(2y)+cos(2x)
stack::copy_and_push_back(*_stack, _stack->size() - 1, _calc_stack);
floating_t* tmp = &((number*)_calc_stack.allocate_back(number::calc_size(), cmd_number))->_value;
floating_t* x = ((complex*)_calc_stack.get_obj(1))->re();
floating_t* y = ((complex*)_calc_stack.get_obj(1))->im();
floating_t* re = ((complex*)_stack->get_obj(0))->re();
floating_t* im = ((complex*)_stack->get_obj(0))->im();
// x->2x
CHECK_MPFR(mpfr_mul_si(x->mpfr, x->mpfr, 2, floating_t::s_mpfr_rnd));
// y->2y
CHECK_MPFR(mpfr_mul_si(y->mpfr, y->mpfr, 2, floating_t::s_mpfr_rnd));
// sin(2x)+sinh(2y)
CHECK_MPFR(mpfr_sin(re->mpfr, x->mpfr, floating_t::s_mpfr_rnd));
CHECK_MPFR(mpfr_sinh(im->mpfr, y->mpfr, floating_t::s_mpfr_rnd));
// cosh(2y)+cos(2x)
CHECK_MPFR(mpfr_cosh(tmp->mpfr, y->mpfr, floating_t::s_mpfr_rnd));
CHECK_MPFR(mpfr_cos(x->mpfr, x->mpfr, floating_t::s_mpfr_rnd));
CHECK_MPFR(mpfr_add(tmp->mpfr, tmp->mpfr, x->mpfr, floating_t::s_mpfr_rnd));
// sin(2x)+sinh(2y) / (cosh(2y)+cos(2x))
CHECK_MPFR(mpfr_div(re->mpfr, re->mpfr, tmp->mpfr, floating_t::s_mpfr_rnd));
CHECK_MPFR(mpfr_div(im->mpfr, im->mpfr, tmp->mpfr, floating_t::s_mpfr_rnd));
_calc_stack.pop_back(2);
} else
ERR_CONTEXT(ret_bad_operand_type);
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
@ -211,39 +94,10 @@ void program::rpn_tan(void) {
void program::rpn_atan(void) {
MIN_ARGUMENTS(1);
if (_stack->get_type(0) == cmd_number) {
floating_t* left = &((number*)_stack->get_obj(0))->_value;
CHECK_MPFR(mpfr_atan(left->mpfr, left->mpfr, floating_t::s_mpfr_rnd));
} else if (_stack->get_type(0) == cmd_complex) {
number* num;
complex* i;
// atan(z)=0.5i(ln((1-iz)/(1+iz))
stack::copy_and_push_back(*_stack, _stack->size() - 1, _calc_stack);
i = (complex*)_calc_stack.get_obj(0);
CHECK_MPFR(mpfr_set_d(i->re()->mpfr, 0.0, floating_t::s_mpfr_rnd));
CHECK_MPFR(mpfr_set_d(i->im()->mpfr, 1.0, floating_t::s_mpfr_rnd));
stack::copy_and_push_back(_calc_stack, 0, *_stack);
rpn_mul();
num = (number*)_stack->allocate_back(number::calc_size(), cmd_number);
CHECK_MPFR(mpfr_set_d(num->_value.mpfr, 1.0, floating_t::s_mpfr_rnd));
rpn_minus(); // iz-1
rpn_neg(); // 1-iz
rpn_dup();
rpn_neg(); // iz-1
num = (number*)_stack->allocate_back(number::calc_size(), cmd_number);
CHECK_MPFR(mpfr_set_d(num->_value.mpfr, 2.0, floating_t::s_mpfr_rnd));
rpn_plus(); // iz+1
rpn_div();
rpn_ln();
CHECK_MPFR(mpfr_set_d(i->im()->mpfr, 0.5, floating_t::s_mpfr_rnd));
stack::copy_and_push_back(_calc_stack, 0, *_stack);
rpn_mul();
_calc_stack.pop_back();
} else
ERR_CONTEXT(ret_bad_operand_type);
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

@ -2,34 +2,22 @@
#define __stack_h__
#include <string.h>
#include <map>
using namespace std;
// allocation base size
#define ALLOC_STACK_CHUNK (64 * 1024)
#include <algorithm>
#include <deque>
#include <map>
#include "object.hpp"
using namespace std;
/// @brief stack object, parens of program, storing execution stack values or programs
///
class stack {
class rpnstack : public deque<object*> {
public:
stack() {
_base = NULL;
_base_pointer = NULL;
_total_size = 0;
_total_count_pointer = 0;
erase();
}
virtual ~stack() {
if (_base != NULL) free(_base);
if (_base_pointer != NULL) free(_base_pointer);
}
/// @brief remove all the stack elements
///
void erase() {
_current = _base;
_count = 0;
rpnstack() {}
virtual ~rpnstack() {
for_each(begin(), end(), [](object* o) { delete o; });
deque::erase(begin(), end());
}
/// @brief copy a whole stack entry and push it back to another stack
@ -38,347 +26,100 @@ class stack {
/// @param index_from index t ocopy from
/// @param to copy to
///
static void copy_and_push_back(stack& from, unsigned int index_from, stack& to) {
object* allocated = to.allocate_back(from.seq_len(index_from), from.seq_type(index_from));
memcpy(allocated, from.seq_obj(index_from), from.seq_len(index_from));
if (allocated->_type == cmd_number)
((number*)allocated)->move();
else if (allocated->_type == cmd_complex)
((complex*)allocated)->move();
static void copy_and_push_front(rpnstack& from, unsigned int index_from, rpnstack& to) {
// carefull: caller must ensure that index_from is correct
to.push_front(from[index_from]->clone());
}
/// @brief copy a whole stack entry and push it back to another stack
/// @brief erase a stack entry from it index
///
/// @param from copy from
/// @param index_from index t ocopy from
/// @param to copy to
/// @param first index to start
/// @param last index to stop
///
static void copy_and_push_back(object* from, stack& to, unsigned int size) {
object* allocated = to.allocate_back(size, from->_type);
memcpy(allocated, from, size);
if (allocated->_type == cmd_number)
((number*)allocated)->move();
else if (allocated->_type == cmd_complex)
((complex*)allocated)->move();
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; });
deque::erase(begin() + first, begin() + last);
}
/// @brief allocate one object back on an already populated (or not) stack
/// the object function move is called on every reallocated object on the stack
/// the object function init is called on the new allocated object if its type is cmd_number or cmd_complex
/// @brief pop front several entries
///
/// @param size the object size in bytes
/// @param type the object type
/// @return object* the allocated object
/// @param levels nb of entries
///
object* allocate_back(unsigned int size, cmd_type_t type) {
object* allocated;
bool data_is_reallocated = false;
char* old_base;
void pop_front(size_t levels = 1) { erase(0, levels); }
void pop() { erase(); }
// manage data memory allocation (add as much as memory it is needed)
if (((_current - _base) + size) > _total_size) {
// calc nb of needed pages
unsigned long page_number = 1 + ((_current - _base) + size - _total_size) / ALLOC_STACK_CHUNK;
_total_size += page_number * ALLOC_STACK_CHUNK;
old_base = _base;
_base = (char*)realloc(_base, _total_size);
_current = _base + (_current - old_base);
data_is_reallocated = true;
}
// manage pointers memory allocation (add one page if needed)
if ((_count + 1) > _total_count_pointer) {
_base_pointer =
(object**)realloc(_base_pointer, (_total_count_pointer * sizeof(object*)) + ALLOC_STACK_CHUNK);
_total_count_pointer += (ALLOC_STACK_CHUNK / sizeof(object));
}
// recalc object pointers in case of base reallocation
if (data_is_reallocated) {
for (int i = 0; i < _count; i++) {
_base_pointer[i] = (object*)(_base + ((char*)_base_pointer[i] - old_base));
if (_base_pointer[i]->_type == cmd_number)
((number*)_base_pointer[i])->move();
else if (_base_pointer[i]->_type == cmd_complex)
((complex*)_base_pointer[i])->move();
}
}
// manage stack itself
_base_pointer[_count++] = (object*)_current;
allocated = (object*)_current;
_current += size;
// init object
allocated->_type = type;
allocated->_size = size;
if (type == cmd_number)
((number*)allocated)->init();
else if (type == cmd_complex)
((complex*)allocated)->init();
return allocated;
// access helpers
cmd_type_t type(int level) {
// carefull: caller must ensure that level is correct
return at(level)->_type;
}
object* pop_back(int pop_count = 1) {
object* back = NULL;
// pop several entries, return the last
while (pop_count-- > 0) {
if (_count > 0) {
_current = (char*)_base_pointer[--_count];
back = (object*)_current;
}
}
return back;
template <class objectType>
auto& obj(int level) {
// carefull: caller must ensure that level is correct
return static_cast<objectType&>(*at(level));
}
/// @brief the number of objects on stack
///
/// @return unsigned int
///
unsigned int size() { return _count; }
/// @brief stack access (stack_level=0=first out)
///
/// @param stack_level the object stack level
/// @return object* pointer on object at this stack level
///
object* get_obj(unsigned int stack_level) { return seq_obj(_count - stack_level - 1); }
/// @brief same as get_obj
///
/// @param stack_level the object stack level
/// @return object* pointer on object at this stack level
///
object* operator[](unsigned int stack_level) { return seq_obj(_count - stack_level - 1); }
/// @brief returns the last object on stack
///
/// @return object* the object
///
object* back() {
object* obj = NULL;
if (_count > 0) obj = _base_pointer[_count - 1];
return obj;
template <class objectType>
auto& value(int level) {
// carefull: caller must ensure that level is correct
return static_cast<objectType*>(at(level))->value;
}
/// @brief get an object len
///
/// @param index the object stack level
/// @return unsigned int the length in bytes
///
unsigned int get_len(unsigned int index) { return seq_len(_count - index - 1); }
/// @brief get an object type
///
/// @param index the object stack level
/// @return cmd_type_t the object type
///
cmd_type_t get_type(unsigned int index) { return seq_type(_count - index - 1); }
/// @brief sequential object access (index is counted from front)
///
/// @param index object index from front
/// @return object* the object pointer
///
object* seq_obj(unsigned int index) {
object* obj = NULL;
if (index < _count) obj = _base_pointer[index];
return obj;
}
/// @brief get an object len
///
/// @param index the object stack level from front
/// @return unsigned int the length in bytes
///
unsigned int seq_len(unsigned int index) {
unsigned int len = 0;
if (index < _count) len = _base_pointer[index]->_size;
return len;
}
/// @brief get an object len
///
/// @param index the object stack level from front
/// @return cmd_type_t the object type
///
cmd_type_t seq_type(unsigned int index) {
cmd_type_t type = cmd_undef;
if (index < _count) type = _base_pointer[index]->_type;
return type;
}
private:
char* _base;
char* _current;
object** _base_pointer;
unsigned int _count; //< stack count
unsigned int _total_count_pointer; //< total number of possible pointers
unsigned int _total_size; //< total allocated data size in bytes
void push(object* o) { deque<object*>::push_front(o); }
};
/// @brief heap object, storing variables (=named object)
///
class heap : public stack {
class heap : public map<string, object*> {
public:
heap() {}
virtual ~heap() {}
virtual ~heap() { clear(); }
/// @brief add a variable on the heap
///
/// @param name the variable name
/// @param obj the variable content
/// @param size the variable size in bytes
/// @return object*
///
object* add(const string name, object* obj, unsigned int size) {
map<string, unsigned int>::iterator i = _map.find(name);
object* local = NULL;
// variable does not exist in heap or already exists but its size is too
// short -> allocate
if (i != _map.end()) local = seq_obj(i->second);
if (local == NULL || (local != NULL && size > local->_size)) {
copy_and_push_back(obj, *this, size);
_map[name] = ((stack*)this)->size() - 1;
} else {
// variable already exists in heap but previous was larger -> don't
// reallocate copy a whole stack entry and push it back to another stack
memcpy(local, obj, size);
if (local->_type == cmd_number)
((number*)local)->move();
else if (local->_type == cmd_complex)
((complex*)local)->move();
}
return local;
void clear() {
for_each(begin(), end(), [](auto it) { delete it.second; });
map::erase(begin(), end());
}
/// @brief get a variable
///
///
/// @param name the variable name
/// @param obj the variable content
/// @param size the variable size in bytes
/// @return true the variable was found
/// @return false the variable was not found
///
bool get(const string name, object*& obj, unsigned int& size) {
bool get(const string name, object*& obj) {
bool ret = false;
map<string, unsigned int>::iterator i = _map.find(name);
if (i != _map.end()) {
obj = seq_obj(i->second);
size = obj->_size;
auto i = find(name);
if (i != end()) {
obj = i->second;
ret = true;
}
return ret;
}
/// @brief replace a variable value by another
///
/// @param name the variable name
/// @param obj the new value
/// @param size the variable size in bytes
/// @return true the variable was found
/// @return false the variable was not found
///
bool replace_value(const string name, object* obj, unsigned int size) {
bool ret = false;
map<string, unsigned int>::iterator i = _map.find(name);
if (i != _map.end()) {
object* obj_dst = seq_obj(i->second);
if (size <= obj_dst->_size) {
(void)memcpy(obj_dst, obj, size);
if (obj_dst->_type == cmd_number)
((number*)obj_dst)->move();
else if (obj_dst->_type == cmd_complex)
((complex*)obj_dst)->move();
ret = true;
}
}
}
/// @brief whether a variable exists in heap or not
///
/// @param name the variable name
/// @return true the variable exists
/// @return false variable does not exist
///
bool exist(const string name) { return (_map.find(name) != _map.end()); }
/// @brief get a variable by its index in heap
///
///
/// @param num the variable index
/// @param name the variable name
/// @param obj the variable content
/// @param size the variable size in bytes
/// @return true the variable was found
/// @return false the variable was not found
///
bool get_by_index(int num, string& name, object*& obj, unsigned int& size) {
if (num >= 0 && num < (int)_map.size()) {
bool get_by_index(int num, string& name, object*& obj) {
if (num >= 0 && num < (int)size()) {
object* local;
map<string, unsigned int>::iterator i = _map.begin();
for (int j = 0; j < num; j++) i++;
local = (object*)seq_obj(i->second);
auto i = begin();
for (; num > 0; num--, i++)
;
obj = i->second;
name = i->first;
obj = local;
size = local->_size;
return true;
} else
return false;
}
/// @brief erase a variable
///
/// @param name the variable name
/// @return true the variable was found
/// @return false the variable was not found
///
bool erase(const string& name) {
map<string, unsigned int>::iterator i = _map.find(name);
bool ret = false;
if (i != _map.end()) {
// remove variable from map
_map.erase(i->first);
ret = true;
// TODO: remove unused stack entries
}
return ret;
}
/// @brief erase all variables
///
void erase_all(void) {
// map
_map.erase(_map.begin(), _map.end());
// and stack
((stack*)this)->erase();
}
/// @brief get the variables nb
///
/// @return unsigned int the variables nb
///
unsigned int count_vars() { return _map.size(); }
private:
map<string, unsigned int> _map;
};
#endif // __stack_h__

View file

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

View file

@ -0,0 +1,47 @@
# TEST FRAMEWORK
`default del`
## testing the stack size 1
-> stack size should be 0
## testing the stack size 2
`1`
-> stack size should be 1
`del`
-> stack size should be 0
## testing the stack size 3
`1 2 3 4 5`
-> stack size should be 5
`del`
## testing stack
`1 2 3`
-> stack should be 1, 2, 3
`del`
-> stack should be
## testing error 1
`del`
-> error should be 0
## testing error 2
`drop`
-> error should be 2

View file

@ -1,47 +0,0 @@
## MODE
default del
# std (1)
del
38 std
-> stack size should be 0
-> error should be 0
del
# std (2)
1 3 /
-> stack should be 0.33333333333333333333333333333333333333
del
# fix (1)
10 fix
1
-> stack should be 1.0000000000
del
# fix (2)
1 4 fix
-> stack should be 1.0000
del
# fix (3)
-1 fix
-> error should be 4
del
# sci (1)
1 12 sci
-> stack should be 1.000000000000e+00
del
# sci (2)
1 2 sci
-> stack should be 1.00e+00
del
# sci (3)
-1 sci
-> error should be 4
del
default

75
test/010-mode.md Normal file
View file

@ -0,0 +1,75 @@
# MODE
`default del`
## std (1)
```
del
38 std
```
-> stack size should be 0
-> error should be 0
`del`
## std (2)
`1 3 /`
-> stack should be 0.33333333333333333333333333333333333333
`del`
## fix (1)
`10 fix 1`
-> stack should be 1.0000000000
`del`
## fix (2)
`1 4 fix`
-> stack should be 1.0000
`del`
## fix (3)
`-1 fix`
-> error should be 4
`del`
## sci (1)
`1 12 sci`
-> stack should be 1.000000000000e+00
`del`
## sci (2)
`1 2 sci`
-> stack should be 1.00e+00
## default
`default`
-> stack should be 1
## sci (3)
`-1 sci`
-> error should be 4
`del`

View file

@ -1,49 +0,0 @@
## GENERAL
default del
# version
version
-> stack size should be 1
del
uname
-> stack size should be 1
del
# type (1)
1 type
-> stack should be "number"
del
# type (2)
"hey" type
-> stack should be "string"
del
# type (3)
<< -> n << n >> >> type
-> stack should be "program"
del
# type (4)
(1,2) type
-> stack should be "complex"
del
# type (5)
type
-> error should be 2
del
# default
2 sci 1
-> stack should be 1.00e+00
default
-> stack should be 1
del
# nop
nop
-> stack size should be 0
-> error should be 0
del

59
test/020-general.md Normal file
View file

@ -0,0 +1,59 @@
# GENERAL
`default del `
## version
`version`
-> stack size should be 1
`del`
## uname
`uname`
-> stack size should be 1
`del`
## default
`2 sci 1`
-> stack should be 1.00e+00
`default`
-> stack should be 1
`del`
## nop
`nop`
-> stack size should be 0
-> error should be 0
`del`
## quit
`q`
-> error should be 8
## quit (2)
`quit`
-> error should be 8
## exit
`exit`
-> error should be 8

101
test/021-parse-string.md Normal file
View file

@ -0,0 +1,101 @@
# PARSE STRING
`default del`
## type
`"hey" type`
-> stack should be "string"
`del`
## void 1
`""`
-> stack should be ""
`del`
## void 2
`"`
-> stack should be ""
`del`
## string
`"abcd"`
-> stack should be "abcd"
`del`
## unterminated
`"abcd`
-> stack should be "abcd"
`del`
## unterminated 2
`"abcd" "abc`
-> stack should be "abcd", "abc"
`del`
## spaces
`"abc d"`
-> stack should be "abc d"
`del`
## spaces 2
`" abcd "`
-> stack should be " abcd "
`del`
## spaces 3
`" abcd " "def" "gh ij"`
-> stack should be " abcd ", "def", "gh ij"
`del`
## spaces 4
`" . abcd . ;; "`
-> stack should be " . abcd . ;; "
`del`
## spaces 5
```
" . abcd . ;; " "ab c
```
-> stack should be " . abcd . ;; ", "ab c "
`del`
## nested types
`"1.0 swap drop`
-> stack should be "1.0 swap drop"
`del`

107
test/022-parse-symbol.md Normal file
View file

@ -0,0 +1,107 @@
# PARSE SYMBOL
`default del `
## type
`'hey' type`
-> stack should be "symbol"
`del`
## void 1
`''`
-> stack should be ''
`del`
## void 2
`'`
-> stack should be ''
`del`
## symbol
`'abcd'`
-> stack should be 'abcd'
`del`
## unterminated
`'abcd`
-> stack should be 'abcd'
`del`
## unterminated 2
`'abcd' 'abc`
-> stack should be 'abcd', 'abc'
`del`
## spaces
`'abc d'`
-> stack should be 'abc d'
`del`
## spaces 2
`' abcd '`
-> stack should be ' abcd '
`del`
## spaces 3
`' abcd ' 'def' 'gh ij'`
-> stack should be ' abcd ', 'def', 'gh ij'
`del`
## spaces 4
`' . abcd . ;; '`
-> stack should be ' . abcd . ;; '
`del`
## spaces 5
`' . abcd . ;; ' 'ab c`
-> stack should be ' . abcd . ;; ', 'ab c'
`del`
## nested types 1
`'1'`
-> stack should be '1'
`del`
## nested types 2
`'1.0 swap drop`
-> stack should be '1.0 swap drop'
`del`

137
test/023-parse-number.md Normal file
View file

@ -0,0 +1,137 @@
# PARSE NUMBER
`default del`
## type
`1.0 type`
-> stack should be "number"
`del`
## numb 1
`3.14 +3.14 -3.14`
-> stack should be 3.14, 3.14, -3.14
`del`
## spaces
` -3.14 -3 .14`
-> stack should be -3.14, -3, 0.14
`del`
## exp entry
`+3.14e2`
-> stack should be 314
`del`
## inf nan
`+inf inf -inf nan`
-> stack should be inf, inf, -inf, nan
`del`
## hex
`0x10 0X10`
-> stack should be 0x10, 0x10
`del`
## hex err
`0x 0X`
-> stack should be '0x', '0X'
`del`
## bin
`0b1101 0b0`
-> stack should be 0b1101, 0b0
`del`
## bin err
`0b`
-> stack should be '0b'
`del`
## base
`del 3b12`
-> stack should be 3b12
## base (2)
`del 0x1e2`
-> stack should be 0x1e2
## base (3)
`del 0x-1e2 5b-1234 0b-1`
-> stack should be -0x1e2, -5b1234, -0b1
## base (4)
`del -0x1e2 -5b1234 -0b1`
-> stack should be -0x1e2, -5b1234, -0b1
## base err
`del 0b12`
-> stack should be '0b12'
## base err (2)
`del 1b0`
-> stack should be '1b0'
`del -1b33`
-> stack should be '-1b33'
`del -63b1`
-> stack should be '-63b1'
## wrong base errors
`del 0b0.1100`
`ab Xb 1b ax 0X 3X`
-> stack should be 0b1, 'ab', 'Xb', '1b', 'ax', '0X', '3X'
## particular writings
`del 10b12345 2b1100`
-> stack should be 12345, 0b1100
## hex powers
`del 0x10p3 -0x2p4`
-> stack should be 0x80, -0x20
`del default`

63
test/024-parse-complex.md Normal file
View file

@ -0,0 +1,63 @@
# PARSE COMPLEX
`default del`
## type
`(1,2) type`
-> stack should be "complex"
`del`
## cplx
`(1,2) (1, 2) ( 1 , 2 )`
-> stack should be (1,2), (1,2), (1,2)
`del`
## cplx inf nan
```
(inf,3)
(-inf,nan) (inf,-inf)
```
-> stack should be (inf,3), (-inf,nan), (inf,-inf)
`del`
## unterminated
`( 3.14e2 , -2`
-> stack should be (314,-2)
`del`
## unterminated 2
`(-inf, nan`
-> stack should be (-inf,nan)
`del`
## unterminated err
```
(
(a
(123
(,
(,)
(12,
(,13)
(,3.14
```
-> stack should be '(', '(a', '(123', '(,', '(,)', '(12,', '(,13)', '(,3.14'
`del`

27
test/025-parse-other.md Normal file
View file

@ -0,0 +1,27 @@
# PARSE SOME COMMANDS
`default del`
## some command
`+`
-> error should be 2
`del`
## some command 2
` +`
-> error should be 2
`del`
## some command 3
` + -`
-> error should be 2
`del`

86
test/026-parse-program.md Normal file
View file

@ -0,0 +1,86 @@
# PARSE PROGRAM
`default del `
## type
`<< I >> type`
-> stack should be "program"
`del`
## prog 1
`<< I am a program >>`
-> stack should be «I am a program»
`del`
## prog 2
`<<I am a program>>`
-> stack should be «I am a program»
`del`
## prog 3
`<< I am a program >>`
-> stack should be «I am a program»
`del`
## prog 4
`«I am a program»`
-> stack should be «I am a program»
`del`
## prog 5
`« I am a program »`
-> stack should be «I am a program»
`del`
## unterminated 1
`<< prog`
-> stack should be «prog»
`del`
## unterminated 2
`« prog`
-> stack should be «prog»
`del`
## unterminated 3
`<< prog>`
-> stack should be «prog>»
`del`
## unterminated 4
```
<<
«
```
-> stack should be «», «»
`del`

365
test/027-base-entry.md Normal file
View file

@ -0,0 +1,365 @@
# REAL AND COMPLEX NUMERICAL ENTRIES
`default del`
## table for fixed entry, 4 fix output >= 0
```
4 fix
1 0.01 0.0001 0.00006 0.00004 0 0.012 0.001256 100 100.001 100.00006 100.00004 12345678910111213.12355
```
-> stack should be 1.0000, 0.0100, 0.0001, 0.0001, 0.0000, 0.0000, 0.0120, 0.0013, 100.0000, 100.0010, 100.0001, 100.0000, 12345678910111213.1236
`del default`
## table for fixed entry, 4 fix output <= 0
```
4 fix
-1 -0.01 -0.0001 -0.00006 -0.00004 -0 -0.012 -0.001256 -100 -100.001 -100.00006 -100.00004 -12345678910111213.12355
```
-> stack should be -1.0000, -0.0100, -0.0001, -0.0001, -0.0000, -0.0000, -0.0120, -0.0013, -100.0000, -100.0010, -100.0001, -100.0000, -12345678910111213.1236
`del default`
## table for sci entry, 4 fix output >= 0
```
4 fix
0e100 1e0 1.e0 1.001e0 1.e-3 1.e-4 6.e-5 4.e-5 1.00001e2 1.0000006e2 1.0000004e2 1234.5678917e2
```
-> stack should be 0.0000, 1.0000, 1.0000, 1.0010, 0.0010, 0.0001, 0.0001, 0.0000, 100.0010, 100.0001, 100.0000, 123456.7892
`del default`
## table for sci entry, 4 fix output <= 0
```
4 fix
-0e100 -1e0 -1.e0 -1.001e0 -1.e-3 -1.e-4 -6.e-5 -4.e-5 -1.00001e2 -1.0000006e2 -1.0000004e2 -1234.5678917e2
```
-> stack should be -0.0000, -1.0000, -1.0000, -1.0010, -0.0010, -0.0001, -0.0001, -0.0000, -100.0010, -100.0001, -100.0000, -123456.7892
`del default`
## table for singularity
```
4 fix
nan @nan@ -nan inf -inf @inf@ -@inf@
```
-> stack should be nan, nan, nan, inf, -inf, inf, -inf
`del default`
## some strange behaviour (1)
```
0 fix
1 2 / dup +
```
-> stack should be 1
`del default`
## some strange behaviour (2)
```
1 fix
0.6
```
-> stack should be 0.6
`del default`
## some strange behaviour (3)
```
0 fix
110.6 0.6
```
-> stack should be 111, 1
`del default`
## hex (1)
`0x4000000000000`
-> stack should be 0x4000000000000
`del default`
## hex (2)
`2 50 ^ hex`
-> stack should be 0x4000000000000
`del default`
## hex (3)
`12.34 hex`
-> stack should be 0xc
`del default`
## dec (1)
`2 50 ^`
-> stack should be 1125899906842624
`del default`
## dec (2)
`0x4000000000000 dec`
-> stack should be 1125899906842624
`del default`
## dec (3)
`12.34 dec`
-> stack should be 12.34
`del default`
## bin (1)
`0b100000000000000000000000000000000000000000000000000 bin`
-> stack should be 0b100000000000000000000000000000000000000000000000000
`del default`
## bin (2)
`2 50 ^ bin`
-> stack should be 0b100000000000000000000000000000000000000000000000000
`del default`
## bin (3)
`0x4000000000000 bin`
-> stack should be 0b100000000000000000000000000000000000000000000000000
`del default`
## bin (4)
`12.34 bin`
-> stack should be 0b1100
`del default`
## base entry (1)
`3b111 dup dec`
-> stack should be 3b111, 13
`del default`
## base entry (2)
`3b114`
-> stack should be '3b114'
`del default`
## base entry (3)
`1b0`
-> stack should be '1b0'
`del default`
## base entry (4)
`62b20 dup dec`
-> stack should be 62b20, 124
`del default`
## base entry (5)
`63b20`
-> stack should be '63b20'
`del default`
## base entry (6)
`2b11001100 0b11001100 ==`
-> stack should be 1
`del default`
## base entry (7)
`10b1234 1234 ==`
-> stack should be 1
`del default`
## base entry (8)
`16b1234 0x1234 ==`
-> stack should be 1
`del default`
## base display (1)
`2 62 for i 62 i base next`
-> stack should be 0b111110, 3b2022, 4b332, 5b222, 6b142, 7b116, 8b76, 9b68, 62, 11b57, 12b52, 13b4a, 14b46, 15b42, 0x3e, 17b3b, 18b38, 19b35, 20b32, 21b2k, 22b2i, 23b2g, 24b2e, 25b2c, 26b2a, 27b28, 28b26, 29b24, 30b22, 31b20, 32b1u, 33b1t, 34b1s, 35b1r, 36b1q, 37b1P, 38b1O, 39b1N, 40b1M, 41b1L, 42b1K, 43b1J, 44b1I, 45b1H, 46b1G, 47b1F, 48b1E, 49b1D, 50b1C, 51b1B, 52b1A, 53b19, 54b18, 55b17, 56b16, 57b15, 58b14, 59b13, 60b12, 61b11, 62b10
`del default`
## base display (2)
`2 62 for i i 62 base next`
-> stack should be 62b2, 62b3, 62b4, 62b5, 62b6, 62b7, 62b8, 62b9, 62bA, 62bB, 62bC, 62bD, 62bE, 62bF, 62bG, 62bH, 62bI, 62bJ, 62bK, 62bL, 62bM, 62bN, 62bO, 62bP, 62bQ, 62bR, 62bS, 62bT, 62bU, 62bV, 62bW, 62bX, 62bY, 62bZ, 62ba, 62bb, 62bc, 62bd, 62be, 62bf, 62bg, 62bh, 62bi, 62bj, 62bk, 62bl, 62bm, 62bn, 62bo, 62bp, 62bq, 62br, 62bs, 62bt, 62bu, 62bv, 62bw, 62bx, 62by, 62bz, 62b10
`del default`
## base display (3)
```
100 dup 3 base ==
13455600 dup 5 base ==
55756 dup 17 base ==
2345321 dup 62 base ==
```
-> stack should be 1, 1, 1, 1
`del default`
## base display (4)
```
100 18 base dup 3 base == dec
13455600 55 base dup 5 base == dec
55756 9 base dup 17 base == dec
2345321 57 base dup 62 base == dec
```
-> stack should be 1, 1, 1, 1
`del default`
## negative base numbers (1)
`1000 hex neg`
-> stack should be -0x3e8
`del default`
## negative base numbers (2)
`1000 7 base neg`
-> stack should be -7b2626
`del default`
## negative base numbers (3)
`1000 bin neg`
-> stack should be -0b1111101000
`del default`
## negative base numbers (4)
`-0b1111101000 3 base`
-> stack should be -3b1101001
`del default`
## base on complexes
```
(0b110,0x102) dup bin swap dup hex dup 5 base
```
-> stack should be (0b110,0b100000010), (0b110,0x102), (0x6,0x102), (5b11,5b2013)
`del default`
## inf should not be based-represented
`-1 bin 0 bin / 1 3 base 0 3 base /`
-> stack should be -inf, inf
`del default`
## nan should not be based-represented
`-0 bin 0 bin / 0 3 base 0 3 base /`
-> stack should be nan, nan
`del default`
## complex base 16
`(0x10,0x20)`
-> stack should be (0x10,0x20)
## complex base 16 (2)
`del (16,32) hex`
-> stack should be (0x10,0x20)
## complex base 2
`del (0b111,0b110)`
-> stack should be (0b111,0b110)
## complex base 2 (2)
`del (7,6) bin`
-> stack should be (0b111,0b110)
## complex multiple bases re / im
`del (0x10,0b111) dup dup hex swap dec`
-> stack should be (0x10,0b111), (0x10,0x7), (16,7)

View file

@ -1,397 +0,0 @@
## BRANCH
default del
# if then else end (1)
1 if then 'ok' end
-> stack should be 'ok'
del
# if then else end (2)
1 if 'before' then 'ok' end
-> stack should be 'before', 'ok'
del
# if then else end (3)
0 if then 'ok' end
-> stack size should be 0
del
# if then else end (4)
0 if then 'ok' end
-> stack size should be 0
del
# if then else end (5)
1 if then 'ok' else 'KO' end
-> stack should be 'ok'
del
# if then else end (6)
1 if then 'ok' 'dokey' else 'KO' end
-> stack should be 'ok', 'dokey'
del
# if then else end (7)
0 if then 'ok' else 'KO' end
-> stack should be 'KO'
del
# if then else end - error case (1)
if then end
-> error should be 2
del
# if then else end - error case (2)
0 if then
-> error should be 11
del
# if then else end - error case (3)
0 if end
-> error should be 11
del
# if then else end - error case (4)
0 if end
-> error should be 11
del
# if then else end - error case (5)
then
-> error should be 11
del
# if then else end - error case (6)
1 if
-> error should be 11
del
# if then else end - error case (7)
else
-> error should be 11
del
# if then else end - error case (8)
end
-> error should be 11
del
# if then else end - error case (9)
"1" if then end
-> error should be 3
del
# ift (1)
1 'ok' ift
-> stack should be 'ok'
del
# ift (2)
0 'ok' ift
-> stack size should be 0
del
# ift (3)
'ok' ift
-> error should be 2
-> stack size should be 1
del
# ift (4)
ift
-> error should be 2
-> stack size should be 0
del
# ifte (1)
1 'ok' 'nok' ifte
-> stack should be 'ok'
del
# ifte (2)
0 'ok' 'nok' ifte
-> stack should be 'nok'
del
# ifte (3)
'ok' 'nok' ifte
-> error should be 2
-> stack size should be 2
del
# ifte (4)
'nok' ifte
-> error should be 2
-> stack size should be 1
del
# ifte (5)
ifte
-> error should be 2
-> stack size should be 0
del
# start next (1)
1 2 start 0 next
-> stack should be 0, 0
del
# start next (2)
2 1 start 0 next
-> stack size should be 0
del
# start next (3)
-2 -1 start 0 next
-> stack should be 0, 0
del
# start next (4)
-1 -2 start 0 next
-> stack size should be 0
del
# start next (5)
1 1 start 0 next
-> stack should be 0
del
# start next - error case (1)
1 start next
->error should be 2
del
# start next - error case (2)
start next
->error should be 2
del
# start next - error case (3)
start
->error should be 11
del
# start next - error case (4)
next
->error should be 11
del
# start next - error case (5)
"1" 2 start next
->error should be 3
del
# start next - error case (5)
1 "2" start next
->error should be 3
del
# for next (1)
23 27 for i i next
-> stack should be 23, 24, 25, 26, 27
del
# for next (2)
1 1 for i i next
-> stack should be 1
del
# for next (3)
27 23 for i i next
-> stack size should be 0
del
# for next (4)
-2 -1 for i i next
-> stack should be -2, -1
del
# for next (5)
-1 -2 for i i next
-> stack size should be 0
del
# for next - error case (1)
1 for i i next
-> error should be 2
del
# for next - error case (2)
for i i next
-> error should be 2
del
# for next - error case (3)
"1" 2 for i i next
-> error should be 3
del
# for next - error case (4)
1 "2" for i i next
-> error should be 3
del
# for next - error case (5)
1 2 for i i
-> error should be 11
del
# for next - error case (6)
for
-> error should be 11
del
# for step (1)
23 27 for i i 1 step
-> stack should be 23, 24, 25, 26, 27
del
# for step (2)
0 1 for i i 0.25 step
-> stack should be 0, 0.25, 0.5, 0.75, 1
del
# for step (3)
-1 0 for i i 0.25 step
-> stack should be -1, -0.75, -0.5, -0.25, -0
del
# for step (4)
0 -1 for i i 0.25 step
-> stack size should be 0
del
# for step (5)
0 -1 for i i -0.25 step
-> stack size should be 0
del
# for step (6) - check boundary integration
1 2 for i i 0.2 step
-> stack should be 1, 1.2, 1.4, 1.6, 1.8, 2
del
# for step (7) - check boundary integration
1 2 for i i 0.5 step
-> stack should be 1, 1.5, 2
del
# for step - error case (1)
0 1 for i i "0.5" step
-> error should be 3
del
# for step - error case (2)
step
-> error should be 11
del
# do..unti (1)
do 'ok' unti 1 end
-> stack should be 'ok'
del
# do..unti (2)
do unti 1 end
-> stack size should be 0
del
# do..unti (3)
1 'a' sto do a unti a 0 > end
-> stack should be 1
del
# do..unti (4)
1 'a' sto do a 'a' 1 sto+ unti a 3 > end
-> stack should be 1, 2, 3
del
# do..unti (5)
"" 0 'a' sto do 'a' 1 sto+ 0 a for b b ->str + next unti a 3 > end
-> stack should be "01012012301234"
del
# do..unti error case (1)
do
-> error should be 11
del
# do..unti error case (2)
do 8 end
-> error should be 11
del
# do..unti error case (3)
unti
-> error should be 11
del
# do..unti error case (4)
do 3 unti
-> error should be 11
del
# do..unti error case (5)
unti 1 end
-> error should be 11
del
# do..unti error case (6)
do 3 repeat 8 end
-> error should be 11
del
# do..unti error case (7)
do 3 until 8 until 9 end
-> error should be 11
del
# while..repeat (1)
while 0 repeat ok end
-> stack size should be 0
del
# while..repeat (2)
2 while dup 0.1 > repeat dup 2 / end
-> stack should be 2, 1, 0.5, 0.25, 0.125, 0.0625
del
# while..repeat (3)
"" 0 'a' sto while a 3 < repeat 'a' 1 sto+ 0 a for b b ->str + next end
-> stack should be "010120123"
del
# while..repeat error case (1)
while
-> error should be 11
del
# while..repeat error case (2)
while 3 end
-> error should be 11
del
# while..repeat error case (3)
repeat
-> error should be 11
del
# while..repeat error case (4)
while 1 repeat
-> error should be 11
del
# while..repeat error case (5)
repeat 1 end
-> error should be 11
del
# while..repeat error case (6)
while 3 repeat 8 repeat 9 end
-> error should be 11
del
# while..repeat error case (7)
while 3 until 8 end
-> error should be 11
del

709
test/030-branch.md Normal file
View file

@ -0,0 +1,709 @@
# BRANCH
`default del`
## if then else end (1)
`1 if then 'ok' end`
-> stack should be 'ok'
`del`
## if then else end (2)
`1 if 'before' then 'ok' end`
-> stack should be 'before', 'ok'
`del`
## if then else end (3)
`0 if then 'ok' end`
-> stack size should be 0
`del`
## if then else end (4)
`0 if then 'ok' end`
-> stack size should be 0
`del`
## if then else end (5)
`1 if then 'ok' else 'KO' end`
-> stack should be 'ok'
`del`
## if then else end (6)
`1 if then 'ok' 'dokey' else 'KO' end`
-> stack should be 'ok', 'dokey'
`del`
## if then else end (7)
`0 if then 'ok' else 'KO' end`
-> stack should be 'KO'
`del`
## if then else end - error case (1)
`if then end`
-> error should be 2
`del`
## if then else end - error case (2)
`0 if then`
-> error should be 11
`del`
## if then else end - error case (3)
`0 if end`
-> error should be 11
`del`
## if then else end - error case (4)
`0 if end`
-> error should be 11
`del`
## if then else end - error case (5)
`then`
-> error should be 11
`del`
## if then else end - error case (6)
`1 if`
-> error should be 11
`del`
## if then else end - error case (7)
`else`
-> error should be 11
`del`
## if then else end - error case (8)
`end`
-> error should be 11
`del`
## if then else end - error case (9)
`"1" if then end`
-> error should be 3
`del`
## ift (1)
`1 'ok' ift`
-> stack should be 'ok'
`del`
## ift (2)
`0 'ok' ift`
-> stack size should be 0
`del`
## ift (3)
`'ok' ift`
-> error should be 2
-> stack size should be 1
`del`
## ift (4)
`ift`
-> error should be 2
-> stack size should be 0
`del`
## ifte (1)
`1 'ok' 'nok' ifte`
-> stack should be 'ok'
`del`
## ifte (2)
`0 'ok' 'nok' ifte`
-> stack should be 'nok'
`del`
## ifte (3)
`'ok' 'nok' ifte`
-> error should be 2
-> stack size should be 2
`del`
## ifte (4)
`'nok' ifte`
-> error should be 2
-> stack size should be 1
`del`
## ifte (5)
`ifte`
-> error should be 2
-> stack size should be 0
`del`
## start next (1)
`1 2 start 0 next`
-> stack should be 0, 0
`del`
## start next (2)
`2 1 start 0 next`
-> stack size should be 0
`del`
## start next (3)
`-2 -1 start 0 next`
-> stack should be 0, 0
`del`
## start next (4)
`-1 -2 start 0 next`
-> stack size should be 0
`del`
## start next (5)
`1 1 start 0 next`
-> stack should be 0
`del`
## start next - cloning objects (1)
`1 2 start 'ok' next`
-> stack should be 'ok', 'ok'
`del`
## start next - cloning objects (2)
`1 2 start ok next`
-> stack should be 'ok', 'ok'
`del`
## start next - cloning objects (3)
`1 2 start "ok" next`
-> stack should be "ok", "ok"
`del`
## start next - cloning objects (4)
`1 2 start (1,2) next`
-> stack should be (1,2), (1,2)
`del`
## start next - cloning objects (5)
`1 2 start «ok» next`
-> stack should be «ok», «ok»
`del`
## start next - error case (1)
`1 start next`
-> error should be 2
`del`
## start next - error case (2)
`start next`
-> error should be 2
`del`
## start next - error case (3)
`start`
-> error should be 11
`del`
## start next - error case (4)
`next`
-> error should be 11
`del`
## start next - error case (5)
`"1" 2 start next`
-> error should be 3
`del`
## start next - error case (6)
`1 "2" start next`
-> error should be 3
`del`
## for next (1)
`23 27 for i i next`
-> stack should be 23, 24, 25, 26, 27
`del`
## for next (2)
`1 1 for i i next`
-> stack should be 1
`del`
## for next (3)
`27 23 for i i next`
-> stack size should be 0
`del`
## for next (4)
`-2 -1 for i i next`
-> stack should be -2, -1
`del`
## for next (5)
`-1 -2 for i i next`
-> stack size should be 0
`del`
## for next - loop variable overwrite
`123 'i' sto 1 2 for i i next`
-> stack should be 1, 2
`del`
## nested for next
`1 2 for i 0 1 for j i (1,0) * j (0,1) * + next next`
-> stack should be (1,0), (1,1), (2,0), (2,1)
`del`
## for next - error case (1)
`1 for i i next`
-> error should be 2
`del`
## for next - error case (2)
`for i i next`
-> error should be 2
`del`
## for next - error case (3)
`"1" 2 for i i next`
-> error should be 3
`del`
## for next - error case (4)
`1 "2" for i i next`
-> error should be 3
`del`
## for next - error case (5)
`1 2 for i i`
-> error should be 11
`del`
## for next - error case (6)
`for`
-> error should be 11
`del`
## for step (1)
`23 27 for i i 1 step`
-> stack should be 23, 24, 25, 26, 27
`del`
## for step (2)
`0 1 for i i 0.25 step`
-> stack should be 0, 0.25, 0.5, 0.75, 1
`del`
## for step (3)
`-1 0 for i i 0.25 step`
-> stack should be -1, -0.75, -0.5, -0.25, -0
`del`
## for step (4)
`0 -1 for i i 0.25 step`
-> stack size should be 0
`del`
## for step (5)
`0 -1 for i i -0.25 step`
-> stack size should be 0
`del`
## for step (6) - check boundary integration
`1 2 for i i 0.2 step`
-> stack should be 1, 1.2, 1.4, 1.6, 1.8, 2
`del`
## for step (7) - check boundary integration
`1 2 for i i 0.5 step`
-> stack should be 1, 1.5, 2
`del`
## nested for step
`0 2 for i 0 6 for j i (1,0) * j (0,1) * + 3 step 2 step`
-> stack should be (0,0), (0,3), (0,6), (2,0), (2,3), (2,6)
`del`
## for step - error case (1)
`0 1 for i i "0.5" step`
-> error should be 3
`del`
## for step - error case (2)
`step`
-> error should be 11
`del`
## do..until (1)
`do 'ok' until 1 end`
-> stack should be 'ok'
`del`
## do..until (2)
`do until 1 end`
-> stack size should be 0
`del`
## do..until (3)
`3 do 1 - 'ok' swap dup until 0 == end drop`
-> stack should be 'ok', 'ok', 'ok'
`del`
## do..until (4)
`1 'a' sto do a 1 + 'a' sto until a 3 > end a`
-> stack should be 4
`del`
## nexted do..until
`1 'i' sto do 0 'j' sto do i (1,0) * j (0,1) * + 1 'j' sto+ until j 1 > end 1 'i' sto+ until i 2 > end`
-> stack should be (1,0), (1,1), (2,0), (2,1)
`del`
## do..until error case (1)
`do`
-> error should be 11
`del`
## do..until error case (2)
`do 8 end`
-> error should be 11
`del`
## do..until error case (3)
`until`
-> error should be 11
`del`
## do..until error case (4)
`do 3 until`
-> error should be 11
`del`
## do..until error case (5)
`until 1 end`
-> error should be 11
`del`
## do..until error case (6)
`do 3 repeat 8 end`
-> error should be 11
`del`
## do..until error case (7)
`do 3 until 8 until 9 end`
-> error should be 11
`del`
## while..repeat (1)
`while 0 repeat ok end`
-> stack size should be 0
`del`
## while..repeat (2)
`2 while dup 0.1 > repeat dup 2 / end`
-> stack should be 2, 1, 0.5, 0.25, 0.125, 0.0625
`del`
## while..repeat (3)
`0 'a' sto while a 3 < repeat a 1 + 'a' sto 100 0 a for b b + next end`
-> stack should be 101, 103, 106
`del`
## nested while .. repeat
`1 'i' sto while i 2 <= repeat 0 'j' sto while j 1 <= repeat i (1,0) * j (0,1) * + 1 'j' sto+ end 1 'i' sto+ end`
-> stack should be (1,0), (1,1), (2,0), (2,1)
`del`
## while..repeat error case (1)
`while`
-> error should be 11
`del`
## while..repeat error case (2)
`while 3 end`
-> error should be 11
`del`
## while..repeat error case (3)
`repeat`
-> error should be 11
`del`
## while..repeat error case (4)
`while 1 repeat`
-> error should be 11
`del`
## while..repeat error case (5)
`repeat 1 end`
-> error should be 11
`del`
## while..repeat error case (6)
`while 3 repeat 8 repeat 9 end`
-> error should be 11
`del`
## while..repeat error case (7)
`while 3 until 8 end`
-> error should be 11
`del`

View file

@ -1,182 +0,0 @@
## STACK TEST
default del
# entry depth (1)
1 depth
-> stack size should be 2
# entry depth (2)
1 del depth
-> stack should be 0
del
# swap
1 2 swap
-> stack size should be 2
-> stack should be 2, 1
del
# swap with filled stack
5 6 1 2 swap
-> stack size should be 4
-> stack should be 5, 6, 2, 1
del
# swap error
5 swap
-> stack size should be 1
-> error should 2
del
# drop
1 2 3 drop
-> stack size should be 2
-> stack should be 1, 2
del
# drop2
1 2 3
drop2
-> stack size should be 1
-> stack should be 1
del
# drop error
drop
-> error should be 2
del
# drop2 error (1)
drop2
-> error should be 2
del
# drop2 error (2)
1 drop2
-> error should be 2
del
# test dup
1 dup
-> stack size should be 2
-> stack should be 1, 1
del
# test dup2
1 2 dup2
-> stack size should be 4
-> stack should be 1, 2, 1, 2
del
# test rot
1 2 3 rot
-> stack size should be 3
-> stack should be 2, 3, 1
del
# test rot with start
5 6 7 1 2 start rot next
-> stack should be 7, 5, 6
del
# test rot with next
5 6 7 1 2 for i rot next
-> stack should be 7, 5, 6
del
# test rot with filled stack
5 6 1 2 3 rot
-> stack size should be 5
-> stack should be 5, 6, 2, 3, 1
del
# test depth
1 2 3
depth
-> stack size should be 4
-> stack should be 1, 2, 3, 3
del
# test pick
1 2 3 4 2 pick
-> stack size should be 5
-> stack should be 1, 2, 3, 4, 3
0 pick
-> error should be 4
7 pick
-> error should be 4
erase
# test erase
-> stack size should be 0
del
# test del
1 2 3 4 5
del
-> stack size should be 0
# test dropn
1 2 2 dropn
-> stack size should be 0
del
# test dropn error
1 2 3 dropn
-> stack size should be 3
-> error should be 2
del
# test dupn
1 2 3 4 3 dupn
-> stack should be 1, 2, 3, 4, 2, 3, 4
del
# test dupn error
1 2 3 4 5 dupn
-> stack size should be 5
-> error should be 2
del
# test roll
1 2 3 4 5 4 roll
-> stack should be 1, 3, 4, 5, 2
del
# test roll with filled stack
10 11 1 2 3 4 5 4 roll
-> stack should be 10, 11, 1, 3, 4, 5, 2
del
# test roll error
1 2 3 4 5 6 roll
-> stack size should be 6
-> error should be 2
del
# test rolld
10 20 30 40 50 3 rolld
-> stack should be 10, 20, 50, 30, 40
del
# test rolld with filled stack
80 90 10 20 30 40 50 3 rolld
-> stack should be 80, 90, 10, 20, 50, 30, 40
del
# test rolld error
1 2 3 4 5 6 rolld
-> stack size should be 6
-> error should be 2
del
# test over
3.14 15.16 over
-> stack should be 3.14, 15.16, 3.14
del
# test over error
2 over
-> stack size should be 1
-> error should be 2
del

289
test/040-stack.md Normal file
View file

@ -0,0 +1,289 @@
# STACK
`default del`
## entry depth (1)
`1 depth`
-> stack size should be 2
## entry depth (2)
`1 del depth`
-> stack should be 0
`del`
## swap
`1 2 swap`
-> stack size should be 2
-> stack should be 2, 1
`del`
## swap with filled stack
`5 6 1 2 swap`
-> stack size should be 4
-> stack should be 5, 6, 2, 1
`del`
## swap error
`5 swap`
-> stack size should be 1
-> error should be 2
`del`
## drop
`1 2 3 drop`
-> stack size should be 2
-> stack should be 1, 2
`del`
## drop2
`1 2 3 drop2`
-> stack size should be 1
-> stack should be 1
`del`
## drop error
`drop`
-> error should be 2
`del`
## drop2 error (1)
`drop2`
-> error should be 2
`del`
## drop2 error (2)
`1 drop2`
-> error should be 2
`del`
## test dup
`1 dup`
-> stack size should be 2
-> stack should be 1, 1
`del`
## test dup2
`1 2 dup2`
-> stack size should be 4
-> stack should be 1, 2, 1, 2
`del`
## test rot
`1 2 3 rot`
-> stack size should be 3
-> stack should be 2, 3, 1
`del`
## test rot 2
`5 6 7 rot rot`
-> stack should be 7, 5, 6
`del`
## test rot with filled stack
`5 6 1 2 3 rot`
-> stack size should be 5
-> stack should be 5, 6, 2, 3, 1
`del`
## test depth
`1 2 3`
`depth`
-> stack size should be 4
-> stack should be 1, 2, 3, 3
`del`
## test pick
`1 2 3 4 2 pick`
-> stack size should be 5
-> stack should be 1, 2, 3, 4, 3
`0 pick`
-> error should be 4
`7 pick`
-> error should be 4
`erase`
## test erase
-> stack size should be 0
`erase`
## test del
`1 2 3 4 5`
`del`
-> stack size should be 0
## test dropn
`1 2 2 dropn`
-> stack size should be 0
`del`
## test dropn error
`1 2 3 dropn`
-> stack size should be 3
-> error should be 2
`del`
## test dupn
`1 2 3 4 3 dupn`
-> stack should be 1, 2, 3, 4, 2, 3, 4
`del`
## test dupn error
`1 2 3 4 5 dupn`
-> stack size should be 4
-> error should be 2
`del`
## test roll
`1 2 3 4 5 4 roll`
-> stack should be 1, 3, 4, 5, 2
`del`
## test roll with filled stack
`10 11 1 2 3 4 5 4 roll`
-> stack should be 10, 11, 1, 3, 4, 5, 2
`del`
## test roll error
`1 2 3 4 5 6 roll`
-> stack size should be 5
-> error should be 2
`del`
## test rolld
`10 20 30 40 50 3 rolld`
-> stack should be 10, 20, 50, 30, 40
`del`
## test rolld with filled stack
`80 90 10 20 30 40 50 3 rolld`
-> stack should be 80, 90, 10, 20, 50, 30, 40
`del`
## test rolld error
`1 2 3 4 5 6 rolld`
-> stack size should be 5
-> error should be 2
`del`
## test over
`3.14 15.16 over`
-> stack should be 3.14, 15.16, 3.14
`del`
## test over error
`2 over`
-> stack size should be 1
-> error should be 2
`del`

View file

@ -1,470 +0,0 @@
## REAL
default del
# real decimal
1
-> stack size should be 1
-> stack should be 1
del
# real decimal (2)
2.345
-> stack should be 2.345
del
# real decimal (3)
1 2.345 3 4.9
-> stack size should be 4
-> stack should be 1, 2.345, 3, 4.9
del
# real hex
0x1234 0x10.10
-> stack should be 0x1234, 0x10
del
# real hex (2)
0x1.234p+12 0x1.01p+4
dec swap dec swap
-> stack should be 4660, 16.0625
del
# real binary
0b11001100
-> stack should be 0b11001100
del
# real inf, nan
inf
@inf@
+inf
+@inf@
-inf
-@inf@
nan
@nan@
-> stack should be inf, inf, inf, inf, -inf, -inf, nan, nan
del
# prec (1)
default
56 prec
pi
-> stack should be 3.141592653589793
del
# prec (2)
default
100 prec
pi
-> stack should be 3.14159265358979323846264338328
del
# prec error (1)
0 prec
-> error should be 4
del
# prec error (2)
0x8000000000000000 prec
-> error should be 4
del default
# round (1)
"nearest" round
-> error should be 0
del
# round (2)
"toward zero" round
-> error should be 0
del
# round (3)
"toward +inf" round
-> error should be 0
del
# round (4)
"toward -inf" round
-> error should be 0
del
# round (5)
"away from zero" round
-> error should be 0
del
# round (6)
round
-> error should be 2
del default
# add (1)
1.2 2.3 +
-> stack should be 3.5
del
# add (2)
1.2 2.3+
-> stack should be 3.5
del
# add (3)
2.3 +
-> error should be 2
-> stack size should be 1
del
# add (4)
+
-> error should be 2
del
# sub (1)
1.2 2.3 -
-> stack should be -1.1
del
# sub (2)
1.2 2.3-
-> stack should be -1.1
del
# sub (3)
2.3 -
-> error should be 2
-> stack size should be 1
del
# sub (4)
-
-> error should be 2
del
# mul (1)
1.2 2.3 *
-> stack should be 2.76
del
# mul (2)
1.2 2.3*
-> stack should be 2.76
del
# mul (3)
2.3 *
-> error should be 2
-> stack size should be 1
del
# mul (4)
*
-> error should be 2
del
# div (1)
1.2 2.3 /
-> stack should be 0.52173913043478260869565217391304347826
del
# div (2)
1.2 2.3/
-> stack should be 0.52173913043478260869565217391304347826
del
# div (3)
2.3 /
-> error should be 2
-> stack size should be 1
del
# div (4)
/
-> error should be 2
del
# chs (1)
3.14 chs
-> stack should be -3.14
del
# chs (2)
chs
-> error should be 2
# neg (1)
3.14 neg
-> stack should be -3.14
del
# neg (2)
neg
-> error should be 2
# inv (1)
2 inv
-> stack should be 0.5
del
# inv (2)
inv
-> error should be 2
# % (1)
2 30 %
-> stack should be 0.6
del
# % (2)
2 30%
-> stack should be 0.6
del
# % (3)
2 %
-> error should be 2
-> stack size should be 1
del
# % (4)
%
-> error should be 2
del
# %CH (1)
2 0.6 %CH
-> stack should be 30
del
# %CH (2)
2 0.6%CH
-> stack should be 30
del
# %CH (3)
2 %CH
-> error should be 2
-> stack size should be 1
del
# %CH (4)
%CH
-> error should be 2
del
# ^ (1)
2 10 ^
-> stack should be 1024
del
# ^ (2)
2 10^
-> stack should be 1024
del
# ^ (3)
2 ^
-> error should be 2
-> stack size should be 1
del
# ^ (4)
^
-> error should be 2
del
# sqrt (1)
9 sqrt
-> stack should be 3
del
# sqrt (2)
sqrt
-> error should be 3
# sq (1)
12 sq
-> stack should be 144
del
# sq (2)
sq
-> error should be 2
# sqr (1)
12 sqr
-> stack should be 144
del
# sqr (2)
sqr
-> error should be 2
# mod (1)
9 4 mod
-> stack should be 1
del
# mod (2)
9 mod
-> error should be 2
-> stack size should be 1
del
# mod (3)
mod
-> error should be 2
del
# abs (1)
-9 abs
-> stack should be 9
del
# abs (2)
9 abs
-> stack should be 9
del
# abs (3)
abs
-> error should be 2
del
# fact (1)
6 fact
-> stack should be 720
del
# fact (2)
'r' fact
-> error should be 3
-> stack size should be 1
del
# fact (3)
fact
-> error should be 2
del
# sign (1)
23 sign -34 sign 0 sign
-> stack should be 1, -1, 0
del
# sign (2)
sign
-> error should be 2
del
# mant (1)
123.456 mant -123.456 mant 0 mant
-> stack should be 0.123456, 0.123456, 0
del
# mant (2)
inf mant
-> error should be 4
-inf mant
-> error should be 4
nan mant
-> error should be 4
del
# xpon (1)
123.456 xpon -123.456 xpon 0 mant
-> stack should be 3, 3, 0
del
# xpon (2)
inf xpon
-> error should be 4
-inf xpon
-> error should be 4
nan xpon
-> error should be 4
del
# min (1)
1 2 min 4 3 min
-> stack should be 1, 3
del
# min (2)
'a' 'g' min
-> error should be 3
del
# min (3)
1 min
-> error should be 2
del
# min (4)
min
-> error should be 2
del
# max (1)
1 2 max 4 3 max
-> stack should be 2, 4
del
# max (2)
'a' 'g' max
-> error should be 3
del
# max (3)
1 max
-> error should be 2
del
# max (4)
max
-> error should be 2
del
# ip (1)
1.22 ip
-> stack should be 1
del
# ip (2)
-1.22 ip
-> stack should be -1
del
# fp (1)
1.22 fp
-> stack should be 0.22
del
# fp (2)
-1.22 fp
-> stack should be -0.22
del
# floor (1)
1.22 floor
-> stack should be 1
del
# floor (2)
-1.22 floor
-> stack should be -2
del
# ceil (1)
1.22 ceil
-> stack should be 2
del
# ceil (2)
-1.22 ceil
-> stack should be -1
del
default

700
test/050-real.md Normal file
View file

@ -0,0 +1,700 @@
# REAL
`default del`
## real decimal
`1`
-> stack size should be 1
-> stack should be 1
`del`
## real decimal (2)
`2.345`
-> stack should be 2.345
`del`
## real decimal (3)
`1 2.345 3 4.9`
-> stack size should be 4
-> stack should be 1, 2.345, 3, 4.9
`del`
## real hex
`0x1234 0x10.10`
-> stack should be 0x1234, 0x10
`del`
## real hex (2)
```
0x1.234p+12 0x1.01p+4
dec swap dec swap
```
-> stack should be 4660, 16.0625
`del`
## real binary
`0b11001100`
-> stack should be 0b11001100
`del`
## real inf, nan
```
inf
@inf@
+inf
+@inf@
-inf
-@inf@
nan
@nan@
```
-> stack should be inf, inf, inf, inf, -inf, -inf, nan, nan
`del`
## prec (1)
```
default
56 prec
pi
```
-> stack should be 3.141592653589793
`del`
## prec (2)
```
default
100 prec
pi
```
-> stack should be 3.14159265358979323846264338328
`del`
## prec error (1)
`0 prec`
-> error should be 4
`del`
## prec error (2)
`0x8000000000000000 prec`
-> error should be 4
`del default`
## round (1)
`"nearest (even)" round`
-> error should be 0
`del`
## round (2)
`"toward zero" round`
-> error should be 0
`del`
## round (3)
`"toward +inf" round`
-> error should be 0
`del`
## round (4)
`"toward -inf" round`
-> error should be 0
`del`
## round (5)
`"away from zero" round`
-> error should be 0
`del`
## round (6)
`"faithful rounding" round`
-> error should be 0
`del`
## round (7)
`"nearest (away from zero)" round`
-> error should be 0
`del`
## round error
`round`
-> error should be 2
`del default`
## add (1)
`1.2 2.3 +`
-> stack should be 3.5
`del`
## add (2)
`2.3 +`
-> error should be 2
-> stack size should be 1
`del`
## add (3)
`+`
-> error should be 2
`del`
## sub (1)
`1.2 2.3 -`
-> stack should be -1.1
`del`
## sub (2)
`2.3 -`
-> error should be 2
-> stack size should be 1
`del`
## sub (3)
`-`
-> error should be 2
`del`
## mul (1)
`1.2 2.3 *`
-> stack should be 2.76
`del`
## mul (2)
`2.3 *`
-> error should be 2
-> stack size should be 1
`del`
## mul (3)
`*`
-> error should be 2
`del`
## div (1)
`1.2 2.3 /`
-> stack should be 0.52173913043478260869565217391304347826
`del`
## div (2)
`2.3 /`
-> error should be 2
-> stack size should be 1
`del`
## div (3)
`/`
-> error should be 2
`del`
## chs (1)
`3.14 chs`
-> stack should be -3.14
`del`
## chs (2)
`chs`
-> error should be 2
## neg (1)
`3.14 neg`
-> stack should be -3.14
`del`
## neg (2)
`neg`
-> error should be 2
## inv (1)
`2 inv`
-> stack should be 0.5
`del`
## inv (2)
`inv`
-> error should be 2
## % (1)
`2 30 %`
-> stack should be 0.6
`del`
## % (2)
`2 %`
-> error should be 2
-> stack size should be 1
`del`
## % (3)
`%`
-> error should be 2
`del`
## %CH (1)
`2 0.6 %CH`
-> stack should be 30
`del`
## %CH (2)
`2 %CH`
-> error should be 2
-> stack size should be 1
`del`
## %CH (3)
`%CH`
-> error should be 2
`del`
## ^ (1)
`2 10 ^`
-> stack should be 1024
`del`
## ^ (2)
`2 ^`
-> error should be 2
-> stack size should be 1
`del`
## ^ (3)
`^`
-> error should be 2
`del`
## sqrt (1)
`9 sqrt`
-> stack should be 3
`del`
## sqrt (2)
`sqrt`
-> error should be 2
## sq (1)
`12 sq`
-> stack should be 144
`del`
## sq (2)
`sq`
-> error should be 2
## mod (1)
`9 4 mod`
-> stack should be 1
`del`
## mod (2)
`9 mod`
-> error should be 2
-> stack size should be 1
`del`
## mod (3)
`mod`
-> error should be 2
`del`
## abs (1)
`-9 abs`
-> stack should be 9
`del`
## abs (2)
`9 abs`
-> stack should be 9
`del`
## abs (3)
`abs`
-> error should be 2
`del`
## fact (1)
`6 fact`
-> stack should be 720
`del`
## fact (2)
`'r' fact`
-> error should be 3
-> stack size should be 1
`del`
## fact (3)
`fact`
-> error should be 2
`del`
## sign (1)
`23 sign -34 sign 0 sign`
-> stack should be 1, -1, 0
`del`
## sign (2)
`sign`
-> error should be 2
`del`
## mant (1)
`123.456 mant -123.456 mant 0 mant`
-> stack should be 0.9645, -0.9645, 0
`del`
## mant (2)
`inf mant`
-> error should be 4
`-inf mant`
-> error should be 4
`nan mant`
-> error should be 4
`del`
## xpon (1)
`123.456 xpon -123.456 xpon 0 mant`
-> stack should be 7, 7, 0
`del`
## xpon (2)
`inf xpon`
-> error should be 4
`-inf xpon`
-> error should be 4
`nan xpon`
-> error should be 4
`del`
## min (1)
`1 2 min 4 3 min`
-> stack should be 1, 3
`del`
## min (2)
`'a' 'g' min`
-> error should be 3
`del`
## min (3)
`1 min`
-> error should be 2
`del`
## min (4)
`min`
-> error should be 2
`del`
## max (1)
`1 2 max 4 3 max`
-> stack should be 2, 4
`del`
## max (2)
`'a' 'g' max`
-> error should be 3
`del`
## max (3)
`1 max`
-> error should be 2
`del`
## max (4)
`max`
-> error should be 2
`del`
## ip (1)
`1.22 ip`
-> stack should be 1
`del`
## ip (2)
`-1.22 ip`
-> stack should be -1
`del`
## fp (1)
`1.22 fp`
-> stack should be 0.22
`del`
## fp (2)
`-1.22 fp`
-> stack should be -0.22
`del`
## floor (1)
`1.22 floor`
-> stack should be 1
`del`
## floor (2)
`-1.22 floor`
-> stack should be -2
`del`
## ceil (1)
`1.22 ceil`
-> stack should be 2
`del`
## ceil (2)
`-1.22 ceil`
-> stack should be -1
`del default`

View file

@ -1,213 +0,0 @@
## STRING
default del
# string entry
"test string"
-> stack size should be 1
-> stack should be "test string"
del
# string entry (2)
"test string
-> stack size should be 1
-> stack should be "test string"
del
# string entry (3)
"
-> stack size should be 1
-> stack should be ""
del
# ->str on real (1)
1
->str
-> stack should be "1"
del
# ->str on real (2)
1.234
->str
-> stack should be "1.234"
del
# ->str on real (3)
1.234
20 fix
->str
-> stack should be "1.23400000000000000000"
del
# ->str on symbol (1)
toto
->str
-> stack should be "'toto'"
del
# ->str on symbol (2)
'toto'
->str
-> stack should be "'toto'"
del
default
# str-> on real (1)
"1"
str->
-> stack should be 1
del
# str-> on real (2)
"1 2.345 3 4.9"
str->
-> stack should be 1, 2.345, 3, 4.9
del
# str-> on real (3)
4 fix
"1 2.345 3 4.9"
str->
-> stack should be 1.0000, 2.3450, 3.0000, 4.9000
del
default
# str-> on constant (1)
"pi"
str->
-> stack should be 3.1415926535897932384626433832795028842
del
# str-> on constant (2)
"'pi' 'e'"
str->
-> stack should be 'pi', 'e'
del
# str-> on command (1)
"2 dup"
str->
-> stack should be 2, 2
del
# str-> on command (2)
"3.14 my_pi sto"
str->
-> stack size should be 0
my_pi
-> stack should be 3.14
del
# str-> on program
"<< -> n << n >> >>"
str->
-> stack should be << -> n << n >> >>
del
# add (1)
12 34 "one" "two" +
-> stack should be 12, 34, "onetwo"
del
# add (2)
"" "one" + "two" "" +
-> stack should be "one", "two"
del
# add (3)
"one" +
-> stack size should be 1
-> error should be 2
del
# chr (1)
"" 33 40 for i i chr + next
-> stack should be "!"#$%&'("
del
# chr (2)
-223 chr 0 chr
-> stack should be "!", "."
del
# num (1)
"!wait" num
-> stack should be 33
del
# num (2)
"" num
-> stack should be 0
del
# size (1)
"hello" size
-> stack should be 5
del
# size (2)
"" size
-> stack should be 0
del
# size, str->, ->str
"hello" str-> ->str size
-> stack should be 7
del
# pos (1)
"my big string" "big" pos
-> stack should be 4
del
# pos (2)
"my big string" "bOg" pos
-> stack should be 0
del
# pos (3)
"my big string" pos
-> error should be 2
-> stack size should be 1
del
# pos (4)
pos
-> error should be 2
del
# sub (1)
"my string to sub" 4 6
sub
-> stack should be "str"
del
# sub (2)
"my string to sub" -1 -2
sub
-> stack should be "m"
del
# sub (3)
"my string to sub" 0 0
sub
-> stack should be "m"
del
# sub (4)
"my string to sub" 6 5
sub
-> stack should be ""
del
# sub (5)
"my string to sub" 100 101
sub
-> stack should be ""
del
# sub (6)
"my string to sub" 14 100
sub
-> stack should be "sub"
del

307
test/060-string.md Normal file
View file

@ -0,0 +1,307 @@
# STRING
`default del`
## string entry
`"test string"`
-> stack size should be 1
-> stack should be "test string"
`del`
## string entry (2)
`"test string`
-> stack size should be 1
-> stack should be "test string"
`del`
## string entry (3)
`"`
-> stack size should be 1
-> stack should be ""
`del`
## ->str on real (1)
`1 ->str`
-> stack should be "1"
`del`
## ->str on real (2)
`1.234 ->str`
-> stack should be "1.234"
`del`
## ->str on real (3)
`1.234 20 fix ->str`
-> stack should be "1.23400000000000000000"
`del`
## ->str on symbol (1)
`toto ->str`
-> stack should be "'toto'"
`del`
## ->str on symbol (2)
`'toto' ->str`
-> stack should be "'toto'"
`del default`
## str-> on real (1)
`"1" str->`
-> stack should be 1
`del`
## str-> on real (2)
`"1 2.345 3 4.9" str->`
-> stack should be 1, 2.345, 3, 4.9
`del`
## str-> on real (3)
`4 fix "1 2.345 3 4.9" str->`
-> stack should be 1.0000, 2.3450, 3.0000, 4.9000
`del default`
## str-> on constant (1)
`"pi" str->`
-> stack should be 3.1415926535897932384626433832795028842
`del`
## str-> on constant (2)
`"'pi' 'e'" str->`
-> stack should be 'pi', 'e'
`del`
## str-> on command (1)
`"2 dup" str->`
-> stack should be 2, 2
`del`
## str-> on command (2)
`"3.14 my_pi sto" str->`
-> stack size should be 0
`my_pi`
-> stack should be 3.14
`del`
## str-> on program
`"<< -> n << n >> >>" str->`
-> stack should be «-> n << n >
`del`
## add (1)
`12 34 "one" "two" +`
-> stack should be 12, 34, "onetwo"
`del`
## add (2)
`"" "one" + "two" "" +`
-> stack should be "one", "two"
`del`
## add (3)
`"one" +`
-> stack size should be 1
-> error should be 2
`del`
## chr (1)
`"" 33 40 for i i chr + next`
-> stack should be "!"#$%&'("
`del`
## chr (2)
`-223 chr 0 chr`
-> stack should be "!", "."
`del`
## num (1)
`"!wait" num`
-> stack should be 33
`del`
## num (2)
`"" num`
-> stack should be 0
`del`
## size (1)
`"hello" size`
-> stack should be 5
`del`
## size (2)
`"" size`
-> stack should be 0
`del`
## size, str->, ->str
`"hello" str-> ->str size`
-> stack should be 7
`del`
## pos (1)
`"my big string" "big" pos`
-> stack should be 4
`del`
## pos (2)
`"my big string" "bOg" pos`
-> stack should be 0
`del`
## pos (3)
`"my big string" pos`
-> error should be 2
-> stack size should be 1
`del`
## pos (4)
`pos`
-> error should be 2
`del`
## sub (1)
`"my string to sub" 4 6`
`sub`
-> stack should be "str"
`del`
## sub (2)
`"my string to sub" -1 -2 sub`
-> stack should be ""
`del`
## sub (3)
`"my string to sub" 0 0 sub`
-> stack should be ""
`del`
## sub (4)
`"my string to sub" 6 5 sub`
-> stack should be ""
`del`
## sub (5)
`"my string to sub" 100 101 sub`
-> stack should be ""
`del`
## sub (6)
`"my string to sub" 14 100 sub`
-> stack should be "sub"
`del`

View file

@ -1,287 +1,505 @@
## TESTS
default del
# LOGICAL TESTS
`default del`
## and (1)
`and`
# and (1)
and
-> error should be 2
1 and
`1 and`
-> error should be 2
del
# and (2)
0 0 and
`del`
## and (2)
`0 0 and`
-> stack size should be 1
-> stack should be 0
del
# and (3)
1 0 and
`del`
## and (3)
`1 0 and`
-> stack size should be 1
-> stack should be 0
del
# and (4)
0 1 and
`del`
## and (4)
`0 1 and`
-> stack size should be 1
-> stack should be 0
del
# and (5)
1 1 and
`del`
## and (5)
`1 1 and`
-> stack size should be 1
-> stack should be 1
del
# or (1)
or
`del`
## or (1)
`or`
-> error should be 2
1 or
`1 or`
-> error should be 2
del
# or (2)
0 0 or
`del`
## or (2)
`0 0 or`
-> stack size should be 1
-> stack should be 0
del
# or (3)
0 1 or
`del`
## or (3)
`0 1 or`
-> stack size should be 1
-> stack should be 1
del
# or (4)
1 0 or
-> stack should be 1
`del`
## or (4)
`1 0 or`
-> stack size should be 1
-> stack should be 1
del
# or (5)
1 1 or
-> stack should be 1
`del`
## or (5)
`1 1 or`
-> stack size should be 1
-> stack should be 1
del
# xor (1)
xor
-> stack should be 1
`del`
## xor (1)
`xor`
-> error should be 2
1 xor
`1 xor`
-> error should be 2
del
# xor (2)
0 0 xor
`del`
## xor (2)
`0 0 xor`
-> stack size should be 1
-> stack should be 0
del
# xor (3)
0 1 xor
`del`
## xor (3)
`0 1 xor`
-> stack size should be 1
-> stack should be 1
del
# xor (4)
1 0 xor
`del`
## xor (4)
`1 0 xor`
-> stack size should be 1
-> stack should be 1
del
# xor (5)
1 1 xor
`del`
## xor (5)
`1 1 xor`
-> stack size should be 1
-> stack should be 0
del
# not (1)
not
`del`
## not (1)
`not`
-> error should be 2
1 not
`1 not`
-> stack size should be 1
-> stack should be 0
del
# not (2)
0 not
-> stack should be 0
`del`
## not (2)
`0 not`
-> stack size should be 1
-> stack should be 1
del
# >
0 0.1 >
-> stack should be 0
del
0.1 0 >
-> stack should be 1
del
1 1 >
-> stack should be 0
del
# >=
0 0.1 >=
-> stack should be 0
del
0.1 0 >=
-> stack should be 1
del
1 1 >=
-> stack should be 1
del
`del`
# <
0 0.1 <
-> stack should be 1
del
0.1 0 <
-> stack should be 0
del
1 1 <
-> stack should be 0
del
## >
# <=
0 0.1 <=
-> stack should be 1
del
0.1 0 <=
-> stack should be 0
del
1 1 <=
-> stack should be 1
del
`0 0.1 >`
# !=
0 0.1 !=
-> stack should be 1
del
1 1 !=
-> stack should be 0
del
# ==
0 0.1 ==
-> stack should be 0
del
1 1 ==
-> stack should be 1
del
`del`
# same
0 0.1 same
-> stack should be 0
del
1 1 same
-> stack should be 1
del
`0.1 0 >`
# complex !=
(1,2) (3,4) !=
-> stack should be 1
del
(1,2) (1,0) !=
-> stack should be 1
del
(1,2) (0,2) !=
-> stack should be 1
del
(1,2) (1,2) !=
-> stack should be 0
del
# complex ==
(1,2) (3,4) ==
-> stack should be 0
del
(1,2) (1,0) ==
-> stack should be 0
del
(1,2) (0,2) ==
-> stack should be 0
del
(1,2) (1,2) ==
-> stack should be 1
del
`del`
# complex same
(1,2) (3,4) same
-> stack should be 0
del
(1,2) (1,0) same
-> stack should be 0
del
(1,2) (0,2) same
-> stack should be 0
del
(1,2) (1,2) same
-> stack should be 1
del
`1 1 >`
# string >
"a" "b" >
-> stack should be 0
del
"b" "a" >
-> stack should be 1
del
"a" "a" >
-> stack should be 0
del
# string >=
"a" "b" >=
-> stack should be 0
del
"b" "a" >=
-> stack should be 1
del
"a" "a" >=
-> stack should be 1
del
`del`
# string <
"a" "b" <
-> stack should be 1
del
"b" "a" <
-> stack should be 0
del
"a" "a" <
-> stack should be 0
del
## >=
# string <=
"a" "b" <=
-> stack should be 1
del
"b" "a" <=
-> stack should be 0
del
"a" "a" <=
-> stack should be 1
del
`0 0.1 >=`
# string !=
"a" "b" !=
-> stack should be 1
del
"a" "a" !=
-> stack should be 0
del
# string ==
"a" "b" ==
-> stack should be 0
del
"a" "a" ==
-> stack should be 1
del
`del`
`0.1 0 >=`
# string same
"a" "b" same
-> stack should be 0
del
"a" "a" same
-> stack should be 1
del
`del`
`1 1 >=`
-> stack should be 1
`del`
## <
`0 0.1 <`
-> stack should be 1
`del`
`0.1 0 <`
-> stack should be 0
`del`
`1 1 <`
-> stack should be 0
`del`
## <=
`0 0.1 <=`
-> stack should be 1
`del`
`0.1 0 <=`
-> stack should be 0
`del`
`1 1 <=`
-> stack should be 1
`del`
## !=
`0 0.1 !=`
-> stack should be 1
`del`
`1 1 !=`
-> stack should be 0
`del`
## ==
`0 0.1 ==`
-> stack should be 0
`del`
`1 1 ==`
-> stack should be 1
`del`
## same
`0 0.1 same`
-> stack should be 0
`del`
`1 1 same`
-> stack should be 1
`del`
## complex !=
`(1,2) (3,4) !=`
-> stack should be 1
`del`
`(1,2) (1,0) !=`
-> stack should be 1
`del`
`(1,2) (0,2) !=`
-> stack should be 1
`del`
`(1,2) (1,2) !=`
-> stack should be 0
`del`
## complex ==
`(1,2) (3,4) ==`
-> stack should be 0
`del`
`(1,2) (1,0) ==`
-> stack should be 0
`del`
`(1,2) (0,2) ==`
-> stack should be 0
`del`
`(1,2) (1,2) ==`
-> stack should be 1
`del`
## complex same
`(1,2) (3,4) same`
-> stack should be 0
`del`
`(1,2) (1,0) same`
-> stack should be 0
`del`
`(1,2) (0,2) same`
-> stack should be 0
`del`
`(1,2) (1,2) same`
-> stack should be 1
`del`
## string >
`"a" "b" >`
-> stack should be 0
`del`
`"b" "a" >`
-> stack should be 1
`del`
`"a" "a" >`
-> stack should be 0
`del`
## string >=
`"a" "b" >=`
-> stack should be 0
`del`
`"b" "a" >=`
-> stack should be 1
`del`
`"a" "a" >=`
-> stack should be 1
`del`
## string <
`"a" "b" <`
-> stack should be 1
`del`
`"b" "a" <`
-> stack should be 0
`del`
`"a" "a" <`
-> stack should be 0
`del`
## string <=
`"a" "b" <=`
-> stack should be 1
`del`
`"b" "a" <=`
-> stack should be 0
`del`
`"a" "a" <=`
-> stack should be 1
`del`
## string !=
`"a" "b" !=`
-> stack should be 1
`del`
`"a" "a" !=`
-> stack should be 0
`del`
## string ==
`"a" "b" ==`
-> stack should be 0
`del`
`"a" "a" ==`
-> stack should be 1
`del`
## string same
`"a" "b" same`
-> stack should be 0
`del`
`"a" "a" same`
-> stack should be 1
`del`

View file

@ -1,226 +0,0 @@
## STORE
default del
# symbol entry
'test'
-> stack size should be 1
-> stack should be 'test'
del
# symbol entry (2)
'test
-> stack size should be 1
-> stack should be 'test'
del
# symbol entry (3)
''
-> stack size should be 1
-> stack should be ''
del
# symbol entry (4)
'
-> stack size should be 1
-> stack should be ''
del
# sto (1)
1 'a' sto
-> stack size should be 0
'a' a
-> stack should be 'a', 1
del
# sto (2)
2 'a' sto a
-> stack should be 2
del
# sto (3)
3 'b' sto b
-> stack should be 3
del
# rcl (1)
'a' rcl
-> stack should be 2
del
# rcl (2)
'b' rcl
-> stack should be 3
del
# rcl (2)
'var' rcl
-> error should be 5
-> stack should be 'var'
del
# sto in prog then rcl
3 << 'r' sto >> eval r 'r' rcl
-> stack should be 3, 3
del
# purge (1)
a 'a' purge a
-> stack should be 2, 'a'
del
# purge (2)
'a' purge
-> error should be 5
-> stack size should be 0
del
# purge (3)
3 'a' sto a 'a' purge
-> stack should be 3
del
# sto+ (1)
8 'a' sto
2 'a' sto+
a
-> stack should be 10
del
# sto+ (2)
2 'a' sto
'a' 2 sto+
a
-> stack should be 4
del
# sto+ (3)
'a' sto+
-> stack size should be 1
-> error should be 2
del
# sto+ (4)
3 'zz' sto+
-> stack should be 3, 'zz'
-> error should be 5
del
# sto- (1)
2 'a' sto
2 'a' sto-
a
-> stack should be 0
del
# sto- (2)
2 'a' sto
'a' 2 sto-
a
-> stack should be 0
del
# sto- (3)
'a' sto-
-> stack size should be 1
-> error should be 2
del
# sto- (4)
3 'zz' sto-
-> stack should be 3, 'zz'
-> error should be 5
del
# sto* (1)
2 'a' sto
3 'a' sto*
a
-> stack should be 6
del
# sto* (2)
2 'a' sto
'a' 3 sto*
a
-> stack should be 6
del
# sto* (3)
'a' sto*
-> stack size should be 1
-> error should be 2
del
# sto* (4)
3 'zz' sto*
-> stack should be 3, 'zz'
-> error should be 5
del
# sto/ (1)
2 'a' sto
6 'a' sto/
a
-> stack should be 3
del
# sto/ (2)
6 'a' sto
'a' 2 sto/
a
-> stack should be 3
del
# sto/ (2)
'a' sto/
-> stack size should be 1
-> error should be 2
del
# sto/ (4)
3 'zz' sto/
-> stack should be 3, 'zz'
-> error should be 5
del
# sneg (1)
7 'a' sto
'a' sneg
a
-> stack should be -7
del
# sneg (2)
sneg
-> error should be 2
del
# sneg (3)
'zz' sneg
-> error should be 5
-> stack should be 'zz'
del
# sinv (1)
4 'a' sto
'a' sinv
a
-> stack should be 0.25
del
# sinv (2)
sinv
-> error should be 2
del
# sinv (3)
'zz' sinv
-> error should be 5
-> stack should be 'zz'
del
# clusr
3.14 'abc' sto clusr 'abc' rcl
-> error should be 5
-> stack should be 'abc'
del

325
test/080-store.md Normal file
View file

@ -0,0 +1,325 @@
# STORE
`default del`
## symbol entry
`'test'`
-> stack size should be 1
-> stack should be 'test'
`del`
## symbol entry (2)
`'test`
-> stack size should be 1
-> stack should be 'test'
`del`
## symbol entry (3)
`''`
-> stack size should be 1
-> stack should be ''
`del`
## symbol entry (4)
`'`
-> stack size should be 1
-> stack should be ''
`del`
## sto (1)
`1 'a' sto`
-> stack size should be 0
`'a' a`
-> stack should be 'a', 1
`del`
## sto (2)
`2 'a' sto a`
-> stack should be 2
`del`
## sto (3)
`3 'b' sto b`
-> stack should be 3
`del`
## rcl (1)
`'a' rcl`
-> stack should be 2
`del`
## rcl (2)
`'b' rcl`
-> stack should be 3
`del`
## rcl (3)
`'var' rcl`
-> error should be 5
-> stack should be 'var'
`del`
## sto in prog then rcl
`3 << 'r' sto >> eval r 'r' rcl`
-> stack should be 3, 3
`del`
## purge (1)
`a 'a' purge a`
-> stack should be 2, 'a'
`del`
## purge (2)
`'a' purge`
-> error should be 5
-> stack size should be 0
`del`
## purge (3)
`3 'a' sto a 'a' purge`
-> stack should be 3
`del`
## sto+ (1)
`8 'a' sto 2 'a' sto+ a`
-> stack should be 10
`del`
## sto+ (2)
`'a' sto+`
-> stack size should be 1
-> error should be 2
`del`
## sto+ (3)
`3 'zz' sto+`
-> stack should be 3, 'zz'
-> error should be 5
`del`
## sto- (1)
`2 'a' sto`
`2 'a' sto-`
`a`
-> stack should be 0
`del`
## sto- (2)
`'a' sto-`
-> stack size should be 1
-> error should be 2
`del`
## sto- (3)
`3 'zz' sto-`
-> stack should be 3, 'zz'
-> error should be 5
`del`
## sto* (1)
`2 'a' sto`
`3 'a' sto*`
`a`
-> stack should be 6
`del`
## sto* (2)
`'a' sto*`
-> stack size should be 1
-> error should be 2
`del`
## sto* (3)
`3 'zz' sto*`
-> stack should be 3, 'zz'
-> error should be 5
`del`
## sto/ (1)
`2 'a' sto`
`4 'a' sto/`
`a`
-> stack should be 0.5
`del`
## sto/ (2)
`'a' sto/`
-> stack size should be 1
-> error should be 2
`del`
## sto/ (4)
`3 'zz' sto/`
-> stack should be 3, 'zz'
-> error should be 5
`del`
## sneg (1)
`7 'a' sto`
`'a' sneg`
`a`
-> stack should be -7
`del`
## sneg (2)
`sneg`
-> error should be 2
`del`
## sneg (3)
`'zz' sneg`
-> error should be 5
-> stack should be 'zz'
`del`
## sinv (1)
`4 'a' sto`
`'a' sinv`
`a`
-> stack should be 0.25
`del`
## sinv (2)
`sinv`
-> error should be 2
`del`
## sinv (3)
`'zz' sinv`
-> error should be 5
-> stack should be 'zz'
`del`
## clusr
`3.14 'abc' sto clusr 'abc' rcl`
-> error should be 5
-> stack should be 'abc'
`del`

View file

@ -1,56 +0,0 @@
## PROGRAM
default del
# program
<< 'one' >>
-> stack size should be 1
-> stack should be << 'one' >>
del
# program (2)
<< 'one' 2
-> stack size should be 1
-> stack should be << 'one' 2 >>
del
# program (3)
<<
-> stack size should be 1
-> stack should be << >>
del
# program (4)
<< << << <<
-> stack size should be 1
-> stack should be << << << << >> >> >> >>
del
# program (5)
<< -> n << n 2 * >> >>
-> stack size should be 1
-> stack should be << -> n << n 2 * >> >>
del
# program imbrication
<< 1 << 2 >> >>
-> stack should be << 1 << 2 >> >>
del
# program evaluation
<< 1 << 2 >> >> dup eval
-> stack should be << 1 << 2 >> >>, 1, << 2 >>
del
# program 1 arg
default
10
<< -> n << 0 1 n for i i 2 * inv + next >> >>
eval
-> stack should be 1.4644841269841269841269841269841269841
del
# program several args
0 1 10 << -> u0 u1 n << u0 u1 1 n start dup2 + rot drop next swap drop >> >> eval
eval
-> stack should be 89
del

142
test/090-program.md Normal file
View file

@ -0,0 +1,142 @@
# PROGRAM
`default del`
## program
`<< 'one' >>`
-> stack size should be 1
-> stack should be «'one'»
`del`
## program (2)
`<< 'one' 2`
-> stack size should be 1
-> stack should be «'one' 2»
`del`
## program (3)
`<<`
-> stack size should be 1
-> stack should be «»
`del`
## program (4)
`<< << << <<`
-> stack size should be 1
-> stack should be «<< << <<»
`del`
## program (5)
`<< -> n << n 2 * >> >>`
-> stack size should be 1
-> stack should be «-> n << n 2 * >
`del`
## program imbrication
`<< 1 << 2 >> >>`
-> stack should be «1 << 2 >
`del`
## program evaluation
`<< 1 << 2 >> >> dup eval`
-> stack should be «1 << 2 >>», 1, «2»
`del`
## program 1 arg
`default`
`10`
`<< -> n << 0 1 n for i i 2 * inv + next >> >>`
`eval`
-> stack should be 1.4644841269841269841269841269841269841
`del`
## nested programs
`0 1 10 << -> u0 u1 n << u0 u1 1 n start dup2 + rot drop next swap drop >> >> eval`
`eval`
-> stack should be 89
`del`
## fibo
```
«dup 1 > if then dup 1 - fibo swap 2 - fibo + else 1 == if then 1 else 0 end end» 'fibo' sto
7 fibo
13 == if then 'ok!' end
```
-> stack should be 'ok!'
`del`
## nested programs with local variables
```
100
10
«dup2 * -> a b c << a b + c / -> d << << -> sym << sym ->str " is " + sym rcl ->str + >> >> 'stringify' sto 'a' stringify 'b' stringify 'c' stringify 'd' stringify >> >>»
eval
```
-> stack should be "'a' is 100", "'b' is 10", "'c' is 1000", "'d' is 0.11"
`del`
## local variables multiple entries
`1 2 3 << -> a b c << "a is " a ->str + "b is " b ->str + "c is " c ->str + >> >> eval`
-> stack should be "a is 1", "b is 2", "c is 3"
`del`
## local variables separation (1)
`123 'n' sto 1 << -> n << "n is " n ->str + >> >> eval`
-> stack should be "n is 1"
`del`
## local variables separation (2)
`123 'n' sto 2 << -> n << n sq << -> n << "n is " n ->str + >> >> >> eval >> eval`
-> stack should be "n is 4"
`del`

View file

@ -1,758 +0,0 @@
## COMPLEX
default del
# entry (1)
(1,2) ( 1,2) (1 ,2) (1, 2) (1,2 )
-> stack should be (1,2), (1,2), (1,2), (1,2), (1,2)
del
# entry (2)
(1.3,2.444555
-> stack should be (1.3,2.444555)
del
# entry (3)
(1,
-> stack should be (1,0)
del
# entry (4)
(nan,+inf)
-> stack should be (nan,inf)
del
# entry (5)
(0x1234,0x10.10)
-> stack should be (0x1.234p+12,0x1.01p+4)
del
# entry (6)
(0b11,0b101)
-> stack should be (3,5)
del
# add (1)
(1.2,2.3) (1,2) +
-> stack should be (2.2,4.3)
del
# add (2)
(1.2,2.3) (1,2)+
-> stack should be (2.2,4.3)
del
# add (3)
(1.2,2.3) 3+
-> stack should be (4.2,2.3)
del
# add (4)
3 (1.2,2.3)+
-> stack should be (4.2,2.3)
del
# sub (1)
(1.2,2.3) (2,2) -
-> stack should be (-0.8,0.3)
del
# sub (2)
(1.2,2.3) (1,2)-
-> stack should be (0.2,0.3)
del
# sub (3)
(1.2,2.3) 1-
-> stack should be (0.2,2.3)
del
# sub (4)
1 (1.2,2.3) -
-> stack should be (-0.2,2.3)
del
# mul (1)
(1,2) (3,4) *
-> stack should be (-5,10)
del
# mul (2)
(3,4) (1,2) *
-> stack should be (-5,10)
del
# mul (3)
(3,4) 2 *
-> stack should be (6,8)
del
# mul (3)
2 (3,4) *
-> stack should be (6,8)
del
# div (1)
(1,2) (3,4) /
-> stack should be (0.44,0.08)
del
# div (2)
(1,2) 2 /
-> stack should be (0.5,1)
del
# div (3)
2 (3,4) /
-> stack should be (0.24,-0.32)
del
# re (1)
(1.2,3.4) re
-> stack should be 1.2
del
# re (2)
3 re
-> error should be 3
del
# re (3)
re
-> error should be 2
del
# im (1)
(1.2,3.4) im
-> stack should be 3.4
del
# im (2)
3 re
-> error should be 3
del
# im (3)
re
-> error should be 2
del
# chs
(3.14,6.28) chs
-> stack should be (-3.14,-6.28)
del
# neg
(-3.14,-6.28) neg
-> stack should be (3.14,6.28)
del
# inv (1)
(2,4) inv
-> stack should be (0.1,-0.2)
del
# inv (2)
(0.1,-0.2) inv
-> stack should be (2,4)
del
# abs
(3,4) abs
-> stack should be 5
del
# sign (1)
(1,0) sign
-> stack should be (1,0)
del
# sign (2)
(0,1) sign
-> stack should be (0,1)
del
# sign (3)
(3,-4) sign
-> stack should be (0.6,-0.8)
del
# sq (1)
(12,10) sq
-> stack should be (44,240)
del
# sqr (1)
(12,10) sq
-> stack should be (44,240)
del
# arg (1)
(1,1) arg pi 4 / ==
-> stack should be 1
del
# arg (2)
1000 prec
(-1,1) arg pi 3 * 4 / ==
-> stack should be 1
default
del
# arg (3)
1000 prec
(1,-1) arg pi chs 4 / ==
-> stack should be 1
default
del
# arg (4)
1000 prec
(-1,-1) arg pi -3 * 4 / ==
-> stack should be 1
default
del
# arg (5)
1000 prec
(1,0) arg 0 ==
-> stack should be 1
default
del
# arg (6)
1000 prec
(0,1) arg pi 2 / ==
-> stack should be 1
default
del
# arg (7)
1000 prec
(-1,0) arg pi ==
-> stack should be 1
default
del
# arg (8)
1000 prec
(0,-1) arg pi neg 2 / ==
-> stack should be 1
default
del
# conj
(1,2) conj dup conj
-> stack should be (1,-2), (1,2)
del
# r->c (1)
1 2 r->c
-> stack should be (1,2)
del
# r->c (2)
1 r->c
-> error should be 2
del
# r->c (3)
r->c
-> error should be 2
del
# r->c (3)
'1' '2' r->c
-> error should be 3
del
# c->r (1)
(1,2) c->r
-> stack should be 1, 2
del
# c->r (2)
c->r
-> error should be 2
del
# c->r (3)
'4' c->r
-> error should be 3
del
# r->p (1)
6 fix
(1,2) r->p
-> stack should be (2.236068,1.107149)
del
# r->p (2)
19 fix
1 r->p
-> error should be 3.000000
del
# r->p (3)
r->p
-> error should be 2
del
# r->p (4)
'1' '2' r->p
-> error should be 3
del
# ^ (1)
6 fix
(1,2) 2 ^
-> stack should be (-3.000000,4.000000)
del
# ^ (2)
(1,2) 4 ^
-> stack should be (-7.000000,-24.000000)
del
# ^ (3)
-3 .2 ^
-> stack should be (1.007818,0.732222)
del
# sqrt (1)
(3,4) sqrt
-> stack should be (2.000000,1.000000)
del
# sqrt (2)
-3 sqrt
-> stack should be (-0.000000,1.732051)
del
# sin (1)
(1,2) sin
-> stack should be (3.165779,1.959601)
del
# sin (2)
(1,-2) sin
-> stack should be (3.165779,-1.959601)
del
# sin (3)
(-1,-2) sin
-> stack should be (-3.165779,-1.959601)
del
# sin (4)
(-1,2) sin
-> stack should be (-3.165779,1.959601)
del
# asin (1)
(1,2) asin
-> stack should be (0.427079,1.528571)
del
# asin (2)
(1,-2) asin
-> stack should be (0.427079,-1.528571)
del
# asin (3)
(-1,-2) asin
-> stack should be (-0.427079,-1.528571)
del
# asin (4)
(-1,2) asin
-> stack should be (-0.427079,1.528571)
del
# cos (1)
(1,2) cos
-> stack should be (2.032723,-3.051898)
del
# cos (2)
(1,-2) cos
-> stack should be (2.032723,3.051898)
del
# cos (3)
(-1,-2) cos
-> stack should be (2.032723,-3.051898)
del
# cos (4)
(-1,2) sin
-> stack should be (-3.165779,1.959601)
del
# acos (1)
(1,2) acos
-> stack should be (1.143718,-1.528571)
del
# acos (2)
(1,-2) acos
-> stack should be (1.143718,1.528571)
del
# acos (3)
(-1,-2) acos
-> stack should be (1.997875,1.528571)
del
# acos (4)
(-1,2) acos
-> stack should be (1.997875,-1.528571)
del
# tan (1)
(1,2) tan
-> stack should be (0.033813,1.014794)
del
# tan (2)
(1,-2) tan
-> stack should be (0.033813,-1.014794)
del
# tan (3)
(-1,-2) tan
-> stack should be (-0.033813,-1.014794)
del
# tan (4)
(-1,2) tan
-> stack should be (-0.033813,1.014794)
del
# atan (1)
(1,2) atan
-> stack should be (1.338973,0.402359)
del
# atan (2)
(1,-2) atan
-> stack should be (1.338973,-0.402359)
del
# atan (3)
(-1,-2) atan
-> stack should be (-1.338973,-0.402359)
del
# atan (4)
(-1,2) atan
-> stack should be (-1.338973,0.402359)
del
# ln (1)
(1,2) ln
-> stack should be (0.804719,1.107149)
del
# ln (2)
(1,-2) ln
-> stack should be (0.804719,-1.107149)
del
# ln (3)
(-1,-2) ln
-> stack should be (0.804719,-2.034444)
del
# ln (4)
(-1,2) ln
-> stack should be (0.804719,2.034444)
del
# lnp1 (1)
(1,2) lnp1
(1,2) 1 + ln ==
-> stack should be 1.000000
del
# lnp1 (2)
(1,-2) lnp1
(1,-2) 1 + ln ==
-> stack should be 1.000000
del
# lnp1 (3)
(-1,-2) lnp1
(-1,-2) 1 + ln ==
-> stack should be 1.000000
del
# lnp1 (4)
(-1,2) lnp1
(-1,2) 1 + ln ==
-> stack should be 1.000000
del
# log (1)
(1,2) log
-> stack should be (0.804719,1.107149)
del
# log (2)
(1,-2) log
-> stack should be (0.804719,-1.107149)
del
# log (3)
(-1,-2) log
-> stack should be (0.804719,-2.034444)
del
# log (4)
(-1,2) log
-> stack should be (0.804719,2.034444)
del
# log10 (1)
(1,2) log10
-> stack should be (0.349485,0.480829)
del
# log10 (2)
(1,-2) log10
-> stack should be (0.349485,-0.480829)
del
# log10 (3)
(-1,-2) log10
-> stack should be (0.349485,-0.883548)
del
# log10 (4)
(-1,2) log10
-> stack should be (0.349485,0.883548)
del
# log2 (1)
(1,2) log2
-> stack should be (1.160964,1.597278)
del
# log2 (2)
(1,-2) log2
-> stack should be (1.160964,-1.597278)
del
# log2 (3)
(-1,-2) log2
-> stack should be (1.160964,-2.935082)
del
# log2 (4)
(-1,2) log2
-> stack should be (1.160964,2.935082)
del
# exp (1)
(1,2) exp
-> stack should be (-1.131204,2.471727)
del
# exp (2)
(1,-2) exp
-> stack should be (-1.131204,-2.471727)
del
# exp (3)
(-1,-2) exp
-> stack should be (-0.153092,-0.334512)
del
# exp (4)
(-1,2) exp
-> stack should be (-0.153092,0.334512)
del
# expm (1)
(1,2) expm
(1,2) exp 1 - ==
-> stack should be 1.000000
del
# expm (2)
(1,-2) expm
(1,-2) exp 1 - ==
-> stack should be 1.000000
del
# expm (3)
(-1,-2) expm
(-1,-2) exp 1 - ==
-> stack should be 1.000000
del
# expm (4)
(-1,2) expm
(-1,2) exp 1 - ==
-> stack should be 1.000000
del
# alog2 (1)
(1,2) alog2
-> stack should be (0.366914,1.966055)
del
# alog2 (2)
(1,-2) alog2
-> stack should be (0.366914,-1.966055)
del
# alog2 (3)
(-1,-2) alog2
-> stack should be (0.091728,-0.491514)
del
# alog2 (4)
(-1,2) alog2
-> stack should be (0.091728,0.491514)
del
# alog10 (1)
(1,2) alog10
-> stack should be (-1.070135,-9.942576)
del
# alog10 (2)
(1,-2) alog10
-> stack should be (-1.070135,9.942576)
del
# alog10 (3)
(-1,-2) alog10
-> stack should be (-0.010701,0.099426)
del
# alog10 (4)
(-1,2) alog10
-> stack should be (-0.010701,-0.099426)
del
# sinh (1)
(1,2) sinh
-> stack should be (-0.489056,1.403119)
del
# sinh (2)
(1,-2) sinh
-> stack should be (-0.489056,-1.403119)
del
# sinh (3)
(-1,-2) sinh
-> stack should be (0.489056,-1.403119)
del
# sinh (4)
(-1,2) sinh
-> stack should be (0.489056,1.403119)
del
# asinh (1)
(1,2) asinh
-> stack should be (1.469352,1.063440)
del
# asinh (2)
(1,-2) asinh
-> stack should be (1.469352,-1.063440)
del
# asinh (3)
(-1,-2) asinh
-> stack should be (-1.469352,-1.063440)
del
# asinh (4)
(-1,2) asinh
-> stack should be (-1.469352,1.063440)
del
# cosh (1)
(1,2) cosh
-> stack should be (-0.489056,1.403119)
del
# cosh (2)
(1,-2) cosh
-> stack should be (-0.489056,-1.403119)
del
# cosh (3)
(-1,-2) cosh
-> stack should be (0.489056,-1.403119)
del
# cosh (4)
(-1,2) cosh
-> stack should be (0.489056,1.403119)
del
# acosh (1)
(1,2) acosh
-> stack should be (1.528571,1.143718)
del
# acosh (2)
(1,-2) acosh
-> stack should be (1.528571,-1.143718)
del
# acosh (3)
(-1,-2) acosh
-> stack should be (-1.528571,1.997875)
del
# acosh (4)
(-1,2) acosh
-> stack should be (-1.528571,-1.997875)
del
# tanh (1)
(1,2) tanh
-> stack should be (0.564133,-0.217934)
del
# tanh (2)
(1,-2) tanh
-> stack should be (0.564133,0.217934)
del
# tanh (3)
(-1,-2) tanh
-> stack should be (-0.564133,0.217934)
del
# tanh (4)
(-1,2) tanh
-> stack should be (-0.564133,-0.217934)
del
# atanh (1)
(1,2) atanh
-> stack should be (0.173287,1.178097)
del
# atanh (2)
(1,-2) atanh
-> stack should be (0.173287,-1.178097)
del
# atanh (3)
(-1,-2) atanh
-> stack should be (-0.173287,-1.178097)
del
# atanh (4)
(-1,2) atanh
-> stack should be (-0.173287,1.178097)
del

535
test/100-complex.md Normal file
View file

@ -0,0 +1,535 @@
# COMPLEX
`default del`
## entry (1)
`(1,2) ( 1,2) (1 ,2) (1, 2) (1,2 )`
-> stack should be (1,2), (1,2), (1,2), (1,2), (1,2)
`del`
## entry (2)
`(1.3,2.444555`
-> stack should be (1.3,2.444555)
`del`
## entry (3)
`(1,`
-> stack should be '(1,'
`del`
## entry (4)
`(nan,+inf)`
-> stack should be (nan,inf)
`del`
## entry (5)
`(0x1234,0x10.10)`
-> stack should be (0x1.234p+12,0x1.01p+4)
`del`
## entry (6)
`(0b11,0b101)`
-> stack should be (0b11,0b101)
`del`
## add (1)
`(1.2,2.3) (1,2) +`
-> stack should be (2.2,4.3)
`del`
## add (2)
`(1.2,2.3) 3 +`
-> stack should be (4.2,2.3)
`del`
## add (3)
`3 (1.2,2.3) +`
-> stack should be (4.2,2.3)
`del`
## sub (1)
`(1.2,2.3) (2,2) -`
-> stack should be (-0.8,0.3)
`del`
## sub (2)
`(1.2,2.3) 1 -`
-> stack should be (0.2,2.3)
`del`
## sub (3)
`1 (1.2,2.3) -`
-> stack should be (-0.2,-2.3)
`del`
## mul (1)
`(1,2) (3,4) *`
-> stack should be (-5,10)
`del`
## mul (2)
`(3,4) (1,2) *`
-> stack should be (-5,10)
`del`
## mul (3)
`(3,4) 2 *`
-> stack should be (6,8)
`del`
## mul (3)
`2 (3,4) *`
-> stack should be (6,8)
`del`
## div (1)
`(1,2) (3,4) /`
-> stack should be (0.44,0.08)
`del`
## div (2)
`(1,2) 2 /`
-> stack should be (0.5,1)
`del`
## div (3)
`2 (3,4) /`
-> stack should be (0.24,-0.32)
`del`
## re (1)
`(1.2,3.4) re`
-> stack should be 1.2
`del`
## re (2)
`3 re`
-> error should be 3
`del`
## re (3)
`re`
-> error should be 2
`del`
## im (1)
`(1.2,3.4) im`
-> stack should be 3.4
`del`
## im (2)
`3 re`
-> error should be 3
`del`
## im (3)
`re`
-> error should be 2
`del`
## chs
`(3.14,6.28) chs`
-> stack should be (-3.14,-6.28)
`del`
## neg
`(-3.14,-6.28) neg`
-> stack should be (3.14,6.28)
`del`
## inv (1)
`(2,4) inv`
-> stack should be (0.1,-0.2)
`del`
## inv (2)
`(0.1,-0.2) inv`
-> stack should be (2,4)
`del`
## abs
`(3,4) abs`
-> stack should be 5
`del`
## sign (1)
`(1,0) sign`
-> stack should be (1,0)
`del`
## sign (2)
`(0,1) sign`
-> stack should be (0,1)
`del`
## sign (3)
`(3,-4) sign`
-> stack should be (0.6,-0.8)
`del`
## sq (1)
`(12,10) sq`
-> stack should be (44,240)
`del`
## sqr (1)
`(12,10) sq`
-> stack should be (44,240)
`del`
## arg (1)
`(1,1) arg pi 4 / ==`
-> stack should be 1
`del`
## arg (2)
`1000 prec`
`(-1,1) arg pi 3 * 4 / ==`
-> stack should be 1
`default del`
## arg (3)
`1000 prec`
`(1,-1) arg pi chs 4 / ==`
-> stack should be 1
`default del`
## arg (4)
`1000 prec`
`(-1,-1) arg pi -3 * 4 / ==`
-> stack should be 1
`default del`
## arg (5)
`1000 prec`
`(1,0) arg 0 ==`
-> stack should be 1
`default del`
## arg (6)
`1000 prec`
`(0,1) arg pi 2 / ==`
-> stack should be 1
`default del`
## arg (7)
`1000 prec`
`(-1,0) arg pi ==`
-> stack should be 1
`default del`
## arg (8)
`1000 prec`
`(0,-1) arg pi neg 2 / ==`
-> stack should be 1
`default del`
## conj
`(1,2) conj dup conj`
-> stack should be (1,-2), (1,2)
`del`
## r->c (1)
`1 2 r->c`
-> stack should be (1,2)
`del`
## r->c (2)
`0x12 0b1101 r->c`
-> stack should be (0x12,0b1101)
`del`
## r->c error (1)
`1 r->c`
-> error should be 2
`del`
## r->c error (2)
`r->c`
-> error should be 2
`del`
## r->c error (3)
`'1' '2' r->c`
-> error should be 3
`del`
## c->r (1)
`(1,2) c->r`
-> stack should be 1, 2
`del`
## c->r (2)
`(0x12,0b1101) c->r`
-> stack should be 0x12, 0b1101
`del`
## c->r error (2)
`c->r`
-> error should be 2
`del`
## c->r error (2)
`'4' c->r`
-> error should be 3
`del`
## r->p (1)
`6 fix`
`(1,2) r->p`
-> stack should be (2.236068,1.107149)
`del`
## r->p (2)
`19 fix`
`1 r->p`
-> error should be 3.000000
`del`
## r->p (3)
`r->p`
-> error should be 2
`del`
## r->p (4)
`'1' '2' r->p`
-> error should be 3
`del`
## ^ (1)
`6 fix`
`(1,2) 2 ^`
-> stack should be (-3.000000,4.000000)
`del`
## ^ (2)
`(1,2) 4 ^`
-> stack should be (-7.000000,-24.000000)
`del`
## ^ (3)
`-3 .2 ^`
-> stack should be (1.007818,0.732222)
`del`
## ^ (4)
`-3 (1,2) ^`
-> stack should be (0.003284,-0.004539)
`del`
## ^ (5)
`(1,2) (1,2) ^`
-> stack should be (-0.222517,0.100709)
`del`
## sqrt (1)
`(3,4) sqrt`
-> stack should be (2.000000,1.000000)
`del`
## sqrt (2)
`-3 sqrt`
-> stack should be (0.000000,1.732051)
`del`

View file

@ -1,196 +0,0 @@
## fix entry
default del
# table for fixed entry, 4 fix output >= 0
4 fix
1 0.01 0.0001 0.00006 0.00004 0 0.012 0.001256 100 100.001 100.00006 100.00004 12345678910111213.12355
-> stack should be 1.0000, 0.0100, 0.0001, 0.0001, 0.0000, 0.0000, 0.0120, 0.0013, 100.0000, 100.0010, 100.0001, 100.0000, 12345678910111213.1236
del default
# table for fixed entry, 4 fix output <= 0
4 fix
-1 -0.01 -0.0001 -0.00006 -0.00004 -0 -0.012 -0.001256 -100 -100.001 -100.00006 -100.00004 -12345678910111213.12355
-> stack should be -1.0000, -0.0100, -0.0001, -0.0001, -0.0000, -0.0000, -0.0120, -0.0013, -100.0000, -100.0010, -100.0001, -100.0000, -12345678910111213.1236
del default
# table for sci entry, 4 fix output >= 0
4 fix
0e100 1e0 1.e0 1.001e0 1.e-3 1.e-4 6.e-5 4.e-5 1.00001e2 1.0000006e2 1.0000004e2 1234.5678917e2
-> stack should be 0.0000, 1.0000, 1.0000, 1.0010, 0.0010, 0.0001, 0.0001, 0.0000, 100.0010, 100.0001, 100.0000, 123456.7892
del default
# table for sci entry, 4 fix output <= 0
4 fix
-0e100 -1e0 -1.e0 -1.001e0 -1.e-3 -1.e-4 -6.e-5 -4.e-5 -1.00001e2 -1.0000006e2 -1.0000004e2 -1234.5678917e2
-> stack should be -0.0000, -1.0000, -1.0000, -1.0010, -0.0010, -0.0001, -0.0001, -0.0000, -100.0010, -100.0001, -100.0000, -123456.7892
del default
# table for singularity
4 fix
nan @nan@ -nan inf -inf @inf@ -@inf@
-> stack should be nan, nan, nan, inf, -inf, inf, -inf
del default
# some strange behaviour (1)
0 fix
1 2 / dup +
-> stack should be 1
del default
# some strange behaviour (2)
1 fix
0.6
-> stack should be 0.6
del default
# some strange behaviour (3)
0 fix
110.6 0.6
-> stack should be 111, 1
del default
# hex (1)
0x4000000000000
-> stack should be 0x4000000000000
del default
# hex (2)
2 50 ^ hex
-> stack should be 0x4000000000000
del default
# hex (3)
12.34 hex
-> stack should be 0xc
del default
# dec (1)
2 50 ^
-> stack should be 1125899906842624
del default
# dec (2)
0x4000000000000 dec
-> stack should be 1125899906842624
del default
# dec (3)
12.34 dec
-> stack should be 12.34
del default
# bin (1)
0b100000000000000000000000000000000000000000000000000 bin
-> stack should be 0b100000000000000000000000000000000000000000000000000
del default
# bin (2)
2 50 ^ bin
-> stack should be 0b100000000000000000000000000000000000000000000000000
del default
# bin (3)
0x4000000000000 bin
-> stack should be 0b100000000000000000000000000000000000000000000000000
del default
# bin (4)
12.34 bin
-> stack should be 0b1100
del default
# base entry (1)
3b111 dup dec
-> stack should be 3b111, 13
del default
# base entry (2)
3b114
-> stack should be 3b11, 4
del default
# base entry (3)
1b0
-> stack should be 1, 'b0'
del default
# base entry (4)
62b20 dup dec
-> stack should be 62b20, 124
del default
# base entry (5)
63b20
-> stack should be 63, 'b20'
del default
# base entry (6)
2b11001100 0b11001100 ==
-> stack should be 2b1
del default
# base entry (7)
10b1234 1234 ==
-> stack should be 10b1
del default
# base entry (8)
16b1234 0x1234 ==
-> stack should be 16b1
del default
# base display (1)
2 62 for i 62 i base next
-> stack should be 2b111110, 3b2022, 4b332, 5b222, 6b142, 7b116, 8b76, 9b68, 10b62, 11b57, 12b52, 13b4a, 14b46, 15b42, 16b3e, 17b3b, 18b38, 19b35, 20b32, 21b2k, 22b2i, 23b2g, 24b2e, 25b2c, 26b2a, 27b28, 28b26, 29b24, 30b22, 31b20, 32b1u, 33b1t, 34b1s, 35b1r, 36b1q, 37b1P, 38b1O, 39b1N, 40b1M, 41b1L, 42b1K, 43b1J, 44b1I, 45b1H, 46b1G, 47b1F, 48b1E, 49b1D, 50b1C, 51b1B, 52b1A, 53b19, 54b18, 55b17, 56b16, 57b15, 58b14, 59b13, 60b12, 61b11, 62b10
del default
# base display (2)
2 62 for i i 62 base next
-> stack should be 62b2, 62b3, 62b4, 62b5, 62b6, 62b7, 62b8, 62b9, 62bA, 62bB, 62bC, 62bD, 62bE, 62bF, 62bG, 62bH, 62bI, 62bJ, 62bK, 62bL, 62bM, 62bN, 62bO, 62bP, 62bQ, 62bR, 62bS, 62bT, 62bU, 62bV, 62bW, 62bX, 62bY, 62bZ, 62ba, 62bb, 62bc, 62bd, 62be, 62bf, 62bg, 62bh, 62bi, 62bj, 62bk, 62bl, 62bm, 62bn, 62bo, 62bp, 62bq, 62br, 62bs, 62bt, 62bu, 62bv, 62bw, 62bx, 62by, 62bz, 62b10
del default
# base display (3)
100 dup 3 base ==
13455600 dup 5 base ==
55756 dup 17 base ==
2345321 dup 62 base ==
-> stack should be 1, 1, 1, 1
del default
# base display (4)
100 18 base dup 3 base == dec
13455600 55 base dup 5 base == dec
55756 9 base dup 17 base == dec
2345321 57 base dup 62 base == dec
-> stack should be 1, 1, 1, 1
del default
# negative base numbers (1)
1000 hex neg
-> stack should be -0x3e8
del default
# negative base numbers (2)
1000 7 base neg
-> stack should be -7b2626
del default
# negative base numbers (3)
1000 bin neg
-> stack should be -0b1111101000
del default
# negative base numbers (4)
-0b1111101000 3 base
-> stack should be -3b1101001
del default
# inf should not be based-represented
-1 bin 0 bin / 1 3 base 0 3 base /
-> stack should be -inf, inf
del default
# nan should not be based-represented
-0 bin 0 bin / 0 3 base 0 3 base /
-> stack should be nan, nan
del default

View file

@ -1,25 +1,41 @@
## date and time
default del
# DATE AND TIME
`default del`
## date
`date type`
# date
date type
-> error should be 0
-> stack should be "number"
del
# time
time type
-> stack should be "number"
`del`
## time
`time type`
-> error should be 0
-> stack should be "number"
del
# ticks (1)
ticks type
-> stack should be "number"
`del`
## ticks (1)
`ticks type`
-> error should be 0
-> stack should be "number"
del
# ticks (2)
ticks ticks - 0 <=
-> stack should be "number"
`del`
## ticks (2)
`ticks ticks - 0 <=`
-> stack should be 1
del
`del`

105
test/120-trig.md Normal file
View file

@ -0,0 +1,105 @@
# TRIGONOMETRY
`del default 6 fix 128 prec`
## pi
`pi`
-> stack should be 3.141593
`del`
## d->r
`180 d->r pi ==`
-> stack should be 1.000000
`del`
## r->d
`pi r->d 180 ==`
-> stack should be 1.000000
`del`
## sin asin
`0 sin pi 2 / sin pi 6 / sin`
`0 asin 0 == 1 asin pi 2 / == 0.5 asin pi 6 / ==`
-> stack should be 0.000000, 1.000000, 0.500000, 1.000000, 1.000000, 1.000000
`del`
## cos acos
`0 cos pi 3 / cos`
`1 acos 0 == 0.5 acos pi 3 / ==`
-> stack should be 1.000000, 0.500000, 1.000000, 1.000000
`del`
## tan atan
`pi 4 / tan 1 == 1 atan pi 4 / ==`
-> stack should be 1.000000, 1.000000
`del`
## sin asin
`(1,2) sin (3.165779,1.959601) asin`
-> stack should be (3.165779,1.959601), (1.000000,2.000000)
`del`
## cos acos
`(1,2) cos (2.032723,-3.051898) acos`
-> stack should be (2.032723,-3.051898), (1.000000,2.000000)
`del`
## tan atan
`(1,2) tan (0.033813,1.014794) atan`
-> stack should be (0.033813,1.014794), (1.000004,1.999996)
`del`
## sin asin cos acos tan atan error
`'ok' sin`
-> error should be 3
`asin`
-> error should be 3
`cos`
-> error should be 3
`acos`
-> error should be 3
`tan`
-> error should be 3
`atan`
-> error should be 3
`del`

219
test/130-logs.md Normal file
View file

@ -0,0 +1,219 @@
# LOGARITHMS
`del default 6 fix`
## euler constant
`e`
-> stack should be 0.577216
`del`
## ln log exp
`2 ln exp`
`2 log exp`
-> stack should be 2.000000, 2.000000
`del`
## lnp1 expm
`4 lnp1 expm`
-> stack should be 4.000000
`del`
## log10 alog10 exp10
`5 log10 alog10`
`5 log10 exp10`
-> stack should be 5.000000, 5.000000
`del`
## log2 alog2 exp2
`6 log2 alog2`
`6 log2 exp2`
-> stack should be 6.000000, 6.000000
`del`
## sinh asinh
`1 sinh asinh`
-> stack should be 1.000000
`del`
## cosh acosh
`1.1 cosh acosh`
-> stack should be 1.100000
`del`
## tanh atanh
`1.2 tanh atanh`
-> stack should be 1.200000
`del`
## complex ln exp
`(-1,-2) ln exp`
-> stack should be (-1.000000,-2.000000)
`del`
## complex log exp
`(1,2) log exp`
-> stack should be (1.000000,2.000000)
`del`
## complex lnp1 expm
`(1,2) lnp1 expm`
-> stack should be (1.000000,2.000000)
`del`
## complex log10 alog10
`(1,-2) log10 alog10`
-> stack should be (1.000000,-2.000000)
`del`
## complex log2 alog2
`(-1,-2) log2 alog2`
-> stack should be (-1.000000,-2.000000)
`del`
## complex sinh asinh
`(1,1.5) sinh asinh`
-> stack should be (1.000000,1.500000)
`del`
## complex cosh acosh
`(1,2) cosh acosh`
-> stack should be (1.000000,2.000000)
`del`
## complex tanh atanh
`(1,1.5) tanh atanh`
-> stack should be (1.000000,1.500000)
`del`
## ln log lnp1 exp expm error
`'ok' ln`
-> error should be 3
`log`
-> error should be 3
`lnp1`
-> error should be 3
`exp`
-> error should be 3
`expm`
-> error should be 3
`del`
## log10 alog10 exp10 log2 alog2 exp2 error
`'ok' log10`
-> error should be 3
`alog10`
-> error should be 3
`exp10`
-> error should be 3
`log2`
-> error should be 3
`alog2`
-> error should be 3
`exp2`
-> error should be 3
`del`
## sinh asinh cosh acosh tanh atanh error
`'ok' sinh`
-> error should be 3
`asinh`
-> error should be 3
`cosh`
-> error should be 3
`acosh`
-> error should be 3
`tanh`
-> error should be 3
`atanh`
-> error should be 3
`del`

View file

@ -1,41 +0,0 @@
## MANUAL TESTS
default del
# GENERAL help - please type help and verify help is shown
-> error should be 0
del
# GENERAL h - please type h and verify help is shown
-> error should be 0
del
# GENERAL ? - please type ? and verify help is shown
-> error should be 0
del
# GENERAL quit - please type quit and verify rpn exits
-> error should be 0
del
# GENERAL q - please type q and verify rpn exits
-> error should be 0
del
# GENERAL exit - please type exit and verify rpn exits
-> error should be 0
del
# STORE vars - please type vars and verify variables are shown
-> error should be 0
del
# STORE edit - please type edit and verify last line comes under the caret
-> error should be 0
del
# HISTORY - please type history and verify rpn history is shown
-> error should be 0
# MULTILINE EDITING - please type a prog like 1 3 for i[ALT-ENTER]i sq[ALT-ENTER]next[ENTER] and verify output
-> error should be 0
del

25
test/999-manual-tests.md Normal file
View file

@ -0,0 +1,25 @@
# MANUAL TESTS
`default del`
## GENERAL help - please type help, h or ? and verify help is shown
-> error should be 0
## STORE vars - please type vars and verify variables are shown
`vars`
-> error should be 0
## STORE edit - please type edit and verify last line comes under the caret
-> error should be 0
## HISTORY - please type history and verify rpn history is shown
-> error should be 0
## MULTILINE EDITING - please type a prog like 1 3 for i[ALT-ENTER]i sq[ALT-ENTER]next[ENTER] and verify output
-> error should be 0

24
test/all.md Normal file
View file

@ -0,0 +1,24 @@
# Chaining all tests
@include 005-test-framework.md
@include 010-mode.md
@include 020-general.md
@include 021-parse-string.md
@include 022-parse-symbol.md
@include 023-parse-number.md
@include 024-parse-complex.md
@include 025-parse-other.md
@include 026-parse-program.md
@include 027-base-entry.md
@include 030-branch.md
@include 040-stack.md
@include 050-real.md
@include 060-string.md
@include 070-test.md
@include 080-store.md
@include 090-program.md
@include 100-complex.md
@include 110-time.md
@include 120-trig.md
@include 130-logs.md
@include 999-manual-tests.md

View file

@ -1,13 +0,0 @@
#include 01-mode.txt
#include 02-general.txt
#include 03-branch.txt
#include 04-stack.txt
#include 05-real.txt
#include 06-string.txt
#include 07-test.txt
#include 08-store.txt
#include 09-program.txt
#include 10-complex.txt
#include 11-base-entry.txt
#include 12-time.txt
#include 99-manual-tests.txt

45
test/mem_test.sh Executable file
View file

@ -0,0 +1,45 @@
#!/bin/bash
rpn=../build/rpn
FG_RED="\033[0;31m"
FG_GREEN="\033[0;32m"
COLOR_OFF="\033[0m"
failed=0
function checkmem {
echo -n "${*}"
valgrind ${rpn} "${*}" 2>&1 | grep "in use at exit: 0 bytes in 0 blocks" >/dev/null
if [ ${?} -eq 0 ]; then
echo -en " .. ${FG_GREEN}" && echo -n "ok" && echo -e "${COLOR_OFF}"
else
echo -en " .. ${FG_RED}" && echo -n "FAILED" && echo -e "${COLOR_OFF}"
failed=1
fi
}
quick_tests=(
"1.2 \"string\" 'ok' (2,3) << 9 nop >>" # base types inputs
"nop help version uname history quit" # general commands
"38 std 38 fix 38 sci 10 prec 2 default \"toward zero\" round" # modes
"1 2 + 2 3 * / 4 - inv neg chs" # base operations on numbers
"(1,2) (2,3) + (4,5) * (4,2) / (8,8) - inv neg chs" # base operations on complexes
"\"ab\" \" cd\" +" # base operations on strings
"2 3 ^ 2 ^ 2 pow" # usual operations on numbers 1
"(2,3) (1,1) ^ 2 ^ 2 (2,3) ^" # usual operations on complexes 1
"1 2 3 swap" # stack operations
)
functional_tests=($(cat all.md | grep "^@include" | awk '{print $2}'))
# echo "Quick rpn memory checks"
# for i in ${!quick_tests[@]}; do
# checkmem "${quick_tests[$i]}"
# done
echo "Functional rpn memory checks"
for i in ${!functional_tests[@]}; do
checkmem "\"${functional_tests[$i]}\" test"
done
exit ${failed}