Open mcspr opened 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.
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:
true
and false
, simplify casts in operators"hello" " world" +
-> "hello world"
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.
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 0popa
-> $ra is now unset and 0 is on the stackpeeka
-> $a is still set to 0, 0 is on the stack