Commit graph

29 commits

Author SHA1 Message Date
e5c4f32126 refactor 2023-11-24 21:57:57 +01:00
8d8fcdd64b Remove duplicated declaration 2023-11-23 21:34:10 +01:00
396c5ad866 gc: Tighter coupling to full context
We'll soon need the whole apfl_ctx in the garbage collector.
2023-11-23 21:30:51 +01:00
63d64b0778 Add regex module "re"
This uses the PCRE2 library to implement regexes in apfl
2023-07-03 23:53:09 +02:00
c07caa9aa2 Implement the registry and a agrv getter to test it
The registry is a global dictionary accessible from C that stores arbitrary
apfl values.
2023-03-31 21:31:07 +02:00
6056e3a450 Implement pairs
Pairs are 2-tuples of values that are constructed and matched with the `::`
operator. They can also be matched with a `:` operator, the LHS is an
expression then, the pair will then only match, if the LHS matches the
result of that expression.

Pairs should be useful to do something similar what sum types / tagged
unions do in statically typed languages, e.g. you could write something
like:

    some := (symbol) # Somthing that creates a unique value
    filter-map := {
      _ [] -> []
      f [x ~xs] ->
        {
          some:y -> [y ~(filter-map f xs)]
          nil -> filter-map f xs
        } (f x)
    }
    filter-map {
      x?even -> some :: (* x 10)
      _ -> nil
    } some-list
2023-03-22 23:54:03 +01:00
2666c0f148 Implement bytecode (de)serialization 2023-03-07 21:40:07 +01:00
a4f7f0884d Make line and column size_t 2023-02-16 21:41:02 +01:00
f4841ff2cd Use stdnoreturn instead of our own APFL_NORETURN 2023-02-16 20:52:27 +01:00
55c95f99ad Rename format => io_writer
To make it clearer that this can be used for writing binary data too.
2023-02-10 21:48:31 +01:00
3f6e1f14a9 Implement loading other scripts 2023-01-31 22:07:28 +01:00
bd19f689b9 Add backtraces to errors 2023-01-28 21:44:56 +01:00
3cc2a83226 Add functions to get information about the call stack 2023-01-28 21:42:40 +01:00
dd580a1519 Improve a bunch of error messages 2023-01-24 21:30:05 +01:00
874638748b Make iterative runner not panic on OOM
The iterative runner used functions that could throw errors on OOM before
protecting itself using apfl_call_protected. An error at that point would
have resulted in calling the panic handler as a last resort.

This now introduces the apfl_do_protected function which allows running C
code protected without needing to wrap it in a cfunc value, thus removing
the need for the functions that could throw in the iterative runner.
2022-11-20 21:44:00 +01:00
3bbd0e79a1 Implement predicates
This implements the last remaining feature of the language syntax! :)

The implementation of this was delightfully simple, since all the heavy
lifting is done by already implemented functions.
2022-11-19 23:26:59 +01:00
20c2880f4c Implement assigning into dictionaries
It's now possible to assign to a key of a dictionary and even to a nested
key path.

This patch changes the way matchers work a bit:
First, a function call stack frame now has a stack of matchers that are
manipulateable instead of a single matcher.
Second, the matcher is now in charge of setting the matched values to the
variables (previously the caller of the matcher needed to extract the
matched values and assign them itself). This change simplifies code
generation, especially for chained assignments and dictionary key paths.

This removes the last usage of APFL_ERR_NOT_IMPLEMENTED :)
2022-11-19 23:26:52 +01:00
0267faede3 Rename matcher_stack -> matcher_state_stack
It's not a stack of matchers, a thing we'll soon need. So let's rename
this.
2022-11-08 22:03:05 +01:00
fa4ed7bf83 Abstract away stdout
Instead of using stdout directly, we're now using a configurable
apfl_format_writer.
2022-10-30 21:21:40 +01:00
8ca24bcd49 Implement subfunctions and argument matching
A function can now have multiple subfunctions with their own parameter
list. These parameters are now no longer constrained to variables and
blanks only, but can also be consts and list destructurings (predicates
are also already compiled but don't get evaluated yet). The first
subfunction that matches the argument list gets evaluated.
2022-08-13 00:50:26 +02:00
1630314dc7 Implement matching assignments
This allows the destructuring of lists into individual values.
We can have arbitrarily nested lists, can check for constant values and can
have up to one '~'-prefixed variable per list, that will capture the
remaining elements of the list.

It is implemented as a second set of bytecode instructions, which define a
matcher. These matchers should also enable us to implement the same pattern
matching capabiities for function parameters.

Not all matching features are implemented yet, predicate matching and
matching into a dictionary key is not implemented yet.
2022-07-28 20:46:32 +02:00
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
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
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
9ce5c20736 No longer evaluate exprs directly 2022-04-21 21:17:17 +02:00
b3322c93e9 Move scope functions/structs out of context.[ch] 2022-04-15 14:41:22 +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