Compare commits

21 Commits
gc ... master

Author SHA1 Message Date
06f17f491c Move variable setting code into utilities. 2018-09-13 23:57:33 -07:00
ca6075e8d5 Add a new method call operator, and more.
The '.' operator now represents method calls. A function f: (a, b)->c
can be called as a.f(b), which is equivalent to f(a, b). Besides
this change, all reserved operators now have a negative precedence
(it's relative, remember?), and some function names were changed.
2018-09-13 17:05:39 -07:00
4425b27b52 Make overloading code a utility function and call from interpreter.
This fixes #10.
2018-09-13 16:09:04 -07:00
25dd70f040 Fix segmentation fault during type initialization. 2018-09-13 15:08:57 -07:00
5617484aff Make parsing function public.
libab already provides a _run_tree ... you can't run_tree if there's
no tree.
2018-09-12 14:43:43 -07:00
b311c854ee Fix segmentation fault on function parsing error. 2018-09-12 14:32:28 -07:00
c4a7117704 Ensure operator does not try to take ownership of string. 2018-08-22 17:45:45 -07:00
899ac31210 Add a README. 2018-08-20 00:00:02 -07:00
01720914e0 Fix bug in table lookups. 2018-08-18 17:30:33 -07:00
8c9acafc93 Fix bug in shunting yard implementation. 2018-08-17 19:10:48 -07:00
37593abe40 Fix GC bug in overloaded functions. 2018-08-14 18:51:00 -07:00
0065fe5e65 Add utility functions to create libab instances on the heap. 2018-08-12 00:46:37 -07:00
de0ad13785 Remove function call prints from interactive. 2018-08-11 22:42:36 -07:00
3d0e4776fc Fix bugs related to partial application types. 2018-08-11 22:26:11 -07:00
3fc7f46680 Increment tree refcount. 2018-08-11 21:19:02 -07:00
cf57c4a29a Remove deliberate memory leak.
It was used to test the GC.
2018-08-11 20:29:08 -07:00
59b03d0a94 Fix partial application memory leak. 2018-08-11 20:27:52 -07:00
ffcbab9d94 Remove unused files. 2018-08-11 20:11:32 -07:00
8847643c2e Merge branch 'gc' 2018-08-11 20:09:56 -07:00
0b7b49d03d Fix memory leak caused by not freeing type. 2018-08-11 00:42:15 -07:00
512d68000f Link math library for atan2 and atan 2018-08-11 00:41:39 -07:00
17 changed files with 388 additions and 156 deletions

74
README.md Normal file
View File

@@ -0,0 +1,74 @@
# libabacus
A math-centered programming language library.
## About
libabacus is effectively a stripped down, embeddable programming language.
It relies on the program it's embedded into to provide the standard library. In fact,
it doesn't even provide an underlying implementation for numbers - the client code
provides a means of convering a string into a number, and a way of freeing that value.
libabacus takes care of the rest. The features of libabacus are geared towards
calculators, so it does not provide higher-level abstractions like OOP.
## Syntax
libabacus has fairly simple syntax. A simple number expression is
```
42
```
If an operator `+` is registered with libabacus, then an expression using
that operator looks like
```
21+21
```
If two functions, `f` and `g` are registered with libabacus, where `f` takes
one argument and `g` takes two, then the syntax for calling these functions is
```
f(42)
g(21, 21)
```
Blocks are a way to combine a sequence of expressions into one. The value of a block
is the result of the last expression in that block. Expressions are delimited by the
`;` character. For example,
```
{ 3; 2; 1 }
```
evalutes to `1`. Blocks are the first bit of syntax we've seen that can evalute to
a value that isn't a number. For instance, the empty block, `{}`, does not
evaluate to a number. Instead, it evalutes to `()`. This is a value of the `unit` type,
which only has one possible value, `()`.
User-defined functions are supported by libabacus:
```
fun square(a: num): num { a*a }
```
Let's pick this apart. The first part of a function declaration is the `fun` keyword. The name
of the function is next, followed by the list of parameters that this function accepts. Parameters
are listed in the form `[name]:[type]`. The return type of the function must also be specified.
Lastly, the block used to evaluate the function is needed.
The block captures the scope in which the function was declared. For instance, in the following piece of code,
```
a = 0;
fun test(): num {
a = a + 1
}
```
`test` actually has access to `a`, and calling `test` many times will yield increasing values of a.
Although all parameters must specify a type, this type can be
arbitrary to allow for polymorphic functions. For instance, a function to apply another function to an argument
twice can be written as:
```
fun apply_twice(func: ('T)->'T, a: 'T): 'T {
func(a);
func(a)
}
```
## Integration
Everything in libabacus revolves around the `libab` struct. This struct is used to keep all the state related to the evaluation,
and also contains the garbage collector. It needs to be instantiated using the `libab_init` function.
```C
libab ab;
libab_init(&ab, parse_function, free_function);
```
Please see `interactive.c` for an example of a simple yet complete implementation.

View File

@@ -135,8 +135,9 @@ void libab_behavior_free(libab_behavior* behavior);
* @param associativity the associativity (left = -1, right = 1) of the * @param associativity the associativity (left = -1, right = 1) of the
* operator. * operator.
* @param function the function this operator represents. * @param function the function this operator represents.
* @result the result of the initialization.
*/ */
void libab_operator_init(libab_operator* op, libab_operator_variant variant, libab_result libab_operator_init(libab_operator* op, libab_operator_variant variant,
int precedence, int associativity, const char* function); int precedence, int associativity, const char* function);
/** /**
* Frees the given operator. * Frees the given operator.

View File

@@ -70,11 +70,25 @@ libab_result libab_interpreter_run(libab_interpreter* intr, libab_tree* tree,
* @param into the reference to store the result into. * @param into the reference to store the result into.
* @return the result of the call. * @return the result of the call.
*/ */
libab_result libab_interpreter_run_function(libab_interpreter* intr, libab_result libab_interpreter_call_function(libab_interpreter* intr,
libab_ref* scope, libab_ref* scope,
const char* function, const char* function,
libab_ref_vec* params, libab_ref_vec* params,
libab_ref* into); libab_ref* into);
/**
* Calls a function value with the given parameters.
* @param intr the interpreter to use to call the function.
* @param scope the scope in which the function should be searched for.
* @param function the function to call.
* @param params the parameters to pass to the function.
* @param into the reference to store the result into.
* @return the result of the call.
*/
libab_result libab_interpreter_call_value(libab_interpreter* intr,
libab_ref* scope,
libab_ref* function,
libab_ref_vec* params,
libab_ref* into);
/** /**
* Gets the unit value from this interpreter. * Gets the unit value from this interpreter.
* @param intr the interpreter from which to get the unit value. * @param intr the interpreter from which to get the unit value.

View File

@@ -232,6 +232,13 @@ void libab_get_false_value(libab* ab, libab_ref* into);
*/ */
void libab_get_bool_value(libab* ab, int val, libab_ref* into); void libab_get_bool_value(libab* ab, int val, libab_ref* into);
/**
* Parses the given piece of code using the given libabacus instance.
* @param ab the instance to use to parse the code.
* @param string the source code to parse.
* @param into the value to store the newly parsed tree into.
*/
libab_result libab_parse(libab* ab, const char* string, libab_tree** into);
/** /**
* Executes the given string of code. * Executes the given string of code.
* @param ab the libabacus instance to use for executing code. * @param ab the libabacus instance to use for executing code.
@@ -256,7 +263,7 @@ libab_result libab_run_tree(libab* ab, libab_tree* tree, libab_ref* value);
* @param param_count the number of parameters given to this function. * @param param_count the number of parameters given to this function.
* @return the result of the call. * @return the result of the call.
*/ */
libab_result libab_run_function(libab* ab, const char* function, libab_result libab_call_function(libab* ab, const char* function,
libab_ref* into, libab_ref* into,
size_t param_count, ...); size_t param_count, ...);
/** /**
@@ -286,7 +293,7 @@ libab_result libab_run_tree_scoped(libab* ab, libab_tree* tree, libab_ref* scope
* @param param_count the number of parameters given to this function. * @param param_count the number of parameters given to this function.
* @return the result of the call. * @return the result of the call.
*/ */
libab_result libab_run_function_scoped(libab* ab, const char* function, libab_ref* scope, libab_result libab_call_function_scoped(libab* ab, const char* function, libab_ref* scope,
libab_ref* into, libab_ref* into,
size_t param_count, ...); size_t param_count, ...);

View File

@@ -1,8 +0,0 @@
#ifndef LIBABACUS_REFCOUNT_INTERNAL_H
#define LIBABACUS_REFCOUNT_INTERNAL_H
#include "refcount.h"
void libab_ref_count_changed(libab_ref_count* count);
#endif

View File

@@ -103,6 +103,28 @@ libab_result libab_create_value_ref(libab* ab, libab_ref* into,
*/ */
libab_result libab_create_value_raw(libab* ab, libab_ref* into, libab_result libab_create_value_raw(libab* ab, libab_ref* into,
void* data, libab_ref* type); void* data, libab_ref* type);
/**
* Overloads a function of the given name.
* @param table the table to insert the function into.
* @param name the name of the function.
* @param function the function to register.
* @return the result of the overload.
*/
libab_result libab_overload_function(libab* ab,
libab_table* table,
const char* name,
libab_ref* function);
/**
* Sets a value under the given name, overriding
* an existing value if it exists.
* @param table the table to store the value into.
* @param name the name of the variable.
* @param the value of the new variable.
* @return the result of setting the variable.
*/
libab_result libab_set_variable(libab_table* table,
const char* name,
libab_ref* value);
/** /**
* Allocates a function that uses internal code to run. * Allocates a function that uses internal code to run.
* @param into the reference into which to store the new function. * @param into the reference into which to store the new function.
@@ -187,5 +209,19 @@ void* libab_unwrap_param(libab_ref_vec* vec, size_t index);
* @param buffer_size the size of the to buffer. * @param buffer_size the size of the to buffer.
*/ */
void libab_sanitize(char* to, const char* from, size_t buffer_size); void libab_sanitize(char* to, const char* from, size_t buffer_size);
/**
* Creates a new instance of libabacus allocated on the heap.
* @param into the pointer into which to store the newly allocated libab instance.
* @param parse_function the function used to parse numbers.
* @param free_function the function used to free parsed numbers.
*/
libab_result libab_create_instance(libab** into,
void* (*parse_function)(const char*),
void (*free_function)(void*));
/**
* Destroys the given libabacus instance allocated on the stack.
* @param into the reference to destroy.
*/
void libab_destroy_instance(libab* into);
#endif #endif

View File

@@ -1,4 +1,5 @@
#include "custom.h" #include "custom.h"
#include "util.h"
void libab_behavior_init_internal(libab_behavior* behavior, void libab_behavior_init_internal(libab_behavior* behavior,
libab_function_ptr func) { libab_function_ptr func) {
@@ -15,6 +16,9 @@ void libab_behavior_init_tree(libab_behavior* behavior, libab_tree* tree) {
void libab_behavior_copy(libab_behavior* behavior, libab_behavior* into) { void libab_behavior_copy(libab_behavior* behavior, libab_behavior* into) {
into->variant = behavior->variant; into->variant = behavior->variant;
into->data_u = behavior->data_u; into->data_u = behavior->data_u;
if(into->variant == BIMPL_TREE) {
into->data_u.tree->int_value++;
}
} }
void libab_behavior_free(libab_behavior* behavior) { void libab_behavior_free(libab_behavior* behavior) {
@@ -23,16 +27,20 @@ void libab_behavior_free(libab_behavior* behavior) {
} }
} }
void libab_operator_init(libab_operator* op, libab_operator_variant variant, libab_result libab_operator_init(libab_operator* op, libab_operator_variant variant,
int precedence, int associativity, const char* function) { int precedence, int associativity, const char* function) {
libab_result result = LIBAB_SUCCESS;
char* into;
op->variant = variant; op->variant = variant;
op->precedence = precedence; op->precedence = precedence;
op->associativity = associativity; op->associativity = associativity;
op->function = function; result = libab_copy_string(&into, function);
op->function = into;
return result;
} }
void libab_operator_free(libab_operator* op) { void libab_operator_free(libab_operator* op) {
free((char*) op->function);
} }
libab_result _function_init(libab_function* function, libab_ref* scope) { libab_result _function_init(libab_function* function, libab_ref* scope) {

View File

@@ -3,7 +3,6 @@
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h> #include <stdio.h>
#include <memory.h> #include <memory.h>
#include "refcount_internal.h"
void libab_gc_list_init(libab_gc_list* list) { void libab_gc_list_init(libab_gc_list* list) {
memset(&list->head_sentinel, 0, sizeof(list->head_sentinel)); memset(&list->head_sentinel, 0, sizeof(list->head_sentinel));

View File

@@ -71,13 +71,11 @@ FUNCTION(xor) {
} }
FUNCTION(atan) { FUNCTION(atan) {
printf("atan called\n");
double* val = libab_unwrap_param(params, 0); double* val = libab_unwrap_param(params, 0);
return create_double_value(ab, atan(*val), into); return create_double_value(ab, atan(*val), into);
} }
FUNCTION(atan2) { FUNCTION(atan2) {
printf("atan2 called\n");
double* left = libab_unwrap_param(params, 0); double* left = libab_unwrap_param(params, 0);
double* right = libab_unwrap_param(params, 1); double* right = libab_unwrap_param(params, 1);
return create_double_value(ab, atan2(*left, *right), into); return create_double_value(ab, atan2(*left, *right), into);
@@ -115,7 +113,6 @@ FUNCTION(print_unit) {
libab_result result = LIBAB_SUCCESS; \ libab_result result = LIBAB_SUCCESS; \
double right; \ double right; \
double left; \ double left; \
printf(#name " called\n"); \
left = *((double*)libab_unwrap_param(params, 0)); \ left = *((double*)libab_unwrap_param(params, 0)); \
right = *((double*)libab_unwrap_param(params, 1)); \ right = *((double*)libab_unwrap_param(params, 1)); \
create_double_value(ab, expression, into); \ create_double_value(ab, expression, into); \
@@ -176,6 +173,7 @@ libab_result register_functions(libab* ab) {
libab_ref_free(&trig_type); libab_ref_free(&trig_type);
libab_ref_free(&atan2_type); libab_ref_free(&atan2_type);
libab_ref_free(&difficult_type); libab_ref_free(&difficult_type);
libab_ref_free(&equals_num_type);
libab_ref_free(&print_num_type); libab_ref_free(&print_num_type);
libab_ref_free(&print_unit_type); libab_ref_free(&print_unit_type);
libab_ref_free(&print_bool_type); libab_ref_free(&print_bool_type);
@@ -200,7 +198,7 @@ libab_result loop(libab* ab, int interaction_count, libab_ref* scope) {
if (eval_result != LIBAB_SUCCESS) { if (eval_result != LIBAB_SUCCESS) {
printf("Invalid input (error code %d).\n", eval_result); printf("Invalid input (error code %d).\n", eval_result);
} else { } else {
result = libab_run_function_scoped(ab, "print", scope, &call_into, 1, &eval_into); result = libab_call_function_scoped(ab, "print", scope, &call_into, 1, &eval_into);
if(result == LIBAB_BAD_CALL) { if(result == LIBAB_BAD_CALL) {
printf("(?)\n"); printf("(?)\n");
result = LIBAB_SUCCESS; result = LIBAB_SUCCESS;
@@ -216,7 +214,6 @@ libab_result loop(libab* ab, int interaction_count, libab_ref* scope) {
int main() { int main() {
libab_result result; libab_result result;
libab_ref scope; libab_ref scope;
libab_ref test;
libab ab; libab ab;
if (libab_init(&ab, impl_parse, impl_free) != LIBAB_SUCCESS) { if (libab_init(&ab, impl_parse, impl_free) != LIBAB_SUCCESS) {
@@ -230,8 +227,6 @@ int main() {
} }
if(result == LIBAB_SUCCESS) { if(result == LIBAB_SUCCESS) {
loop(&ab, INTERACTIONS, &scope); loop(&ab, INTERACTIONS, &scope);
libab_table_search_value(libab_ref_get(&scope), "test", &test);
printf("%p\n", libab_ref_get(&test));
libab_ref_free(&scope); libab_ref_free(&scope);
} }

View File

@@ -623,7 +623,6 @@ libab_result _interpreter_copy_type_offset(libab_ref* type,
libab_result result = LIBAB_SUCCESS; libab_result result = LIBAB_SUCCESS;
libab_parsetype* new_type; libab_parsetype* new_type;
libab_parsetype* copy_of = libab_ref_get(type); libab_parsetype* copy_of = libab_ref_get(type);
size_t index = 0;
if((new_type = malloc(sizeof(*new_type)))) { if((new_type = malloc(sizeof(*new_type)))) {
new_type->variant = copy_of->variant; new_type->variant = copy_of->variant;
new_type->data_u = copy_of->data_u; new_type->data_u = copy_of->data_u;
@@ -632,9 +631,9 @@ libab_result _interpreter_copy_type_offset(libab_ref* type,
if(result == LIBAB_SUCCESS) { if(result == LIBAB_SUCCESS) {
libab_ref child_type_ref; libab_ref child_type_ref;
for(; index < copy_of->children.size - 1 - offset && for(; offset < copy_of->children.size &&
result == LIBAB_SUCCESS; index++) { result == LIBAB_SUCCESS; offset++) {
libab_ref_vec_index(&copy_of->children, offset + index, &child_type_ref); libab_ref_vec_index(&copy_of->children, offset, &child_type_ref);
result = libab_ref_vec_insert(&new_type->children, &child_type_ref); result = libab_ref_vec_insert(&new_type->children, &child_type_ref);
libab_ref_free(&child_type_ref); libab_ref_free(&child_type_ref);
} }
@@ -676,7 +675,7 @@ libab_result _interpreter_partially_apply(struct interpreter_state* state,
result = _interpreter_copy_function_with_params(state->ab, &value->data, params, scope, &new_function); result = _interpreter_copy_function_with_params(state->ab, &value->data, params, scope, &new_function);
if(result == LIBAB_SUCCESS) { if(result == LIBAB_SUCCESS) {
libab_ref_free(&new_type); libab_ref_free(&new_type);
result = _interpreter_copy_type_offset(&value->type, 0, &new_type); result = _interpreter_copy_type_offset(&value->type, params->size, &new_type);
} }
if(result == LIBAB_SUCCESS) { if(result == LIBAB_SUCCESS) {
@@ -1225,7 +1224,7 @@ libab_result _interpreter_run(struct interpreter_state* state, libab_tree* tree,
_interpreter_create_function_value(state, tree, scope, &function); _interpreter_create_function_value(state, tree, scope, &function);
if(result == LIBAB_SUCCESS) { if(result == LIBAB_SUCCESS) {
result = libab_put_table_value(libab_ref_get(scope), result = libab_overload_function(state->ab, libab_ref_get(scope),
tree->string_value, &function); tree->string_value, &function);
if(result != LIBAB_SUCCESS) { if(result != LIBAB_SUCCESS) {
libab_ref_free(&function); libab_ref_free(&function);
@@ -1298,7 +1297,7 @@ libab_result libab_interpreter_run(libab_interpreter* intr, libab_tree* tree,
return result; return result;
} }
libab_result libab_interpreter_run_function(libab_interpreter* intr, libab_result libab_interpreter_call_function(libab_interpreter* intr,
libab_ref* scope, libab_ref* scope,
const char* function, const char* function,
libab_ref_vec* params, libab_ref_vec* params,
@@ -1323,6 +1322,21 @@ libab_result libab_interpreter_run_function(libab_interpreter* intr,
return result; return result;
} }
libab_result libab_interpreter_call_value(libab_interpreter* intr,
libab_ref* scope,
libab_ref* function,
libab_ref_vec* params,
libab_ref* into) {
struct interpreter_state state;
libab_result result = LIBAB_SUCCESS;
_interpreter_init(&state, intr, scope);
result = _interpreter_try_call(&state, function, params, into);
_interpreter_free(&state);
return result;
}
void libab_interpreter_unit_value(libab_interpreter* intr, libab_ref* into) { void libab_interpreter_unit_value(libab_interpreter* intr, libab_ref* into) {
libab_ref_copy(&intr->value_unit, into); libab_ref_copy(&intr->value_unit, into);
} }

View File

@@ -102,7 +102,7 @@ libab_result _register_operator(libab* ab, const char* op,
if ((new_entry = malloc(sizeof(*new_entry)))) { if ((new_entry = malloc(sizeof(*new_entry)))) {
new_entry->variant = ENTRY_OP; new_entry->variant = ENTRY_OP;
new_operator = &(new_entry->data_u.op); new_operator = &(new_entry->data_u.op);
libab_operator_init(new_operator, token_type, precedence, associativity, result = libab_operator_init(new_operator, token_type, precedence, associativity,
function); function);
} else { } else {
result = LIBAB_MALLOC; result = LIBAB_MALLOC;
@@ -161,92 +161,16 @@ libab_result _create_value_function_internal(libab* ab,
return result; return result;
} }
libab_result _create_value_function_list(libab* ab, libab_ref* into, libab_ref* type) {
libab_ref list_ref;
libab_result result = libab_create_function_list(ab, &list_ref, type);
libab_ref_null(into);
if (result == LIBAB_SUCCESS) {
libab_ref_free(into);
result = libab_create_value_ref(ab, into, &list_ref, type);
}
libab_ref_free(&list_ref);
return result;
}
libab_result _libab_register_function_existing(libab* ab,
libab_table_entry* entry,
libab_ref* function_val) {
libab_value* old_value;
libab_parsetype* old_type;
libab_result result = LIBAB_SUCCESS;
old_value = libab_ref_get(&entry->data_u.value);
old_type = libab_ref_get(&old_value->type);
if (old_type->data_u.base == &_basetype_function_list) {
libab_function_list* list = libab_ref_get(&old_value->data);
result = libab_function_list_insert(list, function_val);
} else if (old_type->data_u.base == &_basetype_function) {
libab_ref new_list;
result =
_create_value_function_list(ab, &new_list, &ab->type_function_list);
if (result == LIBAB_SUCCESS) {
libab_function_list* list =
libab_ref_get(&((libab_value*)libab_ref_get(&new_list))->data);
result = libab_function_list_insert(list, &entry->data_u.value);
if (result == LIBAB_SUCCESS) {
result = libab_function_list_insert(list, function_val);
}
}
if (result == LIBAB_SUCCESS) {
libab_ref_swap(&entry->data_u.value, &new_list);
}
libab_ref_free(&new_list);
} else {
libab_ref_swap(&entry->data_u.value, function_val);
}
return result;
}
libab_result _libab_register_function_new(libab* ab, const char* name,
libab_ref* function_val) {
libab_result result = LIBAB_SUCCESS;
libab_table_entry* entry;
if ((entry = malloc(sizeof(*entry)))) {
entry->variant = ENTRY_VALUE;
libab_ref_copy(function_val, &entry->data_u.value);
result = libab_table_put(libab_ref_get(&ab->table), name, entry);
if (result != LIBAB_SUCCESS) {
libab_table_entry_free(entry);
free(entry);
}
} else {
result = LIBAB_MALLOC;
}
return result;
}
libab_result libab_register_function(libab* ab, const char* name, libab_result libab_register_function(libab* ab, const char* name,
libab_ref* type, libab_function_ptr func) { libab_ref* type, libab_function_ptr func) {
libab_table_entry* existing_entry;
libab_ref function_value; libab_ref function_value;
libab_result result = libab_result result =
_create_value_function_internal(ab, &function_value, type, func, &ab->table); _create_value_function_internal(ab, &function_value, type, func, &ab->table);
if (result == LIBAB_SUCCESS) { if (result == LIBAB_SUCCESS) {
existing_entry = libab_table_search_filter( libab_overload_function(ab, libab_ref_get(&ab->table), name, &function_value);
libab_ref_get(&ab->table), name, NULL, libab_table_compare_value);
if (existing_entry) {
result = _libab_register_function_existing(ab, existing_entry,
&function_value);
} else {
result = _libab_register_function_new(ab, name, &function_value);
}
libab_ref_free(&function_value);
} }
libab_ref_free(&function_value);
return result; return result;
} }
@@ -406,7 +330,7 @@ void libab_get_bool_value(libab* ab, int val, libab_ref* into) {
val ? libab_get_true_value(ab, into) : libab_get_false_value(ab, into); val ? libab_get_true_value(ab, into) : libab_get_false_value(ab, into);
} }
libab_result _create_tree(libab* ab, const char* string, libab_tree** into) { libab_result libab_parse(libab* ab, const char* string, libab_tree** into) {
libab_result result = LIBAB_SUCCESS; libab_result result = LIBAB_SUCCESS;
ll tokens; ll tokens;
@@ -442,7 +366,7 @@ libab_result libab_run(libab* ab, const char* string, libab_ref* value) {
libab_tree* root; libab_tree* root;
libab_ref_null(value); libab_ref_null(value);
result = _create_tree(ab, string, &root); result = libab_parse(ab, string, &root);
if (result == LIBAB_SUCCESS) { if (result == LIBAB_SUCCESS) {
libab_ref_free(value); libab_ref_free(value);
@@ -453,7 +377,7 @@ libab_result libab_run(libab* ab, const char* string, libab_ref* value) {
return result; return result;
} }
libab_result libab_run_function(libab* ab, const char* function, libab_result libab_call_function(libab* ab, const char* function,
libab_ref* into, libab_ref* into,
size_t param_count, ...) { size_t param_count, ...) {
libab_ref_vec params; libab_ref_vec params;
@@ -465,7 +389,7 @@ libab_result libab_run_function(libab* ab, const char* function,
result = _handle_va_params(ab, &params, param_count, args); result = _handle_va_params(ab, &params, param_count, args);
if(result == LIBAB_SUCCESS) { if(result == LIBAB_SUCCESS) {
libab_ref_free(into); libab_ref_free(into);
result = libab_interpreter_run_function(&ab->intr, &ab->table, function, &params, into); result = libab_interpreter_call_function(&ab->intr, &ab->table, function, &params, into);
libab_ref_vec_free(&params); libab_ref_vec_free(&params);
} }
@@ -483,7 +407,7 @@ libab_result libab_run_scoped(libab* ab, const char* string, libab_ref* scope, l
libab_tree* root; libab_tree* root;
libab_ref_null(into); libab_ref_null(into);
result = _create_tree(ab, string, &root); result = libab_parse(ab, string, &root);
if(result == LIBAB_SUCCESS) { if(result == LIBAB_SUCCESS) {
libab_ref_free(into); libab_ref_free(into);
result = libab_interpreter_run(&ab->intr, root, scope, SCOPE_NONE, into); result = libab_interpreter_run(&ab->intr, root, scope, SCOPE_NONE, into);
@@ -493,7 +417,7 @@ libab_result libab_run_scoped(libab* ab, const char* string, libab_ref* scope, l
return result; return result;
} }
libab_result libab_run_function_scoped(libab* ab, const char* function, libab_ref* scope, libab_ref* into, libab_result libab_call_function_scoped(libab* ab, const char* function, libab_ref* scope, libab_ref* into,
size_t param_count, ...) { size_t param_count, ...) {
libab_ref_vec params; libab_ref_vec params;
libab_result result; libab_result result;
@@ -504,7 +428,7 @@ libab_result libab_run_function_scoped(libab* ab, const char* function, libab_re
result = _handle_va_params(ab, &params, param_count, args); result = _handle_va_params(ab, &params, param_count, args);
if(result == LIBAB_SUCCESS) { if(result == LIBAB_SUCCESS) {
libab_ref_free(into); libab_ref_free(into);
result = libab_interpreter_run_function(&ab->intr, scope, function, &params, into); result = libab_interpreter_call_function(&ab->intr, scope, function, &params, into);
libab_ref_vec_free(&params); libab_ref_vec_free(&params);
} }

View File

@@ -525,6 +525,7 @@ libab_result _parse_fun(struct parser_state* state, libab_tree** store_into) {
libab_result result = LIBAB_SUCCESS; libab_result result = LIBAB_SUCCESS;
int is_parenth, is_comma; int is_parenth, is_comma;
libab_tree* temp; libab_tree* temp;
*store_into = NULL;
result = _parser_consume_type(state, TOKEN_KW_FUN); result = _parser_consume_type(state, TOKEN_KW_FUN);
if (result == LIBAB_SUCCESS) { if (result == LIBAB_SUCCESS) {
if (_parser_is_type(state, TOKEN_ID)) { if (_parser_is_type(state, TOKEN_ID)) {
@@ -1056,16 +1057,15 @@ libab_result _parse_expression(struct parser_state* state,
while (result == LIBAB_SUCCESS && op_stack.tail && while (result == LIBAB_SUCCESS && op_stack.tail &&
_parser_match_is_op(op_stack.tail->data)) { _parser_match_is_op(op_stack.tail->data)) {
libab_lexer_match* other_token = op_stack.tail->data; libab_lexer_match* other_token = op_stack.tail->data;
if(other_token->type == TOKEN_OP_INFIX) { if(other_token->type == TOKEN_OP_INFIX || other_token->type == TOKEN_OP_RESERVED) {
_parser_find_operator_infix(state, op_stack.tail->data, _parser_find_operator_infix(state, other_token, &other_operator);
&other_operator);
} }
if (other_token->type == TOKEN_OP_PREFIX || if (other_token->type == TOKEN_OP_PREFIX ||
(operator.associativity == - 1 && (operator.associativity == - 1 &&
operator.precedence <= other_operator.precedence) || operator.precedence <= other_operator.precedence) ||
(operator.associativity == 1 && (operator.associativity == 1 &&
operator.precedence<other_operator.precedence)) { operator.precedence < other_operator.precedence)) {
libab_lexer_match* match = ll_poptail(&op_stack); libab_lexer_match* match = ll_poptail(&op_stack);
result = _parser_append_op_node(state, match, &out_stack); result = _parser_append_op_node(state, match, &out_stack);
} else { } else {

View File

@@ -45,7 +45,7 @@ libab_result libab_parsetype_init_va(libab_parsetype* type,
result = libab_ref_vec_insert(&type->children, ref); result = libab_ref_vec_insert(&type->children, ref);
} }
if (free_vec) { if (result != LIBAB_SUCCESS && free_vec) {
libab_ref_vec_free(&type->children); libab_ref_vec_free(&type->children);
} }

View File

@@ -1,16 +0,0 @@
#include "refcount_internal.h"
#include <stdlib.h>
void libab_ref_count_changed(libab_ref_count* count) {
if (count->strong == 0) {
count->strong--;
if (count->free_func) {
count->free_func(count->data);
}
}
if (count->weak == 0) {
if(count->prev) count->prev->next = count->next;
if(count->next) count->next->prev = count->prev;
free(count);
}
}

View File

@@ -4,18 +4,6 @@
#include "value.h" #include "value.h"
#include "libabacus.h" #include "libabacus.h"
libab_result _update_entry(libab_table* table, const char* name, libab_ref* value) {
libab_result result = LIBAB_SUCCESS;
libab_table_entry* value_entry = libab_table_search_entry_value(table, name);
if(value_entry) {
libab_ref_free(&value_entry->data_u.value);
libab_ref_copy(value, &value_entry->data_u.value);
} else {
result = libab_put_table_value(table, name, value);
}
return result;
}
libab_result _behavior_assign(libab* ab, libab_ref* scope, libab_result _behavior_assign(libab* ab, libab_ref* scope,
libab_tree* left, libab_tree* right, libab_tree* left, libab_tree* right,
libab_ref* into) { libab_ref* into) {
@@ -24,7 +12,7 @@ libab_result _behavior_assign(libab* ab, libab_ref* scope,
if(left->variant == TREE_ID) { if(left->variant == TREE_ID) {
result = libab_run_tree_scoped(ab, right, scope, into); result = libab_run_tree_scoped(ab, right, scope, into);
if(result == LIBAB_SUCCESS) { if(result == LIBAB_SUCCESS) {
result = _update_entry(libab_ref_get(scope), left->string_value, into); result = libab_set_variable(libab_ref_get(scope), left->string_value, into);
} }
if(result != LIBAB_SUCCESS) { if(result != LIBAB_SUCCESS) {
@@ -41,6 +29,61 @@ libab_result _behavior_assign(libab* ab, libab_ref* scope,
return result; return result;
} }
libab_result _behavior_method(libab* ab, libab_ref* scope,
libab_tree* left, libab_tree* right,
libab_ref* into) {
libab_result result = LIBAB_SUCCESS;
libab_ref param;
libab_ref callee;
libab_ref_vec params;
int free_params = 0;
size_t index = 0;
libab_ref_null(&callee);
if(right->variant == TREE_CALL) {
result = libab_ref_vec_init(&params);
} else {
result = LIBAB_BAD_CALL;
}
if(result == LIBAB_SUCCESS) {
free_params = 1;
result = libab_run_tree_scoped(ab, left, scope, &param);
if(result == LIBAB_SUCCESS) {
result = libab_ref_vec_insert(&params, &param);
}
libab_ref_free(&param);
}
for(; index < right->children.size - 1 && result == LIBAB_SUCCESS; index++) {
result = libab_run_tree_scoped(ab, vec_index(&right->children, index),
scope, &param);
if(result == LIBAB_SUCCESS) {
result = libab_ref_vec_insert(&params, &param);
}
libab_ref_free(&param);
}
if(result == LIBAB_SUCCESS) {
libab_ref_free(&callee);
result = libab_run_tree_scoped(ab, vec_index(&right->children, right->children.size - 1),
scope, &callee);
}
if(result == LIBAB_SUCCESS) {
result = libab_interpreter_call_value(&ab->intr, scope, &callee, &params, into);
} else {
libab_ref_null(into);
}
if(free_params) {
libab_ref_vec_free(&params);
}
libab_ref_free(&callee);
return result;
}
libab_result _expect_boolean(libab* ab, libab_ref* scope, libab_result _expect_boolean(libab* ab, libab_ref* scope,
libab_tree* to_run, int* into) { libab_tree* to_run, int* into) {
libab_result result = LIBAB_SUCCESS; libab_result result = LIBAB_SUCCESS;
@@ -103,19 +146,25 @@ libab_result _behavior_lor(libab* ab, libab_ref* scope,
static const libab_reserved_operator libab_reserved_operators[] = { static const libab_reserved_operator libab_reserved_operators[] = {
{ {
"=", /* Assignment */ "=", /* Assignment */
0, /* Lowest precedence */ -3, /* Lowest precedence */
1, /* Right associative, a = b = 6 should be a = (b = 6) */ 1, /* Right associative, a = b = 6 should be a = (b = 6) */
_behavior_assign _behavior_assign
}, },
{
".",
-2,
-1,
_behavior_method
},
{ {
"&&", /* Logical and */ "&&", /* Logical and */
0, /* Low precedence */ -1, /* Low precedence */
-1, /* Left associative. */ -1, /* Left associative. */
_behavior_land _behavior_land
}, },
{ {
"||", /* Logical or */ "||", /* Logical or */
0, /* Low precedence */ -1, /* Low precedence */
-1, /* Left associative. */ -1, /* Left associative. */
_behavior_lor _behavior_lor
}, },

View File

@@ -67,7 +67,7 @@ libab_table_entry* libab_table_search_entry_operator(libab_table* table,
} else if (type == OPERATOR_INFIX) { } else if (type == OPERATOR_INFIX) {
entry = libab_table_search_filter(table, string, NULL, entry = libab_table_search_filter(table, string, NULL,
libab_table_compare_op_infix); libab_table_compare_op_infix);
} else if (type == OPERATOR_PREFIX) { } else if (type == OPERATOR_POSTFIX) {
entry = libab_table_search_filter(table, string, NULL, entry = libab_table_search_filter(table, string, NULL,
libab_table_compare_op_postfix); libab_table_compare_op_postfix);
} }

View File

@@ -290,9 +290,115 @@ libab_result libab_create_value_raw(libab* ab, libab_ref* into,
return result; return result;
} }
libab_result _create_value_function_list(libab* ab, libab_ref* into, libab_ref* type) {
libab_ref list_ref;
libab_result result = libab_create_function_list(ab, &list_ref, type);
libab_ref_null(into);
if (result == LIBAB_SUCCESS) {
libab_ref_free(into);
result = libab_create_value_ref(ab, into, &list_ref, type);
}
libab_ref_free(&list_ref);
return result;
}
libab_result _register_function_existing(libab* ab,
libab_table_entry* entry,
libab_ref* function_val) {
libab_value* old_value;
libab_parsetype* old_type;
libab_result result = LIBAB_SUCCESS;
old_value = libab_ref_get(&entry->data_u.value);
old_type = libab_ref_get(&old_value->type);
if (old_type->data_u.base == libab_get_basetype_function_list(ab)) {
libab_function_list* list = libab_ref_get(&old_value->data);
result = libab_function_list_insert(list, function_val);
} else if (old_type->data_u.base == libab_get_basetype_function(ab)) {
libab_ref new_list;
result =
_create_value_function_list(ab, &new_list, &ab->type_function_list);
if (result == LIBAB_SUCCESS) {
libab_function_list* list =
libab_ref_get(&((libab_value*)libab_ref_get(&new_list))->data);
result = libab_function_list_insert(list, &entry->data_u.value);
if (result == LIBAB_SUCCESS) {
result = libab_function_list_insert(list, function_val);
}
}
if (result == LIBAB_SUCCESS) {
libab_ref_swap(&entry->data_u.value, &new_list);
}
libab_ref_free(&new_list);
} else {
libab_ref_swap(&entry->data_u.value, function_val);
}
return result;
}
libab_result _register_function_new(libab* ab, const char* name,
libab_ref* function_val) {
libab_result result = LIBAB_SUCCESS;
libab_table_entry* entry;
if ((entry = malloc(sizeof(*entry)))) {
entry->variant = ENTRY_VALUE;
libab_ref_copy(function_val, &entry->data_u.value);
result = libab_table_put(libab_ref_get(&ab->table), name, entry);
if (result != LIBAB_SUCCESS) {
libab_table_entry_free(entry);
free(entry);
}
} else {
result = LIBAB_MALLOC;
}
return result;
}
libab_result libab_overload_function(libab* ab,
libab_table* table,
const char* name,
libab_ref* function) {
libab_result result = LIBAB_SUCCESS;
libab_table_entry* existing_entry = libab_table_search_filter(
table, name, NULL, libab_table_compare_value);
if (existing_entry) {
result = _register_function_existing(ab, existing_entry,
function);
} else {
result = _register_function_new(ab, name, function);
}
return result;
}
libab_result libab_set_variable(libab_table* table,
const char* name,
libab_ref* value) {
libab_result result = LIBAB_SUCCESS;
libab_table_entry* value_entry = libab_table_search_entry_value(table, name);
if(value_entry) {
libab_ref_free(&value_entry->data_u.value);
libab_ref_copy(value, &value_entry->data_u.value);
} else {
result = libab_put_table_value(table, name, value);
}
return result;
}
void _gc_visit_function_children(void* function, libab_visitor_function_ptr visitor, void* data) { void _gc_visit_function_children(void* function, libab_visitor_function_ptr visitor, void* data) {
size_t index = 0;
libab_function* func = function; libab_function* func = function;
libab_gc_visit(&func->scope, visitor, data); libab_gc_visit(&func->scope, visitor, data);
for(; index < func->params.size; index++) {
libab_gc_visit(&func->params.data[index], visitor, data);
}
} }
libab_result libab_create_function_internal(libab* ab, libab_ref* into, libab_result libab_create_function_internal(libab* ab, libab_ref* into,
@@ -385,6 +491,15 @@ libab_result libab_create_function_behavior(libab* ab, libab_ref* into,
return result; return result;
} }
void _gc_visit_function_list_children(void* list, libab_visitor_function_ptr visitor, void* data) {
size_t index = 0;
libab_function_list* func_list = list;
for(; index < func_list->functions.size; index++) {
libab_gc_visit(&func_list->functions.data[index], visitor, data);
}
}
libab_result libab_create_function_list(libab* ab, libab_ref* into, libab_ref* type) { libab_result libab_create_function_list(libab* ab, libab_ref* into, libab_ref* type) {
libab_function_list* list; libab_function_list* list;
libab_result result = LIBAB_SUCCESS; libab_result result = LIBAB_SUCCESS;
@@ -408,7 +523,7 @@ libab_result libab_create_function_list(libab* ab, libab_ref* into, libab_ref* t
libab_ref_null(into); libab_ref_null(into);
free(list); free(list);
} else { } else {
libab_gc_add(into, _gc_visit_function_children, &ab->containers); libab_gc_add(into, _gc_visit_function_list_children, &ab->containers);
} }
return result; return result;
@@ -471,9 +586,29 @@ void libab_sanitize(char* to, const char* from, size_t buffer_size) {
while (*from && index < (buffer_size - 2)) { while (*from && index < (buffer_size - 2)) {
if (*from == '+' || *from == '*' || *from == '\\' || if (*from == '+' || *from == '*' || *from == '\\' ||
*from == '|' || *from == '[' || *from == ']' || *from == '(' || *from == '|' || *from == '[' || *from == ']' || *from == '(' ||
*from == ')') *from == ')' || *from == '.')
to[index++] = '\\'; to[index++] = '\\';
to[index++] = *(from++); to[index++] = *(from++);
} }
to[index] = '\0'; to[index] = '\0';
} }
libab_result libab_create_instance(libab** into,
void* (*parse_function)(const char*),
void (*free_function)(void*)) {
libab_result result = LIBAB_SUCCESS;
if((*into = malloc(sizeof(**into)))) {
result = libab_init(*into, parse_function, free_function);
if(result != LIBAB_SUCCESS) {
free(*into);
*into = NULL;
}
} else {
result = LIBAB_MALLOC;
}
return result;
}
void libab_destroy_instance(libab* into) {
libab_free(into);
free(into);
}