This way we can see the parse errors again in evaluation mode
Not fully fleshed out yet: We simply use apfl_debug_print_val to dump the
top of the stack (the formatted error) to stderr but don't nicely handle
if there is nothing on the stack (apfl_debug_print_val will print a rather
cryptic "stack index invalid" error). Also the whole dance we need to do to
put the formatted error onto the stack feels rather awkward (there should
probably a function for this) and we also should probably try to push an
error description on the stack in case this moving-string-to-stack business
fails.
Now "only" all other errors need to be put on the stack as a string :)
We now no longer call malloc/free/... directly, but use an allocator object
that is passed around.
This was mainly done as a preparation for a garbage collector: The
collector will need to know, how much memory we're using, introducing the
collector abstraction will allow the GC to hook into the memory allocation
and observe the memory usage.
This has other potential applications:
- We could now be embedded into applications that can't use the libc
allocator.
- There could be an allocator that limits the total amount of used memory,
e.g. for sandboxing purposes.
- In our tests we could use this to simulate out of memory conditions
(implement an allocator that fails at the n-th allocation, increase n by
one and restart the test until there are no more faked OOM conditions).
The function signature of the allocator is basically exactly the same as
the one Lua uses.
This is analogous to dictionaries and ensures that no circular references
can be created when using the exported API in apfl.h.
This also changes apfl_value_copy into apfl_value_incref to better reflect
what it does and to reflect that it is no longer an operation that can
fail.
This avoids copying the string every time we pass it around. Not too
important right now, but will become important onve we're able to evaluate
more complex expressions.