| @@ -0,0 +1,36 @@ | |||
| ((c-mode . ((eval . (company-clang-set-prefix "main.c")) | |||
| (eval . (flycheck-mode 0)) | |||
| (eval . (rainbow-mode 0)))) | |||
| (nil . ((eval . (progn | |||
| (defvar context-mode-map (make-sparse-keymap) | |||
| "Keymap while context-mode is active.") | |||
| (define-minor-mode context-mode | |||
| "A temporary minor mode to be activated only specific to a buffer." | |||
| nil | |||
| :lighter " Context" | |||
| context-mode-map) | |||
| (context-mode 1) | |||
| ;; additional scripts | |||
| (defun save-and-find-test-script-and-compile () | |||
| (interactive) | |||
| (let ((build-script-name "test.bat")) | |||
| (save-and-find-build-script-and-compile))) | |||
| (defun save-and-find-run-script-and-compile () | |||
| (interactive) | |||
| (let ((build-script-name "run.bat")) | |||
| (save-and-find-build-script-and-compile))) | |||
| (defun save-and-find-debug-script-and-compile () | |||
| (interactive) | |||
| (let ((build-script-name "debug.bat")) | |||
| (save-and-find-build-script-and-compile))) | |||
| (defhydra hydra-context (context-mode-map "<f2>") | |||
| "Context Actions:" | |||
| ("b" save-and-find-build-script-and-compile "build" :color blue) | |||
| ("r" save-and-find-run-script-and-compile "run" :color blue) | |||
| ("d" save-and-find-debug-script-and-compile "debug" :color blue) | |||
| ("t" save-and-find-test-script-and-compile "test" :color blue) | |||
| ("q" nil "quit" :color blue)) | |||
| (define-key context-mode-map (kbd "<f2>") 'hydra-context/body) | |||
| ))))) | |||
| @@ -3,4 +3,6 @@ | |||
| /*.ilk | |||
| /*.pdb | |||
| /.vs/ | |||
| /vs/ | |||
| /vs/ | |||
| /*.exe | |||
| *.exe | |||
| @@ -1 +1,12 @@ | |||
| (/ (+ 1 (- 100 7 3)) 2) | |||
| ((lambda (x) (* x x)) 2) | |||
| (setq a 3) | |||
| (let ((a 10)) | |||
| (let ((b 5)) | |||
| (setq a 4)) | |||
| a) | |||
| a | |||
| @@ -2,8 +2,6 @@ | |||
| @setlocal | |||
| pushd %~dp0 | |||
| set start=%time% | |||
| set exeName=lisp.exe | |||
| set binDir=bin | |||
| @@ -17,11 +15,6 @@ if %errorlevel% == 0 ( | |||
| echo. | |||
| if not exist ..\%binDir% mkdir ..\%binDir% | |||
| move %exeName% ..\%binDir%\ > NUL | |||
| pushd ..\%binDir% | |||
| echo ---------- Running ---------- | |||
| call timecmd %exeName% test.lsp | |||
| del %exeName% /S /Q > NUL | |||
| popd | |||
| ) else ( | |||
| echo. | |||
| echo Fucki'n 'ell | |||
| @@ -0,0 +1,3 @@ | |||
| pushd %~dp0\vs | |||
| start lisp.sln | |||
| popd | |||
| @@ -0,0 +1,9 @@ | |||
| @echo off | |||
| pushd %~dp0\bin | |||
| call ..\build.bat | |||
| if %errorlevel% == 0 ( | |||
| echo ---------- Running ---------- | |||
| call timecmd lisp.exe | |||
| ) | |||
| popd | |||
| @@ -1,3 +0,0 @@ | |||
| ((c-mode . ((eval . (company-clang-set-prefix "main.c")) | |||
| (eval . (flycheck-mode 0)) | |||
| (eval . (rainbow-mode 0))))) | |||
| @@ -1,7 +1,8 @@ | |||
| struct Environment { | |||
| struct Environment* parent; | |||
| int key_count; | |||
| int capacity; | |||
| int next_index; | |||
| char** keys; | |||
| Ast_Node* values; | |||
| }; | |||
| @@ -11,16 +12,37 @@ typedef struct Environment Environment; | |||
| Environment* create_empty_environment() { | |||
| Environment* env = new(Environment); | |||
| env->parent = nullptr; | |||
| env->key_count = 0; | |||
| env->keys = nullptr; | |||
| env->values = nullptr; | |||
| int start_capacity = 1; | |||
| env->parent = nullptr; | |||
| env->capacity = start_capacity; | |||
| env->next_index = 0; | |||
| env->keys = (char**)malloc(start_capacity * sizeof(char*)); | |||
| env->values = (Ast_Node*)malloc(start_capacity * sizeof(Ast_Node)); | |||
| return env; | |||
| } | |||
| void define_symbol(Ast_Node* symbol, Ast_Node* value, Environment* env) { | |||
| // NOTE(Felix): right now we are simmply adding the symol at the | |||
| // back of the list without checking if it already exists but are | |||
| // also searching for thesymbol from the back, so we will find the | |||
| // latest defined one first, but a bit messy. Later we should use | |||
| // a hashmap here. @refactor | |||
| if (env->next_index == env->capacity) { | |||
| env->capacity *= 2; | |||
| env->keys = (char**)realloc(env->keys, env->capacity * sizeof(char*)); | |||
| env->values = (Ast_Node*)realloc(env->values, env->capacity * sizeof(Ast_Node)); | |||
| } | |||
| env->keys [env->next_index] = symbol->value.symbol->identifier; | |||
| env->values[env->next_index] = *value; | |||
| ++env->next_index; | |||
| } | |||
| Ast_Node* lookup_symbol(Symbol* sym, Environment* env) { | |||
| for (int i = 0; i < env->key_count; ++i) | |||
| for (int i = env->next_index - 1; i >= 0; --i) | |||
| if (string_equal(env->keys[i], sym->identifier)) | |||
| return env->values+i; | |||
| @@ -30,19 +52,19 @@ Ast_Node* lookup_symbol(Symbol* sym, Environment* env) { | |||
| char* built_in_names[] = { | |||
| // Math stuff | |||
| "+", "-", "*", "/", | |||
| ">", "<", "=", | |||
| ">", "<", "=", "<=", ">=", | |||
| // Conditional stuff | |||
| "if", "and", "or", "not", | |||
| // Cons stuff | |||
| "first", "rest", "pair", | |||
| // rest | |||
| "load", "define", | |||
| "load", "define", | |||
| "lambda", "progn", | |||
| "eval", "quote", | |||
| "print", "read", | |||
| "help" | |||
| "eval", "quote", | |||
| "print", "read", | |||
| "help", "exit" | |||
| }; | |||
| int built_in_count = 23; | |||
| int built_in_count = 26; | |||
| for (int i = 0; i < built_in_count; ++i) { | |||
| if (string_equal(built_in_names[i], sym->identifier)) { | |||
| @@ -7,6 +7,8 @@ typedef enum { | |||
| Error_Type_Not_Yet_Implemented, | |||
| Error_Type_Syntax_Error, | |||
| Error_Type_Unexpected_Eof, | |||
| Error_Type_Unbalanced_Parenthesis, | |||
| Error_Type_Trailing_Garbage, | |||
| Error_Type_Unknown_Error, | |||
| } Error_Type; | |||
| @@ -34,15 +36,16 @@ void create_error(Error_Type type, Ast_Node* location) { | |||
| char* Error_Type_to_string(Error_Type type) { | |||
| switch (type) { | |||
| case Error_Type_Ill_Formed_List: return "Ill formed list"; | |||
| case Error_Type_Not_A_Function: return "Not a function"; | |||
| case Error_Type_Symbol_Not_Defined: return "Symbol not defined"; | |||
| case Error_Type_Wrong_Number_Of_Arguments: return "Wrong number of arguments"; | |||
| case Error_Type_Type_Missmatch: return "Type Missmatch"; | |||
| case Error_Type_Not_Yet_Implemented: return "Not yet implemented"; | |||
| case Error_Type_Ill_Formed_List: return "Evaluation-error: Ill formed list"; | |||
| case Error_Type_Not_A_Function: return "Evaluation-error: Not a function"; | |||
| case Error_Type_Symbol_Not_Defined: return "Evaluation-error: Symbol not defined"; | |||
| case Error_Type_Wrong_Number_Of_Arguments: return "Evaluation-error: Wrong number of arguments"; | |||
| case Error_Type_Type_Missmatch: return "Evaluation-error: Type Missmatch"; | |||
| case Error_Type_Not_Yet_Implemented: return "Evaluation-error: Not yet implemented"; | |||
| case Error_Type_Trailing_Garbage: return "Evaluation-error: Trailing garbage following expression"; | |||
| case Error_Type_Syntax_Error: return "Syntax Error"; | |||
| case Error_Type_Unexpected_Eof: return "Unexpected EOF"; | |||
| case Error_Type_Unexpected_Eof: return "Parsing-error: Unexpected EOF"; | |||
| case Error_Type_Unbalanced_Parenthesis: return "Parsing-error: Unbalanced parenthesis"; | |||
| default: return "Unknown Error"; | |||
| } | |||
| } | |||
| @@ -1,9 +1,5 @@ | |||
| /* Ast_Node* apply_to_lambda () {} */ | |||
| /* Ast_Node* apply_to_built_in (Ast_Node* function, Ast_Node* arguments) { */ | |||
| /* } */ | |||
| Ast_Node* eval_expr(Ast_Node* node, Environment* env); | |||
| bool is_truthy (Ast_Node* expression, Environment* env); | |||
| @@ -43,10 +39,10 @@ void eval_operands(Ast_Node* operands, Environment* env) { | |||
| Ast_Node* eval_expr(Ast_Node* node, Environment* env) { | |||
| #define report_error(_type) { \ | |||
| create_error(_type, node); \ | |||
| log_error(); \ | |||
| exit(1); \ | |||
| return nullptr; \ | |||
| } | |||
| if (error) | |||
| return nullptr; | |||
| Ast_Node* ret = new(Ast_Node); | |||
| switch (node->type) { | |||
| @@ -56,31 +52,112 @@ Ast_Node* eval_expr(Ast_Node* node, Environment* env) { | |||
| case Ast_Node_Type_Symbol: | |||
| return lookup_symbol(node->value.symbol, env); | |||
| case Ast_Node_Type_Number: | |||
| case Ast_Node_Type_Keyword: | |||
| case Ast_Node_Type_String: | |||
| return node; | |||
| case Ast_Node_Type_Pair: { | |||
| Ast_Node* operator = eval_expr(node->value.pair->first, env); | |||
| if (error) return nullptr; | |||
| Ast_Node* operands = node->value.pair->rest; | |||
| // check for built ins functions | |||
| if (operator->type == Ast_Node_Type_Built_In_Function) { | |||
| char* operator_name = operator->value.built_in_function->identifier; | |||
| if (string_equal("quote", operator_name)) { | |||
| return node->value.pair->rest; | |||
| int operands_length = list_length(operands); | |||
| if (operands_length != 1) { | |||
| report_error(Error_Type_Wrong_Number_Of_Arguments); | |||
| } | |||
| return operands->value.pair->first; | |||
| } else if (string_equal("eval", operator_name)) { | |||
| int operands_length = list_length(operands); | |||
| if (operands_length != 1) { | |||
| report_error(Error_Type_Wrong_Number_Of_Arguments); | |||
| } | |||
| if (error) return nullptr; | |||
| return eval_expr(operands->value.pair->first, env); | |||
| } else if (string_equal("+", operator_name)) { | |||
| eval_operands(operands, env); | |||
| if (error) return nullptr; | |||
| return built_in_add(operands); | |||
| } else if (string_equal("-", operator_name)) { | |||
| eval_operands(operands, env); | |||
| if (error) return nullptr; | |||
| return built_in_substract(operands); | |||
| } else if (string_equal("*", operator_name)) { | |||
| eval_operands(operands, env); | |||
| if (error) return nullptr; | |||
| return built_in_multiply(operands); | |||
| } else if (string_equal("/", operator_name)) { | |||
| eval_operands(operands, env); | |||
| if (error) return nullptr; | |||
| return built_in_divide(operands); | |||
| } else if (string_equal("define", operator_name)) { | |||
| int operands_length = list_length(operands); | |||
| if (error) return nullptr; | |||
| if (operands_length != 2) { | |||
| report_error(Error_Type_Wrong_Number_Of_Arguments); | |||
| } | |||
| Ast_Node* symbol = operands->value.pair->first; | |||
| if (symbol->type != Ast_Node_Type_Symbol) | |||
| report_error(Error_Type_Type_Missmatch); | |||
| Ast_Node* value = operands->value.pair->rest->value.pair->first; | |||
| value = eval_expr(value, env); | |||
| define_symbol(symbol, value, env); | |||
| return value; | |||
| } else if (string_equal("exit", operator_name)) { | |||
| int operands_length = list_length(operands); | |||
| if (error) return nullptr; | |||
| if (operands_length > 1) { | |||
| report_error(Error_Type_Wrong_Number_Of_Arguments); | |||
| } | |||
| if (operands_length == 1) { | |||
| Ast_Node* error_code = operands->value.pair->first; | |||
| if (error_code->type != Ast_Node_Type_Number) | |||
| report_error(Error_Type_Type_Missmatch); | |||
| exit((int)error_code->value.number->value); | |||
| } | |||
| exit(0); | |||
| } else if (string_equal("print", operator_name)) { | |||
| int operands_length = list_length(operands); | |||
| if (error) return nullptr; | |||
| if (operands_length != 1) { | |||
| report_error(Error_Type_Wrong_Number_Of_Arguments); | |||
| } | |||
| eval_operands(operands, env); | |||
| if (error) return nullptr; | |||
| print(operands->value.pair->first); | |||
| printf("\n"); | |||
| return operands->value.pair->first; | |||
| } else if (string_equal("read", operator_name)) { | |||
| int operands_length = list_length(operands); | |||
| if (error) return nullptr; | |||
| if (operands_length > 1) { | |||
| report_error(Error_Type_Wrong_Number_Of_Arguments); | |||
| } | |||
| if (operands_length == 1) { | |||
| eval_operands(operands, env); | |||
| if (error) return nullptr; | |||
| Ast_Node* prompt = operands->value.pair->first; | |||
| if (prompt->type == Ast_Node_Type_String) | |||
| printf("%s", prompt->value.string->value); | |||
| else | |||
| print(operands->value.pair->first); | |||
| } | |||
| char* line = read_line(); | |||
| return create_ast_node_string(line, (int)strlen(line)); | |||
| } else if (string_equal("if", operator_name)) { | |||
| int operands_length = list_length(operands); | |||
| if (error) return nullptr; | |||
| if (operands_length != 2 && operands_length != 3) { | |||
| report_error(Error_Type_Wrong_Number_Of_Arguments); | |||
| } | |||
| @@ -89,7 +166,9 @@ Ast_Node* eval_expr(Ast_Node* node, Environment* env) { | |||
| Ast_Node* then_part = operands->value.pair->rest; | |||
| Ast_Node* else_part = then_part->value.pair->rest; | |||
| if (is_truthy(condition, env)) | |||
| bool truthy = is_truthy(condition, env); | |||
| if (error) return nullptr; | |||
| if (truthy) | |||
| return eval_expr(then_part->value.pair->first, env); | |||
| else if (operands_length == 3) | |||
| return eval_expr(else_part->value.pair->first, env); | |||
| @@ -102,6 +181,7 @@ Ast_Node* eval_expr(Ast_Node* node, Environment* env) { | |||
| report_error(Error_Type_Ill_Formed_List); | |||
| } | |||
| result &= is_truthy(operands->value.pair->first, env); | |||
| if (error) return nullptr; | |||
| operands = operands->value.pair->rest; | |||
| if (!result) return create_ast_node_nil(); | |||
| @@ -116,6 +196,7 @@ Ast_Node* eval_expr(Ast_Node* node, Environment* env) { | |||
| report_error(Error_Type_Ill_Formed_List); | |||
| } | |||
| result |= is_truthy(operands->value.pair->first, env); | |||
| if (error) return nullptr; | |||
| operands = operands->value.pair->rest; | |||
| if (result) return create_ast_node_number(1);; | |||
| @@ -129,6 +210,7 @@ Ast_Node* eval_expr(Ast_Node* node, Environment* env) { | |||
| return nullptr; | |||
| } | |||
| bool truthy = is_truthy(operands->value.pair->first, env); | |||
| if (error) return nullptr; | |||
| if (truthy) | |||
| return create_ast_node_nil(); | |||
| return create_ast_node_number(1); | |||
| @@ -150,6 +232,7 @@ Ast_Node* eval_expr(Ast_Node* node, Environment* env) { | |||
| bool is_truthy (Ast_Node* expression, Environment* env) { | |||
| Ast_Node* result = eval_expr(expression, env); | |||
| if (error) return false; | |||
| switch (result->type) { | |||
| case Ast_Node_Type_Nil: return false; | |||
| /* case Ast_Node_Type_Number: return result->value.number->value != 0; */ | |||
| @@ -16,46 +16,66 @@ | |||
| #include "./eval.c" | |||
| #include "./testing.c" | |||
| int interprete_file (char* file_content) { | |||
| Ast_Node_Array_List* program = parse_program(file_content); | |||
| if (error) { | |||
| log_error(); | |||
| return 1; | |||
| } | |||
| Environment* env = create_empty_environment(); | |||
| Ast_Node* result = create_ast_node_nil(); | |||
| for (int i = 0; i < program->next_index; ++i) { | |||
| result = eval_expr(program->data[i], env); | |||
| if (error) { | |||
| log_error(); | |||
| return 1; | |||
| } | |||
| } | |||
| print(result); | |||
| printf("\n"); | |||
| return 0; | |||
| } | |||
| int interprete_stdin () { | |||
| printf("Welcome to the lispy interpreter.\n"); | |||
| char* line; | |||
| Environment* env = create_empty_environment(); | |||
| Ast_Node* parsed, * evaluated; | |||
| while (true) { | |||
| printf(">"); | |||
| line = read_line(); | |||
| parsed = parse_single_expression(line); | |||
| if (error) { | |||
| log_error(); | |||
| delete_error(); | |||
| continue; | |||
| } | |||
| evaluated = eval_expr(parsed, env); | |||
| if (error) { | |||
| log_error(); | |||
| delete_error(); | |||
| continue; | |||
| } | |||
| print(evaluated); | |||
| printf("\n"); | |||
| } | |||
| return 0; | |||
| } | |||
| int main (int argc, char *argv[]) { | |||
| if (argc > 1) { | |||
| char* fileContent = read_entire_file(argv[1]); | |||
| if (fileContent) { | |||
| Ast_Node_Array_List* program = parse_program(fileContent); | |||
| if (error) { | |||
| log_error(); | |||
| exit(1); | |||
| } | |||
| Environment* env = create_empty_environment(); | |||
| Ast_Node* result = create_ast_node_nil(); | |||
| for (int i = 0; i < program->next_index; ++i) { | |||
| result = eval_expr(program->data[i], env); | |||
| if (error) { | |||
| log_error(); | |||
| exit(1); | |||
| } | |||
| } | |||
| print(result); | |||
| printf("\n"); | |||
| char* file_content = read_entire_file(argv[1]); | |||
| if (file_content) { | |||
| return interprete_file(file_content); | |||
| } else { | |||
| printf("The file could not be read\n"); | |||
| return 1; | |||
| } | |||
| } else { | |||
| run_all_tests(); | |||
| printf("Welcome to the lispy interpreter.\n"); | |||
| char* line; | |||
| Environment* env = create_empty_environment(); | |||
| Ast_Node* parsed, * evaluated; | |||
| while (true) { | |||
| printf(">"); | |||
| line = read_line(); | |||
| parsed = parse_single_expression(line); | |||
| evaluated = eval_expr(parsed, env); | |||
| print(evaluated); | |||
| printf("\n"); | |||
| } | |||
| return interprete_stdin(); | |||
| } | |||
| return 0; | |||
| } | |||
| @@ -18,10 +18,8 @@ Ast_Node_Array_List* create_Ast_Node_Array_List() { | |||
| void append_to_Ast_Node_Array_List(Ast_Node_Array_List* list, Ast_Node* node) { | |||
| if (list->next_index == list->length) { | |||
| list->length *= 2; | |||
| list->data = | |||
| (Ast_Node**)realloc(list->data, list->length * sizeof(Ast_Node)); | |||
| list->data = (Ast_Node**)realloc(list->data, list->length * sizeof(Ast_Node)); | |||
| } | |||
| list->data[list->next_index++] = node; | |||
| } | |||
| @@ -231,7 +229,17 @@ Ast_Node* parse_expression(char* text, int* index_in_text) { | |||
| Ast_Node* parse_single_expression(char* text) { | |||
| int index_in_text = 0; | |||
| return parse_expression(text, &index_in_text); | |||
| Ast_Node* result; | |||
| eat_until_code(text, &index_in_text); | |||
| if (text[(index_in_text)] == '(') | |||
| result = parse_expression(text, &index_in_text); | |||
| else | |||
| result = parse_atom(text, &index_in_text); | |||
| eat_until_code(text, &index_in_text); | |||
| if (text[(index_in_text)] == '\0') | |||
| return result; | |||
| create_error(Error_Type_Trailing_Garbage, create_ast_node_nil()); | |||
| return nullptr; | |||
| } | |||
| Ast_Node_Array_List* parse_program(char* text) { | |||
| @@ -247,13 +255,11 @@ Ast_Node_Array_List* parse_program(char* text) { | |||
| return nullptr; | |||
| append_to_Ast_Node_Array_List(program, parsed); | |||
| } break; | |||
| case ';': { | |||
| eat_comment_line(text, &index_in_text); | |||
| } break; | |||
| case ';': | |||
| case ' ': | |||
| case '\t': | |||
| case '\n': { | |||
| ++index_in_text; | |||
| eat_until_code(text, &index_in_text); | |||
| } break; | |||
| default: | |||
| /* syntax error */ | |||
| @@ -0,0 +1,10 @@ | |||
| @echo off | |||
| pushd %~dp0\bin | |||
| call ..\build.bat | |||
| if %errorlevel% == 0 ( | |||
| echo ---------- Testing ---------- | |||
| call timecmd lisp.exe test.lsp | |||
| ) | |||
| popd | |||