Compare commits

..

14 Commits

11 changed files with 134 additions and 42 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

@@ -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

@@ -187,5 +187,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

@@ -15,6 +15,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) {

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);
@@ -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) {

View File

@@ -1056,16 +1056,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

@@ -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

@@ -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

@@ -291,8 +291,12 @@ libab_result libab_create_value_raw(libab* ab, libab_ref* into,
} }
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 +389,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 +421,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;
@@ -477,3 +490,23 @@ void libab_sanitize(char* to, const char* from, size_t buffer_size) {
} }
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);
}