fixed keyword args when appling to a function

This commit is contained in:
Felix Brendel
2019-07-17 12:13:03 +02:00
parent 7d282238bf
commit df3661d77e
4 changed files with 170 additions and 136 deletions
+3
View File
@@ -15,3 +15,6 @@
(printf "addr-of test:" (addr-of test)) (printf "addr-of test:" (addr-of test))
(test :ij :yes) (test :ij :yes)
(define (tt :keys a b :rest r)
(printf a b r))
+2 -1
View File
@@ -9,7 +9,8 @@ pushd bin
taskkill /F /IM %exeName% > NUL 2> NUL taskkill /F /IM %exeName% > NUL 2> NUL
echo ---------- Compiling ---------- echo ---------- Compiling ----------
call ..\timecmd cl ../src/main.cpp /std:c++latest /Fe%exeName% /W3 /Zi /nologo /EHsc /link /NODEFAULTLIB:libucrt libucrtd.lib rem call ..\timecmd cl ../src/main.cpp /std:c++latest /Fe%exeName% /W3 /Zi /nologo /EHsc /link /NODEFAULTLIB:libucrt libucrtd.lib
call ..\timecmd clang++ ../src/main.cpp -o %exeName% -O3 -std=c++17
popd popd
if %errorlevel% == 0 ( if %errorlevel% == 0 (
+163 -133
View File
@@ -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;
// positional arguments proc read_poitional_args = [&]() -> void {
for (int i = 0; i < function->positional_arguments->next_index; ++i) { for (int i = 0; i < function->positional_arguments->next_index; ++i) {
if (Memory::get_type(arguments) != Lisp_Object_Type::Pair) { if (Memory::get_type(arguments) != Lisp_Object_Type::Pair) {
create_wrong_number_of_arguments_error(function->positional_arguments->next_index, i); create_wrong_number_of_arguments_error(function->positional_arguments->next_index, i);
return nullptr; 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?
// 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);
arguments = arguments->value.pair.rest;
}
String_Array_List* read_in_keywords = create_String_array_list();
if (arguments == Memory::nil)
goto checks;
// 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;
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;
} }
// 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_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;
} }
if (!accepted) { };
// TODO(Felix): if we are actually done with all the
// necessary keywords then we have to count the rest proc read_keyword_args = [&]() -> void {
// as :rest here, instead od always creating an error // keyword arguments: use all given ones and keep track of the
// (special case with default variables) // added ones (array list), if end of parameters in encountered or
create_generic_error( // something that is not a keyword is encountered or a keyword
"The function does not take the keyword argument ':%s'", // that is not recognized is encoutered, jump out of the loop.
&(arguments->value.pair.first->value.symbol.identifier)); read_in_keywords = create_String_array_list();
return nullptr;
if (arguments == Memory::nil)
return;
// find out how many keyword args we /have/ to read
for (int i = 0; i < function->keyword_arguments->next_index; ++i) {
if (function->keyword_arguments->values->data[i] == nullptr)
++obligatory_keywords_count;
else
break;
} }
// check if it was already read in
for (int i = 0; i < read_in_keywords->next_index; ++i) { while (Memory::get_type(arguments->value.pair.first) == Lisp_Object_Type::Keyword) {
if (string_equal( // check if this one is even an accepted keyword
arguments->value.pair.first->value.symbol.identifier, bool accepted = false;
read_in_keywords->data[i])) for (int i = 0; i < function->keyword_arguments->next_index; ++i) {
{ if (string_equal(
// TODO(Felix): if we are actually done with all the 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'", "The function does not take the keyword argument ':%s'\n"
&(arguments->value.pair.first->value.symbol.identifier)); "and not all required keyword arguments have been read\n"
return nullptr; "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 // check if it was already read in
// not already read in, is there a next element to actually for (int i = 0; i < read_in_keywords->next_index; ++i) {
// set it to? if (string_equal(
if (Memory::get_type(arguments->value.pair.rest) != Lisp_Object_Type::Pair) { arguments->value.pair.first->value.symbol.identifier,
create_generic_error( read_in_keywords->data[i]))
"Attempting to set the keyword argument ':%s', but no value was supplied.", {
&(arguments->value.pair.first->value.symbol.identifier)); // NOTE(Felix): if we are actually done with all the
return nullptr; // 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 // okay so we found a keyword that has to be read in and was
try sym = Memory::get_or_create_lisp_object_symbol(arguments->value.pair.first->value.symbol.identifier); // not already read in, is there a next element to actually
// NOTE(Felix): It seems we do not need to evaluate the argument here... // set it to?
try define_symbol( if (Memory::get_type(arguments->value.pair.rest) != Lisp_Object_Type::Pair) {
sym, create_generic_error(
Memory::copy_lisp_object_except_pairs(arguments->value.pair.rest->value.pair.first), "Attempting to set the keyword argument ':%s', but no value was supplied.",
new_env); &(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 append_to_array_list(read_in_keywords, arguments->value.pair.first->value.symbol.identifier);
arguments = arguments->value.pair.rest->value.pair.rest; ++read_obligatory_keywords_count;
if (arguments == Memory::nil) { // overstep both for next one
break; arguments = arguments->value.pair.rest->value.pair.rest;
}
}
checks: if (arguments == Memory::nil) {
// 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; break;
} }
} }
if (function->keyword_arguments->values->data[i] == nullptr) { };
// if this one does not have a default value
if (!was_set) { 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;
}
}
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);
}
}
}
};
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 {
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( create_generic_error(
"There was no value supplied for the required " "A rest argument was not declared "
"keyword argument ':%s'.", "but the function was called with additional arguments.");
&defined_keyword->data); return;
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) {
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);
} }
} }
} };
try read_poitional_args();
if (arguments == Memory::nil) { try read_keyword_args();
if (function->rest_argument) { try check_keyword_args();
try sym = Memory::get_or_create_lisp_object_symbol(function->rest_argument); try read_rest_arg();
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);
} 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;
}
}
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
+2 -2
View File
@@ -51,8 +51,8 @@ proc get_exe_dir() -> char* {
} }
if (!path) { if (!path) {
fprintf(stderr, "Failure: %d\n", last_error); fprintf(stderr, "Failure: %ld\n", last_error);
return ""; return nullptr;
} }
else { else {
// remove the exe name, so we are only left with the path // remove the exe name, so we are only left with the path