xoseperez / rpnlib

Reverse Polish Notation calculator for ESP8266 & ESP32 microcontrollers.
GNU Lesser General Public License v3.0
11 stars 2 forks source link

'Register'-like variable push & pop #1

Open mcspr opened 4 years ago

mcspr commented 4 years ago

While it is possible to register variable externally via rpn_variable_set(), it is not possible to store some stack variable or retrieve it later.

Possible syntax: 0 pusha -> $ra is now 0 popa -> $ra is now unset and 0 is on the stack peeka -> $a is still set to 0, 0 is on the stack

mcspr commented 4 years ago

Just to log some work-in-progress which I will document some time later (possibly in the readme of the fork, or possibly a PR) https://github.com/xoseperez/rpnlib/compare/master...mcspr:objs

One register implementation is dc(1) from coreutils: https://en.wikipedia.org/wiki/Dc_(computer_program)#Registers

dc provides at least 256 memory registers, each named by a single character. You can store a number or a string in a register and retrieve it later.

sr Pop the value off the top of the stack and store it into register r. lr Copy the value in register r and push it onto the stack. The value 0 is retrieved if the register is uninitialized. This does not alter the contents of r.

Each register also contains its own stack. The current register value is the top of the register's stack.

Which is a nice and all, but now we have two storage places for almost the same thing. Instead, since we already have variable concept, I slightly reworked stack variable and helpers to allow to store arbitrary (within some made-up limits) value and allow to back-reference variables.

struct rpn_value is replacing raw float as value, implementing bare-bones variant type through union { int32_t, uint32_t, double, char* }. Now it is a null-able type (idk, maybe there is some other way to do this without null). struct rpn_variable stores a reference to the rpn_value struct rpn_stack_value always stores rpn_value and optionally rpn_variable. This allows us to 'notice' variable use, and now it is possible to implement operators that can consume value and know that it came from a variable.

See runner.cpp for an example, rpnlib_operators.cpp now has p operator for _rpn_debug function and = operator to assign variable some value from the stack:

> 15 p
15.000000
> $var p
null
> "hello world" $var = $var p
"hello world"

Code is mvp, runner binary is the only way I have tested it. There are printfs hidden in the rpnlib somewhere and the core implementation uses std::string instead of String since I haven't added testing mock here.

Another possible approach is to have separate stacks for each type and force operator to choose which one it wants. This may help avoid union / variant type of stuff.

mcspr commented 4 years ago

This turned out in a bit more changes than I originally though. Which are almost done, published this as 0.4.0-pre1 at https://github.com/mcspr/rpnlib (as idk whether to expect PR review here, given the rewrite's size)

Main things that I wanted:

I though about adding unsigned to support bit-wise stuff, but not really sure if it is really needed.

There are some subtle bugs (like "1 2 3 4 is parsed as string 1 2 3 4), still, but mostly things are working. I haven't done any proper real device tests yet, but I've built a small local testing script through CMakeLists.txt and all piotest tests pass. It is not pio test, as the test framework is closely bound to the Core version and we can't exactly have an exported library (at least not yet). At this point I will look into improving test coverage of operators and parsing. When building:

$ cmake ../ -DESP8266=~/.platformio/packages/frameworkd-arduinoespressif8266 -DUNITY=~/.platformio/packages/tool-unity
$ cmake --build .

./runner will call simple repl. this is wip, as i still need to tweak some debugging output ./piotest will call the binary produced from the test/piotest/main.cpp built with the ESP8266 Core testing framework.