mirror of
https://github.com/louisrubet/rpn
synced 2025-01-30 20:34:30 +01:00
commit
bafc48ab85
115 changed files with 10239 additions and 9599 deletions
34
.github/workflows/cmake.yml
vendored
Normal file
34
.github/workflows/cmake.yml
vendored
Normal file
|
@ -0,0 +1,34 @@
|
|||
name: CMake
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches: [ v2.4.0-devel, master ]
|
||||
|
||||
env:
|
||||
BUILD_TYPE: Release
|
||||
|
||||
jobs:
|
||||
functional_and_mem_tests:
|
||||
runs-on: ubuntu-20.04
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: 'true'
|
||||
|
||||
- name: Install needed libs
|
||||
run: sudo apt update && sudo apt install -y cmake build-essential libmpfr-dev valgrind
|
||||
|
||||
- name: Configure CMake
|
||||
run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}}
|
||||
|
||||
- name: Build
|
||||
run: make -C ${{github.workspace}}/build -j
|
||||
|
||||
- name: Functional tests
|
||||
working-directory: ${{github.workspace}}/test
|
||||
run: ${{github.workspace}}/build/rpn \"all.md\" test
|
||||
|
||||
- name: Memory tests
|
||||
working-directory: ${{github.workspace}}/test
|
||||
run: ./mem_test.sh ${{github.workspace}}/build/rpn
|
8
.gitignore
vendored
8
.gitignore
vendored
|
@ -22,5 +22,11 @@
|
|||
*.x86_64
|
||||
*.hex
|
||||
|
||||
# editors garbage
|
||||
# Tools workfiles
|
||||
*~
|
||||
.scannerwork/*
|
||||
*bw-output*/*
|
||||
.vscode/*
|
||||
build/*
|
||||
debug/*
|
||||
release/*
|
||||
|
|
8
.gitmodules
vendored
8
.gitmodules
vendored
|
@ -1,6 +1,6 @@
|
|||
[submodule "build/linenoise-ng"]
|
||||
path = build/linenoise-ng
|
||||
url = git@github.com:louisrubet/linenoise-ng.git
|
||||
[submodule "linenoise-ng"]
|
||||
path = linenoise-ng
|
||||
url = git@github.com:louisrubet/linenoise-ng.git
|
||||
url = https://github.com/louisrubet/linenoise-ng.git
|
||||
[submodule "mpreal"]
|
||||
path = mpreal
|
||||
url = https://github.com/advanpix/mpreal
|
||||
|
|
55
CHANGELOG.md
Normal file
55
CHANGELOG.md
Normal file
|
@ -0,0 +1,55 @@
|
|||
# Changelog
|
||||
|
||||
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.0] - 2022-02-28
|
||||
|
||||
### Added
|
||||
|
||||
- Better parser (in fact just a lexer, because that's all it takes).
|
||||
- Use of [mpreal](http://www.holoborodko.com/pavel/mpfr/) instead of raw mpfr for calc on reals.
|
||||
- Use of the standard C++ complex class.
|
||||
- Enhanced code quality and memory usage checks.
|
||||
- Added github actions to pass functional tests and valgrind mem checks at each pull request.
|
||||
- Applied [google c++ style guide](https://google.github.io/styleguide/cppguide.html)
|
||||
- points covered are very numerous: c++17, define guard, name and order of inclusions, static and global variables, common patterns, classes, naming conventions etc.
|
||||
- cpplint used with a CPPLINT.cfg removing some warnings.
|
||||
- clang-format is now based on google style.
|
||||
- Test files are now markdown (.md) files, tests result are slightly changed.
|
||||
- Delived as flatpak, not as rpm and deb anymore.
|
||||
- Automatic version string from git.
|
||||
- hex and bin numbers accept floating point input and C99 standard output.
|
||||
- `«` 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.
|
||||
|
||||
### Changed
|
||||
|
||||
Be carefull, compatibility is broken on these points compared to last version 2.3.2:
|
||||
|
||||
- `<< <<` input doesn't lead to `««»»` but to `«<< »`, preventing to eval the real program content.
|
||||
- Making an operation stick to the previous entry is not possible anymore (ex: `1 2+` instead of `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).
|
||||
- The binary prefix is always `0b` on display, but the input can be `0b`, `0B`, `2b` or `2B`.
|
||||
- The hex prefix is always `0x` on display, but the input 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 the 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.
|
||||
|
||||
### Removed
|
||||
|
||||
- Removed useless `unti`, `repea`, `whil` (prev. existing for HP28S compatibility).
|
||||
- Removed `sqr` function, please use `sq` instead (prev. existing for HP28S compatibility).
|
||||
- Removed CtrlC for interrupting a program (considered useless).
|
||||
- Removed old memory management, efficient but poorly maintainable.
|
||||
- Removed man page generation.
|
||||
|
||||
### Fixed
|
||||
|
||||
- `sub` now only accepts boundaries between 1 and the string length.
|
||||
- `sto/` behavior: sto/ now correctly stores variable divided by constant and not constant divided by variable.
|
||||
- `cosh` now returns the hyp cosinus instead of the hyp sinus.
|
140
CMakeLists.txt
140
CMakeLists.txt
|
@ -1,35 +1,34 @@
|
|||
# -*- mode: CMAKE; -*-
|
||||
|
||||
cmake_minimum_required(VERSION 2.6)
|
||||
|
||||
project(rpn)
|
||||
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
if((NOT CMAKE_BUILD_TYPE MATCHES Debug)
|
||||
AND (NOT CMAKE_BUILD_TYPE MATCHES Release))
|
||||
set(CMAKE_BUILD_TYPE Release CACHE string "Choose the type of build, options are: None Debug Release" FORCE)
|
||||
set(CMAKE_BUILD_TYPE Release CACHE STRING "Choose the type of build, options are: None Debug Release" FORCE)
|
||||
endif()
|
||||
|
||||
message(STATUS "Build mode: ${CMAKE_BUILD_TYPE}")
|
||||
|
||||
if(EXISTS "${PROJECT_SOURCE_DIR}/.git")
|
||||
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")
|
||||
endif()
|
||||
add_definitions(-DGIT_VERSION="${GIT_VERSION}")
|
||||
message("GIT_VERSION is ${GIT_VERSION}")
|
||||
|
||||
# INFO
|
||||
set(RPN_VERSION "2.3.2")
|
||||
set(RPN_DISPLAY_NAME "rpn")
|
||||
set(RPN_URL_INFO_ABOUT "https://github.com/louisrubet/rpn")
|
||||
set(RPN_CONTACT "Louis Rubet <louis@rubet.fr>")
|
||||
set(RPN_FRIENDLY_STRING "rpn - Reverse Polish Notation math language")
|
||||
set(RPN_FRIENDLY_STRING "Reverse Polish Notation math language")
|
||||
set(RPN_LICENSE "LGPLv3")
|
||||
set(RPN_LICENSE_FILE "${PROJECT_SOURCE_DIR}/LICENSE")
|
||||
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")
|
||||
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")
|
||||
endif()
|
||||
|
||||
# custom linenoise-ng
|
||||
if(NOT EXISTS "${PROJECT_SOURCE_DIR}/linenoise-ng/.git")
|
||||
execute_process(COMMAND git submodule init ${PROJECT_SOURCE_DIR}/linenoise-ng)
|
||||
|
@ -37,71 +36,60 @@ 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)
|
||||
|
||||
# src
|
||||
set(RPN_SRC_FILES
|
||||
${PROJECT_SOURCE_DIR}/src/main.cc
|
||||
${PROJECT_SOURCE_DIR}/src/object.cc
|
||||
${PROJECT_SOURCE_DIR}/src/mpreal-out.cc
|
||||
${PROJECT_SOURCE_DIR}/src/program.cc
|
||||
${PROJECT_SOURCE_DIR}/src/lexer.cc
|
||||
${PROJECT_SOURCE_DIR}/src/input.cc
|
||||
${PROJECT_SOURCE_DIR}/src/rpn-branch.cc
|
||||
${PROJECT_SOURCE_DIR}/src/rpn-complex.cc
|
||||
${PROJECT_SOURCE_DIR}/src/rpn-general.cc
|
||||
${PROJECT_SOURCE_DIR}/src/rpn-logs.cc
|
||||
${PROJECT_SOURCE_DIR}/src/rpn-program.cc
|
||||
${PROJECT_SOURCE_DIR}/src/rpn-real.cc
|
||||
${PROJECT_SOURCE_DIR}/src/rpn-stack.cc
|
||||
${PROJECT_SOURCE_DIR}/src/rpn-store.cc
|
||||
${PROJECT_SOURCE_DIR}/src/rpn-string.cc
|
||||
${PROJECT_SOURCE_DIR}/src/rpn-test.cc
|
||||
${PROJECT_SOURCE_DIR}/src/rpn-test-framework.cc
|
||||
${PROJECT_SOURCE_DIR}/src/rpn-time.cc
|
||||
${PROJECT_SOURCE_DIR}/src/rpn-trig.cc)
|
||||
|
||||
set(LINENOISE_NG_SRC_FILES
|
||||
${PROJECT_SOURCE_DIR}/linenoise-ng/src/ConvertUTF.cpp
|
||||
${PROJECT_SOURCE_DIR}/linenoise-ng/src/linenoise.cpp
|
||||
${PROJECT_SOURCE_DIR}/linenoise-ng/src/wcwidth.cpp)
|
||||
|
||||
# compiler options
|
||||
set_source_files_properties(${RPN_SRC_FILES} COMPILE_FLAGS # some harder warnings
|
||||
"-Wall -Wextra -pedantic -Wno-missing-field-initializers")
|
||||
|
||||
if(CMAKE_COMPILER_IS_GNUCXX)
|
||||
message(STATUS "Compiler type GNU: ${CMAKE_CXX_COMPILER}")
|
||||
# TODO still up to date?
|
||||
set(BASE_COMPILER_OPTIONS "-std=c++17 -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 -s")
|
||||
endif()
|
||||
|
||||
add_executable(rpn ${RPN_SRC_FILES} ${LINENOISE_NG_SRC_FILES})
|
||||
|
||||
# build
|
||||
add_executable(
|
||||
rpn
|
||||
${PROJECT_SOURCE_DIR}/src/main.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/object.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/program.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/parse.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/rpn-branch.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/rpn-complex.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/rpn-general.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/rpn-logs.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/rpn-program.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/rpn-real.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/rpn-stack.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/rpn-store.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/rpn-string.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/rpn-test.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/rpn-test-core.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/rpn-time.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/rpn-trig.cpp
|
||||
${PROJECT_SOURCE_DIR}/linenoise-ng/src/ConvertUTF.cpp
|
||||
${PROJECT_SOURCE_DIR}/linenoise-ng/src/linenoise.cpp
|
||||
${PROJECT_SOURCE_DIR}/linenoise-ng/src/wcwidth.cpp
|
||||
)
|
||||
target_link_libraries(rpn mpfr)
|
||||
|
||||
# man
|
||||
add_custom_target(man ALL)
|
||||
add_custom_command(
|
||||
TARGET man
|
||||
COMMAND gzip -f -k ${CMAKE_CURRENT_SOURCE_DIR}/doc/rpn.1
|
||||
OUTPUTS ${CMAKE_CURRENT_SOURCE_DIR}/doc/rpn.1.gz
|
||||
)
|
||||
target_link_libraries(rpn gmp)
|
||||
|
||||
# install
|
||||
install(TARGETS rpn DESTINATION bin)
|
||||
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/doc/rpn.1.gz DESTINATION ${CMAKE_INSTALL_PREFIX}/man/man1)
|
||||
|
||||
# packages (common variables for all packages)
|
||||
set(CPACK_PACKAGE_NAME ${RPN_DISPLAY_NAME})
|
||||
set(CPACK_PACKAGE_VENDOR ${RPN_CONTACT})
|
||||
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY ${RPN_FRIENDLY_STRING})
|
||||
set(CPACK_PACKAGE_DESCRIPTION ${RPN_FRIENDLY_STRING})
|
||||
set(CPACK_PACKAGE_DESCRIPTION_FILE ${RPN_DESCRIPTION_FILE})
|
||||
set(CPACK_RESOURCE_FILE_LICENSE ${RPN_LICENSE_FILE})
|
||||
set(CPACK_RESOURCE_FILE_README ${RPN_DESCRIPTION_FILE})
|
||||
|
||||
set(CPACK_PACKAGE_FILE_NAME "${RPN_DISPLAY_NAME}-${RPN_VERSION}-amd64")
|
||||
set(CPACK_PACKAGE_VERSION ${RPN_VERSION})
|
||||
#set(CPACK_PACKAGE_RELEASE 0)
|
||||
set(CPACK_PACKAGE_CONTACT ${RPN_CONTACT})
|
||||
set(CPACK_PACKAGE_LICENSE ${RPN_LICENSE})
|
||||
set(CPACK_PACKAGING_INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX})
|
||||
|
||||
# DEB package
|
||||
set(CPACK_GENERATOR "DEB;RPM;TGZ")
|
||||
set(CPACK_DEBIAN_PACKAGE_MAINTAINER ${RPN_CONTACT})
|
||||
set(CPACK_DEBIAN_PACKAGE_ARCHITECTURE ${SYSTEM_PROCESSOR})
|
||||
set(CPACK_DEBIAN_PACKAGE_SECTION "math")
|
||||
set(CPACK_DEBIAN_PACKAGE_PRIORITY "optional")
|
||||
|
||||
include(CPack)
|
||||
|
||||
# RPM package
|
||||
set(CPACK_RPM_PACKAGE_REQUIRES "mpfr")
|
||||
|
|
|
@ -1,58 +1,29 @@
|
|||
# **rpn v2.3.2** - generation
|
||||
# Generation
|
||||
|
||||
For now rpn is proposed only for **GNU/Linux**
|
||||
rpn is proposed for **GNU/Linux**.
|
||||
|
||||
It can be generated following the steps below
|
||||
It can be generated following the steps below.
|
||||
|
||||
## version identification
|
||||
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.
|
||||
|
||||
rpn is dynamically linked against GNU MP and GNU MPFR
|
||||
## Generate and install under Ubuntu 20.04 LTS
|
||||
|
||||
and embeds the source code of linenoise-ng as a submodule
|
||||
```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
|
||||
```
|
||||
|
||||
It is necessary to get MPFR to generate rpn
|
||||
## Generate and install under Fedora 35
|
||||
|
||||
## install GNU MPFR headers
|
||||
|
||||
- download **GNU MPFR** from http://www.mpfr.org
|
||||
- install it with usual autotools commands `./configure && make && make install`
|
||||
|
||||
## generate rpn
|
||||
|
||||
- clone [rpn project](https://github.com/louisrubet/rpn/) or download a zipped version from [the release directory](https://github.com/louisrubet/rpn/releases)
|
||||
```
|
||||
# git clone https://github.com/louisrubet/rpn.git
|
||||
```
|
||||
|
||||
- make
|
||||
|
||||
CMake must be installed on the generation machine
|
||||
|
||||
```
|
||||
# cd rpn/
|
||||
# mkdir build
|
||||
# cd build && cmake .. && make
|
||||
```
|
||||
|
||||
- install
|
||||
```
|
||||
# cd rpn/build
|
||||
# sudo make install
|
||||
```
|
||||
|
||||
- packages
|
||||
|
||||
To build RPM package you should install CMake 2.6.0 or higher
|
||||
|
||||
```
|
||||
# cd rpn/build
|
||||
# sudo make package
|
||||
[100%] Built target rpn
|
||||
Run CPack packaging tool...
|
||||
CPack: Create package using DEB
|
||||
(...)
|
||||
CPack: Create package using RPM
|
||||
(...)
|
||||
CPack: Create package using TGZ
|
||||
(...)
|
||||
```
|
||||
```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
|
||||
```
|
||||
|
|
453
MANUAL.md
453
MANUAL.md
|
@ -1,44 +1,39 @@
|
|||
# **rpn** - reference manual
|
||||
|
||||
**rpn**
|
||||
- is a structured full-featured **math-oriented language** inspired by Hewlett-Packard **R**everse **P**olish **L**isp (**HP28S** and **HP48GX** user manuals are provided as references), including **real**, **complex**, **stack**, **store**, **branch**, **program**, **test**, **trig** and **logs** commands and more to come
|
||||
- is implemented as a **command-line calculator** for most popular Linux distributions
|
||||
- is a structured full-featured **math-oriented language** inspired by Hewlett-Packard **R**everse **P**olish **L**isp (**HP28S** and **HP48GX** user manuals are provided as references), including **real**, **complex**, **stack**, **store**, **branch**, **program**, **test**, **trig** and **logs** commands and more to come,
|
||||
- is implemented as a **command-line calculator**,
|
||||
- brings powerfull calculation facilities on floating point numbers with __arbitrary precision__ provided by **GNU MP** and **GNU MPFR** libraries
|
||||
- uses that so cool **reverse polish notation**
|
||||
- uses that so cool **reverse polish notation**.
|
||||
|
||||
## Doc overview
|
||||
|
||||
This page gives some examples and lists the commands currently implemented.
|
||||
|
||||
For a most complete help, please refer to HP28S and HP48GX manuals provided in the sources
|
||||
For a most complete help, please refer to HP28S and HP48GX manuals provided in the sources.
|
||||
|
||||
A help command is provided by rpn:
|
||||
|
||||
```
|
||||
```rpn
|
||||
rpn> help
|
||||
|
||||
rpn v2.3.2, (c) 2017 <louis@rubet.fr>, GNU LGPL v3
|
||||
|
||||
Reverse Polish Notation language
|
||||
|
||||
using GMP v6.1.2 under GNU LGPL
|
||||
MPFR v4.0.1 under GNU LGPL
|
||||
and linenoise-ng v1.0.0 under BSD
|
||||
|
||||
rpn v2.4.0, (c) 2022 <louis@rubet.fr>
|
||||
Reverse Polish Notation CLI calculator
|
||||
|
||||
Syntax: rpn [command]
|
||||
with optional command = list of commands
|
||||
|
||||
|
||||
GENERAL
|
||||
nop no operation
|
||||
help this help message
|
||||
nop no operation
|
||||
help this help message
|
||||
(...)
|
||||
```
|
||||
|
||||
## Quick examples
|
||||
|
||||
### easy calculation with **stacked results**
|
||||
```
|
||||
### Easy calculation with **stacked results**
|
||||
|
||||
```rpn
|
||||
rpn> 1 2 +
|
||||
3
|
||||
rpn> 2 sqrt
|
||||
|
@ -46,8 +41,9 @@ rpn> 2 sqrt
|
|||
1> 1.4142135623730950488016887242096980786
|
||||
```
|
||||
|
||||
### **programs** and **variables**
|
||||
```
|
||||
### **Programs** and **variables**
|
||||
|
||||
```rpn
|
||||
rpn> << rot * swap 2 / neg dup sq rot - sqrt >> 'quadratic_solution' sto
|
||||
rpn> 1 2 -3 quadratic_solution
|
||||
2> -1
|
||||
|
@ -56,17 +52,19 @@ rpn> vars
|
|||
var 1: name 'quadratic_solution', type program, value << rot * swap 2 / chs dup sq rot - sqrt >>
|
||||
```
|
||||
|
||||
### **local variables**
|
||||
```
|
||||
### **Local variables**
|
||||
|
||||
```rpn
|
||||
rpn> << -> x y << x y + ln >> >> 'P' sto
|
||||
rpn> 1 2 P
|
||||
rpn> 1.0986122886681096913952452369225257046
|
||||
```
|
||||
|
||||
### **arbitrary precision**
|
||||
### **Arbitrary precision**
|
||||
|
||||
The number of significant digits can be very large thanks to GNU MPFR
|
||||
|
||||
```
|
||||
```rpn
|
||||
rpn> 256 prec
|
||||
rpn> pi
|
||||
3.1415926535897932384626433832795028841971693993751058209749445923078164062862
|
||||
|
@ -75,10 +73,11 @@ rpn> erase 10000 prec pi
|
|||
rpn>
|
||||
```
|
||||
|
||||
### object types
|
||||
### Object types
|
||||
|
||||
The following objects are managed: **floating numbers**, **complexes**, **symbols**, **strings**, **programs**, plus language **keywords** (commands and flow controls)
|
||||
```
|
||||
The following objects are managed: **floating numbers**, **complexes**, **symbols**, **strings**, **programs**, plus language **keywords** (commands and flow controls).
|
||||
|
||||
```rpn
|
||||
5> 12.3456
|
||||
4> (1,-2.33)
|
||||
3> 'symbol'
|
||||
|
@ -89,28 +88,25 @@ rpn>
|
|||
|
||||
### structured programming
|
||||
|
||||
Provided loop keywords **for|start..next|step**, **do..until**, **while..repeat** and control keywords **if..then..[else]..end**, **ift**, **ifte** allow you to program powerfull algorithms
|
||||
Provided loop keywords **for|start..next|step**, **do..until**, **while..repeat** and control keywords **if..then..[else]..end**, **ift**, **ifte** allow you to program powerfull algorithms.
|
||||
|
||||
### command line
|
||||
|
||||
**rpn** is a cli interface with an **interactive editor** with autocompletion provided by **linenoise-ng**, see https://github.com/arangodb/linenoise-ng
|
||||
**rpn** is a cli interface with an **interactive editor** with autocompletion provided by [linenoise-ng](https://github.com/arangodb/linenoise-ng).
|
||||
|
||||
Autocompletion works like those in Linux shells, with keys \<tab\>, Ctrl-R \<search pattern\> etc.
|
||||
|
||||
## entry
|
||||
|
||||
**reals** can be entered in decimal, binary, hexadecimal or arbitrary base from 2 to 62
|
||||
|
||||
binaries (base 2) are entered as `0b<number>` or `0B<number>`
|
||||
|
||||
hexadecimals (base 16) are entered as `0x<number>` or `0X<number>`
|
||||
|
||||
arbitrary base numbers are entered as `<base>b<number>`
|
||||
|
||||
representation can be individualy changed with keywords `dec`, `bin`, `hex` and `<n> base`
|
||||
- **reals** can be entered in decimal, binary, hexadecimal or arbitrary base from 2 to 62.
|
||||
- Binaries (base 2) are entered as `0b<number>` or `0B<number>`.
|
||||
- Hexadecimals (base 16) are entered as `0x<number>` or `0X<number>`.
|
||||
- Arbitrary base numbers are entered as `<base>b<number>`.
|
||||
- Representation can be individualy changed with keywords `dec`, `bin`, `hex` and `<n> base`.
|
||||
|
||||
ex:
|
||||
```
|
||||
|
||||
```rpn
|
||||
rpn> 5.6 0xaabb 0b1101 7b1252
|
||||
4> 5.6
|
||||
3> 0xaabb
|
||||
|
@ -125,203 +121,204 @@ rpn> 7b1252 dec
|
|||
|
||||
### general
|
||||
|
||||
|keyword|description|
|
||||
|-|-|
|
||||
|`nop` | no operation
|
||||
|`help` `h` `?` | this help message
|
||||
|`quit` `q` `exit` | quit software
|
||||
|`version` | show rpn version
|
||||
|`uname` | show rpn complete identification string
|
||||
|`history`| see commands history
|
||||
| keyword | description |
|
||||
|-------------------|-----------------------------------------|
|
||||
| `nop` | no operation |
|
||||
| `help` `h` `?` | this help message |
|
||||
| `quit` `q` `exit` | quit software |
|
||||
| `version` | show rpn version |
|
||||
| `uname` | show rpn complete identification string |
|
||||
| `history` | see commands history |
|
||||
|
||||
### usual operations - real and complex
|
||||
|
||||
|keyword|description|
|
||||
|-|-|
|
||||
|`+`| addition
|
||||
|`-`| substraction
|
||||
|`neg` `chs`| negation
|
||||
|`*`| multiplication
|
||||
|`/`| division
|
||||
|`inv`| inverse
|
||||
|`^` `pow`| power
|
||||
|`sqrt`| square root
|
||||
|`sq` `sqr`| square
|
||||
|`abs`| absolute value for a number or `sqrt(re*re+im*im)` for a complex
|
||||
|`dec`| decimal representation
|
||||
|`hex`| hexadecimal representation
|
||||
|`bin`| binary representation
|
||||
|`base`| arbitrary base representation
|
||||
|`sign`| sign of a real, unary vector in the same direction for a complex
|
||||
| keyword | description |
|
||||
|-------------|------------------------------------------------------------------|
|
||||
| `+` | addition |
|
||||
| `-` | substraction |
|
||||
| `neg` `chs` | negation |
|
||||
| `*` | multiplication |
|
||||
| `/` | division |
|
||||
| `inv` | inverse |
|
||||
| `^` `pow` | power |
|
||||
| `sqrt` | square root |
|
||||
| `sq` `sqr` | square |
|
||||
| `abs` | absolute value for a number or `sqrt(re*re+im*im)` for a complex |
|
||||
| `dec` | decimal representation |
|
||||
| `hex` | hexadecimal representation |
|
||||
| `bin` | binary representation |
|
||||
| `base` | arbitrary base representation |
|
||||
| `sign` | sign of a real, unary vector in the same direction for a complex |
|
||||
|
||||
### operations on reals
|
||||
|
||||
|keyword|description|
|
||||
|-|-|
|
||||
|`%`| purcent
|
||||
|`%CH`| inverse purcent
|
||||
|`mod`| modulo
|
||||
|`fact`| n! for integer n or Gamma(x+1) for fractional x
|
||||
|`mant`| mantissa of a real number
|
||||
|`xpon`| exponant of a real number
|
||||
|`floor`| largest number <=
|
||||
|`ceil`| smallest number >=
|
||||
|`ip`| integer part
|
||||
|`fp`| fractional part
|
||||
|`min`| min of 2 real numbers
|
||||
|`max`| max of 2 real numbers
|
||||
| keyword | description |
|
||||
|---------|-------------------------------------------------|
|
||||
| `%` | purcent |
|
||||
| `%CH` | inverse purcent |
|
||||
| `mod` | modulo |
|
||||
| `fact` | n! for integer n or Gamma(x+1) for fractional x |
|
||||
| `mant` | mantissa of a real number |
|
||||
| `xpon` | exponant of a real number |
|
||||
| `floor` | largest number <= |
|
||||
| `ceil` | smallest number >= |
|
||||
| `ip` | integer part |
|
||||
| `fp` | fractional part |
|
||||
| `min` | min of 2 real numbers |
|
||||
| `max` | max of 2 real numbers |
|
||||
|
||||
### operations on complexes
|
||||
|
||||
|keyword|description|
|
||||
|-|-|
|
||||
|`re`| complex real part
|
||||
|`im`| complex imaginary part
|
||||
|`conj`| complex conjugate
|
||||
|`arg`| complex argument in radians
|
||||
|`r->p`| rectangular to polar coordinates
|
||||
|`p->r`| polar to rectangular coordinates
|
||||
|`r->c`| transform 2 reals in a complex
|
||||
|`c->r`| transform a complex in 2 reals
|
||||
| keyword | description |
|
||||
|---------|----------------------------------|
|
||||
| `re` | complex real part |
|
||||
| `im` | complex imaginary part |
|
||||
| `conj` | complex conjugate |
|
||||
| `arg` | complex argument in radians |
|
||||
| `r->p` | rectangular to polar coordinates |
|
||||
| `p->r` | polar to rectangular coordinates |
|
||||
| `r->c` | transform 2 reals in a complex |
|
||||
| `c->r` | transform a complex in 2 reals |
|
||||
|
||||
### mode
|
||||
|
||||
|keyword|description|
|
||||
|-|-|
|
||||
|`std`| standard floating numbers representation. ex: `std`
|
||||
|`fix`| fixed point representation. ex: `6 fix`
|
||||
|`sci`| scientific floating point representation. ex: `20 sci`
|
||||
|`prec`| set float precision in bits. ex: `256 prec`
|
||||
|`round`| set float rounding mode. Authorized values are: `"nearest"` `"toward zero"` `"toward +inf"` `"toward -inf"` `"away from zero"`. ex: `"nearest" round`
|
||||
|`default` | set float representation and precision to default
|
||||
|`type` | show type of stack first entry
|
||||
| keyword | description |
|
||||
|-----------|------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `std` | standard floating numbers representation. ex: `std` |
|
||||
| `fix` | fixed point representation. ex: `6 fix` |
|
||||
| `sci` | scientific floating point representation. ex: `20 sci` |
|
||||
| `prec` | set float precision in bits. ex: `256 prec` |
|
||||
| `round` | set float rounding mode. Authorized values are: |
|
||||
| | `"nearest (even)", "toward zero", "toward +inf", "toward -inf", "away from zero", "faithful rounding", "nearest (away from zero)"` |
|
||||
| `default` | set float representation and precision to default |
|
||||
| `type` | show type of stack first entry |
|
||||
|
||||
### test
|
||||
|
||||
|keyword|description|
|
||||
|-|-|
|
||||
|`>`| binary operator >
|
||||
|`>=`| binary operator >=
|
||||
|`<`| binary operator <
|
||||
|`<=`| binary operator <=
|
||||
|`!=`| binary operator != (different)
|
||||
|`==`| binary operator == (equal)
|
||||
|`and`| boolean operator and
|
||||
|`or`| boolean operator or
|
||||
|`xor`| boolean operator xor
|
||||
|`not`| boolean operator not
|
||||
|`same`| boolean operator same (equal)
|
||||
| keyword | description |
|
||||
|---------|--------------------------------|
|
||||
| `>` | binary operator > |
|
||||
| `>=` | binary operator >= |
|
||||
| `<` | binary operator < |
|
||||
| `<=` | binary operator <= |
|
||||
| `!=` | binary operator != (different) |
|
||||
| `==` | binary operator == (equal) |
|
||||
| `and` | boolean operator and |
|
||||
| `or` | boolean operator or |
|
||||
| `xor` | boolean operator xor |
|
||||
| `not` | boolean operator not |
|
||||
| `same` | boolean operator same (equal) |
|
||||
|
||||
### stack
|
||||
|
||||
|keyword|description|
|
||||
|-|-|
|
||||
|`swap`| swap 2 first stack entries
|
||||
|`drop`| drop first stack entry
|
||||
|`drop2`| drop 2 first stack entries
|
||||
|`dropn`| drop n first stack entries
|
||||
|`del` `erase`| drop all stack entries
|
||||
|`rot`| rotate 3 first stack entries
|
||||
|`dup`| duplicate first stack entry
|
||||
|`dup2`| duplicate 2 first stack entries
|
||||
|`dupn`| duplicate n first stack entries
|
||||
|`pick`| push a copy of the given stack level onto the stack
|
||||
|`depth`| give stack depth
|
||||
|`roll`| move a stack entry to the top of the stack
|
||||
|`rolld`| move the element on top of the stack to a higher stack position
|
||||
|`over`| push a copy of the element in stack level 2 onto the stack
|
||||
| keyword | description |
|
||||
|---------------|-----------------------------------------------------------------|
|
||||
| `swap` | swap 2 first stack entries |
|
||||
| `drop` | drop first stack entry |
|
||||
| `drop2` | drop 2 first stack entries |
|
||||
| `dropn` | drop n first stack entries |
|
||||
| `del` `erase` | drop all stack entries |
|
||||
| `rot` | rotate 3 first stack entries |
|
||||
| `dup` | duplicate first stack entry |
|
||||
| `dup2` | duplicate 2 first stack entries |
|
||||
| `dupn` | duplicate n first stack entries |
|
||||
| `pick` | push a copy of the given stack level onto the stack |
|
||||
| `depth` | give stack depth |
|
||||
| `roll` | move a stack entry to the top of the stack |
|
||||
| `rolld` | move the element on top of the stack to a higher stack position |
|
||||
| `over` | push a copy of the element in stack level 2 onto the stack |
|
||||
|
||||
### string
|
||||
|
||||
|keyword|description|
|
||||
|-|-|
|
||||
|`->str`| convert an object into a string
|
||||
|`str->`| convert a string into an object
|
||||
|`chr`| convert ASCII character code in stack level 1 into a string
|
||||
|`num`| return ASCII code of the first character of the string in stack level 1 as a real number
|
||||
|`size`| return the length of the string
|
||||
|`pos`| seach for the string in level 1 within the string in level 2
|
||||
|`sub`| return a substring of the string in level 3
|
||||
| keyword | description |
|
||||
|---------|------------------------------------------------------------------------------------------|
|
||||
| `->str` | convert an object into a string |
|
||||
| `str->` | convert a string into an object |
|
||||
| `chr` | convert ASCII character code in stack level 1 into a string |
|
||||
| `num` | return ASCII code of the first character of the string in stack level 1 as a real number |
|
||||
| `size` | return the length of the string |
|
||||
| `pos` | seach for the string in level 1 within the string in level 2 |
|
||||
| `sub` | return a substring of the string in level 3 |
|
||||
|
||||
### branch
|
||||
|
||||
|keyword|description|
|
||||
|-|-|
|
||||
|`if`| `(test-instruction) if then (true-instructions) else (false-instructions) end`
|
||||
|`then`| used with if
|
||||
|`else`| used with if
|
||||
|`end`| used with various branch instructions
|
||||
|`ift`| similar to if-then-end: `(test-instruction) (true-instruction) ift`
|
||||
|`ifte`| similar to if-then-else-end: `(test-instruction) (true-instruction) (false-instruction) ifte`
|
||||
|`start`| `(start) (end) start (instructions) [next,(step) step]`. ex: `10 20 30 1 2 start + next`
|
||||
|`for`| `(start) (end) for (variable) (instructions) [next,(step) step]`. ex: `1 2 for i i 'a' sto+ 0.1 step`
|
||||
|`next`| used with start and for
|
||||
|`step`| used with start and for
|
||||
|`do`| `do (instructions) until (condition) end`
|
||||
|`until` `unti` | used with do
|
||||
|`while` `whil`| `while (test-instruction) repeat (loop-instructions) end`
|
||||
|`repeat` `repea`| used with while
|
||||
| keyword | description |
|
||||
|----------|-------------------------------------------------------------------------------------------------------|
|
||||
| `if` | `(test-instruction) if then (true-instructions) else (false-instructions) end` |
|
||||
| `then` | used with if |
|
||||
| `else` | used with if |
|
||||
| `end` | used with various branch instructions |
|
||||
| `ift` | similar to if-then-end: `(test-instruction) (true-instruction) ift` |
|
||||
| `ifte` | similar to if-then-else-end: `(test-instruction) (true-instruction) (false-instruction) ifte` |
|
||||
| `start` | `(start) (end) start (instructions) [next,(step) step]`. ex: `10 20 30 1 2 start + next` |
|
||||
| `for` | `(start) (end) for (variable) (instructions) [next,(step) step]`. ex: `1 2 for i i 'a' sto+ 0.1 step` |
|
||||
| `next` | used with start and for |
|
||||
| `step` | used with start and for |
|
||||
| `do` | `do (instructions) until (condition) end` |
|
||||
| `until` | used with do |
|
||||
| `while` | `while (test-instruction) repeat (loop-instructions) end` |
|
||||
| `repeat` | used with while |
|
||||
|
||||
### store
|
||||
|
||||
|keyword|description|
|
||||
|-|-|
|
||||
|`sto`| store a variable. ex: ```1 'name' sto```
|
||||
|`rcl`| recall a variable. ex: ```'name' rcl```
|
||||
|`purge`| delete a variable. ex: ```'name' purge```
|
||||
|`vars`| list all variables
|
||||
|`clusr`| erase all variables
|
||||
|`edit`| edit a variable content
|
||||
|`sto+`| add to a stored variable. ex: 1 'name' sto+ 'name' 2 sto+
|
||||
|`sto-`| substract to a stored variable. ex: 1 'name' sto- 'name' 2 sto-
|
||||
|`sto*`| multiply a stored variable. ex: 3 'name' sto* 'name' 2 sto*
|
||||
|`sto/`| divide a stored variable. ex: 3 'name' sto/ 'name' 2 sto/
|
||||
|`sneg`| negate a variable. ex: 'name' sneg
|
||||
|`sinv`| inverse a variable. ex: 1 'name' sinv
|
||||
| keyword | description |
|
||||
|---------|-----------------------------------------------------------------|
|
||||
| `sto` | store a variable. ex: ```1 'name' sto``` |
|
||||
| `rcl` | recall a variable. ex: ```'name' rcl``` |
|
||||
| `purge` | delete a variable. ex: ```'name' purge``` |
|
||||
| `vars` | list all variables |
|
||||
| `clusr` | erase all variables |
|
||||
| `edit` | edit a variable content |
|
||||
| `sto+` | add to a stored variable. ex: 1 'name' sto+ 'name' 2 sto+ |
|
||||
| `sto-` | substract to a stored variable. ex: 1 'name' sto- 'name' 2 sto- |
|
||||
| `sto*` | multiply a stored variable. ex: 3 'name' sto* 'name' 2 sto* |
|
||||
| `sto/` | divide a stored variable. ex: 3 'name' sto/ 'name' 2 sto/ |
|
||||
| `sneg` | negate a variable. ex: 'name' sneg |
|
||||
| `sinv` | inverse a variable. ex: 1 'name' sinv |
|
||||
|
||||
### program
|
||||
|
||||
|keyword|description|
|
||||
|-|-|
|
||||
|`eval`| evaluate (run) a program, or recall a variable. ex: `'my_prog' eval`
|
||||
|`->`| load program local variables. ex: `<< -> n m << 0 n m for i i + next >> >>`
|
||||
| keyword | description |
|
||||
|---------|-----------------------------------------------------------------------------|
|
||||
| `eval` | evaluate (run) a program, or recall a variable. ex: `'my_prog' eval` |
|
||||
| `->` | load program local variables. ex: `<< -> n m << 0 n m for i i + next >> >>` |
|
||||
|
||||
### trig on reals and complexes
|
||||
|
||||
|keyword|description|
|
||||
|-|-|
|
||||
|`pi`| pi constant
|
||||
|`sin`| sinus
|
||||
|`asin`| arg sinus
|
||||
|`cos`| cosinus
|
||||
|`acos`| arg cosinus
|
||||
|`tan`| tangent
|
||||
|`atan`| arg tangent
|
||||
|`d->r`| convert degrees to radians
|
||||
|`r->d`| convert radians to degrees
|
||||
| keyword | description |
|
||||
|---------|----------------------------|
|
||||
| `pi` | pi constant |
|
||||
| `sin` | sinus |
|
||||
| `asin` | arg sinus |
|
||||
| `cos` | cosinus |
|
||||
| `acos` | arg cosinus |
|
||||
| `tan` | tangent |
|
||||
| `atan` | arg tangent |
|
||||
| `d->r` | convert degrees to radians |
|
||||
| `r->d` | convert radians to degrees |
|
||||
|
||||
### logs on reals and complexes
|
||||
|
||||
|keyword|description|
|
||||
|-|-|
|
||||
|`e`| Euler constant
|
||||
|`ln` `log`| logarithm base e
|
||||
|`lnp1`| ln(1+x) which is useful when x is close to 0
|
||||
|`exp`| exponential
|
||||
|`expm`| exp(x)-1 which is useful when x is close to 0
|
||||
|`log10`| logarithm base 10
|
||||
|`alog10` `exp10`| exponential base 10
|
||||
|`log2`| logarithm base 2
|
||||
|`alog2` `exp2`| exponential base 2
|
||||
|`sinh`| hyperbolic sine
|
||||
|`asinh`| inverse hyperbolic sine
|
||||
|`cosh`| hyperbolic cosine
|
||||
|`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
|
||||
| keyword | description |
|
||||
|------------------|-----------------------------------------------|
|
||||
| `e` | Euler constant |
|
||||
| `ln` `log` | logarithm base e |
|
||||
| `lnp1` | ln(1+x) which is useful when x is close to 0 |
|
||||
| `exp` | exponential |
|
||||
| `expm` | exp(x)-1 which is useful when x is close to 0 |
|
||||
| `log10` | logarithm base 10 |
|
||||
| `alog10` `exp10` | exponential base 10 |
|
||||
| `log2` | logarithm base 2 |
|
||||
| `alog2` `exp2` | exponential base 2 |
|
||||
| `sinh` | hyperbolic sine |
|
||||
| `asinh` | inverse hyperbolic sine |
|
||||
| `cosh` | hyperbolic cosine |
|
||||
| `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 |
|
||||
|
||||
### default
|
||||
|
||||
|
@ -333,46 +330,58 @@ Default rounding mode is 'nearest'
|
|||
|
||||
## Tests
|
||||
|
||||
- A set of complete test sheets are given in the [test](https://github.com/louisrubet/rpn/tree/master/test) subdirectory. Each version is fully tested before delivery
|
||||
- A set of complete test sheets are given in the [test](https://github.com/louisrubet/rpn/tree/master/test) subdirectory.
|
||||
|
||||
- All tests are run each at each pull on a pull request branch. Memory tests (`valgrind`) are also executed on each tests.
|
||||
|
||||
- Test sheets syntax is
|
||||
|
||||
```
|
||||
# cat my_test_sheet.txt
|
||||
```shell
|
||||
cat my_test_sheet.md
|
||||
|
||||
## Test sheet example
|
||||
default erase
|
||||
# Test sheet example
|
||||
`default del`
|
||||
|
||||
## test step 1
|
||||
|
||||
`1 dup 1 +`
|
||||
|
||||
# test step 1
|
||||
1 dup 1 +
|
||||
-> stack size should be 2
|
||||
-> stack should be 1, 2
|
||||
-> error should be 0
|
||||
erase
|
||||
|
||||
# test step 2
|
||||
2 4 / 0.5 ==
|
||||
-> stack should be 1, 2
|
||||
|
||||
-> error should be 0
|
||||
|
||||
`del`
|
||||
|
||||
## test step 2
|
||||
|
||||
`2 4 / 0.5 ==`
|
||||
|
||||
-> stack should be 1
|
||||
erase
|
||||
|
||||
`del`
|
||||
```
|
||||
|
||||
- Test sheet can be played with the command `test`
|
||||
|
||||
```
|
||||
rpn> "my_test_sheet.txt"
|
||||
"my_test_sheet.txt"
|
||||
rpn> test
|
||||
```rpn
|
||||
rpn> "my_test_sheet.md" test
|
||||
|
||||
rpn version is v2.3.2-68-g60099e3
|
||||
|
||||
my_test_sheet.md: Test sheet example
|
||||
## test step 1 PASSED
|
||||
## test step 2 PASSED
|
||||
run 2 tests: 2 passed, 0 failed (4 steps: 4 passed, 0 failed)
|
||||
|
||||
my_test_sheet.txt: Test sheet example
|
||||
# test step 1 PASSED
|
||||
# test step 2 PASSED
|
||||
my_test_sheet.txt: run 2 tests: 2 passed, 0 failed (4 steps: 4 passed, 0 failed)
|
||||
Total: run 2 tests: 2 passed, 0 failed (4 steps: 4 passed, 0 failed)
|
||||
rpn>
|
||||
```
|
||||
|
||||
- Please follow these rules to write correct test sheets:
|
||||
- make the test sheet begins by `default erase`
|
||||
|
||||
- make the test sheet begins by `default del`
|
||||
|
||||
- the 3 existing tests are `-> stack size should be (number)` `-> stack should be (values separated by commas)` `-> error should be (error number)`
|
||||
|
||||
|
|
23
METHODS.md
23
METHODS.md
|
@ -1,23 +0,0 @@
|
|||
# Development methods
|
||||
|
||||
## Versioning
|
||||
|
||||
The software version number conforms to [SemVer semantic 2.0.0](https://semver.org/), aka `v<MAJOR>.<MINOR>.<PATCH>`
|
||||
|
||||
Suffixes `alpha` or `beta` are not used
|
||||
|
||||
## Workflow
|
||||
|
||||
This project conforms to the usual **gitflow** workflow as seen in ![gitflow image](https://i.stack.imgur.com/QxVmJ.png)
|
||||
|
||||
Report to Vincent Driessen page [a-successful-git-branching-model](http://nvie.com/posts/a-successful-git-branching-model/) for a detailed explanation of this workflow
|
||||
|
||||
Branches are named as follows:
|
||||
|
||||
|branch name| description |
|
||||
|-|-|
|
||||
|`master`|usual main git branch, always holds the last software version and its tag plus documents not changing the sw version |
|
||||
|`hotfix-vx.y.z`|hotfixes branches|
|
||||
|`release-vx.y.0`|releases branches, patch number should always be 0 here|
|
||||
|`develop`|dev branch|
|
||||
|`any name`|feature branches|
|
146
README.md
146
README.md
|
@ -1,67 +1,139 @@
|
|||
# **rpn** - **R**everse **P**olish **N**otation language [![License: LGPLv3](https://www.gnu.org/graphics/lgplv3-88x31.png)](https://www.gnu.org/licenses/lgpl-3.0.en.html)
|
||||
# **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> 1 2 + 2 sqrt
|
||||
```rpn
|
||||
rpn> 1 2 +
|
||||
3
|
||||
rpn> 2 sqrt
|
||||
2> 3
|
||||
1> 1.4142135623730950488
|
||||
1> 1.4142135623730950488016887242096980786
|
||||
```
|
||||
|
||||
### arbitrary precision provided by GNU MPFR
|
||||
### Manipulating reals, complexes, strings, symbols on a stack
|
||||
|
||||
```rpn
|
||||
rpn> 1 2 + 2
|
||||
2> 3
|
||||
1> 2
|
||||
rpn> r->c sq conj (1,1) /
|
||||
(-3.5,-8.5)
|
||||
```
|
||||
|
||||
```rpn
|
||||
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
|
||||
rpn> << -> x << x 3 ^ 2 x * - 3 + >> >> 'f' sto
|
||||
rpn> 2 f
|
||||
7
|
||||
rpn> (1,2) f
|
||||
(-10,-6)
|
||||
```
|
||||
|
||||
### Arbitrary precision
|
||||
|
||||
```rpn
|
||||
rpn> 256 prec
|
||||
rpn> pi
|
||||
3.1415926535897932384626433832795028841971693993751058209749445923078164062862
|
||||
rpn>
|
||||
```
|
||||
|
||||
### variables, programs
|
||||
```
|
||||
rpn> << rot * swap 2 / chs dup sq rot - sqrt >> 'quad' sto
|
||||
rpn> << -> x y << x y + ln >> >> 'P' sto
|
||||
### Variables, structured programming
|
||||
|
||||
```rpn
|
||||
rpn> 0 1 10000 for i i sq + next
|
||||
333383335000
|
||||
```
|
||||
|
||||
### and a bunch of functions
|
||||
```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 150 possibilities? (y or n)
|
||||
nop pow fp >= dupn next sto* exp
|
||||
help sqrt min < pick step sto/ expm
|
||||
h sq max <= depth ift sneg log10
|
||||
? sqr re != roll ifte sinv alog10
|
||||
quit abs im == rolld do eval exp10
|
||||
q dec conj and over until -> log2
|
||||
exit hex arg or ->str unti pi alog2
|
||||
test bin c->r xor str-> while sin exp2
|
||||
version base r->c not chr whil asin sinh
|
||||
uname sign p->r same num repeat cos asinh
|
||||
history % r->p swap size repea acos cosh
|
||||
+ %CH std drop pos sto tan acosh
|
||||
- mod fix drop2 sub rcl atan tanh
|
||||
chs fact sci dropn if purge d->r atanh
|
||||
neg mant prec del then vars r->d time
|
||||
* xpon round erase else clusr e date
|
||||
/ floor default rot end edit ln ticks
|
||||
inv ceil type dup start sto+ log
|
||||
^ ip > dup2 for sto- lnp1
|
||||
Display all 146 possibilities? (y or n)
|
||||
nop pow conj < pick step eval exp10
|
||||
help sqrt arg <= depth ift -> log2
|
||||
h sq c->r != roll ifte pi alog2
|
||||
? abs r->c == rolld do sin exp2
|
||||
quit sign p->r and over until asin sinh
|
||||
q % r->p or ->str while cos asinh
|
||||
exit %CH std xor str-> repeat acos cosh
|
||||
test mod fix not chr sto tan acosh
|
||||
version fact sci same num rcl atan tanh
|
||||
uname mant prec swap size purge d->r atanh
|
||||
history xpon round drop pos vars r->d time
|
||||
+ floor default drop2 sub clusr e date
|
||||
- ceil type dropn if edit ln ticks
|
||||
* ip hex del then sto+ log
|
||||
/ fp dec erase else sto- lnp1
|
||||
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
|
||||
```
|
||||
|
||||
## Download
|
||||
|
||||
deb, rpm and tgz files can be found [there](liv/)
|
||||
Available as source code and flatpak under [flathub](https://flathub.org/apps/category/Science) (_coming soon_).
|
||||
|
||||
## Manual
|
||||
|
||||
A reference manual is provided [here](MANUAL.md)
|
||||
|
||||
## Methods
|
||||
|
||||
Development methods are set at [this page](METHODS.md)
|
||||
|
||||
## Generation
|
||||
|
||||
Generation instructions can be found [here](GENERATION.md)
|
||||
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 and superior
|
||||
|
||||
```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++ mpfr mpfr-devel
|
||||
git clone https://github.com/louisrubet/rpn/
|
||||
mkdir -p rpn/build && cd rpn/build
|
||||
cmake ..
|
||||
make -j
|
||||
sudo make install
|
||||
```
|
||||
|
||||
## Contact, contribution, bug report
|
||||
|
||||
|
|
7
TODO.md
Normal file
7
TODO.md
Normal file
|
@ -0,0 +1,7 @@
|
|||
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
|
10
doc/rpn.1
10
doc/rpn.1
|
@ -1,10 +0,0 @@
|
|||
.TH RPN 1
|
||||
.SH NAME
|
||||
rpn \- Reverse Polish Notation calculator
|
||||
.SH SYNOPSIS
|
||||
.B rpn
|
||||
.IR commands ...
|
||||
.SH DESCRIPTION
|
||||
.B rpn is a full-featured math-oriented structured language inspired by Hewlett-Packard Reverse Polish Lisp
|
||||
|
||||
Please visit https://github.com/louisrubet/rpn for source code, a manual and generation instructions
|
|
@ -1 +1 @@
|
|||
Subproject commit 6a37ee0e9f117bb9950159545ecdbdbba41799bf
|
||||
Subproject commit e20977cddd1a5bec3672ecd1a388a5f9ffdefa9a
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
1
mpreal
Submodule
1
mpreal
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit c45d0d522c9bd0dd16d7aac25fa0862dd074ddb0
|
|
@ -1,3 +1,10 @@
|
|||
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
|
||||
AccessModifierOffset: -3
|
11
src/CPPLINT.cfg
Normal file
11
src/CPPLINT.cfg
Normal file
|
@ -0,0 +1,11 @@
|
|||
set noparent
|
||||
|
||||
filter=-whitespace/line_length
|
||||
|
||||
# avoid "Is this a non-const reference? If so, make const or use a pointer"
|
||||
# as it seems to be allowed by google now , cf https://github.com/cpplint/cpplint/issues/148
|
||||
filter=-runtime/references
|
||||
|
||||
# avoid "Include the directory when naming header files"
|
||||
# for files not being in a subdirectory
|
||||
filter=-build/include_subdir
|
123
src/constant.h
123
src/constant.h
|
@ -1,123 +0,0 @@
|
|||
#ifndef CONSTANT_H
|
||||
#define CONSTANT_H
|
||||
|
||||
#include <mpfr.h>
|
||||
|
||||
// default values
|
||||
//
|
||||
|
||||
// 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 */
|
||||
#define DEFAULT_DECIMAL_DIGITS 38
|
||||
|
||||
// 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 {
|
||||
ret_ok,
|
||||
ret_unknown_err,
|
||||
ret_missing_operand,
|
||||
ret_bad_operand_type,
|
||||
ret_out_of_range,
|
||||
ret_unknown_variable,
|
||||
ret_internal,
|
||||
ret_deadly,
|
||||
ret_good_bye,
|
||||
ret_not_impl,
|
||||
ret_nop,
|
||||
ret_syntax,
|
||||
ret_div_by_zero,
|
||||
ret_runtime_error,
|
||||
ret_abort_current_entry,
|
||||
ret_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
|
88
src/debug.h
88
src/debug.h
|
@ -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
|
37
src/escape.h
37
src/escape.h
|
@ -1,37 +0,0 @@
|
|||
#ifndef ESCAPE_H
|
||||
#define ESCAPE_H
|
||||
|
||||
// ANSI escape sequences
|
||||
|
||||
// foreground colors
|
||||
#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
|
73
src/input.cc
Normal file
73
src/input.cc
Normal file
|
@ -0,0 +1,73 @@
|
|||
// Copyright (c) 2014-2022 Louis Rubet
|
||||
|
||||
#include "input.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
vector<string>* Input::ac_list_ = nullptr;
|
||||
|
||||
Input::Input(string& entry, vector<string>& autocompletion_list, string prompt, string multiline_prompt)
|
||||
: status(InputStatus::kContinue) {
|
||||
char* c_entry = nullptr;
|
||||
bool multiline = false;
|
||||
int entry_len;
|
||||
|
||||
ac_list_ = &autocompletion_list;
|
||||
|
||||
// linenoise for entry
|
||||
linenoiseSetCompletionCallback(EntryCompletionGenerator);
|
||||
while (status == InputStatus::kContinue) {
|
||||
// get user entry
|
||||
if (multiline)
|
||||
c_entry = linenoise(multiline_prompt.c_str(), &entry_len);
|
||||
else
|
||||
c_entry = linenoise(prompt.c_str(), &entry_len);
|
||||
|
||||
// Ctrl-C
|
||||
if (linenoiseKeyType() == 1) {
|
||||
if (entry_len > 0 || multiline)
|
||||
status = InputStatus::kAbort;
|
||||
else
|
||||
status = InputStatus::kCtrlc;
|
||||
} else if (linenoiseKeyType() == 3) {
|
||||
multiline = true;
|
||||
if (c_entry != nullptr) entry += c_entry;
|
||||
entry += " ";
|
||||
} else {
|
||||
if (c_entry != nullptr) {
|
||||
entry += c_entry;
|
||||
// keep history
|
||||
if (c_entry[0] != 0) (void)linenoiseHistoryAdd(entry.c_str());
|
||||
status = InputStatus::kOk;
|
||||
} else {
|
||||
status = InputStatus::kError;
|
||||
}
|
||||
}
|
||||
}
|
||||
free(c_entry);
|
||||
}
|
||||
|
||||
/// @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 Input::EntryCompletionGenerator(const char* text, linenoiseCompletions* lc) {
|
||||
if (Input::ac_list_ == nullptr || text == nullptr) return;
|
||||
|
||||
int text_len = strnlen(text, 6);
|
||||
|
||||
if (text_len == 0)
|
||||
// propose all keywords
|
||||
for (string& ac : *Input::ac_list_) linenoiseAddCompletion(lc, ac.c_str());
|
||||
else
|
||||
// propose only keywords matching to text begining
|
||||
for (string& ac : *Input::ac_list_)
|
||||
// compare list entry with text, return if match
|
||||
if (ac.compare(0, text_len, text) == 0) linenoiseAddCompletion(lc, ac.c_str());
|
||||
}
|
||||
|
||||
void Input::Preload(const char* preloadText) {
|
||||
if (preloadText != nullptr) linenoisePreloadBuffer(preloadText);
|
||||
}
|
25
src/input.h
Normal file
25
src/input.h
Normal file
|
@ -0,0 +1,25 @@
|
|||
// Copyright (c) 2014-2022 Louis Rubet
|
||||
|
||||
#ifndef SRC_INPUT_HPP_
|
||||
#define SRC_INPUT_HPP_
|
||||
|
||||
#include <cstdio>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
using std::string, std::vector;
|
||||
|
||||
#include "linenoise.h"
|
||||
|
||||
class Input {
|
||||
public:
|
||||
Input(string& entry, vector<string>& autocompletion_list, string prompt = "rpn> ", string multiline_prompt = ">");
|
||||
enum class InputStatus { kOk, kContinue, kAbort, kCtrlc, kError } status;
|
||||
|
||||
static void Preload(const char* preloadText);
|
||||
|
||||
private:
|
||||
static void EntryCompletionGenerator(const char* text, linenoiseCompletions* lc);
|
||||
static vector<string>* ac_list_;
|
||||
};
|
||||
|
||||
#endif // SRC_INPUT_HPP_
|
259
src/lexer.cc
Normal file
259
src/lexer.cc
Normal file
|
@ -0,0 +1,259 @@
|
|||
// Copyright (c) 2014-2022 Louis Rubet
|
||||
|
||||
#include "lexer.h"
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wpedantic" // allow designated initializers
|
||||
|
||||
bool Lexer::Analyse(string& entry, map<string, ReservedWord>& keywords, vector<SynElement>& elements,
|
||||
vector<SynError>& errors) {
|
||||
size_t jump;
|
||||
for (size_t i = 0; i < entry.size(); i++) {
|
||||
if (isspace(entry[i])) continue;
|
||||
SynElement element{.re = nullptr, .im = nullptr};
|
||||
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;
|
||||
else
|
||||
return false; // no object of any type could be found, this is a lexer error
|
||||
}
|
||||
|
||||
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& next_idx, vector<SynError>& errors __attribute__((unused)),
|
||||
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({kString, .value = entry.substr(idx + 1, i - idx - 1)});
|
||||
next_idx = i + 1;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
elements.push_back({kString, .value = entry.substr(idx + 1, entry.size() - idx - 1)});
|
||||
next_idx = entry.size();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Lexer::ParseSymbol(string& entry, size_t idx, size_t& next_idx, vector<SynError>& errors __attribute__((unused)),
|
||||
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({kSymbol, .value = entry.substr(idx + 1, i - idx - 1), .auto_eval = false});
|
||||
next_idx = i + 1;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
elements.push_back({kSymbol, .value = entry.substr(idx + 1, entry.size() - idx - 1)});
|
||||
next_idx = entry.size();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Lexer::ParseProgram(string& entry, size_t idx, size_t& next_idx, vector<SynError>& errors __attribute__((unused)),
|
||||
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({kProgram, .value = prg});
|
||||
next_idx = i + 2;
|
||||
return true;
|
||||
} else {
|
||||
countNested--;
|
||||
}
|
||||
}
|
||||
}
|
||||
string prg = entry.substr(idx + 2, entry.size() - idx - 2);
|
||||
Trim(prg);
|
||||
elements.push_back({kProgram, .value = prg});
|
||||
next_idx = entry.size();
|
||||
return true;
|
||||
}
|
||||
|
||||
int Lexer::GetBaseAt(string& entry, size_t& next_idx, bool& positive) {
|
||||
// a regex could be "([+-])?((0[xX])|([0-9][0-9]?[bB]))"
|
||||
// regex is not use because dramatically slow
|
||||
// entry is scanned from idxStart, searching for [s]abc (sign and 3 first chars)
|
||||
size_t scan = 0;
|
||||
next_idx = 0;
|
||||
positive = true;
|
||||
if (scan >= entry.size()) return 10;
|
||||
if (entry[scan] == '+') {
|
||||
scan++;
|
||||
next_idx = scan;
|
||||
} else if (entry[scan] == '-') {
|
||||
scan++;
|
||||
next_idx = scan;
|
||||
positive = false;
|
||||
}
|
||||
if (scan + 2 >= entry.size()) return 10;
|
||||
char a = entry[scan];
|
||||
char b = entry[scan + 1];
|
||||
char c = 0;
|
||||
if (scan + 2 < entry.size()) c = entry[scan + 2];
|
||||
if (a == '0') {
|
||||
if (b == 'x' || b == 'X') {
|
||||
next_idx = scan + 2;
|
||||
return 16;
|
||||
}
|
||||
if (b == 'b' || b == 'B') {
|
||||
next_idx = scan + 2;
|
||||
return 2;
|
||||
}
|
||||
} else if (isdigit(a)) {
|
||||
if (b == 'b' || b == 'B') {
|
||||
next_idx = scan + 2;
|
||||
return static_cast<int>(a - '0');
|
||||
}
|
||||
if (isdigit(b) && (c == 'b' || c == 'B')) {
|
||||
next_idx = scan + 3;
|
||||
return 10 * static_cast<int>(a - '0') + static_cast<int>(b - '0');
|
||||
}
|
||||
}
|
||||
return 10;
|
||||
}
|
||||
|
||||
bool Lexer::GetNUmberAt(string& entry, size_t idx, size_t& next_idx, int& base, mpreal** r, char delim) {
|
||||
stringstream ss;
|
||||
string token;
|
||||
bool positive = true;
|
||||
|
||||
next_idx = idx;
|
||||
|
||||
ss.str(entry.substr(idx));
|
||||
if (getline(ss, token, delim)) {
|
||||
size_t numberIdx;
|
||||
next_idx = token.size() + idx + 1;
|
||||
Trim(token);
|
||||
base = GetBaseAt(token, numberIdx, positive);
|
||||
if (base < 2 || base > 62) return false;
|
||||
if (numberIdx != 0) token = token.substr(numberIdx);
|
||||
*r = new mpreal;
|
||||
if (mpfr_set_str((*r)->mpfr_ptr(), token.c_str(), base, mpreal::get_default_rnd()) == 0) {
|
||||
if (!positive) *(*r) = -*(*r);
|
||||
return true;
|
||||
} else {
|
||||
delete *r;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
next_idx = token.size() + idx + 1;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Lexer::ParseNumber(string& entry, size_t idx, size_t& next_idx, vector<SynError>& errors,
|
||||
vector<SynElement>& elements) {
|
||||
mpreal* r = nullptr;
|
||||
int base = 10;
|
||||
if (GetNUmberAt(entry, idx, next_idx, base, &r)) {
|
||||
elements.push_back({kNumber, .re = r, .re_base = base});
|
||||
return true;
|
||||
} else {
|
||||
errors.push_back({entry.size(), "unterminated number"});
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool Lexer::ParseComplex(string& entry, size_t idx, size_t& next_idx, vector<SynError>& errors __attribute__((unused)),
|
||||
vector<SynElement>& elements) {
|
||||
mpreal* re = nullptr;
|
||||
mpreal* im = nullptr;
|
||||
int re_base, im_base = 10;
|
||||
if (idx + 1 == entry.size()) {
|
||||
elements.push_back({kSymbol, .value = entry.substr(idx, entry.size() - idx)});
|
||||
next_idx = entry.size();
|
||||
return true; // complex format error, return a symbol
|
||||
}
|
||||
if (!GetNUmberAt(entry, idx + 1, next_idx, re_base, &re, ',')) {
|
||||
elements.push_back({kSymbol, .value = entry.substr(idx, entry.size() - idx)});
|
||||
next_idx = entry.size();
|
||||
return true; // complex format error, return a symbol
|
||||
}
|
||||
|
||||
size_t i = next_idx;
|
||||
if (i >= entry.size()) {
|
||||
elements.push_back({kSymbol, .value = entry.substr(idx, entry.size() - idx)});
|
||||
next_idx = entry.size();
|
||||
if (re != nullptr) delete re;
|
||||
if (im != nullptr) delete im;
|
||||
return true; // complex format error, return a symbol
|
||||
}
|
||||
|
||||
if (!GetNUmberAt(entry, i, next_idx, im_base, &im, ')')) {
|
||||
elements.push_back({kSymbol, .value = entry.substr(idx, entry.size() - idx)});
|
||||
next_idx = entry.size();
|
||||
if (re != nullptr) delete re;
|
||||
if (im != nullptr) delete im;
|
||||
return true; // complex format error, return a symbol
|
||||
}
|
||||
elements.push_back({kComplex, .re = re, .im = im, .re_base = re_base, .im_base = im_base});
|
||||
next_idx++;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Lexer::ParseReserved(string& entry, size_t idx, size_t& next_idx, 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});
|
||||
next_idx = token.size() + idx;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Lexer::ParseUnknown(string& entry, size_t idx, size_t& next_idx, vector<SynElement>& elements) {
|
||||
stringstream ss(entry.substr(idx));
|
||||
string token;
|
||||
ss >> token;
|
||||
elements.push_back({kSymbol, .value = token, .auto_eval = true});
|
||||
next_idx = token.size() + idx;
|
||||
return true;
|
||||
}
|
||||
|
||||
#pragma GCC diagnostic pop
|
73
src/lexer.h
Normal file
73
src/lexer.h
Normal file
|
@ -0,0 +1,73 @@
|
|||
// Copyright (c) 2014-2022 Louis Rubet
|
||||
|
||||
#ifndef SRC_LEXER_HPP_
|
||||
#define SRC_LEXER_HPP_
|
||||
|
||||
#include <mpreal.h>
|
||||
using mpfr::mpreal;
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
using std::map, std::string, std::vector;
|
||||
|
||||
#include "object.h"
|
||||
|
||||
class Lexer {
|
||||
public:
|
||||
// a structure to describe a syntaxical element and its value
|
||||
struct SynElement {
|
||||
ObjectType type;
|
||||
string value;
|
||||
mpreal* re;
|
||||
mpreal* im;
|
||||
int re_base;
|
||||
int im_base;
|
||||
program_fn_t fn;
|
||||
bool auto_eval;
|
||||
};
|
||||
|
||||
struct SynError {
|
||||
size_t indx;
|
||||
string err;
|
||||
};
|
||||
|
||||
struct ReservedWord {
|
||||
ObjectType 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 Analyse(string& entry, map<string, ReservedWord>& keywords, vector<SynElement>& elements,
|
||||
vector<SynError>& errors);
|
||||
|
||||
private:
|
||||
bool ParseString(string& entry, size_t idx, size_t& next_idx, vector<SynError>& errors,
|
||||
vector<SynElement>& elements);
|
||||
bool ParseSymbol(string& entry, size_t idx, size_t& next_idx, vector<SynError>& errors,
|
||||
vector<SynElement>& elements);
|
||||
bool ParseProgram(string& entry, size_t idx, size_t& next_idx, vector<SynError>& errors,
|
||||
vector<SynElement>& elements);
|
||||
bool ParseNumber(string& entry, size_t idx, size_t& next_idx, vector<SynError>& errors,
|
||||
vector<SynElement>& elements);
|
||||
bool ParseComplex(string& entry, size_t idx, size_t& next_idx, vector<SynError>& errors,
|
||||
vector<SynElement>& elements);
|
||||
bool ParseReserved(string& entry, size_t idx, size_t& next_idx, vector<SynElement>& elements,
|
||||
map<string, ReservedWord>& keywords);
|
||||
bool ParseUnknown(string& entry, size_t idx, size_t& next_idx, vector<SynElement>& elements);
|
||||
|
||||
void Trim(string& s);
|
||||
int GetBaseAt(string& entry, size_t& next_idx, bool& positive);
|
||||
bool GetNUmberAt(string& entry, size_t idx, size_t& next_idx, int& base, mpreal** r, char delim = ' ');
|
||||
};
|
||||
|
||||
#endif // SRC_LEXER_HPP_
|
146
src/main.cc
Normal file
146
src/main.cc
Normal file
|
@ -0,0 +1,146 @@
|
|||
// Copyright (c) 2014-2022 Louis Rubet
|
||||
|
||||
#include <pwd.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <cerrno>
|
||||
#include <csignal>
|
||||
#include <iostream>
|
||||
using std::cerr;
|
||||
|
||||
// internal includes
|
||||
#include "input.h"
|
||||
#include "program.h"
|
||||
|
||||
/// @brief actions to be done at rpn exit
|
||||
///
|
||||
static void ExitInteractive() {
|
||||
struct passwd* pw = getpwuid(getuid());
|
||||
if (pw != nullptr) {
|
||||
stringstream history_path;
|
||||
history_path << pw->pw_dir << "/.RpnHistory";
|
||||
|
||||
// trunc current history
|
||||
ofstream history(history_path.str(), ios_base::out | ios_base::trunc);
|
||||
history.close();
|
||||
|
||||
// save it
|
||||
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
|
||||
///
|
||||
static void EnterInteractive() {
|
||||
struct passwd* pw = getpwuid(getuid());
|
||||
if (pw != nullptr) {
|
||||
stringstream history_path;
|
||||
history_path << pw->pw_dir << "/.RpnHistory";
|
||||
|
||||
// don't care about errors
|
||||
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 CtrlHandler(int sig __attribute__((unused)), siginfo_t* siginfo __attribute__((unused)),
|
||||
void* context __attribute__((unused))) {
|
||||
ExitInteractive();
|
||||
}
|
||||
|
||||
/// @brief setup signals handlers to stop with honours
|
||||
///
|
||||
/// @param prog the prog to catch the signals to, must be checked not nullptr by user
|
||||
///
|
||||
static void CatchSignals() {
|
||||
struct sigaction act = {0};
|
||||
|
||||
act.sa_sigaction = &CtrlHandler;
|
||||
act.sa_flags = SA_SIGINFO;
|
||||
if (sigaction(SIGINT, &act, nullptr) < 0)
|
||||
cerr << "Warning, Ctrl-C cannot be caught [errno=" << errno << ' ' << strerror(errno) << "']" << endl;
|
||||
}
|
||||
|
||||
/// @brief rpn entry point
|
||||
///
|
||||
/// @param argc command line args nb
|
||||
/// @param argv command line args nb
|
||||
/// @return int 0=ok
|
||||
///
|
||||
int main(int argc, char* argv[]) {
|
||||
int ret = 0;
|
||||
bool go_on = true;
|
||||
|
||||
// apply default configuration
|
||||
program::ApplyDefault();
|
||||
|
||||
// run with interactive prompt
|
||||
if (argc == 1) {
|
||||
// init history
|
||||
EnterInteractive();
|
||||
|
||||
// user could stop prog with CtrlC
|
||||
CatchSignals();
|
||||
|
||||
// entry loop
|
||||
heap heap;
|
||||
rpnstack stack;
|
||||
while (go_on) {
|
||||
//
|
||||
// make program from interactive entry
|
||||
program prog(stack, heap);
|
||||
string entry;
|
||||
switch (Input(entry, program::GetAutocompletionWords()).status) {
|
||||
case Input::InputStatus::kOk:
|
||||
// run it
|
||||
if (prog.Parse(entry) == kOk && prog.Run() == kGoodbye)
|
||||
go_on = false;
|
||||
else
|
||||
prog.ShowStack();
|
||||
break;
|
||||
case Input::InputStatus::kCtrlc:
|
||||
go_on = false;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// manage history and exit
|
||||
ExitInteractive();
|
||||
} else { // run with cmd line arguments
|
||||
heap heap;
|
||||
rpnstack stack;
|
||||
program prog(stack, heap);
|
||||
string entry;
|
||||
int i;
|
||||
|
||||
// make one string from entry
|
||||
for (i = 1; i < argc; i++) entry += string(argv[i]) + ' ';
|
||||
|
||||
// make program
|
||||
ret = prog.Parse(entry);
|
||||
if (ret == kOk) {
|
||||
// user could stop prog with CtrlC
|
||||
CatchSignals();
|
||||
|
||||
// run it
|
||||
ret = prog.Run();
|
||||
prog.ShowStack(false);
|
||||
}
|
||||
}
|
||||
|
||||
mpfr_free_cache();
|
||||
|
||||
if (ret != kOk) return EXIT_FAILURE;
|
||||
return EXIT_SUCCESS;
|
||||
}
|
170
src/main.cpp
170
src/main.cpp
|
@ -1,170 +0,0 @@
|
|||
// 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>
|
||||
|
||||
// internal includes
|
||||
#include "program.hpp"
|
||||
|
||||
static heap s_global_heap;
|
||||
static stack s_global_stack;
|
||||
static program* s_prog_to_interrupt = NULL;
|
||||
|
||||
/// @brief actions to be done at rpn exit
|
||||
///
|
||||
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);
|
||||
|
||||
// trunc current history
|
||||
ofstream history(history_path, 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));
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief actions to be done at rpn exit
|
||||
///
|
||||
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);
|
||||
|
||||
// don't care about errors
|
||||
linenoiseHistorySetMaxLen(HISTORY_FILE_MAX_LINES);
|
||||
linenoiseHistoryLoad(history_path);
|
||||
}
|
||||
}
|
||||
|
||||
/// @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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/// @brief setup signals handlers to stop with honours
|
||||
///
|
||||
/// @param prog the prog to catch the signals to, must be checked not NULL by user
|
||||
///
|
||||
static void catch_signals(program* prog) {
|
||||
struct sigaction act;
|
||||
|
||||
s_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));
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
/// @brief rpn entry point
|
||||
///
|
||||
/// @param argc command line args nb
|
||||
/// @param argv command line args nb
|
||||
/// @return int 0=ok
|
||||
///
|
||||
int main(int argc, char* argv[]) {
|
||||
int ret = 0;
|
||||
bool go_on = true;
|
||||
|
||||
// apply default configuration
|
||||
program::apply_default();
|
||||
|
||||
// run with interactive prompt
|
||||
if (argc == 1) {
|
||||
// init history
|
||||
init_interactive_rpn();
|
||||
|
||||
// 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:
|
||||
// user could stop prog with CtrlC
|
||||
catch_signals(&prog);
|
||||
|
||||
// run it
|
||||
if (prog.run(s_global_stack, s_global_heap) == ret_good_bye)
|
||||
go_on = false;
|
||||
else
|
||||
program::show_stack(s_global_stack);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// manage history and exit
|
||||
exit_interactive_rpn();
|
||||
}
|
||||
// run with cmd line arguments
|
||||
else {
|
||||
program prog;
|
||||
string entry;
|
||||
int i;
|
||||
|
||||
// make one string from entry
|
||||
for (i = 1; i < argc; i++) {
|
||||
entry += argv[i];
|
||||
entry += ' ';
|
||||
}
|
||||
|
||||
// make program
|
||||
ret = program::parse(entry.c_str(), prog);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
mpfr_free_cache();
|
||||
|
||||
return ret;
|
||||
}
|
179
src/mpreal-out.cc
Normal file
179
src/mpreal-out.cc
Normal file
|
@ -0,0 +1,179 @@
|
|||
// Copyright (c) 2014-2022 Louis Rubet
|
||||
|
||||
#include "mpreal-out.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
ostream& MprealFmtOutput(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 IsMin(const mpreal& p, mpfr_prec_t prec) {
|
||||
// see mpfr_vasprintf code
|
||||
bool ret;
|
||||
int round_away;
|
||||
switch (mpreal::get_default_rnd()) {
|
||||
case MPFR_RNDA:
|
||||
round_away = 1;
|
||||
break;
|
||||
case MPFR_RNDD:
|
||||
round_away = (p < 0.0);
|
||||
break;
|
||||
case MPFR_RNDU:
|
||||
round_away = (p > 0.0);
|
||||
break;
|
||||
case MPFR_RNDN: {
|
||||
/* compare |p| to y = 0.5*10^(-prec) */
|
||||
mpfr_t y;
|
||||
mpfr_exp_t e = std::max(p.get_prec(), static_cast<mpfr_exp_t>(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 OutBase(ostream& out, int base) {
|
||||
if (base == 16)
|
||||
out << "0x";
|
||||
else if (base == 2)
|
||||
out << "0b";
|
||||
else
|
||||
out << base << "b";
|
||||
}
|
||||
|
||||
static ostream& OutSingular(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 (isnan(value)) {
|
||||
out << "nan";
|
||||
} else if (isinf(value)) {
|
||||
if (value < 0) out << '-';
|
||||
out << "inf";
|
||||
} else {
|
||||
// zero
|
||||
if (value < 0) out << '-'; // signed zero is allowed
|
||||
if (write_after_sign != NULL) out << write_after_sign;
|
||||
OutBase(out, base);
|
||||
out << '0';
|
||||
if (digits > 0) {
|
||||
out << '.';
|
||||
for (int i = 0; i < digits; i++) out << '0';
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
static ostream& OutLittleNumber(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 (value < 0) out << '-';
|
||||
if (write_after_sign != NULL) out << write_after_sign;
|
||||
OutBase(out, base);
|
||||
out << '0';
|
||||
if (digits > 0) {
|
||||
out << '.';
|
||||
for (int i = 0; i < digits - 1; i++) out << '0';
|
||||
|
||||
if (IsMin(value, digits))
|
||||
out << '1';
|
||||
else
|
||||
out << '0';
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
static ostream& OutNumber(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 + static_cast<int>(exp) + 1, value.mpfr_srcptr(),
|
||||
mpreal::get_default_rnd());
|
||||
int nexp = static_cast<int>(exp);
|
||||
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 (nexp < 0) {
|
||||
out << '0';
|
||||
if (digits > 0) {
|
||||
out << '.';
|
||||
for (int i = 0; i < -nexp; i++) out << '0';
|
||||
out << str;
|
||||
for (int i = 0; i < digits - len + nexp; i++) out << '0';
|
||||
}
|
||||
} else {
|
||||
if (nexp == 0) {
|
||||
out << '0';
|
||||
} else {
|
||||
for (int i = 0; i < nexp; i++) out << print_from[i];
|
||||
}
|
||||
if (digits > 0) {
|
||||
out << '.';
|
||||
|
||||
int remaining = std::min(static_cast<int>(strlen(print_from)) - nexp - 1, digits) + 1;
|
||||
for (int i = nexp; i < remaining + nexp; i++) out << print_from[i];
|
||||
for (int i = 0; i < nexp + digits - len; i++) out << '0';
|
||||
}
|
||||
}
|
||||
}
|
||||
mpfr_free_str(str);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
ostream& MprealOutputNBase(ostream& out, int base, const mpreal& value) {
|
||||
// see mpfr_vasprintf code
|
||||
int digits = 0; // forced 0 digits after separator
|
||||
|
||||
// singular = nan, +/-inf or zero
|
||||
if (iszero(value) || isnan(value) || isinf(value)) return OutSingular(out, base, value);
|
||||
|
||||
mpfr_exp_t exp = mpfr_get_exp(value.mpfr_srcptr());
|
||||
if (exp < -digits) return OutLittleNumber(out, base, value);
|
||||
|
||||
return OutNumber(out, base, value);
|
||||
}
|
16
src/mpreal-out.h
Normal file
16
src/mpreal-out.h
Normal file
|
@ -0,0 +1,16 @@
|
|||
// Copyright (c) 2014-2022 Louis Rubet
|
||||
|
||||
#ifndef SRC_MPREAL_OUT_HPP_
|
||||
#define SRC_MPREAL_OUT_HPP_
|
||||
|
||||
#include <mpreal.h>
|
||||
using mpfr::mpreal;
|
||||
|
||||
#include <string>
|
||||
#include <ostream>
|
||||
using std::string, std::ostream;
|
||||
|
||||
ostream& MprealFmtOutput(ostream& out, const string& fmt, const mpreal& value);
|
||||
ostream& MprealOutputNBase(ostream& out, int base, const mpreal& value);
|
||||
|
||||
#endif // SRC_MPREAL_OUT_HPP_
|
43
src/object.cc
Normal file
43
src/object.cc
Normal file
|
@ -0,0 +1,43 @@
|
|||
// Copyright (c) 2014-2022 Louis Rubet
|
||||
|
||||
#include "object.h"
|
||||
|
||||
// number statics
|
||||
Number::mode_enum Number::mode = Number::kDefaultMode;
|
||||
int Number::digits = kDefaultDecimalDigits;
|
||||
|
||||
ostream& Number::ShowValue(ostream& out, const mpreal& value, mode_enum mode, int digits, int base) {
|
||||
stringstream format;
|
||||
switch (base) {
|
||||
case 10:
|
||||
switch (mode) {
|
||||
case kStd:
|
||||
format << "%." << digits << "R*g";
|
||||
break;
|
||||
case kFix:
|
||||
format << "%." << digits << "R*f";
|
||||
break;
|
||||
case kSci:
|
||||
format << "%." << digits << "R*e";
|
||||
break;
|
||||
}
|
||||
return MprealFmtOutput(out, format.str(), value);
|
||||
case 16:
|
||||
if (mode == kStd) {
|
||||
return MprealOutputNBase(out, base, value); // custom function, as none available in mpfr
|
||||
} else {
|
||||
format << "%." << digits << "R*a"; // C99 format
|
||||
return MprealFmtOutput(out, format.str(), value);
|
||||
}
|
||||
case 2:
|
||||
if (mode == kStd) {
|
||||
return MprealOutputNBase(out, base, value); // custom function, as none available in mpfr
|
||||
} else {
|
||||
format << "%." << digits << "R*b"; // C99 format
|
||||
return MprealFmtOutput(out, format.str(), value);
|
||||
}
|
||||
default:
|
||||
// base other than 2, 10, 16
|
||||
return MprealOutputNBase(out, base, value);
|
||||
}
|
||||
}
|
228
src/object.cpp
228
src/object.cpp
|
@ -1,228 +0,0 @@
|
|||
#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;
|
||||
}
|
||||
}
|
198
src/object.h
Normal file
198
src/object.h
Normal file
|
@ -0,0 +1,198 @@
|
|||
// Copyright (c) 2014-2022 Louis Rubet
|
||||
|
||||
#ifndef SRC_OBJECT_H_
|
||||
#define SRC_OBJECT_H_
|
||||
|
||||
#include <mpreal.h>
|
||||
using mpfr::mpreal;
|
||||
|
||||
#include <complex>
|
||||
#include <ostream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
using std::complex, std::ostream, std::string, std::stringstream;
|
||||
|
||||
#include "mpreal-out.h"
|
||||
|
||||
// definitions for objects
|
||||
///
|
||||
typedef enum {
|
||||
kOk,
|
||||
kUnknownError,
|
||||
kMissingOperand,
|
||||
kBadOperandType,
|
||||
kOutOfRange,
|
||||
kUnknownVariable,
|
||||
kInternalError,
|
||||
kDeadlyError,
|
||||
kGoodbye,
|
||||
kNotImplemented,
|
||||
kNop,
|
||||
kSyntaxError,
|
||||
kDivByZero,
|
||||
kRuntimeError,
|
||||
kAbortCurrentEntry,
|
||||
kOutOfMemory,
|
||||
kBadValue,
|
||||
kTestFailed
|
||||
} RetValue;
|
||||
|
||||
typedef enum {
|
||||
kUndef,
|
||||
kNumber, // 3.1416, 1e-1234, 0x12ab, 2b110, 50ba12
|
||||
kComplex, // (1,2)
|
||||
kString, // "string"
|
||||
kSymbol, // 'symbol'
|
||||
kProgram, // << instructions >> «instructions»
|
||||
kKeyword, // langage (reserved) keyword (rot, dup, swap ..)
|
||||
kBranch // langage (reserved) branch keyword (for, if, then ..)
|
||||
} ObjectType;
|
||||
|
||||
class program;
|
||||
class Branch;
|
||||
|
||||
typedef void (program::*program_fn_t)(void);
|
||||
typedef size_t (program::*branch_fn_t)(Branch&);
|
||||
|
||||
/// @brief Object - a generic stack object
|
||||
///
|
||||
struct Object {
|
||||
explicit Object(ObjectType type = kUndef) : _type(type) {}
|
||||
virtual ~Object() {}
|
||||
ObjectType _type;
|
||||
virtual Object* Clone() {
|
||||
Object* o = new Object();
|
||||
if (o != nullptr) *o = *this;
|
||||
return o;
|
||||
}
|
||||
|
||||
virtual string Name() { return string("Object"); }
|
||||
virtual ostream& Show(ostream& out) {
|
||||
out << "(" << Name() << " - unknown representation)";
|
||||
return out;
|
||||
}
|
||||
friend ostream& operator<<(ostream& os, Object* o) { return o->Show(os); }
|
||||
};
|
||||
|
||||
/// @brief stack objects derived from Object
|
||||
///
|
||||
struct Number : Object {
|
||||
Number() : Object(kNumber), base(10) {}
|
||||
explicit Number(const mpreal& value__, int base__ = 10) : Object(kNumber), base(base__), value(value__) {}
|
||||
explicit Number(int value__, int base__ = 10) : Object(kNumber), base(base__), value(value__) {}
|
||||
|
||||
int base;
|
||||
mpreal value;
|
||||
|
||||
virtual Object* Clone() { return new Number(value, base); }
|
||||
virtual string Name() { return string("number"); }
|
||||
virtual ostream& Show(ostream& out) { return ShowValue(out, value, mode, digits, base); }
|
||||
|
||||
// representation mode
|
||||
typedef enum { kStd, kFix, kSci } mode_enum;
|
||||
static mode_enum mode;
|
||||
static constexpr mode_enum kDefaultMode = Number::kStd;
|
||||
|
||||
// precision
|
||||
static constexpr mpfr_prec_t kMpfrDefaultPrecBits = 128;
|
||||
static constexpr int kDefaultDecimalDigits = 38;
|
||||
static int digits;
|
||||
|
||||
// clang-format on
|
||||
static ostream& ShowValue(ostream& out, const mpreal& value, mode_enum mode, int digits, int base);
|
||||
};
|
||||
|
||||
/// @brief stack objects inheriting Object
|
||||
///
|
||||
struct Complex : Object {
|
||||
Complex() : Object(kComplex), re_base(10), im_base(10) {}
|
||||
explicit Complex(complex<mpreal>& value__, int re_base__ = 10, int im_base__ = 10)
|
||||
: Object(kComplex), re_base(re_base__), im_base(im_base__) {
|
||||
value = value__;
|
||||
}
|
||||
explicit Complex(mpreal& re__, mpreal& im__, int re_base__ = 10, int im_base__ = 10)
|
||||
: Object(kComplex), re_base(re_base__), im_base(im_base__) {
|
||||
value.real(re__);
|
||||
value.imag(im__);
|
||||
}
|
||||
|
||||
int re_base, im_base;
|
||||
complex<mpreal> value;
|
||||
|
||||
virtual Object* Clone() { return new Complex(value, re_base, im_base); }
|
||||
virtual string Name() { return string("complex"); }
|
||||
virtual ostream& Show(ostream& out) {
|
||||
out << '(';
|
||||
Number::ShowValue(out, value.real(), Number::mode, Number::digits, re_base);
|
||||
out << ',';
|
||||
Number::ShowValue(out, value.imag(), Number::mode, Number::digits, im_base);
|
||||
return out << ')';
|
||||
}
|
||||
};
|
||||
|
||||
struct String : Object {
|
||||
String() : Object(kString) {}
|
||||
explicit String(const string& value__) : Object(kString), value(value__) {}
|
||||
virtual Object* Clone() { return new String(value); }
|
||||
virtual string Name() { return string("string"); }
|
||||
virtual ostream& Show(ostream& out) { return out << "\"" << value << "\""; }
|
||||
string value;
|
||||
};
|
||||
|
||||
struct Program : Object {
|
||||
Program() : Object(kProgram) {}
|
||||
explicit Program(const string& value__) : Object(kProgram), value(value__) {}
|
||||
virtual Object* Clone() { return new Program(value); }
|
||||
virtual string Name() { return string("program"); }
|
||||
virtual ostream& Show(ostream& out) { return out << "«" << value << "»"; }
|
||||
string value;
|
||||
};
|
||||
|
||||
struct Symbol : Object {
|
||||
explicit Symbol(bool auto_eval__ = true) : Object(kSymbol), auto_eval(auto_eval__) {}
|
||||
explicit Symbol(const string& value__, bool auto_eval__ = true)
|
||||
: Object(kSymbol), value(value__), auto_eval(auto_eval__) {}
|
||||
virtual Object* Clone() { return new Symbol(value, auto_eval); }
|
||||
virtual string Name() { return string("symbol"); }
|
||||
virtual ostream& Show(ostream& out) { return out << "'" << value << "'"; }
|
||||
string value;
|
||||
bool auto_eval;
|
||||
};
|
||||
|
||||
struct Keyword : Object {
|
||||
Keyword() : Object(kKeyword) {}
|
||||
explicit Keyword(program_fn_t fn__, const string& value__) : Object(kKeyword), fn(fn__), value(value__) {}
|
||||
virtual Object* Clone() { return new Keyword(fn, value); }
|
||||
virtual string Name() { return string("keyword"); }
|
||||
program_fn_t fn;
|
||||
string value;
|
||||
};
|
||||
|
||||
struct Branch : Object {
|
||||
Branch() : Object(kBranch) {}
|
||||
explicit Branch(branch_fn_t fn__, const string& value__) : Object(kBranch), fn(fn__), value(value__) {
|
||||
arg1 = static_cast<size_t>(-1);
|
||||
arg2 = static_cast<size_t>(-1);
|
||||
arg3 = static_cast<size_t>(-1);
|
||||
arg_bool = 0;
|
||||
}
|
||||
explicit Branch(Branch& other) : Object(kBranch) {
|
||||
fn = other.fn;
|
||||
arg1 = other.arg1;
|
||||
arg2 = other.arg2;
|
||||
arg3 = other.arg3;
|
||||
first_index = other.first_index;
|
||||
last_index = other.last_index;
|
||||
arg_bool = other.arg_bool;
|
||||
value = other.value;
|
||||
}
|
||||
virtual Object* Clone() { return new Branch(*this); }
|
||||
virtual string Name() { return string("branch"); }
|
||||
branch_fn_t fn;
|
||||
size_t arg1, arg2, arg3;
|
||||
mpreal first_index, last_index;
|
||||
bool arg_bool;
|
||||
string value;
|
||||
};
|
||||
|
||||
#endif // SRC_OBJECT_H_
|
282
src/object.hpp
282
src/object.hpp
|
@ -1,282 +0,0 @@
|
|||
#ifndef OBJECT_HPP
|
||||
#define OBJECT_HPP
|
||||
|
||||
#include <mpfr.h>
|
||||
#include <string.h>
|
||||
|
||||
// definitions for objects
|
||||
////
|
||||
typedef enum {
|
||||
cmd_undef,
|
||||
cmd_number, // floating point number
|
||||
cmd_complex, // complex, couple of floating point numbers
|
||||
cmd_string, // string like "string"
|
||||
cmd_symbol, // symbol like 'symbol'
|
||||
cmd_program, // program like << instructions >>
|
||||
cmd_keyword, // langage keyword
|
||||
cmd_branch, // langage branch keyword
|
||||
cmd_max
|
||||
} cmd_type_t;
|
||||
|
||||
class program;
|
||||
class branch;
|
||||
|
||||
typedef void (program::*program_fn_t)(void);
|
||||
typedef 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
|
||||
cmd_type_t _type;
|
||||
unsigned int _size;
|
||||
|
||||
//
|
||||
unsigned int size() { return _size; }
|
||||
|
||||
void show(FILE* stream = stdout);
|
||||
|
||||
//
|
||||
static const char* s_cmd_type_string[cmd_max];
|
||||
};
|
||||
|
||||
/// @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;
|
||||
|
||||
// publics
|
||||
number() { _type = cmd_number; }
|
||||
|
||||
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); }
|
||||
|
||||
// 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;
|
||||
};
|
||||
|
||||
/// @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();
|
||||
}
|
||||
|
||||
void move() {
|
||||
re()->move();
|
||||
im()->move();
|
||||
}
|
||||
|
||||
static unsigned int calc_size() {
|
||||
return (unsigned int)(sizeof(complex) + 2 * (sizeof(floating_t) + floating_t::s_mpfr_prec_bytes));
|
||||
}
|
||||
};
|
||||
|
||||
/// @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];
|
||||
};
|
||||
|
||||
/// @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];
|
||||
};
|
||||
|
||||
/// @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];
|
||||
};
|
||||
|
||||
/// @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];
|
||||
};
|
||||
|
||||
/// @brief object branch
|
||||
///
|
||||
struct branch : public object {
|
||||
//
|
||||
void set(branch_fn_t fn, const char* value, unsigned int len) {
|
||||
_type = 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;
|
||||
}
|
||||
}
|
||||
|
||||
// branch function
|
||||
branch_fn_t _fn;
|
||||
// args used by cmd_branch cmds
|
||||
int arg1, arg2, arg3;
|
||||
number *farg1, *farg2;
|
||||
bool arg_bool;
|
||||
|
||||
// length of _value, not includiong the terminal '\0'
|
||||
unsigned int _len;
|
||||
char _value[0];
|
||||
};
|
||||
|
||||
#endif
|
627
src/parse.cpp
627
src/parse.cpp
|
@ -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;
|
||||
}
|
724
src/program.cc
Normal file
724
src/program.cc
Normal file
|
@ -0,0 +1,724 @@
|
|||
// Copyright (c) 2014-2022 Louis Rubet
|
||||
|
||||
#include "program.h"
|
||||
|
||||
//< language reserved keywords (allowed types are kKeyword, kBranch or kUndef)
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wcast-function-type" // allow casting kBranch callbacks
|
||||
vector<program::keyword_t> program::keywords_{
|
||||
// GENERAL
|
||||
{kUndef, "", nullptr, "\nGENERAL"},
|
||||
{kKeyword, "nop", &program::RpnNop, "no operation"},
|
||||
{kKeyword, "help", &program::RpnHelp, "this help message"},
|
||||
{kKeyword, "h", &program::RpnHelp, ""},
|
||||
{kKeyword, "?", &program::RpnHelp, ""},
|
||||
{kKeyword, "quit", &program::RpnQuit, "quit software"},
|
||||
{kKeyword, "q", &program::RpnQuit, ""},
|
||||
{kKeyword, "exit", &program::RpnQuit, ""},
|
||||
{kKeyword, "test", &program::RpnTest, ""}, // not seen by user
|
||||
{kKeyword, "version", &program::RpnVersion, "show rpn version"},
|
||||
{kKeyword, "uname", &program::RpnUname, "show rpn complete identification string"},
|
||||
{kKeyword, "history", &program::RpnHistory, "see commands history"},
|
||||
|
||||
// USUAL OPERATIONS ON REALS AND COMPLEXES
|
||||
{kUndef, "", nullptr, "\nUSUAL OPERATIONS ON REALS AND COMPLEXES"},
|
||||
{kKeyword, "+", &program::RpnPlus, "addition"},
|
||||
{kKeyword, "-", &program::RpnMinus, "substraction"},
|
||||
{kKeyword, "*", &program::RpnMul, "multiplication"},
|
||||
{kKeyword, "/", &program::RpnDiv, "division"},
|
||||
{kKeyword, "inv", &program::RpnInv, "inverse"},
|
||||
{kKeyword, "chs", &program::RpnNeg, "negation"},
|
||||
{kKeyword, "neg", &program::RpnNeg, ""},
|
||||
{kKeyword, "^", &program::RpnPower, "power"},
|
||||
{kKeyword, "pow", &program::RpnPower, ""},
|
||||
{kKeyword, "sqrt", &program::RpnSquareroot, "RpnSquare root"},
|
||||
{kKeyword, "sq", &program::RpnSquare, "RpnSquare"},
|
||||
{kKeyword, "abs", &program::RpnAbs, "absolute value (norm for a complex)"},
|
||||
{kKeyword, "sign", &program::RpnSign, "sign of a number or z/|z| for a complex"},
|
||||
|
||||
// OPERATIONS ON REALS
|
||||
{kUndef, "", nullptr, "\nOPERATIONS ON REALS"},
|
||||
{kKeyword, "%", &program::RpnPurcent, "purcent"},
|
||||
{kKeyword, "%CH", &program::RpnPurcentCH, "inverse purcent"},
|
||||
{kKeyword, "mod", &program::RpnModulo, "modulo"},
|
||||
{kKeyword, "fact", &program::RpnFact, "n! for integer n or Gamma(x+1) for fractional x"},
|
||||
{kKeyword, "mant", &program::RpnMant, "mantissa of a real number"},
|
||||
{kKeyword, "xpon", &program::RpnXpon, "exponant of a real number"},
|
||||
{kKeyword, "floor", &program::RpnFloor, "largest number <="},
|
||||
{kKeyword, "ceil", &program::RpnCeil, "smallest number >="},
|
||||
{kKeyword, "ip", &program::RpnIp, "integer part"},
|
||||
{kKeyword, "fp", &program::RpnFp, "fractional part"},
|
||||
{kKeyword, "min", &program::RpnMin, "min of 2 real numbers"},
|
||||
{kKeyword, "max", &program::RpnMax, "max of 2 real numbers"},
|
||||
|
||||
// OPERATIONS ON COMPLEXES
|
||||
{kUndef, "", nullptr, "\nOPERATIONS ON COMPLEXES"},
|
||||
{kKeyword, "re", &program::RpnReal, "complex real part"},
|
||||
{kKeyword, "im", &program::RpnImag, "complex imaginary part"},
|
||||
{kKeyword, "conj", &program::RpnConj, "complex conjugate"},
|
||||
{kKeyword, "arg", &program::RpnArg, "complex argument in radians"},
|
||||
{kKeyword, "c->r", &program::RpnC2r, "transform a complex in 2 reals"},
|
||||
{kKeyword, "r->c", &program::RpnR2c, "transform 2 reals in a complex"},
|
||||
{kKeyword, "p->r", &program::RpnP2r, "cartesian to polar"},
|
||||
{kKeyword, "r->p", &program::RpnR2p, "polar to cartesian"},
|
||||
|
||||
// MODE
|
||||
{kUndef, "", nullptr, "\nMODE"},
|
||||
{kKeyword, "std", &program::RpnStd, "standard floating numbers representation. ex: std"},
|
||||
{kKeyword, "fix", &program::RpnFix, "fixed point representation. ex: 6 fix"},
|
||||
{kKeyword, "sci", &program::RpnSci, "scientific floating point representation. ex: 20 sci"},
|
||||
{kKeyword, "prec", &program::RpnPrecision, "set float precision in bits. ex: 256 prec"},
|
||||
{kKeyword, "round", &program::RpnRound,
|
||||
"set float rounding mode in \n\t\"nearest (even)\", \"toward zero\", \"toward "
|
||||
"+inf\", \"toward -inf\", \"away from zero\", \"faithful rounding\", \"nearest (away from zero)\""
|
||||
"\n\tex: \"nearest (even)\" round"},
|
||||
{kKeyword, "default", &program::RpnDefault, "set float representation and precision to default"},
|
||||
{kKeyword, "type", &program::RpnType, "show type of stack first entry"},
|
||||
{kKeyword, "hex", &program::RpnHex, "hexadecimal representation, applies on stack level 0 only"},
|
||||
{kKeyword, "dec", &program::RpnDec, "decimal representation, applies on stack level 0 only"},
|
||||
{kKeyword, "bin", &program::RpnBin, "binary representation, applies on stack level 0 only"},
|
||||
{kKeyword, "base", &program::RpnBase, "arbitrary base representation, applies on stack level 0 only"},
|
||||
|
||||
// TESTS
|
||||
{kUndef, "", nullptr, "\nTEST"},
|
||||
{kKeyword, ">", &program::RpnSup, "binary operator >"},
|
||||
{kKeyword, ">=", &program::RpnSupEq, "binary operator >="},
|
||||
{kKeyword, "<", &program::RpnInf, "binary operator <"},
|
||||
{kKeyword, "<=", &program::RpnInfEq, "binary operator <="},
|
||||
{kKeyword, "!=", &program::RpnDiff, "binary operator != (different)"},
|
||||
{kKeyword, "==", &program::RpnEq, "binary operator == (equal)"},
|
||||
{kKeyword, "and", &program::RpnTestAnd, "boolean operator and"},
|
||||
{kKeyword, "or", &program::RpnTestOr, "boolean operator or"},
|
||||
{kKeyword, "xor", &program::RpnTestXor, "boolean operator xor"},
|
||||
{kKeyword, "not", &program::RpnTestNot, "boolean operator not"},
|
||||
{kKeyword, "same", &program::RpnSame, "boolean operator same (equal)"},
|
||||
|
||||
// STACK
|
||||
{kUndef, "", nullptr, "\nSTACK"},
|
||||
{kKeyword, "swap", &program::RpnSwap, "swap 2 first stack entries"},
|
||||
{kKeyword, "drop", &program::RpnDrop, "drop first stack entry"},
|
||||
{kKeyword, "drop2", &program::RpnDrop2, "drop 2 first stack entries"},
|
||||
{kKeyword, "dropn", &program::RpnDropn, "drop n first stack entries"},
|
||||
{kKeyword, "del", &program::RpnErase, "drop all stack entries"},
|
||||
{kKeyword, "erase", &program::RpnErase, ""},
|
||||
{kKeyword, "rot", &program::RpnRot, "rotate 3 first stack entries"},
|
||||
{kKeyword, "dup", &program::RpnDup, "duplicate first stack entry"},
|
||||
{kKeyword, "dup2", &program::RpnDup2, "duplicate 2 first stack entries"},
|
||||
{kKeyword, "dupn", &program::RpnDupn, "duplicate n first stack entries"},
|
||||
{kKeyword, "pick", &program::RpnPick, "push a copy of the given stack level onto the stack"},
|
||||
{kKeyword, "depth", &program::RpnDepth, "give stack depth"},
|
||||
{kKeyword, "roll", &program::RpnRoll, "move a stack entry to the top of the stack"},
|
||||
{kKeyword, "rolld", &program::RpnRolld, "move the element on top of the stack to a higher stack position"},
|
||||
{kKeyword, "over", &program::RpnOver, "push a copy of the element in stack level 2 onto the stack"},
|
||||
|
||||
// STRING
|
||||
{kUndef, "", nullptr, "\nSTRING"},
|
||||
{kKeyword, "->str", &program::RpnInstr, "convert an object into a string"},
|
||||
{kKeyword, "str->", &program::RpnStrout, "convert a string into an object"},
|
||||
{kKeyword, "chr", &program::RpnChr, "convert ASCII character code in stack level 1 into a string"},
|
||||
{kKeyword, "num", &program::RpnNum,
|
||||
"return ASCII code of the first character of the string in stack level 1 as a real number"},
|
||||
{kKeyword, "size", &program::RpnStrsize, "return the length of the string"},
|
||||
{kKeyword, "pos", &program::RpnStrpos, "seach for the string in level 1 within the string in level 2"},
|
||||
{kKeyword, "sub", &program::RpnStrsub, "return a substring of the string in level 3"},
|
||||
|
||||
// BRANCH
|
||||
{kUndef, "", nullptr, "\nBRANCH"},
|
||||
{kBranch, "if", (program_fn_t)&program::RpnIf,
|
||||
"if <test-instruction> then <true-instructions> else <false-instructions> "
|
||||
"end"},
|
||||
{kBranch, "then", (program_fn_t)&program::RpnThen, "used with if"},
|
||||
{kBranch, "else", (program_fn_t)&program::RpnElse, "used with if"},
|
||||
{kBranch, "end", (program_fn_t)&program::RpnEnd, "used with various branch instructions"},
|
||||
{kBranch, "start", (program_fn_t)&program::RpnStart, "<start> <end> start <instructions> next|<step> step"},
|
||||
{kBranch, "for", (program_fn_t)&program::RpnFor, "<start> <end> for <variable> <instructions> next|<step> step"},
|
||||
{kBranch, "next", (program_fn_t)&program::RpnNext, "used with start and for"},
|
||||
{kBranch, "step", (program_fn_t)&program::RpnStep, "used with start and for"},
|
||||
{kKeyword, "ift", &program::RpnIft, "similar to if-then-end, <test-instruction> <true-instruction> ift"},
|
||||
{kKeyword, "ifte", &program::RpnIfte,
|
||||
"similar to if-then-else-end, <test-instruction> <true-instruction> "
|
||||
"<false-instruction> ifte"},
|
||||
{kBranch, "do", (program_fn_t)&program::RpnDo, "do <instructions> until <condition> end"},
|
||||
{kBranch, "until", (program_fn_t)&program::RpnUntil, "used with do"},
|
||||
{kBranch, "while", (program_fn_t)&program::RpnWhile, "while <test-instruction> repeat <loop-instructions> end"},
|
||||
{kBranch, "repeat", (program_fn_t)&program::RpnRepeat, "used with while"},
|
||||
|
||||
// STORE
|
||||
{kUndef, "", nullptr, "\nSTORE"},
|
||||
{kKeyword, "sto", &program::RpnSto, "store a variable. ex: 1 'name' sto"},
|
||||
{kKeyword, "rcl", &program::RpnRcl, "recall a variable. ex: 'name' rcl"},
|
||||
{kKeyword, "purge", &program::RpnPurge, "delete a variable. ex: 'name' purge"},
|
||||
{kKeyword, "vars", &program::RpnVars, "list all variables"},
|
||||
{kKeyword, "clusr", &program::RpnClusr, "erase all variables"},
|
||||
{kKeyword, "edit", &program::RpnEdit, "edit a variable content"},
|
||||
{kKeyword, "sto+", &program::RpnStoadd, "add to a stored variable. ex: 1 'name' sto+ 'name' 2 sto+"},
|
||||
{kKeyword, "sto-", &program::RpnStosub, "substract to a stored variable. ex: 1 'name' sto- 'name' 2 sto-"},
|
||||
{kKeyword, "sto*", &program::RpnStomul, "multiply a stored variable. ex: 3 'name' sto* 'name' 2 sto*"},
|
||||
{kKeyword, "sto/", &program::RpnStodiv, "divide a stored variable. ex: 3 'name' sto/ 'name' 2 sto/"},
|
||||
{kKeyword, "sneg", &program::RpnStoneg, "negate a variable. ex: 'name' sneg"},
|
||||
{kKeyword, "sinv", &program::RpnStoinv, "inverse a variable. ex: 1 'name' sinv"},
|
||||
// PROGRAM
|
||||
{kUndef, "", nullptr, "\nPROGRAM"},
|
||||
{kKeyword, "eval", &program::RpnEval, "evaluate (run) a program, or recall a variable. ex: 'my_prog' eval"},
|
||||
{kBranch, "->", (program_fn_t)&program::RpnInprog,
|
||||
"load program local variables. ex: << -> n m << 0 n m for i i + next >> "
|
||||
">>"},
|
||||
|
||||
// TRIG ON REALS AND COMPLEXES
|
||||
{kUndef, "", nullptr, "\nTRIG ON REALS AND COMPLEXES"},
|
||||
{kKeyword, "pi", &program::RpnPi, "pi constant"},
|
||||
{kKeyword, "sin", &program::RpnSin, "sinus"},
|
||||
{kKeyword, "asin", &program::RpnAsin, "arg sinus"},
|
||||
{kKeyword, "cos", &program::RpnCos, "cosinus"},
|
||||
{kKeyword, "acos", &program::RpnAcos, "arg cosinus"},
|
||||
{kKeyword, "tan", &program::RpnTan, "tangent"},
|
||||
{kKeyword, "atan", &program::RpnAtan, "arg tangent"},
|
||||
{kKeyword, "d->r", &program::RpnD2r, "convert degrees to radians"},
|
||||
{kKeyword, "r->d", &program::RpnR2d, "convert radians to degrees"},
|
||||
|
||||
// LOGS ON REALS AND COMPLEXES
|
||||
{kUndef, "", nullptr, "\nLOGS ON REALS AND COMPLEXES"},
|
||||
{kKeyword, "e", &program::RpnE, "Euler constant"},
|
||||
{kKeyword, "ln", &program::RpnLn, "logarithm base e"},
|
||||
{kKeyword, "log", &program::RpnLn, ""},
|
||||
{kKeyword, "lnp1", &program::RpnLnp1, "ln(1+x) which is useful when x is close to 0"},
|
||||
{kKeyword, "exp", &program::RpnExp, "exponential"},
|
||||
{kKeyword, "expm", &program::RpnExpm, "exp(x)-1 which is useful when x is close to 0"},
|
||||
{kKeyword, "log10", &program::RpnLog10, "logarithm base 10"},
|
||||
{kKeyword, "alog10", &program::RpnAlog10, "exponential base 10"},
|
||||
{kKeyword, "exp10", &program::RpnAlog10, ""},
|
||||
{kKeyword, "log2", &program::RpnLog2, "logarithm base 2"},
|
||||
{kKeyword, "alog2", &program::RpnAlog2, "exponential base 2"},
|
||||
{kKeyword, "exp2", &program::RpnAlog2, ""},
|
||||
{kKeyword, "sinh", &program::RpnSinh, "hyperbolic sine"},
|
||||
{kKeyword, "asinh", &program::RpnAsinh, "inverse hyperbolic sine"},
|
||||
{kKeyword, "cosh", &program::RpnCosh, "hyperbolic cosine"},
|
||||
{kKeyword, "acosh", &program::RpnAcosh, "inverse hyperbolic cosine"},
|
||||
{kKeyword, "tanh", &program::RpnTanh, "hyperbolic tangent"},
|
||||
{kKeyword, "atanh", &program::RpnAtanh, "inverse hyperbolic tangent"},
|
||||
|
||||
// TIME AND DATE
|
||||
{kUndef, "", nullptr, "\nTIME AND DATE"},
|
||||
{kKeyword, "time", &program::RpnTime, "time in local format"},
|
||||
{kKeyword, "date", &program::RpnDate, "date in local format"},
|
||||
{kKeyword, "ticks", &program::RpnTicks, "system tick in µs"},
|
||||
};
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
/// autocompletion vector for linenoise autocompletion
|
||||
vector<string>& program::GetAutocompletionWords() {
|
||||
static vector<string> autocompletion_words;
|
||||
if (autocompletion_words.empty())
|
||||
for (auto& kw : keywords_)
|
||||
if (!kw.name.empty()) autocompletion_words.push_back(kw.name);
|
||||
return autocompletion_words;
|
||||
}
|
||||
|
||||
/// @brief run a program on a stack and a heap
|
||||
///
|
||||
/// @return RetValue see this type
|
||||
///
|
||||
RetValue program::Run() {
|
||||
bool go_out = false;
|
||||
RetValue ret = kOk;
|
||||
|
||||
err_ = kOk;
|
||||
err_context_ = "";
|
||||
|
||||
// branches for 'if'
|
||||
ret = Preprocess();
|
||||
if (ret != kOk) {
|
||||
// free allocated
|
||||
for (Object* o : *this) delete o;
|
||||
local_heap_.clear();
|
||||
return ret;
|
||||
}
|
||||
|
||||
// iterate commands
|
||||
for (size_t i = 0; (go_out == false) && (i < size());) {
|
||||
Object* o = at(i);
|
||||
switch (o->_type) {
|
||||
// could be an auto-evaluated symbol
|
||||
case kSymbol:
|
||||
AutoRcl(reinterpret_cast<Symbol*>(o));
|
||||
i++;
|
||||
break;
|
||||
|
||||
// a keyword
|
||||
case kKeyword: {
|
||||
Keyword* k = reinterpret_cast<Keyword*>(o);
|
||||
// call the matching function
|
||||
(this->*(k->fn))();
|
||||
switch (err_) {
|
||||
// no pb -> go on
|
||||
case kOk:
|
||||
break;
|
||||
// explicit go out software
|
||||
case kGoodbye:
|
||||
go_out = true;
|
||||
ret = kGoodbye;
|
||||
break;
|
||||
default:
|
||||
// error: abort prog
|
||||
go_out = true;
|
||||
|
||||
// test error: make rpn return EXIT_FAILURE
|
||||
if (err_ == kTestFailed) ret = kTestFailed;
|
||||
|
||||
// error: show it
|
||||
if (ShowError(err_, err_context_) == kDeadlyError)
|
||||
// pb showing error -> go out software
|
||||
ret = kGoodbye;
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
break;
|
||||
}
|
||||
|
||||
// a branch keyword
|
||||
case kBranch: {
|
||||
// call matching function
|
||||
Branch* b = reinterpret_cast<Branch*>(o);
|
||||
size_t next_cmd = (this->*(b->fn))(*b);
|
||||
switch (next_cmd) {
|
||||
case kStepOut: // step out
|
||||
i++; // meaning 'next command'
|
||||
break;
|
||||
case kRtError: // runtime error
|
||||
(void)ShowError(err_, err_context_);
|
||||
go_out = true;
|
||||
break;
|
||||
default:
|
||||
i = next_cmd;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
// not a command, but a stack entry, manage it
|
||||
// copy the program stack entry to the running stack
|
||||
stack_.push_front(o->Clone());
|
||||
i++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// free allocated
|
||||
for (Object* o : *this) delete o;
|
||||
local_heap_.clear();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// @brief prepare a program for execution
|
||||
/// this is needed before a program can be run
|
||||
/// inner members of branch or keyword objects are filled by this function
|
||||
/// these inner members store for example the index of the next keyword to execute etc.
|
||||
///
|
||||
/// @return RetValue see this type
|
||||
///
|
||||
RetValue program::Preprocess() {
|
||||
struct if_layout_t {
|
||||
if_layout_t()
|
||||
: index_then_or_unti_or_repeat(-1),
|
||||
index_else(-1),
|
||||
index_end(-1),
|
||||
is_do_unti(false),
|
||||
is_while_repeat(false) {}
|
||||
int index_if_or_do_or_while;
|
||||
int index_then_or_unti_or_repeat;
|
||||
int index_else;
|
||||
int index_end;
|
||||
bool is_do_unti;
|
||||
bool is_while_repeat;
|
||||
};
|
||||
// for if-then-else-end
|
||||
vector<struct if_layout_t> vlayout;
|
||||
int layout_index = -1;
|
||||
// for start-end-step
|
||||
vector<int> vstart_index;
|
||||
|
||||
// analyse if-then-else-end branches
|
||||
// analyse start-{next, step} branches
|
||||
for (size_t i = 0; i < size(); i++) {
|
||||
if (at(i)->_type == kBranch) {
|
||||
Branch* k = reinterpret_cast<Branch*>(at(i));
|
||||
if (k->value == "if") {
|
||||
if_layout_t layout;
|
||||
layout.index_if_or_do_or_while = i;
|
||||
vlayout.push_back(layout);
|
||||
layout_index++;
|
||||
} else if (k->value == "then") {
|
||||
size_t next = i + 1;
|
||||
if (next >= size()) next = kStepOut;
|
||||
|
||||
// nothing after 'then' -> error
|
||||
if (next == kStepOut) {
|
||||
// error: show it
|
||||
ShowSyntaxError("missing end after then");
|
||||
return kSyntaxError;
|
||||
}
|
||||
if (layout_index < 0) {
|
||||
// error: show it
|
||||
ShowSyntaxError("missing if before then");
|
||||
return kSyntaxError;
|
||||
}
|
||||
if (vlayout[layout_index].index_then_or_unti_or_repeat != -1) {
|
||||
// error: show it
|
||||
ShowSyntaxError("duplicate then");
|
||||
return kSyntaxError;
|
||||
}
|
||||
vlayout[layout_index].index_then_or_unti_or_repeat = i;
|
||||
k->arg1 = next;
|
||||
k->arg3 = vlayout[layout_index].index_if_or_do_or_while;
|
||||
} else if (k->value == "else") {
|
||||
size_t next = i + 1;
|
||||
if (next >= size()) next = kStepOut;
|
||||
|
||||
// nothing after 'else' -> error
|
||||
if (next == kStepOut) {
|
||||
// error: show it
|
||||
ShowSyntaxError("missing end after else");
|
||||
return kSyntaxError;
|
||||
}
|
||||
if (layout_index < 0) {
|
||||
// error: show it
|
||||
ShowSyntaxError("missing if before else");
|
||||
return kSyntaxError;
|
||||
}
|
||||
if (vlayout[layout_index].index_then_or_unti_or_repeat == -1) {
|
||||
// error: show it
|
||||
ShowSyntaxError("missing then before else");
|
||||
return kSyntaxError;
|
||||
}
|
||||
if (vlayout[layout_index].index_else != -1) {
|
||||
// error: show it
|
||||
ShowSyntaxError("duplicate else");
|
||||
return kSyntaxError;
|
||||
}
|
||||
vlayout[layout_index].index_else = i;
|
||||
k->arg1 = next; // fill branch1 (if was false) of 'else'
|
||||
k->arg3 = vlayout[layout_index].index_if_or_do_or_while;
|
||||
reinterpret_cast<Branch*>(at(vlayout[layout_index].index_then_or_unti_or_repeat))->arg2 =
|
||||
next; // fill branch2 (if was false) of 'then'
|
||||
} else if (k->value == "start") {
|
||||
vstart_index.push_back(i);
|
||||
} else if (k->value == "for") {
|
||||
vstart_index.push_back(i);
|
||||
k->arg1 = i + 1; // arg1 points on symbol variable
|
||||
} else if (k->value == "next") {
|
||||
if (vstart_index.size() == 0) {
|
||||
// error: show it
|
||||
ShowSyntaxError("missing start or for before next");
|
||||
return kSyntaxError;
|
||||
}
|
||||
k->arg1 = vstart_index[vstart_index.size() - 1]; // 'next' arg1 = 'start' index
|
||||
reinterpret_cast<Branch*>(at(vstart_index[vstart_index.size() - 1]))->arg2 =
|
||||
i; // 'for' or 'start' arg2 = 'next' index
|
||||
vstart_index.pop_back();
|
||||
} else if (k->value == "step") {
|
||||
if (vstart_index.size() == 0) {
|
||||
// error: show it
|
||||
ShowSyntaxError("missing start or for before step");
|
||||
return kSyntaxError;
|
||||
}
|
||||
k->arg1 = vstart_index[vstart_index.size() - 1]; // fill 'step' branch1 = 'start' index
|
||||
reinterpret_cast<Branch*>(at(vstart_index[vstart_index.size() - 1]))->arg2 =
|
||||
i; // 'for' or 'start' arg2 = 'next' index
|
||||
vstart_index.pop_back();
|
||||
} else if (k->value == "->") {
|
||||
k->arg1 = i; // arg1 is '->' command index in program
|
||||
} else if (k->value == "do") {
|
||||
if_layout_t layout;
|
||||
layout.index_if_or_do_or_while = i;
|
||||
layout.is_do_unti = true;
|
||||
vlayout.push_back(layout);
|
||||
layout_index++;
|
||||
} else if (k->value == "until") {
|
||||
size_t next = i + 1;
|
||||
if (next >= size()) next = kStepOut;
|
||||
|
||||
// nothing after 'unti' -> error
|
||||
if (next == kStepOut) {
|
||||
// error: show it
|
||||
ShowSyntaxError("missing end");
|
||||
return kSyntaxError;
|
||||
}
|
||||
if (layout_index < 0 || !vlayout[layout_index].is_do_unti) {
|
||||
// error: show it
|
||||
ShowSyntaxError("missing do");
|
||||
return kSyntaxError;
|
||||
}
|
||||
if (vlayout[layout_index].index_then_or_unti_or_repeat != -1) {
|
||||
// error: show it
|
||||
ShowSyntaxError("duplicate until");
|
||||
return kSyntaxError;
|
||||
}
|
||||
vlayout[layout_index].index_then_or_unti_or_repeat = i;
|
||||
} else if (k->value == "while") {
|
||||
if_layout_t layout;
|
||||
layout.index_if_or_do_or_while = i;
|
||||
layout.is_while_repeat = true;
|
||||
vlayout.push_back(layout);
|
||||
layout_index++;
|
||||
} else if (k->value == "repeat") {
|
||||
if (layout_index < 0 || !vlayout[layout_index].is_while_repeat) {
|
||||
// error: show it
|
||||
ShowSyntaxError("missing while");
|
||||
return kSyntaxError;
|
||||
}
|
||||
if (vlayout[layout_index].index_then_or_unti_or_repeat != -1) {
|
||||
// error: show it
|
||||
ShowSyntaxError("duplicate repeat");
|
||||
return kSyntaxError;
|
||||
}
|
||||
vlayout[layout_index].index_then_or_unti_or_repeat = i;
|
||||
} else if (k->value == "end") {
|
||||
size_t next = i + 1;
|
||||
if (next >= size()) next = kStepOut;
|
||||
|
||||
if (layout_index < 0) {
|
||||
// error: show it
|
||||
ShowSyntaxError("missing branch instruction before end");
|
||||
return kSyntaxError;
|
||||
} else {
|
||||
if (vlayout[layout_index].is_do_unti) {
|
||||
// this end closes a do..unti
|
||||
if (vlayout[layout_index].index_end != -1) {
|
||||
// error: show it
|
||||
ShowSyntaxError("duplicate end");
|
||||
return kSyntaxError;
|
||||
}
|
||||
|
||||
if (vlayout[layout_index].index_then_or_unti_or_repeat == -1) {
|
||||
// error: show it
|
||||
ShowSyntaxError("missing until");
|
||||
return kSyntaxError;
|
||||
}
|
||||
|
||||
k->arg1 = vlayout[layout_index].index_if_or_do_or_while + 1;
|
||||
layout_index--;
|
||||
} else if (vlayout[layout_index].is_while_repeat) {
|
||||
// this end closes a while..repeat
|
||||
if (vlayout[layout_index].index_end != -1) {
|
||||
// error: show it
|
||||
ShowSyntaxError("duplicate end");
|
||||
return kSyntaxError;
|
||||
}
|
||||
|
||||
k->arg2 = vlayout[layout_index].index_if_or_do_or_while + 1;
|
||||
|
||||
if (vlayout[layout_index].index_then_or_unti_or_repeat == -1) {
|
||||
// error: show it
|
||||
ShowSyntaxError("missing repeat");
|
||||
return kSyntaxError;
|
||||
}
|
||||
|
||||
// fill 'repeat' arg1 with 'end+1'
|
||||
reinterpret_cast<Branch*>(at(vlayout[layout_index].index_then_or_unti_or_repeat))->arg1 = i + 1;
|
||||
layout_index--;
|
||||
} else {
|
||||
// this end closes an if..then..(else)
|
||||
if (vlayout[layout_index].index_end != -1) {
|
||||
// error: show it
|
||||
ShowSyntaxError("duplicate end");
|
||||
return kSyntaxError;
|
||||
}
|
||||
if (vlayout[layout_index].index_else != -1) {
|
||||
// fill 'end' branch of 'else'
|
||||
reinterpret_cast<Branch*>(at(vlayout[layout_index].index_else))->arg2 = i;
|
||||
} else {
|
||||
// fill 'end' branch of 'then'
|
||||
if (vlayout[layout_index].index_then_or_unti_or_repeat != -1) {
|
||||
reinterpret_cast<Branch*>(at(vlayout[layout_index].index_then_or_unti_or_repeat))
|
||||
->arg2 = i;
|
||||
} else {
|
||||
// error: show it
|
||||
ShowSyntaxError("missing then");
|
||||
return kSyntaxError;
|
||||
}
|
||||
}
|
||||
layout_index--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (layout_index >= 0) {
|
||||
// error: show it
|
||||
ShowSyntaxError("missing end");
|
||||
return kSyntaxError;
|
||||
}
|
||||
if (vstart_index.size() > 0) {
|
||||
// error: show it
|
||||
ShowSyntaxError("missing next or step after for or start");
|
||||
return kSyntaxError;
|
||||
}
|
||||
return kOk;
|
||||
}
|
||||
|
||||
/// @brief parse an entry string: cut it into objects chunks and add them to a program
|
||||
///
|
||||
/// @param entry the entry string
|
||||
/// @param prog the program to be filled
|
||||
/// @return RetValue see this type
|
||||
///
|
||||
RetValue program::Parse(string& entry) {
|
||||
static map<string, Lexer::ReservedWord> keywords_map;
|
||||
vector<Lexer::SynElement> elements;
|
||||
vector<Lexer::SynError> errors;
|
||||
RetValue ret = kOk;
|
||||
|
||||
// prepare map for finding reserved keywords
|
||||
if (keywords_map.empty())
|
||||
for (auto& kw : keywords_)
|
||||
if (!kw.name.empty()) keywords_map[kw.name] = {kw.type, kw.fn};
|
||||
|
||||
// separate the entry string
|
||||
if (Analyse(entry, keywords_map, elements, errors)) {
|
||||
// make objects from parsed elements
|
||||
for (Lexer::SynElement& element : elements) {
|
||||
switch (element.type) {
|
||||
case kNumber:
|
||||
push_back(new Number(*element.re, element.re_base));
|
||||
break;
|
||||
case kComplex:
|
||||
push_back(new Complex(*element.re, *element.im, element.re_base, element.im_base));
|
||||
break;
|
||||
case kString:
|
||||
push_back(new String(element.value));
|
||||
break;
|
||||
case kSymbol:
|
||||
push_back(new Symbol(element.value, element.auto_eval));
|
||||
break;
|
||||
case kProgram:
|
||||
push_back(new Program(element.value));
|
||||
break;
|
||||
case kKeyword:
|
||||
push_back(new Keyword(element.fn, element.value));
|
||||
break;
|
||||
case kBranch:
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wcast-function-type" // allow casting kBranch callbacks
|
||||
push_back(new Branch((branch_fn_t)element.fn, element.value));
|
||||
#pragma GCC diagnostic pop
|
||||
break;
|
||||
default:
|
||||
ShowError(kUnknownError, "error creating program from entry");
|
||||
break;
|
||||
}
|
||||
if (element.re != nullptr) delete element.re;
|
||||
if (element.im != nullptr) delete element.im;
|
||||
}
|
||||
} else {
|
||||
for (SynError& err : errors) ShowSyntaxError(err.err.c_str());
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// @brief show the last error set
|
||||
///
|
||||
/// @return RetValue see this type
|
||||
///
|
||||
RetValue program::ShowError() {
|
||||
RetValue ret;
|
||||
// clang-format off
|
||||
map<RetValue, string> errorStrings{{kOk, "ok"}, {kUnknownError, "unknown command"},
|
||||
{kMissingOperand, "missing operand"}, {kBadOperandType, "bad operand type"},
|
||||
{kOutOfRange, "out of range"}, {kUnknownVariable, "unknown variable"},
|
||||
{kInternalError, "internal error, aborting"}, {kDeadlyError, "deadly"},
|
||||
{kGoodbye, "goodbye"}, {kNotImplemented, "not implemented"},
|
||||
{kNop, "no operation"}, {kSyntaxError, "syntax error"},
|
||||
{kDivByZero, "division by zero"}, {kRuntimeError, "runtime error"},
|
||||
{kAbortCurrentEntry, "aborted current entry"}, {kOutOfMemory, "out of memory"},
|
||||
{kBadValue, "bad value"}, {kTestFailed, "test failed"}
|
||||
};
|
||||
// clang-format on
|
||||
cerr << err_context_ << ": error " << err_ << ": " << errorStrings[err_] << endl;
|
||||
switch (err_) {
|
||||
case kInternalError:
|
||||
case kDeadlyError:
|
||||
ret = kDeadlyError;
|
||||
break;
|
||||
default:
|
||||
ret = kOk;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// @brief record an error as the last error set and show it
|
||||
///
|
||||
/// @param err the error to record
|
||||
/// @param context a context string
|
||||
/// @return RetValue see this type
|
||||
///
|
||||
RetValue program::ShowError(RetValue err, string& context) {
|
||||
// record error
|
||||
err_ = err;
|
||||
err_context_ = context;
|
||||
return ShowError();
|
||||
}
|
||||
|
||||
/// @brief record an error as the last error set and show it
|
||||
///
|
||||
/// @param err the error to record
|
||||
/// @param context a context string
|
||||
/// @return RetValue see this type
|
||||
///
|
||||
RetValue program::ShowError(RetValue err, const char* context) {
|
||||
// record error
|
||||
err_ = err;
|
||||
err_context_ = context;
|
||||
return ShowError();
|
||||
}
|
||||
|
||||
/// @brief set the last error as being a syntax error and show it
|
||||
///
|
||||
/// @param err the error to record
|
||||
/// @param context a context string
|
||||
/// @return RetValue see this type
|
||||
///
|
||||
void program::ShowSyntaxError(const char* context) {
|
||||
// record error
|
||||
err_ = kSyntaxError;
|
||||
err_context_ = context;
|
||||
(void)ShowError();
|
||||
}
|
||||
|
||||
/// @brief return the last error set
|
||||
///
|
||||
/// @return RetValue see this type
|
||||
///
|
||||
RetValue program::GetLastError(void) { return err_; }
|
||||
|
||||
/// @brief show a stack (show its different objects)
|
||||
/// generally a stack is associated to a running program
|
||||
///
|
||||
/// @param show_separator whether to show a stack level prefix or not
|
||||
///
|
||||
void program::ShowStack(bool show_separator) {
|
||||
if (stack_.size() == 1) {
|
||||
cout << stack_[0] << endl;
|
||||
} else {
|
||||
for (int i = stack_.size() - 1; i >= 0; i--) {
|
||||
if (show_separator) cout << i + 1 << "> ";
|
||||
cout << stack_[i] << endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief apply default precision mode and digits
|
||||
///
|
||||
void program::ApplyDefault() {
|
||||
// default float precision, float mode
|
||||
Number::mode = Number::kDefaultMode;
|
||||
Number::digits = Number::kDefaultDecimalDigits;
|
||||
mpreal::set_default_prec(Number::kMpfrDefaultPrecBits);
|
||||
|
||||
static mp_rnd_t def_rnd = mpreal::get_default_rnd();
|
||||
mpreal::set_default_rnd(def_rnd);
|
||||
}
|
665
src/program.cpp
665
src/program.cpp
|
@ -1,665 +0,0 @@
|
|||
#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[] = {
|
||||
// GENERAL
|
||||
{cmd_undef, "", NULL, "\nGENERAL"},
|
||||
{cmd_keyword, "nop", &program::rpn_nop, "no operation"},
|
||||
{cmd_keyword, "help", &program::rpn_help, "this help message"},
|
||||
{cmd_keyword, "h", &program::rpn_help, ""},
|
||||
{cmd_keyword, "?", &program::rpn_help, ""},
|
||||
{cmd_keyword, "quit", &program::rpn_good_bye, "quit software"},
|
||||
{cmd_keyword, "q", &program::rpn_good_bye, ""},
|
||||
{cmd_keyword, "exit", &program::rpn_good_bye, ""},
|
||||
{cmd_keyword, "test", &program::rpn_test, ""}, // not seen by user
|
||||
{cmd_keyword, "version", &program::rpn_version, "show rpn version"},
|
||||
{cmd_keyword, "uname", &program::rpn_uname, "show rpn complete identification string"},
|
||||
{cmd_keyword, "history", &program::rpn_history, "see commands history"},
|
||||
|
||||
// USUAL OPERATIONS ON REALS AND COMPLEXES
|
||||
{cmd_undef, "", NULL, "\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, "^", &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"},
|
||||
|
||||
// OPERATIONS ON REALS
|
||||
{cmd_undef, "", NULL, "\nOPERATIONS ON REALS"},
|
||||
{cmd_keyword, "%", &program::rpn_purcent, "purcent"},
|
||||
{cmd_keyword, "%CH", &program::rpn_purcentCH, "inverse purcent"},
|
||||
{cmd_keyword, "mod", &program::rpn_modulo, "modulo"},
|
||||
{cmd_keyword, "fact", &program::rpn_fact, "n! for integer n or Gamma(x+1) for fractional x"},
|
||||
{cmd_keyword, "mant", &program::rpn_mant, "mantissa of a real number"},
|
||||
{cmd_keyword, "xpon", &program::rpn_xpon, "exponant of a real number"},
|
||||
{cmd_keyword, "floor", &program::rpn_floor, "largest number <="},
|
||||
{cmd_keyword, "ceil", &program::rpn_ceil, "smallest number >="},
|
||||
{cmd_keyword, "ip", &program::rpn_ip, "integer part"},
|
||||
{cmd_keyword, "fp", &program::rpn_fp, "fractional part"},
|
||||
{cmd_keyword, "min", &program::rpn_min, "min of 2 real numbers"},
|
||||
{cmd_keyword, "max", &program::rpn_max, "max of 2 real numbers"},
|
||||
|
||||
// nOPERATIONS ON COMPLEXES
|
||||
{cmd_undef, "", NULL, "\nOPERATIONS ON COMPLEXES"},
|
||||
{cmd_keyword, "re", &program::rpn_re, "complex real part"},
|
||||
{cmd_keyword, "im", &program::rpn_im, "complex imaginary part"},
|
||||
{cmd_keyword, "conj", &program::rpn_conj, "complex conjugate"},
|
||||
{cmd_keyword, "arg", &program::rpn_arg, "complex argument in radians"},
|
||||
{cmd_keyword, "c->r", &program::rpn_c2r, "transform a complex in 2 reals"},
|
||||
{cmd_keyword, "r->c", &program::rpn_r2c, "transform 2 reals in a complex"},
|
||||
{cmd_keyword, "p->r", &program::rpn_p2r, "cartesian to polar"},
|
||||
{cmd_keyword, "r->p", &program::rpn_r2p, "polar to cartesian"},
|
||||
|
||||
// MODE
|
||||
{cmd_undef, "", NULL, "\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"},
|
||||
{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"},
|
||||
|
||||
// TESTS
|
||||
{cmd_undef, "", NULL, "\nTEST"},
|
||||
{cmd_keyword, ">", &program::rpn_sup, "binary operator >"},
|
||||
{cmd_keyword, ">=", &program::rpn_sup_eq, "binary operator >="},
|
||||
{cmd_keyword, "<", &program::rpn_inf, "binary operator <"},
|
||||
{cmd_keyword, "<=", &program::rpn_inf_eq, "binary operator <="},
|
||||
{cmd_keyword, "!=", &program::rpn_diff, "binary operator != (different)"},
|
||||
{cmd_keyword, "==", &program::rpn_eq, "binary operator == (equal)"},
|
||||
{cmd_keyword, "and", &program::rpn_test_and, "boolean operator and"},
|
||||
{cmd_keyword, "or", &program::rpn_test_or, "boolean operator or"},
|
||||
{cmd_keyword, "xor", &program::rpn_test_xor, "boolean operator xor"},
|
||||
{cmd_keyword, "not", &program::rpn_test_not, "boolean operator not"},
|
||||
{cmd_keyword, "same", &program::rpn_same, "boolean operator same (equal)"},
|
||||
|
||||
// STACK
|
||||
{cmd_undef, "", NULL, "\nSTACK"},
|
||||
{cmd_keyword, "swap", &program::rpn_swap, "swap 2 first stack entries"},
|
||||
{cmd_keyword, "drop", &program::rpn_drop, "drop first stack entry"},
|
||||
{cmd_keyword, "drop2", &program::rpn_drop2, "drop 2 first stack entries"},
|
||||
{cmd_keyword, "dropn", &program::rpn_dropn, "drop n first stack entries"},
|
||||
{cmd_keyword, "del", &program::rpn_erase, "drop all stack entries"},
|
||||
{cmd_keyword, "erase", &program::rpn_erase, ""},
|
||||
{cmd_keyword, "rot", &program::rpn_rot, "rotate 3 first stack entries"},
|
||||
{cmd_keyword, "dup", &program::rpn_dup, "duplicate first stack entry"},
|
||||
{cmd_keyword, "dup2", &program::rpn_dup2, "duplicate 2 first stack entries"},
|
||||
{cmd_keyword, "dupn", &program::rpn_dupn, "duplicate n first stack entries"},
|
||||
{cmd_keyword, "pick", &program::rpn_pick, "push a copy of the given stack level onto the stack"},
|
||||
{cmd_keyword, "depth", &program::rpn_depth, "give stack depth"},
|
||||
{cmd_keyword, "roll", &program::rpn_roll, "move a stack entry to the top of the stack"},
|
||||
{cmd_keyword, "rolld", &program::rpn_rolld, "move the element on top of the stack to a higher stack position"},
|
||||
{cmd_keyword, "over", &program::rpn_over, "push a copy of the element in stack level 2 onto the stack"},
|
||||
|
||||
// STRING
|
||||
{cmd_undef, "", NULL, "\nSTRING"},
|
||||
{cmd_keyword, "->str", &program::rpn_instr, "convert an object into a string"},
|
||||
{cmd_keyword, "str->", &program::rpn_strout, "convert a string into an object"},
|
||||
{cmd_keyword, "chr", &program::rpn_chr, "convert ASCII character code in stack level 1 into a string"},
|
||||
{cmd_keyword, "num", &program::rpn_num,
|
||||
"return ASCII code of the first character of the string in stack level 1 "
|
||||
"as a real number"},
|
||||
{cmd_keyword, "size", &program::rpn_strsize, "return the length of the string"},
|
||||
{cmd_keyword, "pos", &program::rpn_strpos, "seach for the string in level 1 within the string in level 2"},
|
||||
{cmd_keyword, "sub", &program::rpn_strsub, "return a substring of the string in level 3"},
|
||||
|
||||
// BRANCH
|
||||
{cmd_undef, "", NULL, "\nBRANCH"},
|
||||
{cmd_branch, "if", (program_fn_t)&program::rpn_if,
|
||||
"if <test-instruction> then <true-instructions> else <false-instructions> "
|
||||
"end"},
|
||||
{cmd_branch, "then", (program_fn_t)&program::rpn_then, "used with if"},
|
||||
{cmd_branch, "else", (program_fn_t)&program::rpn_else, "used with if"},
|
||||
{cmd_branch, "end", (program_fn_t)&program::rpn_end, "used with various branch instructions"},
|
||||
{cmd_branch, "start", (program_fn_t)&program::rpn_start, "<start> <end> start <instructions> next|<step> step"},
|
||||
{cmd_branch, "for", (program_fn_t)&program::rpn_for,
|
||||
"<start> <end> for <variable> <instructions> next|<step> step"},
|
||||
{cmd_branch, "next", (program_fn_t)&program::rpn_next, "used with start and for"},
|
||||
{cmd_branch, "step", (program_fn_t)&program::rpn_step, "used with start and for"},
|
||||
{cmd_keyword, "ift", &program::rpn_ift, "similar to if-then-end, <test-instruction> <true-instruction> ift"},
|
||||
{cmd_keyword, "ifte", &program::rpn_ifte,
|
||||
"similar to if-then-else-end, <test-instruction> <true-instruction> "
|
||||
"<false-instruction> ifte"},
|
||||
{cmd_branch, "do", (program_fn_t)&program::rpn_do, "do <instructions> until <condition> end"},
|
||||
{cmd_branch, "until", (program_fn_t)&program::rpn_until, "used with do"},
|
||||
{cmd_branch, "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_keyword, "sto", &program::rpn_sto, "store a variable. ex: 1 'name' sto"},
|
||||
{cmd_keyword, "rcl", &program::rpn_rcl, "recall a variable. ex: 'name' rcl"},
|
||||
{cmd_keyword, "purge", &program::rpn_purge, "delete a variable. ex: 'name' purge"},
|
||||
{cmd_keyword, "vars", &program::rpn_vars, "list all variables"},
|
||||
{cmd_keyword, "clusr", &program::rpn_clusr, "erase all variables"},
|
||||
{cmd_keyword, "edit", &program::rpn_edit, "edit a variable content"},
|
||||
{cmd_keyword, "sto+", &program::rpn_stoadd, "add to a stored variable. ex: 1 'name' sto+ 'name' 2 sto+"},
|
||||
{cmd_keyword, "sto-", &program::rpn_stosub, "substract to a stored variable. ex: 1 'name' sto- 'name' 2 sto-"},
|
||||
{cmd_keyword, "sto*", &program::rpn_stomul, "multiply a stored variable. ex: 3 'name' sto* 'name' 2 sto*"},
|
||||
{cmd_keyword, "sto/", &program::rpn_stodiv, "divide a stored variable. ex: 3 'name' sto/ 'name' 2 sto/"},
|
||||
{cmd_keyword, "sneg", &program::rpn_stoneg, "negate a variable. ex: 'name' sneg"},
|
||||
{cmd_keyword, "sinv", &program::rpn_stoinv, "inverse a variable. ex: 1 'name' sinv"},
|
||||
|
||||
// PROGRAM
|
||||
{cmd_undef, "", NULL, "\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_keyword, "pi", &program::rpn_pi, "pi constant"},
|
||||
{cmd_keyword, "sin", &program::rpn_sin, "sinus"},
|
||||
{cmd_keyword, "asin", &program::rpn_asin, "arg sinus"},
|
||||
{cmd_keyword, "cos", &program::rpn_cos, "cosinus"},
|
||||
{cmd_keyword, "acos", &program::rpn_acos, "arg cosinus"},
|
||||
{cmd_keyword, "tan", &program::rpn_tan, "tangent"},
|
||||
{cmd_keyword, "atan", &program::rpn_atan, "arg tangent"},
|
||||
{cmd_keyword, "d->r", &program::rpn_d2r, "convert degrees to radians"},
|
||||
{cmd_keyword, "r->d", &program::rpn_r2d, "convert radians to degrees"},
|
||||
|
||||
// LOGS ON REALS AND COMPLEXES
|
||||
{cmd_undef, "", NULL, "\nLOGS ON REALS AND COMPLEXES"},
|
||||
{cmd_keyword, "e", &program::rpn_e, "Euler constant"},
|
||||
{cmd_keyword, "ln", &program::rpn_ln, "logarithm base e"},
|
||||
{cmd_keyword, "log", &program::rpn_ln, ""},
|
||||
{cmd_keyword, "lnp1", &program::rpn_lnp1, "ln(1+x) which is useful when x is close to 0"},
|
||||
{cmd_keyword, "exp", &program::rpn_exp, "exponential"},
|
||||
{cmd_keyword, "expm", &program::rpn_expm, "exp(x)-1 which is useful when x is close to 0"},
|
||||
{cmd_keyword, "log10", &program::rpn_log10, "logarithm base 10"},
|
||||
{cmd_keyword, "alog10", &program::rpn_alog10, "exponential base 10"},
|
||||
{cmd_keyword, "exp10", &program::rpn_alog10, ""},
|
||||
{cmd_keyword, "log2", &program::rpn_log2, "logarithm base 2"},
|
||||
{cmd_keyword, "alog2", &program::rpn_alog2, "exponential base 2"},
|
||||
{cmd_keyword, "exp2", &program::rpn_alog2, ""},
|
||||
{cmd_keyword, "sinh", &program::rpn_sinh, "hyperbolic sine"},
|
||||
{cmd_keyword, "asinh", &program::rpn_asinh, "inverse hyperbolic sine"},
|
||||
{cmd_keyword, "cosh", &program::rpn_sinh, "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_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, ""},
|
||||
};
|
||||
|
||||
/// @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) {
|
||||
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;
|
||||
|
||||
// iterate commands
|
||||
for (int i = 0; (go_out == false) && (interrupt_now == false) && (i < (int)size());) {
|
||||
type = (cmd_type_t)seq_type(i);
|
||||
|
||||
// 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
|
||||
ret = ret_good_bye;
|
||||
}
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
||||
// 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++;
|
||||
}
|
||||
}
|
||||
|
||||
if (interrupt_now) {
|
||||
fprintf(stderr, "\nInterrupted\n");
|
||||
interrupt_now = false;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// @brief stop a program
|
||||
///
|
||||
///
|
||||
void program::stop() { interrupt_now = true; }
|
||||
|
||||
/// @brief return whether a branch object has a given name
|
||||
///
|
||||
/// @param b the branch object
|
||||
/// @param str_to_compare the name
|
||||
/// @param len the name length
|
||||
/// @return true the branch name is str_to_compare
|
||||
/// @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;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
/// @brief prepare a program for execution
|
||||
/// this is needed before a program can be run
|
||||
/// inner members of branch or keyword objects are filled by this function
|
||||
/// these inner members store for example the index of the next keyword to execute etc.
|
||||
///
|
||||
/// @return ret_value see this type
|
||||
///
|
||||
ret_value program::preprocess(void) {
|
||||
// for if-then-else-end
|
||||
vector<struct if_layout_t> vlayout;
|
||||
int layout_index = -1;
|
||||
// for start-end-step
|
||||
vector<int> vstartindex;
|
||||
|
||||
// analyse if-then-else-end branches
|
||||
// analyse start-{next, step} branches
|
||||
for (int i = 0; i < (int)size(); i++) {
|
||||
int type = seq_type(i);
|
||||
if (type == cmd_branch) {
|
||||
branch* k = (branch*)seq_obj(i);
|
||||
if (compare_branch(k, "if", 2)) {
|
||||
if_layout_t layout;
|
||||
layout.index_if_or_do_or_while = i;
|
||||
vlayout.push_back(layout);
|
||||
layout_index++;
|
||||
} else if (compare_branch(k, "then", 4)) {
|
||||
int next = i + 1;
|
||||
if (next >= (int)size()) next = -1;
|
||||
|
||||
// nothing after 'then' -> error
|
||||
if (next == -1) {
|
||||
// error: show it
|
||||
show_syntax_error("missing end after then");
|
||||
return ret_syntax;
|
||||
}
|
||||
if (layout_index < 0) {
|
||||
// error: show it
|
||||
show_syntax_error("missing if before then");
|
||||
return ret_syntax;
|
||||
}
|
||||
if (vlayout[layout_index].index_then_or_unti_or_repeat != -1) {
|
||||
// error: show it
|
||||
show_syntax_error("duplicate then");
|
||||
return ret_syntax;
|
||||
}
|
||||
vlayout[layout_index].index_then_or_unti_or_repeat = i;
|
||||
k->arg1 = next;
|
||||
k->arg3 = vlayout[layout_index].index_if_or_do_or_while;
|
||||
} else if (compare_branch(k, "else", 4)) {
|
||||
int next = i + 1;
|
||||
if (next >= (int)size()) next = -1;
|
||||
|
||||
// nothing after 'else' -> error
|
||||
if (next == -1) {
|
||||
// error: show it
|
||||
show_syntax_error("missing end after else");
|
||||
return ret_syntax;
|
||||
}
|
||||
if (layout_index < 0) {
|
||||
// error: show it
|
||||
show_syntax_error("missing if before else");
|
||||
return ret_syntax;
|
||||
}
|
||||
if (vlayout[layout_index].index_then_or_unti_or_repeat == -1) {
|
||||
// error: show it
|
||||
show_syntax_error("missing then before else");
|
||||
return ret_syntax;
|
||||
}
|
||||
if (vlayout[layout_index].index_else != -1) {
|
||||
// error: show it
|
||||
show_syntax_error("duplicate else");
|
||||
return ret_syntax;
|
||||
}
|
||||
vlayout[layout_index].index_else = i;
|
||||
k->arg1 = next; // fill branch1 (if was false) of 'else'
|
||||
k->arg3 = vlayout[layout_index].index_if_or_do_or_while;
|
||||
((branch*)seq_obj(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);
|
||||
else if (compare_branch(k, "for", 3)) {
|
||||
vstartindex.push_back(i);
|
||||
k->arg1 = i + 1; // arg1 points on symbol variable
|
||||
} else if (compare_branch(k, "next", 4)) {
|
||||
if (vstartindex.size() == 0) {
|
||||
// error: show it
|
||||
show_syntax_error("missing start or for before next");
|
||||
return ret_syntax;
|
||||
}
|
||||
k->arg1 = vstartindex[vstartindex.size() - 1]; // 'next' arg1 = 'start' index
|
||||
((branch*)seq_obj(vstartindex[vstartindex.size() - 1]))->arg2 =
|
||||
i; // 'for' or 'start' arg2 = 'next' index
|
||||
vstartindex.pop_back();
|
||||
} else if (compare_branch(k, "step", 4)) {
|
||||
if (vstartindex.size() == 0) {
|
||||
// error: show it
|
||||
show_syntax_error("missing start or for before step");
|
||||
return ret_syntax;
|
||||
}
|
||||
k->arg1 = vstartindex[vstartindex.size() - 1]; // fill 'step' branch1 = 'start' index
|
||||
((branch*)seq_obj(vstartindex[vstartindex.size() - 1]))->arg2 =
|
||||
i; // 'for' or 'start' arg2 = 'next' index
|
||||
vstartindex.pop_back();
|
||||
} else if (compare_branch(k, "->", 2)) {
|
||||
k->arg1 = i; // arg1 is '->' command index in program
|
||||
} else if (compare_branch(k, "do", 2)) {
|
||||
if_layout_t layout;
|
||||
layout.index_if_or_do_or_while = i;
|
||||
layout.is_do_unti = true;
|
||||
vlayout.push_back(layout);
|
||||
layout_index++;
|
||||
} else if (compare_branch(k, "until", 4)) {
|
||||
int next = i + 1;
|
||||
if (next >= (int)size()) next = -1;
|
||||
|
||||
// nothing after 'unti' -> error
|
||||
if (next == -1) {
|
||||
// error: show it
|
||||
show_syntax_error("missing end");
|
||||
return ret_syntax;
|
||||
}
|
||||
if (layout_index < 0 || !vlayout[layout_index].is_do_unti) {
|
||||
// error: show it
|
||||
show_syntax_error("missing do");
|
||||
return ret_syntax;
|
||||
}
|
||||
if (vlayout[layout_index].index_then_or_unti_or_repeat != -1) {
|
||||
// error: show it
|
||||
show_syntax_error("duplicate until");
|
||||
return ret_syntax;
|
||||
}
|
||||
vlayout[layout_index].index_then_or_unti_or_repeat = i;
|
||||
} else if (compare_branch(k, "while", 4)) {
|
||||
if_layout_t layout;
|
||||
layout.index_if_or_do_or_while = i;
|
||||
layout.is_while_repeat = true;
|
||||
vlayout.push_back(layout);
|
||||
layout_index++;
|
||||
} else if (compare_branch(k, "repeat", 5)) {
|
||||
if (layout_index < 0 || !vlayout[layout_index].is_while_repeat) {
|
||||
// error: show it
|
||||
show_syntax_error("missing while");
|
||||
return ret_syntax;
|
||||
}
|
||||
if (vlayout[layout_index].index_then_or_unti_or_repeat != -1) {
|
||||
// error: show it
|
||||
show_syntax_error("duplicate repeat");
|
||||
return ret_syntax;
|
||||
}
|
||||
vlayout[layout_index].index_then_or_unti_or_repeat = i;
|
||||
} else if (compare_branch(k, "end", 3)) {
|
||||
int next = i + 1;
|
||||
if (next >= (int)size()) next = -1;
|
||||
|
||||
if (layout_index < 0) {
|
||||
// error: show it
|
||||
show_syntax_error("missing branch instruction before end");
|
||||
return ret_syntax;
|
||||
} else {
|
||||
if (vlayout[layout_index].is_do_unti) {
|
||||
// this end closes a do..unti
|
||||
if (vlayout[layout_index].index_end != -1) {
|
||||
// error: show it
|
||||
show_syntax_error("duplicate end");
|
||||
return ret_syntax;
|
||||
}
|
||||
|
||||
if (vlayout[layout_index].index_then_or_unti_or_repeat == -1) {
|
||||
// error: show it
|
||||
show_syntax_error("missing until");
|
||||
return ret_syntax;
|
||||
}
|
||||
|
||||
k->arg1 = vlayout[layout_index].index_if_or_do_or_while + 1;
|
||||
layout_index--;
|
||||
} else if (vlayout[layout_index].is_while_repeat) {
|
||||
// this end closes a while..repeat
|
||||
if (vlayout[layout_index].index_end != -1) {
|
||||
// error: show it
|
||||
show_syntax_error("duplicate end");
|
||||
return ret_syntax;
|
||||
}
|
||||
|
||||
k->arg2 = vlayout[layout_index].index_if_or_do_or_while + 1;
|
||||
|
||||
if (vlayout[layout_index].index_then_or_unti_or_repeat == -1) {
|
||||
// error: show it
|
||||
show_syntax_error("missing repeat");
|
||||
return ret_syntax;
|
||||
}
|
||||
|
||||
// fill 'repeat' arg1 with 'end+1'
|
||||
((branch*)seq_obj(vlayout[layout_index].index_then_or_unti_or_repeat))->arg1 = i + 1;
|
||||
layout_index--;
|
||||
} else {
|
||||
// this end closes an if..then..(else)
|
||||
if (vlayout[layout_index].index_end != -1) {
|
||||
// error: show it
|
||||
show_syntax_error("duplicate end");
|
||||
return ret_syntax;
|
||||
}
|
||||
if (vlayout[layout_index].index_else != -1)
|
||||
// fill 'end' branch of 'else'
|
||||
((branch*)seq_obj(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;
|
||||
else {
|
||||
// error: show it
|
||||
show_syntax_error("missing then");
|
||||
return ret_syntax;
|
||||
}
|
||||
}
|
||||
layout_index--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (layout_index >= 0) {
|
||||
// error: show it
|
||||
show_syntax_error("missing end");
|
||||
return ret_syntax;
|
||||
}
|
||||
if (vstartindex.size() > 0) {
|
||||
// error: show it
|
||||
show_syntax_error("missing next or step after for or start");
|
||||
return ret_syntax;
|
||||
}
|
||||
return ret_ok;
|
||||
}
|
||||
|
||||
/// @brief show the last error set
|
||||
///
|
||||
/// @return ret_value see this type
|
||||
///
|
||||
ret_value program::show_error() {
|
||||
ret_value ret;
|
||||
|
||||
// show last recorded error
|
||||
cerr << _err_context << ": error " << _err << ": " << s_ret_value_string[_err] << endl;
|
||||
switch (_err) {
|
||||
case ret_internal:
|
||||
case ret_deadly:
|
||||
ret = ret_deadly;
|
||||
default:
|
||||
ret = ret_ok;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// @brief record an error as the last error set and show it
|
||||
///
|
||||
/// @param err the error to record
|
||||
/// @param context a context string
|
||||
/// @return ret_value see this type
|
||||
///
|
||||
ret_value program::show_error(ret_value err, string& context) {
|
||||
// record error
|
||||
_err = err;
|
||||
_err_context = context;
|
||||
return show_error();
|
||||
}
|
||||
|
||||
/// @brief record an error as the last error set and show it
|
||||
///
|
||||
/// @param err the error to record
|
||||
/// @param context a context string
|
||||
/// @return ret_value see this type
|
||||
///
|
||||
ret_value program::show_error(ret_value err, const char* context) {
|
||||
// record error
|
||||
_err = err;
|
||||
_err_context = context;
|
||||
return show_error();
|
||||
}
|
||||
|
||||
/// @brief set the last error as being a syntax error and show it
|
||||
///
|
||||
/// @param err the error to record
|
||||
/// @param context a context string
|
||||
/// @return ret_value see this type
|
||||
///
|
||||
void program::show_syntax_error(const char* context) {
|
||||
// record error
|
||||
_err = ret_syntax;
|
||||
_err_context = context;
|
||||
(void)show_error();
|
||||
}
|
||||
|
||||
/// @brief return the last error set
|
||||
///
|
||||
/// @return ret_value see this type
|
||||
///
|
||||
ret_value program::get_err(void) { return _err; }
|
||||
|
||||
/// @brief show a stack (show its different objects)
|
||||
/// generally a stack is associated to a running program
|
||||
///
|
||||
/// @param 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 {
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief apply default precision mode and digits
|
||||
///
|
||||
void program::apply_default() {
|
||||
// default float precision, float mode
|
||||
number::s_mode = DEFAULT_MODE;
|
||||
number::s_decimal_digits = DEFAULT_DECIMAL_DIGITS;
|
||||
|
||||
// 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;
|
||||
}
|
289
src/program.h
Normal file
289
src/program.h
Normal file
|
@ -0,0 +1,289 @@
|
|||
// Copyright (c) 2014-2022 Louis Rubet
|
||||
|
||||
#ifndef SRC_PROGRAM_HPP_
|
||||
#define SRC_PROGRAM_HPP_
|
||||
|
||||
// std c++
|
||||
#include <deque>
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
using namespace std;
|
||||
|
||||
#include <mpreal.h>
|
||||
using mpfr::mpreal;
|
||||
|
||||
// internal includes
|
||||
#include "lexer.h"
|
||||
#include "object.h"
|
||||
#include "stack.h"
|
||||
|
||||
//< program class: the class containing a string parser, all the programs keywords, a stack for running the program
|
||||
class program : public deque<Object*>, public Lexer {
|
||||
public:
|
||||
program(rpnstack& stack__, heap& heap__, program* parent__ = nullptr)
|
||||
: stack_(stack__), heap_(heap__), parent_(parent__) {}
|
||||
virtual ~program() {
|
||||
local_heap_.clear();
|
||||
clear();
|
||||
}
|
||||
|
||||
// parser
|
||||
RetValue Parse(string& entry);
|
||||
|
||||
// running
|
||||
RetValue Run();
|
||||
RetValue Preprocess();
|
||||
|
||||
RetValue ShowError();
|
||||
RetValue ShowError(RetValue err, string& context);
|
||||
RetValue ShowError(RetValue err, const char* context);
|
||||
void ShowSyntaxError(const char* context);
|
||||
RetValue GetLastError(void);
|
||||
|
||||
void ShowStack(bool show_separator = true);
|
||||
|
||||
static void ApplyDefault();
|
||||
|
||||
static vector<string>& GetAutocompletionWords();
|
||||
|
||||
private:
|
||||
// current error and its context
|
||||
RetValue err_;
|
||||
string err_context_;
|
||||
|
||||
// global stack holding results for user
|
||||
rpnstack& stack_;
|
||||
|
||||
// global heap (sto, rcl)
|
||||
heap& heap_;
|
||||
|
||||
// local heap for local loop variables (for..next)
|
||||
heap local_heap_;
|
||||
|
||||
// parent prog for inheriting heaps
|
||||
program* parent_;
|
||||
|
||||
private:
|
||||
// keywords
|
||||
struct keyword_t {
|
||||
ObjectType type;
|
||||
string name;
|
||||
program_fn_t fn;
|
||||
string comment;
|
||||
};
|
||||
static vector<keyword_t> keywords_;
|
||||
|
||||
// keywords implementation
|
||||
////
|
||||
|
||||
// branch
|
||||
size_t RpnIf(Branch& myobj);
|
||||
size_t RpnThen(Branch& myobj);
|
||||
size_t RpnElse(Branch& myobj);
|
||||
size_t RpnEnd(Branch& myobj);
|
||||
size_t RpnDo(Branch& myobj);
|
||||
size_t RpnUntil(Branch& myobj);
|
||||
void RpnIft(void);
|
||||
void RpnIfte(void);
|
||||
size_t RpnWhile(Branch& myobj);
|
||||
size_t RpnRepeat(Branch& myobj);
|
||||
size_t RpnStart(Branch& myobj);
|
||||
size_t RpnFor(Branch& myobj);
|
||||
size_t RpnNext(Branch& myobj);
|
||||
size_t RpnStep(Branch& myobj);
|
||||
enum { kStepOut = static_cast<size_t>(-1), kRtError = static_cast<size_t>(-2) };
|
||||
|
||||
// complex
|
||||
void RpnReal();
|
||||
void RpnImag();
|
||||
void RpnArg();
|
||||
void RpnConj();
|
||||
void RpnR2c();
|
||||
void RpnC2r();
|
||||
void RpnR2p();
|
||||
void RpnP2r();
|
||||
|
||||
// general
|
||||
void RpnNop();
|
||||
void RpnQuit();
|
||||
void RpnHelp();
|
||||
void RpnStd();
|
||||
void RpnFix();
|
||||
void RpnSci();
|
||||
void RpnVersion();
|
||||
void RpnUname();
|
||||
void RpnHistory();
|
||||
void RpnType();
|
||||
void RpnDefault();
|
||||
void RpnPrecision();
|
||||
void RpnRound();
|
||||
|
||||
// logs
|
||||
void RpnE(void);
|
||||
void RpnLog10();
|
||||
void RpnAlog10();
|
||||
void RpnLog2();
|
||||
void RpnAlog2();
|
||||
void RpnLn();
|
||||
void RpnExp();
|
||||
void RpnExpm();
|
||||
void RpnLnp1();
|
||||
void RpnSinh();
|
||||
void RpnAsinh();
|
||||
void RpnCosh();
|
||||
void RpnAcosh();
|
||||
void RpnTanh();
|
||||
void RpnAtanh();
|
||||
|
||||
// program
|
||||
bool FindVariable(string& variable, Object*& obj);
|
||||
void RpnEval(void);
|
||||
int RpnInprog(Branch& inprog_obj);
|
||||
|
||||
// real
|
||||
void RpnPlus();
|
||||
void RpnMinus();
|
||||
void RpnMul();
|
||||
void RpnDiv();
|
||||
void RpnNeg();
|
||||
void RpnInv();
|
||||
void RpnPurcent();
|
||||
void RpnPurcentCH();
|
||||
void RpnPower();
|
||||
void RpnSquareroot();
|
||||
void RpnSquare();
|
||||
void RpnModulo();
|
||||
void RpnAbs();
|
||||
void RpnHex();
|
||||
void RpnBin();
|
||||
void RpnDec();
|
||||
void RpnBase();
|
||||
void RpnFact();
|
||||
void RpnSign();
|
||||
void RpnMant();
|
||||
void RpnXpon();
|
||||
void RpnFloor();
|
||||
void RpnCeil();
|
||||
void RpnFp();
|
||||
void RpnIp();
|
||||
void RpnMin();
|
||||
void RpnMax();
|
||||
|
||||
// stack
|
||||
void RpnSwap(void);
|
||||
void RpnDrop(void);
|
||||
void RpnDrop2(void);
|
||||
void RpnDropn(void);
|
||||
void RpnErase(void);
|
||||
void RpnDup(void);
|
||||
void RpnDup2(void);
|
||||
void RpnDupn(void);
|
||||
void RpnPick(void);
|
||||
void RpnRot(void);
|
||||
void RpnDepth(void);
|
||||
void RpnRoll(void);
|
||||
void RpnRolld(void);
|
||||
void RpnOver(void);
|
||||
|
||||
// store
|
||||
void RpnSto(void);
|
||||
void RpnStoadd(void);
|
||||
void RpnStosub(void);
|
||||
void RpnStomul(void);
|
||||
void RpnStodiv(void);
|
||||
void RpnStoneg(void);
|
||||
void RpnStoinv(void);
|
||||
void RpnRcl(void);
|
||||
void RpnEdit(void);
|
||||
void AutoRcl(Symbol* symb);
|
||||
void RpnPurge(void);
|
||||
void RpnVars(void);
|
||||
void RpnClusr(void);
|
||||
|
||||
// string
|
||||
void RpnInstr();
|
||||
void RpnStrout();
|
||||
void RpnChr();
|
||||
void RpnNum();
|
||||
void RpnStrsize();
|
||||
void RpnStrpos();
|
||||
void RpnStrsub();
|
||||
|
||||
// test-core
|
||||
void RpnTest();
|
||||
void RunTestFile(string test_filename, int& total_tests, int& total_tests_failed, int& total_steps,
|
||||
int& total_steps_failed);
|
||||
|
||||
// test
|
||||
void RpnSup(void);
|
||||
void RpnSupEq(void);
|
||||
void RpnInf(void);
|
||||
void RpnInfEq(void);
|
||||
void RpnDiff(void);
|
||||
void RpnEq(void);
|
||||
void RpnTestAnd(void);
|
||||
void RpnTestOr(void);
|
||||
void RpnTestXor(void);
|
||||
void RpnTestNot(void);
|
||||
void RpnSame(void);
|
||||
|
||||
// trig
|
||||
void RpnPi(void);
|
||||
void RpnD2r(void);
|
||||
void RpnR2d(void);
|
||||
void RpnSin(void);
|
||||
void RpnAsin(void);
|
||||
void RpnCos(void);
|
||||
void RpnAcos(void);
|
||||
void RpnTan(void);
|
||||
void RpnAtan(void);
|
||||
|
||||
// time
|
||||
void RpnTime();
|
||||
void RpnDate();
|
||||
void RpnTicks();
|
||||
};
|
||||
|
||||
// convenience macros for rpn_xx function
|
||||
// carefull : some of these macros modify program flow
|
||||
|
||||
#define ERROR_CONTEXT(err) \
|
||||
do { \
|
||||
err_ = (err); \
|
||||
err_context_ = __FUNCTION__; \
|
||||
} while (0)
|
||||
|
||||
#define MIN_ARGUMENTS(num) \
|
||||
do { \
|
||||
if (stack_.size() < (num)) { \
|
||||
ERROR_CONTEXT(kMissingOperand); \
|
||||
return; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define MIN_ARGUMENTS_RET(num, ret) \
|
||||
do { \
|
||||
if (stack_.size() < (num)) { \
|
||||
ERROR_CONTEXT(kMissingOperand); \
|
||||
return (ret); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define ARG_MUST_BE_OF_TYPE(num, type) \
|
||||
do { \
|
||||
if (stack_.at(num)->_type != (type)) { \
|
||||
ERROR_CONTEXT(kBadOperandType); \
|
||||
return; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define ARG_MUST_BE_OF_TYPE_RET(num, type, ret) \
|
||||
do { \
|
||||
if (stack_.at(num)->_type != (type)) { \
|
||||
ERROR_CONTEXT(kBadOperandType); \
|
||||
return (ret); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#endif // SRC_PROGRAM_HPP_
|
330
src/program.hpp
330
src/program.hpp
|
@ -1,330 +0,0 @@
|
|||
#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
|
||||
#include <mpfr.h>
|
||||
#include "linenoise.h"
|
||||
|
||||
// internal includes
|
||||
#include "constant.h"
|
||||
#include "debug.h"
|
||||
#include "escape.h"
|
||||
#include "object.hpp"
|
||||
#include "stack.hpp"
|
||||
#include "version.h"
|
||||
|
||||
//< convinient structure to preprocess a program
|
||||
struct if_layout_t {
|
||||
if_layout_t()
|
||||
: index_then_or_unti_or_repeat(-1), index_else(-1), index_end(-1), is_do_unti(false), is_while_repeat(false) {}
|
||||
int index_if_or_do_or_while;
|
||||
int index_then_or_unti_or_repeat;
|
||||
int index_else;
|
||||
int index_end;
|
||||
bool is_do_unti;
|
||||
bool is_while_repeat;
|
||||
};
|
||||
|
||||
//< program class: the class containing a string parser, all the programs keywords, a stack for running the program
|
||||
class program : public stack {
|
||||
public:
|
||||
program(program* parent_prog = NULL) {
|
||||
_parent_prog = parent_prog;
|
||||
interrupt_now = false;
|
||||
}
|
||||
|
||||
// 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);
|
||||
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);
|
||||
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);
|
||||
|
||||
ret_value show_error();
|
||||
ret_value show_error(ret_value err, string& context);
|
||||
ret_value show_error(ret_value err, const char* context);
|
||||
void show_syntax_error(const char* context);
|
||||
ret_value get_err(void);
|
||||
|
||||
static void show_stack(stack& st, bool show_separator = true);
|
||||
|
||||
static void apply_default();
|
||||
|
||||
private:
|
||||
bool interrupt_now;
|
||||
|
||||
// current error and its context
|
||||
ret_value _err;
|
||||
string _err_context;
|
||||
|
||||
// global stack holding results for user
|
||||
stack* _stack;
|
||||
|
||||
// global heap (sto, rcl)
|
||||
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;
|
||||
|
||||
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];
|
||||
program_fn_t fn;
|
||||
string comment;
|
||||
};
|
||||
static keyword_t s_keywords[];
|
||||
|
||||
// keywords implementation
|
||||
////
|
||||
|
||||
// branch
|
||||
int rpn_if(branch& myobj);
|
||||
int rpn_then(branch& myobj);
|
||||
int rpn_else(branch& myobj);
|
||||
int rpn_end(branch& myobj);
|
||||
int rpn_do(branch& myobj);
|
||||
int rpn_until(branch& myobj);
|
||||
void rpn_ift(void);
|
||||
void rpn_ifte(void);
|
||||
int rpn_while(branch& myobj);
|
||||
int rpn_repeat(branch& myobj);
|
||||
int rpn_start(branch& myobj);
|
||||
int rpn_for(branch& myobj);
|
||||
int rpn_next(branch& myobj);
|
||||
int rpn_step(branch& myobj);
|
||||
|
||||
// complex
|
||||
void rpn_re();
|
||||
void rpn_im();
|
||||
void rpn_arg();
|
||||
void rpn_conj();
|
||||
void rpn_r2c();
|
||||
void rpn_c2r();
|
||||
void rpn_r2p();
|
||||
void rpn_p2r();
|
||||
|
||||
// general
|
||||
void rpn_nop();
|
||||
void rpn_good_bye();
|
||||
void rpn_help();
|
||||
void rpn_std();
|
||||
void rpn_fix();
|
||||
void rpn_sci();
|
||||
void rpn_version();
|
||||
void rpn_uname();
|
||||
void rpn_history();
|
||||
void rpn_type();
|
||||
void rpn_default();
|
||||
void rpn_precision();
|
||||
void rpn_round();
|
||||
|
||||
// logs
|
||||
void rpn_e(void);
|
||||
void rpn_log10();
|
||||
void rpn_alog10();
|
||||
void rpn_log2();
|
||||
void rpn_alog2();
|
||||
void rpn_ln();
|
||||
void rpn_exp();
|
||||
void rpn_expm();
|
||||
void rpn_lnp1();
|
||||
void rpn_sinh();
|
||||
void rpn_asinh();
|
||||
void rpn_cosh();
|
||||
void rpn_acosh();
|
||||
void rpn_tanh();
|
||||
void rpn_atanh();
|
||||
|
||||
// program
|
||||
bool find_variable(string& variable, object*& obj, unsigned int& size);
|
||||
void rpn_eval(void);
|
||||
int rpn_inprog(branch& myobj);
|
||||
|
||||
// real
|
||||
void rpn_plus();
|
||||
void rpn_minus();
|
||||
void rpn_mul();
|
||||
void do_divide_complexes();
|
||||
void rpn_div();
|
||||
void rpn_neg();
|
||||
void rpn_inv();
|
||||
void rpn_purcent();
|
||||
void rpn_purcentCH();
|
||||
void rpn_power();
|
||||
void rpn_squareroot();
|
||||
void rpn_square();
|
||||
void rpn_modulo();
|
||||
void rpn_abs();
|
||||
void rpn_hex();
|
||||
void rpn_bin();
|
||||
void rpn_dec();
|
||||
void rpn_base();
|
||||
void rpn_fact();
|
||||
void rpn_sign();
|
||||
void rpn_mant();
|
||||
void rpn_xpon();
|
||||
void rpn_floor();
|
||||
void rpn_ceil();
|
||||
void rpn_fp();
|
||||
void rpn_ip();
|
||||
void rpn_min();
|
||||
void rpn_max();
|
||||
|
||||
// stack
|
||||
void rpn_swap(void);
|
||||
void rpn_drop(void);
|
||||
void rpn_drop2(void);
|
||||
void rpn_dropn(void);
|
||||
void rpn_erase(void);
|
||||
void rpn_dup(void);
|
||||
void rpn_dup2(void);
|
||||
void rpn_dupn(void);
|
||||
void rpn_pick(void);
|
||||
void rpn_rot(void);
|
||||
void rpn_depth(void);
|
||||
void rpn_roll(void);
|
||||
void rpn_rolld(void);
|
||||
void rpn_over(void);
|
||||
|
||||
// store
|
||||
void rpn_sto(void);
|
||||
void rpn_stoadd(void);
|
||||
void rpn_stosub(void);
|
||||
void rpn_stomul(void);
|
||||
void rpn_stodiv(void);
|
||||
void rpn_stoneg(void);
|
||||
void rpn_stoinv(void);
|
||||
void rpn_rcl(void);
|
||||
void rpn_edit(void);
|
||||
void auto_rcl(symbol* symb);
|
||||
void rpn_purge(void);
|
||||
void rpn_vars(void);
|
||||
void rpn_clusr(void);
|
||||
|
||||
// string
|
||||
void rpn_instr();
|
||||
void rpn_strout();
|
||||
void rpn_chr();
|
||||
void rpn_num();
|
||||
void rpn_strsize();
|
||||
void rpn_strpos();
|
||||
void rpn_strsub();
|
||||
|
||||
// test-core
|
||||
void test_get_stack(string& stack_is, stack& 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();
|
||||
void rpn_sup(void);
|
||||
void rpn_sup_eq(void);
|
||||
void rpn_inf(void);
|
||||
void rpn_inf_eq(void);
|
||||
void rpn_diff(void);
|
||||
void rpn_eq(void);
|
||||
void rpn_test_and(void);
|
||||
void rpn_test_or(void);
|
||||
void rpn_test_xor(void);
|
||||
void rpn_test_not(void);
|
||||
void rpn_same(void);
|
||||
|
||||
// trig
|
||||
void rpn_pi(void);
|
||||
void rpn_d2r(void);
|
||||
void rpn_r2d(void);
|
||||
void rpn_sin(void);
|
||||
void rpn_asin(void);
|
||||
void rpn_cos(void);
|
||||
void rpn_acos(void);
|
||||
void rpn_tan(void);
|
||||
void rpn_atan(void);
|
||||
|
||||
// time
|
||||
void rpn_time();
|
||||
void rpn_date();
|
||||
void rpn_ticks();
|
||||
};
|
||||
|
||||
// convinience macros for rpn_xx function
|
||||
// carefull : some of these macros modify program flow
|
||||
#define ERR_CONTEXT(err) \
|
||||
do { \
|
||||
_err = (err); \
|
||||
_err_context = __FUNCTION__; \
|
||||
} while (0)
|
||||
|
||||
#define MIN_ARGUMENTS(num) \
|
||||
do { \
|
||||
if (stack_size() < (num)) { \
|
||||
ERR_CONTEXT(ret_missing_operand); \
|
||||
return; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define MIN_ARGUMENTS_RET(num, ret) \
|
||||
do { \
|
||||
if (stack_size() < (num)) { \
|
||||
ERR_CONTEXT(ret_missing_operand); \
|
||||
return (ret); \
|
||||
} \
|
||||
} 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); \
|
||||
} while (0)
|
||||
|
||||
#endif
|
391
src/rpn-branch.cc
Normal file
391
src/rpn-branch.cc
Normal file
|
@ -0,0 +1,391 @@
|
|||
// Copyright (c) 2014-2022 Louis Rubet
|
||||
|
||||
#include "program.h"
|
||||
|
||||
/// @brief if keyword (branch) implementation
|
||||
///
|
||||
/// @param myobj the current branch object
|
||||
/// @return size_t index of the next object to run in the current program
|
||||
/// @return kStepOut next object to run in the current program is current + 1
|
||||
/// @return kRtError something went wrong with preprocess, abort branch
|
||||
///
|
||||
size_t program::RpnIf(Branch& myobj) {
|
||||
// myobj.arg1 = 'if' condition evaluation value
|
||||
MIN_ARGUMENTS_RET(1, kRtError);
|
||||
ARG_MUST_BE_OF_TYPE_RET(0, kNumber, kRtError);
|
||||
|
||||
if (stack_.value<Number>(0) != 0)
|
||||
myobj.arg1 = 1;
|
||||
else
|
||||
myobj.arg1 = 0;
|
||||
stack_.pop();
|
||||
return kStepOut;
|
||||
}
|
||||
|
||||
/// @brief then keyword (branch) implementation
|
||||
///
|
||||
/// @param myobj the current branch object
|
||||
/// @return size_t index of the next object to run in the current program
|
||||
/// @return kStepOut next object to run in the current program is current + 1
|
||||
/// @return kRtError something went wrong with preprocess, abort Branch
|
||||
///
|
||||
size_t program::RpnThen(Branch& myobj) {
|
||||
// myobj.arg1 = index of then + 1
|
||||
// myobj.arg2 = index of else + 1 or end + 1
|
||||
// myobj.arg3 = index of if
|
||||
// if condition is true -> arg1 (= jump to then + 1)
|
||||
// else -> arg2 (= jump to else + 1 or end + 1)
|
||||
Branch* if_cmd;
|
||||
if (myobj.arg3 >= size() || at(myobj.arg3)->_type != kBranch) {
|
||||
ERROR_CONTEXT(kMissingOperand);
|
||||
return kRtError;
|
||||
}
|
||||
if_cmd = reinterpret_cast<Branch*>(at(myobj.arg3));
|
||||
if (if_cmd->arg1 == 1)
|
||||
return myobj.arg1;
|
||||
else
|
||||
return myobj.arg2;
|
||||
}
|
||||
|
||||
/// @brief else keyword (branch) implementation
|
||||
///
|
||||
/// @param myobj the current branch object
|
||||
/// @return size_t index of the next object to run in the current program
|
||||
/// @return kStepOut next object to run in the current program is current + 1
|
||||
/// @return kRtError something went wrong with preprocess, abort branch
|
||||
///
|
||||
size_t program::RpnElse(Branch& myobj) {
|
||||
// myobj.arg1 = index of else + 1
|
||||
// myobj.arg2 = index of end + 1
|
||||
// myobj.arg3 = index of if
|
||||
// if condition was false -> arg1 (= jump to else + 1)
|
||||
// if condition was true -> arg2 (= jump to end + 1)
|
||||
Branch* if_cmd;
|
||||
if (myobj.arg3 >= size() || at(myobj.arg3)->_type != kBranch) {
|
||||
ERROR_CONTEXT(kMissingOperand);
|
||||
return kRtError;
|
||||
}
|
||||
if_cmd = reinterpret_cast<Branch*>(at(myobj.arg3));
|
||||
if (if_cmd->arg1 == 1)
|
||||
return myobj.arg2;
|
||||
else
|
||||
return myobj.arg1;
|
||||
}
|
||||
|
||||
/// @brief end keyword (branch) implementation
|
||||
///
|
||||
/// @param myobj the current branch object
|
||||
/// @return size_t index of the next object to run in the current program
|
||||
/// @return kStepOut next object to run in the current program is current + 1
|
||||
/// @return kRtError something went wrong with preprocess, abort branch
|
||||
///
|
||||
size_t program::RpnEnd(Branch& myobj) {
|
||||
size_t ret = kStepOut;
|
||||
|
||||
// arg1 = index of do+1 in case of do..unti..end
|
||||
if (myobj.arg1 != kStepOut) {
|
||||
// in a template do..until..end
|
||||
MIN_ARGUMENTS_RET(1, kRtError);
|
||||
ARG_MUST_BE_OF_TYPE_RET(0, kNumber, kRtError);
|
||||
|
||||
// check arg
|
||||
if (stack_.value<Number>(0) == 0) ret = myobj.arg1;
|
||||
stack_.pop();
|
||||
} else if (myobj.arg2 != kStepOut) {
|
||||
// arg2 = index of while+1 in case of while..repeat..end
|
||||
ret = myobj.arg2;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// @brief do keyword (branch) implementation
|
||||
///
|
||||
/// @param myobj the current branch object
|
||||
/// @return size_t index of the next object to run in the current program
|
||||
/// @return kStepOut next object to run in the current program is current + 1
|
||||
/// @return kRtError something went wrong with preprocess, abort branch
|
||||
///
|
||||
size_t program::RpnDo(Branch& myobj __attribute__((unused))) {
|
||||
// nothing
|
||||
return kStepOut;
|
||||
}
|
||||
|
||||
/// @brief until keyword (branch) implementation
|
||||
///
|
||||
/// @param myobj the current branch object
|
||||
/// @return size_t index of the next object to run in the current program
|
||||
/// @return kStepOut next object to run in the current program is current + 1
|
||||
/// @return kRtError something went wrong with preprocess, abort Branch
|
||||
///
|
||||
size_t program::RpnUntil(Branch& myobj __attribute__((unused))) {
|
||||
// nothing
|
||||
return kStepOut;
|
||||
}
|
||||
|
||||
/// @brief ift keyword (branch) implementation
|
||||
///
|
||||
/// @param myobj the current branch object
|
||||
/// @return size_t index of the next object to run in the current program
|
||||
/// @return kStepOut next object to run in the current program is current + 1
|
||||
/// @return kRtError something went wrong with preprocess, abort branch
|
||||
///
|
||||
void program::RpnIft(void) {
|
||||
MIN_ARGUMENTS(2);
|
||||
ARG_MUST_BE_OF_TYPE(1, kNumber);
|
||||
|
||||
// check ift arg
|
||||
// arg is true if Number != 0 or if is nan or +/-inf
|
||||
if (stack_.value<Number>(1) != 0)
|
||||
stack_.erase(1);
|
||||
else
|
||||
stack_.erase(0, 2);
|
||||
}
|
||||
|
||||
/// @brief ifte keyword (branch) implementation
|
||||
///
|
||||
/// @param myobj the current branch object
|
||||
/// @return size_t index of the next object to run in the current program
|
||||
/// @return kStepOut next object to run in the current program is current + 1
|
||||
/// @return kRtError something went wrong with preprocess, abort branch
|
||||
///
|
||||
void program::RpnIfte(void) {
|
||||
MIN_ARGUMENTS(3);
|
||||
ARG_MUST_BE_OF_TYPE(2, kNumber);
|
||||
|
||||
// check ifte arg
|
||||
if (stack_.value<Number>(2) != 0) {
|
||||
stack_.erase(2);
|
||||
stack_.pop();
|
||||
} else {
|
||||
stack_.erase(2);
|
||||
stack_.erase(1);
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief while keyword (branch) implementation
|
||||
///
|
||||
/// @param myobj the current branch object
|
||||
/// @return size_t index of the next object to run in the current program
|
||||
/// @return kStepOut next object to run in the current program is current + 1
|
||||
/// @return kRtError something went wrong with preprocess, abort branch
|
||||
///
|
||||
size_t program::RpnWhile(Branch& myobj __attribute__((unused))) {
|
||||
// nothing
|
||||
return kStepOut;
|
||||
}
|
||||
|
||||
/// @brief repeat keyword (branch) implementation
|
||||
///
|
||||
/// @param myobj the current branch object
|
||||
/// @return size_t index of the next object to run in the current program
|
||||
/// @return kStepOut next object to run in the current program is current + 1
|
||||
/// @return kRtError something went wrong with preprocess, abort branch
|
||||
///
|
||||
size_t program::RpnRepeat(Branch& myobj) {
|
||||
size_t ret = kStepOut;
|
||||
|
||||
MIN_ARGUMENTS_RET(1, kRtError);
|
||||
ARG_MUST_BE_OF_TYPE_RET(0, kNumber, kRtError);
|
||||
|
||||
// check arg
|
||||
// myobj.arg1 is end+1
|
||||
if (stack_.value<Number>(0) == 0) ret = myobj.arg1;
|
||||
stack_.pop();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// @brief start keyword (branch) implementation
|
||||
///
|
||||
/// @param myobj the current branch object
|
||||
/// @return size_t index of the next object to run in the current program
|
||||
/// @return kStepOut next object to run in the current program is current + 1
|
||||
/// @return kRtError something went wrong with preprocess, abort branch
|
||||
///
|
||||
size_t program::RpnStart(Branch& myobj) {
|
||||
size_t ret = kStepOut;
|
||||
|
||||
MIN_ARGUMENTS_RET(2, kRtError);
|
||||
ARG_MUST_BE_OF_TYPE_RET(0, kNumber, kRtError);
|
||||
ARG_MUST_BE_OF_TYPE_RET(1, kNumber, kRtError);
|
||||
|
||||
// loop boundaries
|
||||
myobj.first_index = stack_.value<Number>(1);
|
||||
myobj.last_index = stack_.value<Number>(0);
|
||||
stack_.erase(0, 2);
|
||||
|
||||
// test value
|
||||
if (myobj.first_index > myobj.last_index)
|
||||
// last boundary lower than first boundary
|
||||
// -> next command shall be after 'next'
|
||||
// arg2 holds index of 'next'
|
||||
ret = myobj.arg2 + 1;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// @brief for keyword (branch) implementation
|
||||
///
|
||||
/// @param myobj the current branch object
|
||||
/// @return size_t index of the next object to run in the current program
|
||||
/// @return kStepOut next object to run in the current program is current + 1
|
||||
/// @return kRtError something went wrong with preprocess, abort branch
|
||||
///
|
||||
size_t program::RpnFor(Branch& myobj) {
|
||||
size_t ret;
|
||||
|
||||
MIN_ARGUMENTS_RET(2, kRtError);
|
||||
ARG_MUST_BE_OF_TYPE_RET(0, kNumber, kRtError);
|
||||
ARG_MUST_BE_OF_TYPE_RET(1, kNumber, kRtError);
|
||||
|
||||
Symbol* sym;
|
||||
if (myobj.arg1 >= size() || at(myobj.arg1)->_type != kSymbol) {
|
||||
ERROR_CONTEXT(kMissingOperand);
|
||||
return kRtError;
|
||||
}
|
||||
sym = reinterpret_cast<Symbol*>(at(myobj.arg1)); // arg1 = loop variable index
|
||||
|
||||
// loop boundaries
|
||||
myobj.first_index = stack_.value<Number>(1);
|
||||
myobj.last_index = stack_.value<Number>(0);
|
||||
|
||||
// test value
|
||||
if (myobj.first_index > myobj.last_index) {
|
||||
// last boundary lower than first boundary
|
||||
// -> next command shall be after 'next'
|
||||
// arg2 holds index of 'next'
|
||||
ret = myobj.arg2 + 1;
|
||||
} else {
|
||||
// store symbol with first value
|
||||
auto it = local_heap_.find(sym->value);
|
||||
if (it != local_heap_.end()) {
|
||||
delete it->second;
|
||||
local_heap_.erase(it);
|
||||
}
|
||||
local_heap_[sym->value] = stack_.obj<Number>(1).Clone();
|
||||
ret = myobj.arg1 + 1;
|
||||
}
|
||||
|
||||
stack_.erase(0, 2);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// @brief next keyword (branch) implementation
|
||||
///
|
||||
/// @param myobj the current branch object
|
||||
/// @return size_t index of the next object to run in the current program
|
||||
/// @return kStepOut next object to run in the current program is current + 1
|
||||
/// @return kRtError something went wrong with preprocess, abort branch
|
||||
///
|
||||
size_t program::RpnNext(Branch& myobj) {
|
||||
// arg1 = loop variable index
|
||||
// first_index = current point in the loop
|
||||
Branch* start_or_for;
|
||||
if (myobj.arg1 >= size() || at(myobj.arg1)->_type != kBranch) {
|
||||
ERROR_CONTEXT(kMissingOperand);
|
||||
return kRtError;
|
||||
}
|
||||
start_or_for = reinterpret_cast<Branch*>(at(myobj.arg1));
|
||||
if (!myobj.arg_bool) {
|
||||
myobj.arg_bool = true;
|
||||
myobj.first_index = start_or_for->first_index;
|
||||
}
|
||||
|
||||
// increment then test
|
||||
// carefull: round toward minus infinity to avoid missing last boundary (because growing step)
|
||||
mpfr_add(myobj.first_index.mpfr_ptr(), myobj.first_index.mpfr_srcptr(), mpreal(1).mpfr_srcptr(), MPFR_RNDD);
|
||||
|
||||
// for command: increment symbol too
|
||||
if (start_or_for->arg1 != kStepOut) {
|
||||
Symbol* var;
|
||||
if (start_or_for->arg1 >= size() || at(start_or_for->arg1)->_type != kSymbol) {
|
||||
ERROR_CONTEXT(kMissingOperand);
|
||||
return kRtError;
|
||||
}
|
||||
var = reinterpret_cast<Symbol*>(at(start_or_for->arg1));
|
||||
|
||||
// store symbol variable (asserted existing in the local heap)
|
||||
reinterpret_cast<Number*>(local_heap_[var->value])->value = myobj.first_index;
|
||||
}
|
||||
|
||||
// test value
|
||||
if (myobj.first_index > start_or_for->last_index) {
|
||||
// end of loop
|
||||
myobj.arg_bool = false; // init again next time
|
||||
return kStepOut;
|
||||
} else {
|
||||
// for command: next instruction will be after symbol variable
|
||||
if (start_or_for->arg1 != kStepOut) return start_or_for->arg1 + 1;
|
||||
// start command: next instruction will be after start command
|
||||
else
|
||||
return myobj.arg1 + 1;
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief step keyword (branch) implementation
|
||||
///
|
||||
/// @param myobj the current branch object
|
||||
/// @return size_t index of the next object to run in the current program
|
||||
/// @return kStepOut next object to run in the current program is current + 1
|
||||
/// @return kRtError something went wrong with preprocess, abort branch
|
||||
///
|
||||
size_t program::RpnStep(Branch& myobj) {
|
||||
size_t ret;
|
||||
MIN_ARGUMENTS_RET(1, kRtError);
|
||||
ARG_MUST_BE_OF_TYPE_RET(0, kNumber, kRtError);
|
||||
|
||||
mpreal step = stack_.value<Number>(0);
|
||||
stack_.pop();
|
||||
|
||||
// end of loop if step is negative or zero
|
||||
if (step <= 0) {
|
||||
ret = kStepOut;
|
||||
} else {
|
||||
// arg1 = loop variable index
|
||||
// first_index = current count
|
||||
Branch* start_or_for;
|
||||
if (myobj.arg1 >= size() || at(myobj.arg1)->_type != kBranch) {
|
||||
ERROR_CONTEXT(kMissingOperand);
|
||||
return kRtError;
|
||||
}
|
||||
start_or_for = reinterpret_cast<Branch*>(at(myobj.arg1));
|
||||
if (!myobj.arg_bool) {
|
||||
myobj.arg_bool = true;
|
||||
myobj.first_index = start_or_for->first_index;
|
||||
}
|
||||
|
||||
// increment then test
|
||||
// carefull: round toward minus infinity to avoid missing last boundary (because growing step)
|
||||
mpfr_add(myobj.first_index.mpfr_ptr(), myobj.first_index.mpfr_srcptr(), step.mpfr_srcptr(), MPFR_RNDD);
|
||||
|
||||
if (start_or_for->arg1 != kStepOut) {
|
||||
Symbol* var;
|
||||
|
||||
// for command: increment symbol too
|
||||
if (start_or_for->arg1 >= size() || at(start_or_for->arg1)->_type != kSymbol) {
|
||||
ERROR_CONTEXT(kMissingOperand);
|
||||
return kRtError;
|
||||
}
|
||||
var = reinterpret_cast<Symbol*>(at(start_or_for->arg1));
|
||||
// increase symbol variable
|
||||
reinterpret_cast<Number*>(local_heap_[var->value])->value = myobj.first_index;
|
||||
}
|
||||
|
||||
// test loop value is out of range
|
||||
if (myobj.first_index > start_or_for->last_index) {
|
||||
// end of loop
|
||||
myobj.arg_bool = false; // init again next time
|
||||
ret = kStepOut;
|
||||
} else {
|
||||
// for command: next instruction will be after symbol variable
|
||||
if (start_or_for->arg1 != kStepOut) ret = start_or_for->arg1 + 1;
|
||||
// start command: next instruction will be after start command
|
||||
else
|
||||
ret = myobj.arg1 + 1;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
|
@ -1,360 +0,0 @@
|
|||
#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
|
||||
///
|
||||
int program::rpn_if(branch& myobj) {
|
||||
// myobj.arg1 = 'if' condition evaluation value
|
||||
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)
|
||||
myobj.arg1 = 1;
|
||||
else
|
||||
myobj.arg1 = 0;
|
||||
(void)_stack->pop_back();
|
||||
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
|
||||
///
|
||||
int program::rpn_then(branch& myobj) {
|
||||
// myobj.arg1 = index of then + 1
|
||||
// myobj.arg2 = index of else + 1 or end + 1
|
||||
// myobj.arg3 = index of if
|
||||
// if condition is true -> arg1 (= jump to then + 1)
|
||||
// else -> arg2 (= jump to else + 1 or end + 1)
|
||||
branch* if_cmd = (branch*)seq_obj(myobj.arg3);
|
||||
if (if_cmd->arg1 == 1)
|
||||
return myobj.arg1;
|
||||
else
|
||||
return myobj.arg2;
|
||||
}
|
||||
|
||||
/// @brief else keyword (branch) implementation
|
||||
///
|
||||
/// @param myobj the current branch object
|
||||
/// @return 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_else(branch& myobj) {
|
||||
// myobj.arg1 = index of else + 1
|
||||
// myobj.arg2 = index of end + 1
|
||||
// myobj.arg3 = index of if
|
||||
// if condition was false -> arg1 (= jump to else + 1)
|
||||
// if condition was true -> arg2 (= jump to end + 1)
|
||||
branch* if_cmd = (branch*)seq_obj(myobj.arg3);
|
||||
if (if_cmd->arg1 == 1)
|
||||
return myobj.arg2;
|
||||
else
|
||||
return myobj.arg1;
|
||||
}
|
||||
|
||||
/// @brief end keyword (branch) implementation
|
||||
///
|
||||
/// @param myobj the current branch object
|
||||
/// @return 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_end(branch& myobj) {
|
||||
int ret = -1;
|
||||
|
||||
// arg1 = index of do+1 in case of do..unti..end
|
||||
if (myobj.arg1 != -1) {
|
||||
// in a template do..unti..end
|
||||
MIN_ARGUMENTS_RET(1, -(int)ret_runtime_error);
|
||||
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;
|
||||
}
|
||||
// arg2 = index of while+1 in case of while..repeat..end
|
||||
else if (myobj.arg2 != -1)
|
||||
ret = myobj.arg2;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// @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
|
||||
///
|
||||
int program::rpn_do(branch& myobj) {
|
||||
// nothing
|
||||
return -1;
|
||||
}
|
||||
|
||||
/// @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
|
||||
///
|
||||
int program::rpn_until(branch& myobj) {
|
||||
// nothing
|
||||
return -1;
|
||||
}
|
||||
|
||||
/// @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
|
||||
///
|
||||
void program::rpn_ift(void) {
|
||||
MIN_ARGUMENTS(2);
|
||||
ARG_MUST_BE_OF_TYPE(1, cmd_number);
|
||||
|
||||
// check ift arg
|
||||
// arg is true if number != 0 or if is nan or +/-inf
|
||||
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);
|
||||
}
|
||||
|
||||
/// @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
|
||||
///
|
||||
void program::rpn_ifte(void) {
|
||||
MIN_ARGUMENTS(3);
|
||||
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();
|
||||
}
|
||||
|
||||
/// @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
|
||||
///
|
||||
int program::rpn_while(branch& myobj) {
|
||||
// nothing
|
||||
return -1;
|
||||
}
|
||||
|
||||
/// @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
|
||||
///
|
||||
int program::rpn_repeat(branch& myobj) {
|
||||
int ret = -1;
|
||||
|
||||
MIN_ARGUMENTS_RET(1, -(int)ret_runtime_error);
|
||||
ARG_MUST_BE_OF_TYPE_RET(0, cmd_number, -(int)ret_runtime_error);
|
||||
|
||||
// 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;
|
||||
|
||||
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
|
||||
///
|
||||
int program::rpn_start(branch& myobj) {
|
||||
int ret = -1;
|
||||
|
||||
MIN_ARGUMENTS_RET(2, -(int)ret_runtime_error);
|
||||
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();
|
||||
|
||||
// test value
|
||||
if (myobj.farg1->_value > myobj.farg2->_value)
|
||||
// last boundary lower than first boundary
|
||||
// -> next command shall be after 'next'
|
||||
// arg2 holds index of 'next'
|
||||
ret = myobj.arg2 + 1;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// @brief for keyword (branch) implementation
|
||||
///
|
||||
/// @param myobj the current branch object
|
||||
/// @return 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_for(branch& myobj) {
|
||||
int ret;
|
||||
|
||||
MIN_ARGUMENTS_RET(2, -(int)ret_runtime_error);
|
||||
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));
|
||||
|
||||
// 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();
|
||||
|
||||
// test value
|
||||
if (myobj.farg1->_value > myobj.farg2->_value)
|
||||
// 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());
|
||||
ret = myobj.arg1 + 1;
|
||||
}
|
||||
|
||||
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);
|
||||
if (!myobj.arg_bool) {
|
||||
myobj.arg_bool = true;
|
||||
myobj.farg1 = start_or_for->farg1;
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
||||
// 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);
|
||||
|
||||
// increase symbol variable
|
||||
_local_heap.replace_value(string(var->_value), myobj.farg1, myobj.farg1->size());
|
||||
}
|
||||
|
||||
// test value
|
||||
if (myobj.farg1->_value > start_or_for->farg2->_value) {
|
||||
// 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
|
||||
if (start_or_for->arg1 != -1) return start_or_for->arg1 + 1;
|
||||
// start command: next instruction will be after start command
|
||||
else
|
||||
return myobj.arg1 + 1;
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief step keyword (branch) implementation
|
||||
///
|
||||
/// @param myobj the current branch object
|
||||
/// @return 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_step(branch& myobj) {
|
||||
int ret;
|
||||
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();
|
||||
|
||||
// end of loop if step is negative or zero
|
||||
if (mpfr_cmp_d(step->_value.mpfr, 0.0) <= 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);
|
||||
if (!myobj.arg_bool) {
|
||||
myobj.arg_bool = true;
|
||||
myobj.farg1 = start_or_for->farg1;
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
||||
// 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);
|
||||
|
||||
// increase symbol variable
|
||||
_local_heap.replace_value(string(var->_value), myobj.farg1, myobj.farg1->size());
|
||||
}
|
||||
|
||||
// test loop value is out of range
|
||||
if (myobj.farg1->_value > start_or_for->farg2->_value) {
|
||||
// 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
|
||||
if (start_or_for->arg1 != -1) ret = start_or_for->arg1 + 1;
|
||||
// start command: next instruction will be after start command
|
||||
else
|
||||
ret = myobj.arg1 + 1;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
85
src/rpn-complex.cc
Normal file
85
src/rpn-complex.cc
Normal file
|
@ -0,0 +1,85 @@
|
|||
// Copyright (c) 2014-2022 Louis Rubet
|
||||
|
||||
#include "program.h"
|
||||
|
||||
/// @brief re keyword implementation
|
||||
/// the result is stacked on current program stack
|
||||
///
|
||||
void program::RpnReal() {
|
||||
MIN_ARGUMENTS(1);
|
||||
ARG_MUST_BE_OF_TYPE(0, kComplex);
|
||||
stack_.push_front(new Number(real(stack_.value<Complex>(0))));
|
||||
stack_.erase(1);
|
||||
}
|
||||
|
||||
/// @brief im keyword implementation
|
||||
/// the result is stacked on current program stack
|
||||
///
|
||||
void program::RpnImag() {
|
||||
MIN_ARGUMENTS(1);
|
||||
ARG_MUST_BE_OF_TYPE(0, kComplex);
|
||||
stack_.push_front(new Number(imag(stack_.value<Complex>(0))));
|
||||
stack_.erase(1);
|
||||
}
|
||||
|
||||
/// @brief arg keyword implementation
|
||||
/// the result is stacked on current program stack
|
||||
///
|
||||
void program::RpnArg() {
|
||||
MIN_ARGUMENTS(1);
|
||||
ARG_MUST_BE_OF_TYPE(0, kComplex);
|
||||
stack_.push_front(new Number(arg(stack_.value<Complex>(0))));
|
||||
stack_.erase(1);
|
||||
}
|
||||
|
||||
/// @brief conj keyword implementation
|
||||
/// the result is stacked on current program stack
|
||||
///
|
||||
void program::RpnConj() {
|
||||
MIN_ARGUMENTS(1);
|
||||
ARG_MUST_BE_OF_TYPE(0, kComplex);
|
||||
stack_.value<Complex>(0) = conj(stack_.value<Complex>(0));
|
||||
}
|
||||
|
||||
/// @brief r2c keyword implementation
|
||||
/// the result is stacked on current program stack
|
||||
///
|
||||
void program::RpnR2c() {
|
||||
MIN_ARGUMENTS(2);
|
||||
ARG_MUST_BE_OF_TYPE(0, kNumber);
|
||||
ARG_MUST_BE_OF_TYPE(1, kNumber);
|
||||
stack_.push(new Complex(stack_.value<Number>(1), stack_.value<Number>(0), stack_.obj<Complex>(1).re_base, stack_.obj<Complex>(0).re_base));
|
||||
stack_.erase(1, 2);
|
||||
}
|
||||
|
||||
/// @brief c2r keyword implementation
|
||||
/// the result is stacked on current program stack
|
||||
///
|
||||
void program::RpnC2r() {
|
||||
MIN_ARGUMENTS(1);
|
||||
ARG_MUST_BE_OF_TYPE(0, kComplex);
|
||||
stack_.push(new Number(real(stack_.value<Complex>(0)), stack_.obj<Complex>(0).re_base));
|
||||
stack_.push(new Number(imag(stack_.value<Complex>(1)), stack_.obj<Complex>(1).im_base));
|
||||
stack_.erase(2);
|
||||
}
|
||||
|
||||
/// @brief r2p keyword implementation
|
||||
/// the result is stacked on current program stack
|
||||
///
|
||||
void program::RpnR2p() {
|
||||
MIN_ARGUMENTS(1);
|
||||
ARG_MUST_BE_OF_TYPE(0, kComplex);
|
||||
mpreal rho = abs(stack_.value<Complex>(0));
|
||||
mpreal theta = arg(stack_.value<Complex>(0));
|
||||
stack_.value<Complex>(0).real(rho);
|
||||
stack_.value<Complex>(0).imag(theta);
|
||||
}
|
||||
|
||||
/// @brief p2r keyword implementation
|
||||
/// the result is stacked on current program stack
|
||||
///
|
||||
void program::RpnP2r() {
|
||||
MIN_ARGUMENTS(1);
|
||||
ARG_MUST_BE_OF_TYPE(0, kComplex);
|
||||
stack_.value<Complex>(0) = polar(abs(stack_.value<Complex>(0)), arg(stack_.value<Complex>(0)));
|
||||
}
|
|
@ -1,148 +0,0 @@
|
|||
#include "program.hpp"
|
||||
|
||||
/// @brief re keyword implementation
|
||||
/// the result is stacked on current program stack
|
||||
///
|
||||
void program::rpn_re() {
|
||||
MIN_ARGUMENTS(1);
|
||||
ARG_MUST_BE_OF_TYPE(0, cmd_complex);
|
||||
|
||||
stack::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();
|
||||
}
|
||||
|
||||
/// @brief im keyword implementation
|
||||
/// the result is stacked on current program stack
|
||||
///
|
||||
void program::rpn_im() {
|
||||
MIN_ARGUMENTS(1);
|
||||
ARG_MUST_BE_OF_TYPE(0, cmd_complex);
|
||||
|
||||
stack::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();
|
||||
}
|
||||
|
||||
/// @brief arg keyword implementation
|
||||
/// the result is stacked on current program stack
|
||||
///
|
||||
void program::rpn_arg() {
|
||||
MIN_ARGUMENTS(1);
|
||||
ARG_MUST_BE_OF_TYPE(0, cmd_complex);
|
||||
|
||||
// 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();
|
||||
}
|
||||
|
||||
/// @brief conj keyword implementation
|
||||
/// the result is stacked on current program stack
|
||||
///
|
||||
void program::rpn_conj() {
|
||||
MIN_ARGUMENTS(1);
|
||||
ARG_MUST_BE_OF_TYPE(0, cmd_complex);
|
||||
|
||||
complex* cplx = (complex*)_stack->back();
|
||||
CHECK_MPFR(mpfr_neg(cplx->im()->mpfr, cplx->im()->mpfr, floating_t::s_mpfr_rnd));
|
||||
}
|
||||
|
||||
/// @brief r2c keyword implementation
|
||||
/// the result is stacked on current program stack
|
||||
///
|
||||
void program::rpn_r2c() {
|
||||
MIN_ARGUMENTS(2);
|
||||
ARG_MUST_BE_OF_TYPE(0, cmd_number);
|
||||
ARG_MUST_BE_OF_TYPE(1, cmd_number);
|
||||
|
||||
stack::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);
|
||||
}
|
||||
|
||||
/// @brief c2r keyword implementation
|
||||
/// the result is stacked on current program stack
|
||||
///
|
||||
void program::rpn_c2r() {
|
||||
MIN_ARGUMENTS(1);
|
||||
ARG_MUST_BE_OF_TYPE(0, cmd_complex);
|
||||
|
||||
stack::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();
|
||||
}
|
||||
|
||||
/// @brief r2p keyword implementation
|
||||
/// the result is stacked on current program stack
|
||||
///
|
||||
void program::rpn_r2p() {
|
||||
MIN_ARGUMENTS(1);
|
||||
ARG_MUST_BE_OF_TYPE(0, cmd_complex);
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
/// @brief p2r keyword implementation
|
||||
/// the result is stacked on current program stack
|
||||
///
|
||||
void program::rpn_p2r() {
|
||||
MIN_ARGUMENTS(1);
|
||||
ARG_MUST_BE_OF_TYPE(0, cmd_complex);
|
||||
|
||||
stack::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));
|
||||
}
|
222
src/rpn-general.cc
Normal file
222
src/rpn-general.cc
Normal file
|
@ -0,0 +1,222 @@
|
|||
// Copyright (c) 2014-2022 Louis Rubet
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
using std::cout, std::string, std::pair;
|
||||
|
||||
#include "linenoise.h"
|
||||
#include "program.h"
|
||||
#include "version.h"
|
||||
|
||||
#define ATTR_BOLD "\33[1m"
|
||||
#define ATTR_OFF "\33[0m"
|
||||
|
||||
#define MPFR_ROUND \
|
||||
{"nearest (even)", MPFR_RNDN}, {"toward zero", MPFR_RNDZ}, {"toward +inf", MPFR_RNDU}, {"toward -inf", MPFR_RNDD}, \
|
||||
{"away from zero", MPFR_RNDA}, {"faithful rounding", MPFR_RNDF}, { \
|
||||
"nearest (away from zero)", MPFR_RNDNA \
|
||||
}
|
||||
|
||||
static const char _description[] =
|
||||
ATTR_BOLD "R" ATTR_OFF "everse " ATTR_BOLD "P" ATTR_OFF "olish " ATTR_BOLD "N" ATTR_OFF "otation CLI calculator";
|
||||
|
||||
static const char _syntax[] = ATTR_BOLD "Syntax" ATTR_OFF ": rpn [command]\nwith optional command = list of commands";
|
||||
|
||||
static const char _uname[] = ATTR_BOLD "rpn " RPN_VERSION ", (c) 2022 <louis@rubet.fr>" ATTR_OFF;
|
||||
|
||||
/// @brief nop keyword implementation
|
||||
///
|
||||
void program::RpnNop() {
|
||||
// nop
|
||||
}
|
||||
|
||||
/// @brief quit keyword implementation
|
||||
///
|
||||
void program::RpnQuit() { ERROR_CONTEXT(kGoodbye); }
|
||||
|
||||
/// @brief nop keyword implementation
|
||||
/// the result is written on stdout
|
||||
///
|
||||
void program::RpnHelp() {
|
||||
// software name
|
||||
cout << endl << _uname << endl;
|
||||
|
||||
// _description
|
||||
cout << _description << endl << endl;
|
||||
|
||||
// _syntax
|
||||
cout << _syntax << endl;
|
||||
|
||||
// keywords
|
||||
for (auto& kw : keywords_)
|
||||
if (!kw.comment.empty()) {
|
||||
// titles in bold
|
||||
if (kw.type == kUndef) cout << ATTR_BOLD;
|
||||
// show title or keyword + comment
|
||||
cout << kw.name << '\t' << kw.comment << endl;
|
||||
if (kw.type == kUndef) cout << ATTR_OFF;
|
||||
}
|
||||
cout << endl;
|
||||
|
||||
// show mode
|
||||
cout << "Current float mode is ";
|
||||
switch (Number::mode) {
|
||||
case Number::kStd:
|
||||
cout << "'std'";
|
||||
break;
|
||||
case Number::kFix:
|
||||
cout << "'fix'";
|
||||
break;
|
||||
case Number::kSci:
|
||||
cout << "'sci'";
|
||||
break;
|
||||
default:
|
||||
cout << "unknown";
|
||||
break;
|
||||
}
|
||||
|
||||
// bits precision, decimal digits and rounding mode
|
||||
cout << " with " << Number::digits << " digits after the decimal point" << endl;
|
||||
cout << "Current floating point precision is " << static_cast<int>(mpreal::get_default_prec()) << " bits" << endl;
|
||||
vector<pair<string, mpfr_rnd_t>> rnd{MPFR_ROUND};
|
||||
for (auto& rn : rnd)
|
||||
if (rn.second == mpreal::get_default_rnd()) {
|
||||
cout << "Current rounding mode is '" << rn.first << '\'' << endl;
|
||||
break;
|
||||
}
|
||||
cout << endl;
|
||||
}
|
||||
|
||||
/// @brief whether a printed precision is in the precision min/max
|
||||
///
|
||||
/// @param precision the precision in digits
|
||||
/// @return true the precision is good
|
||||
/// @return false the precision is not good
|
||||
///
|
||||
static bool check_decimal_digits(int precision) { return precision >= 0; }
|
||||
|
||||
/// @brief std keyword implementation
|
||||
///
|
||||
void program::RpnStd() {
|
||||
MIN_ARGUMENTS(1);
|
||||
ARG_MUST_BE_OF_TYPE(0, kNumber);
|
||||
|
||||
int digits = static_cast<int>(stack_.value<Number>(0).toLong());
|
||||
|
||||
if (check_decimal_digits(digits)) {
|
||||
// set mode, decimal digits and print format
|
||||
Number::mode = Number::kStd;
|
||||
Number::digits = digits;
|
||||
stack_.pop();
|
||||
} else {
|
||||
ERROR_CONTEXT(kOutOfRange);
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief fix keyword implementation
|
||||
///
|
||||
void program::RpnFix() {
|
||||
MIN_ARGUMENTS(1);
|
||||
ARG_MUST_BE_OF_TYPE(0, kNumber);
|
||||
|
||||
int digits = static_cast<int>(stack_.value<Number>(0).toLong());
|
||||
|
||||
if (check_decimal_digits(digits)) {
|
||||
// set mode, decimal digits and print format
|
||||
Number::mode = Number::kFix;
|
||||
Number::digits = digits;
|
||||
stack_.pop();
|
||||
} else {
|
||||
ERROR_CONTEXT(kOutOfRange);
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief sci keyword implementation
|
||||
///
|
||||
void program::RpnSci() {
|
||||
MIN_ARGUMENTS(1);
|
||||
ARG_MUST_BE_OF_TYPE(0, kNumber);
|
||||
|
||||
int digits = static_cast<int>(stack_.value<Number>(0).toLong());
|
||||
|
||||
if (check_decimal_digits(digits)) {
|
||||
// set mode, decimal digits and print format
|
||||
Number::mode = Number::kSci;
|
||||
Number::digits = digits;
|
||||
stack_.pop();
|
||||
} else {
|
||||
ERROR_CONTEXT(kOutOfRange);
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief _version keyword implementation
|
||||
///
|
||||
void program::RpnVersion() { stack_.push_front(new String(RPN_VERSION)); }
|
||||
|
||||
/// @brief _uname keyword implementation
|
||||
///
|
||||
void program::RpnUname() { stack_.push_front(new String(_uname)); }
|
||||
|
||||
/// @brief history keyword implementation
|
||||
///
|
||||
void program::RpnHistory() {
|
||||
// see command history on stdout
|
||||
int index = 0;
|
||||
char* line = linenoiseHistoryLine(index);
|
||||
while (line != nullptr) {
|
||||
cout << line << endl;
|
||||
free(line);
|
||||
line = linenoiseHistoryLine(++index);
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief type keyword implementation
|
||||
///
|
||||
void program::RpnType() {
|
||||
MIN_ARGUMENTS(1);
|
||||
stack_.push(new String(stack_.at(0)->Name()));
|
||||
stack_.erase(1);
|
||||
}
|
||||
|
||||
/// @brief default keyword implementation
|
||||
///
|
||||
void program::RpnDefault() { program::ApplyDefault(); }
|
||||
|
||||
/// @brief prec keyword implementation
|
||||
///
|
||||
void program::RpnPrecision() {
|
||||
MIN_ARGUMENTS(1);
|
||||
ARG_MUST_BE_OF_TYPE(0, kNumber);
|
||||
|
||||
// set precision
|
||||
int prec = static_cast<int>(stack_.value<Number>(0).toLong());
|
||||
if (prec >= MPFR_PREC_MIN && prec <= MPFR_PREC_MAX) {
|
||||
mpreal::set_default_prec(prec);
|
||||
|
||||
// modify digits seen by user if std mode
|
||||
if (Number::mode == Number::kStd) {
|
||||
// calc max nb of digits user can see with the current bit precision
|
||||
Number::digits = mpfr::bits2digits(mpreal::get_default_prec());
|
||||
}
|
||||
stack_.pop();
|
||||
} else {
|
||||
ERROR_CONTEXT(kOutOfRange);
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief round keyword implementation
|
||||
///
|
||||
void program::RpnRound() {
|
||||
MIN_ARGUMENTS(1);
|
||||
ARG_MUST_BE_OF_TYPE(0, kString);
|
||||
|
||||
map<string, mpfr_rnd_t> matchRound{MPFR_ROUND};
|
||||
|
||||
auto found = matchRound.find(stack_.value<String>(0));
|
||||
if (found != matchRound.end())
|
||||
mpreal::set_default_rnd(found->second);
|
||||
else
|
||||
ERROR_CONTEXT(kOutOfRange);
|
||||
stack_.pop();
|
||||
}
|
|
@ -1,244 +0,0 @@
|
|||
#include "program.hpp"
|
||||
|
||||
/// @brief nop keyword implementation
|
||||
///
|
||||
void program::rpn_nop() {
|
||||
// nop
|
||||
}
|
||||
|
||||
/// @brief quit keyword implementation
|
||||
///
|
||||
void program::rpn_good_bye() { ERR_CONTEXT(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);
|
||||
|
||||
// description
|
||||
printf("%s\n\n", description);
|
||||
|
||||
// syntax
|
||||
printf("%s\n", syntax);
|
||||
|
||||
// keywords
|
||||
unsigned int i = 0;
|
||||
while (s_keywords[i].type != cmd_max) {
|
||||
if (s_keywords[i].comment.size() != 0) {
|
||||
// titles in bold
|
||||
if (s_keywords[i].type == cmd_undef) printf(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);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
// show mode
|
||||
printf("Current float mode is ");
|
||||
switch (number::s_mode) {
|
||||
case number::std:
|
||||
printf("'std'");
|
||||
break;
|
||||
case number::fix:
|
||||
printf("'fix'");
|
||||
break;
|
||||
case number::sci:
|
||||
printf("'sci'");
|
||||
break;
|
||||
default:
|
||||
printf("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();
|
||||
}
|
||||
|
||||
/// @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
|
||||
///
|
||||
static bool check_decimal_digits(double precision) {
|
||||
bool ret = true;
|
||||
|
||||
// MPFR_PREC_MAX mpfr_prec_t depends on _MPFR_PREC_FORMAT macro (see mpfr.h)
|
||||
// this could not exceed 63 bits max (0x7FFFFFFFFFFFFFFF)
|
||||
double prec_max = (double)MPFR_PREC_MAX;
|
||||
double prec_min = 0.0;
|
||||
|
||||
if (precision < prec_min || precision > prec_max) ret = false;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// @brief std keyword implementation
|
||||
///
|
||||
void program::rpn_std() {
|
||||
MIN_ARGUMENTS(1);
|
||||
ARG_MUST_BE_OF_TYPE(0, cmd_number);
|
||||
|
||||
double digits = double(((number*)_stack->pop_back())->_value);
|
||||
|
||||
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);
|
||||
} else
|
||||
ERR_CONTEXT(ret_out_of_range);
|
||||
}
|
||||
|
||||
/// @brief fix keyword implementation
|
||||
///
|
||||
void program::rpn_fix() {
|
||||
MIN_ARGUMENTS(1);
|
||||
ARG_MUST_BE_OF_TYPE(0, cmd_number);
|
||||
|
||||
double digits = double(((number*)_stack->pop_back())->_value);
|
||||
|
||||
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);
|
||||
} else
|
||||
ERR_CONTEXT(ret_out_of_range);
|
||||
}
|
||||
|
||||
/// @brief sci keyword implementation
|
||||
///
|
||||
void program::rpn_sci() {
|
||||
MIN_ARGUMENTS(1);
|
||||
ARG_MUST_BE_OF_TYPE(0, cmd_number);
|
||||
|
||||
double digits = double(((number*)_stack->pop_back())->_value);
|
||||
|
||||
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);
|
||||
} else
|
||||
ERR_CONTEXT(ret_out_of_range);
|
||||
}
|
||||
|
||||
/// @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);
|
||||
}
|
||||
|
||||
/// @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);
|
||||
}
|
||||
|
||||
/// @brief history keyword implementation
|
||||
///
|
||||
void program::rpn_history() {
|
||||
// see command history on stdout
|
||||
int index = 0;
|
||||
char* line = linenoiseHistoryLine(index);
|
||||
while (line != NULL) {
|
||||
cout << line << endl;
|
||||
free(line);
|
||||
line = linenoiseHistoryLine(++index);
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief type keyword implementation
|
||||
///
|
||||
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);
|
||||
}
|
||||
|
||||
/// @brief default keyword implementation
|
||||
///
|
||||
void program::rpn_default() { program::apply_default(); }
|
||||
|
||||
/// @brief prec keyword implementation
|
||||
///
|
||||
void program::rpn_precision() {
|
||||
MIN_ARGUMENTS(1);
|
||||
ARG_MUST_BE_OF_TYPE(0, cmd_number);
|
||||
|
||||
// set precision
|
||||
unsigned long prec = mpfr_get_ui(((number*)_stack->pop_back())->_value.mpfr, floating_t::s_mpfr_rnd);
|
||||
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);
|
||||
|
||||
// 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);
|
||||
}
|
||||
} else
|
||||
ERR_CONTEXT(ret_out_of_range);
|
||||
}
|
||||
|
||||
/// @brief round keyword implementation
|
||||
///
|
||||
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);
|
||||
}
|
175
src/rpn-logs.cc
Normal file
175
src/rpn-logs.cc
Normal file
|
@ -0,0 +1,175 @@
|
|||
// Copyright (c) 2014-2022 Louis Rubet
|
||||
|
||||
#include "program.h"
|
||||
|
||||
/// @brief e keyword implementation
|
||||
///
|
||||
void program::RpnE(void) { stack_.push(new Number(mpfr::const_euler())); }
|
||||
|
||||
/// @brief log10 keyword implementation
|
||||
///
|
||||
void program::RpnLog10() {
|
||||
MIN_ARGUMENTS(1);
|
||||
if (stack_.type(0) == kNumber)
|
||||
stack_.value<Number>(0) = log10(stack_.value<Number>(0));
|
||||
else if (stack_.type(0) == kComplex)
|
||||
stack_.value<Complex>(0) = log10(stack_.value<Complex>(0));
|
||||
else
|
||||
ERROR_CONTEXT(kBadOperandType);
|
||||
}
|
||||
|
||||
/// @brief alog10 keyword implementation
|
||||
///
|
||||
void program::RpnAlog10() {
|
||||
MIN_ARGUMENTS(1);
|
||||
if (stack_.type(0) == kNumber)
|
||||
stack_.value<Number>(0) = exp(log(mpreal(10)) * stack_.value<Number>(0));
|
||||
else if (stack_.type(0) == kComplex)
|
||||
stack_.value<Complex>(0) = exp(log(mpreal(10)) * stack_.value<Complex>(0));
|
||||
else
|
||||
ERROR_CONTEXT(kBadOperandType);
|
||||
}
|
||||
|
||||
/// @brief log2 keyword implementation
|
||||
///
|
||||
void program::RpnLog2() {
|
||||
MIN_ARGUMENTS(1);
|
||||
if (stack_.type(0) == kNumber)
|
||||
stack_.value<Number>(0) = log(stack_.value<Number>(0)) / mpfr::const_log2();
|
||||
else if (stack_.type(0) == kComplex)
|
||||
stack_.value<Complex>(0) = log(stack_.value<Complex>(0)) / mpfr::const_log2();
|
||||
else
|
||||
ERROR_CONTEXT(kBadOperandType);
|
||||
}
|
||||
|
||||
/// @brief alog2 keyword implementation
|
||||
///
|
||||
void program::RpnAlog2() {
|
||||
MIN_ARGUMENTS(1);
|
||||
if (stack_.type(0) == kNumber)
|
||||
stack_.value<Number>(0) = exp(mpfr::const_log2() * stack_.value<Number>(0));
|
||||
else if (stack_.type(0) == kComplex)
|
||||
stack_.value<Complex>(0) = exp(mpfr::const_log2() * stack_.value<Complex>(0));
|
||||
else
|
||||
ERROR_CONTEXT(kBadOperandType);
|
||||
}
|
||||
|
||||
/// @brief ln keyword implementation
|
||||
///
|
||||
void program::RpnLn() {
|
||||
MIN_ARGUMENTS(1);
|
||||
if (stack_.type(0) == kNumber)
|
||||
stack_.value<Number>(0) = log(stack_.value<Number>(0));
|
||||
else if (stack_.type(0) == kComplex)
|
||||
stack_.value<Complex>(0) = log(stack_.value<Complex>(0));
|
||||
else
|
||||
ERROR_CONTEXT(kBadOperandType);
|
||||
}
|
||||
|
||||
/// @brief exp keyword implementation
|
||||
///
|
||||
void program::RpnExp() {
|
||||
MIN_ARGUMENTS(1);
|
||||
if (stack_.type(0) == kNumber)
|
||||
stack_.value<Number>(0) = exp(stack_.value<Number>(0));
|
||||
else if (stack_.type(0) == kComplex)
|
||||
stack_.value<Complex>(0) = exp(stack_.value<Complex>(0));
|
||||
else
|
||||
ERROR_CONTEXT(kBadOperandType);
|
||||
}
|
||||
|
||||
/// @brief expm keyword implementation
|
||||
///
|
||||
void program::RpnExpm() {
|
||||
MIN_ARGUMENTS(1);
|
||||
if (stack_.type(0) == kNumber)
|
||||
stack_.value<Number>(0) = exp(stack_.value<Number>(0)) - mpreal(1);
|
||||
else if (stack_.type(0) == kComplex)
|
||||
stack_.value<Complex>(0) = exp(stack_.value<Complex>(0)) - mpreal(1);
|
||||
else
|
||||
ERROR_CONTEXT(kBadOperandType);
|
||||
}
|
||||
|
||||
/// @brief lnp1 keyword implementation
|
||||
///
|
||||
void program::RpnLnp1() {
|
||||
MIN_ARGUMENTS(1);
|
||||
if (stack_.type(0) == kNumber)
|
||||
stack_.value<Number>(0) = log(stack_.value<Number>(0) + 1);
|
||||
else if (stack_.type(0) == kComplex)
|
||||
stack_.value<Complex>(0) = log(stack_.value<Complex>(0) + mpreal(1));
|
||||
else
|
||||
ERROR_CONTEXT(kBadOperandType);
|
||||
}
|
||||
|
||||
/// @brief sinh keyword implementation
|
||||
///
|
||||
void program::RpnSinh() {
|
||||
MIN_ARGUMENTS(1);
|
||||
if (stack_.type(0) == kNumber)
|
||||
stack_.value<Number>(0) = sinh(stack_.value<Number>(0));
|
||||
else if (stack_.type(0) == kComplex)
|
||||
stack_.value<Complex>(0) = sinh(stack_.value<Complex>(0));
|
||||
else
|
||||
ERROR_CONTEXT(kBadOperandType);
|
||||
}
|
||||
|
||||
/// @brief asinh keyword implementation
|
||||
///
|
||||
void program::RpnAsinh() {
|
||||
MIN_ARGUMENTS(1);
|
||||
if (stack_.type(0) == kNumber)
|
||||
stack_.value<Number>(0) = asinh(stack_.value<Number>(0));
|
||||
else if (stack_.type(0) == kComplex)
|
||||
stack_.value<Complex>(0) = asinh(stack_.value<Complex>(0));
|
||||
else
|
||||
ERROR_CONTEXT(kBadOperandType);
|
||||
}
|
||||
|
||||
/// @brief cosh keyword implementation
|
||||
///
|
||||
void program::RpnCosh() {
|
||||
MIN_ARGUMENTS(1);
|
||||
if (stack_.type(0) == kNumber)
|
||||
stack_.value<Number>(0) = cosh(stack_.value<Number>(0));
|
||||
else if (stack_.type(0) == kComplex)
|
||||
stack_.value<Complex>(0) = cosh(stack_.value<Complex>(0));
|
||||
else
|
||||
ERROR_CONTEXT(kBadOperandType);
|
||||
}
|
||||
|
||||
/// @brief acosh keyword implementation
|
||||
///
|
||||
void program::RpnAcosh() {
|
||||
MIN_ARGUMENTS(1);
|
||||
if (stack_.type(0) == kNumber)
|
||||
stack_.value<Number>(0) = acosh(stack_.value<Number>(0));
|
||||
else if (stack_.type(0) == kComplex)
|
||||
stack_.value<Complex>(0) = acosh(stack_.value<Complex>(0));
|
||||
else
|
||||
ERROR_CONTEXT(kBadOperandType);
|
||||
}
|
||||
|
||||
/// @brief tanh keyword implementation
|
||||
///
|
||||
void program::RpnTanh() {
|
||||
MIN_ARGUMENTS(1);
|
||||
if (stack_.type(0) == kNumber)
|
||||
stack_.value<Number>(0) = tanh(stack_.value<Number>(0));
|
||||
else if (stack_.type(0) == kComplex)
|
||||
stack_.value<Complex>(0) = tanh(stack_.value<Complex>(0));
|
||||
else
|
||||
ERROR_CONTEXT(kBadOperandType);
|
||||
}
|
||||
|
||||
/// @brief atanh keyword implementation
|
||||
///
|
||||
void program::RpnAtanh() {
|
||||
MIN_ARGUMENTS(1);
|
||||
if (stack_.type(0) == kNumber)
|
||||
stack_.value<Number>(0) = atanh(stack_.value<Number>(0));
|
||||
else if (stack_.type(0) == kComplex)
|
||||
stack_.value<Complex>(0) = atanh(stack_.value<Complex>(0));
|
||||
else
|
||||
ERROR_CONTEXT(kBadOperandType);
|
||||
}
|
350
src/rpn-logs.cpp
350
src/rpn-logs.cpp
|
@ -1,350 +0,0 @@
|
|||
#include "program.hpp"
|
||||
|
||||
/// @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));
|
||||
}
|
||||
|
||||
/// @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);
|
||||
}
|
||||
|
||||
/// @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();
|
||||
}
|
||||
}
|
||||
|
||||
/// @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);
|
||||
}
|
||||
|
||||
/// @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();
|
||||
}
|
||||
}
|
||||
|
||||
/// @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);
|
||||
}
|
||||
|
||||
/// @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);
|
||||
}
|
||||
|
||||
/// @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);
|
||||
}
|
||||
|
||||
/// @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);
|
||||
}
|
||||
|
||||
/// @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);
|
||||
}
|
||||
|
||||
/// @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);
|
||||
}
|
||||
|
||||
/// @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);
|
||||
}
|
||||
|
||||
/// @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);
|
||||
}
|
||||
|
||||
/// @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);
|
||||
}
|
||||
|
||||
/// @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);
|
||||
}
|
152
src/rpn-program.cc
Normal file
152
src/rpn-program.cc
Normal file
|
@ -0,0 +1,152 @@
|
|||
// Copyright (c) 2014-2022 Louis Rubet
|
||||
|
||||
#include "program.h"
|
||||
|
||||
/// @brief find variable by its name in local heap, successive parents heaps, global heap
|
||||
///
|
||||
/// @param variable the variable name to find
|
||||
/// @param obj the variable object found
|
||||
/// @return true variable was found
|
||||
/// @return false variable was not found
|
||||
///
|
||||
bool program::FindVariable(string& variable, Object*& obj) {
|
||||
bool found = false;
|
||||
program* parent = parent_;
|
||||
|
||||
if (local_heap_.get(variable, obj)) {
|
||||
found = true;
|
||||
} else {
|
||||
while (parent != nullptr) {
|
||||
if (parent->local_heap_.get(variable, obj)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
parent = parent->parent_;
|
||||
}
|
||||
if (!found) {
|
||||
if (heap_.get(variable, obj)) found = true;
|
||||
}
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
/// @brief eval keyword implementation
|
||||
///
|
||||
void program::RpnEval(void) {
|
||||
bool run_prog = false;
|
||||
string prog_text;
|
||||
|
||||
MIN_ARGUMENTS(1);
|
||||
if (stack_.type(0) == kSymbol) {
|
||||
// recall a variable
|
||||
Object* obj;
|
||||
string variable(stack_.value<Symbol>(0));
|
||||
stack_.pop();
|
||||
|
||||
// if variable holds a program, run this program
|
||||
if (FindVariable(variable, obj)) {
|
||||
if (obj->_type == kProgram) {
|
||||
prog_text = stack_.value<Program>(0);
|
||||
stack_.pop();
|
||||
run_prog = true;
|
||||
} else {
|
||||
// else recall this variable (i.e. stack_ its content)
|
||||
stack_.push_front(obj);
|
||||
}
|
||||
} else {
|
||||
ERROR_CONTEXT(kUnknownVariable);
|
||||
}
|
||||
} else if (stack_.type(0) == kProgram) {
|
||||
// eval a program
|
||||
prog_text = stack_.value<Program>(0);
|
||||
stack_.pop();
|
||||
run_prog = true;
|
||||
} else {
|
||||
ERROR_CONTEXT(kBadOperandType);
|
||||
}
|
||||
|
||||
// run prog if any
|
||||
if (run_prog) {
|
||||
program prog(stack_, heap_, this);
|
||||
|
||||
// make program from entry
|
||||
if (prog.Parse(prog_text) == kOk) {
|
||||
// run it
|
||||
prog.Run();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief -> keyword (Branch) implementation
|
||||
///
|
||||
int program::RpnInprog(Branch& inprog_obj) {
|
||||
string context("->"); // for showing errors
|
||||
size_t count_symbols = 0;
|
||||
bool prog_found = false;
|
||||
|
||||
if (inprog_obj.arg1 == kStepOut) {
|
||||
ERROR_CONTEXT(kUnknownError);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// syntax must be
|
||||
// -> <auto evaluated symbol #1> ... <auto evaluated symbol #n> <program>
|
||||
|
||||
// find next Program object
|
||||
for (unsigned int i = inprog_obj.arg1 + 1; i < size(); i++) {
|
||||
// count symbol
|
||||
if (at(i)->_type == kSymbol) {
|
||||
count_symbols++;
|
||||
} else if (at(i)->_type == kProgram) {
|
||||
// stop if prog
|
||||
prog_found = true;
|
||||
break;
|
||||
} else {
|
||||
// found something other than symbol
|
||||
ERROR_CONTEXT(kBadOperandType);
|
||||
ShowError(err_, context);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// found 0 symbols
|
||||
if (count_symbols == 0) {
|
||||
ERROR_CONTEXT(kSyntaxError);
|
||||
ShowError(err_, context);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// <program> is missing
|
||||
if (!prog_found) {
|
||||
ERROR_CONTEXT(kSyntaxError);
|
||||
ShowError(err_, context);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// check symbols Number vs stack_ size
|
||||
if (stack_.size() < count_symbols) {
|
||||
ERROR_CONTEXT(kMissingOperand);
|
||||
ShowError(err_, context);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// load variables
|
||||
for (size_t i = inprog_obj.arg1 + count_symbols; i > inprog_obj.arg1; i--) {
|
||||
local_heap_[reinterpret_cast<Symbol*>(at(i))->value] = stack_.at(0)->Clone();
|
||||
stack_.pop();
|
||||
}
|
||||
|
||||
// run the program
|
||||
string& entry = reinterpret_cast<Program*>(at(inprog_obj.arg1 + count_symbols + 1))->value;
|
||||
program prog(stack_, heap_, this);
|
||||
|
||||
// make the program from entry
|
||||
if (prog.Parse(entry) == kOk) {
|
||||
// run it
|
||||
prog.Run();
|
||||
}
|
||||
|
||||
// point on next command
|
||||
return inprog_obj.arg1 + count_symbols + 2;
|
||||
}
|
|
@ -1,149 +0,0 @@
|
|||
#include "program.hpp"
|
||||
|
||||
/// @brief find variable by its name in local heap, parens 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 found = false;
|
||||
program* parent = _parent_prog;
|
||||
|
||||
if (_local_heap.get(variable, obj, size))
|
||||
found = true;
|
||||
else {
|
||||
while (parent != NULL) {
|
||||
if (parent->_local_heap.get(variable, obj, size)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
parent = parent->_parent_prog;
|
||||
}
|
||||
if (!found) {
|
||||
if (_heap->get(variable, obj, size)) found = true;
|
||||
}
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
/// @brief eval keyword implementation
|
||||
///
|
||||
void program::rpn_eval(void) {
|
||||
bool run_prog = false;
|
||||
string prog_text;
|
||||
|
||||
MIN_ARGUMENTS(1);
|
||||
if (IS_ARG_TYPE(0, cmd_symbol)) {
|
||||
// recall a variable
|
||||
object* obj;
|
||||
unsigned int size;
|
||||
string variable(((symbol*)_stack->back())->_value);
|
||||
|
||||
// if variable holds a program, run this program
|
||||
if (find_variable(variable, obj, size)) {
|
||||
if (obj->_type == cmd_program) {
|
||||
prog_text = ((oprogram*)obj)->_value;
|
||||
(void)_stack->pop_back();
|
||||
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);
|
||||
}
|
||||
} else
|
||||
ERR_CONTEXT(ret_unknown_variable);
|
||||
} else if (IS_ARG_TYPE(0, cmd_program)) {
|
||||
// eval a program
|
||||
prog_text = ((oprogram*)_stack->pop_back())->_value;
|
||||
run_prog = true;
|
||||
} else
|
||||
ERR_CONTEXT(ret_bad_operand_type);
|
||||
|
||||
// run prog if any
|
||||
if (run_prog) {
|
||||
program prog(this);
|
||||
|
||||
// make program from entry
|
||||
if (program::parse(prog_text.c_str(), prog) == ret_ok) {
|
||||
// run it
|
||||
prog.run(*_stack, *_heap);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief -> keyword (branch) implementation
|
||||
///
|
||||
int program::rpn_inprog(branch& myobj) {
|
||||
string context("->"); // for showing errors
|
||||
int count_symbols = 0;
|
||||
bool prog_found = false;
|
||||
|
||||
if (myobj.arg1 == -1) {
|
||||
ERR_CONTEXT(ret_unknown_err);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// syntax must be
|
||||
// -> <auto evaluated symbol #1> ... <auto evaluated symbol #n> <oprogram>
|
||||
|
||||
// find next oprogram object
|
||||
for (unsigned int i = myobj.arg1 + 1; i < size(); i++) {
|
||||
// count symbol
|
||||
if (seq_type(i) == cmd_symbol) count_symbols++;
|
||||
// stop if prog
|
||||
else if (seq_type(i) == cmd_program) {
|
||||
prog_found = true;
|
||||
break;
|
||||
}
|
||||
// found something other than symbol
|
||||
else {
|
||||
ERR_CONTEXT(ret_bad_operand_type);
|
||||
show_error(_err, context);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// found 0 symbols
|
||||
if (count_symbols == 0) {
|
||||
ERR_CONTEXT(ret_syntax);
|
||||
show_error(_err, context);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// <oprogram> is missing
|
||||
if (!prog_found) {
|
||||
ERR_CONTEXT(ret_syntax);
|
||||
show_error(_err, context);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// check symbols number vs stack size
|
||||
if (stack_size() < count_symbols) {
|
||||
ERR_CONTEXT(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();
|
||||
}
|
||||
|
||||
// run the program
|
||||
string entry(((oprogram*)seq_obj(myobj.arg1 + count_symbols + 1))->_value);
|
||||
program prog(this);
|
||||
|
||||
// make the program from entry
|
||||
if (program::parse(entry.c_str(), prog) == ret_ok) {
|
||||
// run it
|
||||
prog.run(*_stack, *_heap);
|
||||
}
|
||||
|
||||
// point on next command
|
||||
return myobj.arg1 + count_symbols + 2;
|
||||
}
|
389
src/rpn-real.cc
Normal file
389
src/rpn-real.cc
Normal file
|
@ -0,0 +1,389 @@
|
|||
// Copyright (c) 2014-2022 Louis Rubet
|
||||
|
||||
#include "program.h"
|
||||
|
||||
/// @brief + keyword implementation
|
||||
///
|
||||
void program::RpnPlus() {
|
||||
MIN_ARGUMENTS(2);
|
||||
if (stack_.type(0) == kString && stack_.type(1) == kString) {
|
||||
stack_.value<String>(1) += stack_.value<String>(0);
|
||||
stack_.pop();
|
||||
} else if (stack_.type(0) == kNumber && stack_.type(1) == kNumber) {
|
||||
stack_.value<Number>(1) += stack_.value<Number>(0);
|
||||
stack_.pop();
|
||||
} else if (stack_.type(0) == kComplex && stack_.type(1) == kComplex) {
|
||||
stack_.value<Complex>(1) += stack_.value<Complex>(0);
|
||||
stack_.pop();
|
||||
} else if (stack_.type(0) == kNumber && stack_.type(1) == kComplex) {
|
||||
stack_.value<Complex>(1) += stack_.value<Number>(0);
|
||||
stack_.pop();
|
||||
} else if (stack_.type(0) == kComplex && stack_.type(1) == kNumber) {
|
||||
RpnSwap();
|
||||
stack_.value<Complex>(1) += stack_.value<Number>(0);
|
||||
stack_.pop();
|
||||
} else {
|
||||
ERROR_CONTEXT(kBadOperandType);
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief - keyword implementation
|
||||
///
|
||||
void program::RpnMinus() {
|
||||
MIN_ARGUMENTS(2);
|
||||
if (stack_.type(0) == kNumber && stack_.type(1) == kNumber) {
|
||||
stack_.value<Number>(1) -= stack_.value<Number>(0);
|
||||
stack_.pop();
|
||||
} else if (stack_.type(0) == kComplex && stack_.type(1) == kComplex) {
|
||||
stack_.value<Complex>(1) -= stack_.value<Complex>(0);
|
||||
stack_.pop();
|
||||
} else if (stack_.type(0) == kNumber && stack_.type(1) == kComplex) {
|
||||
stack_.value<Complex>(1) -= stack_.value<Number>(0);
|
||||
stack_.pop();
|
||||
} else if (stack_.type(0) == kComplex && stack_.type(1) == kNumber) {
|
||||
RpnSwap();
|
||||
stack_.value<Complex>(1) = stack_.value<Number>(0) - stack_.value<Complex>(1);
|
||||
stack_.pop();
|
||||
} else {
|
||||
ERROR_CONTEXT(kBadOperandType);
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief * keyword implementation
|
||||
///
|
||||
void program::RpnMul() {
|
||||
MIN_ARGUMENTS(2);
|
||||
if (stack_.type(0) == kNumber && stack_.type(1) == kNumber) {
|
||||
stack_.value<Number>(1) *= stack_.value<Number>(0);
|
||||
stack_.pop();
|
||||
} else if (stack_.type(0) == kComplex && stack_.type(1) == kComplex) {
|
||||
stack_.value<Complex>(1) *= stack_.value<Complex>(0);
|
||||
stack_.pop();
|
||||
} else if (stack_.type(0) == kNumber && stack_.type(1) == kComplex) {
|
||||
stack_.value<Complex>(1) *= stack_.value<Number>(0);
|
||||
stack_.pop();
|
||||
} else if (stack_.type(0) == kComplex && stack_.type(1) == kNumber) {
|
||||
RpnSwap();
|
||||
stack_.value<Complex>(1) *= stack_.value<Number>(0);
|
||||
stack_.pop();
|
||||
} else {
|
||||
ERROR_CONTEXT(kBadOperandType);
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief / keyword implementation
|
||||
///
|
||||
void program::RpnDiv() {
|
||||
MIN_ARGUMENTS(2);
|
||||
if (stack_.type(0) == kNumber && stack_.type(1) == kNumber) {
|
||||
stack_.value<Number>(1) /= stack_.value<Number>(0);
|
||||
stack_.pop();
|
||||
} else if (stack_.type(0) == kComplex && stack_.type(1) == kComplex) {
|
||||
stack_.value<Complex>(1) /= stack_.value<Complex>(0);
|
||||
stack_.pop();
|
||||
} else if (stack_.type(0) == kNumber && stack_.type(1) == kComplex) {
|
||||
stack_.value<Complex>(1) /= stack_.value<Number>(0);
|
||||
stack_.pop();
|
||||
} else if (stack_.type(0) == kComplex && stack_.type(1) == kNumber) {
|
||||
RpnSwap();
|
||||
stack_.value<Complex>(1) = stack_.value<Number>(0) / stack_.value<Complex>(1);
|
||||
stack_.pop();
|
||||
} else {
|
||||
ERROR_CONTEXT(kBadOperandType);
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief neg keyword implementation
|
||||
///
|
||||
void program::RpnNeg() {
|
||||
MIN_ARGUMENTS(1);
|
||||
if (stack_.type(0) == kNumber)
|
||||
stack_.value<Number>(0) = -stack_.value<Number>(0);
|
||||
else if (stack_.type(0) == kComplex)
|
||||
stack_.value<Complex>(0) = -stack_.value<Complex>(0);
|
||||
else
|
||||
ERROR_CONTEXT(kBadOperandType);
|
||||
}
|
||||
|
||||
/// @brief inv keyword implementation
|
||||
///
|
||||
void program::RpnInv() {
|
||||
MIN_ARGUMENTS(1);
|
||||
if (stack_.type(0) == kNumber)
|
||||
stack_.value<Number>(0) = 1 / stack_.value<Number>(0);
|
||||
else if (stack_.type(0) == kComplex)
|
||||
stack_.value<Complex>(0) = mpreal(1) / stack_.value<Complex>(0);
|
||||
else
|
||||
ERROR_CONTEXT(kBadOperandType);
|
||||
}
|
||||
|
||||
/// @brief power keyword implementation
|
||||
///
|
||||
void program::RpnPower() {
|
||||
MIN_ARGUMENTS(2);
|
||||
if (stack_.type(0) == kNumber && stack_.type(1) == kNumber) {
|
||||
if (stack_.value<Number>(1) >= 0) {
|
||||
stack_.value<Number>(1) = pow(stack_.value<Number>(1), stack_.value<Number>(0));
|
||||
stack_.pop();
|
||||
} else {
|
||||
mpreal zero;
|
||||
stack_.push(new Complex(stack_.value<Number>(1), zero, stack_.obj<Number>(1).base));
|
||||
stack_.value<Complex>(0) = pow(stack_.value<Complex>(0), stack_.value<Number>(1));
|
||||
stack_.erase(1, 2);
|
||||
}
|
||||
} else if (stack_.type(0) == kComplex && stack_.type(1) == kComplex) {
|
||||
stack_.value<Complex>(1) = pow(stack_.value<Complex>(1), stack_.value<Complex>(0));
|
||||
stack_.pop();
|
||||
} else if (stack_.type(0) == kNumber && stack_.type(1) == kComplex) {
|
||||
stack_.value<Complex>(1) = pow(stack_.value<Complex>(1), stack_.value<Number>(0));
|
||||
stack_.pop();
|
||||
} else if (stack_.type(0) == kComplex && stack_.type(1) == kNumber) {
|
||||
RpnSwap();
|
||||
stack_.value<Complex>(1) = pow(stack_.value<Number>(0), stack_.value<Complex>(1));
|
||||
stack_.pop();
|
||||
} else {
|
||||
ERROR_CONTEXT(kBadOperandType);
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief sqrt keyword implementation
|
||||
///
|
||||
void program::RpnSquareroot() {
|
||||
MIN_ARGUMENTS(1);
|
||||
if (stack_.type(0) == kNumber) {
|
||||
if (stack_.value<Number>(0) >= 0) {
|
||||
stack_.value<Number>(0) = sqrt(stack_.value<Number>(0));
|
||||
} else {
|
||||
// negative number -> square root is complex
|
||||
mpreal zero;
|
||||
stack_.push(new Complex(stack_.value<Number>(0), zero,
|
||||
stack_.obj<Number>(0).base)); // TODO(louis) manage new errors
|
||||
stack_.value<Complex>(0) = sqrt(stack_.value<Complex>(0));
|
||||
stack_.erase(1);
|
||||
}
|
||||
} else if (stack_.type(0) == kComplex) {
|
||||
stack_.value<Complex>(0) = sqrt(stack_.value<Complex>(0));
|
||||
} else {
|
||||
ERROR_CONTEXT(kBadOperandType);
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief hex keyword implementation
|
||||
///
|
||||
void program::RpnHex() {
|
||||
MIN_ARGUMENTS(1);
|
||||
if (stack_.type(0) == kNumber) {
|
||||
stack_.obj<Number>(0).base = 16;
|
||||
} else if (stack_.type(0) == kComplex) {
|
||||
stack_.obj<Complex>(0).re_base = 16;
|
||||
stack_.obj<Complex>(0).im_base = 16;
|
||||
} else {
|
||||
ERROR_CONTEXT(kBadOperandType);
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief bin keyword implementation
|
||||
///
|
||||
void program::RpnBin() {
|
||||
MIN_ARGUMENTS(1);
|
||||
if (stack_.type(0) == kNumber) {
|
||||
stack_.obj<Number>(0).base = 2;
|
||||
} else if (stack_.type(0) == kComplex) {
|
||||
stack_.obj<Complex>(0).re_base = 2;
|
||||
stack_.obj<Complex>(0).im_base = 2;
|
||||
} else {
|
||||
ERROR_CONTEXT(kBadOperandType);
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief dec keyword implementation
|
||||
///
|
||||
void program::RpnDec() {
|
||||
MIN_ARGUMENTS(1);
|
||||
if (stack_.type(0) == kNumber) {
|
||||
stack_.obj<Number>(0).base = 10;
|
||||
} else if (stack_.type(0) == kComplex) {
|
||||
stack_.obj<Complex>(0).re_base = 10;
|
||||
stack_.obj<Complex>(0).im_base = 10;
|
||||
} else {
|
||||
ERROR_CONTEXT(kBadOperandType);
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief base keyword implementation
|
||||
///
|
||||
void program::RpnBase() {
|
||||
MIN_ARGUMENTS(2);
|
||||
if (stack_.type(1) == kNumber || stack_.type(1) == kComplex) {
|
||||
int base = static_cast<int>(stack_.value<Number>(0).toLong());
|
||||
stack_.pop();
|
||||
if (base >= 2 && base <= 62) {
|
||||
if (stack_.type(0) == kNumber) {
|
||||
stack_.obj<Number>(0).base = base;
|
||||
} else {
|
||||
stack_.obj<Complex>(0).re_base = base;
|
||||
stack_.obj<Complex>(0).im_base = base;
|
||||
}
|
||||
} else {
|
||||
ERROR_CONTEXT(kOutOfRange);
|
||||
}
|
||||
} else {
|
||||
ERROR_CONTEXT(kBadOperandType);
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief % (purcent) keyword implementation
|
||||
///
|
||||
void program::RpnPurcent() {
|
||||
MIN_ARGUMENTS(2);
|
||||
ARG_MUST_BE_OF_TYPE(0, kNumber);
|
||||
ARG_MUST_BE_OF_TYPE(1, kNumber);
|
||||
stack_.value<Number>(1) *= stack_.value<Number>(0) / 100;
|
||||
stack_.pop();
|
||||
}
|
||||
|
||||
/// @brief %CH keyword implementation
|
||||
///
|
||||
void program::RpnPurcentCH() {
|
||||
MIN_ARGUMENTS(2);
|
||||
ARG_MUST_BE_OF_TYPE(0, kNumber);
|
||||
ARG_MUST_BE_OF_TYPE(1, kNumber);
|
||||
stack_.value<Number>(1) = (stack_.value<Number>(0) * 100) / stack_.value<Number>(1);
|
||||
stack_.pop();
|
||||
}
|
||||
|
||||
/// @brief sq keyword implementation
|
||||
///
|
||||
void program::RpnSquare() {
|
||||
MIN_ARGUMENTS(1);
|
||||
if (stack_.type(0) == kNumber)
|
||||
stack_.value<Number>(0) *= stack_.value<Number>(0);
|
||||
else if (stack_.at(0)->_type == kComplex)
|
||||
stack_.value<Complex>(0) *= stack_.value<Complex>(0);
|
||||
else
|
||||
ERROR_CONTEXT(kBadOperandType);
|
||||
}
|
||||
|
||||
/// @brief mod keyword implementation
|
||||
///
|
||||
void program::RpnModulo() {
|
||||
MIN_ARGUMENTS(2);
|
||||
ARG_MUST_BE_OF_TYPE(0, kNumber);
|
||||
ARG_MUST_BE_OF_TYPE(1, kNumber);
|
||||
stack_.value<Number>(1) = fmod(stack_.value<Number>(1), stack_.value<Number>(0));
|
||||
stack_.pop();
|
||||
}
|
||||
|
||||
/// @brief abs keyword implementation
|
||||
///
|
||||
void program::RpnAbs() {
|
||||
MIN_ARGUMENTS(1);
|
||||
if (stack_.type(0) == kNumber) {
|
||||
stack_.value<Number>(0) = abs(stack_.value<Number>(0));
|
||||
} else if (stack_.type(0) == kComplex) {
|
||||
stack_.push(new Number(abs(stack_.value<Complex>(0))));
|
||||
stack_.erase(1);
|
||||
} else {
|
||||
ERROR_CONTEXT(kBadOperandType);
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief fact (factorial) keyword implementation
|
||||
///
|
||||
void program::RpnFact() {
|
||||
MIN_ARGUMENTS(1);
|
||||
ARG_MUST_BE_OF_TYPE(0, kNumber);
|
||||
// fact(n) = gamma(n+1)
|
||||
stack_.value<Number>(0) = gamma(stack_.value<Number>(0) + 1);
|
||||
}
|
||||
|
||||
/// @brief sign keyword implementation
|
||||
///
|
||||
void program::RpnSign() {
|
||||
MIN_ARGUMENTS(1);
|
||||
if (stack_.type(0) == kNumber)
|
||||
stack_.value<Number>(0) = sgn(stack_.value<Number>(0));
|
||||
else if (stack_.at(0)->_type == kComplex)
|
||||
stack_.value<Complex>(0) = stack_.value<Complex>(0) / abs(stack_.value<Complex>(0));
|
||||
else
|
||||
ERROR_CONTEXT(kBadOperandType);
|
||||
}
|
||||
|
||||
/// @brief mant keyword implementation
|
||||
///
|
||||
void program::RpnMant() {
|
||||
MIN_ARGUMENTS(1);
|
||||
ARG_MUST_BE_OF_TYPE(0, kNumber);
|
||||
if (!isfinite(stack_.value<Number>(0))) {
|
||||
ERROR_CONTEXT(kOutOfRange);
|
||||
return;
|
||||
}
|
||||
mp_exp_t exp;
|
||||
stack_.value<Number>(0) = frexp(stack_.value<Number>(0), &exp);
|
||||
}
|
||||
|
||||
/// @brief xpon keyword implementation
|
||||
///
|
||||
void program::RpnXpon() {
|
||||
MIN_ARGUMENTS(1);
|
||||
ARG_MUST_BE_OF_TYPE(0, kNumber);
|
||||
if (!isfinite(stack_.value<Number>(0))) {
|
||||
ERROR_CONTEXT(kOutOfRange);
|
||||
return;
|
||||
}
|
||||
mp_exp_t exp;
|
||||
(void)frexp(stack_.value<Number>(0), &exp);
|
||||
stack_.value<Number>(0) = exp;
|
||||
}
|
||||
|
||||
/// @brief floor keyword implementation
|
||||
///
|
||||
void program::RpnFloor() {
|
||||
MIN_ARGUMENTS(1);
|
||||
ARG_MUST_BE_OF_TYPE(0, kNumber);
|
||||
stack_.value<Number>(0) = floor(stack_.value<Number>(0));
|
||||
}
|
||||
|
||||
/// @brief ceil keyword implementation
|
||||
///
|
||||
void program::RpnCeil() {
|
||||
MIN_ARGUMENTS(1);
|
||||
ARG_MUST_BE_OF_TYPE(0, kNumber);
|
||||
stack_.value<Number>(0) = ceil(stack_.value<Number>(0));
|
||||
}
|
||||
|
||||
/// @brief fp keyword implementation
|
||||
///
|
||||
void program::RpnFp() {
|
||||
MIN_ARGUMENTS(1);
|
||||
ARG_MUST_BE_OF_TYPE(0, kNumber);
|
||||
stack_.value<Number>(0) = frac(stack_.value<Number>(0));
|
||||
}
|
||||
|
||||
/// @brief ip keyword implementation
|
||||
///
|
||||
void program::RpnIp() {
|
||||
MIN_ARGUMENTS(1);
|
||||
ARG_MUST_BE_OF_TYPE(0, kNumber);
|
||||
stack_.value<Number>(0) = trunc(stack_.value<Number>(0));
|
||||
}
|
||||
|
||||
/// @brief min keyword implementation
|
||||
///
|
||||
void program::RpnMin() {
|
||||
MIN_ARGUMENTS(2);
|
||||
ARG_MUST_BE_OF_TYPE(0, kNumber);
|
||||
ARG_MUST_BE_OF_TYPE(1, kNumber);
|
||||
stack_.value<Number>(0) = min(stack_.value<Number>(0), stack_.value<Number>(1));
|
||||
stack_.erase(1);
|
||||
}
|
||||
|
||||
/// @brief max keyword implementation
|
||||
///
|
||||
void program::RpnMax() {
|
||||
MIN_ARGUMENTS(2);
|
||||
ARG_MUST_BE_OF_TYPE(0, kNumber);
|
||||
ARG_MUST_BE_OF_TYPE(1, kNumber);
|
||||
stack_.value<Number>(0) = max(stack_.value<Number>(0), stack_.value<Number>(1));
|
||||
stack_.erase(1);
|
||||
}
|
662
src/rpn-real.cpp
662
src/rpn-real.cpp
|
@ -1,662 +0,0 @@
|
|||
#include "program.hpp"
|
||||
|
||||
/// @brief + keyword implementation
|
||||
///
|
||||
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();
|
||||
}
|
||||
// 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));
|
||||
}
|
||||
// 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));
|
||||
}
|
||||
// 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));
|
||||
}
|
||||
// adding number+complex
|
||||
else if (_stack->get_type(0) == cmd_complex && _stack->get_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));
|
||||
} else
|
||||
ERR_CONTEXT(ret_bad_operand_type);
|
||||
}
|
||||
|
||||
/// @brief - keyword implementation
|
||||
///
|
||||
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));
|
||||
}
|
||||
// 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));
|
||||
}
|
||||
// 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));
|
||||
}
|
||||
// substracting number-complex
|
||||
else if (_stack->get_type(0) == cmd_complex && _stack->get_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));
|
||||
} else
|
||||
ERR_CONTEXT(ret_bad_operand_type);
|
||||
}
|
||||
|
||||
/// @brief * keyword implementation
|
||||
///
|
||||
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));
|
||||
}
|
||||
// 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();
|
||||
}
|
||||
// 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));
|
||||
}
|
||||
// multiplying number*complex
|
||||
else if (_stack->get_type(0) == cmd_complex && _stack->get_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));
|
||||
} 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);
|
||||
}
|
||||
|
||||
/// @brief / keyword implementation
|
||||
///
|
||||
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));
|
||||
}
|
||||
// dividing complexes
|
||||
else if (_stack->get_type(0) == cmd_complex && _stack->get_type(1) == cmd_complex)
|
||||
do_divide_complexes();
|
||||
// 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));
|
||||
}
|
||||
// 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
|
||||
ERR_CONTEXT(ret_bad_operand_type);
|
||||
}
|
||||
|
||||
/// @brief neg keyword implementation
|
||||
///
|
||||
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);
|
||||
}
|
||||
|
||||
/// @brief inv keyword implementation
|
||||
///
|
||||
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();
|
||||
} else
|
||||
ERR_CONTEXT(ret_bad_operand_type);
|
||||
}
|
||||
|
||||
/// @brief % (purcent) keyword implementation
|
||||
///
|
||||
void program::rpn_purcent() {
|
||||
MIN_ARGUMENTS(2);
|
||||
ARG_MUST_BE_OF_TYPE(0, cmd_number);
|
||||
ARG_MUST_BE_OF_TYPE(1, cmd_number);
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
/// @brief %CH keyword implementation
|
||||
///
|
||||
void program::rpn_purcentCH() {
|
||||
MIN_ARGUMENTS(2);
|
||||
ARG_MUST_BE_OF_TYPE(0, cmd_number);
|
||||
ARG_MUST_BE_OF_TYPE(1, cmd_number);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/// @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);
|
||||
}
|
||||
|
||||
/// @brief mod keyword implementation
|
||||
///
|
||||
void program::rpn_modulo() {
|
||||
MIN_ARGUMENTS(2);
|
||||
ARG_MUST_BE_OF_TYPE(0, cmd_number);
|
||||
ARG_MUST_BE_OF_TYPE(1, cmd_number);
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
/// @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();
|
||||
} 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);
|
||||
}
|
||||
|
||||
/// @brief fact (factorial) keyword implementation
|
||||
///
|
||||
void program::rpn_fact() {
|
||||
MIN_ARGUMENTS(1);
|
||||
ARG_MUST_BE_OF_TYPE(0, cmd_number);
|
||||
|
||||
// fact(n) = gamma(n+1)
|
||||
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));
|
||||
}
|
||||
|
||||
/// @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);
|
||||
}
|
||||
|
||||
/// @brief mant keyword implementation
|
||||
///
|
||||
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);
|
||||
}
|
||||
|
||||
/// @brief xpon keyword implementation
|
||||
///
|
||||
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);
|
||||
}
|
||||
|
||||
/// @brief floor keyword implementation
|
||||
///
|
||||
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));
|
||||
}
|
||||
|
||||
/// @brief ceil keyword implementation
|
||||
///
|
||||
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));
|
||||
}
|
||||
|
||||
/// @brief fp keyword implementation
|
||||
///
|
||||
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));
|
||||
}
|
||||
|
||||
/// @brief ip keyword implementation
|
||||
///
|
||||
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));
|
||||
}
|
||||
|
||||
/// @brief min keyword implementation
|
||||
///
|
||||
void program::rpn_min() {
|
||||
MIN_ARGUMENTS(2);
|
||||
ARG_MUST_BE_OF_TYPE(0, cmd_number);
|
||||
ARG_MUST_BE_OF_TYPE(1, cmd_number);
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
/// @brief max keyword implementation
|
||||
///
|
||||
void program::rpn_max() {
|
||||
MIN_ARGUMENTS(2);
|
||||
ARG_MUST_BE_OF_TYPE(0, cmd_number);
|
||||
ARG_MUST_BE_OF_TYPE(1, cmd_number);
|
||||
|
||||
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));
|
||||
}
|
149
src/rpn-stack.cc
Normal file
149
src/rpn-stack.cc
Normal file
|
@ -0,0 +1,149 @@
|
|||
// Copyright (c) 2014-2022 Louis Rubet
|
||||
|
||||
#include "program.h"
|
||||
|
||||
/// @brief swap keyword implementation
|
||||
///
|
||||
void program::RpnSwap(void) {
|
||||
MIN_ARGUMENTS(2);
|
||||
Object* tmp = stack_.front();
|
||||
stack_.erase(0, 1, false);
|
||||
stack_.insert(stack_.begin() + 1, tmp);
|
||||
}
|
||||
|
||||
/// @brief drop keyword implementation
|
||||
///
|
||||
void program::RpnDrop(void) {
|
||||
MIN_ARGUMENTS(1);
|
||||
stack_.pop();
|
||||
}
|
||||
|
||||
/// @brief drop2 keyword implementation
|
||||
///
|
||||
void program::RpnDrop2(void) {
|
||||
MIN_ARGUMENTS(2);
|
||||
stack_.erase(0, 2);
|
||||
}
|
||||
|
||||
/// @brief dropn keyword implementation
|
||||
///
|
||||
void program::RpnDropn(void) {
|
||||
MIN_ARGUMENTS(1);
|
||||
ARG_MUST_BE_OF_TYPE(0, kNumber);
|
||||
|
||||
size_t args = stack_.value<Number>(0).toULong();
|
||||
MIN_ARGUMENTS(args + 1);
|
||||
stack_.erase(0, args + 1);
|
||||
}
|
||||
|
||||
/// @brief erase / del keyword implementation
|
||||
///
|
||||
void program::RpnErase(void) { stack_.erase(0, stack_.size()); }
|
||||
|
||||
/// @brief dup keyword implementation
|
||||
///
|
||||
void program::RpnDup(void) {
|
||||
MIN_ARGUMENTS(1);
|
||||
stack_.push_front(stack_.at(0)->Clone());
|
||||
}
|
||||
|
||||
/// @brief dupn keyword implementation
|
||||
///
|
||||
void program::RpnDupn(void) {
|
||||
MIN_ARGUMENTS(1);
|
||||
ARG_MUST_BE_OF_TYPE(0, kNumber);
|
||||
|
||||
size_t args = stack_.value<Number>(0).toULong();
|
||||
stack_.pop();
|
||||
if (args == 0) {
|
||||
ERROR_CONTEXT(kOutOfRange);
|
||||
return;
|
||||
}
|
||||
|
||||
MIN_ARGUMENTS(args);
|
||||
for (size_t i = 0; i < args; i++) stack_.push_front(stack_.at(args - 1)->Clone());
|
||||
}
|
||||
|
||||
/// @brief dup2 keyword implementation
|
||||
///
|
||||
void program::RpnDup2(void) {
|
||||
MIN_ARGUMENTS(2);
|
||||
stack_.push_front(stack_.at(1)->Clone());
|
||||
stack_.push_front(stack_.at(1)->Clone());
|
||||
}
|
||||
|
||||
/// @brief pick keyword implementation
|
||||
///
|
||||
void program::RpnPick(void) {
|
||||
MIN_ARGUMENTS(1);
|
||||
ARG_MUST_BE_OF_TYPE(0, kNumber);
|
||||
|
||||
size_t to_pick = static_cast<int>(stack_.value<Number>(0).toLong());
|
||||
stack_.pop();
|
||||
|
||||
// treat stack_ depth errors
|
||||
if ((to_pick == 0) || (to_pick > stack_.size())) {
|
||||
ERROR_CONTEXT(kOutOfRange);
|
||||
return;
|
||||
}
|
||||
|
||||
stack_.push_front(stack_.at(to_pick - 1)->Clone());
|
||||
}
|
||||
|
||||
/// @brief rot keyword implementation
|
||||
///
|
||||
void program::RpnRot(void) {
|
||||
MIN_ARGUMENTS(3);
|
||||
Object* tmp = stack_.at(2);
|
||||
stack_.erase(2, 1, false);
|
||||
stack_.insert(stack_.begin(), tmp);
|
||||
}
|
||||
|
||||
/// @brief depth keyword implementation
|
||||
///
|
||||
void program::RpnDepth(void) { stack_.push_front(new Number(stack_.size())); }
|
||||
|
||||
/// @brief roll keyword implementation
|
||||
///
|
||||
void program::RpnRoll(void) {
|
||||
MIN_ARGUMENTS(1);
|
||||
ARG_MUST_BE_OF_TYPE(0, kNumber);
|
||||
|
||||
size_t args = static_cast<int>(stack_.value<Number>(0).toLong());
|
||||
stack_.pop();
|
||||
MIN_ARGUMENTS(args);
|
||||
if (args == 0) {
|
||||
ERROR_CONTEXT(kOutOfRange);
|
||||
return;
|
||||
}
|
||||
|
||||
Object* tmp = stack_.at(args - 1);
|
||||
stack_.erase(args - 1, 1, false);
|
||||
stack_.insert(stack_.begin(), tmp);
|
||||
}
|
||||
|
||||
/// @brief rolld keyword implementation
|
||||
///
|
||||
void program::RpnRolld(void) {
|
||||
MIN_ARGUMENTS(2);
|
||||
ARG_MUST_BE_OF_TYPE(0, kNumber);
|
||||
|
||||
size_t args = static_cast<int>(stack_.value<Number>(0).toLong());
|
||||
stack_.pop();
|
||||
MIN_ARGUMENTS(args);
|
||||
if (args == 0) {
|
||||
ERROR_CONTEXT(kOutOfRange);
|
||||
return;
|
||||
}
|
||||
|
||||
Object* tmp = stack_.at(0);
|
||||
stack_.erase(0, 1, false);
|
||||
stack_.insert(stack_.begin() + args - 1, tmp);
|
||||
}
|
||||
|
||||
/// @brief over keyword implementation
|
||||
///
|
||||
void program::RpnOver(void) {
|
||||
MIN_ARGUMENTS(2);
|
||||
stack_.push_front(stack_.at(1)->Clone());
|
||||
}
|
|
@ -1,162 +0,0 @@
|
|||
#include "program.hpp"
|
||||
|
||||
/// @brief swap keyword implementation
|
||||
///
|
||||
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);
|
||||
}
|
||||
|
||||
/// @brief drop keyword implementation
|
||||
///
|
||||
void program::rpn_drop(void) {
|
||||
MIN_ARGUMENTS(1);
|
||||
(void)_stack->pop_back();
|
||||
}
|
||||
|
||||
/// @brief drop2 keyword implementation
|
||||
///
|
||||
void program::rpn_drop2(void) {
|
||||
MIN_ARGUMENTS(2);
|
||||
(void)_stack->pop_back(2);
|
||||
}
|
||||
|
||||
/// @brief dropn keyword implementation
|
||||
///
|
||||
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);
|
||||
MIN_ARGUMENTS(args + 1);
|
||||
|
||||
(void)_stack->pop_back(args + 1);
|
||||
}
|
||||
|
||||
/// @brief erase / del keyword implementation
|
||||
///
|
||||
void program::rpn_erase(void) { (void)_stack->pop_back(_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);
|
||||
}
|
||||
|
||||
/// @brief dupn keyword implementation
|
||||
///
|
||||
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();
|
||||
|
||||
for (int i = 0; i < args; i++) stack::copy_and_push_back(*_stack, _stack->size() - args, *_stack);
|
||||
}
|
||||
|
||||
/// @brief pick keyword implementation
|
||||
///
|
||||
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);
|
||||
|
||||
// treat stack depth errors
|
||||
if ((to_pick == 0) || (to_pick > _stack->size())) {
|
||||
ERR_CONTEXT(ret_out_of_range);
|
||||
return;
|
||||
}
|
||||
|
||||
stack::copy_and_push_back(*_stack, _stack->size() - to_pick, *_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);
|
||||
}
|
||||
|
||||
/// @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);
|
||||
}
|
||||
|
||||
/// @brief roll keyword implementation
|
||||
///
|
||||
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();
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/// @brief rolld keyword implementation
|
||||
///
|
||||
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();
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/// @brief over keyword implementation
|
||||
///
|
||||
void program::rpn_over(void) {
|
||||
MIN_ARGUMENTS(2);
|
||||
|
||||
stack::copy_and_push_back(*_stack, _stack->size() - 2, *_stack);
|
||||
}
|
224
src/rpn-store.cc
Normal file
224
src/rpn-store.cc
Normal file
|
@ -0,0 +1,224 @@
|
|||
// Copyright (c) 2014-2022 Louis Rubet
|
||||
|
||||
#include "input.h"
|
||||
#include "program.h"
|
||||
|
||||
/// @brief sto keyword implementation
|
||||
///
|
||||
void program::RpnSto(void) {
|
||||
MIN_ARGUMENTS(2);
|
||||
ARG_MUST_BE_OF_TYPE(0, kSymbol);
|
||||
|
||||
// store symbol with first value
|
||||
const auto it = heap_.find(stack_.value<String>(0));
|
||||
if (it != heap_.end()) {
|
||||
delete it->second;
|
||||
heap_.erase(it);
|
||||
}
|
||||
heap_[stack_.value<String>(0)] = stack_.at(1)->Clone();
|
||||
stack_.erase(0, 2);
|
||||
}
|
||||
|
||||
/// @brief sto+ keyword implementation
|
||||
///
|
||||
void program::RpnStoadd(void) {
|
||||
MIN_ARGUMENTS(2);
|
||||
ARG_MUST_BE_OF_TYPE(0, kSymbol);
|
||||
if (heap_.find(stack_.value<String>(0)) == heap_.end()) {
|
||||
ERROR_CONTEXT(kUnknownVariable);
|
||||
return;
|
||||
}
|
||||
RpnDup();
|
||||
RpnRcl(); // TODO(louis) is rcl the good one? it will recall local variables too
|
||||
RpnRot();
|
||||
RpnPlus();
|
||||
RpnSwap();
|
||||
RpnSto();
|
||||
}
|
||||
|
||||
/// @brief sto- keyword implementation
|
||||
///
|
||||
void program::RpnStosub(void) {
|
||||
MIN_ARGUMENTS(2);
|
||||
ARG_MUST_BE_OF_TYPE(0, kSymbol);
|
||||
if (heap_.find(stack_.value<String>(0)) == heap_.end()) {
|
||||
ERROR_CONTEXT(kUnknownVariable);
|
||||
return;
|
||||
}
|
||||
RpnDup();
|
||||
RpnRcl();
|
||||
RpnRot();
|
||||
RpnMinus();
|
||||
RpnSwap();
|
||||
RpnSto();
|
||||
}
|
||||
|
||||
/// @brief sto* keyword implementation
|
||||
///
|
||||
void program::RpnStomul(void) {
|
||||
MIN_ARGUMENTS(2);
|
||||
ARG_MUST_BE_OF_TYPE(0, kSymbol);
|
||||
if (heap_.find(stack_.value<String>(0)) == heap_.end()) {
|
||||
ERROR_CONTEXT(kUnknownVariable);
|
||||
return;
|
||||
}
|
||||
RpnDup();
|
||||
RpnRcl();
|
||||
RpnRot();
|
||||
RpnMul();
|
||||
RpnSwap();
|
||||
RpnSto();
|
||||
}
|
||||
|
||||
/// @brief sto/ keyword implementation
|
||||
///
|
||||
void program::RpnStodiv(void) {
|
||||
MIN_ARGUMENTS(2);
|
||||
ARG_MUST_BE_OF_TYPE(0, kSymbol);
|
||||
if (heap_.find(stack_.value<String>(0)) == heap_.end()) {
|
||||
ERROR_CONTEXT(kUnknownVariable);
|
||||
return;
|
||||
}
|
||||
RpnDup();
|
||||
RpnRcl();
|
||||
RpnRot();
|
||||
RpnDiv();
|
||||
RpnSwap();
|
||||
RpnSto();
|
||||
}
|
||||
|
||||
/// @brief stosneg keyword implementation
|
||||
///
|
||||
void program::RpnStoneg(void) {
|
||||
MIN_ARGUMENTS(1);
|
||||
ARG_MUST_BE_OF_TYPE(0, kSymbol);
|
||||
if (heap_.find(stack_.value<String>(0)) == heap_.end()) {
|
||||
ERROR_CONTEXT(kUnknownVariable);
|
||||
return;
|
||||
}
|
||||
RpnDup();
|
||||
RpnRcl();
|
||||
RpnNeg();
|
||||
RpnSwap();
|
||||
RpnSto();
|
||||
}
|
||||
|
||||
/// @brief sinv keyword implementation
|
||||
///
|
||||
void program::RpnStoinv(void) {
|
||||
MIN_ARGUMENTS(1);
|
||||
ARG_MUST_BE_OF_TYPE(0, kSymbol);
|
||||
if (heap_.find(stack_.value<String>(0)) == heap_.end()) {
|
||||
ERROR_CONTEXT(kUnknownVariable);
|
||||
return;
|
||||
}
|
||||
RpnDup();
|
||||
RpnRcl();
|
||||
RpnInv();
|
||||
RpnSwap();
|
||||
RpnSto();
|
||||
}
|
||||
|
||||
/// @brief rcl keyword implementation
|
||||
///
|
||||
void program::RpnRcl(void) {
|
||||
MIN_ARGUMENTS(1);
|
||||
ARG_MUST_BE_OF_TYPE(0, kSymbol);
|
||||
|
||||
// recall a variable
|
||||
Object* obj;
|
||||
string variable(stack_.value<Symbol>(0));
|
||||
|
||||
// mind the order of heaps
|
||||
if (FindVariable(variable, obj)) {
|
||||
(void)stack_.pop();
|
||||
stack_.push_front(obj->Clone());
|
||||
} else {
|
||||
ERROR_CONTEXT(kUnknownVariable);
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief edit keyword implementation
|
||||
///
|
||||
void program::RpnEdit(void) {
|
||||
MIN_ARGUMENTS(1);
|
||||
|
||||
ostringstream st;
|
||||
|
||||
// re-write stack_ objet in a stream
|
||||
stack_.at(0)->Show(st);
|
||||
stack_.pop();
|
||||
|
||||
// set it as the linenoise line entry
|
||||
Input::Preload(st.str().c_str());
|
||||
}
|
||||
|
||||
/// @brief recall then eval a symbol variable if it is auto-evaluable
|
||||
///
|
||||
/// @param symb the smlbol to recall and autoeval
|
||||
///
|
||||
void program::AutoRcl(Symbol* symb) {
|
||||
if (symb->auto_eval) {
|
||||
Object* obj;
|
||||
string variable(symb->value);
|
||||
|
||||
// mind the order of heaps
|
||||
if (FindVariable(variable, obj)) {
|
||||
stack_.push_front(obj->Clone());
|
||||
if (obj->_type == kProgram) RpnEval();
|
||||
} else {
|
||||
stack_.push_front(symb->Clone());
|
||||
}
|
||||
} else {
|
||||
stack_.push_front(symb->Clone());
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief purge keyword implementation
|
||||
///
|
||||
void program::RpnPurge(void) {
|
||||
MIN_ARGUMENTS(1);
|
||||
ARG_MUST_BE_OF_TYPE(0, kSymbol);
|
||||
|
||||
const auto i = heap_.find(stack_.value<Symbol>(0));
|
||||
if (i != heap_.end()) {
|
||||
delete i->second;
|
||||
heap_.erase(i);
|
||||
} else {
|
||||
ERROR_CONTEXT(kUnknownVariable);
|
||||
}
|
||||
stack_.pop();
|
||||
}
|
||||
|
||||
/// @brief vars keyword implementation
|
||||
///
|
||||
void program::RpnVars(void) {
|
||||
program* parent = parent_;
|
||||
string name;
|
||||
int index = 1;
|
||||
|
||||
// heap variables
|
||||
for (auto i : heap_) {
|
||||
cout << "var " << index++ << ": name '" << i.first << "', type " << i.second->Name() << ", value ";
|
||||
i.second->Show(cout) << endl;
|
||||
}
|
||||
|
||||
// local variables
|
||||
for (auto i : local_heap_) {
|
||||
cout << "var " << index++ << ": name '" << i.first << "', type " << i.second->Name() << ", value ";
|
||||
i.second->Show(cout) << endl;
|
||||
}
|
||||
|
||||
// parents local variables
|
||||
while (parent != nullptr) {
|
||||
for (auto i : parent->local_heap_) {
|
||||
cout << "parent var " << index++ << ": name '" << i.first << "', type " << i.second->Name() << ", value ";
|
||||
i.second->Show(cout) << endl;
|
||||
}
|
||||
parent = parent->parent_;
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief clusr keyword implementation
|
||||
///
|
||||
void program::RpnClusr(void) { heap_.clear(); }
|
|
@ -1,294 +0,0 @@
|
|||
#include "program.hpp"
|
||||
|
||||
/// @brief sto keyword implementation
|
||||
///
|
||||
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();
|
||||
}
|
||||
|
||||
/// @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);
|
||||
}
|
||||
|
||||
/// @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);
|
||||
}
|
||||
|
||||
/// @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);
|
||||
}
|
||||
|
||||
/// @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);
|
||||
}
|
||||
|
||||
/// @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);
|
||||
}
|
||||
|
||||
/// @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);
|
||||
}
|
||||
|
||||
/// @brief rcl keyword implementation
|
||||
///
|
||||
void program::rpn_rcl(void) {
|
||||
MIN_ARGUMENTS(1);
|
||||
ARG_MUST_BE_OF_TYPE(0, cmd_symbol);
|
||||
|
||||
// recall a variable
|
||||
object* obj;
|
||||
unsigned int size;
|
||||
string variable(((symbol*)_stack->back())->_value);
|
||||
|
||||
// mind the order of heaps
|
||||
if (find_variable(variable, obj, size)) {
|
||||
(void)_stack->pop_back();
|
||||
stack::copy_and_push_back(obj, *_stack, size);
|
||||
} else
|
||||
ERR_CONTEXT(ret_unknown_variable);
|
||||
}
|
||||
|
||||
/// @brief edit keyword implementation
|
||||
///
|
||||
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);
|
||||
|
||||
// edit: stuff chars using linenoise facility
|
||||
int len = (int)ftell(tmp);
|
||||
rewind(tmp);
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
/// @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) {
|
||||
object* obj;
|
||||
unsigned int size;
|
||||
string variable(symb->_value);
|
||||
|
||||
// mind the order of heaps
|
||||
if (find_variable(variable, obj, size)) {
|
||||
stack::copy_and_push_back(obj, *_stack, size);
|
||||
if (obj->_type == cmd_program) rpn_eval();
|
||||
} else
|
||||
stack::copy_and_push_back(symb, *_stack, symb->size());
|
||||
} else
|
||||
stack::copy_and_push_back(symb, *_stack, symb->size());
|
||||
}
|
||||
|
||||
/// @brief purge keyword implementation
|
||||
///
|
||||
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);
|
||||
}
|
||||
|
||||
/// @brief vars keyword implementation
|
||||
///
|
||||
void program::rpn_vars(void) {
|
||||
object* obj;
|
||||
unsigned int size;
|
||||
program* parent = _parent_prog;
|
||||
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");
|
||||
}
|
||||
|
||||
// parents local variables
|
||||
while (parent != NULL) {
|
||||
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");
|
||||
}
|
||||
parent = parent->_parent_prog;
|
||||
}
|
||||
|
||||
// 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");
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief clusr keyword implementation
|
||||
///
|
||||
void program::rpn_clusr(void) { _heap->erase_all(); }
|
96
src/rpn-string.cc
Normal file
96
src/rpn-string.cc
Normal file
|
@ -0,0 +1,96 @@
|
|||
// Copyright (c) 2014-2022 Louis Rubet
|
||||
|
||||
#include <fcntl.h>
|
||||
|
||||
#include "program.h"
|
||||
|
||||
/// @brief ->str keyword implementation
|
||||
///
|
||||
void program::RpnInstr() {
|
||||
MIN_ARGUMENTS(1);
|
||||
|
||||
// stringify only if not already a string
|
||||
if (stack_.type(0) != kString) {
|
||||
stringstream ss;
|
||||
ss << stack_.at(0);
|
||||
stack_.pop();
|
||||
stack_.push(new String(ss.str()));
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief str-> keyword implementation
|
||||
///
|
||||
void program::RpnStrout() {
|
||||
MIN_ARGUMENTS(1);
|
||||
ARG_MUST_BE_OF_TYPE(0, kString);
|
||||
|
||||
string entry(stack_.value<String>(0));
|
||||
program prog(stack_, heap_);
|
||||
stack_.pop();
|
||||
|
||||
// make program from string in stack_ level 1
|
||||
if (prog.Parse(entry) == kOk)
|
||||
// run it
|
||||
prog.Run();
|
||||
}
|
||||
|
||||
/// @brief chr keyword implementation
|
||||
///
|
||||
void program::RpnChr() {
|
||||
MIN_ARGUMENTS(1);
|
||||
ARG_MUST_BE_OF_TYPE(0, kNumber);
|
||||
char the_chr = static_cast<char>(stack_.value<Number>(0).toLong());
|
||||
stack_.pop();
|
||||
if (the_chr < 32 || the_chr > 126) the_chr = '.';
|
||||
stack_.push_front(new String(string(1, the_chr)));
|
||||
}
|
||||
|
||||
/// @brief num keyword implementation
|
||||
///
|
||||
void program::RpnNum() {
|
||||
MIN_ARGUMENTS(1);
|
||||
ARG_MUST_BE_OF_TYPE(0, kString);
|
||||
if (stack_.value<String>(0).size() > 0)
|
||||
stack_.push_front(new Number(stack_.value<String>(0)[0]));
|
||||
else
|
||||
stack_.push_front(new Number(0));
|
||||
stack_.erase(1);
|
||||
}
|
||||
|
||||
/// @brief size keyword implementation
|
||||
///
|
||||
void program::RpnStrsize() {
|
||||
MIN_ARGUMENTS(1);
|
||||
ARG_MUST_BE_OF_TYPE(0, kString);
|
||||
stack_.push_front(new Number(stack_.value<String>(0).size()));
|
||||
stack_.erase(1);
|
||||
}
|
||||
|
||||
/// @brief pos keyword implementation
|
||||
///
|
||||
void program::RpnStrpos() {
|
||||
MIN_ARGUMENTS(2);
|
||||
ARG_MUST_BE_OF_TYPE(0, kString);
|
||||
ARG_MUST_BE_OF_TYPE(1, kString);
|
||||
|
||||
size_t pos = stack_.value<String>(1).find(stack_.value<String>(0)) + 1;
|
||||
stack_.erase(0, 2);
|
||||
stack_.push_front(new Number(pos));
|
||||
}
|
||||
|
||||
/// @brief sub keyword implementation
|
||||
///
|
||||
void program::RpnStrsub() {
|
||||
MIN_ARGUMENTS(3);
|
||||
ARG_MUST_BE_OF_TYPE(0, kNumber);
|
||||
ARG_MUST_BE_OF_TYPE(1, kNumber);
|
||||
ARG_MUST_BE_OF_TYPE(2, kString);
|
||||
|
||||
size_t first = stack_.value<Number>(1).toULong();
|
||||
size_t len = stack_.value<Number>(0).toULong() - first + 1;
|
||||
first--;
|
||||
|
||||
if (first > stack_.value<String>(2).size()) first = len = 0;
|
||||
stack_.push(new String(stack_.value<String>(2).substr(first, len)));
|
||||
stack_.erase(1, 3);
|
||||
}
|
|
@ -1,146 +0,0 @@
|
|||
#include "program.hpp"
|
||||
|
||||
/// @brief ->str keyword implementation
|
||||
///
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief str-> keyword implementation
|
||||
///
|
||||
void program::rpn_strout() {
|
||||
MIN_ARGUMENTS(1);
|
||||
ARG_MUST_BE_OF_TYPE(0, cmd_string);
|
||||
|
||||
string entry(((ostring*)_stack->pop_back())->_value);
|
||||
|
||||
program prog;
|
||||
|
||||
// make program from string in stack level 1
|
||||
if (program::parse(entry.c_str(), prog) == ret_ok)
|
||||
// run it
|
||||
prog.run(*_stack, *_heap);
|
||||
}
|
||||
|
||||
/// @brief chr keyword implementation
|
||||
///
|
||||
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);
|
||||
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;
|
||||
}
|
||||
|
||||
/// @brief num keyword implementation
|
||||
///
|
||||
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;
|
||||
}
|
||||
|
||||
/// @brief size keyword implementation
|
||||
///
|
||||
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;
|
||||
}
|
||||
|
||||
/// @brief pos keyword implementation
|
||||
///
|
||||
void program::rpn_strpos() {
|
||||
MIN_ARGUMENTS(2);
|
||||
ARG_MUST_BE_OF_TYPE(0, cmd_string);
|
||||
ARG_MUST_BE_OF_TYPE(1, cmd_string);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/// @brief sub keyword implementation
|
||||
///
|
||||
void program::rpn_strsub() {
|
||||
MIN_ARGUMENTS(3);
|
||||
ARG_MUST_BE_OF_TYPE(0, cmd_number);
|
||||
ARG_MUST_BE_OF_TYPE(1, cmd_number);
|
||||
ARG_MUST_BE_OF_TYPE(2, cmd_string);
|
||||
|
||||
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;
|
||||
|
||||
_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;
|
||||
}
|
||||
}
|
|
@ -1,239 +0,0 @@
|
|||
#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);
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief show the tests results
|
||||
///
|
||||
/// @param title test title
|
||||
/// @param tests tests nb
|
||||
/// @param tests_failed failed tests nb
|
||||
/// @param steps steps nb
|
||||
/// @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);
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
/// @brief test keyword implementation
|
||||
///
|
||||
void program::rpn_test() {
|
||||
MIN_ARGUMENTS(1);
|
||||
ARG_MUST_BE_OF_TYPE(0, cmd_string);
|
||||
|
||||
int total_tests = 0;
|
||||
int total_tests_failed = 0;
|
||||
int total_steps = 0;
|
||||
int total_steps_failed = 0;
|
||||
|
||||
string test_filename = ((ostring*)_stack->pop_back())->_value;
|
||||
printf("\nrpn version is %s\n", version);
|
||||
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);
|
||||
}
|
||||
|
||||
/// @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
|
||||
/// @param total_steps the total steps nb
|
||||
/// @param total_steps_failed the total failed steps nb
|
||||
///
|
||||
void program::test(string test_filename, int& total_tests, int& total_tests_failed, int& total_steps,
|
||||
int& total_steps_failed) {
|
||||
const string stack_size("-> stack size should be ");
|
||||
const string stack_value("-> stack should be ");
|
||||
const string cmd_error("-> error should be ");
|
||||
const string cmd_exit("exit test");
|
||||
|
||||
ifstream test_file(test_filename.c_str());
|
||||
int tests = 0;
|
||||
int tests_failed = 0;
|
||||
int steps = 0;
|
||||
int steps_failed = 0;
|
||||
|
||||
if (test_file.is_open()) {
|
||||
string test_title;
|
||||
string entry;
|
||||
ret_value ret;
|
||||
stack stk;
|
||||
heap hp;
|
||||
bool failed = false;
|
||||
bool is_first_step;
|
||||
bool is_test_error_shown;
|
||||
int last_err;
|
||||
stringstream cerr_buffer;
|
||||
streambuf* cerr_old_buffer;
|
||||
|
||||
// redirect cerr
|
||||
cerr_old_buffer = cerr.rdbuf(cerr_buffer.rdbuf());
|
||||
|
||||
while (!test_file.eof()) {
|
||||
getline(test_file, entry);
|
||||
|
||||
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) == "# ") {
|
||||
// indicates the status of previous test
|
||||
if (failed == false && tests > 0) printf(FG_GREEN " PASSED" COLOR_OFF "\n");
|
||||
failed = false;
|
||||
|
||||
// read a test title
|
||||
test_title = entry;
|
||||
is_first_step = true;
|
||||
is_test_error_shown = false;
|
||||
printf("%s", test_title.c_str());
|
||||
}
|
||||
// treat "-> stack size should be "
|
||||
else if (entry.find(stack_size, 0) == 0) {
|
||||
// count test and step
|
||||
if (is_first_step) tests++;
|
||||
steps++;
|
||||
|
||||
// check current stack size
|
||||
istringstream isub;
|
||||
int size;
|
||||
|
||||
isub.str(entry.substr(stack_size.size()));
|
||||
isub >> size;
|
||||
if (size != (int)stk.size()) {
|
||||
// count fail test and step
|
||||
if (!is_test_error_shown) {
|
||||
printf(FG_RED " FAIL" COLOR_OFF "\n");
|
||||
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());
|
||||
failed = true;
|
||||
}
|
||||
is_first_step = false;
|
||||
}
|
||||
// treat "-> stack should be "
|
||||
else if (entry.find(stack_value, 0) == 0) {
|
||||
// count test
|
||||
if (is_first_step) tests++;
|
||||
steps++;
|
||||
|
||||
// check current stack value
|
||||
string stack_should_be = entry.substr(stack_value.size());
|
||||
string stack_is;
|
||||
|
||||
test_get_stack(stack_is, stk);
|
||||
|
||||
if (stack_is != stack_should_be) {
|
||||
// count fail test and step
|
||||
if (!is_test_error_shown) {
|
||||
printf(FG_RED " FAIL" COLOR_OFF "\n");
|
||||
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());
|
||||
failed = true;
|
||||
}
|
||||
is_first_step = false;
|
||||
}
|
||||
// treat "-> error should be "
|
||||
else if (entry.find(cmd_error, 0) == 0) {
|
||||
// count test
|
||||
if (is_first_step) tests++;
|
||||
steps++;
|
||||
|
||||
// check current error
|
||||
istringstream isub;
|
||||
int err_should_be;
|
||||
isub.str(entry.substr(cmd_error.size()));
|
||||
isub >> err_should_be;
|
||||
if (err_should_be != last_err) {
|
||||
// count fail test and step
|
||||
if (!is_test_error_shown) {
|
||||
printf(FG_RED " FAIL" COLOR_OFF "\n");
|
||||
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);
|
||||
failed = true;
|
||||
}
|
||||
is_first_step = false;
|
||||
} else if (entry.find(cmd_exit, 0) == 0) {
|
||||
// forced test end
|
||||
break;
|
||||
} else if (entry.size() > 0) {
|
||||
// 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// last test
|
||||
// indicates the status of previous test
|
||||
if (failed == false && tests > 0) printf(FG_GREEN " PASSED" COLOR_OFF "\n");
|
||||
|
||||
// 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);
|
||||
|
||||
total_tests += tests;
|
||||
total_tests_failed += tests_failed;
|
||||
total_steps += steps;
|
||||
total_steps_failed += steps_failed;
|
||||
}
|
||||
} else
|
||||
fprintf(stderr, "test file '%s' not found\n", test_filename.c_str());
|
||||
}
|
277
src/rpn-test-framework.cc
Normal file
277
src/rpn-test-framework.cc
Normal file
|
@ -0,0 +1,277 @@
|
|||
// Copyright (c) 2014-2022 Louis Rubet
|
||||
|
||||
#include <string>
|
||||
using std::string;
|
||||
|
||||
#include "program.h"
|
||||
#include "version.h"
|
||||
|
||||
// foreground colors
|
||||
static const char FG_RED[] = "\33[31m";
|
||||
static const char FG_GREEN[] = "\33[32m";
|
||||
static const char COLOR_OFF[] = "\33[m";
|
||||
|
||||
static void FindAndReplaceAll(string& data, string toSearch, string replaceStr) {
|
||||
// Get the first occurrence
|
||||
size_t pos = data.find(toSearch);
|
||||
// Repeat till end is reached
|
||||
while (pos != string::npos) {
|
||||
// Replace this occurrence of Sub String
|
||||
data.replace(pos, toSearch.size(), replaceStr);
|
||||
// Get the next occurrence from the current position
|
||||
pos = data.find(toSearch, pos + replaceStr.size());
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief write stack in a string, each entry separated between commas
|
||||
///
|
||||
/// @param stack_is the output string
|
||||
/// @param stk the stack
|
||||
///
|
||||
static void GetStackAsString(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 = static_cast<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
|
||||
/// @param steps steps nb
|
||||
/// @param steps_failed failed steps nb
|
||||
///
|
||||
static void ShowTestResult(string title, int tests, int tests_failed, int steps, int steps_failed) {
|
||||
if (!title.empty()) cout << title << ": ";
|
||||
cout << "run " << tests << " tests: " << tests - tests_failed << " passed, ";
|
||||
if (tests_failed > 0) cout << FG_RED;
|
||||
cout << tests_failed << " failed";
|
||||
if (tests_failed > 0) cout << COLOR_OFF;
|
||||
|
||||
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
|
||||
///
|
||||
void program::RpnTest() {
|
||||
MIN_ARGUMENTS(1);
|
||||
ARG_MUST_BE_OF_TYPE(0, kString);
|
||||
|
||||
int total_tests = 0;
|
||||
int total_tests_failed = 0;
|
||||
int total_steps = 0;
|
||||
int total_steps_failed = 0;
|
||||
|
||||
string test_filename = stack_.value<String>(0);
|
||||
stack_.pop();
|
||||
cout << endl << "rpn version is " << RPN_VERSION << endl;
|
||||
RunTestFile(test_filename, total_tests, total_tests_failed, total_steps, total_steps_failed);
|
||||
ShowTestResult("\nTotal", total_tests, total_tests_failed, total_steps, total_steps_failed);
|
||||
|
||||
// notify to caller that test succeeded or not
|
||||
if (total_tests_failed > 0) {
|
||||
err_ = kTestFailed;
|
||||
err_context_ = string("rpn version ") + RPN_VERSION + ", test file " + test_filename;
|
||||
}
|
||||
}
|
||||
|
||||
/// @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
|
||||
/// @param total_steps the total steps nb
|
||||
/// @param total_steps_failed the total failed steps nb
|
||||
///
|
||||
void program::RunTestFile(string test_filename, int& total_tests, int& total_tests_failed, int& total_steps,
|
||||
int& total_steps_failed) {
|
||||
const string stack_size("-> stack size should be ");
|
||||
const string stack_value("-> stack should be ");
|
||||
const string cmd_error("-> error should be ");
|
||||
const string cmd_exit("exit test");
|
||||
|
||||
ifstream test_file(test_filename.c_str());
|
||||
int tests = 0;
|
||||
int tests_failed = 0;
|
||||
int steps = 0;
|
||||
int steps_failed = 0;
|
||||
|
||||
if (test_file.is_open()) {
|
||||
string test_title;
|
||||
string entry;
|
||||
RetValue ret;
|
||||
rpnstack stk;
|
||||
heap hp;
|
||||
bool failed = false;
|
||||
bool is_first_step = true;
|
||||
bool is_test_error_shown = false;
|
||||
int last_err = static_cast<int>(kOk);
|
||||
stringstream cerr_buffer;
|
||||
streambuf* cerr_old_buffer;
|
||||
|
||||
// redirect cerr
|
||||
cerr_old_buffer = cerr.rdbuf(cerr_buffer.rdbuf());
|
||||
|
||||
while (!test_file.eof()) {
|
||||
getline(test_file, entry);
|
||||
if (entry.empty()) continue;
|
||||
|
||||
if (entry.substr(0, 8) == "@include") {
|
||||
RunTestFile(entry.substr(9), total_tests, total_tests_failed, total_steps, total_steps_failed);
|
||||
} else if (entry.substr(0, 2) == "# ") {
|
||||
cout << endl << test_filename << ": " << entry.substr(2) << endl;
|
||||
} else if (entry.substr(0, 3) == "## ") {
|
||||
// indicates the status of previous test
|
||||
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;
|
||||
cout << test_title;
|
||||
} else if (entry.find(stack_size, 0) == 0) { // treat "-> stack size should be "
|
||||
// count test and step
|
||||
if (is_first_step) tests++;
|
||||
steps++;
|
||||
|
||||
// check current stack size
|
||||
istringstream isub;
|
||||
int size;
|
||||
|
||||
isub.str(entry.substr(stack_size.size()));
|
||||
isub >> size;
|
||||
if (size != static_cast<int>(stk.size())) {
|
||||
// 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 << '\t' << entry << endl;
|
||||
cout << "\t but real stack size is " << FG_RED << stk.size() << COLOR_OFF << endl;
|
||||
failed = true;
|
||||
}
|
||||
is_first_step = false;
|
||||
} else if (entry.find(stack_value, 0) == 0) { // treat "-> stack should be "
|
||||
// count test
|
||||
if (is_first_step) tests++;
|
||||
steps++;
|
||||
|
||||
// check current stack value
|
||||
string stack_should_be = entry.substr(stack_value.size());
|
||||
string stack_is;
|
||||
|
||||
GetStackAsString(stack_is, stk);
|
||||
|
||||
if (stack_is != stack_should_be) {
|
||||
// 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 << '\t' << entry << endl;
|
||||
cout << "\t but real stack is " << FG_RED << stack_is << COLOR_OFF << endl;
|
||||
failed = true;
|
||||
}
|
||||
is_first_step = false;
|
||||
} else if (entry.find(cmd_error, 0) == 0) { // treat "-> error should be "
|
||||
// count test
|
||||
if (is_first_step) tests++;
|
||||
steps++;
|
||||
|
||||
// check current error
|
||||
istringstream isub;
|
||||
int err_should_be;
|
||||
isub.str(entry.substr(cmd_error.size()));
|
||||
isub >> err_should_be;
|
||||
if (err_should_be != last_err) {
|
||||
// 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 << '\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.find("->", 0) == 0) { // treat unknown "->"
|
||||
// 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
|
||||
FindAndReplaceAll(entry, "`", "");
|
||||
if (!entry.empty()) {
|
||||
program prog(stk, hp);
|
||||
ret = prog.Parse(entry);
|
||||
if (ret == kOk) {
|
||||
// run it
|
||||
(void)prog.Run();
|
||||
last_err = static_cast<int>(prog.GetLastError());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// last test
|
||||
// indicates the status of previous test
|
||||
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) {
|
||||
ShowTestResult("", tests, tests_failed, steps, steps_failed);
|
||||
|
||||
total_tests += tests;
|
||||
total_tests_failed += tests_failed;
|
||||
total_steps += steps;
|
||||
total_steps_failed += steps_failed;
|
||||
}
|
||||
} else {
|
||||
cerr << "test file '" << test_filename << "' not found" << endl;
|
||||
}
|
||||
}
|
166
src/rpn-test.cc
Normal file
166
src/rpn-test.cc
Normal file
|
@ -0,0 +1,166 @@
|
|||
// Copyright (c) 2014-2022 Louis Rubet
|
||||
|
||||
#include "program.h"
|
||||
|
||||
/// @brief compared 2 strings on top of the stack_
|
||||
///
|
||||
/// @return 0 strings are equal
|
||||
/// @return !0 strings are not equal (see strcmp output)
|
||||
///
|
||||
static int CmpStringOnStackTop(rpnstack& stk) {
|
||||
// stack_ should have 2 strings at level 1 and 2
|
||||
// this function removes these 2 entries
|
||||
int res = stk.value<String>(1).compare(stk.value<String>(0));
|
||||
stk.erase(0, 2);
|
||||
return res;
|
||||
}
|
||||
|
||||
/// @brief > keyword implementation
|
||||
///
|
||||
void program::RpnSup(void) {
|
||||
MIN_ARGUMENTS(2);
|
||||
if (stack_.type(0) == kNumber && stack_.type(1) == kNumber) {
|
||||
stack_.push_front(new Number(stack_.value<Number>(1) > stack_.value<Number>(0)));
|
||||
stack_.erase(1, 2);
|
||||
} else if (stack_.type(0) == kString && stack_.type(1) == kString) {
|
||||
stack_.push_front(new Number(CmpStringOnStackTop(stack_) == 1));
|
||||
stack_.erase(1, 2);
|
||||
} else {
|
||||
ERROR_CONTEXT(kBadOperandType);
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief >= keyword implementation
|
||||
///
|
||||
void program::RpnSupEq(void) {
|
||||
MIN_ARGUMENTS(2);
|
||||
if (stack_.type(0) == kNumber && stack_.type(1) == kNumber) {
|
||||
stack_.push_front(new Number(stack_.value<Number>(1) >= stack_.value<Number>(0)));
|
||||
stack_.erase(1, 2);
|
||||
} else if (stack_.type(0) == kString && stack_.type(1) == kString) {
|
||||
stack_.push_front(new Number(CmpStringOnStackTop(stack_) != -1));
|
||||
stack_.erase(1, 2);
|
||||
} else {
|
||||
ERROR_CONTEXT(kBadOperandType);
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief < keyword implementation
|
||||
///
|
||||
void program::RpnInf(void) {
|
||||
MIN_ARGUMENTS(2);
|
||||
|
||||
if (stack_.type(0) == kNumber && stack_.type(1) == kNumber) {
|
||||
stack_.push_front(new Number(stack_.value<Number>(1) < stack_.value<Number>(0)));
|
||||
stack_.erase(1, 2);
|
||||
} else if (stack_.type(0) == kString && stack_.type(1) == kString) {
|
||||
stack_.push_front(new Number(CmpStringOnStackTop(stack_) == -1));
|
||||
stack_.erase(1, 2);
|
||||
} else {
|
||||
ERROR_CONTEXT(kBadOperandType);
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief <= keyword implementation
|
||||
///
|
||||
void program::RpnInfEq(void) {
|
||||
MIN_ARGUMENTS(2);
|
||||
if (stack_.type(0) == kNumber && stack_.type(1) == kNumber) {
|
||||
stack_.push_front(new Number(stack_.value<Number>(1) <= stack_.value<Number>(0)));
|
||||
stack_.erase(1, 2);
|
||||
} else if (stack_.type(0) == kString && stack_.type(1) == kString) {
|
||||
stack_.push_front(new Number(CmpStringOnStackTop(stack_) != 1));
|
||||
stack_.erase(1, 2);
|
||||
} else {
|
||||
ERROR_CONTEXT(kBadOperandType);
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief != keyword implementation
|
||||
///
|
||||
void program::RpnDiff(void) {
|
||||
MIN_ARGUMENTS(2);
|
||||
if (stack_.type(0) == kNumber && stack_.type(1) == kNumber) {
|
||||
stack_.push_front(new Number(stack_.value<Number>(1) != stack_.value<Number>(0)));
|
||||
stack_.erase(1, 2);
|
||||
} else if (stack_.type(0) == kComplex && stack_.type(1) == kComplex) {
|
||||
stack_.push_front(new Number(stack_.value<Complex>(1) != stack_.value<Complex>(0)));
|
||||
stack_.erase(1, 2);
|
||||
} else if (stack_.type(0) == kString && stack_.type(1) == kString) {
|
||||
stack_.push_front(new Number(CmpStringOnStackTop(stack_) != 0));
|
||||
stack_.erase(1, 2);
|
||||
} else {
|
||||
ERROR_CONTEXT(kBadOperandType);
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief == keyword implementation
|
||||
///
|
||||
void program::RpnEq(void) {
|
||||
MIN_ARGUMENTS(2);
|
||||
if (stack_.type(0) == kNumber && stack_.type(1) == kNumber) {
|
||||
stack_.push_front(new Number(stack_.value<Number>(1) == stack_.value<Number>(0)));
|
||||
stack_.erase(1, 2);
|
||||
} else if (stack_.type(0) == kComplex && stack_.type(1) == kComplex) {
|
||||
stack_.push_front(new Number(stack_.value<Complex>(1) == stack_.value<Complex>(0)));
|
||||
stack_.erase(1, 2);
|
||||
} else if (stack_.type(0) == kString && stack_.type(1) == kString) {
|
||||
stack_.push_front(new Number(CmpStringOnStackTop(stack_) == 0));
|
||||
stack_.erase(1, 2);
|
||||
} else {
|
||||
ERROR_CONTEXT(kBadOperandType);
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief and keyword implementation
|
||||
///
|
||||
void program::RpnTestAnd(void) {
|
||||
MIN_ARGUMENTS(2);
|
||||
ARG_MUST_BE_OF_TYPE(0, kNumber);
|
||||
ARG_MUST_BE_OF_TYPE(1, kNumber);
|
||||
if (stack_.value<Number>(0) != 0 && stack_.value<Number>(1) != 0)
|
||||
stack_.push(new Number(1));
|
||||
else
|
||||
stack_.push(new Number(0));
|
||||
stack_.erase(1, 2);
|
||||
}
|
||||
|
||||
/// @brief or keyword implementation
|
||||
///
|
||||
void program::RpnTestOr(void) {
|
||||
MIN_ARGUMENTS(2);
|
||||
ARG_MUST_BE_OF_TYPE(0, kNumber);
|
||||
ARG_MUST_BE_OF_TYPE(1, kNumber);
|
||||
if (stack_.value<Number>(0) != 0 || stack_.value<Number>(1) != 0)
|
||||
stack_.push(new Number(1));
|
||||
else
|
||||
stack_.push(new Number(0));
|
||||
stack_.erase(1, 2);
|
||||
}
|
||||
|
||||
/// @brief xor keyword implementation
|
||||
///
|
||||
void program::RpnTestXor(void) {
|
||||
MIN_ARGUMENTS(2);
|
||||
ARG_MUST_BE_OF_TYPE(0, kNumber);
|
||||
ARG_MUST_BE_OF_TYPE(1, kNumber);
|
||||
if ((stack_.value<Number>(0) != 0) ^ (stack_.value<Number>(1) != 0))
|
||||
stack_.push(new Number(1));
|
||||
else
|
||||
stack_.push(new Number(0));
|
||||
stack_.erase(1, 2);
|
||||
}
|
||||
|
||||
/// @brief not keyword implementation
|
||||
///
|
||||
void program::RpnTestNot(void) {
|
||||
MIN_ARGUMENTS(1);
|
||||
ARG_MUST_BE_OF_TYPE(0, kNumber);
|
||||
|
||||
stack_.push(new Number(stack_.value<Number>(0) == 0 ? 1 : 0));
|
||||
stack_.erase(1, 1);
|
||||
}
|
||||
|
||||
/// @brief test same implementation
|
||||
///
|
||||
void program::RpnSame(void) { RpnEq(); }
|
260
src/rpn-test.cpp
260
src/rpn-test.cpp
|
@ -1,260 +0,0 @@
|
|||
#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
|
||||
// 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));
|
||||
}
|
||||
|
||||
/// @brief > keyword implementation
|
||||
///
|
||||
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);
|
||||
}
|
||||
// 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
|
||||
ERR_CONTEXT(ret_bad_operand_type);
|
||||
}
|
||||
|
||||
/// @brief >= keyword implementation
|
||||
///
|
||||
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);
|
||||
}
|
||||
// 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
|
||||
ERR_CONTEXT(ret_bad_operand_type);
|
||||
}
|
||||
|
||||
/// @brief < keyword implementation
|
||||
///
|
||||
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);
|
||||
}
|
||||
// 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
|
||||
ERR_CONTEXT(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);
|
||||
}
|
||||
// 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
|
||||
ERR_CONTEXT(ret_bad_operand_type);
|
||||
}
|
||||
|
||||
/// @brief != keyword implementation
|
||||
///
|
||||
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);
|
||||
}
|
||||
// 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);
|
||||
}
|
||||
// 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
|
||||
ERR_CONTEXT(ret_bad_operand_type);
|
||||
}
|
||||
|
||||
/// @brief == keyword implementation
|
||||
///
|
||||
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);
|
||||
}
|
||||
// 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);
|
||||
}
|
||||
// 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
|
||||
ERR_CONTEXT(ret_bad_operand_type);
|
||||
}
|
||||
|
||||
/// @brief and keyword implementation
|
||||
///
|
||||
void program::rpn_test_and(void) {
|
||||
MIN_ARGUMENTS(2);
|
||||
ARG_MUST_BE_OF_TYPE(0, cmd_number);
|
||||
ARG_MUST_BE_OF_TYPE(1, cmd_number);
|
||||
|
||||
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);
|
||||
else
|
||||
mpfr_set_si(left->_value.mpfr, 0, floating_t::s_mpfr_rnd);
|
||||
}
|
||||
|
||||
/// @brief or keyword implementation
|
||||
///
|
||||
void program::rpn_test_or(void) {
|
||||
MIN_ARGUMENTS(2);
|
||||
ARG_MUST_BE_OF_TYPE(0, cmd_number);
|
||||
ARG_MUST_BE_OF_TYPE(1, cmd_number);
|
||||
|
||||
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);
|
||||
else
|
||||
mpfr_set_si(left->_value.mpfr, 0, floating_t::s_mpfr_rnd);
|
||||
}
|
||||
|
||||
/// @brief xor keyword implementation
|
||||
///
|
||||
void program::rpn_test_xor(void) {
|
||||
MIN_ARGUMENTS(2);
|
||||
ARG_MUST_BE_OF_TYPE(0, cmd_number);
|
||||
ARG_MUST_BE_OF_TYPE(1, cmd_number);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief not keyword implementation
|
||||
///
|
||||
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);
|
||||
}
|
||||
|
||||
/// @brief test same implementation
|
||||
///
|
||||
void program::rpn_same(void) { rpn_eq(); }
|
73
src/rpn-time.cc
Normal file
73
src/rpn-time.cc
Normal file
|
@ -0,0 +1,73 @@
|
|||
// Copyright (c) 2014-2022 Louis Rubet
|
||||
|
||||
#include <ctime>
|
||||
|
||||
#include "program.h"
|
||||
|
||||
/// @brief time keyword implementation
|
||||
///
|
||||
void program::RpnTime() {
|
||||
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 format = HH.MMSSssssss
|
||||
date = (static_cast<double>(tm->tm_hour) * 10000000000.0 + static_cast<double>(tm->tm_min) * 100000000.0 +
|
||||
static_cast<double>(tm->tm_sec) * 1000000.0 + static_cast<double>(ts.tv_nsec / 1000));
|
||||
|
||||
// push it
|
||||
// division after push for real precision
|
||||
stack_.push(new Number(date));
|
||||
stack_.value<Number>(0) /= 10000000000.0;
|
||||
} else {
|
||||
ERROR_CONTEXT(kInternalError);
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief date keyword implementation
|
||||
///
|
||||
void program::RpnDate() {
|
||||
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 format = (M)M.DDYYYY
|
||||
date = static_cast<double>(tm->tm_mon + 1) * 1000000.0 + static_cast<double>(tm->tm_mday) * 10000.0 +
|
||||
static_cast<double>(tm->tm_year + 1900);
|
||||
// division after push for real precision
|
||||
stack_.push(new Number(date));
|
||||
stack_.value<Number>(0) /= 1000000.0;
|
||||
} else {
|
||||
ERROR_CONTEXT(kInternalError);
|
||||
}
|
||||
}
|
||||
|
||||
/// @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<double>(ts.tv_sec) + static_cast<double>(ts.tv_nsec / 1000);
|
||||
stack_.push(new Number(date));
|
||||
} else {
|
||||
ERROR_CONTEXT(kInternalError);
|
||||
}
|
||||
}
|
|
@ -1,73 +0,0 @@
|
|||
#include <time.h>
|
||||
#include "program.hpp"
|
||||
|
||||
/// @brief time keyword implementation
|
||||
///
|
||||
void program::rpn_time() {
|
||||
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 != NULL) {
|
||||
// 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));
|
||||
} else
|
||||
ERR_CONTEXT(ret_internal);
|
||||
}
|
||||
|
||||
/// @brief date keyword implementation
|
||||
///
|
||||
void program::rpn_date() {
|
||||
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 != NULL) {
|
||||
// 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));
|
||||
} else
|
||||
ERR_CONTEXT(ret_internal);
|
||||
}
|
||||
|
||||
/// @brief ticks keyword implementation
|
||||
///
|
||||
void program::rpn_ticks() {
|
||||
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 != NULL) {
|
||||
// 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));
|
||||
} else
|
||||
ERR_CONTEXT(ret_internal);
|
||||
}
|
99
src/rpn-trig.cc
Normal file
99
src/rpn-trig.cc
Normal file
|
@ -0,0 +1,99 @@
|
|||
// Copyright (c) 2014-2022 Louis Rubet
|
||||
|
||||
#include "program.h"
|
||||
|
||||
/// @brief pi keyword implementation
|
||||
///
|
||||
void program::RpnPi(void) {
|
||||
stack_.push_front(new Number(mpfr::const_pi()));
|
||||
}
|
||||
|
||||
/// @brief d->r keyword implementation
|
||||
///
|
||||
void program::RpnD2r(void) {
|
||||
MIN_ARGUMENTS(1);
|
||||
ARG_MUST_BE_OF_TYPE(0, kNumber);
|
||||
stack_.value<Number>(0) *= mpfr::const_pi();
|
||||
stack_.value<Number>(0) /= 180;
|
||||
}
|
||||
|
||||
/// @brief r->d keyword implementation
|
||||
///
|
||||
void program::RpnR2d(void) {
|
||||
MIN_ARGUMENTS(1);
|
||||
ARG_MUST_BE_OF_TYPE(0, kNumber);
|
||||
stack_.value<Number>(0) /= mpfr::const_pi();
|
||||
stack_.value<Number>(0) *= 180;
|
||||
}
|
||||
|
||||
/// @brief sin keyword implementation
|
||||
///
|
||||
void program::RpnSin(void) {
|
||||
MIN_ARGUMENTS(1);
|
||||
if (stack_.type(0) == kNumber)
|
||||
stack_.value<Number>(0) = sin(stack_.value<Number>(0));
|
||||
else if (stack_.type(0) == kComplex)
|
||||
stack_.value<Complex>(0) = sin(stack_.value<Complex>(0));
|
||||
else
|
||||
ERROR_CONTEXT(kBadOperandType);
|
||||
}
|
||||
|
||||
/// @brief asin keyword implementation
|
||||
///
|
||||
void program::RpnAsin(void) {
|
||||
MIN_ARGUMENTS(1);
|
||||
if (stack_.type(0) == kNumber)
|
||||
stack_.value<Number>(0) = asin(stack_.value<Number>(0));
|
||||
else if (stack_.type(0) == kComplex)
|
||||
stack_.value<Complex>(0) = asin(stack_.value<Complex>(0));
|
||||
else
|
||||
ERROR_CONTEXT(kBadOperandType);
|
||||
}
|
||||
|
||||
/// @brief cos keyword implementation
|
||||
///
|
||||
void program::RpnCos(void) {
|
||||
MIN_ARGUMENTS(1);
|
||||
if (stack_.type(0) == kNumber)
|
||||
stack_.value<Number>(0) = cos(stack_.value<Number>(0));
|
||||
else if (stack_.type(0) == kComplex)
|
||||
stack_.value<Complex>(0) = cos(stack_.value<Complex>(0));
|
||||
else
|
||||
ERROR_CONTEXT(kBadOperandType);
|
||||
}
|
||||
|
||||
/// @brief acos keyword implementation
|
||||
///
|
||||
void program::RpnAcos(void) {
|
||||
MIN_ARGUMENTS(1);
|
||||
if (stack_.type(0) == kNumber)
|
||||
stack_.value<Number>(0) = acos(stack_.value<Number>(0));
|
||||
else if (stack_.type(0) == kComplex)
|
||||
stack_.value<Complex>(0) = acos(stack_.value<Complex>(0));
|
||||
else
|
||||
ERROR_CONTEXT(kBadOperandType);
|
||||
}
|
||||
|
||||
/// @brief tan keyword implementation
|
||||
///
|
||||
void program::RpnTan(void) {
|
||||
MIN_ARGUMENTS(1);
|
||||
if (stack_.type(0) == kNumber)
|
||||
stack_.value<Number>(0) = tan(stack_.value<Number>(0));
|
||||
else if (stack_.type(0) == kComplex)
|
||||
stack_.value<Complex>(0) = tan(stack_.value<Complex>(0));
|
||||
else
|
||||
ERROR_CONTEXT(kBadOperandType);
|
||||
}
|
||||
|
||||
/// @brief atan keyword implementation
|
||||
///
|
||||
void program::RpnAtan(void) {
|
||||
MIN_ARGUMENTS(1);
|
||||
if (stack_.type(0) == kNumber)
|
||||
stack_.value<Number>(0) = atan(stack_.value<Number>(0));
|
||||
else if (stack_.type(0) == kComplex)
|
||||
stack_.value<Complex>(0) = atan(stack_.value<Complex>(0));
|
||||
else
|
||||
ERROR_CONTEXT(kBadOperandType);
|
||||
}
|
249
src/rpn-trig.cpp
249
src/rpn-trig.cpp
|
@ -1,249 +0,0 @@
|
|||
#include "program.hpp"
|
||||
|
||||
/// @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));
|
||||
}
|
||||
|
||||
/// @brief d->r keyword implementation
|
||||
///
|
||||
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));
|
||||
}
|
||||
|
||||
/// @brief r->d keyword implementation
|
||||
///
|
||||
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));
|
||||
}
|
||||
|
||||
/// @brief sin keyword implementation
|
||||
///
|
||||
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);
|
||||
}
|
||||
|
||||
/// @brief asin keyword implementation
|
||||
///
|
||||
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);
|
||||
}
|
||||
|
||||
/// @brief cos keyword implementation
|
||||
///
|
||||
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);
|
||||
}
|
||||
|
||||
/// @brief acos keyword implementation
|
||||
///
|
||||
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);
|
||||
}
|
||||
|
||||
/// @brief tan keyword implementation
|
||||
///
|
||||
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);
|
||||
}
|
||||
|
||||
/// @brief atan keyword implementation
|
||||
///
|
||||
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);
|
||||
}
|
77
src/stack.h
Normal file
77
src/stack.h
Normal file
|
@ -0,0 +1,77 @@
|
|||
// Copyright (c) 2014-2022 Louis Rubet
|
||||
|
||||
#ifndef SRC_STACK_HPP_
|
||||
#define SRC_STACK_HPP_
|
||||
|
||||
#include <deque>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
using std::deque, std::map, std::string;
|
||||
|
||||
#include "object.h"
|
||||
|
||||
/// @brief stack object, parens of program, storing execution stack values or programs
|
||||
///
|
||||
class rpnstack : public deque<Object*> {
|
||||
public:
|
||||
rpnstack() {}
|
||||
virtual ~rpnstack() {
|
||||
for_each(begin(), end(), [](Object* o) { delete o; });
|
||||
deque::erase(begin(), end());
|
||||
}
|
||||
|
||||
// stack manipulation
|
||||
void erase(size_t first = 0, size_t nb = 1, bool del = true) {
|
||||
size_t last = std::min(first + nb, size());
|
||||
if (del) for_each(begin() + first, begin() + last, [](Object* o) { delete o; });
|
||||
deque::erase(begin() + first, begin() + last);
|
||||
}
|
||||
|
||||
void pop() { erase(); }
|
||||
|
||||
// access helpers
|
||||
//
|
||||
ObjectType type(int level) {
|
||||
// carefull: caller must ensure that level is correct
|
||||
return at(level)->_type;
|
||||
}
|
||||
|
||||
template <class objectType>
|
||||
auto& obj(int level) {
|
||||
// carefull: caller must ensure that level is correct
|
||||
return static_cast<objectType&>(*at(level));
|
||||
}
|
||||
|
||||
template <class objectType>
|
||||
auto& value(int level) {
|
||||
// carefull: caller must ensure that level is correct
|
||||
return static_cast<objectType*>(at(level))->value;
|
||||
}
|
||||
|
||||
void push(Object* o) { deque<Object*>::push_front(o); }
|
||||
};
|
||||
|
||||
/// @brief heap object, storing variables (=named object)
|
||||
///
|
||||
class heap : public map<string, Object*> {
|
||||
public:
|
||||
heap() {}
|
||||
virtual ~heap() { clear(); }
|
||||
|
||||
void clear() {
|
||||
for_each(begin(), end(), [](auto it) { delete it.second; });
|
||||
map::erase(begin(), end());
|
||||
}
|
||||
|
||||
bool get(const string name, Object*& obj) {
|
||||
auto i = find(name);
|
||||
if (i != end()) {
|
||||
obj = i->second;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
#endif // SRC_STACK_HPP_
|
384
src/stack.hpp
384
src/stack.hpp
|
@ -1,384 +0,0 @@
|
|||
#ifndef __stack_h__
|
||||
#define __stack_h__
|
||||
|
||||
#include <string.h>
|
||||
#include <map>
|
||||
using namespace std;
|
||||
|
||||
// allocation base size
|
||||
#define ALLOC_STACK_CHUNK (64 * 1024)
|
||||
|
||||
/// @brief stack object, parens of program, storing execution stack values or programs
|
||||
///
|
||||
class stack {
|
||||
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;
|
||||
}
|
||||
|
||||
/// @brief copy a whole stack entry and push it back to another stack
|
||||
///
|
||||
/// @param from copy from
|
||||
/// @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();
|
||||
}
|
||||
|
||||
/// @brief copy a whole stack entry and push it back to another stack
|
||||
///
|
||||
/// @param from copy from
|
||||
/// @param index_from index t ocopy from
|
||||
/// @param to copy to
|
||||
///
|
||||
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();
|
||||
}
|
||||
|
||||
/// @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
|
||||
///
|
||||
/// @param size the object size in bytes
|
||||
/// @param type the object type
|
||||
/// @return object* the allocated object
|
||||
///
|
||||
object* allocate_back(unsigned int size, cmd_type_t type) {
|
||||
object* allocated;
|
||||
bool data_is_reallocated = false;
|
||||
char* old_base;
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/// @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;
|
||||
}
|
||||
|
||||
/// @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
|
||||
};
|
||||
|
||||
/// @brief heap object, storing variables (=named object)
|
||||
///
|
||||
class heap : public stack {
|
||||
public:
|
||||
heap() {}
|
||||
virtual ~heap() {}
|
||||
|
||||
/// @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;
|
||||
}
|
||||
|
||||
/// @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 ret = false;
|
||||
map<string, unsigned int>::iterator i = _map.find(name);
|
||||
|
||||
if (i != _map.end()) {
|
||||
obj = seq_obj(i->second);
|
||||
size = obj->_size;
|
||||
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()) {
|
||||
object* local;
|
||||
map<string, unsigned int>::iterator i = _map.begin();
|
||||
|
||||
for (int j = 0; j < num; j++) i++;
|
||||
|
||||
local = (object*)seq_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__
|
|
@ -1,22 +1,8 @@
|
|||
// 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";
|
||||
// Copyright (c) 2014-2022 Louis Rubet
|
||||
|
||||
#define STRINGIFY(a) STRINGIFY_ONE(a)
|
||||
#define STRINGIFY_ONE(a) #a
|
||||
#ifndef SRC_VERSION_H_
|
||||
#define SRC_VERSION_H_
|
||||
|
||||
// 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";
|
||||
#define RPN_VERSION GIT_VERSION // set by cmake from git tag
|
||||
|
||||
// syntax
|
||||
static const char syntax[] = ATTR_BOLD "Syntax" ATTR_OFF
|
||||
": rpn [command]\n"
|
||||
"with optional command = list of commands";
|
||||
#endif // SRC_VERSION_H_
|
||||
|
|
47
test/005-test-framework.md
Normal file
47
test/005-test-framework.md
Normal 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
|
|
@ -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
75
test/010-mode.md
Normal 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`
|
|
@ -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
59
test/020-general.md
Normal 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
101
test/021-parse-string.md
Normal 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
107
test/022-parse-symbol.md
Normal 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`
|
169
test/023-parse-number.md
Normal file
169
test/023-parse-number.md
Normal file
|
@ -0,0 +1,169 @@
|
|||
# PARSE NUMBER
|
||||
|
||||
`default del`
|
||||
|
||||
## type
|
||||
|
||||
`1.0 type`
|
||||
|
||||
-> stack should be "number"
|
||||
|
||||
`del`
|
||||
|
||||
## 1-shot entries
|
||||
|
||||
```
|
||||
0
|
||||
0.
|
||||
.0
|
||||
0.0
|
||||
-0
|
||||
+0
|
||||
+.0
|
||||
+0.
|
||||
0.1
|
||||
+0.1
|
||||
-0.1
|
||||
-.1
|
||||
+.1
|
||||
```
|
||||
|
||||
-> stack should be 0, 0, 0, 0, -0, 0, 0, 0, 0.1, 0.1, -0.1, -0.1, 0.1
|
||||
|
||||
`del`
|
||||
|
||||
## n-shot entries
|
||||
|
||||
```
|
||||
0 0. .0 0.0 -0 +0 +.0 +0. 0.1 +0.1 -0.1 -.1 +.1
|
||||
```
|
||||
|
||||
-> stack should be 0, 0, 0, 0, -0, 0, 0, 0, 0.1, 0.1, -0.1, -0.1, 0.1
|
||||
|
||||
`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
63
test/024-parse-complex.md
Normal 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
27
test/025-parse-other.md
Normal 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
86
test/026-parse-program.md
Normal 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
365
test/027-base-entry.md
Normal 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)
|
|
@ -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
709
test/030-branch.md
Normal 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`
|
|
@ -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
289
test/040-stack.md
Normal 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`
|
470
test/05-real.txt
470
test/05-real.txt
|
@ -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
|
780
test/050-real.md
Normal file
780
test/050-real.md
Normal file
|
@ -0,0 +1,780 @@
|
|||
# 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 hex (3)
|
||||
|
||||
`0x12.345 4 fix`
|
||||
|
||||
-> stack should be 0x1.2345p+4
|
||||
|
||||
`4 sci`
|
||||
|
||||
-> stack should be 0x1.2345p+4
|
||||
|
||||
`2 fix`
|
||||
|
||||
-> stack should be 0x1.23p+4
|
||||
|
||||
`8 fix`
|
||||
|
||||
-> stack should be 0x1.23450000p+4
|
||||
|
||||
`del default`
|
||||
|
||||
## real hex (4)
|
||||
|
||||
`0x0.0001234`
|
||||
|
||||
-> stack should be 0x0
|
||||
|
||||
`7 fix`
|
||||
|
||||
-> stack should be 0x1.2340000p-16
|
||||
|
||||
`7 sci`
|
||||
|
||||
-> stack should be 0x1.2340000p-16
|
||||
|
||||
`2 fix`
|
||||
|
||||
-> stack should be 0x1.23p-16
|
||||
|
||||
`del default`
|
||||
|
||||
## real binary
|
||||
|
||||
`0b11001100`
|
||||
|
||||
-> stack should be 0b11001100
|
||||
|
||||
`del`
|
||||
|
||||
# real binary (2)
|
||||
|
||||
`0b11.001 4 fix`
|
||||
|
||||
-> stack should be 1.1001p+1
|
||||
|
||||
`4 sci`
|
||||
|
||||
-> stack should be 1.1001p+1
|
||||
|
||||
`2 fix`
|
||||
|
||||
-> stack should be 1.10p+1
|
||||
|
||||
`8 fix`
|
||||
|
||||
-> stack should be 1.10010000p+1
|
||||
|
||||
`del default`
|
||||
|
||||
# real binary (3)
|
||||
|
||||
`0b0.0001101`
|
||||
|
||||
-> stack should be 0b0
|
||||
|
||||
`7 fix`
|
||||
|
||||
-> stack should be 1.1010000p-4
|
||||
|
||||
`7 sci`
|
||||
|
||||
-> stack should be 1.1010000p-4
|
||||
|
||||
`2 fix`
|
||||
|
||||
-> stack should be 1.10p-4
|
||||
|
||||
`del default`
|
||||
|
||||
## 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`
|
|
@ -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
307
test/060-string.md
Normal 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`
|
|
@ -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`
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue