diff --git a/bin/tests/evaluation_of_default_args.slime b/bin/tests/evaluation_of_default_args.slime new file mode 100644 index 0000000..aae5501 --- /dev/null +++ b/bin/tests/evaluation_of_default_args.slime @@ -0,0 +1,7 @@ +((lambda (:keys k1 :defaults-to (+ 1 2 3)) + (assert (= k1 6)))) + +((lambda (:keys k1 :defaults-to ()) + (when k1 + (assert ())) + (assert (= k1 ())))) diff --git a/manual/manual.org b/manual/manual.org index 98771c0..d0216d4 100644 --- a/manual/manual.org +++ b/manual/manual.org @@ -198,6 +198,7 @@ The programmer can also define their own special forms using =special-lambda= an be explained later. * Symbols and keywords +* Truthyness * Lambdas Slime allows for creating anonymous functions called *lambdas*. We did not talk about binding variables, we will do this in [[Define]], but we can still use lambdas now. Remember that Lisp @@ -327,18 +328,19 @@ call to that function will look like. ** Functions with keyword arguments A sometimes more convenient way of passing arguments to a function is using keyword arguments. Using -keyword arguments a function call could look like this: \[\texttt{(function :arg1 value1 :arg2 -value2)}\] here the function accepts two arguments named =arg1= and =arg2=. The user of this +keyword arguments a function call could look like this: \[\texttt{(function :arg1 value1 :arg2 +value2)}\] here the function accepts two arguments named =arg1= and =arg2=. The user of this function can see more clearly excatly which argument will be assigned wich value. This notation also -allows for switching the argument order. The following function call is equivalent to the call +allows for switching the argument order. The following function call is equivalent to the call above. \[\texttt{(function :arg2 value2 :arg1 value1)}\]. For this to work however, the function must be defined to accept these keyword arguments. To do this the special marker =:keys= has to be inserted into the argument list of a =lambda= or a function -=define=. All following arguments *must* be supplied as keyword arguments, /unless/ they are also -supplied with a default value, in which case they do not need to be supplied. To attach a default -value to a keyword argument, insert =:defaults-to = after the keyword argument name. An -example of all of this can be seen in [[code:keyword-args]]. +=define=. All following arguments *must* be supplied as keyword arguments, /unless/ they are also +supplied with a default value, in which case they do not need to be supplied. To attach a default +value to a keyword argument, insert =:defaults-to = after the keyword argument name. An +example of all of this can be seen in [[code:keyword-args]]. Important note: keyword arguments must be +defined and supplied after all the regular arguments. {{{slime_header}}} #+name: code:keyword-args @@ -361,10 +363,41 @@ example of all of this can be seen in [[code:keyword-args]]. : 54 : 54 +** Functions with rest arguments +If the programmer wants to create a function that can accept any number of arguments, they can use +the =rest= argument. It is defined after the special marker =:rest= and after the rest argument, no +other arguments can be defined. In the execution of the fuction, the rest arguent will be assigned +to a list containing all the supplied values. The rest argument can be used in conjunction with the +other argument types, regular arguments and keyword arguments. +{{{slime_header}}} +#+name: code:rest-args +#+caption: A more complex functoin definition using keyword arguments +#+begin_src slime + (define (execute-operation operation + :keys + do-logging :defaults-to () + :rest values) + (define result (apply operation values)) + (when do-logging + (printf "Executing operation" + operation + "agains values yielded:" + result)) + result) + + (printf (execute-operation '+ 1 2 3)) + (printf (execute-operation '* + :do-logging t + 10 11)) +#+end_src +#+RESULTS: code:rest-args +: evaluates to => +: 6 +: Executing operation [C-function] agains values yielded: 110 +: 110 -** Functions with rest arguments * Environments * Built-in functions diff --git a/src/error.cpp b/src/error.cpp index 2fef0ba..df7155c 100644 --- a/src/error.cpp +++ b/src/error.cpp @@ -14,7 +14,7 @@ proc create_error(const char* c_file_name, int c_file_line, Lisp_Object* type, S delete_error(); debug_break(); - visualize_lisp_machine(); + // visualize_lisp_machine(); using Globals::error; error = new(Error); diff --git a/src/eval.cpp b/src/eval.cpp index 6cb3252..a7cbb83 100644 --- a/src/eval.cpp +++ b/src/eval.cpp @@ -24,11 +24,11 @@ proc apply_arguments_to_function(Lisp_Object* arguments, Function* function) -> 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; @@ -80,8 +80,9 @@ proc apply_arguments_to_function(Lisp_Object* arguments, Function* function) -> } // 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), - define_symbol(sym, arguments->value.pair.rest->value.pair.first, new_env); + 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, arguments->value.pair.rest->value.pair.first, new_env); append_to_array_list(read_in_keywords, arguments->value.pair.first->value.symbol.identifier); @@ -257,9 +258,11 @@ proc parse_argument_list(Lisp_Object* arguments, Function* function) -> void { // would be an error next = next->value.pair.rest; if (Memory::get_type(next) == Lisp_Object_Type::Pair) { + Lisp_Object* ret; + try_void ret = eval_expr(next->value.pair.first, function->parent_environment); append_to_keyword_argument_list(function->keyword_arguments, arguments->value.pair.first->value.symbol.identifier, - next->value.pair.first); + ret); arguments = next->value.pair.rest; } else { create_parsing_error("Expecting a value after 'defaults-to'"); @@ -426,6 +429,7 @@ proc eval_expr(Lisp_Object* node, Environment* env) -> Lisp_Object* { proc is_truthy(Lisp_Object* expression, Environment* env) -> bool { Lisp_Object* result; try result = eval_expr(expression, env); + return result != Memory::nil; } diff --git a/src/memory.cpp b/src/memory.cpp index cd4dbfd..4610f5d 100644 --- a/src/memory.cpp +++ b/src/memory.cpp @@ -283,6 +283,9 @@ namespace Memory { } proc copy_lisp_object(Lisp_Object* n) -> Lisp_Object* { + if (n == nil || n == t) + return n; + Lisp_Object* target; try target = create_lisp_object(); *target = *n; diff --git a/src/testing.cpp b/src/testing.cpp index eaf1550..a2e4085 100644 --- a/src/testing.cpp +++ b/src/testing.cpp @@ -635,6 +635,7 @@ proc run_all_tests() -> bool { printf("\n-- Test Files --\n"); + invoke_test_script("evaluation_of_default_args"); invoke_test_script("lexical_scope"); invoke_test_script("class_macro"); invoke_test_script("sicp");