diff options
author | dec05eba <dec05eba@protonmail.com> | 2020-01-24 09:11:53 +0100 |
---|---|---|
committer | dec05eba <dec05eba@protonmail.com> | 2020-01-24 09:11:53 +0100 |
commit | 1dd53ce54c2008e3a11a636a496853cf6f9a5d65 (patch) | |
tree | 73f8ff8d048c8b1e4c6cf7acfd3e229650d044d5 | |
parent | 26f8fbc2c657ecffc874410691dd3fc83ba11131 (diff) |
Convert hash map to gc, implement more instructions and call command
-rw-r--r-- | Makefile | 14 | ||||
-rw-r--r-- | README.md | 2 | ||||
-rw-r--r-- | example.tsl | 3 | ||||
-rw-r--r-- | include/bytecode.h | 2 | ||||
-rw-r--r-- | include/command.h | 12 | ||||
-rw-r--r-- | include/parser.h | 2 | ||||
-rw-r--r-- | include/program.h | 29 | ||||
-rw-r--r-- | include/std/buffer.h | 9 | ||||
-rw-r--r-- | include/std/hash_map.h | 25 | ||||
-rw-r--r-- | include/std_gc/hash_map.h | 29 | ||||
-rw-r--r-- | include/string_view.h (renamed from include/std/string_view.h) | 2 | ||||
-rw-r--r-- | include/tokenizer.h | 6 | ||||
-rw-r--r-- | include/value.h | 10 | ||||
-rw-r--r-- | src/command.c | 90 | ||||
-rw-r--r-- | src/main.c | 19 | ||||
-rw-r--r-- | src/parser.c | 13 | ||||
-rw-r--r-- | src/program.c | 162 | ||||
-rw-r--r-- | src/std/buffer.c | 25 | ||||
-rw-r--r-- | src/std_gc/hash_map.c (renamed from src/std/hash_map.c) | 127 | ||||
-rw-r--r-- | src/tokenizer.c | 2 | ||||
-rw-r--r-- | src/value.c | 45 |
21 files changed, 440 insertions, 188 deletions
@@ -1,6 +1,6 @@ -CFLAGS = -Wall -Wextra -Werror -g3 -std=c89 -pedantic -fPIE +CFLAGS = -Wall -Wextra -Werror -g3 -std=c89 -pedantic -fPIE -DDEBUG LIBS = -lgc -OBJ = build/main.o build/tokenizer.o build/parser.o build/program.o build/bytecode.o build/buffer.o build/hash_map.o +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 CC = cc all: build_dir $(OBJ) @@ -30,8 +30,14 @@ build/program.o: src/program.c include/program.h build/bytecode.o: src/bytecode.c include/bytecode.h $(CC) -c src/bytecode.c -o build/bytecode.o $(CFLAGS) +build/value.o: src/value.c include/value.h + $(CC) -c src/value.c -o build/value.o $(CFLAGS) + +build/command.o: src/command.c include/command.h + $(CC) -c src/command.c -o build/command.o $(CFLAGS) + build/buffer.o: src/std/buffer.c include/std/buffer.h $(CC) -c src/std/buffer.c -o build/buffer.o $(CFLAGS) -build/hash_map.o: src/std/hash_map.c include/std/hash_map.h - $(CC) -c src/std/hash_map.c -o build/hash_map.o $(CFLAGS) +build/hash_map.o: src/std_gc/hash_map.c include/std_gc/hash_map.h + $(CC) -c src/std_gc/hash_map.c -o build/hash_map.o $(CFLAGS) @@ -4,5 +4,5 @@ See example.tsl for an example of what tsl looks like. # 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. -* Use a list instead of a hash map for variable lookup, since the list will be small most of the time. +* Use a list instead of a hash map for variable lookup in the parser, since the list will be small most of the time. * Implement sandboxes similar to lua, by disabling use of import, file io and other functions.
\ No newline at end of file diff --git a/example.tsl b/example.tsl index 501ddaa..18253a8 100644 --- a/example.tsl +++ b/example.tsl @@ -75,4 +75,5 @@ value1 = value1 + 23 # loadca "https://example.com" # callc 2 # setv "response" -response = $(curl https://example.com)
\ No newline at end of file +response = $(echo hello world) +# compile error (empty command): $()
\ No newline at end of file diff --git a/include/bytecode.h b/include/bytecode.h index 06ebf19..912da89 100644 --- a/include/bytecode.h +++ b/include/bytecode.h @@ -2,7 +2,7 @@ #define TSL_BYTECODE_H #include "std/buffer.h" -#include "std/string_view.h" +#include "string_view.h" #include "value.h" #include <stdint.h> diff --git a/include/command.h b/include/command.h new file mode 100644 index 0000000..6e6490b --- /dev/null +++ b/include/command.h @@ -0,0 +1,12 @@ +#ifndef TSL_COMMAND_H +#define TSL_COMMAND_H + +#include "string_view.h" + +/* Return 1 if you want to continue reading. @data is null-terminated */ +typedef int (*ProgramOutputCallback)(char *data, int size, void *userdata); + +/* Returns the program exit code (a positive value), or a negative value on failure */ +int tsl_command_exec(char **args, ProgramOutputCallback output_callback, void *userdata); + +#endif /* TSL_COMMAND_H */ diff --git a/include/parser.h b/include/parser.h index 440fbac..9fdb9b6 100644 --- a/include/parser.h +++ b/include/parser.h @@ -13,6 +13,6 @@ typedef enum { TODO: Make this function load a file instead of parsing memory. This is needed because when using @import function instead tsl, it will load a file anyways. */ -TslParseResult tsl_parse(const char *code, size_t code_size, TslProgram *program_output); +TslParseResult tsl_parse(char *code, size_t code_size, TslProgram *program_output); #endif /* TSL_PARSER_H */ diff --git a/include/program.h b/include/program.h index c097c7b..5c4737b 100644 --- a/include/program.h +++ b/include/program.h @@ -2,16 +2,35 @@ #define TSL_PROGRAM_H #include "std/buffer.h" -#include "std/hash_map.h" +#include "std_gc/hash_map.h" +#include "string_view.h" #include "value.h" -#define TSL_STACK_MAX_SIZE 255 +typedef enum { + TSL_STACK_VALUE_TYPE_NUMBER, + TSL_STACK_VALUE_TYPE_BOOL, + TSL_STACK_VALUE_TYPE_STRING, + TSL_STACK_VALUE_TYPE_FUNCTION, + TSL_STACK_VALUE_TYPE_VARIABLE, + TSL_STACK_VALUE_TYPE_NULL, + TSL_STACK_VALUE_TYPE_COMMAND_ARG +} TslStackValueType; + +typedef struct { + union { + int integer; + double number; + TslBool boolean; + TslStringView str; + } data; + TslStackValueType type; +} TslStackValue; typedef struct { TslBuffer /*TslBytecode*/ function_bytecode_list; - TslHashMap /*TslStringView, TslValue*/ variables; - TslValue stack_values[TSL_STACK_MAX_SIZE]; - size_t stack_index; + /* Allocated with GC and is the root object of the program. When this is deallocated, the whole program is deallocated */ + TslHashMap /*TslStringView, TslValue*/ *variables; + TslBuffer /*TslStackValue*/ stack_values; } TslProgram; typedef enum { diff --git a/include/std/buffer.h b/include/std/buffer.h index 3aad48b..7253d88 100644 --- a/include/std/buffer.h +++ b/include/std/buffer.h @@ -17,8 +17,13 @@ void tsl_buffer_init(TslBuffer *self); void tsl_buffer_deinit(TslBuffer *self); int tsl_buffer_append(TslBuffer *self, const void *data, size_t size); -/* The buffer has to >= @size */ -void tsl_buffer_pop(TslBuffer *self, size_t size); +/* + This function changes the size of the buffer without changing the capacity + and returns the value at the back of the buffer, which is valid until @tsl_buffer_append or + @tsl_buffer_deinit is called. + The buffer size has to be larger or equal to @size. +*/ +void* tsl_buffer_pop(TslBuffer *self, size_t size); void* tsl_buffer_begin(TslBuffer *self); void* tsl_buffer_end(TslBuffer *self); void tsl_buffer_move(TslBuffer *dst, TslBuffer *src); diff --git a/include/std/hash_map.h b/include/std/hash_map.h deleted file mode 100644 index b430d22..0000000 --- a/include/std/hash_map.h +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef TSL_HASH_MAP_H -#define TSL_HASH_MAP_H - -#include "string_view.h" -#include <stdint.h> - -typedef uint64_t (*TslHashFunc)(const void *data, size_t size); - -/* TODO: Optimize small hash map by using the members of the struct instead of allocating on heap */ -typedef struct { - void *buckets_data; /* value=TslHashMapNode<void*>, data=|hash(uint64_t) + key_size(size_t) + key_data(...) data_size(size_t) + data_data(...)| */ - size_t buckets_size; - size_t buckets_capacity; -} TslHashMap; - -void tsl_hash_map_init(TslHashMap *self); -void tsl_hash_map_deinit(TslHashMap *self); - -int tsl_hash_map_insert(TslHashMap *self, const TslStringView *key, const void *data, size_t size, TslHashFunc hash_func); -/* Get a reference to the value by key @key, or NULL if it doesn't exist */ -void* tsl_hash_map_get(TslHashMap *self, const TslStringView *key, TslHashFunc hash_func); -/* Get a reference to the value by key @key, or insert a new value for the key with a size of @size */ -int tsl_hash_map_get_or_create(TslHashMap *self, const TslStringView *key, size_t size, TslHashFunc hash_func, void **output); - -#endif /* TSL_HASH_MAP_H */ diff --git a/include/std_gc/hash_map.h b/include/std_gc/hash_map.h new file mode 100644 index 0000000..e67dd75 --- /dev/null +++ b/include/std_gc/hash_map.h @@ -0,0 +1,29 @@ +#ifndef TSL_HASH_MAP_H +#define TSL_HASH_MAP_H + +#include "../value.h" +#include <stdint.h> + +/* TODO: Optimize small hash map by using the members of the struct instead of allocating on heap */ +typedef struct { + void *buckets_data; /* value=TslHashMapNode */ + size_t buckets_size; + size_t buckets_capacity; +} TslHashMap; + +void tsl_hash_map_init(TslHashMap *self); + +int tsl_hash_map_insert(TslHashMap *self, const TslValue *key, TslValue *value); +/* + Get a reference to the value by key @key, or NULL if it doesn't exist. + The returned pointer is only valid until until reallocation after after insert inserting a new value into the hash map. +*/ +TslValue* tsl_hash_map_get(TslHashMap *self, const TslValue *key); +/* + Get a reference to the value by key @key, or insert a new value for the key (a TslValue). + Returns NULL on failure. + The returned pointer is only valid until until reallocation after after insert inserting a new value into the hash map. +*/ +TslValue* tsl_hash_map_get_or_create(TslHashMap *self, const TslValue *key); + +#endif /* TSL_HASH_MAP_H */ diff --git a/include/std/string_view.h b/include/string_view.h index 4f9552a..1209c1a 100644 --- a/include/std/string_view.h +++ b/include/string_view.h @@ -4,7 +4,7 @@ #include <stddef.h> typedef struct { - const char *data; + char *data; size_t size; } TslStringView; diff --git a/include/tokenizer.h b/include/tokenizer.h index 2e7d42b..875396a 100644 --- a/include/tokenizer.h +++ b/include/tokenizer.h @@ -1,7 +1,7 @@ #ifndef TSL_TOKENIZER_H #define TSL_TOKENIZER_H -#include "std/string_view.h" +#include "string_view.h" typedef enum { TSL_TOKEN_END_OF_FILE, @@ -38,7 +38,7 @@ typedef struct { } TslTokenizerPeek; typedef struct { - const char *code; + char *code; size_t code_size; size_t code_index; size_t prev_code_index; @@ -52,7 +52,7 @@ typedef struct { char arithmetic_symbol; } TslTokenizer; -void tsl_tokenizer_init(TslTokenizer *self, const char *code, size_t code_size); +void tsl_tokenizer_init(TslTokenizer *self, char *code, size_t code_size); TslToken tsl_tokenizer_next(TslTokenizer *self); int tsl_tokenizer_accept(TslTokenizer *self, TslToken expected_token); diff --git a/include/value.h b/include/value.h index 58e57fe..e26cc4a 100644 --- a/include/value.h +++ b/include/value.h @@ -3,11 +3,13 @@ #include <stddef.h> #include <stdint.h> +#include "string_view.h" typedef enum { TSL_TYPE_NULL, TSL_TYPE_NUMBER, TSL_TYPE_STRING, + TSL_TYPE_STRING_VIEW, TSL_TYPE_BOOL, TSL_TYPE_LIST, TSL_TYPE_MAP, @@ -23,13 +25,14 @@ typedef enum { } TslBool; typedef struct { - char *data; + uint8_t *data; size_t size; } TslString; /* TODO: Implement this */ typedef struct { void *data; + size_t size; } TslList; /* TODO: Implement this */ @@ -41,6 +44,7 @@ typedef struct { union { TslNumber number; TslString *string; + TslStringView string_view; TslBool boolean; TslList *list; TslMap *map; @@ -49,4 +53,8 @@ typedef struct { uint8_t type; } TslValue; +uint64_t tsl_value_hash(const TslValue *value); +/* Returns 1 if equal, otherwise returns 0 */ +int tsl_value_equals(const TslValue *lhs, const TslValue *rhs); + #endif /* TSL_VALUE_H */ diff --git a/src/command.c b/src/command.c new file mode 100644 index 0000000..c413502 --- /dev/null +++ b/src/command.c @@ -0,0 +1,90 @@ +#include "../include/command.h" +#include <unistd.h> +#include <sys/wait.h> +#include <errno.h> +#include <string.h> +#include <stdio.h> +#include <assert.h> + +#define READ_END 0 +#define WRITE_END 1 + +int tsl_command_exec(char **args, ProgramOutputCallback output_callback, void *userdata) { + int fd[2]; + pid_t pid; + + /* 1 arguments */ + if(args[0] == NULL) + return -1; + + if(pipe(fd) == -1) { + perror("Failed to open pipe"); + return -2; + } + + pid = fork(); + if(pid == -1) { + perror("Failed to fork"); + return -3; + } else if(pid == 0) { /* child */ + dup2(fd[WRITE_END], STDOUT_FILENO); + close(fd[READ_END]); + close(fd[WRITE_END]); + + execvp(args[0], args); + return 0; + } else { /* parent */ + int result = 0; + int status; + char buffer[2048]; + int exit_status; + + close(fd[WRITE_END]); + for(;;) { + ssize_t bytes_read = read(fd[READ_END], buffer, sizeof(buffer) - 1); + if(bytes_read == 0) { + break; + } else if(bytes_read == -1) { + int err = errno; + fprintf(stderr, "Failed to read from pipe to program %s, error: %s\n", args[0], strerror(err)); + result = -err; + goto cleanup; + } + + buffer[bytes_read] = '\0'; + if(output_callback && !output_callback(buffer, bytes_read, userdata)) + break; + } + + if(waitpid(pid, &status, 0) == -1) { + perror("waitpid failed"); + result = -5; + goto cleanup; + } + + if(!WIFEXITED(status)) { + result = -4; + goto cleanup; + } + + exit_status = WEXITSTATUS(status); + if(exit_status != 0) { + char **arg = args; + fprintf(stderr, "Failed to execute program ("); + + while(*arg) { + if(arg != args) + fputc(' ', stderr); + fprintf(stderr, "'%s'", *arg); + ++arg; + } + fprintf(stderr, "), exit status %d\n", exit_status); + result = -exit_status; + goto cleanup; + } + + cleanup: + close(fd[READ_END]); + return result; + } +} @@ -56,6 +56,7 @@ int main(int argc, char **argv) { char *file_content; size_t filesize; TslProgram program; + int result = 0; if(argc != 2) { usage(); @@ -64,16 +65,22 @@ int main(int argc, char **argv) { file_content = file_get_content(argv[1], &filesize); if(!file_content) - return 1; + return 2; tsl_program_init(&program); - if(!tsl_parse(file_content, filesize, &program)) - return 2; - if(!tsl_program_run(&program)) - return 3; + if(!tsl_parse(file_content, filesize, &program)) { + result = 3; + goto cleanup; + } + + if(!tsl_program_run(&program)) { + result = 4; + goto cleanup; + } + cleanup: free(file_content); tsl_program_deinit(&program); - return 0; + return result; } diff --git a/src/parser.c b/src/parser.c index 098962a..a6f340f 100644 --- a/src/parser.c +++ b/src/parser.c @@ -26,7 +26,7 @@ static int tsl_parser_get_current_function_index(TslParser *self) { static TslParseResult tsl_parser_parse_rhs(TslParser *self); static TslParseResult tsl_parser_parse_expressions(TslParser *self, TslToken end_token); -static int tsl_parser_init(TslParser *self, const char *code, size_t code_size) { +static int tsl_parser_init(TslParser *self, char *code, size_t code_size) { TslBytecode bytecode_writer; int result1 = 1; int result2 = 1; @@ -216,7 +216,6 @@ static TslParseResult tsl_parser_parse_func_call(TslParser *self, int *num_args) } } -/* TODO: Do not allow empty command */ /* TODO: Allow command inside another command */ /* COMMAND = '(' TSL_COMMAND_TOKEN_ARG* ')' */ static TslParseResult tsl_parser_parse_command(TslParser *self, int *num_args) { @@ -328,8 +327,12 @@ TslParseResult tsl_parser_parse_rhs(TslParser *self) { } case TSL_TOKEN_DOLLAR_SIGN: { int num_args; - return tsl_parser_parse_command(self, &num_args) && - tsl_bytecode_add_ins1(get_function_bytecode(self), TSL_OPCODE_CALLC, num_args); + return_if_error(tsl_parser_parse_command(self, &num_args)) + if(num_args == 0) { + fprintf(stderr, "Error: Command can't be empty\n"); + return TSL_PARSE_RESULT_ERR; + } + return tsl_bytecode_add_ins1(get_function_bytecode(self), TSL_OPCODE_CALLC, num_args); } default: fprintf(stderr, "Error: Expected variable, number, bool, null, map, list, function or command, got TODO (%d) (line: %d)\n", token, tsl_tokenizer_get_line_by_index(&self->tokenizer, self->tokenizer.prev_code_index)); @@ -365,7 +368,7 @@ TslParseResult tsl_parser_parse_expressions(TslParser *self, TslToken end_token) return TSL_PARSE_RESULT_OK; } -TslParseResult tsl_parse(const char *code, size_t code_size, TslProgram *program_output) { +TslParseResult tsl_parse(char *code, size_t code_size, TslProgram *program_output) { TslParseResult result; TslParser parser; diff --git a/src/program.c b/src/program.c index 3ec6f85..0a0027b 100644 --- a/src/program.c +++ b/src/program.c @@ -1,12 +1,26 @@ #include "../include/program.h" #include "../include/bytecode.h" +#include "../include/command.h" +#ifdef DEBUG +#define GC_DEBUG +#endif +#include <gc.h> #include <stdio.h> #include <assert.h> +#include <stdlib.h> +#include <string.h> + +#define return_if_error(expr) \ + { \ + if(!(expr)) { \ + result = TSL_PROGRAM_RESULT_ERR; \ + goto cleanup; \ + } \ + } void tsl_program_init(TslProgram *self) { + GC_INIT(); /* TODO: Remove this */ tsl_buffer_init(&self->function_bytecode_list); - tsl_hash_map_init(&self->variables); - self->stack_index = 0; } void tsl_program_deinit(TslProgram *self) { @@ -17,34 +31,31 @@ void tsl_program_deinit(TslProgram *self) { ++bytecode_writer; } tsl_buffer_deinit(&self->function_bytecode_list); - tsl_hash_map_deinit(&self->variables); } -static uint64_t hash_string_view(const void *data, size_t size) { - uint64_t result = 0xdec05eba; - const uint8_t *p = data; - while(size) { - result = ((result << 5) + result) + *p; - ++p; - --size; - } - return result; +static int program_output_temp(char *data, int size, void *userdata) { + (void)size; + (void)userdata; + fputs(data, stdout); + return 1; } TslProgramResult tsl_program_run(TslProgram *self) { - #define push(value, type_enum, value_field) \ - do { \ - if(self->stack_index == TSL_STACK_MAX_SIZE) \ - return TSL_PROGRAM_RESULT_ERR; \ - self->stack_values[self->stack_index].data.value_field = (value); \ - self->stack_values[self->stack_index].type = (type_enum); \ - ++self->stack_index; \ - } while(0) - + 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); - printf("#############################\n"); + + 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"); + /* TODO: Verify if these don't cause unaligned memory access on non-x86 platforms */ while(instruction != instruction_end) { TslOpcode opcode = *(TslOpcode*)instruction; @@ -54,46 +65,84 @@ TslProgramResult tsl_program_run(TslProgram *self) { TslInstructionType4 *instruction_type4 = (TslInstructionType4*)instruction; switch(opcode) { case TSL_OPCODE_LOADN: { + TslStackValue stack_value; + stack_value.data.number = instruction_type2->value; + stack_value.type = TSL_STACK_VALUE_TYPE_NUMBER; + return_if_error(tsl_buffer_append(&self->stack_values, &stack_value, sizeof(stack_value))); printf("loadn %f\n", instruction_type2->value); - push(instruction_type2->value, TSL_TYPE_NUMBER, number); instruction += sizeof(TslInstructionType2); break; } case TSL_OPCODE_LOADB: { + TslStackValue stack_value; + stack_value.data.boolean = instruction_type3->value; + stack_value.type = TSL_STACK_VALUE_TYPE_BOOL; + return_if_error(tsl_buffer_append(&self->stack_values, &stack_value, sizeof(stack_value))); printf("loadb %s\n", instruction_type3->value ? "true" : "false"); - push(instruction_type3->value, TSL_TYPE_BOOL, boolean); instruction += sizeof(TslInstructionType3); break; } case TSL_OPCODE_LOADS: { + #if 0 + TslString *str = GC_MALLOC(sizeof(TslString)); + char *str_data = GC_MALLOC_ATOMIC(instruction_type4->value.size + 1); + if(!str) { + fprintf(stderr, "Error: out of memory\n"); + return TSL_PROGRAM_RESULT_ERR; + } + if(!str_data) { + fprintf(stderr, "Error: out of memory\n"); + return TSL_PROGRAM_RESULT_ERR; + } + memcpy(str->data, instruction_type4->value.data, instruction_type4->value.size); + str->data[instruction_type4->value.size] = '\0'; + str->size = instruction_type4->value.size; + #endif + TslStackValue stack_value; + stack_value.data.str = instruction_type4->value; + stack_value.type = TSL_STACK_VALUE_TYPE_STRING; + return_if_error(tsl_buffer_append(&self->stack_values, &stack_value, sizeof(stack_value))); printf("loads \"%.*s\"\n", (int)instruction_type4->value.size, instruction_type4->value.data); instruction += sizeof(TslInstructionType4); break; } case TSL_OPCODE_LOADF: { + TslStackValue stack_value; + stack_value.data.integer = instruction_type1->value; + stack_value.type = TSL_STACK_VALUE_TYPE_FUNCTION; + return_if_error(tsl_buffer_append(&self->stack_values, &stack_value, sizeof(stack_value))); printf("loadf %d\n", instruction_type1->value); instruction += sizeof(TslInstructionType1); break; } case TSL_OPCODE_LOADV: { + TslStackValue stack_value; + stack_value.data.str = instruction_type4->value; + stack_value.type = TSL_STACK_VALUE_TYPE_VARIABLE; + return_if_error(tsl_buffer_append(&self->stack_values, &stack_value, sizeof(stack_value))); printf("loadv \"%.*s\"\n", (int)instruction_type4->value.size, instruction_type4->value.data); instruction += sizeof(TslInstructionType4); break; } case TSL_OPCODE_LOADNULL: { + TslStackValue stack_value; + stack_value.type = TSL_STACK_VALUE_TYPE_NULL; + return_if_error(tsl_buffer_append(&self->stack_values, &stack_value, sizeof(stack_value))); printf("loadnull\n"); instruction += sizeof(TslInstructionType5); break; } case TSL_OPCODE_SETV: { - TslValue stack_value; - TslValue *value; + TslValue map_key; + TslValue *stack_value; + TslValue *map_value; + map_key.type = TSL_TYPE_STRING_VIEW; + map_key.data.string_view = instruction_type4->value; + stack_value = tsl_buffer_pop(&self->stack_values, sizeof(TslStackValue)); + map_value = tsl_hash_map_get_or_create(self->variables, &map_key); + return_if_error(map_value); + *map_value = *stack_value; printf("setv \"%.*s\"\n", (int)instruction_type4->value.size, instruction_type4->value.data); - assert(self->stack_index > 0); - stack_value = self->stack_values[--self->stack_index]; - if(!tsl_hash_map_get_or_create(&self->variables, &instruction_type4->value, sizeof(TslValue), hash_string_view, (void**)&value)) - return TSL_PROGRAM_RESULT_ERR; - *value = stack_value; instruction += sizeof(TslInstructionType4); break; } @@ -138,16 +187,65 @@ TslProgramResult tsl_program_run(TslProgram *self) { break; } case TSL_OPCODE_LOADCA: { + TslStackValue stack_value; + stack_value.data.str = instruction_type4->value; + stack_value.type = TSL_STACK_VALUE_TYPE_COMMAND_ARG; + return_if_error(tsl_buffer_append(&self->stack_values, &stack_value, sizeof(stack_value))); printf("loadca \"%.*s\"\n", (int)instruction_type4->value.size, instruction_type4->value.data); instruction += sizeof(TslInstructionType4); break; } case TSL_OPCODE_CALLC: { + TslStackValue *args_begin = tsl_buffer_pop(&self->stack_values, sizeof(TslStackValue) * instruction_type1->value); + TslStackValue *args_end = args_begin + instruction_type1->value; + TslStackValue *args = args_begin; + char *command_args[255]; + char temp_null_save[255]; + size_t arg_index = 0; + assert(instruction_type1->value > 0); + { + /* TODO: Support infinite amount of args */ + assert(args_end - args_begin < 255); + printf("Running command: "); + while(args != args_end) { + assert(args->type == TSL_STACK_VALUE_TYPE_COMMAND_ARG); + temp_null_save[arg_index] = args->data.str.data[args->data.str.size]; + args->data.str.data[args->data.str.size] = '\0'; + + command_args[arg_index] = args->data.str.data; + printf("%s ", command_args[arg_index]); + ++arg_index; + ++args; + } + command_args[arg_index] = NULL; + printf("\n"); + } + + tsl_command_exec(command_args, program_output_temp, NULL); + + { + args = args_begin; + arg_index = 0; + /* TODO: Support infinite amount of args */ + while(args != args_end) { + args->data.str.data[args->data.str.size] = temp_null_save[arg_index]; + ++arg_index; + ++args; + } + command_args[arg_index] = NULL; + } + printf("callc %d\n", instruction_type1->value); instruction += sizeof(TslInstructionType1); break; } } } - return TSL_PROGRAM_RESULT_OK; + + cleanup: + assert(self->stack_values.size == 0); /* All push instructions (mostly load) haven't been handled yet. This is a bug in tsl */ + tsl_buffer_deinit(&self->stack_values); + GC_FREE(self->variables); /* Free the root object, resulting in all objects in the program being free'd */ + self->variables = NULL; + return result; } diff --git a/src/std/buffer.c b/src/std/buffer.c index 343173a..b0099bb 100644 --- a/src/std/buffer.c +++ b/src/std/buffer.c @@ -18,24 +18,25 @@ void tsl_buffer_deinit(TslBuffer *self) { static int tsl_buffer_ensure_capacity(TslBuffer *self, size_t new_size) { void *new_ptr; + size_t new_capacity = self->capacity; if(new_size <= self->capacity) return 1; + + if(new_capacity == 0) + new_capacity = 8; - if(self->capacity != 0) { - size_t new_capacity = self->capacity; - while(new_capacity < new_size) { - new_capacity <<= 1; - } - new_size = new_capacity; + while(new_capacity < new_size) { + new_capacity <<= 1; } - new_ptr = realloc(self->data, new_size); + + new_ptr = realloc(self->data, new_capacity); if(!new_ptr) { fprintf(stderr, "Error: buffer append failed. Reason: out of memory\n"); return 0; } self->data = new_ptr; - self->capacity = new_size; + self->capacity = new_capacity; return 1; } @@ -47,9 +48,15 @@ int tsl_buffer_append(TslBuffer *self, const void *data, size_t size) { return 1; } -void tsl_buffer_pop(TslBuffer *self, size_t size) { +void* tsl_buffer_pop(TslBuffer *self, size_t size) { assert(self->size >= size); self->size -= size; + return (char*)self->data + self->size; +} + +void* tsl_buffer_top(TslBuffer *self, size_t data_size) { + tsl_buffer_pop(self, data_size); + return (char*)self->data + self->size; } void* tsl_buffer_begin(TslBuffer *self) { diff --git a/src/std/hash_map.c b/src/std_gc/hash_map.c index 9d8b4c4..1960ffc 100644 --- a/src/std/hash_map.c +++ b/src/std_gc/hash_map.c @@ -1,81 +1,44 @@ -#include "../../include/std/hash_map.h" +#include "../../include/std_gc/hash_map.h" #include <assert.h> #include <string.h> -#include <stdlib.h> #include <stdio.h> +#include <gc.h> typedef struct TslHashMapNode TslHashMapNode; struct TslHashMapNode { - void *data; + uint64_t hash; + TslValue key; + TslValue value; TslHashMapNode *next; }; -static void hash_map_node_get(TslHashMapNode *self, uint64_t *hash, TslStringView *key, size_t *size, uint8_t **data) { - memcpy(hash, (uint8_t*)self->data, sizeof(uint64_t)); - memcpy(&key->size, (uint8_t*)self->data + sizeof(uint64_t), sizeof(key->size)); - key->data = (const char*)self->data + sizeof(uint64_t) + sizeof(key->size); - memcpy(size, (uint8_t*)self->data + sizeof(uint64_t) + sizeof(key->size) + key->size, sizeof(size_t)); - *data = (uint8_t*)self->data + sizeof(uint64_t) + sizeof(key->size) + key->size + sizeof(size_t); -} - void tsl_hash_map_init(TslHashMap *self) { self->buckets_data = NULL; self->buckets_size = 0; self->buckets_capacity = 0; } -void tsl_hash_map_deinit(TslHashMap *self) { - TslHashMapNode **bucket = (TslHashMapNode**)self->buckets_data; - TslHashMapNode **bucket_end = (TslHashMapNode**)((char*)self->buckets_data + self->buckets_capacity); - while(bucket != bucket_end) { - TslHashMapNode *node = *bucket; - while(node) { - TslHashMapNode *prev_node = node; - node = node->next; - free(prev_node->data); - free(prev_node); - } - ++bucket; - } - free(self->buckets_data); -} - /* TODO: Remove if (data) and if (output) */ -static int tsl_hash_map_append_bucket(TslHashMapNode **head_node, uint64_t hash, const TslStringView *key, size_t size, const void *data, void **output) { +static int tsl_hash_map_append_bucket(TslHashMapNode **head_node, uint64_t hash, const TslValue *key, TslValue *value, TslValue **output) { TslHashMapNode *next_node; - uint8_t *node_data = malloc(sizeof(hash) + sizeof(key->size) + key->size + sizeof(size) + size); if(output) *output = NULL; - if(!node_data) { - fprintf(stderr, "Error: hash map append failed. Reason: out of memory\n"); - return 0; - } - - next_node = malloc(sizeof(TslHashMapNode)); + next_node = GC_MALLOC(sizeof(TslHashMapNode)); if(!next_node) { - free(node_data); - fprintf(stderr, "Error: hash map append failed. Reason: out of memory\n"); + fprintf(stderr, "Error: hash map insert failed. Reason: out of memory\n"); return 0; } - memcpy(node_data, &hash, sizeof(hash)); - /* TODO: Instead of allocating space for the key, use the key data pointer and size directly. */ - memcpy(node_data + sizeof(hash), &key->size, sizeof(key->size)); - memcpy(node_data + sizeof(hash) + sizeof(key->size), key->data, key->size); - /* - TODO: Instead of allocating space for the data, allow the user to pass a pointer in the insert - method and use that directly. - */ - memcpy(node_data + sizeof(hash) + sizeof(key->size) + key->size, &size, sizeof(size)); - if(data) - memcpy(node_data + sizeof(hash) + sizeof(key->size) + key->size + sizeof(size), data, size); + next_node->hash = hash; + next_node->key = *key; + if(value) + next_node->value = *value; if(output) - *output = node_data + sizeof(hash) + sizeof(key->size) + key->size + sizeof(size); + *output = &next_node->value; - next_node->data = node_data; if(*head_node) { next_node->next = (*head_node)->next; (*head_node)->next = next_node; @@ -83,6 +46,7 @@ static int tsl_hash_map_append_bucket(TslHashMapNode **head_node, uint64_t hash, next_node->next = NULL; *head_node = next_node; } + return 1; } @@ -100,15 +64,9 @@ static void tsl_hash_map_reorder_nodes(TslHashMap *self, size_t old_capacity) { TslHashMapNode *node = *bucket; TslHashMapNode *prev_node = node; /* Set to node for optimization reason, where prev_node->next = node->next; which becomes no-op */ int all_nodes_moved = 1; + while(node) { - uint64_t hash; - TslStringView key; - size_t size; - uint8_t *data; - size_t index; - hash_map_node_get(node, &hash, &key, &size, &data); - - index = tsl_hash_map_get_index(self, hash); + size_t index = tsl_hash_map_get_index(self, node->hash); if(index != bucket_index) { TslHashMapNode **new_bucket = (TslHashMapNode**)self->buckets_data + index; prev_node->next = node->next; @@ -151,7 +109,7 @@ static int tsl_hash_map_ensure_bucket_capacity_for_one_new_item(TslHashMap *self new_capacity <<= 1; } - new_ptr = realloc(self->buckets_data, new_capacity); + new_ptr = GC_REALLOC(self->buckets_data, new_capacity); if(!new_ptr) { fprintf(stderr, "Error: hash map realloc failed. Reason: out of memory\n"); return 0; @@ -160,6 +118,7 @@ static int tsl_hash_map_ensure_bucket_capacity_for_one_new_item(TslHashMap *self self->buckets_data = new_ptr; self->buckets_capacity = new_capacity; { + /* TODO: Remove this. This is not needed since GC_REALLOC sets the data to 0 */ TslHashMapNode **bucket = (TslHashMapNode**)((char*)self->buckets_data + old_capacity); TslHashMapNode **bucket_end = (TslHashMapNode**)((char*)self->buckets_data + new_capacity); while(bucket != bucket_end) { @@ -175,73 +134,61 @@ static int tsl_hash_map_ensure_bucket_capacity_for_one_new_item(TslHashMap *self return 1; } -int tsl_hash_map_insert(TslHashMap *self, const TslStringView *key, const void *data, size_t size, TslHashFunc hash_func) { - uint64_t hash = hash_func(key->data, key->size); +int tsl_hash_map_insert(TslHashMap *self, const TslValue *key, TslValue *value) { + uint64_t hash = tsl_value_hash(key); size_t index; TslHashMapNode **bucket; - assert(!tsl_hash_map_get(self, key, hash_func)); - assert(size > 0); + assert(!tsl_hash_map_get(self, key)); if(!tsl_hash_map_ensure_bucket_capacity_for_one_new_item(self)) return 0; index = tsl_hash_map_get_index(self, hash); bucket = (TslHashMapNode**)self->buckets_data + index; - return tsl_hash_map_append_bucket(bucket, hash, key, size, data, NULL); + return tsl_hash_map_append_bucket(bucket, hash, key, value, NULL); } -void* tsl_hash_map_get(TslHashMap *self, const TslStringView *key, TslHashFunc hash_func) { +TslValue* tsl_hash_map_get(TslHashMap *self, const TslValue *key) { uint64_t hash; size_t index; TslHashMapNode *node; if(self->buckets_capacity == 0) return NULL; - hash = hash_func(key->data, key->size); + hash = tsl_value_hash(key); index = tsl_hash_map_get_index(self, hash); node = *((TslHashMapNode**)self->buckets_data + index); while(node) { - uint64_t node_hash; - TslStringView node_key; - size_t node_size; - uint8_t *node_data; - hash_map_node_get(node, &node_hash, &node_key, &node_size, &node_data); - if(hash == node_hash && key->size == node_key.size && memcmp(key->data, node_key.data, node_key.size) == 0) - return node_data; + if(hash == node->hash && tsl_value_equals(key, &node->key)) + return &node->value; node = node->next; } return NULL; } -int tsl_hash_map_get_or_create(TslHashMap *self, const TslStringView *key, size_t size, TslHashFunc hash_func, void **output) { +TslValue* tsl_hash_map_get_or_create(TslHashMap *self, const TslValue *key) { uint64_t hash; size_t index; TslHashMapNode **bucket; TslHashMapNode *node; - assert(size > 0); + TslValue *value; if(self->buckets_capacity == 0) { - if(!tsl_hash_map_ensure_bucket_capacity_for_one_new_item(self)) { - *output = NULL; - return 0; - } + if(!tsl_hash_map_ensure_bucket_capacity_for_one_new_item(self)) + return NULL; } - hash = hash_func(key->data, key->size); + hash = tsl_value_hash(key); index = tsl_hash_map_get_index(self, hash); bucket = (TslHashMapNode**)self->buckets_data + index; node = *bucket; while(node) { - uint64_t node_hash; - TslStringView node_key; - size_t node_size; - uint8_t *node_data; - hash_map_node_get(node, &node_hash, &node_key, &node_size, &node_data); - if(hash == node_hash && key->size == node_key.size && memcmp(key->data, node_key.data, node_key.size) == 0) { - *output = node_data; - return 1; - } + if(hash == node->hash && tsl_value_equals(key, &node->key)) + return &node->value; node = node->next; } - return tsl_hash_map_append_bucket(bucket, hash, key, size, NULL, output); + if(!tsl_hash_map_append_bucket(bucket, hash, key, NULL, &value)) + return NULL; + + return value; } diff --git a/src/tokenizer.c b/src/tokenizer.c index 02592a9..a990052 100644 --- a/src/tokenizer.c +++ b/src/tokenizer.c @@ -4,7 +4,7 @@ #include <assert.h> #include <stdint.h> -void tsl_tokenizer_init(TslTokenizer *self, const char *code, size_t code_size) { +void tsl_tokenizer_init(TslTokenizer *self, char *code, size_t code_size) { self->code = code; self->code_size = code_size; self->code_index = 0; diff --git a/src/value.c b/src/value.c new file mode 100644 index 0000000..690527f --- /dev/null +++ b/src/value.c @@ -0,0 +1,45 @@ +#include "../include/value.h" +#include <string.h> + +static uint64_t hash_range(const uint8_t *data, size_t size) { + uint64_t result = 0xdec05eba; + while(size) { + result = ((result << 5) + result) + *data; + ++data; + --size; + } + return result; +} + +uint64_t tsl_value_hash(const TslValue *key) { + switch(key->type) { + case TSL_TYPE_NULL: + return 0; + case TSL_TYPE_NUMBER: + return *(uint64_t*)&key->data.number; + case TSL_TYPE_STRING: + return hash_range(key->data.string->data, key->data.string->size); + case TSL_TYPE_BOOL: + return key->data.boolean; + case TSL_TYPE_LIST: + return (uint64_t)key->data.list; + case TSL_TYPE_MAP: + return (uint64_t)key->data.map; + case TSL_TYPE_USERDATA: + return (uint64_t)key->data.userdata; + } + return 0; +} + +int tsl_value_equals(const TslValue *lhs, const TslValue *rhs) { + if(lhs->type == rhs->type) { + if(lhs->type == TSL_TYPE_STRING) { + return lhs->data.string->size == rhs->data.string->size + && memcmp(lhs->data.string->data, rhs->data.string->data, lhs->data.string->size) == 0; + } else { + return *(uint64_t*)&lhs->data == *(uint64_t*)&rhs->data; + } + } else { + return 0; + } +} |