Ast_Node* apply_arguments_to_function (Ast_Node* arguments, Ast_Node* function) { printf("calling function, yey\n"); return create_ast_node_nil(); } /** 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); return; } } if (arguments->value.pair->first->type != Ast_Node_Type_Symbol) { create_error(Error_Type_Ill_Formed_Lambda_List, arguments); 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); 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); 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); return; } } if (arguments->value.pair->first->type != Ast_Node_Type_Symbol) { create_error(Error_Type_Ill_Formed_Lambda_List, arguments); 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); 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); 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); 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); } } else { printf("this should not happen?"); create_error(Error_Type_Unknown_Error, arguments); } } 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); 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); return 0; } /** Copies a list, in that it creates a new list, however the items are 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: (define a 10) (define condition (quote (= a 10))) (eval condition) > 1.00000 If we wouldn't copy the parameters list, after calling eval would be baked into the quoted list. So even after changing a, the result of (eval condition) would be 1.00000. **/ Ast_Node* copy_list(Ast_Node* node) { // we don't copy immutables in here if (node->type != Ast_Node_Type_Pair) { return node; } Ast_Node* result = new(Ast_Node); result->type = Ast_Node_Type_Pair; result->value.pair = new(Pair); result->value.pair->first = copy_list(node->value.pair->first); result->value.pair->rest = copy_list(node->value.pair->rest); return result; } Ast_Node* eval_expr(Ast_Node* node, Environment* env); bool is_truthy (Ast_Node* expression, Environment* env); 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(16); result->keyword_keys = create_Ast_Node_Array_List(16); result->keyword_values = create_Ast_Node_Array_List(16); 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; } return result; } 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; } // NOTE(Felix): Instead of copying and then mutating it, we could // just build the resulting list on the fly 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_arguments; } Ast_Node* eval_expr(Ast_Node* node, Environment* env) { #define report_error(_type) { \ create_error(_type, node); \ return nullptr; \ } if (error) return nullptr; Ast_Node* ret = new(Ast_Node); switch (node->type) { case Ast_Node_Type_Nil: ret->type = Ast_Node_Type_Nil; return ret; case Ast_Node_Type_Symbol: Ast_Node* symbol; try { symbol = lookup_symbol(node->value.symbol, 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; try { operator = eval_expr(node->value.pair->first, env); } 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_Lambda: { /* * (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 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); } } 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; } // 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_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_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 create_ast_node_nil(); } case Built_In_Not: { 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_number(1); } case Built_In_If: { 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; } case Built_In_Quote: { arguments_length = list_length(arguments); if (arguments_length != 1) { report_error(Error_Type_Wrong_Number_Of_Arguments); } return arguments->value.pair->first; } case Built_In_Define: { 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_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; } } // 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; Parsed_Arguments* evaluated_and_parsed_arguments; try { evaluated_arguments = eval_arguments(arguments, env, &arguments_length); evaluated_and_parsed_arguments = eval_and_parse_arguments(arguments, env); } switch (operator->value.built_in_function->type) { case Built_In_Addition: { return built_in_add(evaluated_and_parsed_arguments); } case Built_In_Subtraction: { return built_in_substract(evaluated_and_parsed_arguments); } case Built_In_Multiplication: { return built_in_multiply(evaluated_and_parsed_arguments); } case Built_In_Division: { return built_in_divide(evaluated_and_parsed_arguments); } case Built_In_Equal: { return built_in_equals(evaluated_and_parsed_arguments); } case Built_In_Pair: { if (arguments_length != 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); } 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); 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); 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); } 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_Print: { /* if (arguments_length != 1) { */ /* report_error(Error_Type_Wrong_Number_Of_Arguments); */ /* } */ /* print(evaluated_arguments->value.pair->first); */ /* printf("\n"); */ /* return arguments->value.pair->first; */ if (evaluated_and_parsed_arguments->positional_arguments->next_index == 0) { report_error(Error_Type_Wrong_Number_Of_Arguments); } char* sep = " "; char* end = "\n"; Ast_Node* temp = extract_keyword_value("sep", evaluated_and_parsed_arguments); if (temp) { try { assert_type(temp, Ast_Node_Type_String); } sep = temp->value.string->value; } temp = extract_keyword_value("end", evaluated_and_parsed_arguments); if (temp) { try { assert_type(temp, Ast_Node_Type_String); } end = temp->value.string->value; } print(*evaluated_and_parsed_arguments->positional_arguments->data); for (int i = 1; i < evaluated_and_parsed_arguments->positional_arguments->next_index; ++i) { printf("%s", sep); print(*(evaluated_and_parsed_arguments->positional_arguments->data+i)); } printf("%s", end); // return last positional argument return *(evaluated_and_parsed_arguments->positional_arguments->data+ (evaluated_and_parsed_arguments->positional_arguments->next_index-1)); } case Built_In_Read: { 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)); } case Built_In_Type: { 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_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"); } } case Built_In_Exit: { 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); } default: report_error(Error_Type_Not_Yet_Implemented); } } // assume it's lambda function and evaluate the arguments arguments = eval_arguments(arguments, env, &arguments_length); if (operator->type == Ast_Node_Type_Function) { Ast_Node* result; try { result = apply_arguments_to_function(arguments, operator); } return result; } } default: 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; }