From 46f76fc3d9964a8a6128e3102af9e77b56b7dec3 Mon Sep 17 00:00:00 2001 From: FelixBrendel Date: Sat, 13 Oct 2018 17:50:46 +0200 Subject: [PATCH] built-ins are identified by an enum now --- .dir-locals.el | 4 +- src/ast.c | 106 ++++++++++- src/built_ins.c | 5 +- src/env.c | 34 +--- src/error.c | 2 + src/eval.c | 496 +++++++++++++++++++++++++----------------------- src/io.c | 2 +- src/testing.c | 3 +- todo.org | 4 + 9 files changed, 374 insertions(+), 282 deletions(-) diff --git a/.dir-locals.el b/.dir-locals.el index beab90c..4565eb5 100644 --- a/.dir-locals.el +++ b/.dir-locals.el @@ -32,5 +32,5 @@ ("d" save-and-find-debug-script-and-compile "debug" :color blue) ("t" save-and-find-test-script-and-compile "test" :color blue) ("q" nil "quit" :color blue)) - (define-key context-mode-map (kbd "") 'hydra-context/body) - ))))) + + (define-key context-mode-map (kbd "") 'hydra-context/body)))))) diff --git a/src/ast.c b/src/ast.c index 5eb5349..96c4ec3 100644 --- a/src/ast.c +++ b/src/ast.c @@ -46,8 +46,77 @@ typedef struct { char* docstring; } Function; +typedef enum { + Built_In_Addition, + Built_In_Subtraction, + Built_In_Multiplication, + Built_In_Division, + Built_In_Greater, + Built_In_Less, + Built_In_Equal, + Built_In_Greater_Equal, + Built_In_Less_Equal, + Built_In_If, + Built_In_And, + Built_In_Or, + Built_In_Not, + Built_In_Pair, + Built_In_First, + Built_In_Rest, + Built_In_Load, + Built_In_Define, + Built_In_Lambda, + Built_In_Eval, + Built_In_Quote, + Built_In_Prog, + Built_In_List, + Built_In_Print, + Built_In_Read, + Built_In_Info, + Built_In_Type, + Built_In_Exit, +} 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_Define: return "define"; + case Built_In_Division: return "/"; + case Built_In_Equal: return "="; + 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_List: return "list"; + case Built_In_Load: return "load"; + case Built_In_Multiplication: return "*"; + 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_Type: return "type"; + } + + return "Built in string missing in Built_In_Name_to_string"; +} + typedef struct { - char* identifier; + Built_In_Name type; } Built_In_Function; struct Ast_Node { @@ -106,11 +175,42 @@ Ast_Node* create_ast_node_keyword(char* keyword) { return node; } -Ast_Node* create_ast_node_built_in_function(char* identifier) { +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_Subtraction; + else if (string_equal(name, "*")) type = Built_In_Multiplication; + else if (string_equal(name, "/")) type = Built_In_Division; + 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, "<")) type = Built_In_Less; + else if (string_equal(name, "<=")) type = Built_In_Less_Equal; + else if (string_equal(name, "if")) type = Built_In_If; + else if (string_equal(name, "and")) type = Built_In_And; + else if (string_equal(name, "or")) type = Built_In_Or; + else if (string_equal(name, "not")) type = Built_In_Not; + else if (string_equal(name, "pair")) type = Built_In_Pair; + else if (string_equal(name, "first")) type = Built_In_First; + else if (string_equal(name, "rest")) type = Built_In_Rest; + else if (string_equal(name, "load")) type = Built_In_Load; + else if (string_equal(name, "define")) type = Built_In_Define; + else if (string_equal(name, "lambda")) type = Built_In_Lambda; + else if (string_equal(name, "eval")) type = Built_In_Eval; + else if (string_equal(name, "quote")) type = Built_In_Quote; + else if (string_equal(name, "prog")) type = Built_In_Prog; + else if (string_equal(name, "list")) type = Built_In_List; + else if (string_equal(name, "print")) type = Built_In_Print; + else if (string_equal(name, "read")) type = Built_In_Read; + else if (string_equal(name, "info")) type = Built_In_Info; + else if (string_equal(name, "type")) type = Built_In_Type; + else if (string_equal(name, "exit")) type = Built_In_Exit; + else return nullptr; + Ast_Node* node = new(Ast_Node); node->type = Ast_Node_Type_Built_In_Function; node->value.built_in_function = new(Built_In_Function); - node->value.built_in_function->identifier = identifier; + node->value.built_in_function->type = type; return node; } diff --git a/src/built_ins.c b/src/built_ins.c index 63ad94e..259eba0 100644 --- a/src/built_ins.c +++ b/src/built_ins.c @@ -6,9 +6,8 @@ bool ast_node_equal(Ast_Node* n1, Ast_Node* n2) { switch (n1->type) { case Ast_Node_Type_Built_In_Function: - return string_equal( - n1->value.built_in_function->identifier, - n2->value.built_in_function->identifier); + 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 diff --git a/src/env.c b/src/env.c index 30a1f68..1cd8794 100644 --- a/src/env.c +++ b/src/env.c @@ -3,6 +3,7 @@ struct Environment { struct Environment* parent; int capacity; int next_index; + // TODO(Felix): Use a hashmap here. char** keys; Ast_Node* values; }; @@ -49,37 +50,10 @@ Ast_Node* lookup_symbol(Symbol* sym, Environment* env) { if (env->parent) return lookup_symbol(sym, env->parent); - char* built_in_names[] = { - // Math stuff - "+", "-", "*", "/", - ">", "<", "=", "<=", ">=", - // Conditional stuff - "if", "and", "or", "not", - // Cons stuff - "pair", "first", "rest", - // rest - "load", "define", - "lambda", "progn", - "eval", "quote", "prog", "list", - "print", "read", - "info", "type", "exit" - }; - int built_in_count = sizeof(built_in_names) / sizeof(char*); - - for (int i = 0; i < built_in_count; ++i) { - if (string_equal(built_in_names[i], sym->identifier)) { - Ast_Node* ret = new(Ast_Node); - ret->type = Ast_Node_Type_Built_In_Function; - ret->value.built_in_function = new(Built_In_Function); - ret->value.built_in_function->identifier = built_in_names[i]; - return ret; - } - } + Ast_Node* built_in = create_ast_node_built_in_function(sym->identifier); + if (built_in) + return built_in; create_error(Error_Type_Symbol_Not_Defined, create_ast_node_nil()); - /* char* message; */ - /* asprintf(&message, "Symbol not defined: %s\n", sym->identifier); */ - /* panic(message); */ - return nullptr; } diff --git a/src/error.c b/src/error.c index fa67ce2..b83e07b 100644 --- a/src/error.c +++ b/src/error.c @@ -1,5 +1,6 @@ typedef enum { Error_Type_Ill_Formed_List, + Error_Type_Ill_Formed_Arguments, Error_Type_Wrong_Number_Of_Arguments, Error_Type_Type_Missmatch, Error_Type_Symbol_Not_Defined, @@ -37,6 +38,7 @@ void create_error(Error_Type type, Ast_Node* location) { char* Error_Type_to_string(Error_Type type) { switch (type) { case Error_Type_Ill_Formed_List: return "Evaluation-error: Ill formed list"; + case Error_Type_Ill_Formed_Arguments: return "Evaluation-error: Ill formed arguments"; case Error_Type_Not_A_Function: return "Evaluation-error: Not a function"; case Error_Type_Symbol_Not_Defined: return "Evaluation-error: Symbol not defined"; case Error_Type_Wrong_Number_Of_Arguments: return "Evaluation-error: Wrong number of arguments"; diff --git a/src/eval.c b/src/eval.c index 4918f8f..7222487 100644 --- a/src/eval.c +++ b/src/eval.c @@ -27,7 +27,7 @@ int list_length(Ast_Node* node) { /** Copies a list, in that it creates a new list, however the items are - the same as in the original (same pointers). This is neede to copy + the same as in the original (same pointers). This is needed to copy a list when evaluating a parameters list, but not wanting to change the parameters list. This happens if you have someting like: @@ -56,27 +56,83 @@ Ast_Node* copy_list(Ast_Node* node) { return result; } -Ast_Node* eval_operands(Ast_Node* operands, Environment* env) { - if (operands->type == Ast_Node_Type_Nil) { - return operands; +typedef struct { + Ast_Node_Array_List* positional_arguments; + // TODO(Felix): Really use hashmap (keyword[sting] -> + // value[Ast_Node*]) here + Ast_Node_Array_List* keyword_keys; + Ast_Node_Array_List* keyword_values; +} Parsed_Arguments; + +Parsed_Arguments* eval_and_parse_arguments(Ast_Node* arguments, Environment* env) { + // TODO(Felix): if arguments is nil then maybe return a constant + // Parsed_Arguments_nil? + + + typedef enum { + Expecting_Anything, + Expecting_Keyword_Key, + Expecting_Keyword_Value + } Parse_State; + + Parse_State parse_state = Expecting_Anything; + + Parsed_Arguments* result = new(Parsed_Arguments); + result->positional_arguments = create_Ast_Node_Array_List(); + result->keyword_keys = create_Ast_Node_Array_List(); + result->keyword_values = create_Ast_Node_Array_List(); + + Ast_Node* current_head = copy_list(arguments); + Ast_Node* current_evaluated_head; + while (current_head->type == Ast_Node_Type_Pair) { + try { + current_evaluated_head = eval_expr(current_head->value.pair->first, env); + } + if (current_evaluated_head->type == Ast_Node_Type_Keyword) { + if (parse_state == Expecting_Anything || Expecting_Keyword_Key) { + append_to_Ast_Node_Array_List(result->keyword_keys, current_evaluated_head); + parse_state = Expecting_Keyword_Value; + } else { + create_error(Error_Type_Ill_Formed_Arguments, arguments); + return nullptr; + } + } else { + if (parse_state == Expecting_Anything) + append_to_Ast_Node_Array_List(result->positional_arguments, current_evaluated_head); + else if (parse_state == Expecting_Keyword_Value) { + append_to_Ast_Node_Array_List(result->keyword_values, current_evaluated_head); + parse_state = Expecting_Keyword_Key; + } + else { + create_error(Error_Type_Ill_Formed_Arguments, arguments); + return nullptr; + } + } + current_head = current_head->value.pair->rest; } - // NOTE(Feilx): Instead of copying and then mutating it, we could + return result; +} + +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; + } + + // NOTE(Felix): Instead of copying and then mutating it, we could // just build the resulting list on the fly - Ast_Node* evaluated_operands = copy_list(operands); - Ast_Node* current_head = evaluated_operands; - /* print(operands); */ - /* printf("\n"); */ - /* print(evaluated_operands); */ - /* printf("\n"); */ + Ast_Node* evaluated_arguments = copy_list(arguments); + Ast_Node* current_head = evaluated_arguments; while (current_head->type == Ast_Node_Type_Pair) { try { current_head->value.pair->first = eval_expr(current_head->value.pair->first, env); } current_head = current_head->value.pair->rest; + ++(*out_arguments_length); } - return evaluated_operands; + return evaluated_arguments; } Ast_Node* eval_expr(Ast_Node* node, Environment* env) { @@ -108,96 +164,107 @@ Ast_Node* eval_expr(Ast_Node* node, Environment* env) { operator = eval_expr(node->value.pair->first, env); } - Ast_Node* operands = node->value.pair->rest; - int operands_length; + Ast_Node* arguments = node->value.pair->rest; + int arguments_length; - // check for built ins functions + // check for special form if (operator->type == Ast_Node_Type_Built_In_Function) { - char* operator_name = operator->value.built_in_function->identifier; - if (string_equal("quote", operator_name)) { - operands_length = list_length(operands); - if (operands_length != 1) { - report_error(Error_Type_Wrong_Number_Of_Arguments); - } - return operands->value.pair->first; - } else if (string_equal("eval", operator_name)) { - try { - operands_length = list_length(operands); - } - if (operands_length != 1) { - report_error(Error_Type_Wrong_Number_Of_Arguments); - } - try { - operands = eval_operands(operands, env); - } - /* print(operands); */ - Ast_Node* result; - try { - result = eval_expr(operands->value.pair->first, env); - } - return result; - } else if (string_equal("prog", operator_name)) { - if (operands->type == Ast_Node_Type_Nil) - return operands; + switch (operator->value.built_in_function->type) { + 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; - operands = copy_list(operands); - Ast_Node* evaluated_operands; - try { - evaluated_operands = eval_operands(operands, env); + if (!result) return create_ast_node_nil(); } - // skip to the last evaluated operand and return it, - // we use eval_operands here instead of doing it - // manually, becuase we want to increase code reuse, - // but at the cost that we have to find the end of the - // list again - while (evaluated_operands->value.pair->rest->type == Ast_Node_Type_Pair) { - evaluated_operands = evaluated_operands->value.pair->rest; + return create_ast_node_number(1); + } + 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_number(1);; } - return evaluated_operands->value.pair->first; - } else if (string_equal("list", operator_name)) { + + return create_ast_node_nil(); + } + case Built_In_Not: { try { - operands = eval_operands(operands, env); + arguments_length = list_length(arguments); } - return operands; - } else if (string_equal("=", operator_name)) { - try { - operands = eval_operands(operands, env); + if (arguments_length != 1) { + report_error(Error_Type_Wrong_Number_Of_Arguments); } - return built_in_equals(operands); - } else if (string_equal("+", operator_name)) { + bool truthy; try { - operands = eval_operands(operands, env); + truthy = is_truthy(arguments->value.pair->first, env); } - return built_in_add(operands); - } else if (string_equal("-", operator_name)) { + if (truthy) + return create_ast_node_nil(); + return create_ast_node_number(1); + } + case Built_In_If: { try { - operands = eval_operands(operands, env); + arguments_length = list_length(arguments); } - return built_in_substract(operands); - } else if (string_equal("*", operator_name)) { - try { - operands = eval_operands(operands, env); + if (arguments_length != 2 && arguments_length != 3) { + report_error(Error_Type_Wrong_Number_Of_Arguments); } - return built_in_multiply(operands); - } else if (string_equal("/", operator_name)) { + + 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 { - operands = eval_operands(operands, env); + 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(Error_Type_Wrong_Number_Of_Arguments); } - return built_in_divide(operands); - } else if (string_equal("define", operator_name)) { + return arguments->value.pair->first; + } + case Built_In_Define: { try { - operands_length = list_length(operands); + arguments_length = list_length(arguments); } - if (operands_length != 2) { + if (arguments_length != 2) { report_error(Error_Type_Wrong_Number_Of_Arguments); } - Ast_Node* symbol = operands->value.pair->first; + Ast_Node* symbol = arguments->value.pair->first; if (symbol->type != Ast_Node_Type_Symbol) report_error(Error_Type_Type_Missmatch); - Ast_Node* value = operands->value.pair->rest->value.pair->first; + Ast_Node* value = arguments->value.pair->rest->value.pair->first; try { value = eval_expr(value, env); } @@ -205,206 +272,151 @@ Ast_Node* eval_expr(Ast_Node* node, Environment* env) { define_symbol(symbol, value, env); return value; - } else if (string_equal("type", operator_name)) { - try { - operands_length = list_length(operands); - } - if (operands_length != 1) { + } + } + + // 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_Pair: { + if (arguments_length != 2) { report_error(Error_Type_Wrong_Number_Of_Arguments); } - try { - operands = eval_operands(operands, env); - } - Ast_Node_Type type = operands->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: 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_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_pair(evaluated_arguments->value.pair->first, evaluated_arguments->value.pair->rest->value.pair->first); + } + case Built_In_First: { + 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); - } else if (string_equal("exit", operator_name)) { - try { - operands_length = list_length(operands); - } - if (operands_length > 1) { + return evaluated_arguments->value.pair->first->value.pair->first; + } + case Built_In_Rest: { + 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); - if (operands_length == 1) { - try { - operands = eval_operands(operands, env); - } - Ast_Node* error_code = operands->value.pair->first; - if (error_code->type != Ast_Node_Type_Number) - report_error(Error_Type_Type_Missmatch); - - exit((int)error_code->value.number->value); + return evaluated_arguments->value.pair->first->value.pair->rest; + } + case Built_In_Eval: { + if (arguments_length != 1) { + report_error(Error_Type_Wrong_Number_Of_Arguments); } - exit(0); - - } else if (string_equal("print", operator_name)) { + Ast_Node* result; try { - operands_length = list_length(operands); + result = eval_expr(evaluated_arguments->value.pair->first, env); } - if (operands_length != 1) { - report_error(Error_Type_Wrong_Number_Of_Arguments); + 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; } - try { - operands = eval_operands(operands, env); + return evaluated_arguments->value.pair->first; + } + case Built_In_List: { + return evaluated_arguments; + } + case Built_In_Print: { + if (arguments_length != 1) { + report_error(Error_Type_Wrong_Number_Of_Arguments); } - print(operands->value.pair->first); + print(evaluated_arguments->value.pair->first); printf("\n"); - return operands->value.pair->first; - } else if (string_equal("read", operator_name)) { - try { - operands_length = list_length(operands); - } - if (operands_length > 1) { + return arguments->value.pair->first; + } + case Built_In_Read: { + if (arguments_length > 1) { report_error(Error_Type_Wrong_Number_Of_Arguments); } - if (operands_length == 1) { - try { - operands = eval_operands(operands, env); - } - Ast_Node* prompt = operands->value.pair->first; + 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(operands->value.pair->first); + print(evaluated_arguments->value.pair->first); } char* line = read_line(); return create_ast_node_string(line, (int)strlen(line)); - } else if (string_equal("pair", operator_name)) { - try { - operands_length = list_length(operands); - } - if (operands_length != 2) { - report_error(Error_Type_Wrong_Number_Of_Arguments); - } - try { - operands = eval_operands(operands, env); - } - - return create_ast_node_pair(operands->value.pair->first, operands->value.pair->rest->value.pair->first); - } else if (string_equal("first", operator_name)) { - try { - operands_length = list_length(operands); - } - if (operands_length != 1) { - report_error(Error_Type_Wrong_Number_Of_Arguments); - } - try { - operands = eval_operands(operands, env); - } - if (operands->value.pair->first->type == Ast_Node_Type_Nil) - return create_ast_node_nil(); - if (operands->value.pair->first->type != Ast_Node_Type_Pair) - report_error(Error_Type_Type_Missmatch); - - return operands->value.pair->first->value.pair->first; - } else if (string_equal("rest", operator_name)) { - try { - operands_length = list_length(operands); - } - if (operands_length != 1) { + } + case Built_In_Type: { + if (arguments_length != 1) { report_error(Error_Type_Wrong_Number_Of_Arguments); } - try { - operands = eval_operands(operands, env); - } - if (operands->value.pair->first->type == Ast_Node_Type_Nil) - return create_ast_node_nil(); - if (operands->value.pair->first->type != Ast_Node_Type_Pair) - report_error(Error_Type_Type_Missmatch); - return operands->value.pair->first->value.pair->rest; - } else if (string_equal("if", operator_name)) { - try { - operands_length = list_length(operands); + 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: 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_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"); } - if (operands_length != 2 && operands_length != 3) { + } + case Built_In_Exit: { + if (arguments_length > 1) { report_error(Error_Type_Wrong_Number_Of_Arguments); } - Ast_Node* condition = operands->value.pair->first; - Ast_Node* then_part = operands->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 (operands_length == 3) - try { - result = eval_expr(else_part->value.pair->first, env); - } - else return create_ast_node_nil(); - return result; - - } else if (string_equal("and", operator_name)) { - bool result = true; - while (operands->type != Ast_Node_Type_Nil) { - if (operands->type != Ast_Node_Type_Pair) { - report_error(Error_Type_Ill_Formed_List); - } - try { - result &= is_truthy(operands->value.pair->first, env); - } - operands = operands->value.pair->rest; - - if (!result) return create_ast_node_nil(); - } - - return create_ast_node_number(1); - - } else if (string_equal("or", operator_name)) { - bool result = false; - while (operands->type != Ast_Node_Type_Nil) { - if (operands->type != Ast_Node_Type_Pair) { - report_error(Error_Type_Ill_Formed_List); - } - try { - result |= is_truthy(operands->value.pair->first, env); - } - operands = operands->value.pair->rest; + 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); - if (result) return create_ast_node_number(1);; + exit((int)error_code->value.number->value); } + exit(0); + } - return create_ast_node_nil(); - } else if (string_equal("not", operator_name)) { - try { - operands_length = list_length(operands); - } - if (operands_length != 1) { - report_error(Error_Type_Wrong_Number_Of_Arguments); - } - bool truthy; - try { - truthy = is_truthy(operands->value.pair->first, env); - } - if (truthy) - return create_ast_node_nil(); - return create_ast_node_number(1); - } else { + default: report_error(Error_Type_Not_Yet_Implemented); } + } - // assume it's lambda function and evaluate the operands in - // place - operands = eval_operands(operands, env); + // assume it's lambda function and evaluate the arguments + arguments = eval_arguments(arguments, env, &arguments_length); } default: diff --git a/src/io.c b/src/io.c index 14672ef..c8c346e 100644 --- a/src/io.c +++ b/src/io.c @@ -71,7 +71,7 @@ void print(Ast_Node* node) { printf("[lambda]"); } break; case (Ast_Node_Type_Built_In_Function): { - printf("[built-in-function %s]", node->value.built_in_function->identifier); + printf("[built-in-function %s]", Built_In_Name_to_string(node->value.built_in_function->type)); } break; case (Ast_Node_Type_Pair): { Ast_Node* head = node; diff --git a/src/testing.c b/src/testing.c index c24d607..fba1fac 100644 --- a/src/testing.c +++ b/src/testing.c @@ -82,7 +82,8 @@ testresult test_eval_operands() { char operands_string[] = "((eval 1) (+ 1 2) \"okay\" (eval :haha))"; Ast_Node* operands = parse_single_expression(operands_string); - operands = eval_operands(operands, create_empty_environment()); + int operands_length; + operands = eval_arguments(operands, create_empty_environment(), &operands_length); assert_no_error(error); assert_equal_int(list_length(operands), 4); diff --git a/todo.org b/todo.org index ec79b8a..ca0cceb 100644 --- a/todo.org +++ b/todo.org @@ -1,4 +1,8 @@ * TODO =assert_equal_type= macro in testing +* DONE use an enum for builtin identifiers + CLOSED: [2018-10-11 Do 17:15] +* TODO dont create new nils or builtins, but store one of each globally +* TODO source code locations for errors * Build-in forms ** DONE +