diff --git a/.dir-locals.el b/.dir-locals.el index 36224f3..39bb4e6 100644 --- a/.dir-locals.el +++ b/.dir-locals.el @@ -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 . (rainbow-mode 0)))) (nil . ((eval . (progn diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..73a87d5 --- /dev/null +++ b/CMakeLists.txt @@ -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}) diff --git a/bin/test.slime b/bin/test.slime index d7aab74..63b72ba 100644 --- a/bin/test.slime +++ b/bin/test.slime @@ -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 " ") diff --git a/build.bat b/build.bat index 806cf4d..c9a9b48 100644 --- a/build.bat +++ b/build.bat @@ -11,7 +11,7 @@ pushd build taskkill /F /IM %exeName% > NUL 2> NUL 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 ( echo. diff --git a/build/slime.exe.dbg b/build/slime.exe.dbg index ace130a..951aec9 100644 Binary files a/build/slime.exe.dbg and b/build/slime.exe.dbg differ diff --git a/src/assert.c b/src/assert.cpp similarity index 100% rename from src/assert.c rename to src/assert.cpp diff --git a/src/ast.c b/src/ast.cpp similarity index 50% rename from src/ast.c rename to src/ast.cpp index 11e6787..1a8999e 100644 --- a/src/ast.c +++ b/src/ast.cpp @@ -12,7 +12,7 @@ typedef enum { Ast_Node_Type_Pair, Ast_Node_Type_Function, - Ast_Node_Type_Built_In_Function, + Ast_Node_Type_CFunction, } Ast_Node_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_Keyword): return "keyword"; 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"; } return "unknown"; @@ -122,96 +122,10 @@ typedef struct { struct Ast_Node* body; // implicit prog } 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 { - Built_In_Name type; -} Built_In_Function; + std::function function; +} CFunction; struct Ast_Node { Source_Code_Location* sourceCodeLocation; @@ -223,7 +137,7 @@ struct Ast_Node { String* string; Pair* pair; Function* function; - Built_In_Function* built_in_function; + CFunction* cfunction; } value; }; // was forward declarated @@ -290,52 +204,11 @@ Ast_Node* create_ast_node_keyword(char* keyword) { 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 function) { 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; } diff --git a/src/built_ins.c b/src/built_ins.c deleted file mode 100644 index c804156..0000000 --- a/src/built_ins.c +++ /dev/null @@ -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; - } -} diff --git a/src/built_ins.cpp b/src/built_ins.cpp new file mode 100644 index 0000000..7ce0289 --- /dev/null +++ b/src/built_ins.cpp @@ -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 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; +} diff --git a/src/env.c b/src/env.cpp similarity index 95% rename from src/env.c rename to src/env.cpp index b30aecb..6fa9962 100644 --- a/src/env.c +++ b/src/env.cpp @@ -144,10 +144,13 @@ Ast_Node* lookup_symbol(Ast_Node* node, Environment* env) { 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); /* printf("%s\n", sym->identifier); */ diff --git a/src/error.c b/src/error.cpp similarity index 98% rename from src/error.c rename to src/error.cpp index b9de3b8..e4249b1 100644 --- a/src/error.c +++ b/src/error.cpp @@ -15,7 +15,7 @@ typedef enum { Error_Type_Wrong_Number_Of_Arguments, } Error_Type; -typedef struct { +typedef struct Error_t { Error_Type type; Source_Code_Location* location; } Error; @@ -31,7 +31,7 @@ void delete_error() { void create_error(Error_Type type, Source_Code_Location* location) { delete_error(); - + __debugbreak(); error = new(Error); error->type = type; error->location = location; diff --git a/src/eval.c b/src/eval.c deleted file mode 100644 index 5e9b30c..0000000 --- a/src/eval.c +++ /dev/null @@ -1,1015 +0,0 @@ -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; \ - } -#define report_error_operator(_type) { \ - create_error(_type, operator->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* operator; - if (node->value.pair->first->type != Ast_Node_Type_Built_In_Function && - node->value.pair->first->type != Ast_Node_Type_Function) - { - try { - operator = eval_expr(node->value.pair->first, env); - } - } else { - operator = node->value.pair->first; - } - - Ast_Node* arguments = node->value.pair->rest; - int arguments_length; - - // check for special form - if (operator->type == Ast_Node_Type_Built_In_Function) { - switch (operator->value.built_in_function->type) { - case Built_In_Breakpoint: { - print_environment(env); -#ifdef _DEBUG - __debugbreak(); -#endif - return create_ast_node_nil(); - } - case Built_In_Info: { - try { - arguments_length = list_length(arguments); - } - if (arguments_length != 1) { - report_error_operator(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(); - } - case Built_In_Macro: - case Built_In_Lambda: { - /* - * (lambda ()) - * (lambda (x d) (+ 1 2) (- 1 2) (* 1 2)) - */ - try { - arguments_length = list_length(arguments); - } - if (arguments_length == 0) - report_error_operator(Error_Type_Wrong_Number_Of_Arguments); - - - Function* function = new(Function); - if (operator->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_built_in_function("prog"), - arguments); - - Ast_Node* ret = new(Ast_Node); - ret->type = Ast_Node_Type_Function; - ret->value.function = function; - return ret; - } - case Built_In_Let: { - // (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 = rest_sym->value.pair->first; - - 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; - - } - case Built_In_And: { - 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(); - } - case Built_In_Or: { - 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(); - } - case Built_In_Not: { - try { - arguments_length = list_length(arguments); - } - if (arguments_length != 1) { - report_error_operator(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(); - } - case Built_In_While: { - try { - arguments_length = list_length(arguments); - } - - if (arguments_length < 2) { - report_error_operator(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; - - } - case Built_In_If: { - try { - arguments_length = list_length(arguments); - } - if (arguments_length != 2 && arguments_length != 3) { - report_error_operator(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; - } - case Built_In_Quote: { - arguments_length = list_length(arguments); - if (arguments_length != 1) { - report_error_operator(Error_Type_Wrong_Number_Of_Arguments); - } - return arguments->value.pair->first; - } - case Built_In_Try: { - try { - arguments_length = list_length(arguments); - } - if (arguments_length != 2) { - report_error_operator(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; - } - case Built_In_Macro_Define: - case Built_In_Define: { - try { - arguments_length = list_length(arguments); - } - if (arguments_length != 2) { - report_error_operator(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; - /* print(value); */ - try { - value = eval_expr(value, env); - } - /* print(value); */ - - if (operator->value.built_in_function->type == Built_In_Macro_Define) { - /* printf("Defining %s in the macro env to be ", symbol->value.symbol->identifier); */ - /* print(value); */ - - define_macro_symbol(symbol, value, env); - } - else - define_symbol(symbol, value, env); - - return value; - } - } - - // okay it is not a special form, so in any case we want - // to evaluate the arguments; eval_arguments will also tell - // us the arguments_length. - Ast_Node* evaluated_arguments; - try { - evaluated_arguments = eval_arguments(arguments, env, &arguments_length); - } - - switch (operator->value.built_in_function->type) { - case Built_In_Addition: { - return built_in_add(evaluated_arguments); - } - case Built_In_Subtraction: { - return built_in_substract(evaluated_arguments); - } - case Built_In_Multiplication: { - return built_in_multiply(evaluated_arguments); - } - case Built_In_Division: { - return built_in_divide(evaluated_arguments); - } - case Built_In_Equal: { - return built_in_equals(evaluated_arguments); - } - case Built_In_Greater: { - return built_in_greater(evaluated_arguments); - } - case Built_In_Greater_Equal: { - return built_in_greater_equal(evaluated_arguments); - } - case Built_In_Less: { - return built_in_less(evaluated_arguments); - } - case Built_In_Less_Equal: { - return built_in_less_equal(evaluated_arguments); - } - case Built_In_Mutate: { - if (arguments_length != 2) - report_error_operator(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; - } - case Built_In_Copy: { - if (arguments_length != 1) - report_error_operator(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; - } - case Built_In_Load: { - if (arguments_length != 1) - report_error_operator(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; - } - case Built_In_Pair: { - if (arguments_length != 2) { - report_error_operator(Error_Type_Wrong_Number_Of_Arguments); - } - return create_ast_node_pair(evaluated_arguments->value.pair->first, evaluated_arguments->value.pair->rest->value.pair->first); - } - case Built_In_First: { - if (arguments_length != 1) { - report_error_operator(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; - } - case Built_In_Rest: { - if (arguments_length != 1) { - report_error_operator(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; - } - case Built_In_Eval: { - if (arguments_length != 1) { - report_error_operator(Error_Type_Wrong_Number_Of_Arguments); - } - Ast_Node* result; - try { - result = eval_expr(evaluated_arguments->value.pair->first, env); - } - return result; - } - case Built_In_Prog: { - 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; - } - case Built_In_List: { - return evaluated_arguments; - } - case Built_In_Error: { - if (arguments_length != 0) { - report_error_operator(Error_Type_Wrong_Number_Of_Arguments); - } - report_error(Error_Type_Unknown_Error); - } - case Built_In_Print: { - if (arguments_length != 1) { - report_error_operator(Error_Type_Wrong_Number_Of_Arguments); - } - print(evaluated_arguments->value.pair->first); - /* printf("\n"); */ - return arguments->value.pair->first; - } - case Built_In_Read: { - if (arguments_length > 1) { - report_error_operator(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)); - } - case Built_In_Type: { - if (arguments_length != 1) { - report_error_operator(Error_Type_Wrong_Number_Of_Arguments); - } - - Ast_Node_Type type = evaluated_arguments->value.pair->first->type; - switch (type) { - case Ast_Node_Type_Built_In_Function: return create_ast_node_keyword("built-in-function"); - 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"); - } - } - case Built_In_Exit: { - if (arguments_length > 1) { - report_error_operator(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); - } - - default: - report_error(Error_Type_Not_Yet_Implemented); - } - - } - - if (operator->type == Ast_Node_Type_Function) { - if (!operator->value.function->is_macro) { - try { - arguments = eval_arguments(arguments, env, &arguments_length); - } - } - - Ast_Node* result; - try { - result = apply_arguments_to_function(arguments, operator->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; - -} diff --git a/src/eval.cpp b/src/eval.cpp new file mode 100644 index 0000000..bbfa142 --- /dev/null +++ b/src/eval.cpp @@ -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; + +} diff --git a/src/helpers.c b/src/helpers.cpp similarity index 85% rename from src/helpers.c rename to src/helpers.cpp index ee25f47..34e4ce7 100644 --- a/src/helpers.c +++ b/src/helpers.cpp @@ -1,4 +1,4 @@ -#define new(type) (type*)malloc(sizeof(type)) +#define new(type) new type #define nullptr NULL #define concat_( a, b) a##b @@ -55,7 +55,7 @@ define_array_list(char*, String); -typedef enum { false, true } bool; +//typedef enum { false, true } bool; int string_equal(char* a, char* 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 len = _vscprintf_so(fmt, ap); if (len == -1) return -1; - char *str = malloc((size_t) len + 1); + char *str = (char *)malloc((size_t) len + 1); if (!str) return -1; int r = vsnprintf(str, len + 1, fmt, ap); /* "secure" version of vsprintf */ if (r == -1) return free(str), -1; @@ -99,6 +99,9 @@ static char get_nibble(char c) { return (c - '0'); } +#define true 1 +#define false 0 +#define bool char bool unescape_string(char* in) { if (!in) return true; @@ -190,8 +193,50 @@ char* read_entire_file (char* filename) { /* 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* line = malloc(100), * linep = line; + char* line = (char*)malloc(100), * linep = line; size_t lenmax = 100, len = lenmax; int c; @@ -207,7 +252,7 @@ char* read_line() { if(--len == 0) { len = lenmax; - char * linen = realloc(linep, lenmax *= 2); + char* linen = (char*)realloc(linep, lenmax *= 2); if(linen == NULL) { free(linep); @@ -228,7 +273,8 @@ char* read_line() { } (*line)--; // we dont want the \n actually *line = '\0'; - return linep; + // BUG(Felix): Why do we have to add 1 here? + return linep + 1; } diff --git a/src/init.c b/src/init.c deleted file mode 100644 index fa93ae6..0000000 --- a/src/init.c +++ /dev/null @@ -1,18 +0,0 @@ -// for colored console output -#ifdef _WIN32 -#include -#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?) -} diff --git a/src/io.c b/src/io.cpp similarity index 93% rename from src/io.c rename to src/io.cpp index 523242f..ca7fe8c 100644 --- a/src/io.c +++ b/src/io.cpp @@ -66,8 +66,8 @@ void print(Ast_Node* node) { else printf("[lambda]"); } 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; case (Ast_Node_Type_Pair): { Ast_Node* head = node; @@ -112,6 +112,6 @@ void log_error() { Error_Type_to_string(error->type), console_normal); printf(" in: %s", console_cyan); - print_error_location(error->location); + print_error_location(); printf("%s\n", console_normal); } diff --git a/src/main.c b/src/main.cpp similarity index 75% rename from src/main.c rename to src/main.cpp index b9b749a..cbcfe6c 100644 --- a/src/main.c +++ b/src/main.cpp @@ -1,3 +1,4 @@ +#pragma once #define _CRT_SECURE_NO_DEPRECATE #include #include @@ -5,48 +6,52 @@ #include /* needed for va_list */ #include #include +#include -#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); if (!file_content) { create_error(Error_Type_Unknown_Error, nullptr); } 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); + load_built_ins_into_environment(env); - try_void { + try { built_in_load("pre.slime", env); } Ast_Node* result = create_ast_node_nil(); for (int i = 0; i < program->next_index; ++i) { - try_void { + try { result = eval_expr(program->data[i], env); } } + + return result; } int interprete_stdin () { printf("Welcome to the lispy interpreter.\n"); char* line; Environment* env = create_empty_environment(Environment_Type_Let); + load_built_ins_into_environment(env); built_in_load("pre.slime", env); if (error) { @@ -57,7 +62,7 @@ int interprete_stdin () { Ast_Node* parsed, * evaluated; while (true) { printf(">"); - line = read_line(); + line = read_expression(); parsed = parse_single_expression(line); if (error) { log_error(); @@ -77,8 +82,6 @@ int interprete_stdin () { } int main (int argc, char *argv[]) { - init(); - if (argc > 1) { interprete_file(argv[1]); if (error) { diff --git a/src/parse.c b/src/parse.cpp similarity index 100% rename from src/parse.c rename to src/parse.cpp diff --git a/src/testing.c b/src/testing.cpp similarity index 91% rename from src/testing.c rename to src/testing.cpp index 6588783..e751766 100644 --- a/src/testing.c +++ b/src/testing.cpp @@ -89,7 +89,7 @@ testresult test_eval_operands() { char operands_string[] = "((eval 1) (+ 1 2) \"okay\" (eval :haha))"; Ast_Node* operands = parse_single_expression(operands_string); 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_equal_int(list_length(operands), 4); @@ -233,7 +233,7 @@ testresult test_parse_expression() { testresult test_built_in_add() { char exp_string[] = "(+ 10 4)"; 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_not_null(result); @@ -246,7 +246,7 @@ testresult test_built_in_add() { testresult test_built_in_substract() { char exp_string[] = "(- 10 4)"; 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_not_null(result); @@ -260,7 +260,7 @@ testresult test_built_in_substract() { testresult test_built_in_multiply() { char exp_string[] = "(* 10 4)"; 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_not_null(result); @@ -274,7 +274,7 @@ testresult test_built_in_multiply() { testresult test_built_in_divide() { char exp_string[] = "(/ 20 4)"; 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_not_null(result); @@ -288,7 +288,7 @@ testresult test_built_in_divide() { testresult test_built_in_if() { char exp_string1[] = "(if 1 4 5)"; 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_not_null(result); @@ -297,7 +297,7 @@ testresult test_built_in_if() { char exp_string2[] = "(if () 4 5)"; 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_not_null(result); @@ -310,7 +310,7 @@ testresult test_built_in_if() { testresult test_built_in_and() { char exp_string1[] = "(and 1 \"asd\" 4)"; 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_not_null(result); @@ -319,7 +319,7 @@ testresult test_built_in_and() { // a false case char exp_string2[] = "(and () \"asd\" 4)"; 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_not_null(result); @@ -331,7 +331,7 @@ testresult test_built_in_and() { testresult test_built_in_or() { char exp_string1[] = "(or \"asd\" nil)"; 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_not_null(result); @@ -340,7 +340,7 @@ testresult test_built_in_or() { // a false case char exp_string2[] = "(or () ())"; 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_not_null(result); @@ -353,7 +353,7 @@ testresult test_built_in_or() { testresult test_built_in_not() { char exp_string1[] = "(not ())"; 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 assert_no_error(error); @@ -363,7 +363,7 @@ testresult test_built_in_not() { // a false case char exp_string2[] = "(not \"asd xD\")"; 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_not_null(result); diff --git a/test.bat b/test.bat index 5c794e4..d566461 100644 --- a/test.bat +++ b/test.bat @@ -4,7 +4,7 @@ pushd %~dp0\bin call ..\build.bat if %errorlevel% == 0 ( echo ---------- Testing ---------- - call timecmd slime.exe test.slime + call timecmd ..\build\slime.exe test.slime ) popd diff --git a/vs/slime.vcxproj b/vs/slime.vcxproj index 8236687..cd4d45a 100644 --- a/vs/slime.vcxproj +++ b/vs/slime.vcxproj @@ -168,9 +168,9 @@ - + - \ No newline at end of file +