|
- 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;
-
- }
|