From fa1f9af151526a6679d605c8af91d502cef90b75 Mon Sep 17 00:00:00 2001 From: dec05eba Date: Sun, 26 Jan 2020 11:56:53 +0100 Subject: Implement the basis of a function call --- Makefile | 5 +- README.md | 2 + example.tsl | 4 +- examples/curl.tsl | 3 ++ src/program.c | 136 ++++++++++++++++++++++++++++++++++++------------------ 5 files changed, 103 insertions(+), 47 deletions(-) mode change 100644 => 100755 example.tsl create mode 100755 examples/curl.tsl diff --git a/Makefile b/Makefile index f6ec581..095856f 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ LIBS = -lgc OBJ = build/main.o build/tokenizer.o build/parser.o build/program.o build/bytecode.o build/value.o build/command.o build/buffer.o build/hash_map.o build/list.o CC = cc -all: build_dir $(OBJ) +build: build_dir $(OBJ) $(CC) -o build/tsl $(OBJ) $(LIBS) clean: @@ -15,6 +15,9 @@ build_dir: compiledb: make clean; bear make +install: build + install -m +x -s build/tsl /usr/local/bin/tsl + build/main.o: src/main.c include/tokenizer.h $(CC) -c src/main.c -o build/main.o $(CFLAGS) diff --git a/README.md b/README.md index 072cc5c..1426b2e 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ A tiny scripting language that is designed to be a replacement for small shell/python scripts.\ Written in ANSI C to allow embedding everywhere and a WTFPL license that allows it to be used anywhere without any restrictions.\ See example.tsl for an example of what tsl looks like. +# Installation +Run: `sudo make install`. The tsl binary will be installed in /usr/bin/local. # TODO * Remove dependency on `gc`. Write our own gc instead. * Implement big int, which numbers should automatically switch to when they are too large to fit into double. diff --git a/example.tsl b/example.tsl old mode 100644 new mode 100755 index 5146713..092e7e5 --- a/example.tsl +++ b/example.tsl @@ -1,3 +1,5 @@ +#!/usr/bin/env tsl + # loadn 1 # setv "value1" value1 = 1 @@ -80,7 +82,7 @@ value8 = fn (value) {} value9 = { "hello": "world", "sayHello": fn() { - + result = $(echo hello) } } diff --git a/examples/curl.tsl b/examples/curl.tsl new file mode 100755 index 0000000..9f722d4 --- /dev/null +++ b/examples/curl.tsl @@ -0,0 +1,3 @@ +#!/usr/bin/env tsl + +result = $(curl https://wttr.in/) diff --git a/src/program.c b/src/program.c index efb9407..88f94a4 100644 --- a/src/program.c +++ b/src/program.c @@ -38,6 +38,7 @@ void tsl_program_deinit(TslProgram *self) { ++bytecode_writer; } tsl_buffer_deinit(&self->function_bytecode_list); + GC_deinit(); /* TODO: Remove this */ } static int program_output_temp(char *data, int size, void *userdata) { @@ -70,7 +71,8 @@ static TslProgramResult tsl_string_ref_get_item_at_index(const TslStringView *se return TSL_PROGRAM_RESULT_OK; } -static TslProgramResult tsl_value_create_from_stack_value(TslProgram *self, TslStackValue *src, TslValue *dst) { +static TslProgramResult tsl_value_create_from_stack_value(TslProgram *self, TslValue *dst) { + TslStackValue *src = tsl_buffer_pop(&self->stack_values, sizeof(TslStackValue)); tsl_value_clear(dst); switch(src->type) { case TSL_STACK_VALUE_TYPE_NUMBER: { @@ -136,10 +138,8 @@ static TslProgramResult tsl_value_create_from_stack_value(TslProgram *self, TslS break; } case TSL_STACK_VALUE_TYPE_LIST: { - TslStackValue *items_begin = tsl_buffer_pop(&self->stack_values, sizeof(TslStackValue) * src->data.integer); - TslStackValue *items_end = items_begin + src->data.integer; - TslStackValue *item = items_end - 1; TslValue *list_item; + int i = src->data.integer - 1; dst->data.list = GC_MALLOC(sizeof(TslList)); return_if_error(dst->data.list); @@ -148,44 +148,40 @@ static TslProgramResult tsl_value_create_from_stack_value(TslProgram *self, TslS dst->data.list->size = sizeof(TslValue) * src->data.integer; list_item = tsl_list_end(dst->data.list) - sizeof(TslValue); - while(item != items_begin - 1) { - return_if_error(tsl_value_create_from_stack_value(self, item, list_item)); + while(i >= 0) { + return_if_error(tsl_value_create_from_stack_value(self, list_item)); --list_item; - --item; + --i; } dst->type = TSL_TYPE_LIST; break; } case TSL_STACK_VALUE_TYPE_MAP: { - TslStackValue *items_begin = tsl_buffer_pop(&self->stack_values, sizeof(TslStackValue) * src->data.integer); - TslStackValue *items_end = items_begin + src->data.integer; - TslStackValue *item = items_end - 1; + int i = src->data.integer - 1; dst->data.map = GC_MALLOC(sizeof(TslHashMap)); return_if_error(dst->data.map); tsl_hash_map_init(dst->data.map); assert(src->data.integer % 2 == 0); - while(item != items_begin - 1) { + while(i >= 0) { TslValue map_key; TslValue map_value; - return_if_error(tsl_value_create_from_stack_value(self, item, &map_value)); - return_if_error(tsl_value_create_from_stack_value(self, item - 1, &map_key)); + return_if_error(tsl_value_create_from_stack_value(self, &map_value)); + return_if_error(tsl_value_create_from_stack_value(self, &map_key)); return_if_error(tsl_hash_map_insert(dst->data.map, &map_key, &map_value)); - item -= 2; + i -= 2; } dst->type = TSL_TYPE_MAP; break; } case TSL_STACK_VALUE_TYPE_INDEX: { - TslStackValue *args = tsl_buffer_pop(&self->stack_values, sizeof(TslStackValue) * 2); TslValue var; TslValue key; - - return_if_error(tsl_value_create_from_stack_value(self, args + 1, &key)); - return_if_error(tsl_value_create_from_stack_value(self, args, &var)); + return_if_error(tsl_value_create_from_stack_value(self, &key)); + return_if_error(tsl_value_create_from_stack_value(self, &var)); if(var.type == TSL_TYPE_LIST) { TslValue *list_item; @@ -236,13 +232,12 @@ static TslProgramResult tsl_value_create_from_stack_value(TslProgram *self, TslS } static TslProgramResult tsl_program_perform_operation(TslProgram *self, TslNumber(*operation_func)(TslNumber lhs, TslNumber rhs)) { - TslStackValue *args = tsl_buffer_pop(&self->stack_values, sizeof(TslStackValue) * 2); TslValue lhs_value; TslValue rhs_value; TslStackValue stack_value; - return_if_error(tsl_value_create_from_stack_value(self, args + 1, &rhs_value)); - return_if_error(tsl_value_create_from_stack_value(self, args, &lhs_value)); + return_if_error(tsl_value_create_from_stack_value(self, &rhs_value)); + return_if_error(tsl_value_create_from_stack_value(self, &lhs_value)); if(lhs_value.type != TSL_TYPE_NUMBER) { fprintf(stderr, "Error: Unable to perform operation '+' between a TODO and a TODO\n"); @@ -270,21 +265,12 @@ static TslNumber number_operation_div(TslNumber lhs, TslNumber rhs) { return lhs / rhs; } -TslProgramResult tsl_program_run(TslProgram *self) { +static TslProgramResult tsl_program_run_function(TslProgram *self, int function_index) { TslProgramResult result = TSL_PROGRAM_RESULT_OK; - TslBytecode *file_scope_bytecode = tsl_buffer_begin(&self->function_bytecode_list); - char *instruction = tsl_buffer_begin(&file_scope_bytecode->buffer); - char *instruction_end = tsl_buffer_end(&file_scope_bytecode->buffer); - - self->variables = GC_MALLOC_UNCOLLECTABLE(sizeof(TslHashMap)); - if(!self->variables) { - fprintf(stderr, "Error: Failed to allocate root object\n"); - return TSL_PROGRAM_RESULT_ERR; - } - tsl_hash_map_init(self->variables); - tsl_buffer_init(&self->stack_values); - - printf("###########################\n"); + TslBytecode *function_bytecode = (TslBytecode*)tsl_buffer_begin(&self->function_bytecode_list) + function_index; + char *instruction = tsl_buffer_begin(&function_bytecode->buffer); + char *instruction_end = tsl_buffer_end(&function_bytecode->buffer); + size_t prev_stack_value_size = self->stack_values.size; /* TODO: Verify if these don't cause unaligned memory access on non-x86 platforms */ while(instruction != instruction_end) { @@ -348,7 +334,6 @@ TslProgramResult tsl_program_run(TslProgram *self) { break; } case TSL_OPCODE_SETV: { - TslStackValue *stack_value = tsl_buffer_pop(&self->stack_values, sizeof(TslStackValue)); TslValue map_key; TslValue *map_value; map_key.type = TSL_TYPE_STRING_REF; @@ -357,7 +342,7 @@ TslProgramResult tsl_program_run(TslProgram *self) { map_value = tsl_hash_map_get_or_create(self->variables, &map_key); cleanup_if_error(map_value); - cleanup_if_error(tsl_value_create_from_stack_value(self, stack_value, map_value)); + cleanup_if_error(tsl_value_create_from_stack_value(self, map_value)); /*printf("setv \"%.*s\"\n", (int)instruction_type4->value.size, instruction_type4->value.data);*/ instruction += sizeof(TslInstructionType4); break; @@ -389,22 +374,56 @@ TslProgramResult tsl_program_run(TslProgram *self) { break; } case TSL_OPCODE_CALLF: { - /* + 1, since the first arg is the function index and the other args are the args for the function */ - TslStackValue *args = tsl_buffer_pop(&self->stack_values, sizeof(TslStackValue) * (1 + instruction_type1->value)); - (void)args; + size_t args_size = 1 + instruction_type1->value; + TslStackValue *args = (TslStackValue*)tsl_buffer_end(&self->stack_values) - args_size; + assert(self->stack_values.size >= args_size); + + { + /* Resolved values pushed to stack and replace them in the stack */ + size_t prev_stack_size = self->stack_values.size; + TslStackValue *arg = (TslStackValue*)tsl_buffer_end(&self->stack_values) - 1; + TslStackValue *args_begin = args - 1; + while(arg != args_begin) { + TslValue new_value; + cleanup_if_error(tsl_value_create_from_stack_value(self, &new_value)); + arg->data.variable = new_value; + arg->type = TSL_STACK_VALUE_TYPE_VARIABLE; + --arg; + } + /* + Reset the stack size here because @tsl_value_create_from_stack_value which is called above pops the stack (without reallocation). + TODO: Modify @tsl_value_create_from_stack_value to not pop stack and instead operate with stack pointer. + */ + self->stack_values.size = prev_stack_size; + } + + assert(args[0].type == TSL_STACK_VALUE_TYPE_VARIABLE); + if(args[0].data.variable.type != TSL_TYPE_FUNCTION) { + fprintf(stderr, "Error: Unable to call a non-function type\n"); + goto cleanup; + } + + cleanup_if_error(tsl_program_run_function(self, args[0].data.variable.data.function)); + tsl_buffer_pop(&self->stack_values, sizeof(TslStackValue) * args_size); + /* TODO: Implement this */ + { + /* TODO: This is only temporary. This should be replaced with push the result of the function call onto the stack */ + TslStackValue stack_value; + stack_value.type = TSL_STACK_VALUE_TYPE_NULL; + cleanup_if_error(tsl_buffer_append(&self->stack_values, &stack_value, sizeof(stack_value))); + } /*printf("callf %d\n", instruction_type1->value);*/ instruction += sizeof(TslInstructionType1); break; } case TSL_OPCODE_ADD: { - TslStackValue *args = tsl_buffer_pop(&self->stack_values, sizeof(TslStackValue) * 2); TslValue lhs_value; TslValue rhs_value; TslStackValue stack_value; - cleanup_if_error(tsl_value_create_from_stack_value(self, args + 1, &rhs_value)); - cleanup_if_error(tsl_value_create_from_stack_value(self, args, &lhs_value)); + cleanup_if_error(tsl_value_create_from_stack_value(self, &rhs_value)); + cleanup_if_error(tsl_value_create_from_stack_value(self, &lhs_value)); cleanup_if_error(tsl_value_add(&lhs_value, &rhs_value, &stack_value.data.variable)); stack_value.type = TSL_STACK_VALUE_TYPE_VARIABLE; @@ -479,6 +498,13 @@ TslProgramResult tsl_program_run(TslProgram *self) { } command_args[arg_index] = NULL; } + + { + /* TODO: This is only temporary. This should be replaced with push the result of the command onto the stack */ + TslStackValue stack_value; + stack_value.type = TSL_STACK_VALUE_TYPE_NULL; + cleanup_if_error(tsl_buffer_append(&self->stack_values, &stack_value, sizeof(stack_value))); + } /*printf("callc %d\n", instruction_type1->value);*/ instruction += sizeof(TslInstructionType1); @@ -489,9 +515,29 @@ TslProgramResult tsl_program_run(TslProgram *self) { cleanup: /* - If self->stack_values.size is more than 0, then there are operations (for example CALLF) that pushes a value to the stack but the returned value - is not assigned to any variable. At the end of functions, these stack values are cleaned up even if they are not handled. + This a bug in the compiler. The compiler pop'ed more values that pushed to the stack. + It's fine if there were values that were pushed to the stack but not pop'ed. This happens for example if there is a function call + and the result is not assigned to a variable (in other words, the result is ignored). + This will automatically be cleaned up here and in loops it will be cleaned up at the end of the loop scope. */ + assert(self->stack_values.size >= prev_stack_value_size); + self->stack_values.size = prev_stack_value_size; + return result; +} + +TslProgramResult tsl_program_run(TslProgram *self) { + TslProgramResult result = TSL_PROGRAM_RESULT_OK; + + self->variables = GC_MALLOC_UNCOLLECTABLE(sizeof(TslHashMap)); + if(!self->variables) { + fprintf(stderr, "Error: Failed to allocate root object\n"); + return TSL_PROGRAM_RESULT_ERR; + } + tsl_hash_map_init(self->variables); + tsl_buffer_init(&self->stack_values); + + printf("###########################\n"); + result = tsl_program_run_function(self, 0); tsl_buffer_deinit(&self->stack_values); GC_FREE(self->variables); /* Free the root object, resulting in all objects in the program being free'd */ -- cgit v1.2.3