From 5a5a37e3f8f49edfad0df570a195aa429c5ed548 Mon Sep 17 00:00:00 2001 From: Avinal Kumar Date: Tue, 3 Aug 2021 22:27:49 +0530 Subject: [PATCH] add BASIC interpreter Signed-off-by: Avinal Kumar --- libs/basic/include/basic/arch.h | 28 + libs/basic/include/basic/array.h | 28 + libs/basic/include/basic/callback.h | 19 + libs/basic/include/basic/dictionary.h | 25 + libs/basic/include/basic/error.h | 14 + libs/basic/include/basic/hexdump.h | 13 + libs/basic/include/basic/io.h | 18 + libs/basic/include/basic/kbhit.h | 13 + libs/basic/include/basic/lines.h | 42 + libs/basic/include/basic/parser.h | 87 ++ libs/basic/include/basic/tokenizer.h | 70 + libs/basic/include/basic/variables.h | 41 + libs/basic/src/arch.c | 102 ++ libs/basic/src/array.c | 56 + libs/basic/src/dictionary.c | 163 ++ libs/basic/src/error.c | 37 + libs/basic/src/hexdump.c | 42 + libs/basic/src/io.c | 49 + libs/basic/src/kbhit.c | 30 + libs/basic/src/lines.c | 263 ++++ libs/basic/src/parser.c | 2071 +++++++++++++++++++++++++ libs/basic/src/tokenizer.c | 222 +++ libs/basic/src/variables.c | 337 ++++ 23 files changed, 3770 insertions(+) create mode 100644 libs/basic/include/basic/arch.h create mode 100644 libs/basic/include/basic/array.h create mode 100644 libs/basic/include/basic/callback.h create mode 100644 libs/basic/include/basic/dictionary.h create mode 100644 libs/basic/include/basic/error.h create mode 100644 libs/basic/include/basic/hexdump.h create mode 100644 libs/basic/include/basic/io.h create mode 100644 libs/basic/include/basic/kbhit.h create mode 100644 libs/basic/include/basic/lines.h create mode 100644 libs/basic/include/basic/parser.h create mode 100644 libs/basic/include/basic/tokenizer.h create mode 100644 libs/basic/include/basic/variables.h create mode 100644 libs/basic/src/arch.c create mode 100644 libs/basic/src/array.c create mode 100644 libs/basic/src/dictionary.c create mode 100644 libs/basic/src/error.c create mode 100644 libs/basic/src/hexdump.c create mode 100644 libs/basic/src/io.c create mode 100644 libs/basic/src/kbhit.c create mode 100644 libs/basic/src/lines.c create mode 100644 libs/basic/src/parser.c create mode 100644 libs/basic/src/tokenizer.c create mode 100644 libs/basic/src/variables.c diff --git a/libs/basic/include/basic/arch.h b/libs/basic/include/basic/arch.h new file mode 100644 index 0000000..5165310 --- /dev/null +++ b/libs/basic/include/basic/arch.h @@ -0,0 +1,28 @@ +/** + * SPDX-License-Identifier: MIT + * SPDX-FileCopyrightText: 2015-2016 Johan Van den Brande + * + * @file arch.h + */ + +#ifndef BASIC_ARCH_H_ +#define BASIC_ARCH_H_ + +#include +#include +#include + +int arch_init(void); + +typedef void (*arch_load_out_cb)(char* line, void* context); +int arch_load(char* filename, arch_load_out_cb cb, void* context); + +typedef uint16_t (*arch_save_cb)(char** line, void* context); +int arch_save(char* filename, arch_save_cb cb, void* context); + +typedef void (*arch_dir_out_cb)(char* name, size_t size, bool label, void* context); +int arch_dir(arch_dir_out_cb cb, void* context); + +int arch_delete(char* filename); + +#endif // BASIC_ARCH_H_ \ No newline at end of file diff --git a/libs/basic/include/basic/array.h b/libs/basic/include/basic/array.h new file mode 100644 index 0000000..ba566ea --- /dev/null +++ b/libs/basic/include/basic/array.h @@ -0,0 +1,28 @@ +/** + * SPDX-License-Identifier: MIT + * SPDX-FileCopyrightText: 2015-2016 Johan Van den Brande + * + * @file array.h + */ + +#ifndef BASIC_ARRAY_H_ +#define BASIC_ARRAY_H_ + +#include +typedef struct array array; + +array* array_new(size_t element_size); + +array* array_alloc(array* array, size_t size); + +void array_destroy(array* array); + +void* array_push(array* array, void* value); + +void* array_get(array* array, size_t index); + +void* array_set(array* array, size_t index, void* value); + +size_t array_size(array* array); + +#endif // BASIC_ARRAY_H_ \ No newline at end of file diff --git a/libs/basic/include/basic/callback.h b/libs/basic/include/basic/callback.h new file mode 100644 index 0000000..4e40ee6 --- /dev/null +++ b/libs/basic/include/basic/callback.h @@ -0,0 +1,19 @@ +/** + * SPDX-License-Identifier: MIT + * SPDX-FileCopyrightText: 2015-2016 Johan Van den Brande + * + * @file callback.h + */ + +#ifndef BASIC_CALLBACK_H_ +#define BASIC_CALLBACK_H_ + +#include + +typedef struct error { + int error; +}; + +typedef bool (*callback)(error err, void* data); + +#endif // BASIC_CALLBACK_H_ \ No newline at end of file diff --git a/libs/basic/include/basic/dictionary.h b/libs/basic/include/basic/dictionary.h new file mode 100644 index 0000000..495455a --- /dev/null +++ b/libs/basic/include/basic/dictionary.h @@ -0,0 +1,25 @@ +/** + * SPDX-License-Identifier: MIT + * SPDX-FileCopyrightText: 2015-2016 Johan Van den Brande + * + * @file dictionary.h + */ + +#ifndef BASIC_DICTIONARY_H_ +#define BASIC_DICTIONARY_H_ + +#include + +typedef struct dictionary dictionary; + +typedef void (*dictionary_each_cb)(char* name, void* value, void* context); + +dictionary* dictionary_new(void); +void dictionary_destroy(dictionary* d, dictionary_each_cb cb); +void dictionary_put(dictionary* d, char* name, void* value); +bool dictionary_has(dictionary* d, char* name); +void* dictionary_get(dictionary* d, char* name); +void dictionary_each(dictionary* d, dictionary_each_cb cb, void* context); +void* dictionary_del(dictionary* d, char* name); + +#endif // BASIC_DICTIONARY_H_ diff --git a/libs/basic/include/basic/error.h b/libs/basic/include/basic/error.h new file mode 100644 index 0000000..d25ab33 --- /dev/null +++ b/libs/basic/include/basic/error.h @@ -0,0 +1,14 @@ +/** + * SPDX-License-Identifier: MIT + * SPDX-FileCopyrightText: 2015-2016 Johan Van den Brande + * + * @file error.h + */ + +#ifndef BASIC_ERROR_H_ +#define BASIC_ERROR_H_ + +extern char *last_error; +void error(const char *error_msg); + +#endif // BASIC_ERROR_H_ diff --git a/libs/basic/include/basic/hexdump.h b/libs/basic/include/basic/hexdump.h new file mode 100644 index 0000000..c4fb970 --- /dev/null +++ b/libs/basic/include/basic/hexdump.h @@ -0,0 +1,13 @@ +/** + * SPDX-License-Identifier: MIT + * SPDX-FileCopyrightText: 2015-2016 Johan Van den Brande + * + * @file hexdump.h + */ + +#ifndef BASIC_HEXDUMP_H_ +#define BASIC_HEXDUMP_H_ + +void hexdump(char *desc, void *addr, int len); + +#endif // BASIC_HEXDUMP_H_ diff --git a/libs/basic/include/basic/io.h b/libs/basic/include/basic/io.h new file mode 100644 index 0000000..4dd580e --- /dev/null +++ b/libs/basic/include/basic/io.h @@ -0,0 +1,18 @@ +/** + * SPDX-License-Identifier: MIT + * SPDX-FileCopyrightText: 2015-2016 Johan Van den Brande + * + * @file io.h + */ + +#ifndef BASIC_IO_H_ +#define BASIC_IO_H_ + +#include + +typedef int (*basic_putchar)(int ch); +typedef int (*basic_getchar)(void); +void basic_io_print(char* buffer); +char* basic_io_readline(char* prompt, char* buffer, size_t buffer_size); + +#endif // BASIC_IO_H_ diff --git a/libs/basic/include/basic/kbhit.h b/libs/basic/include/basic/kbhit.h new file mode 100644 index 0000000..fd6dbd3 --- /dev/null +++ b/libs/basic/include/basic/kbhit.h @@ -0,0 +1,13 @@ +/** + * SPDX-License-Identifier: MIT + * SPDX-FileCopyrightText: 2015-2016 Johan Van den Brande + * + * @file kbhit.h + */ + +#ifndef BASIC_KBHIT_H_ +#define BASIC_KBHIT_H_ + +int kbhit(void); + +#endif // BASIC_KBHIT_H_ diff --git a/libs/basic/include/basic/lines.h b/libs/basic/include/basic/lines.h new file mode 100644 index 0000000..917c998 --- /dev/null +++ b/libs/basic/include/basic/lines.h @@ -0,0 +1,42 @@ +/** + * SPDX-License-Identifier: MIT + * SPDX-FileCopyrightText: 2015-2016 Johan Van den Brande + * + * @file lines.h + */ + +#ifndef BASIC_LINES_H_ +#define BASIC_LINES_H_ +#include +#include +#include + +typedef struct line line; + +struct line { + uint16_t number; + uint8_t length; + char contents; +}; + +void lines_init(char* memory, size_t memory_size); + +size_t lines_memory_used(void); +size_t lines_memory_available(void); + +bool lines_delete(uint16_t number); + +bool lines_store(uint16_t number, char* contents); + +typedef void (*lines_list_cb)(uint16_t number, char* contents); + +void lines_list(uint16_t start, uint16_t end, lines_list_cb out); + +void lines_clear(void); + +char* lines_get_contents(uint16_t number); + +uint16_t lines_first(void); +uint16_t lines_next(uint16_t number); + +#endif // BASIC_LINES_H_ diff --git a/libs/basic/include/basic/parser.h b/libs/basic/include/basic/parser.h new file mode 100644 index 0000000..4df0b76 --- /dev/null +++ b/libs/basic/include/basic/parser.h @@ -0,0 +1,87 @@ +/** + * SPDX-License-Identifier: MIT + * SPDX-FileCopyrightText: 2015-2016 Johan Van den Brande + * + * @file parser.h + */ + +#ifndef BASIC_PARSER_H_ +#define BASIC_PARSER_H_ + +#include +#include +#include + +float evaluate(char* expression_string); + +void evaluate_print(char* line); + +void evaluate_print_func_param(char* func, float param); + +const char* evaluate_last_error(void); +void clear_last_error(void); + +void basic_init(size_t memory_size, size_t stack_size); + +void basic_destroy(void); + +void basic_register_io(basic_putchar putch, basic_getchar getch); + +char* basic_readline(char* prompt, char* buffer, size_t buffer_size); + +void basic_eval(char* line); +void basic_run(void); + +// For extensions + +typedef float (*function)(float number); + +typedef struct { + token _token; + function _function; +} token_to_function; + +typedef enum { + basic_function_type_keyword, + basic_function_type_op, + basic_function_type_numeric, + basic_function_type_string, + basic_function_type_print +} basic_function_type; + +typedef enum { kind_numeric, kind_string } kind; + +typedef union { + float number; + char* string; +} value; + +typedef struct { + kind kind; + bool empty; + bool mallocd; + value value; +} basic_type; + +typedef int (*function_0)(basic_type* rv); +typedef int (*function_1)(basic_type* v1, basic_type* rv); +typedef int (*function_2)(basic_type* v1, basic_type* v2, basic_type* rv); +typedef int (*function_3)(basic_type* v1, basic_type* v2, basic_type* v3, basic_type* rv); +typedef int (*function_4)(basic_type* v1, basic_type* v2, basic_type* v3, basic_type* v4, + basic_type* rv); +typedef int (*function_5)(basic_type* v1, basic_type* v2, basic_type* v3, basic_type* v4, + basic_type* v5, basic_type* rv); + +token register_token(char* token_name); +token register_function_0(basic_function_type type, char* keyword, function_0 function); +token register_function_1(basic_function_type type, char* keyword, function_1 function, kind v1); +token register_function_2(basic_function_type type, char* keyword, function_2 function, kind v1, + kind v2); +token register_function_3(basic_function_type type, char* keyword, function_3 function, kind v1, + kind v2, kind v3); +token register_function_4(basic_function_type type, char* keyword, function_4 function, kind v1, + kind v2, kind v3, kind v4); +token register_function_5(basic_function_type type, char* keyword, function_5 function, kind v1, + kind v2, kind v3, kind v4, kind v5); + +#endif // BASIC_PARSER_H_ diff --git a/libs/basic/include/basic/tokenizer.h b/libs/basic/include/basic/tokenizer.h new file mode 100644 index 0000000..d81495d --- /dev/null +++ b/libs/basic/include/basic/tokenizer.h @@ -0,0 +1,70 @@ +/** + * SPDX-License-Identifier: MIT + * SPDX-FileCopyrightText: 2015-2016 Johan Van den Brande + * + * @file tokenizer.h + */ + +#ifndef BASIC_TOKENIZER_H_ +#define BASIC_TOKENIZER_H_ + +#include + +#define tokenizer_string_length 64 +#define tokenizer_variable_length 8 + +typedef unsigned int token; +typedef char* token_name; +typedef char* token_keyword; + +typedef struct { + token token; + token_name name; +} token_entry; + +#define add_token(t, k) static token_entry _##t = {t, k}; + +typedef enum { + // Standard token types needed by the tokenizer + T_THE_END, + T_ERROR, + T_EOF, + T_NUMBER, + T_STRING, + T_VARIABLE_STRING, + T_VARIABLE_NUMBER, + + // Some tokens that are standard as well + T_PLUS, + T_MINUS, + T_MULTIPLY, + T_DIVIDE, + T_LEFT_BANANA, + T_RIGHT_BANANA, + T_COLON, + T_SEMICOLON, + T_EQUALS, + T_LESS, + T_GREATER, + T_COMMA, + TOKEN_TYPE_END +} token_type; + +void tokenizer_setup(void); +void tokenizer_init(char* input); +token tokenizer_get_next_token(void); + +float tokenizer_get_number(void); +char* tokenizer_get_string(void); +void tokenizer_get_variable_name(char* name); + +char* tokenizer_token_name(token); + +char* tokenizer_char_pointer(char* set); + +void tokenizer_add_tokens(token_entry* tokens); + +void tokenizer_register_token(token_entry* entry); +void tokenizer_free_registered_tokens(void); + +#endif // BASIC_TOKENIZER_H_ diff --git a/libs/basic/include/basic/variables.h b/libs/basic/include/basic/variables.h new file mode 100644 index 0000000..3810b2e --- /dev/null +++ b/libs/basic/include/basic/variables.h @@ -0,0 +1,41 @@ +/** + * SPDX-License-Identifier: MIT + * SPDX-FileCopyrightText: 2015-2016 Johan Van den Brande + * + * @file variable.h + */ + +#ifndef BASIC_VARIABLES_H_ +#define BASIC_VARIABLES_H_ + +#include + +typedef enum { variable_type_unknown, variable_type_numeric, variable_type_string } variable_type; + +typedef struct variable variable; + +bool variables_init(void); +void variables_destroy(void); + +variable* variable_get(char* name); + +char* variable_get_string(char* name); +float variable_get_numeric(char* name); + +variable* variable_set_string(char* name, char* value); +variable* variable_set_numeric(char* name, float value); + +variable_type variable_get_type(char* name); + +variable* variable_array_init(char* name, variable_type type, size_t dimensions, size_t* vector); +variable* variable_array_set_string(char* name, char* value, size_t* vector); +char* variable_array_get_string(char* name, size_t* vector); +variable* variable_array_set_numeric(char* name, float value, size_t* vector); +float variable_array_get_numeric(char* name, size_t* vector); + +typedef void (*variables_each_cb)(variable* var, void* context); +void variables_each(variables_each_cb each, void* context); + +void variable_dump(variable* var); + +#endif // BASIC_VARIABLES_H_ diff --git a/libs/basic/src/arch.c b/libs/basic/src/arch.c new file mode 100644 index 0000000..ca44624 --- /dev/null +++ b/libs/basic/src/arch.c @@ -0,0 +1,102 @@ +/** + * SPDX-License-Identifier: MIT + * SPDX-FileCopyrightText: 2015-2016 Johan Van den Brande + * + * @file arch.c + */ + +#define _GNU_SOURCE +#include "arch.h" + +#include +#include +#include +#include +#include + +int arch_init(void) { return 0; } + +static char* _get_path(void) { + static char* _path = NULL; + _path = getenv("BASIC_PATH"); + if (_path == NULL) { + _path = "."; + } + if (_path[strlen(_path) - 1] == '/') { + _path[strlen(_path) - 1] = '\0'; + } + return _path; +} + +int arch_load(char* name, arch_load_out_cb cb, void* context) { + char* filename; + asprintf(&filename, "%s/%s.bas", _get_path(), name); + FILE* fp = fopen(filename, "r"); + if (!fp) { + return 1; + } + char line[256]; + while (fgets(line, 256, fp) != NULL) { + cb(line, context); + } + fclose(fp); + free(filename); + return 0; +} + +int arch_save(char* name, arch_save_cb cb, void* context) { + char* line; + char* filename; + asprintf(&filename, "%s/%s.bas", _get_path(), name); + + FILE* fp = fopen(filename, "w"); + if (!fp) { + return 1; + } + for (;;) { + uint16_t number = cb(&line, context); + if (line == NULL) { + break; + } + fprintf(fp, "%d %s\n", number, line); + } + fclose(fp); + + free(filename); + + return 0; +} + +int arch_dir(arch_dir_out_cb cb, void* context) { + char out[256]; + snprintf(out, sizeof(out), "dir: %s", _get_path()); + cb(out, 0, true, context); + + struct stat stats; + struct dirent* ent; + DIR* dir; + dir = opendir(_get_path()); + while ((ent = readdir(dir)) != NULL) { + char* name = ent->d_name; + if (strlen(name) > 4) { + char* ext = name + strlen(name) - 4; + if (strncmp(ext, ".bas", 4) == 0) { + snprintf(out, sizeof(out), "%s/%s", _get_path(), name); + stat(out, &stats); + name[strlen(name) - 4] = '\0'; + cb(name, stats.st_size, false, context); + } + } + } + closedir(dir); + + return 0; +} + +int arch_delete(char* name) { + char* filename; + asprintf(&filename, "%s/%s.bas", _get_path(), name); + remove(filename); + free(filename); + return 0; +} diff --git a/libs/basic/src/array.c b/libs/basic/src/array.c new file mode 100644 index 0000000..3959a6c --- /dev/null +++ b/libs/basic/src/array.c @@ -0,0 +1,56 @@ +/** + * SPDX-License-Identifier: MIT + * SPDX-FileCopyrightText: 2015-2016 Johan Van den Brande + * + * @file array.c + */ + +#include "array.h" + +#include +#include +#include + +struct array { + size_t element_size; + size_t size; + char* ptr; +}; + +array* array_new(size_t element_size) { + array* a = malloc(sizeof(array)); + a->element_size = element_size; + a->size = 0; + a->ptr = NULL; + return a; +} + +array* array_alloc(array* array, size_t size) { + array->size = size; + array->ptr = realloc(array->ptr, array->element_size * array->size); + memset(array->ptr, 0, array->element_size * array->size); + return array; +} + +void array_destroy(array* array) { + free(array->ptr); + free(array); +} + +void* array_push(array* array, void* value) { + array->size++; + array->ptr = realloc(array->ptr, array->element_size * array->size); + void* element = array->ptr + array->element_size * (array->size - 1); + memcpy(element, value, array->element_size); + return element; +} + +void* array_get(array* array, size_t index) { return array->ptr + index * array->element_size; } + +void* array_set(array* array, size_t index, void* value) { + void* element = array_get(array, index); + memcpy(element, value, array->element_size); + return element; +} + +size_t array_size(array* array) { return array->size; } diff --git a/libs/basic/src/dictionary.c b/libs/basic/src/dictionary.c new file mode 100644 index 0000000..53ebb93 --- /dev/null +++ b/libs/basic/src/dictionary.c @@ -0,0 +1,163 @@ +/** + * SPDX-License-Identifier: MIT + * SPDX-FileCopyrightText: 2015-2016 Johan Van den Brande + * + * @file dictionary.c + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include + +typedef struct entry entry; +struct entry { + entry* next; + char* name; + void* value; +}; +#define HASHSIZE 13 + +struct dictionary { + entry* hashtab[HASHSIZE]; +}; + +static unsigned int hash(char* name) { + unsigned int hashval; + for (hashval = 0; *name != '\0'; name++) { + hashval = *name + 31 * hashval; + } + return hashval % HASHSIZE; +} + +static entry* _get(dictionary* d, char* name) { + entry* entry; + for (entry = d->hashtab[hash(name)]; entry != NULL; entry = entry->next) { + if (strcmp(name, entry->name) == 0) { + return entry; + } + } + return NULL; +} + +void* dictionary_get(dictionary* d, char* name) { + entry* entry = _get(d, name); + + if (entry) { + return entry->value; + } + + return NULL; +} + +bool dictionary_has(dictionary* d, char* name) { + entry* entry = _get(d, name); + + if (entry) { + return true; + } + + return false; +} + +void dictionary_put(dictionary* d, char* name, void* value) { + entry* element; + unsigned int hashval; + + element = _get(d, name); + + if (element == NULL) { + element = (entry*)malloc(sizeof(*element)); + if (element == NULL || (element->name = strdup(name)) == NULL) { + return; + } + hashval = hash(name); + element->next = d->hashtab[hashval]; + d->hashtab[hashval] = element; + } + element->value = value; +} + +void* dictionary_del(dictionary* d, char* name) { + entry* root = d->hashtab[hash(name)]; + + if (root == NULL) { + return NULL; + } + + if (strcmp(name, root->name) == 0) { + d->hashtab[hash(name)] = root->next; + void* value = root->value; + free(root->name); + free(root); + return value; + } + + entry* element = root; + while (element->next) { + entry* next = element->next; + if (strcmp(name, next->name) == 0) { + element->next = next->next; + void* value = next->value; + free(next->name); + free(next); + return value; + } + } + + return NULL; +} + +void dictionary_each(dictionary* d, dictionary_each_cb cb, void* context) { + entry* next_entry = NULL; + + if (!cb) { + return; + } + + for (size_t i = 0; i < HASHSIZE; i++) { + entry* entry = d->hashtab[i]; + while (entry) { + next_entry = entry->next; + cb(entry->name, entry->value, context); + entry = next_entry; + } + } +} + +dictionary* dictionary_new() { + dictionary* d = malloc(sizeof(dictionary)); + if (d == NULL) { + return NULL; + } + for (size_t i = 0; i < HASHSIZE; i++) { + d->hashtab[i] = NULL; + } + return d; +} + +typedef struct { + dictionary* d; + dictionary_each_cb cb; +} _free_s; + +static void destroy_cb_pass_1(char* name, void* value, void* context) { + _free_s* ctx = (_free_s*)context; + dictionary_each_cb free_cb = ctx->cb; + free_cb(name, value, NULL); +} + +static void destroy_cb_pass_2(char* name, void* value, void* context) { + _free_s* ctx = (_free_s*)context; + dictionary* d = ctx->d; + dictionary_del(d, name); +} + +void dictionary_destroy(dictionary* d, dictionary_each_cb free_cb) { + _free_s ctx = {.d = d, .cb = free_cb}; + dictionary_each(d, destroy_cb_pass_1, &ctx); + dictionary_each(d, destroy_cb_pass_2, &ctx); + free(d); +} diff --git a/libs/basic/src/error.c b/libs/basic/src/error.c new file mode 100644 index 0000000..808e97d --- /dev/null +++ b/libs/basic/src/error.c @@ -0,0 +1,37 @@ +/** + * SPDX-License-Identifier: MIT + * SPDX-FileCopyrightText: 2015-2016 Johan Van den Brande + * + * @file error.c + */ + +#include +#include +#include +#include + +extern uint16_t __line; + +const char *last_error = NULL; + +void error(const char *error_msg) { + void *array[10]; + size_t size; + char **strings; + size_t i; + + last_error = error_msg; + + printf("--- ERROR: %d %s\n", __line, error_msg); + + size = backtrace(array, 10); + strings = backtrace_symbols(array, size); + + printf("SHOW %zd STACK FRAMES:\n", size); + + for (i = 0; i < size; i++) { + printf(" %s\n", strings[i]); + } + + free(strings); +} diff --git a/libs/basic/src/hexdump.c b/libs/basic/src/hexdump.c new file mode 100644 index 0000000..95f30b1 --- /dev/null +++ b/libs/basic/src/hexdump.c @@ -0,0 +1,42 @@ +/** + * SPDX-License-Identifier: MIT + * SPDX-FileCopyrightText: 2015-2016 Johan Van den Brande + * + * @file hexdump.c + */ + +#include + +void hexdump(char *desc, void *addr, int len) { + int i; + unsigned char buff[17]; + unsigned char *pc = (unsigned char *)addr; + + if (desc != NULL) { + printf("%s:\n", desc); + } + + for (i = 0; i < len; i++) { + if ((i % 16) == 0) { + if (i != 0) { + printf(" %s\n", buff); + } + printf(" %04x ", i); + } + + printf(" %02x", pc[i]); + + if ((pc[i] < 0x20) || (pc[i] > 0x7e)) { + buff[i % 16] = '.'; + } else { + buff[i % 16] = pc[i]; + } + buff[(i % 16) + 1] = '\0'; + } + + while ((i % 16) != 0) { + printf(" "); + i++; + } + printf(" %s\n", buff); +} diff --git a/libs/basic/src/io.c b/libs/basic/src/io.c new file mode 100644 index 0000000..29183bd --- /dev/null +++ b/libs/basic/src/io.c @@ -0,0 +1,49 @@ +/** + * SPDX-License-Identifier: MIT + * SPDX-FileCopyrightText: 2015-2016 Johan Van den Brande + * + * @file io.c + */ + +#include +#include +#include +#include + +extern basic_putchar __putch; +extern basic_getchar __getch; + +void basic_io_print(char* buffer) { + for (size_t i = 0; i < strlen(buffer); ++i) { + __putch(buffer[i]); + } +} + +char* basic_io_readline(char* prompt, char* buffer, size_t buffer_size) { + size_t len = 0; + char ch; + basic_io_print(prompt); + while ((ch = __getch()) != '\n' && len < buffer_size - 1) { +#ifdef BASIC_READLINE_ECHO + __putch(ch); +#endif + switch (ch) { + case '\b': + if (len > 0) { + buffer[--len] = '\0'; +#ifdef BASIC_READLINE_ECHO + __putch(' '); + __putch('\b'); +#endif + } + break; + default: + buffer[len++] = ch; + } + } +#ifdef BASIC_READLINE_ECHO + __putch('\n'); +#endif + buffer[len] = '\0'; + return buffer; +} diff --git a/libs/basic/src/kbhit.c b/libs/basic/src/kbhit.c new file mode 100644 index 0000000..c50430c --- /dev/null +++ b/libs/basic/src/kbhit.c @@ -0,0 +1,30 @@ +/** + * SPDX-License-Identifier: MIT + * SPDX-FileCopyrightText: 2015-2016 Johan Van den Brande + * + * @file kbhit.c + */ + +#include +#include +#include +#include +#include + +int kbhit(void) { + static const int STDIN = 0; + static bool initialized = false; + + if (!initialized) { + struct termios term; + tcgetattr(STDIN, &term); + term.c_lflag &= ~ICANON; + tcsetattr(STDIN, TCSANOW, &term); + setbuf(stdin, NULL); + initialized = true; + } + + int bytesWaiting; + ioctl(STDIN, FIONREAD, &bytesWaiting); + return bytesWaiting; +} diff --git a/libs/basic/src/lines.c b/libs/basic/src/lines.c new file mode 100644 index 0000000..7257df4 --- /dev/null +++ b/libs/basic/src/lines.c @@ -0,0 +1,263 @@ +/** + * SPDX-License-Identifier: MIT + * SPDX-FileCopyrightText: 2015-2016 Johan Van den Brande + * + * @file lines.c + */ + +#include "lines.h" + +#include +#include +#include + +#include "hexdump.h" + +static char* __memory; +static char* __memory_end; +static size_t __memory_size; + +static line* _next(line* l) { + char* p = (char*)l; + p += sizeof(line) - 1 + l->length; + return (line*)p; +} + +static bool _is_end(line* l) { return l && l->number == 0 && l->length == 0; } + +static line* _find_end(line* l) { + line* n = l; + while (!_is_end(n)) { + n = _next(n); + } + return n; +} + +void lines_init(char* memory, size_t memory_size) { + __memory = memory; + __memory_end = memory; + __memory_size = memory_size; + + // Signal end + line* l = (line*)__memory; + l->number = 0; + l->length = 0; +} + +size_t lines_memory_used(void) { + char* p = __memory; + line* start = (line*)p; + line* end = _find_end(start); + end = _next(end); + + char* m_start = (char*)start; + char* m_end = (char*)end; + + return m_end - m_start; +} + +size_t lines_memory_available(void) { return __memory_size - lines_memory_used(); } + +bool lines_store(uint16_t number, char* contents) { + char* p = __memory; + line* l = (line*)p; + while (!_is_end(l)) { + line* next = _next(l); + + // Find line that is to be insert after. That line has a line number < + // insert and the next line has a > + if (l->number < number && next->number > number) { + // The address of the insert is the same as the next line + line* insert = next; + + // But we need to move the memory block holding the rest to the right. + line* end = _find_end(insert); + end = _next(end); // Move to next empty slot (we keep the sentinel in the copy) + + // We have the end*, calculate size to move + char* m_src = (char*)insert; + char* m_end = (char*)end; + size_t m_size = m_end - m_src; + + // Calculate offset to move + size_t insert_size = sizeof(line) - 1 + strlen(contents) + 1; + char* m_dst = m_src + insert_size; + + // Move the memory block + memmove(m_dst, m_src, m_size); + + // Set the data of the insert + insert->number = number; + insert->length = strlen(contents) + 1; + strcpy(&(insert->contents), contents); + return true; + } + // Replace + if (l->number == number) { + // We need to shift the memory to the new offset determined by the size of + // the line to be inserted + + line* end = _find_end(l); + end = _next(end); // Move to next empty slot (we keep the sentinel in the copy) + + // Calculate size of block + char* m_src = (char*)next; + char* m_end = (char*)end; + size_t m_size = m_end - m_src; + + // Calculate offset to move + size_t replace_size = sizeof(line) - 1 + strlen(contents) + 1; + size_t actual_size = sizeof(line) - 1 + strlen(&(l->contents)) + 1; + int offset = replace_size - actual_size; + char* m_dst = m_src + offset; + + // Move the memory block + memmove(m_dst, m_src, m_size); + + // Set the data of the replace + l->length = strlen(contents) + 1; + strcpy(&(l->contents), contents); + return true; + } + // Prepend + if (l->number > number) { + // The address of the insert is the same as the actual line + line* insert = l; + + // But we need to move the memory block holding the rest to the right. + line* end = _find_end(insert); + end = _next(end); // Move to next empty slot (we keep the sentinel in the copy) + + // We have the end*, calculate size to move + char* m_src = (char*)insert; + char* m_end = (char*)end; + size_t m_size = m_end - m_src; + + // Calculate offset to move + size_t insert_size = sizeof(line) - 1 + strlen(contents) + 1; + char* m_dst = m_src + insert_size; + + // Move the memory block + memmove(m_dst, m_src, m_size); + + // Set the data of the insert + insert->number = number; + insert->length = strlen(contents) + 1; + strcpy(&(insert->contents), contents); + return true; + } + + l = next; + } + + l->number = number; + l->length = strlen(contents) + 1; // Length is offset to next line + strcpy(&(l->contents), contents); + + line* end = _next(l); + end->number = 0; + end->length = 0; + return true; +} + +bool lines_delete(uint16_t number) { + // find the line + line* l = (line*)__memory; + while (!_is_end(l) && l->number != number) { + l = _next(l); + } + + if (_is_end(l)) { + return false; + } + + // l is the line to delete + // check if this is the last line + line* next = _next(l); + if (_is_end(next)) { + memset(l, 0x00, sizeof(line) - 1 + strlen(&(l->contents)) + 1); + l->number = 0; + l->length = 0; + strcpy(&(l->contents), ""); + } else { + char* dst = (char*)l; + char* src = (char*)next; + + line* lend = _find_end(next); + lend = _next(lend); // Move to next empty slot (we keep the sentinel in the copy) + char* end = (char*)lend; + size_t size = (char*)end - src; + memmove(dst, src, size); + + size_t rest = src - dst; + memset(end - rest, 0x00, rest); + } + return true; +} + +static bool _in_range(uint16_t i, uint16_t low, uint16_t high) { + if (low == 0 && high == 0) { + return true; + } + if (low == 0 && i <= high) { + return true; + } + if (high == 0 && i >= low) { + return true; + } + if (i >= low && i <= high) { + return true; + } + return false; +} + +void lines_list(uint16_t start, uint16_t end, lines_list_cb out) { + char* p = __memory; + + line* l = (line*)p; + while (!_is_end(l)) { + if (_in_range(l->number, start, end)) { + out(l->number, &(l->contents)); + } + l = _next(l); + } +} + +void lines_clear(void) { + char* end = (char*)_next(_find_end((line*)__memory)); + memset(__memory, 0x00, end - __memory); + line* l = (line*)__memory; + l->number = 0; + l->length = 0; +} + +char* lines_get_contents(uint16_t number) { + line* l = (line*)__memory; + while (!_is_end(l) && l->number != number) { + l = _next(l); + } + + if (_is_end(l)) { + return NULL; + } + + return &(l->contents); +} + +uint16_t lines_first(void) { + line* l = (line*)__memory; + return l->number; +} + +uint16_t lines_next(uint16_t number) { + line* l = (line*)__memory; + while (!_is_end(l) && l->number <= number) { + l = _next(l); + } + + if (number == l->number) { + return 0; + } + + return l->number; +} diff --git a/libs/basic/src/parser.c b/libs/basic/src/parser.c new file mode 100644 index 0000000..797cb28 --- /dev/null +++ b/libs/basic/src/parser.c @@ -0,0 +1,2071 @@ +/** + * SPDX-License-Identifier: MIT + * SPDX-FileCopyrightText: 2015-2016 Johan Van den Brande + * + * @file parser.c + */ + +#define _GNU_SOURCE +#include "parser.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "arch.h" +#include "array.h" +#include "error.h" +#include "io.h" +#include "kbhit.h" +#include "lines.h" +#include "tokenizer.h" +#include "variables.h" + +char* _dummy = 0; + +/* + line = [number] statement [ : statement ] CR + + statement = + PRINT expression-list [ ; ] + | IF relation-expression THEN statement + | GOTO expression + | INPUT variable-list + | LET variable = expression + | GOSUB expression + | RETURN + | FOR numeric_variable '=' numeric_expression TO numeric_expression [ STEP + number ] | CLEAR | NEW | LIST | RUN | END | DIM variable "(" expression ")" | + SAVE literal_string | LOAD literal_string | DELETE literal_string | DIR | DEF + FN(X) = expression + + expression-list = ( string | expression ) [, expression-list] + + variable-list = variable [, variable-list] + + expression = string_expression | numeric_expression + + numeric_expression = ["+"|"-"] term {("+"|"-"|"OR") term} . + + term = factor {( "*" | "/" | "AND" ) factor} . + + factor = + func "(" expression ")" + | number + | "(" expression ")" + | variable + | relation-expression + + relation-expression = + expression relation-operator expression + + func = + ABS + | AND + | ATN + | COS + | EXP + | INT + | LOG + | NOT + | OR + | RND + | SGN + | SIN + | SQR + | TAN + + string = literal_string | string_func "(" string_expression ")" + + literal_string = '"' ... '"' + + string_func = + CHR$ + ... + + string_expression = literal_string | string_variable + + variable = ( numeric_variable | string_variable | indexed_variable ) + + numeric_variable = A | B | C ... | X | Y | Z + + string_variable = A$ | B$ | C$ ... | X$ | Y$ | Z$ + + indexed_variable = ( numeric_variable | string_variable ) "(" expression ")" + + relation-operator = ( "<" | "<=" | "=" | ">=" | ">" ) + +*/ + +#define MAX_LINE tokenizer_string_length + +typedef union { + function_0 function_0; + function_1 function_1; + function_2 function_2; + function_3 function_3; + function_4 function_4; + function_5 function_5; +} basic_function_union; + +typedef struct { + token token; + basic_function_type type : 3; + size_t nr_arguments : 3; + kind kind_1 : 1; + kind kind_2 : 1; + kind kind_3 : 1; + kind kind_4 : 1; + kind kind_5 : 1; + basic_function_union function; +} basic_function; + +static array* basic_tokens = NULL; +static array* basic_functions = NULL; +static token t_keyword_print; +static token t_keyword_print_short; +static token t_keyword_spc; +static token t_keyword_tab; +static token t_keyword_goto; +static token t_keyword_on; +static token t_keyword_if; +static token t_keyword_then; +static token t_keyword_gosub; +static token t_keyword_return; +static token t_keyword_list; +static token t_keyword_clear; +static token t_keyword_new; +static token t_keyword_run; +static token t_keyword_end; +static token t_keyword_stop; +static token t_keyword_for; +static token t_keyword_to; +static token t_keyword_step; +static token t_keyword_next; +static token t_keyword_rem; +static token t_keyword_dim; +static token t_keyword_data; +static token t_keyword_read; +static token t_keyword_restore; +static token t_keyword_cls; +static token t_keyword_load; +static token t_keyword_save; +static token t_keyword_delete; +static token t_keyword_dir; + +static token t_op_or; +static token t_op_and; + +uint16_t __line; +static char* __cursor; +static char* __memory; +static char* __stack; +static size_t __memory_size; +static size_t __stack_size; +static size_t __program_size; +static size_t __stack_p; + +basic_putchar __putch = putchar; +basic_getchar __getch = getchar; + +bool __RUNNING = false; +bool __EVALUATING = false; +bool __REPL = true; +bool __STOPPED = false; + +typedef enum { data_state_init, data_state_find, data_state_read } data_state; + +typedef struct { + uint16_t line; + char* cursor; + char* char_pointer; + data_state state : 2; +} data_pointer; + +static data_pointer __data; + +typedef union { + float numeric; + char* string; +} expression_value; + +typedef enum { expression_type_numeric, expression_type_string } expression_type; + +typedef struct { + expression_type type; + expression_value value; +} expression_result; + +typedef enum { stack_frame_type_for, stack_frame_type_gosub } stack_frame_type; + +typedef struct { + stack_frame_type type; + char variable_name[tokenizer_variable_length]; + float end_value; + float step; + size_t line; + char* cursor; +} stack_frame_for; + +typedef struct { + stack_frame_type type; + size_t line; + char* cursor; +} stack_frame_gosub; + +static int basic_dispatch_function(basic_function* function, basic_type* rv); +static basic_function* find_basic_function_by_type(token sym, basic_function_type type); + +static size_t get_vector(size_t* vector, size_t size); +static char* string_term(void); +int str_len(basic_type* str, basic_type* rv); +int str_asc(basic_type* str, basic_type* rv); +int str_val(basic_type* str, basic_type* rv); +int str_str(basic_type* number, basic_type* rv); +int dump(basic_type* rv); +static void move_to_next_statement(void); + +typedef enum { OP_NOP, OP_LT, OP_LE, OP_EQ, OP_GE, OP_GT, OP_NE } relop; + +static bool string_condition(char* left, char* right, relop op); +static bool numeric_condition(float left, float right, relop op); +static relop get_relop(void); + +token sym; +void get_sym(void) { sym = tokenizer_get_next_token(); } + +static void set_line(uint16_t line_number) { + __line = line_number; + char* cursor = lines_get_contents(__line); + tokenizer_char_pointer(cursor); +} + +static float numeric_expression(void); +static char* string_expression(void); + +void expression(expression_result* result) { + char* string = string_expression(); + if (NULL != string) { + // Got string, check for relop and apply + relop op = get_relop(); + if (op == OP_NOP) { + result->type = expression_type_string; + result->value.string = string; + } else { + char* string_right = string_expression(); + result->type = expression_type_numeric; + result->value.numeric = string_condition(string, string_right, op); + free(string_right); + free(string); + } + } else { + result->type = expression_type_numeric; + result->value.numeric = numeric_expression(); + } +} + +static void expression_print(expression_result* expr) { + if (expr->type == expression_type_string) { + basic_io_print(expr->value.string); + } else if (expr->type == expression_type_numeric) { + char buffer[16]; + float value = expr->value.numeric; + long ivalue = (int)value; + if (ivalue == value) { + snprintf(buffer, sizeof(buffer), "%ld", ivalue); + basic_io_print(buffer); + } else { + snprintf(buffer, sizeof(buffer), "%f", value); + basic_io_print(buffer); + } + } else { + error("UNKNOWN EXPRESSION"); + } +} + +static int f_abs(basic_type* n, basic_type* rv) { + rv->kind = kind_numeric; + rv->value.number = fabs(n->value.number); + return 0; +} + +static int f_rnd(basic_type* n, basic_type* rv) { + rv->kind = kind_numeric; + if (n->value.number > 0) { + int random = rand(); + rv->value.number = (random * 1.0) / RAND_MAX; + return 0; + } + + if (n->value.number < 0) { + srand(n->value.number); + int random = rand(); + rv->value.number = (random * 1.0) / RAND_MAX; + return 0; + } + + time_t now; + struct tm* tm; + now = time(NULL); + tm = localtime(&now); + rv->value.number = (tm->tm_sec * 1.0) / 60; + return 0; +} + +static int f_int(basic_type* n, basic_type* rv) { + rv->kind = kind_numeric; + int i = (int)n->value.number; + rv->value.number = 1.0 * i; + return 0; +} + +static int f_sqr(basic_type* n, basic_type* rv) { + rv->kind = kind_numeric; + rv->value.number = (float)sqrt((double)n->value.number); + return 0; +} + +static int f_sgn(basic_type* n, basic_type* rv) { + rv->kind = kind_numeric; + if (n->value.number < 0) { + rv->value.number = -1.0; + } else if (n->value.number > 0) { + rv->value.number = 1.0; + } else { + rv->value.number = 0.0; + } + return 0; +} + +static int f_not(basic_type* n, basic_type* rv) { + rv->kind = kind_numeric; + rv->value.number = (float)(~(int)n->value.number); + return 0; +} + +static int f_sin(basic_type* n, basic_type* rv) { + rv->kind = kind_numeric; + rv->value.number = sinf(n->value.number); + return 0; +} + +static int f_cos(basic_type* n, basic_type* rv) { + rv->kind = kind_numeric; + rv->value.number = cosf(n->value.number); + return 0; +} + +static int f_tan(basic_type* n, basic_type* rv) { + rv->kind = kind_numeric; + rv->value.number = tanf(n->value.number); + return 0; +} + +static int f_log(basic_type* n, basic_type* rv) { + rv->kind = kind_numeric; + rv->value.number = logf(n->value.number); + return 0; +} + +static int f_exp(basic_type* n, basic_type* rv) { + rv->kind = kind_numeric; + rv->value.number = expf(n->value.number); + return 0; +} + +static int f_pow(basic_type* x, basic_type* y, basic_type* rv) { + rv->kind = kind_numeric; + rv->value.number = powf(x->value.number, y->value.number); + return 0; +} + +static int f_atn(basic_type* n, basic_type* rv) { + rv->kind = kind_numeric; + rv->value.number = atanf(n->value.number); + return 0; +} + +static float _or(float a, float b) { return (float)((int)a | (int)b); } + +static float _and(float a, float b) { return (float)((int)a & (int)b); } + +static int str_chr(basic_type* n, basic_type* rv) { + rv->kind = kind_string; + char* chr; + asprintf(&chr, "%c", (int)n->value.number); + rv->value.string = chr; + rv->mallocd = true; + return 0; +} + +static int str_mid(basic_type* str, basic_type* start, basic_type* length, basic_type* rv) { + rv->kind = kind_string; + char* source = str->value.string; + int _start = (int)start->value.number - 1; + if (_start > strlen(source)) _start = strlen(source); + int _length; + if (length->empty) { + _length = strlen(source) - _start; + } else { + _length = (int)length->value.number; + if (_length + _start > strlen(source)) _length = strlen(source) - _start; + } + char* string = strdup(&source[_start]); + string[_length] = '\0'; + rv->value.string = string; + rv->mallocd = true; + return 0; +} + +static int str_right(basic_type* str, basic_type* length, basic_type* rv) { + rv->kind = kind_string; + char* source = str->value.string; + rv->value.string = strdup(&source[strlen(source) - (int)length->value.number]); + rv->mallocd = true; + return 0; +} + +static int str_left(basic_type* str, basic_type* length, basic_type* rv) { + rv->kind = kind_string; + rv->value.string = strdup(str->value.string); + rv->value.string[(int)length->value.number] = '\0'; + rv->mallocd = true; + return 0; +} + +bool accept(token t) { + if (t == sym) { + get_sym(); + return true; + } + return false; +} + +static bool expect(token t) { + if (accept(t)) { + return true; + } + error("UNEXPECTED SYMBOL"); + return false; +} + +static float factor(void); + +static float numeric_factor(void) { + float number = 0; + basic_function* bf; + if ((bf = find_basic_function_by_type(sym, basic_function_type_numeric)) != NULL) { + basic_type rv; + basic_dispatch_function(bf, &rv); + if (rv.kind != kind_numeric) { + error("EXPECTED NUMERIC FACTOR"); + } + number = rv.value.number; + } else if (sym == T_NUMBER) { + number = tokenizer_get_number(); + accept(T_NUMBER); + } else if (sym == T_VARIABLE_NUMBER) { + char var_name[tokenizer_variable_length]; + tokenizer_get_variable_name(var_name); + get_sym(); + if (sym == T_LEFT_BANANA) { + size_t l = strlen(var_name); + var_name[l] = '('; + var_name[l + 1] = '\0'; + accept(T_LEFT_BANANA); + size_t vector[5]; + get_vector(vector, 5); + number = variable_array_get_numeric(var_name, vector); + expect(T_RIGHT_BANANA); + } else { + number = variable_get_numeric(var_name); + accept(T_VARIABLE_NUMBER); + } + } else if (accept(T_LEFT_BANANA)) { + number = numeric_expression(); + expect(T_RIGHT_BANANA); + } else { + error("FACTOR SYNTAX ERROR"); + get_sym(); + } + + relop op = get_relop(); + if (op != OP_NOP) { + float right_number = factor(); + number = numeric_condition(number, right_number, op); + } + + return number; +} + +static float factor(void) { + if (sym == T_STRING || sym == T_VARIABLE_STRING) { + char* s1 = string_term(); + relop op = get_relop(); + if (op == OP_NOP) { + free(s1); + error("EXPECTED RELOP"); + return 0; + } + char* s2 = string_term(); + float r = string_condition(s1, s2, op); + free(s2); + free(s1); + return r; + } else { + return numeric_factor(); + } +} + +static float term(void) { + float f1 = factor(); + while (sym == T_MULTIPLY || sym == T_DIVIDE || sym == t_op_and) { + token operator= sym; + get_sym(); + float f2 = factor(); + switch (operator) { + case T_MULTIPLY: + f1 = f1 * f2; + break; + case T_DIVIDE: + f1 = f1 / f2; + break; + default: + if (operator== t_op_and) { + f1 = _and(f1, f2); + } else { + error("TERM SYNTAX ERROR"); + } + } + } + return f1; +} + +static float numeric_expression(void) { + token operator= T_PLUS; + if (sym == T_PLUS || sym == T_MINUS) { + operator= sym; + get_sym(); + } + float t1 = term(); + if (operator== T_MINUS) { + t1 = -1 * t1; + } + while (sym == T_PLUS || sym == T_MINUS || sym == t_op_or) { + operator= sym; + get_sym(); + float t2 = term(); + switch (operator) { + case T_PLUS: + t1 = t1 + t2; + break; + case T_MINUS: + t1 = t1 - t2; + break; + default: + if (operator== t_op_or) { + t1 = _or(t1, t2); + } else { + error("EXPRESSION SYNTAX ERROR"); + } + } + } + + return t1; +} + +static void ready(void) { + if (__REPL) { + puts("READY."); + } +} + +static void list_out(uint16_t number, char* contents) { + char buffer[tokenizer_string_length]; + snprintf(buffer, sizeof(buffer), "%d %s\n", number, contents); + basic_io_print(buffer); +} + +static int do_list(basic_type* rv) { + uint16_t start = 0; + uint16_t end = 0; + + accept(t_keyword_list); + if (sym == T_NUMBER) { + start = (uint16_t)tokenizer_get_number(); + accept(T_NUMBER); + if (sym == T_MINUS) { + accept(T_MINUS); + if (sym == T_NUMBER) { + end = (uint16_t)tokenizer_get_number(); + accept(T_NUMBER); + } + } + } + + lines_list(start, end, list_out); + ready(); + + return 0; +} + +static int do_clear(basic_type* rv) { + accept(t_keyword_clear); + lines_clear(); + ready(); + return 0; +} + +static char* string_term(void) { + char* string = NULL; + char var_name[tokenizer_variable_length]; + + switch (sym) { + case T_STRING: + string = strdup(tokenizer_get_string()); + accept(T_STRING); + break; + case T_VARIABLE_STRING: + tokenizer_get_variable_name(var_name); + get_sym(); + if (sym == T_LEFT_BANANA) { + size_t l = strlen(var_name); + var_name[l] = '('; + var_name[l + 1] = '\0'; + // printf("name: %s\n", var_name); + accept(T_LEFT_BANANA); + size_t vector[5]; + get_vector(vector, 5); + string = strdup(variable_array_get_string(var_name, vector)); + if (string == NULL) string = _dummy; + + expect(T_RIGHT_BANANA); + } else { + string = strdup(variable_get_string(var_name)); + accept(T_VARIABLE_STRING); + } + break; + default: { + basic_function* bf = find_basic_function_by_type(sym, basic_function_type_string); + if (bf != NULL) { + basic_type rv; + basic_dispatch_function(bf, &rv); + if (rv.kind != kind_string) { + error("EXPECTED STRING TERM"); + } + string = strdup(rv.value.string); + if (rv.mallocd == true) free(rv.value.string); + } + } break; + } + + return string; +} + +static char* string_expression(void) { + char* s1 = string_term(); + + while (sym == T_PLUS) { + accept(T_PLUS); + char* s2 = string_term(); + size_t len = strlen(s1) + strlen(s2) + 1; + s1 = realloc(s1, len); + s1 = strcat(s1, s2); + free(s2); + } + + return s1; +} + +static int do_print(basic_type* rv) { + accept(t_keyword_print); + accept(t_keyword_print_short); + + if (sym == T_EOF || sym == T_COLON) // Just a print stm + { + __putch('\n'); + } else { + while (sym != T_EOF && sym != T_COLON) { + basic_function* bf = find_basic_function_by_type(sym, basic_function_type_print); + if (bf != NULL) { + basic_type rv; + basic_dispatch_function(bf, &rv); + } else { + expression_result expr; + expression(&expr); + expression_print(&expr); + if (expr.type == expression_type_string) { + if (expr.value.string != _dummy) free(expr.value.string); + } + } + + if (sym == T_SEMICOLON) { + accept(T_SEMICOLON); + } else if (sym == T_COMMA) { + accept(T_COMMA); + __putch('\t'); + } else { + __putch('\n'); + } + } + } + + fflush(stdout); + + return 0; +} + +static int do_spc(basic_type* n, basic_type* rv) { + for (size_t i = 0; i < n->value.number; i++) { + __putch(' '); + } + rv->kind = kind_numeric; + rv->value.number = 0; + return 0; +} + +static int do_tab(basic_type* n, basic_type* rv) { + for (size_t i = 0; i < n->value.number; i++) { + __putch(' '); + } + rv->kind = kind_numeric; + rv->value.number = 0; + return 0; +} + +static int do_cls(basic_type* rv) { + basic_io_print("\033[2J"); + basic_io_print("\033[0;0H"); + rv->kind = kind_numeric; + rv->value.number = 0; + return 0; +} + +static int do_goto(basic_type* rv) { + accept(t_keyword_goto); + + if (sym != T_NUMBER) { + error("GOTO EXPECTED NUMBER"); + return 0; + } + + int line_number = (int)tokenizer_get_number(); + accept(T_NUMBER); + + char* line = lines_get_contents(line_number); + if (line == NULL) { + error("GOTO LINE NOT FOUND"); + return 0; + } + + set_line(line_number); + + return 0; +} + +static size_t get_list(size_t* list, size_t max_size) { + size_t size = 0; + do { + if (sym == T_COMMA) { + accept(T_COMMA); + } + float n = numeric_expression(); + list[size] = n; + size++; + if (size > max_size) { + error("LIST MAX SIZE"); + return size; + } + } while (sym == T_COMMA); + + return size; +} + +static int do_on_goto(basic_type* rv) { + accept(t_keyword_on); + + int index = numeric_expression(); + + token what = T_EOF; + if (sym == t_keyword_goto) { + what = t_keyword_goto; + } else if (sym == t_keyword_gosub) { + what = t_keyword_gosub; + } else { + error("ON WITHOUT GOTO OR GOSUB"); + return 0; + } + accept(what); + + size_t list[10]; + size_t size = get_list(list, 10); + + if (index > size) { + error("ON OUT OF BOUNDS"); + return 0; + } + + size_t line_number = list[index - 1]; + if (what == t_keyword_goto) { + char* line = lines_get_contents(line_number); + if (line == NULL) { + error("LINE NOT FOUND"); + } + set_line(line_number); + } else { + stack_frame_gosub* g; + if (__stack_p < sizeof(stack_frame_gosub)) { + error("STACK FULL"); + return 0; + } + + __stack_p -= sizeof(stack_frame_gosub); + g = (stack_frame_gosub*)&(__stack[__stack_p]); + + g->type = stack_frame_type_gosub; + g->line = __line; + g->cursor = tokenizer_char_pointer(NULL); + set_line(line_number); + } + return 0; +} + +static int do_gosub(basic_type* rv) { + accept(t_keyword_gosub); + + if (sym != T_NUMBER) { + error("EXPECTED NUMBER"); + return 0; + } + int line_number = (int)tokenizer_get_number(); + accept(T_NUMBER); + + stack_frame_gosub* g; + if (__stack_p < sizeof(stack_frame_gosub)) { + error("STACK FULL"); + return 0; + } + + __stack_p -= sizeof(stack_frame_gosub); + g = (stack_frame_gosub*)&(__stack[__stack_p]); + + g->type = stack_frame_type_gosub; + g->line = __line; + g->cursor = tokenizer_char_pointer(NULL); + + set_line(line_number); + + return 0; +} + +static int do_return(basic_type* rv) { + accept(t_keyword_return); + + stack_frame_gosub* g; + g = (stack_frame_gosub*)&(__stack[__stack_p]); + + if (g->type != stack_frame_type_gosub) { + error("EXPECTED GOSUB STACK FRAME"); + return 0; + } + + __line = g->line; + tokenizer_char_pointer(g->cursor); + + __stack_p += sizeof(stack_frame_gosub); + + return 0; +} + +static int do_for(basic_type* rv) { + accept(t_keyword_for); + + if (sym != T_VARIABLE_NUMBER) { + error("EXPECTED VAR"); + return 0; + } + + char name[tokenizer_variable_length]; + tokenizer_get_variable_name(name); + get_sym(); + expect(T_EQUALS); + float value = numeric_expression(); + variable_set_numeric(name, value); + + expect(t_keyword_to); + + float end_value = numeric_expression(); + + float step = 1.0; + if (sym != T_EOF && sym != T_COLON) { + expect(t_keyword_step); + step = numeric_expression(); + } + + stack_frame_for* f; + if (__stack_p < sizeof(stack_frame_for)) { + error("STACK FULL"); + return 0; + } + + __stack_p -= sizeof(stack_frame_for); + f = (stack_frame_for*)&(__stack[__stack_p]); + + f->type = stack_frame_type_for; + strncpy(f->variable_name, name, tokenizer_variable_length); + f->end_value = end_value; + f->step = step; + f->line = __line; + f->cursor = tokenizer_char_pointer(NULL); + + return 0; +} + +static int do_next(basic_type* rv) { + accept(t_keyword_next); + + stack_frame_for* f; + f = (stack_frame_for*)&(__stack[__stack_p]); + + if (f->type != stack_frame_type_for) { + error("EXPECTED FOR STACK FRAME"); + return 0; + } + + if (sym == T_VARIABLE_NUMBER) { + char var_name[tokenizer_variable_length]; + tokenizer_get_variable_name(var_name); + accept(T_VARIABLE_NUMBER); + if (strcmp(var_name, f->variable_name) != 0) { + char _error[40]; + snprintf(_error, sizeof(_error), "EXPECTED NEXT WITH %s, GOT %s", var_name, f->variable_name); + error(_error); + return 0; + } + } + + float value = variable_get_numeric(f->variable_name) + f->step; + if ((f->step > 0 && value > f->end_value) || (f->step < 0 && value < f->end_value)) { + __stack_p += sizeof(stack_frame_for); + return 0; + } + + variable_set_numeric(f->variable_name, value); + + __line = f->line; + tokenizer_char_pointer(f->cursor); + + return 0; +} + +static int do_end(basic_type* rv) { + __RUNNING = false; + return 0; +} + +static int do_rem(basic_type* rv) { + accept(t_keyword_rem); + set_line(lines_next(__line)); + get_sym(); + return 0; +} + +static size_t get_vector(size_t* vector, size_t size) { + for (size_t i = 0; i < size; i++) { + vector[i] = 0; + } + size_t dimensions = 0; + while (sym != T_RIGHT_BANANA) { + float n = numeric_expression(); + vector[dimensions] = n; + dimensions++; + if (dimensions > size) { + error("MAX DIM"); + return dimensions; + } + if (sym == T_COMMA) { + accept(T_COMMA); + } + } + return dimensions; +} + +static int do_dim(basic_type* rv) { + accept(t_keyword_dim); + + while (sym != T_EOF && sym != T_COLON) { + if (sym == T_VARIABLE_NUMBER || sym == T_VARIABLE_STRING) { + variable_type type + = (sym == T_VARIABLE_STRING) ? variable_type_string : variable_type_numeric; + size_t vector[5]; + char name[tokenizer_variable_length]; + tokenizer_get_variable_name(name); + + size_t l = strlen(name); + name[l] = '('; + name[l + 1] = '\0'; + accept(sym); + expect(T_LEFT_BANANA); + size_t dimensions = get_vector(vector, 5); + expect(T_RIGHT_BANANA); + + variable_array_init(name, type, dimensions, vector); + } + + if (sym == T_COMMA) { + accept(T_COMMA); + } + } + + return 0; +} + +static int do_data(basic_type* rv) { + accept(t_keyword_data); + move_to_next_statement(); + return 0; +} + +static bool _data_find(variable_type type, value* value) { + tokenizer_init(__data.cursor); + tokenizer_char_pointer(__data.char_pointer); + while (__data.cursor) { + get_sym(); + while (sym != T_EOF) { + if (sym == t_keyword_data) { + accept(t_keyword_data); + if (type == variable_type_string) { + value->string = tokenizer_get_string(); + } else { + value->number = tokenizer_get_number(); + } + __data.state = data_state_read; + __data.char_pointer = tokenizer_char_pointer(NULL); + return true; + } + get_sym(); + } + __data.line = lines_next(__data.line); + __data.cursor = lines_get_contents(__data.line); + tokenizer_init(__data.cursor); + } + return false; +} + +static bool _data_read(variable_type type, value* value) { + bool rv = false; + + tokenizer_init(__data.cursor); + tokenizer_char_pointer(__data.char_pointer); + get_sym(); + if (sym != T_EOF) { + accept(T_COMMA); // seperated by comma's + if (type == variable_type_string) { + value->string = tokenizer_get_string(); + } else { + value->number = tokenizer_get_number(); + } + __data.char_pointer = tokenizer_char_pointer(NULL); + rv = true; + } else { + __data.cursor = lines_get_contents(__data.line); + } + return rv; +} + +static bool _do_data_read(variable_type type, value* value) { + char* save_pointer = tokenizer_char_pointer(NULL); + bool rv = false; + + switch (__data.state) { + case data_state_init: + __data.line = lines_first(); + __data.cursor = lines_get_contents(__data.line); + __data.char_pointer = tokenizer_char_pointer(NULL); + __data.state = data_state_find; + rv = _data_find(type, value); + break; + + case data_state_find: + rv = _data_find(type, value); + break; + + case data_state_read: { + bool data_found = _data_read(type, value); + if (data_found) { + rv = true; + } else { + rv = _data_find(type, value); + } + } + } + + tokenizer_init(__cursor); + tokenizer_char_pointer(save_pointer); + + return rv; +} + +static int do_read(basic_type* rv) { + bool is_array = false; + size_t vector[5]; + + accept(t_keyword_read); + + // if not initialized data_pointer, find first data statement + // while not end of variable list + // read data, put in variable + // proceed to next data statement + while (sym != T_EOF && sym != T_COLON) { + if (sym == T_VARIABLE_NUMBER || sym == T_VARIABLE_STRING) { + variable_type type + = (sym == T_VARIABLE_STRING) ? variable_type_string : variable_type_numeric; + char name[tokenizer_variable_length]; + tokenizer_get_variable_name(name); + accept(sym); + if (sym == T_LEFT_BANANA) { + is_array = true; + size_t l = strlen(name); + name[l] = '('; + name[l + 1] = '\0'; + accept(T_LEFT_BANANA); + get_vector(vector, 5); + expect(T_RIGHT_BANANA); + } + value v; + bool read_ok = _do_data_read(type, &v); + if (!read_ok) { + error("READ WITHOUT DATA"); + return 0; + } + if (type == variable_type_string) { + if (is_array) { + variable_array_set_string(name, v.string, vector); + } else { + variable_set_string(name, v.string); + } + } else { + if (is_array) { + variable_array_set_numeric(name, v.number, vector); + } else { + variable_set_numeric(name, v.number); + } + } + } + get_sym(); + accept(T_COMMA); + } + + return 0; +} + +static int do_restore(basic_type* rv) { + accept(t_keyword_restore); + __data.line = 0; + __data.cursor = 0; + __data.state = data_state_init; + return 0; +} + +static int is_comment(const char* s) { return s[0] == '#'; } + +static int is_empty(const char* s) { + while (*s != '\0') { + if (!isspace(*s)) return 0; + s++; + } + return 1; +} + +// Insert '\0' behind first non space character, starting from right. +static void _trim(char* s) { + char* p = s + strlen(s) - 1; // without the '\0' + while (p >= s && isspace(*p)) { + --p; + } + *(p + 1) = '\0'; +} + +static void _store(char* line) { + int number; + sscanf(line, "%d", &number); + + char* p = line; + while (isdigit(*p)) { + p++; + } + while (isspace(*p)) { + p++; + } + _trim(p); + printf("%d %s\n", number, p); + lines_store((uint16_t)number, p); +} + +static void _load_cb(char* line, void* context) { + if (!(is_empty(line) || is_comment(line))) { + _store(line); + } +} + +static int do_load(basic_type* rv) { + accept(t_keyword_load); + if (sym != T_STRING) { + error("EXPECTED LITERAL STRING"); + return 0; + } + char* filename = tokenizer_get_string(); + accept(T_STRING); + lines_clear(); + arch_load(filename, _load_cb, NULL); + ready(); + + return 0; +} + +typedef struct { + uint16_t number; +} _save_cb_ctx; + +static uint16_t _save_cb(char** line, void* context) { + _save_cb_ctx* ctx = (_save_cb_ctx*)context; + uint16_t number = ctx->number; + ctx->number = lines_next(number); + + *line = lines_get_contents(number); + + return number; +} + +static int do_save(basic_type* rv) { + accept(t_keyword_save); + if (sym != T_STRING) { + error("EXPECTED LITERAL STRING"); + return 0; + } + char* filename = tokenizer_get_string(); + accept(T_STRING); + _save_cb_ctx ctx; + ctx.number = lines_first(); + arch_save(filename, _save_cb, &ctx); + ready(); + + return 0; +} + +static int do_delete(basic_type* rv) { + accept(t_keyword_delete); + if (sym != T_STRING) { + error("EXPECTED LITERAL STRING"); + return 0; + } + char* filename = tokenizer_get_string(); + accept(T_STRING); + + arch_delete(filename); + ready(); + + return 0; +} + +static void _dir_cb(char* name, size_t size, bool label, void* context) { + if (label) { + printf("-- %-13s --\n", name); + } else { + printf("> %-8s : %6ld\n", name, size); + } +} + +static int do_dir(basic_type* rv) { + accept(t_keyword_dir); + arch_dir(_dir_cb, NULL); + ready(); + + return 0; +} + +// static int +// do_def_fn(basic_type* rv) +// { +// accept(t_keyword_def); +// +// if(sym != t_keyword_fn){ +// error("EXPECTED FN"); +// return 0; +// } +// +// // Find 'X' between '(', ')'. +// +// // Associate 'X' with the location of the expression (string pointer). +// // When 'evalled', use the string pointer to run the expression valuator. +// +// return 0; +// } + +static void parse_line(void); +static bool statement(void); + +static int do_run(basic_type* rv) { + __line = lines_first(); + __cursor = lines_get_contents(__line); + tokenizer_init(__cursor); + __RUNNING = true; + __STOPPED = false; + while (__cursor && __RUNNING) { + get_sym(); + if (sym == T_EOF) { + __line = lines_next(__line); + __cursor = lines_get_contents(__line); + if (__cursor == NULL) { + __RUNNING = false; + break; + } + tokenizer_init(__cursor); + } + parse_line(); + } + + ready(); + + return 0; +} + +static relop get_relop(void) { + if (sym == T_LESS) { + accept(T_LESS); + if (sym == T_EQUALS) { + accept(T_EQUALS); + return OP_LE; + } else if (sym == T_GREATER) { + accept(T_GREATER); + return OP_NE; + } + return OP_LT; + } + + if (sym == T_EQUALS) { + accept(T_EQUALS); + return OP_EQ; + } + + if (sym == T_GREATER) { + accept(T_GREATER); + if (sym == T_EQUALS) { + accept(T_EQUALS); + return OP_GE; + } + return OP_GT; + } + + return OP_NOP; +} + +static bool numeric_condition(float left, float right, relop op) { + switch (op) { + case OP_NOP: + error("EXPECTED RELOP"); + break; + case OP_LT: + return left < right; + case OP_LE: + return left <= right; + case OP_EQ: + return left == right; + case OP_GE: + return left >= right; + case OP_GT: + return left > right; + case OP_NE: + return left != right; + } + + return false; +} + +static bool string_condition(char* left, char* right, relop op) { + int comparison = strcmp(left, right); + + switch (op) { + case OP_NOP: + error("EXPECTED RELOP"); + break; + case OP_LT: + return comparison < 0; + case OP_LE: + return comparison <= 0; + case OP_EQ: + return comparison == 0; + case OP_NE: + return comparison != 0; + case OP_GE: + return comparison >= 0; + case OP_GT: + return comparison > 0; + } + + return false; +} + +static bool condition(expression_result* left_er, expression_result* right_er, relop op) { + if (left_er->type == expression_type_numeric) { + if (right_er->type != expression_type_numeric) { + error("EXPECTED NUMERIC RIGHT HAND TYPE"); + } + return numeric_condition(left_er->value.numeric, right_er->value.numeric, op); + } else { + if (right_er->type != expression_type_string) { + error("EXPECTED STRING RIGHT HAND TYPE"); + } + return string_condition(left_er->value.string, right_er->value.string, op); + ; + } +} + +static void move_to_next_statement(void) { + while (sym != T_EOF && sym != T_COLON) { + get_sym(); + } +} + +static void move_to_next_line(void) { + set_line(lines_next(__line)); + get_sym(); +} + +static int do_if(basic_type* rv) { + expression_result left_side, right_side; + bool result; + + expression(&left_side); + + if (left_side.type == expression_type_string) { + relop op = get_relop(); + expression(&right_side); + result = condition(&left_side, &right_side, op); + } else { + result = left_side.value.numeric == 1.0; + } + + if (sym != t_keyword_then) { + error("IF WITHOUT THEN"); + return 0; + } + + if (result) { + get_sym(); + + if (sym == T_NUMBER) { + float line_number = tokenizer_get_number(); + accept(T_NUMBER); + set_line(line_number); + } else { + parse_line(); + } + + } else { + move_to_next_line(); + } + + return 0; +} + +static int do_let(basic_type* rv) { + bool is_array = false; + size_t vector[5]; + + if (sym != T_VARIABLE_NUMBER && sym != T_VARIABLE_STRING) { + error("EXPECTED VAR"); + return 0; + } + + char name[tokenizer_variable_length]; + tokenizer_get_variable_name(name); + token var_type = sym; + get_sym(); + if (sym == T_LEFT_BANANA) { + is_array = true; + size_t l = strlen(name); + name[l] = '('; + name[l + 1] = '\0'; + accept(T_LEFT_BANANA); + get_vector(vector, 5); + expect(T_RIGHT_BANANA); + } + expect(T_EQUALS); + + if (var_type == T_VARIABLE_NUMBER) { + float value = numeric_expression(); + if (is_array) { + variable_array_set_numeric(name, value, vector); + } else { + variable_set_numeric(name, value); + } + } + + if (var_type == T_VARIABLE_STRING) { + char* value = string_expression(); + if (is_array) { + variable_array_set_string(name, value, vector); + } else { + variable_set_string(name, value); + } + free(value); + } + + return 0; +} + +static int do_input(basic_type* rv) { + bool prompt = false; + expression_result expr; + + if (sym != T_VARIABLE_NUMBER && sym != T_VARIABLE_STRING) { + expression(&expr); + if (sym == T_COMMA || sym == T_SEMICOLON) { + get_sym(); + } else { + error("UNEXPECTED TOKEN"); + } + prompt = true; + } + + if (sym != T_VARIABLE_NUMBER && sym != T_VARIABLE_STRING) { + error("EXPECTED VAR"); + return 0; + } + + char name[tokenizer_variable_length]; + token type = sym; + if (type == T_VARIABLE_NUMBER) { + tokenizer_get_variable_name(name); + accept(T_VARIABLE_NUMBER); + } + + if (type == T_VARIABLE_STRING) { + tokenizer_get_variable_name(name); + accept(T_VARIABLE_STRING); + } + + if (prompt) { + expression_print(&expr); + if (expr.type == expression_type_string) { + free(expr.value.string); + } + } + char line[MAX_LINE]; + basic_io_readline((prompt ? " " : "? "), line, sizeof(line)); + + if (type == T_VARIABLE_NUMBER) { + char* t; + float value = strtof(line, &t); + variable_set_numeric(name, value); + } + + if (type == T_VARIABLE_STRING) { + variable_set_string(name, line); + } + + return 0; +} + +static int do_get(basic_type* rv) { + if (sym != T_VARIABLE_STRING) { + error("EXPECTED STRING VAR"); + return 0; + } + + char name[tokenizer_variable_length]; + tokenizer_get_variable_name(name); + + accept(T_VARIABLE_STRING); + + char c[4] = ""; + if (kbhit()) { + int ch = __getch(); + if (ch == 10) { + ch = 13; + } + snprintf(c, sizeof(c), "%c", ch); + } + variable_set_string(name, c); + + return 0; +} + +int do_sleep(basic_type* delay, basic_type* rv) { + int milliseconds = delay->value.number; + + struct timespec ts; + ts.tv_sec = milliseconds / 1000; + ts.tv_nsec = (milliseconds % 1000) * 1000000; + nanosleep(&ts, NULL); + + rv->kind = kind_numeric; + rv->value.number = 0; + return 0; +} + +static void parse_line(void) { + while (sym != T_EOF + //&& sym != T_COLON + ) { + bool ok = statement(); + if (!ok) { + break; + } + } +} + +static bool statement(void) { + switch (sym) { + case T_ERROR: + error("STATEMENT ERROR"); + break; + case T_COLON: + accept(sym); + break; + default: { + basic_function* bf = find_basic_function_by_type(sym, basic_function_type_keyword); + if (bf != NULL) { + basic_type rv; + basic_dispatch_function(bf, &rv); + } else { + do_let(NULL); + } + } break; + } + return last_error == NULL; +} + +void basic_destroy(void) { + variables_destroy(); + tokenizer_free_registered_tokens(); + array_destroy(basic_tokens); + array_destroy(basic_functions); + free(__stack); + free(__memory); +} + +void basic_init(size_t memory_size, size_t stack_size) { + __memory = malloc(memory_size); + if (!__memory) { + error("CANNOT ALLOCATE PROGRAM SPACE"); + return; + } + __memory_size = memory_size; + __program_size = __memory_size; + + __stack = malloc(stack_size); + if (!__stack) { + error("CANNOT ALLOCATE STACK SPACE"); + return; + } + __stack_size = stack_size; + __stack_p = __stack_size; + + __line = 0; + __data.state = data_state_init; + + basic_tokens = array_new(sizeof(token_entry)); + basic_functions = array_new(sizeof(basic_function)); + + tokenizer_setup(); + + // Which ones do we keep for xmega if memory pressure too big? (X marks delete + // for xmega) + + // BASIC keywords + t_keyword_list = register_function_0(basic_function_type_keyword, "LIST", do_list); + t_keyword_clear = register_function_0(basic_function_type_keyword, "CLEAR", do_clear); // X + t_keyword_new = register_function_0(basic_function_type_keyword, "NEW", do_clear); + t_keyword_goto = register_function_0(basic_function_type_keyword, "GOTO", do_goto); + t_keyword_on = register_function_0(basic_function_type_keyword, "ON", do_on_goto); + t_keyword_gosub = register_function_0(basic_function_type_keyword, "GOSUB", do_gosub); + t_keyword_return = register_function_0(basic_function_type_keyword, "RETURN", do_return); + t_keyword_run = register_function_0(basic_function_type_keyword, "RUN", do_run); + t_keyword_if = register_function_0(basic_function_type_keyword, "IF", do_if); + t_keyword_then = register_token("THEN"); + t_keyword_for = register_function_0(basic_function_type_keyword, "FOR", do_for); + t_keyword_to = register_token("TO"); + t_keyword_step = register_token("STEP"); + t_keyword_next = register_function_0(basic_function_type_keyword, "NEXT", do_next); + t_keyword_end = register_function_0(basic_function_type_keyword, "END", do_end); + t_keyword_stop = register_function_0(basic_function_type_keyword, "STOP", do_end); + t_keyword_rem = register_function_0(basic_function_type_keyword, "REM", do_rem); + t_keyword_dim = register_function_0(basic_function_type_keyword, "DIM", do_dim); + t_keyword_data = register_function_0(basic_function_type_keyword, "DATA", do_data); + t_keyword_read = register_function_0(basic_function_type_keyword, "READ", do_read); + t_keyword_restore = register_function_0(basic_function_type_keyword, "RESTORE", do_restore); + t_keyword_load = register_function_0(basic_function_type_keyword, "LOAD", do_load); + t_keyword_save = register_function_0(basic_function_type_keyword, "SAVE", do_save); + t_keyword_delete = register_function_0(basic_function_type_keyword, "DELETE", do_delete); + t_keyword_dir = register_function_0(basic_function_type_keyword, "DIR", do_dir); + + register_function_0(basic_function_type_keyword, "LET", do_let); + register_function_0(basic_function_type_keyword, "INPUT", do_input); + register_function_0(basic_function_type_keyword, "GET", do_get); + + // LOGICAL and BINARY operators + t_op_or = register_token("OR"); + t_op_and = register_token("AND"); + + // Output related + t_keyword_print = register_function_0(basic_function_type_keyword, "PRINT", do_print); + t_keyword_print_short = register_function_0(basic_function_type_keyword, "?", do_print); + t_keyword_spc = register_function_1(basic_function_type_print, "SPC", do_spc, kind_numeric); + t_keyword_tab = register_function_1(basic_function_type_print, "TAB", do_tab, kind_numeric); + t_keyword_cls = register_function_0(basic_function_type_keyword, "CLS", do_cls); + + // BASIC functions + register_function_1(basic_function_type_numeric, "ABS", f_abs, kind_numeric); + register_function_1(basic_function_type_numeric, "SIN", f_sin, kind_numeric); + register_function_1(basic_function_type_numeric, "COS", f_cos, kind_numeric); + register_function_1(basic_function_type_numeric, "RND", f_rnd, kind_numeric); + register_function_1(basic_function_type_numeric, "INT", f_int, kind_numeric); + register_function_1(basic_function_type_numeric, "TAN", f_tan, kind_numeric); + register_function_1(basic_function_type_numeric, "SQR", f_sqr, kind_numeric); + register_function_1(basic_function_type_numeric, "SGN", f_sgn, kind_numeric); + register_function_1(basic_function_type_numeric, "LOG", f_log, kind_numeric); + register_function_1(basic_function_type_numeric, "EXP", f_exp, kind_numeric); + register_function_2(basic_function_type_numeric, "POW", f_pow, kind_numeric, kind_numeric); + register_function_1(basic_function_type_numeric, "ATN", f_atn, kind_numeric); + register_function_1(basic_function_type_numeric, "NOT", f_not, kind_numeric); + + // BASIC string functions + register_function_1(basic_function_type_numeric, "LEN", str_len, kind_string); + register_function_1(basic_function_type_string, "CHR$", str_chr, kind_numeric); + register_function_3(basic_function_type_string, "MID$", str_mid, kind_string, kind_numeric, + kind_numeric); + register_function_2(basic_function_type_string, "LEFT$", str_left, kind_string, kind_numeric); + register_function_2(basic_function_type_string, "RIGHT$", str_right, kind_string, kind_numeric); + register_function_1(basic_function_type_numeric, "ASC", str_asc, kind_string); + register_function_1(basic_function_type_numeric, "VAL", str_val, kind_string); + register_function_1(basic_function_type_string, "STR$", str_str, kind_numeric); + + // Special + register_function_1(basic_function_type_keyword, "SLEEP", do_sleep, kind_numeric); + + // DEBUG + register_function_0(basic_function_type_keyword, "DUMP", dump); + + lines_init(__memory, __program_size); + variables_init(); + __data.line = 0; + __data.cursor = 0; + __data.state = data_state_init; + + arch_init(); +} + +void basic_register_io(basic_putchar putch, basic_getchar getch) { + __putch = putch; + __getch = getch; +} + +void basic_run(void) { + __REPL = false; + basic_eval("RUN"); +} + +void basic_eval(char* line) { + if (strlen(line) > 0 && (strlen(line) - 1) > tokenizer_string_length) { + error("LINE TOO LONG"); + return; + } + + char line_string[tokenizer_string_length]; + strncpy(line_string, line, sizeof(line_string)); + _trim(line_string); + if (is_empty(line_string) || is_comment(line_string)) { + return; + } + + last_error = NULL; + tokenizer_init(line_string); + get_sym(); + if (sym == T_NUMBER) { + float line_number = tokenizer_get_number(); + char* line = tokenizer_char_pointer(NULL); + get_sym(); + if (sym == T_EOF) { + lines_delete(line_number); + } else { + lines_store(line_number, line); + } + } else { + __EVALUATING = true; + while (__EVALUATING) { + parse_line(); + accept(sym); + + if (sym == T_EOF || sym == T_ERROR) __EVALUATING = false; + } + } + // printf("stack available: %" PRIu16 "\n", __stack_p); + // printf("memory available: %" PRIu16 "\n", lines_memory_available() ); +} + +float evaluate(char* expression_string) { + last_error = NULL; + tokenizer_init(expression_string); + get_sym(); + float result = numeric_expression(); + expect(T_EOF); + return result; +} + +void evaluate_print(char* line) { + float result = evaluate(line); + printf("%s = %f\n", line, result); +} + +const char* evaluate_last_error(void) { return last_error; } + +void clear_last_error(void) { last_error = NULL; } + +// - Register functions + +static size_t basic_token_id = TOKEN_TYPE_END + 1000; + +token register_token(char* token_name) { + token_entry token; + + token.token = basic_token_id++; + token.name = token_name; + + tokenizer_register_token(&token); + + return token.token; +} + +token register_function_0(basic_function_type type, char* keyword, function_0 function) { + token t = register_token(keyword); + basic_function bf + = {.token = t, .type = type, .nr_arguments = 0, .function.function_0 = function}; + + array_push(basic_functions, &bf); + + return t; +} + +token register_function_1(basic_function_type type, char* keyword, function_1 function, kind v1) { + token t = register_token(keyword); + basic_function bf = { + .token = t, .type = type, .nr_arguments = 1, .kind_1 = v1, .function.function_1 = function}; + + array_push(basic_functions, &bf); + + return t; +} + +token register_function_2(basic_function_type type, char* keyword, function_2 function, kind v1, + kind v2) { + token t = register_token(keyword); + basic_function bf = {.token = t, + .type = type, + .nr_arguments = 2, + .kind_1 = v1, + .kind_2 = v2, + .function.function_2 = function}; + + array_push(basic_functions, &bf); + + return t; +} + +token register_function_3(basic_function_type type, char* keyword, function_3 function, kind v1, + kind v2, kind v3) { + token t = register_token(keyword); + basic_function bf = {.token = t, + .type = type, + .nr_arguments = 3, + .kind_1 = v1, + .kind_2 = v2, + .kind_3 = v3, + .function.function_3 = function}; + + array_push(basic_functions, &bf); + + return t; +} + +token register_function_4(basic_function_type type, char* keyword, function_4 function, kind v1, + kind v2, kind v3, kind v4) { + token t = register_token(keyword); + basic_function bf = {.token = t, + .type = type, + .nr_arguments = 4, + .kind_1 = v1, + .kind_2 = v2, + .kind_3 = v3, + .kind_4 = v4, + .function.function_4 = function}; + + array_push(basic_functions, &bf); + + return t; +} + +token register_function_5(basic_function_type type, char* keyword, function_5 function, kind v1, + kind v2, kind v3, kind v4, kind v5) { + token t = register_token(keyword); + basic_function bf = {.token = t, + .type = type, + .nr_arguments = 5, + .kind_1 = v1, + .kind_2 = v2, + .kind_3 = v3, + .kind_4 = v4, + .kind_5 = v5, + .function.function_5 = function}; + + array_push(basic_functions, &bf); + + return t; +} + +static basic_function* find_basic_function_by_type(token sym, basic_function_type type) { + for (size_t i = 0; i < array_size(basic_functions); i++) { + basic_function* bf = (basic_function*)array_get(basic_functions, i); + if (bf->type == type && bf->token == sym) { + return bf; + } + } + return NULL; +} + +static void get_parameter(kind k, basic_type* v) { + v->empty = false; + v->mallocd = false; + if (k == kind_string) { + char* s = string_expression(); + v->kind = kind_string; + v->value.string = s; + } else { + float n = numeric_expression(); + v->kind = kind_numeric; + v->value.number = n; + } +} + +static kind function_kind_i(basic_function* function, int i) { + switch (i) { + case 0: + return function->kind_1; + case 1: + return function->kind_2; + case 2: + return function->kind_3; + case 3: + return function->kind_4; + case 4: + return function->kind_5; + default: + return kind_numeric; + } +} + +static int basic_dispatch_function(basic_function* function, basic_type* rv) { + if (function->nr_arguments > 5) { + error("MAX ARGUMENTS"); + return -1; + } + + accept(sym); + + if (function->nr_arguments == 0 && function->type == basic_function_type_keyword) { + function->function.function_0(rv); + return 0; + } + + basic_type v[5]; + expect(T_LEFT_BANANA); + int i = 0; + for (; i < function->nr_arguments; i++) { + if (sym != T_RIGHT_BANANA) { + get_parameter(function_kind_i(function, i), &(v[i])); + if (i < function->nr_arguments - 1) { + if (sym != T_COMMA) { + // Probably the rest are default parameters... + break; // break for loop + } + expect(T_COMMA); + } + } + } + // loop further, marking empty the variables + for (; i < function->nr_arguments; i++) { + v[i].empty = true; + v[i].mallocd = false; + v[i].kind = kind_numeric; + } + expect(T_RIGHT_BANANA); + + rv->mallocd = false; + switch (function->nr_arguments) { + case 0: + function->function.function_0(rv); + break; + case 1: + function->function.function_1(&(v[0]), rv); + break; + case 2: + function->function.function_2(&(v[0]), &(v[1]), rv); + break; + case 3: + function->function.function_3(&(v[0]), &(v[1]), &(v[2]), rv); + break; + case 4: + function->function.function_4(&(v[0]), &(v[1]), &(v[2]), &(v[3]), rv); + break; + case 5: + function->function.function_5(&(v[0]), &(v[1]), &(v[2]), &(v[3]), &(v[4]), rv); + break; + default: + return -1; + } + + for (int i = 0; i < function->nr_arguments; i++) { + if (v[i].kind == kind_string) { + free(v[i].value.string); + } + } + + return 0; +} + +int str_len(basic_type* str, basic_type* rv) { + rv->kind = kind_numeric; + rv->value.number = (int)strlen(str->value.string); + return 0; +} + +int str_asc(basic_type* str, basic_type* rv) { + rv->kind = kind_numeric; + rv->value.number = (int)*(str->value.string); + return 0; +} + +int str_val(basic_type* str, basic_type* rv) { + rv->kind = kind_numeric; + rv->value.number = atof(str->value.string); + return 0; +} + +int str_str(basic_type* number, basic_type* rv) { + rv->kind = kind_string; + asprintf(&(rv->value.string), "%f", number->value.number); + rv->mallocd = true; + return 0; +} + +void dump_var(variable* var, void* context) { variable_dump(var); } + +int dump(basic_type* rv) { + variables_each(dump_var, NULL); + return 0; +} diff --git a/libs/basic/src/tokenizer.c b/libs/basic/src/tokenizer.c new file mode 100644 index 0000000..8d59a8f --- /dev/null +++ b/libs/basic/src/tokenizer.c @@ -0,0 +1,222 @@ +/** + * SPDX-License-Identifier: MIT + * SPDX-FileCopyrightText: 2015-2016 Johan Van den Brande + * + * @file tokenizer.c + */ + +#include "tokenizer.h" + +#include +#include +#include +#include +#include +#include + +#include "arch.h" +#include "array.h" +#include "hexdump.h" + +static array *token_array = NULL; + +add_token(T_ERROR, NULL); +add_token(T_EOF, NULL); +add_token(T_NUMBER, NULL); +add_token(T_STRING, NULL); +add_token(T_VARIABLE_STRING, NULL); +add_token(T_VARIABLE_NUMBER, NULL); +add_token(T_PLUS, "+"); +add_token(T_MINUS, "-"); +add_token(T_MULTIPLY, "*"); +add_token(T_DIVIDE, "/"); +add_token(T_LEFT_BANANA, "("); +add_token(T_RIGHT_BANANA, ")"); +add_token(T_COLON, ":"); +add_token(T_SEMICOLON, ";"); +add_token(T_EQUALS, "="); +add_token(T_LESS, "<"); +add_token(T_GREATER, ">"); +add_token(T_COMMA, ","); + +char *tokenizer_line = NULL; +char *tokenizer_p = NULL; +char *tokenizer_next_p = NULL; + +token tokenizer_actual_token; +float tokenizer_actual_number; +char tokenizer_actual_char; +char tokenizer_actual_string[tokenizer_string_length]; +char tokenizer_actual_variable[tokenizer_variable_length]; + +void tokenizer_setup(void) { + token_array = array_new(sizeof(token_entry)); + tokenizer_register_token(&_T_ERROR); + tokenizer_register_token(&_T_EOF); + tokenizer_register_token(&_T_NUMBER); + tokenizer_register_token(&_T_STRING); + tokenizer_register_token(&_T_VARIABLE_STRING); + tokenizer_register_token(&_T_VARIABLE_NUMBER); + tokenizer_register_token(&_T_PLUS); + tokenizer_register_token(&_T_MINUS); + tokenizer_register_token(&_T_MULTIPLY); + tokenizer_register_token(&_T_DIVIDE); + tokenizer_register_token(&_T_LEFT_BANANA); + tokenizer_register_token(&_T_RIGHT_BANANA); + tokenizer_register_token(&_T_COLON); + tokenizer_register_token(&_T_SEMICOLON); + tokenizer_register_token(&_T_EQUALS); + tokenizer_register_token(&_T_LESS); + tokenizer_register_token(&_T_GREATER); + tokenizer_register_token(&_T_COMMA); +} + +void tokenizer_init(char *input) { + tokenizer_line = input; + tokenizer_p = tokenizer_next_p = tokenizer_line; +} + +char *tokenizer_char_pointer(char *set) { + if (set != NULL) { + tokenizer_p = set; + return NULL; + } + + // Skip white space + while (*tokenizer_p && isspace(*tokenizer_p)) { + tokenizer_p++; + } + return tokenizer_p; +} + +static bool isvarchar(char c) { + if (c >= 'A' && c <= 'Z') { + return true; + } + + if (c == '$') { + return true; + } + + if (c >= '0' && c <= '9') { + return true; + } + + return false; +} + +token _find_registered(void) { + for (size_t i = 0; i < array_size(token_array); i++) { + token_entry *entry = (token_entry *)array_get(token_array, i); + if (entry->name == NULL) continue; + + if (strncmp(tokenizer_p, entry->name, strlen(entry->name)) == 0) { + tokenizer_next_p = tokenizer_p + strlen(entry->name); + tokenizer_p = tokenizer_next_p; + return entry->token; + } + } + return T_THE_END; +} + +token tokenizer_get_next_token(void) { + if (!*tokenizer_p) { + return T_EOF; + } + + // Skip white space + while (*tokenizer_p && isspace(*tokenizer_p)) { + tokenizer_p++; + } + + // Check for number + if (isdigit(*tokenizer_p) || *tokenizer_p == '.') { + tokenizer_next_p = tokenizer_p; + size_t l = 0; + while (*tokenizer_next_p && + (isdigit(*tokenizer_next_p) || *tokenizer_next_p == '.')) { + l++; + tokenizer_next_p++; + } + char number[l + 1]; + memset(number, 0, l + 1); + strncpy(number, tokenizer_p, l); + number[l] = '\0'; + tokenizer_p = tokenizer_next_p; + float f; + sscanf(number, "%f", &f); + tokenizer_actual_number = f; + return T_NUMBER; + } + + // Check for string + if ('"' == *tokenizer_p) { + tokenizer_p++; // skip " + tokenizer_next_p = tokenizer_p; + size_t l = 0; + while (*tokenizer_next_p && '"' != *tokenizer_next_p) { + l++; + tokenizer_next_p++; + } + + if (*tokenizer_next_p) { + tokenizer_next_p++; // skip trailing " + } + + if (l > 80) { + return T_ERROR; + } + + memcpy(tokenizer_actual_string, tokenizer_p, l); + tokenizer_actual_string[l] = '\0'; + + tokenizer_p = tokenizer_next_p; + + return T_STRING; + } + + token t = _find_registered(); + if (t != T_THE_END) { + return t; + } + + // Check for variable + tokenizer_next_p = tokenizer_p; + size_t len = 0; + while (*tokenizer_next_p && isvarchar(*tokenizer_next_p)) { + len++; + tokenizer_next_p++; + } + + if (len > tokenizer_variable_length) { + return T_ERROR; + } + + if (len > 0) { + memcpy(tokenizer_actual_variable, tokenizer_p, len); + tokenizer_actual_variable[len] = '\0'; + tokenizer_p = tokenizer_next_p; + if (tokenizer_actual_variable[len - 1] == '$') { + return T_VARIABLE_STRING; + } + return T_VARIABLE_NUMBER; + } + + return T_ERROR; +} + +float tokenizer_get_number(void) { return tokenizer_actual_number; } + +char *tokenizer_get_string(void) { return tokenizer_actual_string; } + +void tokenizer_get_variable_name(char *name) { + strncpy(name, tokenizer_actual_variable, sizeof(tokenizer_actual_variable)); +} + +void tokenizer_register_token(token_entry *entry) { + array_push(token_array, entry); +} + +void tokenizer_free_registered_tokens(void) { + array_destroy(token_array); +} diff --git a/libs/basic/src/variables.c b/libs/basic/src/variables.c new file mode 100644 index 0000000..33d468b --- /dev/null +++ b/libs/basic/src/variables.c @@ -0,0 +1,337 @@ +/** + * SPDX-License-Identifier: MIT + * SPDX-FileCopyrightText: 2015-2016 Johan Van den Brande + * + * @file variables.c + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include + +typedef union { + float num; + char* string; +} variable_value; + +struct variable { + char* name; + variable_type type; + variable_value value; + bool is_array; + size_t nr_dimensions; + size_t dimensions[5]; + array* array; +}; + +dictionary* _dictionary = NULL; + +const char* E_INDEX_OUT_OF_BOUNDS = "INDEX OUT OF BOUNDS"; +const char* E_VAR_NOT_FOUND = "VAR NOT FOUND"; + +static void vector_print(size_t* vector, size_t dimensions); + +bool variables_init(void) { + _dictionary = dictionary_new(); + return _dictionary != NULL; +} + +static void cb(char* name, void* value, void* context) { + variable* var = (variable*)value; + if (var->type == variable_type_string) { + if (var->value.string != NULL) free(var->value.string); + } + if (var->is_array) { + array_destroy(var->array); + } + if (var->name != NULL) free(var->name); + free(var); +} + +void variables_destroy(void) { dictionary_destroy(_dictionary, cb); } + +variable* variable_get(char* name) { return dictionary_get(_dictionary, name); } + +char* variable_get_string(char* name) { + variable* var = dictionary_get(_dictionary, name); + if (!var) { + var = variable_set_string(name, ""); + } + return var->value.string; +} + +float variable_get_numeric(char* name) { + variable* var = dictionary_get(_dictionary, name); + if (!var) { + var = variable_set_numeric(name, 0); + } + return var->value.num; +} + +variable* variable_set_string(char* name, char* value) { + variable* var = dictionary_get(_dictionary, name); + if (var == NULL) { + var = (variable*)malloc(sizeof(variable)); + var->name = strdup(name); + var->type = variable_type_string; + var->is_array = false; + } else { + if (var->value.string != NULL) { + free(var->value.string); + } + } + var->value.string = strdup(value); + dictionary_put(_dictionary, name, var); + return var; +} + +variable* variable_set_numeric(char* name, float value) { + variable* var = dictionary_get(_dictionary, name); + if (var == NULL) { + var = (variable*)malloc(sizeof(variable)); + var->name = strdup(name); + var->type = variable_type_numeric; + var->is_array = false; + } + var->value.num = value; + dictionary_put(_dictionary, name, var); + return var; +} + +variable_type variable_get_type(char* name) { + variable* var = dictionary_get(_dictionary, name); + return var->type; +} + +static size_t calc_size(variable* var) { + size_t size = 1; + for (size_t i = 0; i < var->nr_dimensions; ++i) { + size *= var->dimensions[i]; + } + return size; +} + +static bool check_in_bounds(variable* var, size_t* vector) { + for (size_t i = 0; i < var->nr_dimensions; i++) { + size_t vector_i = vector[i]; + if (vector_i > var->dimensions[i]) { + return false; + } + } + + return true; +} + +/* + + DIM A(2,3) + w h + x y + A(1,1) 0 + A(1,2) 1 + A(1,3) 2 + A(2,1) 3 + A(2,2) 4 + A(2,3) 5 + + (x*ySize*zSize + y*zSize + z) + + v[0] = x + v[1] = y + v[2] = z + + v[0] * d[1] * ... d[n] + v[1] * d[2] ... d[n] + ... + v[n] + + + */ + +static size_t calc_index(variable* var, size_t* vector) { + size_t index = 0; + for (size_t i = 0; i < var->nr_dimensions; ++i) { + // size_t product = vector[i] - 1; + size_t product = vector[i]; + for (size_t j = i + 1; j < var->nr_dimensions; ++j) { + product *= var->dimensions[j]; + } + index += product; + } + return index; +} + +variable* variable_array_init(char* name, variable_type type, size_t dimensions, size_t* vector) { + variable* var = (variable*)malloc(sizeof(variable)); + var->name = strdup(name); + var->is_array = true; + var->value.string = NULL; + var->value.num = 0; + var->type = type; + var->nr_dimensions = dimensions; + var->dimensions[0] = vector[0] + 1; + var->dimensions[1] = vector[1] + 1; + var->dimensions[2] = vector[2] + 1; + var->dimensions[3] = vector[3] + 1; + var->dimensions[4] = vector[4] + 1; + var->array = array_new(sizeof(variable_value)); + array_alloc(var->array, calc_size(var)); + dictionary_put(_dictionary, name, var); + return var; +} + +variable* variable_array_set_string(char* name, char* value, size_t* vector) { + variable* var = dictionary_get(_dictionary, name); + if (var == NULL) { + error(E_VAR_NOT_FOUND); + return NULL; + } + + if (!check_in_bounds(var, vector)) { + error(E_INDEX_OUT_OF_BOUNDS); + return NULL; + } + + size_t index = calc_index(var, vector); + variable_value val; + val.string = strdup(value); + array_set(var->array, index, &val); + + return var; +} + +char* variable_array_get_string(char* name, size_t* vector) { + variable* var = dictionary_get(_dictionary, name); + if (var == NULL) { + error(E_VAR_NOT_FOUND); + return NULL; + } + + if (!check_in_bounds(var, vector)) { + error(E_INDEX_OUT_OF_BOUNDS); + return NULL; + } + + size_t index = calc_index(var, vector); + variable_value* val = array_get(var->array, index); + + return val->string; +} + +variable* variable_array_set_numeric(char* name, float value, size_t* vector) { + variable* var = dictionary_get(_dictionary, name); + if (var == NULL) { + error(E_VAR_NOT_FOUND); + return NULL; + } + + if (!check_in_bounds(var, vector)) { + error(E_INDEX_OUT_OF_BOUNDS); + return NULL; + } + + // variable_dump(var); + size_t index = calc_index(var, vector); + // printf("index = %ld\n", index); + variable_value val; + val.num = value; + array_set(var->array, index, &val); + // variable_dump(var); + + return var; +} + +float variable_array_get_numeric(char* name, size_t* vector) { + variable* var = dictionary_get(_dictionary, name); + if (var == NULL) { + error(E_VAR_NOT_FOUND); + return 0; + } + + if (!check_in_bounds(var, vector)) { + error(E_INDEX_OUT_OF_BOUNDS); + return 0; + } + + size_t index = calc_index(var, vector); + variable_value* val = array_get(var->array, index); + + return val->num; +} + +struct each_v_ctx { + variables_each_cb cb; + void* context; +}; + +void each_v(char* name, void* value, void* context) { + struct each_v_ctx* ctx = (struct each_v_ctx*)context; + variable* var = (variable*)value; + ctx->cb(var, ctx->context); +} + +void variables_each(variables_each_cb each, void* context) { + struct each_v_ctx ctx = {.cb = each, .context = context}; + dictionary_each(_dictionary, each_v, &ctx); +} + +static void calc_vector(variable* var, size_t index, size_t* vector) { + size_t product = 1; + for (size_t i = 1; i < var->nr_dimensions; ++i) { + product *= var->dimensions[i]; + } + + for (int i = 0; i < var->nr_dimensions; ++i) { + vector[i] = index / product; + index %= product; + if ((i + 1) < var->nr_dimensions) { + product /= var->dimensions[i + 1]; + } + } +} + +static void vector_print(size_t* vector, size_t dimensions) { + for (size_t j = 0; j < dimensions; j++) { + printf("%ld", vector[j]); + if (j < dimensions - 1) { + printf(","); + } + } +} + +void variable_dump(variable* var) { + printf( + "-- variable\n" + "\tname:'%s'\n" + "\ttype: %s\n", + var->name, (var->type == variable_type_numeric) ? "number" : "string"); + + if (var->is_array) { + printf("\tdimensions: %ld\n", var->nr_dimensions); + for (size_t d = 0; d < var->nr_dimensions; d++) { + printf("\tdim %ld size = %ld\n", d, var->dimensions[d]); + } + printf("\tarray size: %ld\n", array_size(var->array)); + for (size_t i = 0; i < array_size(var->array); i++) { + size_t vector[5]; + calc_vector(var, i, vector); + printf("\t%3ld %s", i, var->name); + vector_print(vector, var->nr_dimensions); + printf(") = "); + variable_value* val = array_get(var->array, i); + if (var->type == variable_type_string) { + printf("%s\n", (val->string) ? val->string : ""); + } else { + printf("%f\n", val->num); + } + } + } else { + if (var->type == variable_type_numeric) { + printf("\tvalue: %f\n", var->value.num); + } else { + printf("\tvalue: '%s'\n", var->value.string); + } + } +}