Fixed an error where a nil value could not be used as the default value as a keyword argument since coly_lisp_object would be invoked before assigning nil as the default argument, producing a separate nil value, which then fails the nil tests becase it lives in a different location as Memory::nil.master
| @@ -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 ())))) | |||||
| @@ -198,6 +198,7 @@ The programmer can also define their own special forms using =special-lambda= an | |||||
| be explained later. | be explained later. | ||||
| * Symbols and keywords | * Symbols and keywords | ||||
| * Truthyness | |||||
| * Lambdas | * Lambdas | ||||
| Slime allows for creating anonymous functions called *lambdas*. We did not talk about binding | 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 | 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 | ** Functions with keyword arguments | ||||
| A sometimes more convenient way of passing arguments to a function is using keyword arguments. Using | 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 | 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)}\]. | 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 | 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 | 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 <value>= 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 <value>= 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}}} | {{{slime_header}}} | ||||
| #+name: code:keyword-args | #+name: code:keyword-args | ||||
| @@ -361,10 +363,41 @@ example of all of this can be seen in [[code:keyword-args]]. | |||||
| : 54 | : 54 | ||||
| : 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 | * Environments | ||||
| * Built-in functions | * Built-in functions | ||||
| @@ -14,7 +14,7 @@ proc create_error(const char* c_file_name, int c_file_line, Lisp_Object* type, S | |||||
| delete_error(); | delete_error(); | ||||
| debug_break(); | debug_break(); | ||||
| visualize_lisp_machine(); | |||||
| // visualize_lisp_machine(); | |||||
| using Globals::error; | using Globals::error; | ||||
| error = new(Error); | error = new(Error); | ||||
| @@ -24,11 +24,11 @@ proc apply_arguments_to_function(Lisp_Object* arguments, Function* function) -> | |||||
| if (arguments == Memory::nil) | if (arguments == Memory::nil) | ||||
| goto checks; | goto checks; | ||||
| // keyword arguments: use all given ones and keep track of the | // keyword arguments: use all given ones and keep track of the | ||||
| // added ones (array list), if end of parameters in encountered or | // added ones (array list), if end of parameters in encountered or | ||||
| // something that is not a keyword is encountered or a keyword | // something that is not a keyword is encountered or a keyword | ||||
| // that is not recognized is encoutered, jump out of the loop. | // that is not recognized is encoutered, jump out of the loop. | ||||
| while (Memory::get_type(arguments->value.pair.first) == Lisp_Object_Type::Keyword) { | while (Memory::get_type(arguments->value.pair.first) == Lisp_Object_Type::Keyword) { | ||||
| // check if this one is even an accepted keyword | // check if this one is even an accepted keyword | ||||
| bool accepted = false; | 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 | // 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); | 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 | // would be an error | ||||
| next = next->value.pair.rest; | next = next->value.pair.rest; | ||||
| if (Memory::get_type(next) == Lisp_Object_Type::Pair) { | 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, | append_to_keyword_argument_list(function->keyword_arguments, | ||||
| arguments->value.pair.first->value.symbol.identifier, | arguments->value.pair.first->value.symbol.identifier, | ||||
| next->value.pair.first); | |||||
| ret); | |||||
| arguments = next->value.pair.rest; | arguments = next->value.pair.rest; | ||||
| } else { | } else { | ||||
| create_parsing_error("Expecting a value after 'defaults-to'"); | 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 { | proc is_truthy(Lisp_Object* expression, Environment* env) -> bool { | ||||
| Lisp_Object* result; | Lisp_Object* result; | ||||
| try result = eval_expr(expression, env); | try result = eval_expr(expression, env); | ||||
| return result != Memory::nil; | return result != Memory::nil; | ||||
| } | } | ||||
| @@ -283,6 +283,9 @@ namespace Memory { | |||||
| } | } | ||||
| proc copy_lisp_object(Lisp_Object* n) -> Lisp_Object* { | proc copy_lisp_object(Lisp_Object* n) -> Lisp_Object* { | ||||
| if (n == nil || n == t) | |||||
| return n; | |||||
| Lisp_Object* target; | Lisp_Object* target; | ||||
| try target = create_lisp_object(); | try target = create_lisp_object(); | ||||
| *target = *n; | *target = *n; | ||||
| @@ -635,6 +635,7 @@ proc run_all_tests() -> bool { | |||||
| printf("\n-- Test Files --\n"); | printf("\n-- Test Files --\n"); | ||||
| invoke_test_script("evaluation_of_default_args"); | |||||
| invoke_test_script("lexical_scope"); | invoke_test_script("lexical_scope"); | ||||
| invoke_test_script("class_macro"); | invoke_test_script("class_macro"); | ||||
| invoke_test_script("sicp"); | invoke_test_script("sicp"); | ||||