| @@ -1,4 +1,4 @@ | |||||
| ((c-mode . ((eval . (company-clang-set-prefix "main.c")) | |||||
| ((c++-mode . ((eval . (company-clang-set-prefix "main.cpp")) | |||||
| (eval . (flycheck-mode 0)) | (eval . (flycheck-mode 0)) | ||||
| (eval . (rainbow-mode 0)))) | (eval . (rainbow-mode 0)))) | ||||
| (nil . ((eval . (progn | (nil . ((eval . (progn | ||||
| @@ -0,0 +1,22 @@ | |||||
| ACME_MODULE( | |||||
| #========================================================================== | |||||
| # general module information | |||||
| #========================================================================== | |||||
| NAME TSE_LispIntegration | |||||
| TYPE LIBRARY | |||||
| #========================================================================== | |||||
| # files of this module | |||||
| #========================================================================== | |||||
| INCLUDE_BASE src | |||||
| bin | |||||
| FILES_PRIVATE_HEADER src/*.h | |||||
| FILES_SOURCE src/main.cpp | |||||
| #DEPENDENCIES TSE_Engine | |||||
| ) | |||||
| file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/bin/pre.slime | |||||
| DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) | |||||
| file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/bin/test.slime | |||||
| DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) | |||||
| @@ -1,4 +1,12 @@ | |||||
| (defun ! (n) | |||||
| (ifs (< n 2) | |||||
| 1 | |||||
| (* n (! (- n 1))))) | |||||
| (defun make-vector (x y z) | |||||
| (let ((local-x x) | |||||
| (local-y y) | |||||
| (local-z z)) | |||||
| (lambda () | |||||
| local-x))) | |||||
| (define v (make-vector 1 2 3)) | |||||
| (print (v)) | |||||
| (read " ") | |||||
| @@ -11,7 +11,7 @@ pushd build | |||||
| taskkill /F /IM %exeName% > NUL 2> NUL | taskkill /F /IM %exeName% > NUL 2> NUL | ||||
| echo ---------- Compiling ---------- | echo ---------- Compiling ---------- | ||||
| call timecmd cl ../src/main.c /Fe%exeName% /D_DEBUG /W3 /TC /Zi /nologo /EHsc /link | |||||
| call timecmd cl ../src/main.cpp /Fe%exeName% /D_DEBUG /W3 /Zi /nologo /EHsc /link /NODEFAULTLIB:libucrt libucrtd.lib | |||||
| if %errorlevel% == 0 ( | if %errorlevel% == 0 ( | ||||
| echo. | echo. | ||||
| @@ -12,7 +12,7 @@ typedef enum { | |||||
| Ast_Node_Type_Pair, | Ast_Node_Type_Pair, | ||||
| Ast_Node_Type_Function, | Ast_Node_Type_Function, | ||||
| Ast_Node_Type_Built_In_Function, | |||||
| Ast_Node_Type_CFunction, | |||||
| } Ast_Node_Type; | } Ast_Node_Type; | ||||
| char* Ast_Node_Type_to_string(Ast_Node_Type type) { | char* Ast_Node_Type_to_string(Ast_Node_Type type) { | ||||
| @@ -24,7 +24,7 @@ char* Ast_Node_Type_to_string(Ast_Node_Type type) { | |||||
| case(Ast_Node_Type_Symbol): return "symbol"; | case(Ast_Node_Type_Symbol): return "symbol"; | ||||
| case(Ast_Node_Type_Keyword): return "keyword"; | case(Ast_Node_Type_Keyword): return "keyword"; | ||||
| case(Ast_Node_Type_Function): return "function"; | case(Ast_Node_Type_Function): return "function"; | ||||
| case(Ast_Node_Type_Built_In_Function): return "built-in function"; | |||||
| case(Ast_Node_Type_CFunction): return "C-function"; | |||||
| case(Ast_Node_Type_Pair): return "pair"; | case(Ast_Node_Type_Pair): return "pair"; | ||||
| } | } | ||||
| return "unknown"; | return "unknown"; | ||||
| @@ -122,96 +122,10 @@ typedef struct { | |||||
| struct Ast_Node* body; // implicit prog | struct Ast_Node* body; // implicit prog | ||||
| } Function; | } Function; | ||||
| typedef enum { | |||||
| Built_In_Addition, | |||||
| Built_In_And, | |||||
| Built_In_Breakpoint, | |||||
| Built_In_Copy, | |||||
| Built_In_Define, | |||||
| Built_In_Division, | |||||
| Built_In_Equal, | |||||
| Built_In_Eval, | |||||
| Built_In_Error, | |||||
| Built_In_Exit, | |||||
| Built_In_First, | |||||
| Built_In_Greater, | |||||
| Built_In_Greater_Equal, | |||||
| Built_In_If, | |||||
| Built_In_Info, | |||||
| Built_In_Lambda, | |||||
| Built_In_Less, | |||||
| Built_In_Less_Equal, | |||||
| Built_In_Let, | |||||
| Built_In_List, | |||||
| Built_In_Load, | |||||
| Built_In_Macro, | |||||
| Built_In_Macro_Define, | |||||
| Built_In_Multiplication, | |||||
| Built_In_Mutate, | |||||
| Built_In_Not, | |||||
| Built_In_Or, | |||||
| Built_In_Pair, | |||||
| Built_In_Print, | |||||
| Built_In_Prog, | |||||
| Built_In_Quote, | |||||
| Built_In_Read, | |||||
| Built_In_Rest, | |||||
| Built_In_Subtraction, | |||||
| Built_In_Try, | |||||
| Built_In_Type, | |||||
| Built_In_While, | |||||
| } Built_In_Name; | |||||
| /** | |||||
| This is used only for printing. | |||||
| **/ | |||||
| char* Built_In_Name_to_string(Built_In_Name name) { | |||||
| switch (name) { | |||||
| case Built_In_Addition: return "+"; | |||||
| case Built_In_And: return "and"; | |||||
| case Built_In_Breakpoint: return "breakpoint"; | |||||
| case Built_In_Copy: return "copy"; | |||||
| case Built_In_Define: return "define"; | |||||
| case Built_In_Division: return "/"; | |||||
| case Built_In_Equal: return "="; | |||||
| case Built_In_Error: return "error"; | |||||
| case Built_In_Eval: return "eval"; | |||||
| case Built_In_Exit: return "exit"; | |||||
| case Built_In_First: return "first"; | |||||
| case Built_In_Greater: return ">"; | |||||
| case Built_In_Greater_Equal: return ">="; | |||||
| case Built_In_If: return "if"; | |||||
| case Built_In_Info: return "info"; | |||||
| case Built_In_Lambda: return "lambda"; | |||||
| case Built_In_Less: return "<"; | |||||
| case Built_In_Less_Equal: return "<="; | |||||
| case Built_In_Let: return "let"; | |||||
| case Built_In_List: return "list"; | |||||
| case Built_In_Load: return "load"; | |||||
| case Built_In_Macro: return "macro"; | |||||
| case Built_In_Macro_Define: return "macro-define"; | |||||
| case Built_In_Multiplication: return "*"; | |||||
| case Built_In_Mutate: return "mutate"; | |||||
| case Built_In_Not: return "not"; | |||||
| case Built_In_Or: return "or"; | |||||
| case Built_In_Pair: return "pair"; | |||||
| case Built_In_Print: return "print"; | |||||
| case Built_In_Prog: return "prog"; | |||||
| case Built_In_Quote: return "quote"; | |||||
| case Built_In_Read: return "read"; | |||||
| case Built_In_Rest: return "rest"; | |||||
| case Built_In_Subtraction: return "-"; | |||||
| case Built_In_Try: return "try"; | |||||
| case Built_In_Type: return "type"; | |||||
| case Built_In_While: return "while"; | |||||
| } | |||||
| return "Built in string missing in Built_In_Name_to_string"; | |||||
| } | |||||
| struct Environment; | |||||
| typedef struct { | typedef struct { | ||||
| Built_In_Name type; | |||||
| } Built_In_Function; | |||||
| std::function<Ast_Node*(Ast_Node*, Environment*)> function; | |||||
| } CFunction; | |||||
| struct Ast_Node { | struct Ast_Node { | ||||
| Source_Code_Location* sourceCodeLocation; | Source_Code_Location* sourceCodeLocation; | ||||
| @@ -223,7 +137,7 @@ struct Ast_Node { | |||||
| String* string; | String* string; | ||||
| Pair* pair; | Pair* pair; | ||||
| Function* function; | Function* function; | ||||
| Built_In_Function* built_in_function; | |||||
| CFunction* cfunction; | |||||
| } value; | } value; | ||||
| }; | }; | ||||
| // was forward declarated | // was forward declarated | ||||
| @@ -290,52 +204,11 @@ Ast_Node* create_ast_node_keyword(char* keyword) { | |||||
| return node; | return node; | ||||
| } | } | ||||
| Ast_Node* create_ast_node_built_in_function(char* name) { | |||||
| Built_In_Name type; | |||||
| if (string_equal(name, "+")) type = Built_In_Addition; | |||||
| else if (string_equal(name, "*")) type = Built_In_Multiplication; | |||||
| else if (string_equal(name, "-")) type = Built_In_Subtraction; | |||||
| else if (string_equal(name, "/")) type = Built_In_Division; | |||||
| else if (string_equal(name, "<")) type = Built_In_Less; | |||||
| else if (string_equal(name, "<=")) type = Built_In_Less_Equal; | |||||
| else if (string_equal(name, "=")) type = Built_In_Equal; | |||||
| else if (string_equal(name, ">")) type = Built_In_Greater; | |||||
| else if (string_equal(name, ">=")) type = Built_In_Greater_Equal; | |||||
| else if (string_equal(name, "and")) type = Built_In_And; | |||||
| else if (string_equal(name, "breakpoint")) type = Built_In_Breakpoint; | |||||
| else if (string_equal(name, "copy")) type = Built_In_Copy; | |||||
| else if (string_equal(name, "define")) type = Built_In_Define; | |||||
| else if (string_equal(name, "error")) type = Built_In_Error; | |||||
| else if (string_equal(name, "eval")) type = Built_In_Eval; | |||||
| else if (string_equal(name, "exit")) type = Built_In_Exit; | |||||
| else if (string_equal(name, "first")) type = Built_In_First; | |||||
| else if (string_equal(name, "if")) type = Built_In_If; | |||||
| else if (string_equal(name, "info")) type = Built_In_Info; | |||||
| else if (string_equal(name, "info")) type = Built_In_Info; | |||||
| else if (string_equal(name, "lambda")) type = Built_In_Lambda; | |||||
| else if (string_equal(name, "let")) type = Built_In_Let; | |||||
| else if (string_equal(name, "list")) type = Built_In_List; | |||||
| else if (string_equal(name, "load")) type = Built_In_Load; | |||||
| else if (string_equal(name, "macro")) type = Built_In_Macro; | |||||
| else if (string_equal(name, "macro-define")) type = Built_In_Macro_Define; | |||||
| else if (string_equal(name, "mutate")) type = Built_In_Mutate; | |||||
| else if (string_equal(name, "not")) type = Built_In_Not; | |||||
| else if (string_equal(name, "or")) type = Built_In_Or; | |||||
| else if (string_equal(name, "pair")) type = Built_In_Pair; | |||||
| else if (string_equal(name, "print")) type = Built_In_Print; | |||||
| else if (string_equal(name, "prog")) type = Built_In_Prog; | |||||
| else if (string_equal(name, "quote")) type = Built_In_Quote; | |||||
| else if (string_equal(name, "read")) type = Built_In_Read; | |||||
| else if (string_equal(name, "rest")) type = Built_In_Rest; | |||||
| else if (string_equal(name, "try")) type = Built_In_Try; | |||||
| else if (string_equal(name, "type")) type = Built_In_Type; | |||||
| else if (string_equal(name, "while")) type = Built_In_While; | |||||
| else return nullptr; | |||||
| Ast_Node* create_ast_node_cfunction(std::function<Ast_Node*(Ast_Node*, Environment*)> function) { | |||||
| Ast_Node* node = create_ast_node(); | Ast_Node* node = create_ast_node(); | ||||
| node->type = Ast_Node_Type_Built_In_Function; | |||||
| node->value.built_in_function = new(Built_In_Function); | |||||
| node->value.built_in_function->type = type; | |||||
| node->type = Ast_Node_Type_CFunction; | |||||
| node->value.cfunction = new(CFunction); | |||||
| node->value.cfunction->function = function; | |||||
| return node; | return node; | ||||
| } | } | ||||
| @@ -1,218 +0,0 @@ | |||||
| Ast_Node* eval_expr(Ast_Node* node, Environment* env); | |||||
| bool ast_node_equal(Ast_Node* n1, Ast_Node* n2) { | |||||
| if (n1 == n2) | |||||
| return true; | |||||
| if (n1->type != n2->type) | |||||
| return false; | |||||
| switch (n1->type) { | |||||
| case Ast_Node_Type_Built_In_Function: | |||||
| return n1->value.built_in_function->type | |||||
| == n2->value.built_in_function->type; | |||||
| case Ast_Node_Type_Function: | |||||
| // if they have the same pointer, true is | |||||
| // returned a few lines above | |||||
| return false; | |||||
| case Ast_Node_Type_Keyword: | |||||
| return string_equal( | |||||
| n1->value.keyword->identifier, | |||||
| n2->value.keyword->identifier); | |||||
| case Ast_Node_Type_T: | |||||
| case Ast_Node_Type_Nil: | |||||
| return true; | |||||
| case Ast_Node_Type_Number: | |||||
| return | |||||
| n1->value.number->value == | |||||
| n2->value.number->value; | |||||
| case Ast_Node_Type_Pair: | |||||
| create_error(Error_Type_Not_Yet_Implemented, n1->sourceCodeLocation); | |||||
| return false; | |||||
| case Ast_Node_Type_String: | |||||
| return string_equal( | |||||
| n1->value.string->value, | |||||
| n2->value.string->value); | |||||
| case Ast_Node_Type_Symbol: | |||||
| return string_equal( | |||||
| n1->value.symbol->identifier, | |||||
| n2->value.symbol->identifier); | |||||
| } | |||||
| // we should never reach here | |||||
| return false; | |||||
| } | |||||
| Ast_Node* built_in_equals(Ast_Node* operands) { | |||||
| if (operands->type == Ast_Node_Type_Nil) | |||||
| return create_ast_node_t(); | |||||
| Ast_Node* first = operands->value.pair->first; | |||||
| while (operands->type == Ast_Node_Type_Pair) { | |||||
| if (!ast_node_equal(operands->value.pair->first, first)) | |||||
| return create_ast_node_nil(); | |||||
| operands = operands->value.pair->rest; | |||||
| } | |||||
| return create_ast_node_t(); | |||||
| } | |||||
| Ast_Node* built_in_greater(Ast_Node* operands) { | |||||
| double last_number = strtod("Inf", NULL); | |||||
| while (operands->type == Ast_Node_Type_Pair) { | |||||
| try { | |||||
| assert_type(operands->value.pair->first, Ast_Node_Type_Number); | |||||
| } | |||||
| if (operands->value.pair->first->value.number->value >= last_number) | |||||
| return create_ast_node_nil(); | |||||
| last_number = operands->value.pair->first->value.number->value; | |||||
| operands = operands->value.pair->rest; | |||||
| } | |||||
| return create_ast_node_t(); | |||||
| } | |||||
| Ast_Node* built_in_greater_equal(Ast_Node* operands) { | |||||
| double last_number = strtod("Inf", NULL); | |||||
| while (operands->type == Ast_Node_Type_Pair) { | |||||
| try { | |||||
| assert_type(operands->value.pair->first, Ast_Node_Type_Number); | |||||
| } | |||||
| if (operands->value.pair->first->value.number->value > last_number) | |||||
| return create_ast_node_nil(); | |||||
| last_number = operands->value.pair->first->value.number->value; | |||||
| operands = operands->value.pair->rest; | |||||
| } | |||||
| return create_ast_node_t(); | |||||
| } | |||||
| Ast_Node* built_in_less(Ast_Node* operands) { | |||||
| double last_number = strtod("-Inf", NULL); | |||||
| while (operands->type == Ast_Node_Type_Pair) { | |||||
| try { | |||||
| assert_type(operands->value.pair->first, Ast_Node_Type_Number); | |||||
| } | |||||
| if (operands->value.pair->first->value.number->value <= last_number) | |||||
| return create_ast_node_nil(); | |||||
| last_number = operands->value.pair->first->value.number->value; | |||||
| operands = operands->value.pair->rest; | |||||
| } | |||||
| return create_ast_node_t(); | |||||
| } | |||||
| Ast_Node* built_in_less_equal(Ast_Node* operands) { | |||||
| double last_number = strtod("-Inf", NULL); | |||||
| while (operands->type == Ast_Node_Type_Pair) { | |||||
| try { | |||||
| assert_type(operands->value.pair->first, Ast_Node_Type_Number); | |||||
| } | |||||
| if (operands->value.pair->first->value.number->value < last_number) | |||||
| return create_ast_node_nil(); | |||||
| last_number = operands->value.pair->first->value.number->value; | |||||
| operands = operands->value.pair->rest; | |||||
| } | |||||
| return create_ast_node_t(); | |||||
| } | |||||
| Ast_Node* built_in_add(Ast_Node* operands) { | |||||
| double sum = 0; | |||||
| while (operands->type == Ast_Node_Type_Pair) { | |||||
| try { | |||||
| assert_type(operands->value.pair->first, Ast_Node_Type_Number); | |||||
| } | |||||
| sum += operands->value.pair->first->value.number->value; | |||||
| operands = operands->value.pair->rest; | |||||
| } | |||||
| return create_ast_node_number(sum); | |||||
| } | |||||
| Ast_Node* built_in_substract(Ast_Node* operands) { | |||||
| try { | |||||
| assert_type(operands->value.pair->first, Ast_Node_Type_Number); | |||||
| } | |||||
| double difference = operands->value.pair->first->value.number->value; | |||||
| operands = operands->value.pair->rest; | |||||
| while (operands->type == Ast_Node_Type_Pair) { | |||||
| try { | |||||
| assert_type(operands->value.pair->first, Ast_Node_Type_Number); | |||||
| } | |||||
| difference -= operands->value.pair->first->value.number->value; | |||||
| operands = operands->value.pair->rest; | |||||
| } | |||||
| return create_ast_node_number(difference); | |||||
| } | |||||
| Ast_Node* built_in_multiply(Ast_Node* operands) { | |||||
| try { | |||||
| assert_type(operands->value.pair->first, Ast_Node_Type_Number); | |||||
| } | |||||
| double product = operands->value.pair->first->value.number->value; | |||||
| operands = operands->value.pair->rest; | |||||
| while (operands->type == Ast_Node_Type_Pair) { | |||||
| try { | |||||
| assert_type(operands->value.pair->first, Ast_Node_Type_Number); | |||||
| } | |||||
| product *= operands->value.pair->first->value.number->value; | |||||
| operands = operands->value.pair->rest; | |||||
| } | |||||
| return create_ast_node_number(product); | |||||
| } | |||||
| Ast_Node* built_in_divide(Ast_Node* operands) { | |||||
| try { | |||||
| assert_type(operands->value.pair->first, Ast_Node_Type_Number); | |||||
| } | |||||
| double quotient = operands->value.pair->first->value.number->value; | |||||
| operands = operands->value.pair->rest; | |||||
| while (operands->type == Ast_Node_Type_Pair) { | |||||
| try { | |||||
| assert_type(operands->value.pair->first, Ast_Node_Type_Number); | |||||
| } | |||||
| quotient /= operands->value.pair->first->value.number->value; | |||||
| operands = operands->value.pair->rest; | |||||
| } | |||||
| return create_ast_node_number(quotient); | |||||
| } | |||||
| Ast_Node* built_in_load(char* file_name, Environment* env) { | |||||
| char* file_content = read_entire_file(file_name); | |||||
| if (file_content) { | |||||
| Ast_Node* result = create_ast_node_nil(); | |||||
| Ast_Node_Array_List* program; | |||||
| try { | |||||
| program = parse_program(file_name, file_content); | |||||
| } | |||||
| for (int i = 0; i < program->next_index; ++i) { | |||||
| try { | |||||
| result = eval_expr(program->data[i], env); | |||||
| } | |||||
| } | |||||
| return result; | |||||
| } else { | |||||
| create_error(Error_Type_Unknown_Error, nullptr); | |||||
| return nullptr; | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,968 @@ | |||||
| Ast_Node* eval_arguments(Ast_Node* arguments, Environment* env, int *out_arguments_length); | |||||
| int list_length(Ast_Node* node); | |||||
| Ast_Node* eval_expr(Ast_Node* node, Environment* env); | |||||
| bool is_truthy (Ast_Node* expression, Environment* env); | |||||
| void parse_argument_list(Ast_Node* arguments, Function* function); | |||||
| bool ast_node_equal(Ast_Node* n1, Ast_Node* n2) { | |||||
| if (n1 == n2) | |||||
| return true; | |||||
| if (n1->type != n2->type) | |||||
| return false; | |||||
| switch (n1->type) { | |||||
| case Ast_Node_Type_CFunction: | |||||
| // TODO(Felix): make comparing work again. | |||||
| return false; | |||||
| /* return n1->value.built_in_function->type */ | |||||
| /* == n2->value.built_in_function->type; */ | |||||
| case Ast_Node_Type_Function: | |||||
| // if they have the same pointer, true is | |||||
| // returned a few lines above | |||||
| return false; | |||||
| case Ast_Node_Type_Keyword: | |||||
| return string_equal( | |||||
| n1->value.keyword->identifier, | |||||
| n2->value.keyword->identifier); | |||||
| case Ast_Node_Type_T: | |||||
| case Ast_Node_Type_Nil: | |||||
| return true; | |||||
| case Ast_Node_Type_Number: | |||||
| return | |||||
| n1->value.number->value == | |||||
| n2->value.number->value; | |||||
| case Ast_Node_Type_Pair: | |||||
| create_error(Error_Type_Not_Yet_Implemented, n1->sourceCodeLocation); | |||||
| return false; | |||||
| case Ast_Node_Type_String: | |||||
| return string_equal( | |||||
| n1->value.string->value, | |||||
| n2->value.string->value); | |||||
| case Ast_Node_Type_Symbol: | |||||
| return string_equal( | |||||
| n1->value.symbol->identifier, | |||||
| n2->value.symbol->identifier); | |||||
| } | |||||
| // we should never reach here | |||||
| return false; | |||||
| } | |||||
| Ast_Node* built_in_equals(Ast_Node* arguments, Environment* env) { | |||||
| int arguments_length; | |||||
| try { | |||||
| arguments = eval_arguments(arguments, env, &arguments_length); | |||||
| } | |||||
| if (arguments->type == Ast_Node_Type_Nil) | |||||
| return create_ast_node_t(); | |||||
| Ast_Node* first = arguments->value.pair->first; | |||||
| while (arguments->type == Ast_Node_Type_Pair) { | |||||
| if (!ast_node_equal(arguments->value.pair->first, first)) | |||||
| return create_ast_node_nil(); | |||||
| arguments = arguments->value.pair->rest; | |||||
| } | |||||
| return create_ast_node_t(); | |||||
| } | |||||
| Ast_Node* built_in_greater(Ast_Node* arguments, Environment* env) { | |||||
| int arguments_length; | |||||
| try { | |||||
| arguments = eval_arguments(arguments, env, &arguments_length); | |||||
| } | |||||
| double last_number = strtod("Inf", NULL); | |||||
| while (arguments->type == Ast_Node_Type_Pair) { | |||||
| try { | |||||
| assert_type(arguments->value.pair->first, Ast_Node_Type_Number); | |||||
| } | |||||
| if (arguments->value.pair->first->value.number->value >= last_number) | |||||
| return create_ast_node_nil(); | |||||
| last_number = arguments->value.pair->first->value.number->value; | |||||
| arguments = arguments->value.pair->rest; | |||||
| } | |||||
| return create_ast_node_t(); | |||||
| } | |||||
| Ast_Node* built_in_greater_equal(Ast_Node* arguments, Environment* env) { | |||||
| int arguments_length; | |||||
| try { | |||||
| arguments = eval_arguments(arguments, env, &arguments_length); | |||||
| } | |||||
| double last_number = strtod("Inf", NULL); | |||||
| while (arguments->type == Ast_Node_Type_Pair) { | |||||
| try { | |||||
| assert_type(arguments->value.pair->first, Ast_Node_Type_Number); | |||||
| } | |||||
| if (arguments->value.pair->first->value.number->value > last_number) | |||||
| return create_ast_node_nil(); | |||||
| last_number = arguments->value.pair->first->value.number->value; | |||||
| arguments = arguments->value.pair->rest; | |||||
| } | |||||
| return create_ast_node_t(); | |||||
| } | |||||
| Ast_Node* built_in_less(Ast_Node* arguments, Environment* env) { | |||||
| int arguments_length; | |||||
| try { | |||||
| arguments = eval_arguments(arguments, env, &arguments_length); | |||||
| } | |||||
| double last_number = strtod("-Inf", NULL); | |||||
| while (arguments->type == Ast_Node_Type_Pair) { | |||||
| try { | |||||
| assert_type(arguments->value.pair->first, Ast_Node_Type_Number); | |||||
| } | |||||
| if (arguments->value.pair->first->value.number->value <= last_number) | |||||
| return create_ast_node_nil(); | |||||
| last_number = arguments->value.pair->first->value.number->value; | |||||
| arguments = arguments->value.pair->rest; | |||||
| } | |||||
| return create_ast_node_t(); | |||||
| } | |||||
| Ast_Node* built_in_less_equal(Ast_Node* arguments, Environment* env) { | |||||
| int arguments_length; | |||||
| try { | |||||
| arguments = eval_arguments(arguments, env, &arguments_length); | |||||
| } | |||||
| double last_number = strtod("-Inf", NULL); | |||||
| while (arguments->type == Ast_Node_Type_Pair) { | |||||
| try { | |||||
| assert_type(arguments->value.pair->first, Ast_Node_Type_Number); | |||||
| } | |||||
| if (arguments->value.pair->first->value.number->value < last_number) | |||||
| return create_ast_node_nil(); | |||||
| last_number = arguments->value.pair->first->value.number->value; | |||||
| arguments = arguments->value.pair->rest; | |||||
| } | |||||
| return create_ast_node_t(); | |||||
| } | |||||
| Ast_Node* built_in_add(Ast_Node* arguments, Environment* env) { | |||||
| int arguments_length; | |||||
| try { | |||||
| arguments = eval_arguments(arguments, env, &arguments_length); | |||||
| } | |||||
| double sum = 0; | |||||
| while (arguments->type == Ast_Node_Type_Pair) { | |||||
| try { | |||||
| assert_type(arguments->value.pair->first, Ast_Node_Type_Number); | |||||
| } | |||||
| sum += arguments->value.pair->first->value.number->value; | |||||
| arguments = arguments->value.pair->rest; | |||||
| } | |||||
| return create_ast_node_number(sum); | |||||
| } | |||||
| Ast_Node* built_in_substract(Ast_Node* arguments, Environment* env) { | |||||
| int arguments_length; | |||||
| try { | |||||
| arguments = eval_arguments(arguments, env, &arguments_length); | |||||
| } | |||||
| try { | |||||
| assert_type(arguments->value.pair->first, Ast_Node_Type_Number); | |||||
| } | |||||
| double difference = arguments->value.pair->first->value.number->value; | |||||
| arguments = arguments->value.pair->rest; | |||||
| while (arguments->type == Ast_Node_Type_Pair) { | |||||
| try { | |||||
| assert_type(arguments->value.pair->first, Ast_Node_Type_Number); | |||||
| } | |||||
| difference -= arguments->value.pair->first->value.number->value; | |||||
| arguments = arguments->value.pair->rest; | |||||
| } | |||||
| return create_ast_node_number(difference); | |||||
| } | |||||
| Ast_Node* built_in_multiply(Ast_Node* arguments, Environment* env) { | |||||
| int arguments_length; | |||||
| try { | |||||
| arguments = eval_arguments(arguments, env, &arguments_length); | |||||
| } | |||||
| try { | |||||
| assert_type(arguments->value.pair->first, Ast_Node_Type_Number); | |||||
| } | |||||
| double product = arguments->value.pair->first->value.number->value; | |||||
| arguments = arguments->value.pair->rest; | |||||
| while (arguments->type == Ast_Node_Type_Pair) { | |||||
| try { | |||||
| assert_type(arguments->value.pair->first, Ast_Node_Type_Number); | |||||
| } | |||||
| product *= arguments->value.pair->first->value.number->value; | |||||
| arguments = arguments->value.pair->rest; | |||||
| } | |||||
| return create_ast_node_number(product); | |||||
| } | |||||
| Ast_Node* built_in_divide(Ast_Node* arguments, Environment* env) { | |||||
| int arguments_length; | |||||
| try { | |||||
| arguments = eval_arguments(arguments, env, &arguments_length); | |||||
| } | |||||
| try { | |||||
| assert_type(arguments->value.pair->first, Ast_Node_Type_Number); | |||||
| } | |||||
| double quotient = arguments->value.pair->first->value.number->value; | |||||
| arguments = arguments->value.pair->rest; | |||||
| while (arguments->type == Ast_Node_Type_Pair) { | |||||
| try { | |||||
| assert_type(arguments->value.pair->first, Ast_Node_Type_Number); | |||||
| } | |||||
| quotient /= arguments->value.pair->first->value.number->value; | |||||
| arguments = arguments->value.pair->rest; | |||||
| } | |||||
| return create_ast_node_number(quotient); | |||||
| } | |||||
| Ast_Node* built_in_load(char* file_name, Environment* env) { | |||||
| char* file_content = read_entire_file(file_name); | |||||
| if (file_content) { | |||||
| Ast_Node* result = create_ast_node_nil(); | |||||
| Ast_Node_Array_List* program; | |||||
| try { | |||||
| program = parse_program(file_name, file_content); | |||||
| } | |||||
| for (int i = 0; i < program->next_index; ++i) { | |||||
| try { | |||||
| result = eval_expr(program->data[i], env); | |||||
| } | |||||
| } | |||||
| return result; | |||||
| } else { | |||||
| create_error(Error_Type_Unknown_Error, nullptr); | |||||
| return nullptr; | |||||
| } | |||||
| } | |||||
| void load_built_ins_into_environment(Environment* env) { | |||||
| int arguments_length; | |||||
| Ast_Node* evaluated_arguments; | |||||
| #define cLambda [=](Ast_Node* arguments, Environment* env) mutable -> Ast_Node* | |||||
| #define report_error(_type) { \ | |||||
| create_error(_type, arguments->sourceCodeLocation); \ | |||||
| return nullptr; \ | |||||
| } | |||||
| auto defun = [&](char* name, std::function<Ast_Node*(Ast_Node*, Environment*)> fun) { | |||||
| define_symbol( | |||||
| create_ast_node_symbol(name), | |||||
| create_ast_node_cfunction(fun), | |||||
| env); | |||||
| }; | |||||
| defun("=", built_in_equals); | |||||
| defun(">", built_in_greater); | |||||
| defun(">=", built_in_greater_equal); | |||||
| defun("<", built_in_less); | |||||
| defun("<=", built_in_less_equal); | |||||
| defun("+", built_in_add); | |||||
| defun("-", built_in_substract); | |||||
| defun("*", built_in_multiply); | |||||
| defun("/", built_in_divide); | |||||
| defun("define", cLambda { | |||||
| try { | |||||
| arguments_length = list_length(arguments); | |||||
| } | |||||
| if (arguments_length != 2) { | |||||
| report_error(Error_Type_Wrong_Number_Of_Arguments); | |||||
| } | |||||
| Ast_Node* symbol = arguments->value.pair->first; | |||||
| if (symbol->type == Ast_Node_Type_Pair) { | |||||
| try { | |||||
| symbol = eval_expr(symbol, env); | |||||
| } | |||||
| } | |||||
| if (symbol->type != Ast_Node_Type_Symbol) { | |||||
| report_error(Error_Type_Type_Missmatch); | |||||
| } | |||||
| Ast_Node* value = arguments->value.pair->rest->value.pair->first; | |||||
| try { | |||||
| value = eval_expr(value, env); | |||||
| } | |||||
| define_symbol(symbol, value, env); | |||||
| return value; | |||||
| }); | |||||
| defun("macro-define", cLambda { | |||||
| try { | |||||
| arguments_length = list_length(arguments); | |||||
| } | |||||
| if (arguments_length != 2) { | |||||
| report_error(Error_Type_Wrong_Number_Of_Arguments); | |||||
| } | |||||
| Ast_Node* symbol = arguments->value.pair->first; | |||||
| if (symbol->type == Ast_Node_Type_Pair) { | |||||
| try { | |||||
| symbol = eval_expr(symbol, env); | |||||
| } | |||||
| } | |||||
| if (symbol->type != Ast_Node_Type_Symbol) { | |||||
| report_error(Error_Type_Type_Missmatch); | |||||
| } | |||||
| Ast_Node* value = arguments->value.pair->rest->value.pair->first; | |||||
| try { | |||||
| value = eval_expr(value, env); | |||||
| } | |||||
| define_macro_symbol(symbol, value, env); | |||||
| return value; | |||||
| }); | |||||
| defun("mutate", cLambda { | |||||
| try { | |||||
| evaluated_arguments = eval_arguments(arguments, env, &arguments_length); | |||||
| } | |||||
| if (arguments_length != 2) | |||||
| report_error(Error_Type_Wrong_Number_Of_Arguments); | |||||
| if (evaluated_arguments->value.pair->first->type == Ast_Node_Type_Nil || | |||||
| evaluated_arguments->value.pair->first->type == Ast_Node_Type_Keyword) | |||||
| { | |||||
| report_error(Error_Type_Type_Missmatch); | |||||
| } | |||||
| Ast_Node* target = evaluated_arguments->value.pair->first; | |||||
| Ast_Node* source = evaluated_arguments->value.pair->rest->value.pair->first; | |||||
| *target = *source; | |||||
| return target; | |||||
| }); | |||||
| defun("if", cLambda { | |||||
| try { | |||||
| arguments_length = list_length(arguments); | |||||
| } | |||||
| if (arguments_length != 2 && arguments_length != 3) { | |||||
| report_error(Error_Type_Wrong_Number_Of_Arguments); | |||||
| } | |||||
| Ast_Node* condition = arguments->value.pair->first; | |||||
| Ast_Node* then_part = arguments->value.pair->rest; | |||||
| Ast_Node* else_part = then_part->value.pair->rest; | |||||
| bool truthy; | |||||
| try { | |||||
| truthy = is_truthy(condition, env); | |||||
| } | |||||
| Ast_Node* result; | |||||
| if (truthy) | |||||
| try{ | |||||
| result = eval_expr(then_part->value.pair->first, env); | |||||
| } | |||||
| else if (arguments_length == 3) | |||||
| try { | |||||
| result = eval_expr(else_part->value.pair->first, env); | |||||
| } | |||||
| else return create_ast_node_nil(); | |||||
| return result; | |||||
| }); | |||||
| defun("quote", cLambda { | |||||
| try { | |||||
| arguments_length = list_length(arguments); | |||||
| } | |||||
| if (arguments_length != 1) { | |||||
| report_error(Error_Type_Wrong_Number_Of_Arguments); | |||||
| } | |||||
| return arguments->value.pair->first; | |||||
| }); | |||||
| defun("and", cLambda { | |||||
| bool result = true; | |||||
| while (arguments->type != Ast_Node_Type_Nil) { | |||||
| if (arguments->type != Ast_Node_Type_Pair) { | |||||
| report_error(Error_Type_Ill_Formed_List); | |||||
| } | |||||
| try { | |||||
| result &= is_truthy(arguments->value.pair->first, env); | |||||
| } | |||||
| arguments = arguments->value.pair->rest; | |||||
| if (!result) return create_ast_node_nil(); | |||||
| } | |||||
| return create_ast_node_t(); | |||||
| }); | |||||
| defun("or", cLambda { | |||||
| bool result = false; | |||||
| while (arguments->type != Ast_Node_Type_Nil) { | |||||
| if (arguments->type != Ast_Node_Type_Pair) { | |||||
| report_error(Error_Type_Ill_Formed_List); | |||||
| } | |||||
| try { | |||||
| result |= is_truthy(arguments->value.pair->first, env); | |||||
| } | |||||
| arguments = arguments->value.pair->rest; | |||||
| if (result) return create_ast_node_t(); | |||||
| } | |||||
| return create_ast_node_nil(); | |||||
| }); | |||||
| defun("not", cLambda { | |||||
| try { | |||||
| arguments_length = list_length(arguments); | |||||
| } | |||||
| if (arguments_length != 1) { | |||||
| report_error(Error_Type_Wrong_Number_Of_Arguments); | |||||
| } | |||||
| bool truthy; | |||||
| try { | |||||
| truthy = is_truthy(arguments->value.pair->first, env); | |||||
| } | |||||
| if (truthy) | |||||
| return create_ast_node_nil(); | |||||
| return create_ast_node_t(); | |||||
| }); | |||||
| defun("while", cLambda { | |||||
| try { | |||||
| arguments_length = list_length(arguments); | |||||
| } | |||||
| if (arguments_length < 2) { | |||||
| report_error(Error_Type_Wrong_Number_Of_Arguments); | |||||
| } | |||||
| Ast_Node* condition_part = arguments->value.pair->first; | |||||
| Ast_Node* condition; | |||||
| Ast_Node* then_part = arguments->value.pair->rest; | |||||
| Ast_Node* result = create_ast_node_nil(); | |||||
| while (true) { | |||||
| try { | |||||
| condition = eval_expr(condition_part, env); | |||||
| } | |||||
| if (condition->type == Ast_Node_Type_Nil) { | |||||
| break; | |||||
| } | |||||
| try { | |||||
| result = eval_expr(then_part->value.pair->first, env); | |||||
| } | |||||
| } | |||||
| return result; | |||||
| }); | |||||
| defun("let", cLambda { | |||||
| // (let ((a 10)(b 20)) (body1) (body2)) | |||||
| try { | |||||
| arguments_length = list_length(arguments); | |||||
| } | |||||
| if (arguments_length < 1) | |||||
| report_error(Error_Type_Wrong_Number_Of_Arguments); | |||||
| Environment* let_env = create_child_environment(env, Environment_Type_Let); | |||||
| Ast_Node* bindings = arguments->value.pair->first; | |||||
| while (true) { | |||||
| if (bindings->type == Ast_Node_Type_Nil) { | |||||
| break; | |||||
| } else if (bindings->type != Ast_Node_Type_Pair) { | |||||
| report_error(Error_Type_Ill_Formed_Arguments); | |||||
| } | |||||
| Ast_Node* sym = bindings->value.pair->first->value.pair->first; | |||||
| if(sym->type != Ast_Node_Type_Symbol) { | |||||
| report_error(Error_Type_Ill_Formed_Arguments); | |||||
| } | |||||
| Ast_Node* rest_sym = bindings->value.pair->first->value.pair->rest; | |||||
| if (rest_sym->type != Ast_Node_Type_Pair) { | |||||
| report_error(Error_Type_Ill_Formed_Arguments); | |||||
| } | |||||
| if (rest_sym->value.pair->rest->type != Ast_Node_Type_Nil) { | |||||
| report_error(Error_Type_Ill_Formed_Arguments); | |||||
| } | |||||
| Ast_Node* value = eval_expr(rest_sym->value.pair->first, env); | |||||
| define_symbol(sym, value, let_env); | |||||
| bindings = bindings->value.pair->rest; | |||||
| } | |||||
| arguments = arguments->value.pair->rest; | |||||
| Ast_Node* evaluated_arguments; | |||||
| try { | |||||
| evaluated_arguments = eval_arguments(arguments, let_env, &arguments_length); | |||||
| } | |||||
| if (evaluated_arguments->type == Ast_Node_Type_Nil) | |||||
| return evaluated_arguments; | |||||
| // skip to the last evaluated operand and return it, | |||||
| // we use eval_arguments here instead of doing it | |||||
| // manually, because we want to increase code reuse, | |||||
| // but at the cost that we have to find the end of the | |||||
| // list again | |||||
| while (evaluated_arguments->value.pair->rest->type == Ast_Node_Type_Pair) { | |||||
| evaluated_arguments = evaluated_arguments->value.pair->rest; | |||||
| } | |||||
| return evaluated_arguments->value.pair->first; | |||||
| }); | |||||
| defun("lambda", cLambda { | |||||
| /* | |||||
| * (lambda ()) | |||||
| * (lambda (x d) (+ 1 2) (- 1 2) (* 1 2)) | |||||
| */ | |||||
| try { | |||||
| arguments_length = list_length(arguments); | |||||
| } | |||||
| if (arguments_length == 0) | |||||
| report_error(Error_Type_Wrong_Number_Of_Arguments); | |||||
| Function* function = new(Function); | |||||
| /* if (lispOperator->value.built_in_function->type == Built_In_Macro) { */ | |||||
| /* function->is_macro = true; */ | |||||
| /* } else { */ | |||||
| function->is_macro = false; | |||||
| /* } */ | |||||
| // if parameters were specified | |||||
| if (arguments->value.pair->first->type != Ast_Node_Type_Nil) { | |||||
| try { | |||||
| assert_type(arguments->value.pair->first, Ast_Node_Type_Pair); | |||||
| } | |||||
| try { | |||||
| parse_argument_list(arguments->value.pair->first, function); | |||||
| } | |||||
| } else { | |||||
| function->positional_arguments = create_positional_argument_list(1); | |||||
| function->keyword_arguments = create_keyword_argument_list(1); | |||||
| function->rest_argument = nullptr; | |||||
| } | |||||
| arguments = arguments->value.pair->rest; | |||||
| // if there is a docstring, use it | |||||
| if (arguments->value.pair->first->type == Ast_Node_Type_String) { | |||||
| function->docstring = arguments->value.pair->first->value.string->value; | |||||
| arguments = arguments->value.pair->rest; | |||||
| } else { | |||||
| function->docstring = nullptr; | |||||
| } | |||||
| // we are now in the function body, just wrap it in an | |||||
| // implicit prog | |||||
| function->body = create_ast_node_pair( | |||||
| create_ast_node_symbol("prog"), | |||||
| arguments); | |||||
| Ast_Node* ret = new(Ast_Node); | |||||
| ret->type = Ast_Node_Type_Function; | |||||
| ret->value.function = function; | |||||
| return ret; | |||||
| }); | |||||
| defun("macro", cLambda { | |||||
| /* | |||||
| * (macro ()) | |||||
| * (macro (x d) (+ 1 2) (- 1 2) (* 1 2)) | |||||
| */ | |||||
| try { | |||||
| arguments_length = list_length(arguments); | |||||
| } | |||||
| if (arguments_length == 0) | |||||
| report_error(Error_Type_Wrong_Number_Of_Arguments); | |||||
| Function* function = new(Function); | |||||
| function->is_macro = true; | |||||
| // if parameters were specified | |||||
| if (arguments->value.pair->first->type != Ast_Node_Type_Nil) { | |||||
| try { | |||||
| assert_type(arguments->value.pair->first, Ast_Node_Type_Pair); | |||||
| } | |||||
| try { | |||||
| parse_argument_list(arguments->value.pair->first, function); | |||||
| } | |||||
| } else { | |||||
| function->positional_arguments = create_positional_argument_list(1); | |||||
| function->keyword_arguments = create_keyword_argument_list(1); | |||||
| function->rest_argument = nullptr; | |||||
| } | |||||
| arguments = arguments->value.pair->rest; | |||||
| // if there is a docstring, use it | |||||
| if (arguments->value.pair->first->type == Ast_Node_Type_String) { | |||||
| function->docstring = arguments->value.pair->first->value.string->value; | |||||
| arguments = arguments->value.pair->rest; | |||||
| } else { | |||||
| function->docstring = nullptr; | |||||
| } | |||||
| // we are now in the function body, just wrap it in an | |||||
| // implicit prog | |||||
| function->body = create_ast_node_pair( | |||||
| create_ast_node_symbol("prog"), | |||||
| arguments); | |||||
| Ast_Node* ret = new(Ast_Node); | |||||
| ret->type = Ast_Node_Type_Function; | |||||
| ret->value.function = function; | |||||
| return ret; | |||||
| }); | |||||
| defun("eval", cLambda { | |||||
| try { | |||||
| evaluated_arguments = eval_arguments(arguments, env, &arguments_length); | |||||
| } | |||||
| if (arguments_length != 1) { | |||||
| report_error(Error_Type_Wrong_Number_Of_Arguments); | |||||
| } | |||||
| Ast_Node* result; | |||||
| try { | |||||
| result = eval_expr(evaluated_arguments->value.pair->first, env); | |||||
| } | |||||
| return result; | |||||
| }); | |||||
| defun("prog", cLambda { | |||||
| try { | |||||
| evaluated_arguments = eval_arguments(arguments, env, &arguments_length); | |||||
| } | |||||
| if (evaluated_arguments->type == Ast_Node_Type_Nil) | |||||
| return evaluated_arguments; | |||||
| // skip to the last evaluated operand and return it, | |||||
| // we use eval_arguments here instead of doing it | |||||
| // manually, because we want to increase code reuse, | |||||
| // but at the cost that we have to find the end of the | |||||
| // list again | |||||
| while (evaluated_arguments->value.pair->rest->type == Ast_Node_Type_Pair) { | |||||
| evaluated_arguments = evaluated_arguments->value.pair->rest; | |||||
| } | |||||
| return evaluated_arguments->value.pair->first; | |||||
| }); | |||||
| defun("list", cLambda { | |||||
| try { | |||||
| evaluated_arguments = eval_arguments(arguments, env, &arguments_length); | |||||
| } | |||||
| return evaluated_arguments; | |||||
| }); | |||||
| defun("pair", cLambda { | |||||
| try { | |||||
| evaluated_arguments = eval_arguments(arguments, env, &arguments_length); | |||||
| } | |||||
| // BUG(Felix): Why is arguments_length for '(1 (2)) == 3 and not 2?? | |||||
| // BUG(Felix): Why is arguments_length for '(1 (2)) == 3 and not 2?? | |||||
| // BUG(Felix): Why is arguments_length for '(1 (2)) == 3 and not 2?? | |||||
| /* if (arguments_length != 2) { */ | |||||
| if (list_length(evaluated_arguments) != 2) { | |||||
| report_error(Error_Type_Wrong_Number_Of_Arguments); | |||||
| } | |||||
| return create_ast_node_pair(evaluated_arguments->value.pair->first, evaluated_arguments->value.pair->rest->value.pair->first); | |||||
| }); | |||||
| defun("first", cLambda { | |||||
| try { | |||||
| evaluated_arguments = eval_arguments(arguments, env, &arguments_length); | |||||
| } | |||||
| if (arguments_length != 1) { | |||||
| report_error(Error_Type_Wrong_Number_Of_Arguments); | |||||
| } | |||||
| if (evaluated_arguments->value.pair->first->type == Ast_Node_Type_Nil) | |||||
| return create_ast_node_nil(); | |||||
| if (evaluated_arguments->value.pair->first->type != Ast_Node_Type_Pair) | |||||
| report_error(Error_Type_Type_Missmatch); | |||||
| return evaluated_arguments->value.pair->first->value.pair->first; | |||||
| }); | |||||
| defun("rest", cLambda { | |||||
| try { | |||||
| evaluated_arguments = eval_arguments(arguments, env, &arguments_length); | |||||
| } | |||||
| if (arguments_length != 1) { | |||||
| report_error(Error_Type_Wrong_Number_Of_Arguments); | |||||
| } | |||||
| if (evaluated_arguments->value.pair->first->type == Ast_Node_Type_Nil) | |||||
| return create_ast_node_nil(); | |||||
| if (evaluated_arguments->value.pair->first->type != Ast_Node_Type_Pair) | |||||
| report_error(Error_Type_Type_Missmatch); | |||||
| return evaluated_arguments->value.pair->first->value.pair->rest; | |||||
| }); | |||||
| defun("type", cLambda { | |||||
| try { | |||||
| evaluated_arguments = eval_arguments(arguments, env, &arguments_length); | |||||
| } | |||||
| if (arguments_length != 1) { | |||||
| report_error(Error_Type_Wrong_Number_Of_Arguments); | |||||
| } | |||||
| Ast_Node_Type type = evaluated_arguments->value.pair->first->type; | |||||
| switch (type) { | |||||
| case Ast_Node_Type_CFunction: return create_ast_node_keyword("cfunction"); | |||||
| case Ast_Node_Type_Function: { | |||||
| if (evaluated_arguments->value.pair->first->value.function->is_macro) | |||||
| return create_ast_node_keyword("dynamic-macro"); | |||||
| return create_ast_node_keyword("dynamic-function"); | |||||
| } | |||||
| case Ast_Node_Type_Keyword: return create_ast_node_keyword("keyword"); | |||||
| case Ast_Node_Type_Nil: return create_ast_node_keyword("nil"); | |||||
| case Ast_Node_Type_T: return create_ast_node_keyword("t"); | |||||
| case Ast_Node_Type_Number: return create_ast_node_keyword("number"); | |||||
| case Ast_Node_Type_Pair: return create_ast_node_keyword("pair"); | |||||
| case Ast_Node_Type_String: return create_ast_node_keyword("string"); | |||||
| case Ast_Node_Type_Symbol: return create_ast_node_keyword("symbol"); | |||||
| } | |||||
| return create_ast_node_keyword("unknown"); | |||||
| }); | |||||
| defun("info", cLambda { | |||||
| try { | |||||
| arguments_length = list_length(arguments); | |||||
| } | |||||
| if (arguments_length != 1) { | |||||
| report_error(Error_Type_Wrong_Number_Of_Arguments); | |||||
| } | |||||
| print(arguments->value.pair->first); | |||||
| Ast_Node* type = eval_expr( | |||||
| create_ast_node_pair( | |||||
| create_ast_node_symbol("type"), | |||||
| create_ast_node_pair(arguments->value.pair->first, create_ast_node_nil())), | |||||
| env); | |||||
| if (type) { | |||||
| printf(" is of type "); | |||||
| print(type); | |||||
| printf("\n"); | |||||
| // just make sure type was not redefined and | |||||
| // returns something that is not a keyword | |||||
| if (type->type == Ast_Node_Type_Keyword && | |||||
| (string_equal(type->value.keyword->identifier, "dynamic-function") || | |||||
| string_equal(type->value.keyword->identifier, "dynamic-macro"))) | |||||
| { | |||||
| Ast_Node* fun = eval_expr(arguments->value.pair->first, env); | |||||
| printf("\nMacro? %s\n", (fun->value.function->is_macro) ? "yes" : "no"); | |||||
| if (fun->value.function->docstring) | |||||
| printf("Docstring:\n==========\n%s\n\n", fun->value.function->docstring); | |||||
| else | |||||
| printf("No docstring avaliable\n"); | |||||
| printf("Arguments:\n==========\n"); | |||||
| printf("Postitional: {"); | |||||
| if (fun->value.function->positional_arguments->next_index != 0) { | |||||
| printf("%s", fun->value.function->positional_arguments->identifiers[0]); | |||||
| for (int i = 1; i < fun->value.function->positional_arguments->next_index; ++i) { | |||||
| printf(", %s", fun->value.function->positional_arguments->identifiers[i]); | |||||
| } | |||||
| } | |||||
| printf("}\n"); | |||||
| printf("Keyword: {"); | |||||
| if (fun->value.function->keyword_arguments->next_index != 0) { | |||||
| printf("%s", fun->value.function->keyword_arguments->identifiers[0]); | |||||
| if (fun->value.function->keyword_arguments->values->data[0]) { | |||||
| printf(" ("); | |||||
| print(fun->value.function->keyword_arguments->values->data[0]); | |||||
| printf(")"); | |||||
| } | |||||
| for (int i = 1; i < fun->value.function->keyword_arguments->next_index; ++i) { | |||||
| printf(", %s", fun->value.function->keyword_arguments->identifiers[i]); | |||||
| if (fun->value.function->keyword_arguments->values->data[i]) { | |||||
| printf(" ("); | |||||
| print(fun->value.function->keyword_arguments->values->data[i]); | |||||
| printf(")"); | |||||
| } | |||||
| } | |||||
| } | |||||
| printf("}\n"); | |||||
| printf("Rest: {"); | |||||
| if (fun->value.function->rest_argument) | |||||
| printf("%s", fun->value.function->rest_argument); | |||||
| printf("}\n"); | |||||
| } | |||||
| } else { | |||||
| printf(" is not defined\n"); | |||||
| delete_error(); | |||||
| } | |||||
| return create_ast_node_nil(); | |||||
| }); | |||||
| defun("print", cLambda { | |||||
| try { | |||||
| evaluated_arguments = eval_arguments(arguments, env, &arguments_length); | |||||
| } | |||||
| if (arguments_length != 1) { | |||||
| report_error(Error_Type_Wrong_Number_Of_Arguments); | |||||
| } | |||||
| print(evaluated_arguments->value.pair->first); | |||||
| printf("\n"); | |||||
| return create_ast_node_nil(); | |||||
| }); | |||||
| defun("read", cLambda { | |||||
| try { | |||||
| evaluated_arguments = eval_arguments(arguments, env, &arguments_length); | |||||
| } | |||||
| if (arguments_length > 1) { | |||||
| report_error(Error_Type_Wrong_Number_Of_Arguments); | |||||
| } | |||||
| if (arguments_length == 1) { | |||||
| Ast_Node* prompt = evaluated_arguments->value.pair->first; | |||||
| /* if (prompt->type == Ast_Node_Type_String) */ | |||||
| /* printf("%s", prompt->value.string->value); */ | |||||
| /* else */ | |||||
| print(evaluated_arguments->value.pair->first); | |||||
| } | |||||
| char* line = read_line(); | |||||
| return create_ast_node_string(line, (int)strlen(line)); | |||||
| }); | |||||
| defun("exit", cLambda { | |||||
| try { | |||||
| evaluated_arguments = eval_arguments(arguments, env, &arguments_length); | |||||
| } | |||||
| if (arguments_length > 1) { | |||||
| report_error(Error_Type_Wrong_Number_Of_Arguments); | |||||
| } | |||||
| if (arguments_length == 1) { | |||||
| Ast_Node* error_code = evaluated_arguments->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); | |||||
| }); | |||||
| defun("break", cLambda { | |||||
| print_environment(env); | |||||
| #ifdef _DEBUG | |||||
| __debugbreak(); | |||||
| #endif | |||||
| return create_ast_node_nil(); | |||||
| }); | |||||
| defun("try", cLambda { | |||||
| try { | |||||
| arguments_length = list_length(arguments); | |||||
| } | |||||
| if (arguments_length != 2) { | |||||
| report_error(Error_Type_Wrong_Number_Of_Arguments); | |||||
| } | |||||
| Ast_Node* try_part = arguments->value.pair->first; | |||||
| Ast_Node* catch_part = arguments->value.pair->rest->value.pair->first; | |||||
| Ast_Node* result; | |||||
| result = eval_expr(try_part, env); | |||||
| if (error) { | |||||
| delete_error(); | |||||
| try { | |||||
| result = eval_expr(catch_part, env); | |||||
| } | |||||
| } | |||||
| return result; | |||||
| }); | |||||
| defun("load", cLambda { | |||||
| try { | |||||
| evaluated_arguments = eval_arguments(arguments, env, &arguments_length); | |||||
| } | |||||
| if (arguments_length != 1) | |||||
| report_error(Error_Type_Wrong_Number_Of_Arguments); | |||||
| if (evaluated_arguments->value.pair->first->type != Ast_Node_Type_String) | |||||
| report_error(Error_Type_Type_Missmatch); | |||||
| Ast_Node* result; | |||||
| try { | |||||
| result = built_in_load( | |||||
| evaluated_arguments->value.pair->first->value.string->value, env); | |||||
| } | |||||
| return result; | |||||
| }); | |||||
| defun("copy", cLambda { | |||||
| try { | |||||
| evaluated_arguments = eval_arguments(arguments, env, &arguments_length); | |||||
| } | |||||
| if (arguments_length != 1) | |||||
| report_error(Error_Type_Wrong_Number_Of_Arguments); | |||||
| if (evaluated_arguments->value.pair->first->type == Ast_Node_Type_Nil || | |||||
| evaluated_arguments->value.pair->first->type == Ast_Node_Type_Keyword) | |||||
| { | |||||
| report_error(Error_Type_Type_Missmatch); | |||||
| } | |||||
| Ast_Node* target = new(Ast_Node); | |||||
| Ast_Node* source = evaluated_arguments->value.pair->first; | |||||
| *target = *source; | |||||
| return target; | |||||
| }); | |||||
| defun("error", cLambda { | |||||
| try { | |||||
| evaluated_arguments = eval_arguments(arguments, env, &arguments_length); | |||||
| } | |||||
| if (arguments_length != 0) { | |||||
| report_error(Error_Type_Wrong_Number_Of_Arguments); | |||||
| } | |||||
| report_error(Error_Type_Unknown_Error); | |||||
| }); | |||||
| #undef report_error | |||||
| #undef cLambda | |||||
| } | |||||
| Environment* create_built_ins_environment() { | |||||
| Environment* ret = create_child_environment(nullptr, Environment_Type_Let); | |||||
| load_built_ins_into_environment(ret); | |||||
| return ret; | |||||
| } | |||||
| @@ -144,10 +144,13 @@ Ast_Node* lookup_symbol(Ast_Node* node, Environment* env) { | |||||
| return create_ast_node_t(); | return create_ast_node_t(); | ||||
| } | } | ||||
| result = create_ast_node_built_in_function(sym->identifier); | |||||
| result->sourceCodeLocation = node->sourceCodeLocation; | |||||
| if (result) | |||||
| return result; | |||||
| // we should not need this anymore when we have c lambdas as built | |||||
| // in functions | |||||
| /* result = create_ast_node_built_in_function(sym->identifier); */ | |||||
| /* result->sourceCodeLocation = node->sourceCodeLocation; */ | |||||
| /* if (result) */ | |||||
| /* return result; */ | |||||
| create_error(Error_Type_Symbol_Not_Defined, node->sourceCodeLocation); | create_error(Error_Type_Symbol_Not_Defined, node->sourceCodeLocation); | ||||
| /* printf("%s\n", sym->identifier); */ | /* printf("%s\n", sym->identifier); */ | ||||
| @@ -15,7 +15,7 @@ typedef enum { | |||||
| Error_Type_Wrong_Number_Of_Arguments, | Error_Type_Wrong_Number_Of_Arguments, | ||||
| } Error_Type; | } Error_Type; | ||||
| typedef struct { | |||||
| typedef struct Error_t { | |||||
| Error_Type type; | Error_Type type; | ||||
| Source_Code_Location* location; | Source_Code_Location* location; | ||||
| } Error; | } Error; | ||||
| @@ -31,7 +31,7 @@ void delete_error() { | |||||
| void create_error(Error_Type type, Source_Code_Location* location) { | void create_error(Error_Type type, Source_Code_Location* location) { | ||||
| delete_error(); | delete_error(); | ||||
| __debugbreak(); | |||||
| error = new(Error); | error = new(Error); | ||||
| error->type = type; | error->type = type; | ||||
| error->location = location; | error->location = location; | ||||
| @@ -0,0 +1,444 @@ | |||||
| Ast_Node* eval_expr(Ast_Node* node, Environment* env); | |||||
| Ast_Node* apply_arguments_to_function(Ast_Node* arguments, Function* function, Environment* parent) { | |||||
| // NOTE(Felix): if it is a macro, we will set it later, so we can | |||||
| // use the default "define_symbol" method here instead of | |||||
| // switching between "define_symbol" and "define_macro_symbol" all | |||||
| // the time | |||||
| Environment* new_env = create_child_environment(parent, Environment_Type_Lambda); | |||||
| // positional arguments | |||||
| for (int i = 0; i < function->positional_arguments->next_index; ++i) { | |||||
| if (arguments->type == Ast_Node_Type_Pair) { | |||||
| // TODO(Felix): here we create new ast_node_symbols from | |||||
| // their identifiers but before we converted them to | |||||
| // strings from symbols... Wo maybe just use the symbols? | |||||
| define_symbol( | |||||
| create_ast_node_symbol(function->positional_arguments->identifiers[i]), | |||||
| arguments->value.pair->first, new_env); | |||||
| } else { | |||||
| // not enough arguments given | |||||
| create_error(Error_Type_Ill_Formed_Arguments, arguments->sourceCodeLocation); | |||||
| return nullptr; | |||||
| } | |||||
| arguments = arguments->value.pair->rest; | |||||
| } | |||||
| if (arguments->type == Ast_Node_Type_Nil) | |||||
| goto eval_time; | |||||
| String_Array_List* read_in_keywords = create_String_array_list(16); | |||||
| // keyword arguments: use all given ones and keep track of the | |||||
| // added ones (array list), if end of parameters in encountered or | |||||
| // something that is not a keyword is encountered or a keyword | |||||
| // that is not recognized is encoutered, jump out of the loop. | |||||
| while (arguments->value.pair->first->type == Ast_Node_Type_Keyword) { | |||||
| // check if this one is even an accepted keyword | |||||
| bool accepted = false; | |||||
| for (int i = 0; i < function->keyword_arguments->next_index; ++i) { | |||||
| if (string_equal( | |||||
| arguments->value.pair->first->value.keyword->identifier, | |||||
| function->keyword_arguments->identifiers[i])) | |||||
| { | |||||
| accepted = true; | |||||
| break; | |||||
| } | |||||
| } | |||||
| if (!accepted) { | |||||
| create_error(Error_Type_Ill_Formed_Arguments, arguments->sourceCodeLocation); | |||||
| return nullptr; | |||||
| } | |||||
| // check if it was already read in | |||||
| for (int i = 0; i < read_in_keywords->next_index; ++i) { | |||||
| if (string_equal( | |||||
| arguments->value.pair->first->value.keyword->identifier, | |||||
| read_in_keywords->data[i])) | |||||
| { | |||||
| // TODO(Felix): if we are actually done with all the | |||||
| // necessary keywords then we have to count the rest | |||||
| // as :rest here, instead od always creating an error | |||||
| // (special case with default variables) | |||||
| create_error(Error_Type_Ill_Formed_Arguments, arguments->sourceCodeLocation); | |||||
| return nullptr; | |||||
| } | |||||
| } | |||||
| // okay so we found a keyword that has to be read in and was | |||||
| // not already read in, is there a next element to actually | |||||
| // set it to? | |||||
| if (arguments->value.pair->rest->type != Ast_Node_Type_Pair) { | |||||
| create_error(Error_Type_Ill_Formed_Arguments, arguments->sourceCodeLocation); | |||||
| return nullptr; | |||||
| } | |||||
| // if not set it and then add it to the array list | |||||
| define_symbol( | |||||
| create_ast_node_symbol(arguments->value.pair->first->value.keyword->identifier), | |||||
| arguments->value.pair->rest->value.pair->first, | |||||
| new_env); | |||||
| append_to_String_array_list(read_in_keywords, arguments->value.pair->first->value.keyword->identifier); | |||||
| // overstep both for next one | |||||
| arguments = arguments->value.pair->rest->value.pair->rest; | |||||
| if (arguments->type == Ast_Node_Type_Nil) { | |||||
| break; | |||||
| } | |||||
| } | |||||
| // check if all necessary keywords have been read in | |||||
| for (int i = 0; i < function->keyword_arguments->next_index; ++i) { | |||||
| char* defined_keyword = function->keyword_arguments->identifiers[i]; | |||||
| bool was_set = false; | |||||
| for (int j = 0; j < read_in_keywords->next_index; ++j) { | |||||
| if (string_equal( | |||||
| read_in_keywords->data[j], | |||||
| defined_keyword)) | |||||
| { | |||||
| was_set = true; | |||||
| break; | |||||
| } | |||||
| } | |||||
| if (function->keyword_arguments->values->data[i] == nullptr) { | |||||
| // if this one does not have a default value | |||||
| if (!was_set) { | |||||
| create_error(Error_Type_Ill_Formed_Arguments, arguments->sourceCodeLocation); | |||||
| return nullptr; | |||||
| } | |||||
| } else { | |||||
| // this one does have a default value, lets see if we have | |||||
| // to use it or if the user supplied his own | |||||
| if (!was_set) { | |||||
| define_symbol( | |||||
| create_ast_node_symbol(defined_keyword), | |||||
| copy_ast_node(function->keyword_arguments->values->data[i]), new_env); | |||||
| } | |||||
| } | |||||
| } | |||||
| if (arguments->type == Ast_Node_Type_Nil) { | |||||
| if (function->rest_argument) { | |||||
| define_symbol( | |||||
| create_ast_node_symbol(function->rest_argument), | |||||
| create_ast_node_nil(), new_env); | |||||
| } | |||||
| } else { | |||||
| if (function->rest_argument) { | |||||
| define_symbol( | |||||
| create_ast_node_symbol(function->rest_argument), | |||||
| arguments, new_env); | |||||
| } else { | |||||
| // rest was not declared but additional arguments were found | |||||
| create_error(Error_Type_Ill_Formed_Arguments, arguments->sourceCodeLocation); | |||||
| return nullptr; | |||||
| } | |||||
| } | |||||
| eval_time: { | |||||
| Ast_Node* result; | |||||
| // don't have to check every time if it is macro environment or | |||||
| // not | |||||
| if (function->is_macro) | |||||
| new_env->type = Environment_Type_Macro; | |||||
| try { | |||||
| result = eval_expr(function->body, new_env); | |||||
| } | |||||
| return result; | |||||
| } | |||||
| } | |||||
| /* | |||||
| (prog | |||||
| (define type--before type) | |||||
| (define type | |||||
| (lambda (e) | |||||
| (if (and (= (type--before e) :pair) (= (first e) :my-type)) | |||||
| :my-type | |||||
| (type--before e)))) | |||||
| ) | |||||
| */ | |||||
| /** | |||||
| This parses the argument specification of funcitons into their | |||||
| Function struct. It dois this by allocating new | |||||
| positional_arguments, keyword_arguments and rest_argument and | |||||
| filling it in | |||||
| */ | |||||
| void parse_argument_list(Ast_Node* arguments, Function* function) { | |||||
| // first init the fields | |||||
| function->positional_arguments = create_positional_argument_list(16); | |||||
| function->keyword_arguments = create_keyword_argument_list(16); | |||||
| function->rest_argument = nullptr; | |||||
| // okay let's try to read some positional arguments | |||||
| while (arguments->type == Ast_Node_Type_Pair) { | |||||
| if (arguments->value.pair->first->type == Ast_Node_Type_Keyword) { | |||||
| if (string_equal(arguments->value.pair->first->value.keyword->identifier, "keys") || | |||||
| string_equal(arguments->value.pair->first->value.keyword->identifier, "rest")) | |||||
| break; | |||||
| else { | |||||
| create_error(Error_Type_Ill_Formed_Lambda_List, arguments->sourceCodeLocation); | |||||
| return; | |||||
| } | |||||
| } | |||||
| if (arguments->value.pair->first->type != Ast_Node_Type_Symbol) { | |||||
| create_error(Error_Type_Ill_Formed_Lambda_List, arguments->sourceCodeLocation); | |||||
| return; | |||||
| } | |||||
| // okay wow we found an actual symbol | |||||
| append_to_positional_argument_list( | |||||
| function->positional_arguments, | |||||
| arguments->value.pair->first->value.symbol->identifier); | |||||
| arguments = arguments->value.pair->rest; | |||||
| } | |||||
| // okay we are done with positional arguments, lets check for | |||||
| // keywords, | |||||
| if (arguments->type != Ast_Node_Type_Pair) { | |||||
| if (arguments->type != Ast_Node_Type_Nil) | |||||
| create_error(Error_Type_Ill_Formed_Lambda_List, arguments->sourceCodeLocation); | |||||
| return; | |||||
| } | |||||
| if (arguments->value.pair->first->type == Ast_Node_Type_Keyword && | |||||
| string_equal(arguments->value.pair->first->value.keyword->identifier, "keys")) | |||||
| { | |||||
| arguments = arguments->value.pair->rest; | |||||
| if (arguments->type != Ast_Node_Type_Pair || | |||||
| arguments->value.pair->first->type != Ast_Node_Type_Symbol) | |||||
| { | |||||
| create_error(Error_Type_Ill_Formed_Lambda_List, arguments->sourceCodeLocation); | |||||
| return; | |||||
| } | |||||
| while (arguments->type == Ast_Node_Type_Pair) { | |||||
| if (arguments->value.pair->first->type == Ast_Node_Type_Keyword) { | |||||
| if (string_equal(arguments->value.pair->first->value.keyword->identifier, "rest")) | |||||
| break; | |||||
| else { | |||||
| create_error(Error_Type_Ill_Formed_Lambda_List, arguments->sourceCodeLocation); | |||||
| return; | |||||
| } | |||||
| } | |||||
| if (arguments->value.pair->first->type != Ast_Node_Type_Symbol) { | |||||
| create_error(Error_Type_Ill_Formed_Lambda_List, arguments->sourceCodeLocation); | |||||
| return; | |||||
| } | |||||
| // we found a symbol (arguments->value.pair->first) for | |||||
| // the keyword args! Let's check if the next arguement is | |||||
| // :defaults-to | |||||
| Ast_Node* next = arguments->value.pair->rest; | |||||
| if (next->type == Ast_Node_Type_Pair && | |||||
| next->value.pair->first->type == Ast_Node_Type_Keyword && | |||||
| string_equal(next->value.pair->first->value.keyword->identifier, | |||||
| "defaults-to")) | |||||
| { | |||||
| // check if there is a next argument too, otherwise it | |||||
| // would be an error | |||||
| next = next->value.pair->rest; | |||||
| if (next->type == Ast_Node_Type_Pair) { | |||||
| append_to_keyword_argument_list(function->keyword_arguments, | |||||
| arguments->value.pair->first->value.symbol->identifier, | |||||
| next->value.pair->first); | |||||
| arguments = next->value.pair->rest; | |||||
| } else { | |||||
| create_error(Error_Type_Ill_Formed_Lambda_List, arguments->sourceCodeLocation); | |||||
| return; | |||||
| } | |||||
| } else { | |||||
| // No :defaults-to, so just add it to the list | |||||
| append_to_keyword_argument_list(function->keyword_arguments, | |||||
| arguments->value.pair->first->value.symbol->identifier, | |||||
| nullptr); | |||||
| arguments = next; | |||||
| } | |||||
| } | |||||
| } | |||||
| // Now we are also done with keyword arguments, lets check for | |||||
| // if there is a rest argument | |||||
| if (arguments->type != Ast_Node_Type_Pair) { | |||||
| if (arguments->type != Ast_Node_Type_Nil) | |||||
| create_error(Error_Type_Ill_Formed_Lambda_List, arguments->sourceCodeLocation); | |||||
| return; | |||||
| } | |||||
| if (arguments->value.pair->first->type == Ast_Node_Type_Keyword && | |||||
| string_equal(arguments->value.pair->first->value.keyword->identifier, "rest")) | |||||
| { | |||||
| arguments = arguments->value.pair->rest; | |||||
| if (arguments->type != Ast_Node_Type_Pair || | |||||
| arguments->value.pair->first->type != Ast_Node_Type_Symbol) | |||||
| { | |||||
| create_error(Error_Type_Ill_Formed_Lambda_List, arguments->sourceCodeLocation); | |||||
| return; | |||||
| } | |||||
| function->rest_argument = arguments->value.pair->first->value.symbol->identifier; | |||||
| if (arguments->value.pair->rest->type != Ast_Node_Type_Nil) { | |||||
| create_error(Error_Type_Ill_Formed_Lambda_List, arguments->sourceCodeLocation); | |||||
| } | |||||
| } else { | |||||
| printf("this should not happen?"); | |||||
| create_error(Error_Type_Unknown_Error, arguments->sourceCodeLocation); | |||||
| } | |||||
| } | |||||
| int list_length(Ast_Node* node) { | |||||
| if (node->type == Ast_Node_Type_Nil) | |||||
| return 0; | |||||
| if (node->type != Ast_Node_Type_Pair) { | |||||
| create_error(Error_Type_Type_Missmatch, node->sourceCodeLocation); | |||||
| return 0; | |||||
| } | |||||
| int len = 0; | |||||
| while (node->type == Ast_Node_Type_Pair) { | |||||
| ++len; | |||||
| node = node->value.pair->rest; | |||||
| if (node->type == Ast_Node_Type_Nil) | |||||
| return len; | |||||
| } | |||||
| create_error(Error_Type_Ill_Formed_List, node->sourceCodeLocation); | |||||
| return 0; | |||||
| } | |||||
| bool is_truthy (Ast_Node* expression, Environment* env); | |||||
| Ast_Node* extract_keyword_value(char* keyword, Parsed_Arguments* args) { | |||||
| // NOTE(Felix): This will be a hashmap lookup later | |||||
| for (int i = 0; i < args->keyword_keys->next_index; ++i) { | |||||
| if (string_equal(args->keyword_keys->data[i]->value.keyword->identifier, keyword)) | |||||
| return args->keyword_values->data[i]; | |||||
| } | |||||
| return nullptr; | |||||
| } | |||||
| Ast_Node* eval_arguments(Ast_Node* arguments, Environment* env, int *out_arguments_length) { | |||||
| *out_arguments_length = 0; | |||||
| if (arguments->type == Ast_Node_Type_Nil) { | |||||
| return arguments; | |||||
| } | |||||
| Ast_Node* evaluated_arguments = create_ast_node_pair(nullptr, nullptr); | |||||
| Ast_Node* evaluated_arguments_head = evaluated_arguments; | |||||
| Ast_Node* current_head = arguments; | |||||
| while (current_head->type == Ast_Node_Type_Pair) { | |||||
| try { | |||||
| evaluated_arguments_head->value.pair->first = | |||||
| eval_expr(current_head->value.pair->first, env); | |||||
| } | |||||
| current_head = current_head->value.pair->rest; | |||||
| if (current_head->type == Ast_Node_Type_Pair) { | |||||
| evaluated_arguments_head->value.pair->rest = create_ast_node_pair(nullptr, nullptr); | |||||
| evaluated_arguments_head = evaluated_arguments_head->value.pair->rest; | |||||
| } else if (current_head->type == Ast_Node_Type_Nil) { | |||||
| evaluated_arguments_head->value.pair->rest = current_head; | |||||
| } else { | |||||
| create_error(Error_Type_Ill_Formed_Arguments, arguments->sourceCodeLocation); | |||||
| return nullptr; | |||||
| } | |||||
| ++(*out_arguments_length); | |||||
| } | |||||
| return evaluated_arguments; | |||||
| } | |||||
| Ast_Node* eval_expr(Ast_Node* node, Environment* env) { | |||||
| #define report_error(_type) { \ | |||||
| create_error(_type, node->sourceCodeLocation); \ | |||||
| return nullptr; \ | |||||
| } | |||||
| if (error) | |||||
| return nullptr; | |||||
| Ast_Node* ret = new(Ast_Node); | |||||
| switch (node->type) { | |||||
| case Ast_Node_Type_T: | |||||
| case Ast_Node_Type_Nil: | |||||
| return node; | |||||
| case Ast_Node_Type_Symbol: { | |||||
| Ast_Node* symbol; | |||||
| try { | |||||
| symbol = lookup_symbol(node, env); | |||||
| } | |||||
| return symbol; | |||||
| } | |||||
| case Ast_Node_Type_Number: | |||||
| case Ast_Node_Type_Keyword: | |||||
| case Ast_Node_Type_String: | |||||
| return node; | |||||
| case Ast_Node_Type_Pair: { | |||||
| Ast_Node* lispOperator; | |||||
| if (node->value.pair->first->type != Ast_Node_Type_CFunction && | |||||
| node->value.pair->first->type != Ast_Node_Type_Function) | |||||
| { | |||||
| try { | |||||
| lispOperator = eval_expr(node->value.pair->first, env); | |||||
| } | |||||
| } else { | |||||
| lispOperator = node->value.pair->first; | |||||
| } | |||||
| Ast_Node* arguments = node->value.pair->rest; | |||||
| int arguments_length; | |||||
| // check for c function | |||||
| if (lispOperator->type == Ast_Node_Type_CFunction) { | |||||
| Ast_Node* result = lispOperator->value.cfunction->function(arguments, env); | |||||
| return result; | |||||
| } | |||||
| // check for list function | |||||
| if (lispOperator->type == Ast_Node_Type_Function) { | |||||
| if (!lispOperator->value.function->is_macro) { | |||||
| try { | |||||
| arguments = eval_arguments(arguments, env, &arguments_length); | |||||
| } | |||||
| } | |||||
| Ast_Node* result; | |||||
| try { | |||||
| result = apply_arguments_to_function(arguments, lispOperator->value.function, env); | |||||
| } | |||||
| return result; | |||||
| } | |||||
| } | |||||
| default: { | |||||
| #ifdef _DEBUG | |||||
| __debugbreak(); | |||||
| #endif | |||||
| report_error(Error_Type_Not_A_Function); | |||||
| } | |||||
| } | |||||
| #undef report_error | |||||
| } | |||||
| bool is_truthy (Ast_Node* expression, Environment* env) { | |||||
| Ast_Node* result; | |||||
| try { | |||||
| result = eval_expr(expression, env); | |||||
| } | |||||
| if (result->type == Ast_Node_Type_Nil) | |||||
| return false; | |||||
| return true; | |||||
| } | |||||
| @@ -1,4 +1,4 @@ | |||||
| #define new(type) (type*)malloc(sizeof(type)) | |||||
| #define new(type) new type | |||||
| #define nullptr NULL | #define nullptr NULL | ||||
| #define concat_( a, b) a##b | #define concat_( a, b) a##b | ||||
| @@ -55,7 +55,7 @@ | |||||
| define_array_list(char*, String); | define_array_list(char*, String); | ||||
| typedef enum { false, true } bool; | |||||
| //typedef enum { false, true } bool; | |||||
| int string_equal(char* a, char* b) { | int string_equal(char* a, char* b) { | ||||
| return !strcmp(a, b); | return !strcmp(a, b); | ||||
| @@ -74,7 +74,7 @@ int _vscprintf_so(const char * format, va_list pargs) { | |||||
| int vasprintf(char **strp, const char *fmt, va_list ap) { | int vasprintf(char **strp, const char *fmt, va_list ap) { | ||||
| int len = _vscprintf_so(fmt, ap); | int len = _vscprintf_so(fmt, ap); | ||||
| if (len == -1) return -1; | if (len == -1) return -1; | ||||
| char *str = malloc((size_t) len + 1); | |||||
| char *str = (char *)malloc((size_t) len + 1); | |||||
| if (!str) return -1; | if (!str) return -1; | ||||
| int r = vsnprintf(str, len + 1, fmt, ap); /* "secure" version of vsprintf */ | int r = vsnprintf(str, len + 1, fmt, ap); /* "secure" version of vsprintf */ | ||||
| if (r == -1) return free(str), -1; | if (r == -1) return free(str), -1; | ||||
| @@ -99,6 +99,9 @@ static char get_nibble(char c) { | |||||
| return (c - '0'); | return (c - '0'); | ||||
| } | } | ||||
| #define true 1 | |||||
| #define false 0 | |||||
| #define bool char | |||||
| bool unescape_string(char* in) { | bool unescape_string(char* in) { | ||||
| if (!in) | if (!in) | ||||
| return true; | return true; | ||||
| @@ -190,8 +193,50 @@ char* read_entire_file (char* filename) { | |||||
| /* Don't forget to call free() later! */ | /* Don't forget to call free() later! */ | ||||
| } | } | ||||
| char* read_expression() { | |||||
| char* line = (char*)malloc(100), * linep = line; | |||||
| size_t lenmax = 100, len = lenmax; | |||||
| int c; | |||||
| int nesting = 0; | |||||
| if(line == NULL) | |||||
| return NULL; | |||||
| for(;;) { | |||||
| c = fgetc(stdin); | |||||
| if(c == EOF) | |||||
| break; | |||||
| if(--len == 0) { | |||||
| len = lenmax; | |||||
| char * linen = (char*)realloc(linep, lenmax *= 2); | |||||
| if(linen == NULL) { | |||||
| free(linep); | |||||
| return NULL; | |||||
| } | |||||
| line = linen + (line - linep); | |||||
| linep = linen; | |||||
| } | |||||
| *line++; | |||||
| if((*line = (char)c) == '(') | |||||
| ++nesting; | |||||
| else if((*line = (char)c) == ')') | |||||
| --nesting; | |||||
| else if((*line = (char)c) == '\n') | |||||
| if (nesting == 0) | |||||
| break; | |||||
| } | |||||
| (*line)--; // we dont want the \n actually | |||||
| *line = '\0'; | |||||
| // BUG(Felix): Why do we have to add 1 here? | |||||
| return linep + 1; | |||||
| } | |||||
| char* read_line() { | char* read_line() { | ||||
| char* line = malloc(100), * linep = line; | |||||
| char* line = (char*)malloc(100), * linep = line; | |||||
| size_t lenmax = 100, len = lenmax; | size_t lenmax = 100, len = lenmax; | ||||
| int c; | int c; | ||||
| @@ -207,7 +252,7 @@ char* read_line() { | |||||
| if(--len == 0) { | if(--len == 0) { | ||||
| len = lenmax; | len = lenmax; | ||||
| char * linen = realloc(linep, lenmax *= 2); | |||||
| char* linen = (char*)realloc(linep, lenmax *= 2); | |||||
| if(linen == NULL) { | if(linen == NULL) { | ||||
| free(linep); | free(linep); | ||||
| @@ -228,7 +273,8 @@ char* read_line() { | |||||
| } | } | ||||
| (*line)--; // we dont want the \n actually | (*line)--; // we dont want the \n actually | ||||
| *line = '\0'; | *line = '\0'; | ||||
| return linep; | |||||
| // BUG(Felix): Why do we have to add 1 here? | |||||
| return linep + 1; | |||||
| } | } | ||||
| @@ -1,18 +0,0 @@ | |||||
| // for colored console output | |||||
| #ifdef _WIN32 | |||||
| #include <windows.h> | |||||
| #pragma comment(lib, "Kernel32.lib") | |||||
| #endif | |||||
| void init () { | |||||
| // On Windows: manually enable colored console output | |||||
| #ifdef _WIN32 | |||||
| SetConsoleMode(GetStdHandle(STD_OUTPUT_HANDLE), | |||||
| ENABLE_PROCESSED_OUTPUT | | |||||
| ENABLE_WRAP_AT_EOL_OUTPUT | | |||||
| ENABLE_VIRTUAL_TERMINAL_PROCESSING); | |||||
| #endif | |||||
| // TODO(Felix): Init the constants here (Bulit-in function | |||||
| // ast_nodes, nil, type_keywords ..more?) | |||||
| } | |||||
| @@ -66,8 +66,8 @@ void print(Ast_Node* node) { | |||||
| else | else | ||||
| printf("[lambda]"); | printf("[lambda]"); | ||||
| } break; | } break; | ||||
| case (Ast_Node_Type_Built_In_Function): { | |||||
| printf("[built-in-function %s]", Built_In_Name_to_string(node->value.built_in_function->type)); | |||||
| case (Ast_Node_Type_CFunction): { | |||||
| printf("[C-function]"); | |||||
| } break; | } break; | ||||
| case (Ast_Node_Type_Pair): { | case (Ast_Node_Type_Pair): { | ||||
| Ast_Node* head = node; | Ast_Node* head = node; | ||||
| @@ -112,6 +112,6 @@ void log_error() { | |||||
| Error_Type_to_string(error->type), | Error_Type_to_string(error->type), | ||||
| console_normal); | console_normal); | ||||
| printf(" in: %s", console_cyan); | printf(" in: %s", console_cyan); | ||||
| print_error_location(error->location); | |||||
| print_error_location(); | |||||
| printf("%s\n", console_normal); | printf("%s\n", console_normal); | ||||
| } | } | ||||
| @@ -1,3 +1,4 @@ | |||||
| #pragma once | |||||
| #define _CRT_SECURE_NO_DEPRECATE | #define _CRT_SECURE_NO_DEPRECATE | ||||
| #include <stdio.h> | #include <stdio.h> | ||||
| #include <string.h> | #include <string.h> | ||||
| @@ -5,48 +6,52 @@ | |||||
| #include <stdarg.h> /* needed for va_list */ | #include <stdarg.h> /* needed for va_list */ | ||||
| #include <ctype.h> | #include <ctype.h> | ||||
| #include <math.h> | #include <math.h> | ||||
| #include <functional> | |||||
| #include "./helpers.c" | |||||
| #include "./ast.c" | |||||
| #include "./error.c" | |||||
| #include "./io.c" | |||||
| #include "./assert.c" | |||||
| #include "./parse.c" | |||||
| #include "./env.c" | |||||
| #include "./built_ins.c" | |||||
| #include "./eval.c" | |||||
| #include "./testing.c" | |||||
| #include "./init.c" | |||||
| #include "./helpers.cpp" | |||||
| #include "./ast.cpp" | |||||
| #include "./error.cpp" | |||||
| #include "./io.cpp" | |||||
| #include "./assert.cpp" | |||||
| #include "./parse.cpp" | |||||
| #include "./env.cpp" | |||||
| #include "./built_ins.cpp" | |||||
| #include "./eval.cpp" | |||||
| #include "./testing.cpp" | |||||
| void interprete_file (char* file_name) { | |||||
| Ast_Node* interprete_file (char* file_name) { | |||||
| char* file_content = read_entire_file(file_name); | char* file_content = read_entire_file(file_name); | ||||
| if (!file_content) { | if (!file_content) { | ||||
| create_error(Error_Type_Unknown_Error, nullptr); | create_error(Error_Type_Unknown_Error, nullptr); | ||||
| } | } | ||||
| Ast_Node_Array_List* program; | Ast_Node_Array_List* program; | ||||
| try_void { | |||||
| parse_program(file_name, file_content); | |||||
| try { | |||||
| program = parse_program(file_name, file_content); | |||||
| } | } | ||||
| Environment* env = create_empty_environment(Environment_Type_Let); | Environment* env = create_empty_environment(Environment_Type_Let); | ||||
| load_built_ins_into_environment(env); | |||||
| try_void { | |||||
| try { | |||||
| built_in_load("pre.slime", env); | built_in_load("pre.slime", env); | ||||
| } | } | ||||
| Ast_Node* result = create_ast_node_nil(); | Ast_Node* result = create_ast_node_nil(); | ||||
| for (int i = 0; i < program->next_index; ++i) { | for (int i = 0; i < program->next_index; ++i) { | ||||
| try_void { | |||||
| try { | |||||
| result = eval_expr(program->data[i], env); | result = eval_expr(program->data[i], env); | ||||
| } | } | ||||
| } | } | ||||
| return result; | |||||
| } | } | ||||
| int interprete_stdin () { | int interprete_stdin () { | ||||
| printf("Welcome to the lispy interpreter.\n"); | printf("Welcome to the lispy interpreter.\n"); | ||||
| char* line; | char* line; | ||||
| Environment* env = create_empty_environment(Environment_Type_Let); | Environment* env = create_empty_environment(Environment_Type_Let); | ||||
| load_built_ins_into_environment(env); | |||||
| built_in_load("pre.slime", env); | built_in_load("pre.slime", env); | ||||
| if (error) { | if (error) { | ||||
| @@ -57,7 +62,7 @@ int interprete_stdin () { | |||||
| Ast_Node* parsed, * evaluated; | Ast_Node* parsed, * evaluated; | ||||
| while (true) { | while (true) { | ||||
| printf(">"); | printf(">"); | ||||
| line = read_line(); | |||||
| line = read_expression(); | |||||
| parsed = parse_single_expression(line); | parsed = parse_single_expression(line); | ||||
| if (error) { | if (error) { | ||||
| log_error(); | log_error(); | ||||
| @@ -77,8 +82,6 @@ int interprete_stdin () { | |||||
| } | } | ||||
| int main (int argc, char *argv[]) { | int main (int argc, char *argv[]) { | ||||
| init(); | |||||
| if (argc > 1) { | if (argc > 1) { | ||||
| interprete_file(argv[1]); | interprete_file(argv[1]); | ||||
| if (error) { | if (error) { | ||||
| @@ -89,7 +89,7 @@ testresult test_eval_operands() { | |||||
| char operands_string[] = "((eval 1) (+ 1 2) \"okay\" (eval :haha))"; | char operands_string[] = "((eval 1) (+ 1 2) \"okay\" (eval :haha))"; | ||||
| Ast_Node* operands = parse_single_expression(operands_string); | Ast_Node* operands = parse_single_expression(operands_string); | ||||
| int operands_length; | int operands_length; | ||||
| operands = eval_arguments(operands, create_empty_environment(Environment_Type_Let), &operands_length); | |||||
| operands = eval_arguments(operands, create_built_ins_environment(), &operands_length); | |||||
| assert_no_error(error); | assert_no_error(error); | ||||
| assert_equal_int(list_length(operands), 4); | assert_equal_int(list_length(operands), 4); | ||||
| @@ -233,7 +233,7 @@ testresult test_parse_expression() { | |||||
| testresult test_built_in_add() { | testresult test_built_in_add() { | ||||
| char exp_string[] = "(+ 10 4)"; | char exp_string[] = "(+ 10 4)"; | ||||
| Ast_Node* expression = parse_single_expression(exp_string); | Ast_Node* expression = parse_single_expression(exp_string); | ||||
| Ast_Node* result = eval_expr(expression, create_empty_environment(Environment_Type_Let)); | |||||
| Ast_Node* result = eval_expr(expression, create_built_ins_environment()); | |||||
| assert_no_error(error); | assert_no_error(error); | ||||
| assert_not_null(result); | assert_not_null(result); | ||||
| @@ -246,7 +246,7 @@ testresult test_built_in_add() { | |||||
| testresult test_built_in_substract() { | testresult test_built_in_substract() { | ||||
| char exp_string[] = "(- 10 4)"; | char exp_string[] = "(- 10 4)"; | ||||
| Ast_Node* expression = parse_single_expression(exp_string); | Ast_Node* expression = parse_single_expression(exp_string); | ||||
| Ast_Node* result = eval_expr(expression, create_empty_environment(Environment_Type_Let)); | |||||
| Ast_Node* result = eval_expr(expression, create_built_ins_environment()); | |||||
| assert_no_error(error); | assert_no_error(error); | ||||
| assert_not_null(result); | assert_not_null(result); | ||||
| @@ -260,7 +260,7 @@ testresult test_built_in_substract() { | |||||
| testresult test_built_in_multiply() { | testresult test_built_in_multiply() { | ||||
| char exp_string[] = "(* 10 4)"; | char exp_string[] = "(* 10 4)"; | ||||
| Ast_Node* expression = parse_single_expression(exp_string); | Ast_Node* expression = parse_single_expression(exp_string); | ||||
| Ast_Node* result = eval_expr(expression, create_empty_environment(Environment_Type_Let)); | |||||
| Ast_Node* result = eval_expr(expression, create_built_ins_environment()); | |||||
| assert_no_error(error); | assert_no_error(error); | ||||
| assert_not_null(result); | assert_not_null(result); | ||||
| @@ -274,7 +274,7 @@ testresult test_built_in_multiply() { | |||||
| testresult test_built_in_divide() { | testresult test_built_in_divide() { | ||||
| char exp_string[] = "(/ 20 4)"; | char exp_string[] = "(/ 20 4)"; | ||||
| Ast_Node* expression = parse_single_expression(exp_string); | Ast_Node* expression = parse_single_expression(exp_string); | ||||
| Ast_Node* result = eval_expr(expression, create_empty_environment(Environment_Type_Let)); | |||||
| Ast_Node* result = eval_expr(expression, create_built_ins_environment()); | |||||
| assert_null(error); | assert_null(error); | ||||
| assert_not_null(result); | assert_not_null(result); | ||||
| @@ -288,7 +288,7 @@ testresult test_built_in_divide() { | |||||
| testresult test_built_in_if() { | testresult test_built_in_if() { | ||||
| char exp_string1[] = "(if 1 4 5)"; | char exp_string1[] = "(if 1 4 5)"; | ||||
| Ast_Node* expression = parse_single_expression(exp_string1); | Ast_Node* expression = parse_single_expression(exp_string1); | ||||
| Ast_Node* result = eval_expr(expression, create_empty_environment(Environment_Type_Let)); | |||||
| Ast_Node* result = eval_expr(expression, create_built_ins_environment()); | |||||
| assert_no_error(error); | assert_no_error(error); | ||||
| assert_not_null(result); | assert_not_null(result); | ||||
| @@ -297,7 +297,7 @@ testresult test_built_in_if() { | |||||
| char exp_string2[] = "(if () 4 5)"; | char exp_string2[] = "(if () 4 5)"; | ||||
| expression = parse_single_expression(exp_string2); | expression = parse_single_expression(exp_string2); | ||||
| result = eval_expr(expression, create_empty_environment(Environment_Type_Let)); | |||||
| result = eval_expr(expression, create_built_ins_environment()); | |||||
| assert_no_error(error); | assert_no_error(error); | ||||
| assert_not_null(result); | assert_not_null(result); | ||||
| @@ -310,7 +310,7 @@ testresult test_built_in_if() { | |||||
| testresult test_built_in_and() { | testresult test_built_in_and() { | ||||
| char exp_string1[] = "(and 1 \"asd\" 4)"; | char exp_string1[] = "(and 1 \"asd\" 4)"; | ||||
| Ast_Node* expression = parse_single_expression(exp_string1); | Ast_Node* expression = parse_single_expression(exp_string1); | ||||
| Ast_Node* result = eval_expr(expression, create_empty_environment(Environment_Type_Let)); | |||||
| Ast_Node* result = eval_expr(expression, create_built_ins_environment()); | |||||
| assert_no_error(error); | assert_no_error(error); | ||||
| assert_not_null(result); | assert_not_null(result); | ||||
| @@ -319,7 +319,7 @@ testresult test_built_in_and() { | |||||
| // a false case | // a false case | ||||
| char exp_string2[] = "(and () \"asd\" 4)"; | char exp_string2[] = "(and () \"asd\" 4)"; | ||||
| expression = parse_single_expression(exp_string2); | expression = parse_single_expression(exp_string2); | ||||
| result = eval_expr(expression, create_empty_environment(Environment_Type_Let)); | |||||
| result = eval_expr(expression, create_built_ins_environment()); | |||||
| assert_no_error(error); | assert_no_error(error); | ||||
| assert_not_null(result); | assert_not_null(result); | ||||
| @@ -331,7 +331,7 @@ testresult test_built_in_and() { | |||||
| testresult test_built_in_or() { | testresult test_built_in_or() { | ||||
| char exp_string1[] = "(or \"asd\" nil)"; | char exp_string1[] = "(or \"asd\" nil)"; | ||||
| Ast_Node* expression = parse_single_expression(exp_string1); | Ast_Node* expression = parse_single_expression(exp_string1); | ||||
| Ast_Node* result = eval_expr(expression, create_empty_environment(Environment_Type_Let)); | |||||
| Ast_Node* result = eval_expr(expression, create_built_ins_environment()); | |||||
| assert_no_error(error); | assert_no_error(error); | ||||
| assert_not_null(result); | assert_not_null(result); | ||||
| @@ -340,7 +340,7 @@ testresult test_built_in_or() { | |||||
| // a false case | // a false case | ||||
| char exp_string2[] = "(or () ())"; | char exp_string2[] = "(or () ())"; | ||||
| expression = parse_single_expression(exp_string2); | expression = parse_single_expression(exp_string2); | ||||
| result = eval_expr(expression, create_empty_environment(Environment_Type_Let)); | |||||
| result = eval_expr(expression, create_built_ins_environment()); | |||||
| assert_no_error(error); | assert_no_error(error); | ||||
| assert_not_null(result); | assert_not_null(result); | ||||
| @@ -353,7 +353,7 @@ testresult test_built_in_or() { | |||||
| testresult test_built_in_not() { | testresult test_built_in_not() { | ||||
| char exp_string1[] = "(not ())"; | char exp_string1[] = "(not ())"; | ||||
| Ast_Node* expression = parse_single_expression(exp_string1); | Ast_Node* expression = parse_single_expression(exp_string1); | ||||
| Ast_Node* result = eval_expr(expression, create_empty_environment(Environment_Type_Let)); | |||||
| Ast_Node* result = eval_expr(expression, create_built_ins_environment()); | |||||
| // a true case | // a true case | ||||
| assert_no_error(error); | assert_no_error(error); | ||||
| @@ -363,7 +363,7 @@ testresult test_built_in_not() { | |||||
| // a false case | // a false case | ||||
| char exp_string2[] = "(not \"asd xD\")"; | char exp_string2[] = "(not \"asd xD\")"; | ||||
| expression = parse_single_expression(exp_string2); | expression = parse_single_expression(exp_string2); | ||||
| result = eval_expr(expression, create_empty_environment(Environment_Type_Let)); | |||||
| result = eval_expr(expression, create_built_ins_environment()); | |||||
| assert_no_error(error); | assert_no_error(error); | ||||
| assert_not_null(result); | assert_not_null(result); | ||||
| @@ -4,7 +4,7 @@ pushd %~dp0\bin | |||||
| call ..\build.bat | call ..\build.bat | ||||
| if %errorlevel% == 0 ( | if %errorlevel% == 0 ( | ||||
| echo ---------- Testing ---------- | echo ---------- Testing ---------- | ||||
| call timecmd slime.exe test.slime | |||||
| call timecmd ..\build\slime.exe test.slime | |||||
| ) | ) | ||||
| popd | popd | ||||
| @@ -168,9 +168,9 @@ | |||||
| </Link> | </Link> | ||||
| </ItemDefinitionGroup> | </ItemDefinitionGroup> | ||||
| <ItemGroup> | <ItemGroup> | ||||
| <ClCompile Include="..\src\main.c" /> | |||||
| <ClCompile Include="..\src\main.cpp" /> | |||||
| </ItemGroup> | </ItemGroup> | ||||
| <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> | <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> | ||||
| <ImportGroup Label="ExtensionTargets"> | <ImportGroup Label="ExtensionTargets"> | ||||
| </ImportGroup> | </ImportGroup> | ||||
| </Project> | |||||
| </Project> | |||||