aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordec05eba <dec05eba@protonmail.com>2020-01-24 09:11:53 +0100
committerdec05eba <dec05eba@protonmail.com>2020-01-24 09:11:53 +0100
commit1dd53ce54c2008e3a11a636a496853cf6f9a5d65 (patch)
tree73f8ff8d048c8b1e4c6cf7acfd3e229650d044d5
parent26f8fbc2c657ecffc874410691dd3fc83ba11131 (diff)
Convert hash map to gc, implement more instructions and call command
-rw-r--r--Makefile14
-rw-r--r--README.md2
-rw-r--r--example.tsl3
-rw-r--r--include/bytecode.h2
-rw-r--r--include/command.h12
-rw-r--r--include/parser.h2
-rw-r--r--include/program.h29
-rw-r--r--include/std/buffer.h9
-rw-r--r--include/std/hash_map.h25
-rw-r--r--include/std_gc/hash_map.h29
-rw-r--r--include/string_view.h (renamed from include/std/string_view.h)2
-rw-r--r--include/tokenizer.h6
-rw-r--r--include/value.h10
-rw-r--r--src/command.c90
-rw-r--r--src/main.c19
-rw-r--r--src/parser.c13
-rw-r--r--src/program.c162
-rw-r--r--src/std/buffer.c25
-rw-r--r--src/std_gc/hash_map.c (renamed from src/std/hash_map.c)127
-rw-r--r--src/tokenizer.c2
-rw-r--r--src/value.c45
21 files changed, 440 insertions, 188 deletions
diff --git a/Makefile b/Makefile
index 16e7513..5e14b47 100644
--- a/Makefile
+++ b/Makefile
@@ -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)
diff --git a/README.md b/README.md
index 3077b8f..072cc5c 100644
--- a/README.md
+++ b/README.md
@@ -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;
+ }
+}
diff --git a/src/main.c b/src/main.c
index 2166ff5..e36d912 100644
--- a/src/main.c
+++ b/src/main.c
@@ -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;
+ }
+}