|
|
@@ -3,168 +3,198 @@ proc apply_arguments_to_function(Lisp_Object* arguments, Function* function) -> |
|
|
try new_env = Memory::create_child_environment(function->parent_environment); |
|
|
try new_env = Memory::create_child_environment(function->parent_environment); |
|
|
|
|
|
|
|
|
Lisp_Object* sym, *val; // used as temp storage to use `try` |
|
|
Lisp_Object* sym, *val; // used as temp storage to use `try` |
|
|
|
|
|
String_Array_List* read_in_keywords; |
|
|
|
|
|
int obligatory_keywords_count = 0; |
|
|
|
|
|
int read_obligatory_keywords_count = 0; |
|
|
|
|
|
|
|
|
|
|
|
proc read_poitional_args = [&]() -> void { |
|
|
|
|
|
for (int i = 0; i < function->positional_arguments->next_index; ++i) { |
|
|
|
|
|
if (Memory::get_type(arguments) != Lisp_Object_Type::Pair) { |
|
|
|
|
|
create_wrong_number_of_arguments_error(function->positional_arguments->next_index, i); |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
// TODO(Felix): here we create new lisp_object_symbols from |
|
|
|
|
|
// their identifiers but before we converted them to |
|
|
|
|
|
// strings from symbols... Wo maybe just use the symbols? |
|
|
|
|
|
|
|
|
// positional arguments |
|
|
|
|
|
for (int i = 0; i < function->positional_arguments->next_index; ++i) { |
|
|
|
|
|
if (Memory::get_type(arguments) != Lisp_Object_Type::Pair) { |
|
|
|
|
|
create_wrong_number_of_arguments_error(function->positional_arguments->next_index, i); |
|
|
|
|
|
return nullptr; |
|
|
|
|
|
} |
|
|
|
|
|
// TODO(Felix): here we create new lisp_object_symbols from |
|
|
|
|
|
// their identifiers but before we converted them to |
|
|
|
|
|
// strings from symbols... Wo maybe just use the symbols? |
|
|
|
|
|
|
|
|
|
|
|
// NOTE(Felix): We have to copy all the arguments, otherwise |
|
|
|
|
|
// we change the program code. |
|
|
|
|
|
try sym = Memory::get_or_create_lisp_object_symbol(function->positional_arguments->identifiers[i]); |
|
|
|
|
|
define_symbol( |
|
|
|
|
|
sym, |
|
|
|
|
|
Memory::copy_lisp_object_except_pairs(arguments->value.pair.first), |
|
|
|
|
|
new_env); |
|
|
|
|
|
|
|
|
// NOTE(Felix): We have to copy all the arguments, otherwise |
|
|
|
|
|
// we change the program code. |
|
|
|
|
|
try_void sym = Memory::get_or_create_lisp_object_symbol(function->positional_arguments->identifiers[i]); |
|
|
|
|
|
define_symbol( |
|
|
|
|
|
sym, |
|
|
|
|
|
Memory::copy_lisp_object_except_pairs(arguments->value.pair.first), |
|
|
|
|
|
new_env); |
|
|
|
|
|
|
|
|
arguments = arguments->value.pair.rest; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
arguments = arguments->value.pair.rest; |
|
|
|
|
|
} |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
String_Array_List* read_in_keywords = create_String_array_list(); |
|
|
|
|
|
|
|
|
proc read_keyword_args = [&]() -> void { |
|
|
|
|
|
// 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. |
|
|
|
|
|
read_in_keywords = create_String_array_list(); |
|
|
|
|
|
|
|
|
if (arguments == Memory::nil) |
|
|
|
|
|
goto checks; |
|
|
|
|
|
|
|
|
if (arguments == Memory::nil) |
|
|
|
|
|
return; |
|
|
|
|
|
|
|
|
// 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 (Memory::get_type(arguments->value.pair.first) == Lisp_Object_Type::Keyword) { |
|
|
|
|
|
// check if this one is even an accepted keyword |
|
|
|
|
|
bool accepted = false; |
|
|
|
|
|
|
|
|
// find out how many keyword args we /have/ to read |
|
|
for (int i = 0; i < function->keyword_arguments->next_index; ++i) { |
|
|
for (int i = 0; i < function->keyword_arguments->next_index; ++i) { |
|
|
if (string_equal( |
|
|
|
|
|
arguments->value.pair.first->value.symbol.identifier, |
|
|
|
|
|
function->keyword_arguments->identifiers[i])) |
|
|
|
|
|
{ |
|
|
|
|
|
accepted = true; |
|
|
|
|
|
|
|
|
if (function->keyword_arguments->values->data[i] == nullptr) |
|
|
|
|
|
++obligatory_keywords_count; |
|
|
|
|
|
else |
|
|
break; |
|
|
break; |
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
if (!accepted) { |
|
|
|
|
|
// 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_generic_error( |
|
|
|
|
|
"The function does not take the keyword argument ':%s'", |
|
|
|
|
|
&(arguments->value.pair.first->value.symbol.identifier)); |
|
|
|
|
|
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.symbol.identifier, |
|
|
|
|
|
read_in_keywords->data[i])) |
|
|
|
|
|
{ |
|
|
|
|
|
// TODO(Felix): if we are actually done with all the |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
while (Memory::get_type(arguments->value.pair.first) == Lisp_Object_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.symbol.identifier, |
|
|
|
|
|
function->keyword_arguments->identifiers[i])) |
|
|
|
|
|
{ |
|
|
|
|
|
accepted = true; |
|
|
|
|
|
break; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
if (!accepted) { |
|
|
|
|
|
// NOTE(Felix): if we are actually done with all the |
|
|
// necessary keywords then we have to count the rest |
|
|
// necessary keywords then we have to count the rest |
|
|
// as :rest here, instead od always creating an error |
|
|
// as :rest here, instead od always creating an error |
|
|
// (special case with default variables) |
|
|
// (special case with default variables) |
|
|
|
|
|
if (read_obligatory_keywords_count == obligatory_keywords_count) |
|
|
|
|
|
return; |
|
|
create_generic_error( |
|
|
create_generic_error( |
|
|
"The function already read the keyword argument ':%s'", |
|
|
|
|
|
&(arguments->value.pair.first->value.symbol.identifier)); |
|
|
|
|
|
return nullptr; |
|
|
|
|
|
|
|
|
"The function does not take the keyword argument ':%s'\n" |
|
|
|
|
|
"and not all required keyword arguments have been read\n" |
|
|
|
|
|
"in to potentially count it as the rest argument.", |
|
|
|
|
|
&(arguments->value.pair.first->value.symbol.identifier->data)); |
|
|
|
|
|
return; |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 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 (Memory::get_type(arguments->value.pair.rest) != Lisp_Object_Type::Pair) { |
|
|
|
|
|
create_generic_error( |
|
|
|
|
|
"Attempting to set the keyword argument ':%s', but no value was supplied.", |
|
|
|
|
|
&(arguments->value.pair.first->value.symbol.identifier)); |
|
|
|
|
|
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.symbol.identifier, |
|
|
|
|
|
read_in_keywords->data[i])) |
|
|
|
|
|
{ |
|
|
|
|
|
// NOTE(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) |
|
|
|
|
|
if (read_obligatory_keywords_count == obligatory_keywords_count) |
|
|
|
|
|
return; |
|
|
|
|
|
create_generic_error( |
|
|
|
|
|
"The function already read the keyword argument ':%s'", |
|
|
|
|
|
&(arguments->value.pair.first->value.symbol.identifier->data)); |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
// if not set it and then add it to the array list |
|
|
|
|
|
try sym = Memory::get_or_create_lisp_object_symbol(arguments->value.pair.first->value.symbol.identifier); |
|
|
|
|
|
// NOTE(Felix): It seems we do not need to evaluate the argument here... |
|
|
|
|
|
try define_symbol( |
|
|
|
|
|
sym, |
|
|
|
|
|
Memory::copy_lisp_object_except_pairs(arguments->value.pair.rest->value.pair.first), |
|
|
|
|
|
new_env); |
|
|
|
|
|
|
|
|
// 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 (Memory::get_type(arguments->value.pair.rest) != Lisp_Object_Type::Pair) { |
|
|
|
|
|
create_generic_error( |
|
|
|
|
|
"Attempting to set the keyword argument ':%s', but no value was supplied.", |
|
|
|
|
|
&(arguments->value.pair.first->value.symbol.identifier->data)); |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
append_to_array_list(read_in_keywords, arguments->value.pair.first->value.symbol.identifier); |
|
|
|
|
|
|
|
|
// if not set it and then add it to the array list |
|
|
|
|
|
try_void sym = Memory::get_or_create_lisp_object_symbol(arguments->value.pair.first->value.symbol.identifier); |
|
|
|
|
|
// NOTE(Felix): It seems we do not need to evaluate the argument here... |
|
|
|
|
|
try_void define_symbol( |
|
|
|
|
|
sym, |
|
|
|
|
|
Memory::copy_lisp_object_except_pairs(arguments->value.pair.rest->value.pair.first), |
|
|
|
|
|
new_env); |
|
|
|
|
|
|
|
|
// overstep both for next one |
|
|
|
|
|
arguments = arguments->value.pair.rest->value.pair.rest; |
|
|
|
|
|
|
|
|
append_to_array_list(read_in_keywords, arguments->value.pair.first->value.symbol.identifier); |
|
|
|
|
|
++read_obligatory_keywords_count; |
|
|
|
|
|
|
|
|
if (arguments == Memory::nil) { |
|
|
|
|
|
break; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
// overstep both for next one |
|
|
|
|
|
arguments = arguments->value.pair.rest->value.pair.rest; |
|
|
|
|
|
|
|
|
checks: |
|
|
|
|
|
// check if all necessary keywords have been read in |
|
|
|
|
|
for (int i = 0; i < function->keyword_arguments->next_index; ++i) { |
|
|
|
|
|
String* 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; |
|
|
|
|
|
|
|
|
if (arguments == Memory::nil) { |
|
|
break; |
|
|
break; |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
if (function->keyword_arguments->values->data[i] == nullptr) { |
|
|
|
|
|
// if this one does not have a default value |
|
|
|
|
|
if (!was_set) { |
|
|
|
|
|
create_generic_error( |
|
|
|
|
|
"There was no value supplied for the required " |
|
|
|
|
|
"keyword argument ':%s'.", |
|
|
|
|
|
&defined_keyword->data); |
|
|
|
|
|
return nullptr; |
|
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
proc check_keyword_args = [&]() -> void { |
|
|
|
|
|
// check if all necessary keywords have been read in |
|
|
|
|
|
for (int i = 0; i < function->keyword_arguments->next_index; ++i) { |
|
|
|
|
|
String* 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; |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
} 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) { |
|
|
|
|
|
try sym = Memory::get_or_create_lisp_object_symbol(defined_keyword); |
|
|
|
|
|
try val = Memory::copy_lisp_object_except_pairs(function->keyword_arguments->values->data[i]); |
|
|
|
|
|
define_symbol(sym, val, new_env); |
|
|
|
|
|
|
|
|
if (function->keyword_arguments->values->data[i] == nullptr) { |
|
|
|
|
|
// if this one does not have a default value |
|
|
|
|
|
if (!was_set) { |
|
|
|
|
|
create_generic_error( |
|
|
|
|
|
"There was no value supplied for the required " |
|
|
|
|
|
"keyword argument ':%s'.", |
|
|
|
|
|
&defined_keyword->data); |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
} 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) { |
|
|
|
|
|
try_void sym = Memory::get_or_create_lisp_object_symbol(defined_keyword); |
|
|
|
|
|
try_void val = Memory::copy_lisp_object_except_pairs(function->keyword_arguments->values->data[i]); |
|
|
|
|
|
define_symbol(sym, val, new_env); |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
if (arguments == Memory::nil) { |
|
|
|
|
|
if (function->rest_argument) { |
|
|
|
|
|
try sym = Memory::get_or_create_lisp_object_symbol(function->rest_argument); |
|
|
|
|
|
define_symbol(sym, Memory::nil, new_env); |
|
|
|
|
|
} |
|
|
|
|
|
} else { |
|
|
|
|
|
if (function->rest_argument) { |
|
|
|
|
|
try sym = Memory::get_or_create_lisp_object_symbol(function->rest_argument); |
|
|
|
|
|
define_symbol( |
|
|
|
|
|
sym, |
|
|
|
|
|
// NOTE(Felix): arguments will be a list, and I THINK |
|
|
|
|
|
// we do not need to copy it... |
|
|
|
|
|
arguments, |
|
|
|
|
|
new_env); |
|
|
|
|
|
|
|
|
proc read_rest_arg = [&]() -> void { |
|
|
|
|
|
if (arguments == Memory::nil) { |
|
|
|
|
|
if (function->rest_argument) { |
|
|
|
|
|
try_void sym = Memory::get_or_create_lisp_object_symbol(function->rest_argument); |
|
|
|
|
|
define_symbol(sym, Memory::nil, new_env); |
|
|
|
|
|
} |
|
|
} else { |
|
|
} else { |
|
|
// rest was not declared but additional arguments were found |
|
|
|
|
|
create_generic_error( |
|
|
|
|
|
"A rest argument was not declared " |
|
|
|
|
|
"but the function was called with additional arguments."); |
|
|
|
|
|
return nullptr; |
|
|
|
|
|
|
|
|
if (function->rest_argument) { |
|
|
|
|
|
try_void sym = Memory::get_or_create_lisp_object_symbol(function->rest_argument); |
|
|
|
|
|
define_symbol( |
|
|
|
|
|
sym, |
|
|
|
|
|
// NOTE(Felix): arguments will be a list, and I THINK |
|
|
|
|
|
// we do not need to copy it... |
|
|
|
|
|
arguments, |
|
|
|
|
|
new_env); |
|
|
|
|
|
} else { |
|
|
|
|
|
// rest was not declared but additional arguments were found |
|
|
|
|
|
create_generic_error( |
|
|
|
|
|
"A rest argument was not declared " |
|
|
|
|
|
"but the function was called with additional arguments."); |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
try read_poitional_args(); |
|
|
|
|
|
try read_keyword_args(); |
|
|
|
|
|
try check_keyword_args(); |
|
|
|
|
|
try read_rest_arg(); |
|
|
|
|
|
|
|
|
Lisp_Object* result; |
|
|
Lisp_Object* result; |
|
|
try result = eval_expr(function->body, new_env); |
|
|
try result = eval_expr(function->body, new_env); |
|
|
return result; |
|
|
return result; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
This parses the argument specification of funcitons into their |
|
|
This parses the argument specification of funcitons into their |
|
|
Function struct. It does this by allocating new |
|
|
Function struct. It does this by allocating new |
|
|
|