Commit graph

111 commits

Author SHA1 Message Date
4f9a33628c Implement first global function "print"
This introduces the globals and moves some previously non-public functions
into apfl.h.
2022-07-12 22:13:07 +02:00
a4f7f0f2ff Implement functions and function calls
We can now define and call functions. Lexical closure scopes are also
working :).

It's limited to simple functions or complex functions with a single
argument list of only variable names for now.
2022-07-11 21:41:05 +02:00
eb7cb0baf7 gc+context: Make stack no longer a gc object
The new roots callback mechanism makes it possible to traverse the gc
objects on the stack without needing the stack to be an gc object.

This also uncovered a nasty bug in apfl_stack_pop where we passed in a
wrong mem pointer into apfl_resizable_splice. We should probably find a way
to make the apfl_resizable_* functions a bit safer, the need for casting
stuff to void** easily hides errors.
2022-07-01 22:40:43 +02:00
1a23c63ec6 gc+bytecode: Fully visit all instruction_list children
We accidentally stopped at the first child before m(
2022-07-01 22:03:52 +02:00
c974b675e1 gc: Use a callback to get the (non-tmp) roots
This let's us get rid of that awkward hashmap in the GC that was used as a
set, makes determining the roots more flexible and now gc_init can't even
fail any more, as there are no allocations happening here any more :)
2022-07-01 22:00:58 +02:00
8d947244c9 Implement exceptions-like error handling
Most functzions will no longer return an enum apfl_result, but will raise
an error that bubbles up.
2022-06-26 16:06:14 +02:00
4b9170969b compile.c: Extract common ilist put code into macro 2022-06-17 20:22:05 +02:00
2c04e57a0c Implement a simple web playground REPL with emscripten 2022-06-05 22:31:02 +02:00
64f2be5afc Move variable out of switch
This doesn't complie with emcc
2022-06-05 22:30:18 +02:00
09f7df79bb apfl_debug_print_val: Use format writer abstraction 2022-06-05 22:06:33 +02:00
a773e929bf Fix checking type of wrong value
We already checked dst_val, we need to check src_val too!
2022-04-24 16:45:25 +02:00
ad141a86fb Push some errors as const strings onto the stack 2022-04-24 16:13:39 +02:00
24c283c85f compile: Use apfl_string_move_into_new_gc_string 2022-04-23 22:46:27 +02:00
bb89b2ae49 Add constant string values 2022-04-23 22:15:10 +02:00
0de243995b Ditch that weird internal.h header 2022-04-22 23:17:28 +02:00
d1b2ba7a53 Use formatter for printing values 2022-04-22 23:13:01 +02:00
a4fd3acfac Use formatter for expr printing 2022-04-22 22:52:53 +02:00
b4a7dfe1f6 Move allocation macros from internal.h to alloc.h 2022-04-22 21:43:19 +02:00
29d9c75c4e Add functions for creating nil/bool/number/string values 2022-04-22 21:39:03 +02:00
7c3a760ebf gitignore: Ignore make dist/distcheck generated files 2022-04-21 22:57:22 +02:00
0f6f136873 Push formatted parser error on stack during evaluation
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 :)
2022-04-21 22:55:11 +02:00
f8bab311d7 Add formatter abstraction for errors
This will allow us to format errors as strings later by implementing a
different format writer.
2022-04-21 22:14:37 +02:00
9ce5c20736 No longer evaluate exprs directly 2022-04-21 21:17:17 +02:00
b7c88635d9 Redo source reader interface
The callback and the opaque data are now grouped together in a struct
instead of being passed individually into the tokenizer.

This also exposes the string source reader struct and therefore removes
the need of heap allocating it. Neat!
2022-04-15 22:35:36 +02:00
70c3d6e3e4 README: Fix spelling mistakes 2022-04-15 17:13:30 +02:00
82a9858902 apfl_value_hash: Return directly instead of "goto ok" 2022-04-15 14:49:28 +02:00
b3322c93e9 Move scope functions/structs out of context.[ch] 2022-04-15 14:41:22 +02:00
6add5e47b9 hashmap: Improve cursor / peek API
- peek functions will now return pointers directly
- cursor access methods can now return an error, if the cursor is already
  at the end

Also rewrote some cursor loops to use the HASHMAP_EACH macro.
2022-04-11 23:31:20 +02:00
f26759b3f0 expr: Remove refcount in body elements 2022-04-11 22:44:04 +02:00
90a80152e1 Implement mark&sweep garbage collection and bytecode compilation
Instead of the previous refcount base garbage collection, we're now using
a basic tri-color mark&sweep collector. This is done to support cyclical
value relationships in the future (functions can form cycles, all values
implemented up to this point can not).

The collector maintains a set of roots and a set of objects (grouped into
blocks). The GC enabled objects are no longer allocated manually, but will
be allocated by the GC. The GC also wraps an allocator, this way the GC
knows, if we ran out of memory and will try to get out of this situation by
performing a full collection cycle.

The tri-color abstraction was chosen for two reasons:

- We don't have to maintain a list of objects that need to be marked, we
  can simply grab the next grey one.
- It should allow us to later implement incremental collection (right now
  we only do a stop-the-world collection).

This also switches to a bytecode based evaluation of the code: We no longer
directly evaluate the AST, but first compile it into a series of
instructions, that are evaluated in a separate step. This was done in
preparation for inplementing functions: We only need to turn a function
body into instructions instead of evaluating the node again with each call
of the function. Also, since an instruction list is implemented as a GC
object, this then removes manual memory management of the function body and
it's child nodes. Since the GC and the bytecode go hand in hand, this was
done in one (giant) commit.

As a downside, we've now lost the ability do do list matching on
assignments. I've already started to work on implementing this in the new
architecture, but left it out of this commit, as it's already quite a large
commit :)
2022-04-11 22:24:22 +02:00
4088fdd1c3 Fix typo 2022-02-25 21:54:27 +01:00
bb464d8226 Hashmap: Implement prepared setting
With a prepared set, the potential allocation, which can fail, is split
from the actual setting, which can not fail.
2022-02-25 20:50:43 +01:00
40320aaa4a Fix copying dicts
We didn't properly copy the opaque value before (a heap-allocated allocator
pointer), resulting in a use-after-free of the opaque on the copied map if
the old map was destroyed.
2022-02-15 22:32:15 +01:00
8df0991415 eval.c: Don't create copy of already refcounted string 2022-02-14 21:56:36 +01:00
5f8462fe34 Add apfl_stackidx typedef
This makes it clear that a value of that type is meant as an index into the
stack.
2022-02-10 22:39:39 +01:00
09c6459565 Fix typo 2022-02-10 22:04:48 +01:00
d86c9375a4 Shorter name for internal enum apfl_value_type 2022-02-10 21:56:48 +01:00
dd314c5abb Shorter name for internal enum get_item_result 2022-02-10 21:56:30 +01:00
78bd057c37 eval.c: Free the stack with FREE_LIST instead of FREE_OBJ
This way we pass the correct oldsize to the allocator.
2022-02-09 20:44:21 +01:00
ebf3fc89ff Introduce allocator abstraction
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.
2022-02-08 22:53:13 +01:00
ad80ff8f85 Remove dead code 2022-02-08 21:42:54 +01:00
db0fb2aee4 expr: Make body objects refcounted
We'll soon need this, once we implement function definitions
2022-01-22 17:16:28 +01:00
0ab36ec37e hashmap: Add test cases
Also get rid of the silly hashmap_foo demo program.
2022-01-22 15:57:10 +01:00
d539b920c9 test.h: Don't force tests to use must_alloc 2022-01-22 15:56:19 +01:00
2966d6dc7c hashmap: Don't hide hashmap struct behind a pointer any more
Since the hashmap is meant for internal use only, there is no need to hide
the internal data. This also allows us to save some malloc() calls.
2022-01-22 15:55:56 +01:00
afd5d8e1d2 Makefile.am: Add missing internal header 2022-01-22 15:48:17 +01:00
21efc85dba Convert to Lua-style stack API
Only for evaluating expressions for now and right now the only exposed
operation is to debug print a value on the stack, this obviously needs to
be expanded.

I've done this for two reasons:

1. A Lua-style stack API is much nicer to work with than to manually manage
   refcounts.
2. We'll soon need a more sophisticated garbage collector (if you even want
   to count the refcounting as garbage collection). For this, the GC will
   need root objects to start tracing for live objects, the stack will be
   one of these roots.
2022-01-20 22:45:09 +01:00
cc79bab69f resizable: Implement splicing 2022-01-20 21:33:04 +01:00
d81bef9184 parser/tokenizer: Save textual data as refcounted strings
This avoids creating refcounted strings during evaluation and makes it
easier to use the same parsed string in multiple places (should be
useful once we implement functions).
2022-01-18 21:18:27 +01:00
c9a935b161 Support assigning into dictionaries
You can now set keys in dictionaries to a value. If a key in a key path is
missing, we automatically create an empty dictionary. Otherwise setting
deeply nested keys becomes annoying.
2022-01-15 23:09:24 +01:00