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))
(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
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
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);
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
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);
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;
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?
// 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
// 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;
};
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)
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) {
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
// 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));
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;
}
}
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(
"There was no value supplied for the required "
"keyword argument ':%s'.",
&defined_keyword->data);
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);
"A rest argument was not declared "
"but the function was called with additional arguments.");
return;
}
}
}
};
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);
} 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;
}
}
try read_poitional_args();
try read_keyword_args();
try check_keyword_args();
try read_rest_arg();
Lisp_Object* result;
try result = eval_expr(function->body, new_env);
return result;
}
/**
This parses the argument specification of funcitons into their
Function struct. It does this by allocating new
+2 -2
View File
@@ -51,8 +51,8 @@ proc get_exe_dir() -> char* {
}
if (!path) {
fprintf(stderr, "Failure: %d\n", last_error);
return "";
fprintf(stderr, "Failure: %ld\n", last_error);
return nullptr;
}
else {
// remove the exe name, so we are only left with the path