diff --git a/CHANGELOG.md b/CHANGELOG.md index a01664a..ee89820 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,27 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [2.4.1] - 2022-03-02 + +### Added + +- A welcome message has been added in interactive mode. + +### Changed + +- The function `date` return format is now a string `"YYY-MM-DD"` (local ISO 8601). +- The function `time` return format is now a string `"HH:MM:SS"` (local ISO 8601). + +### Removed + +- The useless function `nop` has been removed. +- GENERATION.md file has been removed, the generation instructions are in README.md. +- The useless file TODO.md has been removed. + +### Fixed + +- The function `ticks` now returns correctly the current date in µs + ## [2.4.0] - 2022-02-28 ### Added diff --git a/CMakeLists.txt b/CMakeLists.txt index c0a68a6..169848a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,7 +12,7 @@ endif() message(STATUS "Build mode: ${CMAKE_BUILD_TYPE}") if(EXISTS "${PROJECT_SOURCE_DIR}/.git") - execute_process(COMMAND git describe --long HEAD OUTPUT_VARIABLE "GIT_VERSION" OUTPUT_STRIP_TRAILING_WHITESPACE) + execute_process(COMMAND git describe HEAD OUTPUT_VARIABLE "GIT_VERSION" OUTPUT_STRIP_TRAILING_WHITESPACE) add_definitions(-DGIT_VERSION="${GIT_VERSION}") else(EXISTS ${PROJECT_SOURCE_DIR}/.git) set(GIT_VERSION "unknown") diff --git a/GENERATION.md b/GENERATION.md deleted file mode 100644 index c9ab38d..0000000 --- a/GENERATION.md +++ /dev/null @@ -1,29 +0,0 @@ -# Generation - -rpn is proposed for **GNU/Linux**. - -It can be generated following the steps below. - -rpn is dynamically linked against GNU MP and GNU MPFR and integrates [linenoise-ng](https://github.com/louisrubet/linenoise-ng.git) and [mpreal](http://www.holoborodko.com/pavel/mpfr/) code as git submodules. - -## Generate and install under Ubuntu 20.04 LTS - -```shell -sudo apt install git cmake g++ libmpfr6 libmpfr-dev -git clone https://github.com/louisrubet/rpn/ -mkdir -p rpn/build && cd rpn/build -cmake .. -make -j -sudo make install -``` - -## Generate and install under Fedora 35 - -```shell -sudo dnf install git cmake g++ mpfrf mpfr-devel -git clone https://github.com/louisrubet/rpn/ -mkdir -p rpn/build && cd rpn/build -cmake .. -make -j -sudo make install -``` diff --git a/MANUAL.md b/MANUAL.md index ba9f0d6..e7e8553 100644 --- a/MANUAL.md +++ b/MANUAL.md @@ -17,14 +17,13 @@ A help command is provided by rpn: ```rpn rpn> help -rpn v2.4.0, (c) 2022 +rpn v2.4.1, (c) 2022 Reverse Polish Notation CLI calculator Syntax: rpn [command] with optional command = list of commands GENERAL -nop no operation help this help message (...) ``` @@ -123,7 +122,6 @@ rpn> 7b1252 dec | keyword | description | |-------------------|-----------------------------------------| -| `nop` | no operation | | `help` `h` `?` | this help message | | `quit` `q` `exit` | quit software | | `version` | show rpn version | @@ -316,9 +314,9 @@ rpn> 7b1252 dec | `acosh` | inverse hyperbolic cosine | | `tanh` | hyperbolic tangent | | `atanh` | inverse hyperbolic tangent | -| `time` | time in format HH.MMSSssssss | -| `date` | date in format (M)M.DDYYYY | -| `ticks` | system tick in µs | +| `time` | local time in ISO 8601 format HH:MM:SS | +| `date` | local date in ISO 8601 format YYYY-MM-DD | +| `ticks` | local date and time in µs | ### default diff --git a/README.md b/README.md index cdd27d7..ab2b75d 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,16 @@ # **rpn** - **R**everse **P**olish **N**otation CLI calculator [![License: LGPLv3](https://www.gnu.org/graphics/lgplv3-88x31.png)](https://www.gnu.org/licenses/lgpl-3.0.en.html) -### A math functional language using reverse polish notation +### A math functional language using reverse (postfix) polish notation ```rpn -rpn> 1 2 + 2 sqrt +rpn> 1 2 + +3 +rpn> 2 sqrt 2> 3 -1> 1.4142135623730950488 +1> 1.4142135623730950488016887242096980786 ``` -### Manipulating reals, complexes, strings, variables on a stack +### Manipulating reals, complexes, strings, symbols on a stack ```rpn rpn> 1 2 + 2 @@ -19,8 +21,15 @@ rpn> r->c sq conj (1,1) / ``` ```rpn -rpn> "sqrt of 2 is " 2 sqrt ->str + -"sqrt of 2 is 1.4142135623730950488016887242096980786" +rpn> 0x1234 dec +4660 +rpn> bin +0b1001000110100 +``` + +```rpn +rpn> 4 fix "sqrt of 2 is about " 2 sqrt ->str + +"sqrt of 2 is about 1.4142" ``` ```rpn @@ -31,7 +40,7 @@ rpn> (1,2) f (-10,-6) ``` -### Arbitrary precision provided by GNU MPFR +### Arbitrary precision ```rpn rpn> 256 prec @@ -43,18 +52,31 @@ rpn> ### Variables, structured programming ```rpn -rpn> « rot * swap 2 / chs dup sq rot - sqrt » 'quad' sto rpn> 0 1 10000 for i i sq + next 333383335000 +``` + +```rpn rpn> a 1 > if then a sq 'calc' eval else 'stop' eval end ``` +```rpn +rpn> << dup +> 1 > if then +> dup 1 - fibo swap 2 - fibo + +> else +> 1 == 1 0 ifte +> end >> +>'fibo' sto +rpn> 12 fibo +144 +``` + ### Available functions ```rpn rpn> -Display all 146 possibilities? (y or n) -nop pow conj < pick step eval exp10 +Display all 145 possibilities? (y or n) help sqrt arg <= depth ift -> log2 h sq c->r != roll ifte pi alog2 ? abs r->c == rolld do sin exp2 @@ -73,6 +95,7 @@ inv min bin rot end sto* exp chs max base dup start sto/ expm neg re > dup2 for sneg log10 ^ im >= dupn next sinv alog10 +pow conj < pick step eval exp10 ``` ## Download @@ -85,12 +108,12 @@ A reference manual is provided [here](MANUAL.md) ## Generation -rpn is written in C++ and is dynamically linked against GNU MP and GNU MPFR. -It integrates [linenoise-ng](https://github.com/louisrubet/linenoise-ng.git) and [mpreal](http://www.holoborodko.com/pavel/mpfr/) code as git submodules. +rpn is written in C++ and is dynamically linked to GNU MP and GNU MPFR. +It integrates [linenoise-ng](https://github.com/louisrubet/linenoise-ng.git) and [mpreal](http://www.holoborodko.com/pavel/mpfr/) source code as git submodules. It can be generated following the steps below: -## Generate and install under Ubuntu 20.04 LTS +## Generate and install under Ubuntu 20.04 LTS and superior ```shell sudo apt install git cmake g++ libmpfr6 libmpfr-dev @@ -104,7 +127,7 @@ sudo make install ## Generate and install under Fedora 35 ```shell -sudo dnf install git cmake g++ mpfrf mpfr-devel +sudo dnf install git cmake g++ mpfr mpfr-devel git clone https://github.com/louisrubet/rpn/ mkdir -p rpn/build && cd rpn/build cmake .. diff --git a/TODO.md b/TODO.md deleted file mode 100644 index ffb181b..0000000 --- a/TODO.md +++ /dev/null @@ -1,7 +0,0 @@ -TODO - -missing tests / problems -- les arguments d'une fonction en erreur doivent ils être consommés ? - ex embettant : sto+ -- supprimer les vector et map static (il y en a 3 dans program) -- cpplint to install and run automatically diff --git a/src/main.cc b/src/main.cc index f4e5cca..95b2657 100644 --- a/src/main.cc +++ b/src/main.cc @@ -85,6 +85,8 @@ int main(int argc, char* argv[]) { // run with interactive prompt if (argc == 1) { + program::Welcome(); + // init history EnterInteractive(); diff --git a/src/object.h b/src/object.h index 9e9eefd..0c307ed 100644 --- a/src/object.h +++ b/src/object.h @@ -80,6 +80,7 @@ struct Number : Object { Number() : Object(kNumber), base(10) {} explicit Number(const mpreal& value__, int base__ = 10) : Object(kNumber), base(base__), value(value__) {} explicit Number(int value__, int base__ = 10) : Object(kNumber), base(base__), value(value__) {} + explicit Number(uint64_t value__, int base__ = 10) : Object(kNumber), base(base__), value(value__) {} int base; mpreal value; diff --git a/src/program.cc b/src/program.cc index e1e6eaf..693585b 100644 --- a/src/program.cc +++ b/src/program.cc @@ -8,7 +8,6 @@ vector program::keywords_{ // GENERAL {kUndef, "", nullptr, "\nGENERAL"}, - {kKeyword, "nop", &program::RpnNop, "no operation"}, {kKeyword, "help", &program::RpnHelp, "this help message"}, {kKeyword, "h", &program::RpnHelp, ""}, {kKeyword, "?", &program::RpnHelp, ""}, @@ -199,9 +198,9 @@ vector program::keywords_{ // TIME AND DATE {kUndef, "", nullptr, "\nTIME AND DATE"}, - {kKeyword, "time", &program::RpnTime, "time in local format"}, - {kKeyword, "date", &program::RpnDate, "date in local format"}, - {kKeyword, "ticks", &program::RpnTicks, "system tick in µs"}, + {kKeyword, "time", &program::RpnTime, "local time in ISO 8601 format"}, + {kKeyword, "date", &program::RpnDate, "local date in ISO 8601 format"}, + {kKeyword, "ticks", &program::RpnTicks, "local date and time in µs"} }; #pragma GCC diagnostic pop diff --git a/src/program.h b/src/program.h index 865d701..e31038e 100644 --- a/src/program.h +++ b/src/program.h @@ -44,6 +44,7 @@ class program : public deque, public Lexer { void ShowStack(bool show_separator = true); static void ApplyDefault(); + static void Welcome(); static vector& GetAutocompletionWords(); @@ -105,7 +106,6 @@ class program : public deque, public Lexer { void RpnP2r(); // general - void RpnNop(); void RpnQuit(); void RpnHelp(); void RpnStd(); diff --git a/src/rpn-general.cc b/src/rpn-general.cc index 882c7eb..878b754 100644 --- a/src/rpn-general.cc +++ b/src/rpn-general.cc @@ -23,13 +23,9 @@ static const char _description[] = static const char _syntax[] = ATTR_BOLD "Syntax" ATTR_OFF ": rpn [command]\nwith optional command = list of commands"; -static const char _uname[] = ATTR_BOLD "rpn v" RPN_VERSION ", (c) 2022 " ATTR_OFF; +static const char _uname[] = ATTR_BOLD "rpn " RPN_VERSION ", (c) 2022 " ATTR_OFF; -/// @brief nop keyword implementation -/// -void program::RpnNop() { - // nop -} +static const char _welcome[] = ATTR_BOLD "rpn " RPN_VERSION ATTR_OFF "\nType h or help for more information."; /// @brief quit keyword implementation /// @@ -88,6 +84,10 @@ void program::RpnHelp() { cout << endl; } +/// @brief welcome string +/// +void program::Welcome() { cout << _welcome << endl; } + /// @brief whether a printed precision is in the precision min/max /// /// @param precision the precision in digits diff --git a/src/rpn-time.cc b/src/rpn-time.cc index e7afbc8..18f0d99 100644 --- a/src/rpn-time.cc +++ b/src/rpn-time.cc @@ -1,29 +1,24 @@ // Copyright (c) 2014-2022 Louis Rubet +#include #include +using namespace std::chrono; #include "program.h" /// @brief time keyword implementation /// void program::RpnTime() { - struct timespec ts; - struct tm* tm; - double date; + std::time_t rawtime = system_clock::to_time_t(system_clock::now()); + struct tm tm; - // get local date - clock_gettime(CLOCK_REALTIME, &ts); - time_t time = (time_t)ts.tv_sec; - tm = localtime(&time); - if (tm != nullptr) { - // date format = HH.MMSSssssss - date = (static_cast(tm->tm_hour) * 10000000000.0 + static_cast(tm->tm_min) * 100000000.0 + - static_cast(tm->tm_sec) * 1000000.0 + static_cast(ts.tv_nsec / 1000)); - - // push it - // division after push for real precision - stack_.push(new Number(date)); - stack_.value(0) /= 10000000000.0; + if (localtime_r(&rawtime, &tm) != nullptr) { + char buffer[80]; + size_t sz = strftime(buffer, sizeof(buffer), "%T", &tm); + if (sz > 0) + stack_.push(new String(buffer)); + else + ERROR_CONTEXT(kInternalError); } else { ERROR_CONTEXT(kInternalError); } @@ -32,21 +27,16 @@ void program::RpnTime() { /// @brief date keyword implementation /// void program::RpnDate() { - struct timespec ts; - struct tm* tm; - double date; + std::time_t rawtime = system_clock::to_time_t(system_clock::now()); + struct tm tm; - // get local date - clock_gettime(CLOCK_REALTIME, &ts); - time_t time = (time_t)ts.tv_sec; - tm = localtime(&time); - if (tm != nullptr) { - // date format = (M)M.DDYYYY - date = static_cast(tm->tm_mon + 1) * 1000000.0 + static_cast(tm->tm_mday) * 10000.0 + - static_cast(tm->tm_year + 1900); - // division after push for real precision - stack_.push(new Number(date)); - stack_.value(0) /= 1000000.0; + if (localtime_r(&rawtime, &tm) != nullptr) { + char buffer[80]; + size_t sz = strftime(buffer, sizeof(buffer), "%F", &tm); + if (sz > 0) + stack_.push(new String(buffer)); + else + ERROR_CONTEXT(kInternalError); } else { ERROR_CONTEXT(kInternalError); } @@ -55,19 +45,6 @@ void program::RpnDate() { /// @brief ticks keyword implementation /// void program::RpnTicks() { - struct timespec ts; - struct tm* tm; - double date; - - // get local date - clock_gettime(CLOCK_REALTIME, &ts); - time_t time = (time_t)ts.tv_sec; - tm = localtime(&time); - if (tm != nullptr) { - // date in µs - date = 1000000.0 * static_cast(ts.tv_sec) + static_cast(ts.tv_nsec / 1000); - stack_.push(new Number(date)); - } else { - ERROR_CONTEXT(kInternalError); - } + uint64_t time_span = (uint64_t)duration_cast(high_resolution_clock::now().time_since_epoch()).count(); + stack_.push(new Number(time_span)); } diff --git a/test/020-general.md b/test/020-general.md index ea4056e..870fee1 100644 --- a/test/020-general.md +++ b/test/020-general.md @@ -30,16 +30,6 @@ `del` -## nop - -`nop` - --> stack size should be 0 - --> error should be 0 - -`del` - ## quit `q` diff --git a/test/090-program.md b/test/090-program.md index 8042afc..fb66292 100644 --- a/test/090-program.md +++ b/test/090-program.md @@ -95,7 +95,6 @@ ## fibo ``` -«dup 1 > if then dup 1 - fibo swap 2 - fibo + else 1 == if then 1 else 0 end end» 'fibo' sto «dup 1 > if then dup 1 - fibo swap 2 - fibo + else 1 == 1 0 ifte end» 'fibo' sto 7 fibo 13 == if then 'ok!' end diff --git a/test/110-time.md b/test/110-time.md index b246c05..ebf1198 100644 --- a/test/110-time.md +++ b/test/110-time.md @@ -8,7 +8,7 @@ -> error should be 0 --> stack should be "number" +-> stack should be "string" `del` @@ -18,7 +18,7 @@ -> error should be 0 --> stack should be "number" +-> stack should be "string" `del`