From df3661d77e2d71e2bddb6f51add53593b1f79c25 Mon Sep 17 00:00:00 2001 From: Felix Brendel Date: Wed, 17 Jul 2019 12:13:03 +0200 Subject: [PATCH] fixed keyword args when appling to a function --- bin/test.slime | 3 + build.bat | 3 +- src/eval.cpp | 284 ++++++++++++++++++++++++++--------------------- src/platform.cpp | 4 +- 4 files changed, 164 insertions(+), 130 deletions(-) diff --git a/bin/test.slime b/bin/test.slime index f338eb0..9e9956c 100644 --- a/bin/test.slime +++ b/bin/test.slime @@ -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)) diff --git a/build.bat b/build.bat index 0793b25..076e90f 100644 --- a/build.bat +++ b/build.bat @@ -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 ( diff --git a/src/eval.cpp b/src/eval.cpp index e19fb5e..fa5c053 100644 --- a/src/eval.cpp +++ b/src/eval.cpp @@ -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; + + 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) { - 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; - } - } - 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 // 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) { - 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 { - // 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; 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 diff --git a/src/platform.cpp b/src/platform.cpp index 1b01039..70b7da5 100644 --- a/src/platform.cpp +++ b/src/platform.cpp @@ -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