diff --git a/bin/test.lsp b/bin/test.slime
similarity index 100%
rename from bin/test.lsp
rename to bin/test.slime
diff --git a/build.bat b/build.bat
index 7915cb5..9099ce9 100644
--- a/build.bat
+++ b/build.bat
@@ -2,7 +2,7 @@
@setlocal
pushd %~dp0
-set exeName=lisp.exe
+set exeName=slime.exe
set binDir=bin
mkdir quickbuild 2>nul
diff --git a/debug.bat b/debug.bat
index b95544b..8bba81a 100644
--- a/debug.bat
+++ b/debug.bat
@@ -1,4 +1,4 @@
@echo off
pushd %~dp0\vs
-start lisp.sln
+start slime.sln
popd
diff --git a/run.bat b/run.bat
index 7abd6bf..198cd5f 100644
--- a/run.bat
+++ b/run.bat
@@ -4,6 +4,6 @@ pushd %~dp0\bin
call ..\build.bat
if %errorlevel% == 0 (
echo ---------- Running ----------
- call timecmd lisp.exe
+ call timecmd slime.exe
)
popd
diff --git a/src/ast.c b/src/ast.c
index 2215d7b..aff1bb4 100644
--- a/src/ast.c
+++ b/src/ast.c
@@ -36,14 +36,77 @@ typedef struct {
} Pair;
typedef struct {
- Pair* regular_params;
- Pair* keyword_params;
-} Lambda_List;
+ struct Ast_Node** data;
+ int length;
+ int next_index;
+} Ast_Node_Array_List;
+
+
+typedef struct {
+ char** identifiers;
+ int next_index;
+ int length;
+} Positional_Arguments;
typedef struct {
- Lambda_List* parameters;
- Pair* form;
- char* docstring;
+ char** identifiers;
+ // values[i] will be nullptr if no defalut value was declared for
+ // key identifiers[i]
+ Ast_Node_Array_List* values;
+ int next_index;
+ int length;
+} Keyword_Arguments;
+
+
+Ast_Node_Array_List* create_Ast_Node_Array_List(int initial_length);
+void append_to_Ast_Node_Array_List(Ast_Node_Array_List* list, struct Ast_Node* node);
+
+Positional_Arguments* create_positional_argument_list(int initial_capacity) {
+ Positional_Arguments* ret = new(Positional_Arguments);
+ ret->identifiers = (char**)malloc(initial_capacity * sizeof(char*));
+ ret->next_index = 0;
+ ret->length = initial_capacity;
+ return ret;
+}
+
+void append_to_positional_argument_list(Positional_Arguments* args, char* identifier) {
+ if (args->next_index == args->length) {
+ args->length *= 2;
+ args->identifiers = (char**)realloc(args->identifiers, args->length * sizeof(char*));
+ }
+ args->identifiers[args->next_index++] = identifier;
+}
+
+Keyword_Arguments* create_keyword_argument_list(int initial_capacity) {
+ Keyword_Arguments* ret = new(Keyword_Arguments);
+ ret->identifiers = (char**)malloc(initial_capacity * sizeof(char*));
+ ret->values = create_Ast_Node_Array_List(initial_capacity);
+ ret->next_index = 0;
+ ret->length = initial_capacity;
+ return ret;
+}
+
+void append_to_keyword_argument_list(Keyword_Arguments* args,
+ char* identifier,
+ struct Ast_Node* default_value)
+{
+ if (args->next_index == args->length) {
+ args->length *= 2;
+ args->identifiers = (char**)realloc(args->identifiers, args->length * sizeof(char*));
+ }
+
+ args->identifiers[args->next_index++] = identifier;
+ append_to_Ast_Node_Array_List(args->values, default_value);
+}
+
+
+typedef struct {
+ char* docstring;
+ Positional_Arguments* positional_arguments;
+ Keyword_Arguments* keyword_arguments;
+ // rest_argument will be nullptr if no rest argument is declared
+ char* rest_argument;
+ struct Ast_Node* body; // implicit prog
} Function;
typedef enum {
@@ -134,12 +197,6 @@ struct Ast_Node {
// was forward declarated
typedef struct Ast_Node Ast_Node;
-typedef struct {
- Ast_Node** data;
- int length;
- int next_index;
-} Ast_Node_Array_List;
-
typedef struct {
Ast_Node_Array_List* positional_arguments;
// TODO(Felix): Really use hashmap (keyword[sting] ->
diff --git a/src/error.c b/src/error.c
index b95fb9c..99d635f 100644
--- a/src/error.c
+++ b/src/error.c
@@ -1,6 +1,7 @@
typedef enum {
Error_Type_Ill_Formed_List,
Error_Type_Ill_Formed_Arguments,
+ Error_Type_Ill_Formed_Lambda_List,
Error_Type_Wrong_Number_Of_Arguments,
Error_Type_Unknown_Keyword_Argument,
Error_Type_Type_Missmatch,
@@ -40,6 +41,7 @@ char* Error_Type_to_string(Error_Type type) {
switch (type) {
case Error_Type_Ill_Formed_List: return "Evaluation-error: Ill formed list";
case Error_Type_Ill_Formed_Arguments: return "Evaluation-error: Ill formed arguments";
+ case Error_Type_Ill_Formed_Lambda_List: return "Evaluation-error: Ill formed lambda list";
case Error_Type_Unknown_Keyword_Argument: return "Evaluation-error: Unknown keyword argument";
case Error_Type_Not_A_Function: return "Evaluation-error: Not a function";
case Error_Type_Symbol_Not_Defined: return "Evaluation-error: Symbol not defined";
diff --git a/src/eval.c b/src/eval.c
index a98ed3e..01b28b2 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -1,7 +1,140 @@
-/* Ast_Node* apply_to_lambda () {} */
-Ast_Node* eval_expr(Ast_Node* node, Environment* env);
-bool is_truthy (Ast_Node* expression, Environment* env);
+Ast_Node* apply_arguments_to_function (Ast_Node* arguments, Ast_Node* function) {
+ printf("calling function, yey\n");
+
+ return create_ast_node_nil();
+}
+
+/**
+ This parses the argument specification of funcitons into their
+ Function struct. It dois this by allocating new
+ positional_arguments, keyword_arguments and rest_argument and
+ filling it in
+*/
+void parse_argument_list(Ast_Node* arguments, Function* function) {
+ // first init the fields
+ function->positional_arguments = create_positional_argument_list(16);
+ function->keyword_arguments = create_keyword_argument_list(16);
+ function->rest_argument = nullptr;
+
+ // okay let's try to read some positional arguments
+ while (arguments->type == Ast_Node_Type_Pair) {
+ if (arguments->value.pair->first->type == Ast_Node_Type_Keyword) {
+ if (string_equal(arguments->value.pair->first->value.keyword->identifier, "keys") ||
+ string_equal(arguments->value.pair->first->value.keyword->identifier, "rest"))
+ break;
+ else {
+ create_error(Error_Type_Ill_Formed_Lambda_List, arguments);
+ return;
+ }
+ }
+
+ if (arguments->value.pair->first->type != Ast_Node_Type_Symbol) {
+ create_error(Error_Type_Ill_Formed_Lambda_List, arguments);
+ return;
+ }
+
+ // okay wow we found an actual symbol
+ append_to_positional_argument_list(
+ function->positional_arguments,
+ arguments->value.pair->first->value.symbol->identifier);
+
+ arguments = arguments->value.pair->rest;
+ }
+
+ // okay we are done with positional arguments, lets check for
+ // keywords,
+ if (arguments->type != Ast_Node_Type_Pair) {
+ if (arguments->type != Ast_Node_Type_Nil)
+ create_error(Error_Type_Ill_Formed_Lambda_List, arguments);
+ return;
+ }
+
+ if (arguments->value.pair->first->type == Ast_Node_Type_Keyword &&
+ string_equal(arguments->value.pair->first->value.keyword->identifier, "keys"))
+ {
+ arguments = arguments->value.pair->rest;
+ if (arguments->type != Ast_Node_Type_Pair ||
+ arguments->value.pair->first->type != Ast_Node_Type_Symbol)
+ {
+ create_error(Error_Type_Ill_Formed_Lambda_List, arguments);
+ return;
+ }
+
+ while (arguments->type == Ast_Node_Type_Pair) {
+ if (arguments->value.pair->first->type == Ast_Node_Type_Keyword) {
+ if (string_equal(arguments->value.pair->first->value.keyword->identifier, "rest"))
+ break;
+ else {
+ create_error(Error_Type_Ill_Formed_Lambda_List, arguments);
+ return;
+ }
+ }
+
+ if (arguments->value.pair->first->type != Ast_Node_Type_Symbol) {
+ create_error(Error_Type_Ill_Formed_Lambda_List, arguments);
+ return;
+ }
+
+ // we found a symbol (arguments->value.pair->first) for
+ // the keyword args! Let's check if the next arguement is
+ // :defaults-to
+ Ast_Node* next = arguments->value.pair->rest;
+ if (next->type == Ast_Node_Type_Pair &&
+ next->value.pair->first->type == Ast_Node_Type_Keyword &&
+ string_equal(next->value.pair->first->value.keyword->identifier,
+ "defaults-to"))
+ {
+ // check if there is a next argument too, otherwise it
+ // would be an error
+ next = next->value.pair->rest;
+ if (next->type == Ast_Node_Type_Pair) {
+ append_to_keyword_argument_list(function->keyword_arguments,
+ arguments->value.pair->first->value.symbol->identifier,
+ next->value.pair->first);
+ arguments = next->value.pair->rest;
+ } else {
+ create_error(Error_Type_Ill_Formed_Lambda_List, arguments);
+ return;
+ }
+ } else {
+ // No :defaults-to, so just add it to the list
+ append_to_keyword_argument_list(function->keyword_arguments,
+ arguments->value.pair->first->value.symbol->identifier,
+ nullptr);
+ arguments = next;
+ }
+ }
+ }
+
+
+ // Now we are also done with keyword arguments, lets check for
+ // if there is a rest argument
+ if (arguments->type != Ast_Node_Type_Pair) {
+ if (arguments->type != Ast_Node_Type_Nil)
+ create_error(Error_Type_Ill_Formed_Lambda_List, arguments);
+ return;
+ }
+
+ if (arguments->value.pair->first->type == Ast_Node_Type_Keyword &&
+ string_equal(arguments->value.pair->first->value.keyword->identifier, "rest"))
+ {
+ arguments = arguments->value.pair->rest;
+ if (arguments->type != Ast_Node_Type_Pair ||
+ arguments->value.pair->first->type != Ast_Node_Type_Symbol)
+ {
+ create_error(Error_Type_Ill_Formed_Lambda_List, arguments);
+ return;
+ }
+ function->rest_argument = arguments->value.pair->first->value.symbol->identifier;
+ if (arguments->value.pair->rest->type != Ast_Node_Type_Nil) {
+ create_error(Error_Type_Ill_Formed_Lambda_List, arguments);
+ }
+ } else {
+ printf("this should not happen?");
+ create_error(Error_Type_Unknown_Error, arguments);
+ }
+}
int list_length(Ast_Node* node) {
@@ -56,6 +189,9 @@ Ast_Node* copy_list(Ast_Node* node) {
return result;
}
+Ast_Node* eval_expr(Ast_Node* node, Environment* env);
+bool is_truthy (Ast_Node* expression, Environment* env);
+
Parsed_Arguments* eval_and_parse_arguments(Ast_Node* arguments, Environment* env) {
// TODO(Felix): if arguments is nil then maybe return a constant
// Parsed_Arguments_nil?
@@ -70,9 +206,9 @@ Parsed_Arguments* eval_and_parse_arguments(Ast_Node* arguments, Environment* env
Parse_State parse_state = Expecting_Anything;
Parsed_Arguments* result = new(Parsed_Arguments);
- result->positional_arguments = create_Ast_Node_Array_List();
- result->keyword_keys = create_Ast_Node_Array_List();
- result->keyword_values = create_Ast_Node_Array_List();
+ result->positional_arguments = create_Ast_Node_Array_List(16);
+ result->keyword_keys = create_Ast_Node_Array_List(16);
+ result->keyword_values = create_Ast_Node_Array_List(16);
Ast_Node* current_head = copy_list(arguments);
Ast_Node* current_evaluated_head;
@@ -171,6 +307,47 @@ Ast_Node* eval_expr(Ast_Node* node, Environment* env) {
// check for special form
if (operator->type == Ast_Node_Type_Built_In_Function) {
switch (operator->value.built_in_function->type) {
+ case Built_In_Lambda: {
+ /*
+ * (lambda ())
+ * (lambda (x d) (+ 1 2) (- 1 2) (* 1 2))
+ */
+ try {
+ arguments_length = list_length(arguments);
+ }
+ if (arguments_length == 0)
+ report_error(Error_Type_Wrong_Number_Of_Arguments);
+
+
+ Function* function = new(Function);
+ // if parameters were specified
+ if (arguments->value.pair->first->type != Ast_Node_Type_Nil) {
+ try {
+ assert_type(arguments->value.pair->first, Ast_Node_Type_Pair);
+ }
+ try {
+ parse_argument_list(arguments->value.pair->first, function);
+ }
+ }
+
+ arguments = arguments->value.pair->rest;
+ // if there is a docstring, use it
+ if (arguments->value.pair->first->type == Ast_Node_Type_String) {
+ function->docstring = arguments->value.pair->first->value.string->value;
+ arguments = arguments->value.pair->rest;
+ }
+
+ // we are now in the function body, just wrap it in an
+ // implicit prog
+ function->body = create_ast_node_pair(
+ create_ast_node_built_in_function("prog"),
+ arguments);
+
+ Ast_Node* ret = new(Ast_Node);
+ ret->type = Ast_Node_Type_Function;
+ ret->value.function = function;
+ return ret;
+ }
case Built_In_And: {
bool result = true;
while (arguments->type != Ast_Node_Type_Nil) {
@@ -454,6 +631,13 @@ Ast_Node* eval_expr(Ast_Node* node, Environment* env) {
// assume it's lambda function and evaluate the arguments
arguments = eval_arguments(arguments, env, &arguments_length);
+ if (operator->type == Ast_Node_Type_Function) {
+ Ast_Node* result;
+ try {
+ result = apply_arguments_to_function(arguments, operator);
+ }
+ return result;
+ }
}
default:
diff --git a/src/init.c b/src/init.c
index 0936392..fa93ae6 100644
--- a/src/init.c
+++ b/src/init.c
@@ -14,5 +14,5 @@ void init () {
#endif
// TODO(Felix): Init the constants here (Bulit-in function
- // ast_nodes, nil, ..more?)
+ // ast_nodes, nil, type_keywords ..more?)
}
diff --git a/src/io.c b/src/io.c
index c8c346e..2668954 100644
--- a/src/io.c
+++ b/src/io.c
@@ -69,6 +69,7 @@ void print(Ast_Node* node) {
} break;
case (Ast_Node_Type_Function): {
printf("[lambda]");
+ /* print(node->value.function->body); */
} break;
case (Ast_Node_Type_Built_In_Function): {
printf("[built-in-function %s]", Built_In_Name_to_string(node->value.built_in_function->type));
diff --git a/src/parse.c b/src/parse.c
index c0b63e8..7004604 100644
--- a/src/parse.c
+++ b/src/parse.c
@@ -1,22 +1,23 @@
-Ast_Node_Array_List* create_Ast_Node_Array_List() {
+Ast_Node_Array_List* create_Ast_Node_Array_List(int initial_length) {
Ast_Node_Array_List* ret = new (Ast_Node_Array_List);
// create one with 16 entries first
- ret->length = 16;
- ret->data = (Ast_Node**)malloc(ret->length * sizeof(Ast_Node));
+ ret->length = initial_length;
+ ret->data = (struct Ast_Node**)malloc(initial_length * sizeof(struct Ast_Node));
ret->next_index = 0;
return ret;
}
-void append_to_Ast_Node_Array_List(Ast_Node_Array_List* list, Ast_Node* node) {
+void append_to_Ast_Node_Array_List(Ast_Node_Array_List* list, struct Ast_Node* node) {
if (list->next_index == list->length) {
list->length *= 2;
- list->data = (Ast_Node**)realloc(list->data, list->length * sizeof(Ast_Node));
+ list->data = (struct Ast_Node**)realloc(list->data, list->length * sizeof(struct Ast_Node));
}
list->data[list->next_index++] = node;
}
+
void eat_comment_line(char* text, int* index_in_text) {
// safety check if we are actually starting a comment here
if (text[*index_in_text] != ';')
@@ -237,7 +238,7 @@ Ast_Node* parse_single_expression(char* text) {
}
Ast_Node_Array_List* parse_program(char* text) {
- Ast_Node_Array_List* program = create_Ast_Node_Array_List();
+ Ast_Node_Array_List* program = create_Ast_Node_Array_List(16);
int index_in_text = 0;
diff --git a/test.bat b/test.bat
index 53ed669..5c794e4 100644
--- a/test.bat
+++ b/test.bat
@@ -4,7 +4,7 @@ pushd %~dp0\bin
call ..\build.bat
if %errorlevel% == 0 (
echo ---------- Testing ----------
- call timecmd lisp.exe test.lsp
+ call timecmd slime.exe test.slime
)
popd
diff --git a/todo.html b/todo.html
new file mode 100644
index 0000000..c5d53d7
--- /dev/null
+++ b/todo.html
@@ -0,0 +1,539 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
1 Arguments
+
+
+In Emacs lisp, keyword arguments must be passed in the same order as they were
+defined in, so ((lambda (a &key b c) (+ a b c)) 1 3 :b 2) will preduce an
+error, because b was passed as last argument but was defined as second argument.
+
+
+
+
((lambda(a b)(+ a b)) 1 2);; 3
+((lambda(a &key b)(+ a b)) 1 :b 2);; 3
+((lambda(a &key b)(+ a b)):b 2 1);; error
+
+
+
+
+
+But if mulitiple keyword arguments are defined, their ordering does not matter.
+
+
+
+
((lambda(a &key b &key c &key d)(+ a b c d)) 1 :d 4 :c 3 :b 2);; 10
+
+
+
+
+It is even possible to have a non keyword argument bewtween keyword arguments
+and when calling, switch the order.
+
+
+
+
((lambda(a &key b c &key d)(+ a b c d)) 1 :d 4 3 :b 2);; 10
+((lambda(a &key b c &key d)(+ a b c d)) 1 :d 4 :b 2 3);; error
+
+
+
+
+However somehow it is not possible to flip the ordering at the first argument
+
+
+
+
((lambda(a &key b)(+ a b)) 2 :b 1);; 3
+((lambda(a &key b)(+ a b)):b 2 1);; error
+((lambda(&key a b)(+ a b)):a 2 1);; 3
+((lambda(&key a b)(+ a b)) 1 :a 2);; error
+
+
+
+
+So it seems, positional arguments have to be at the exact position they were
+defined in, but keyword arguments can appear in any order, but at the places
+where keyword arguments have been defined.
+
+
+
+
+
1.1 Idea
+
+
+Every argument can be set using keys, but keyword arguments always come strictly
+after positional arguments
+
+
+
+
+
((lambda(a b c d)) 1 2 3 4);; ok
+((lambda(a b c d)) 1 2 :c 2 :d 1);; ok
+((lambda(a b c d)) 1 2 :d 1 :c 2);; ok
+((lambda(a b c d)):d 1 1 2 :c 2);; error, keyword arguments must come last
+((lambda(a b c d)) 1 2 :c 2 :d 1 :a 2);; error, 'a' was passed two times
+
+((lambda(a b c d)) 1 2 :d 1);; error, c was not passed, but does not have a default value
+((lambda(a b c :default 10 d)) 1 2 :d 1);; okay again
+
+
+
+
+The drawback then is that you can never pass a keyword as a positional argument,
+because it will always try to assign the next argument to the variable defined
+by the keyword. You could still pass it as a keyword argument though. This is
+especially annoying because we want to use keywords as type identifiers.
+
+
+
+
+
(= (type "hello"):string)
+
+
+
+
+
+
+
((lambda(i-wanna-get-a-kw)):hey);; error, unmatched value for keyword argument
+((lambda(i-wanna-get-a-kw)):i-wanna-get-a-kw:hey);; this would work again
+
+
+
+
+Other Idea is to mark the place in the parameter list where keyword argumetns
+start and arguments before that are positional and will not be stuffed into
+key-value pairs and arguments after that will be keyword arguments
+
(= (type "hello"):string);; this will work again because (=) does
+ ;; not use keyword arguments
+ ((lambda(i-wanna-get-a-kw)):hey);; would work because of the same reason
+
+ (define fun
+ (lambda(:keys a b c d)
+ (+ a b c d)))
+
+ (fun 1 2 3 4);; okay
+ (fun 1 2 :d 3 :c 4);; okay
+ (fun 1 2 :d 3 :c 4 :a 2);; error, 'a' was passed two times
+ (fun :c 1 :b 2 :d 3 :a 4);; okay
+ (fun :c 1 :b 2 :d 3 4);; error, positional argument after keyword
+ ;; and fun does not accept rest parameter
+
+ (define fun2
+ (lambda(:quoted op a b :keys c d :rest r)
+ (print r)
+ (op a b c d)))
+
+>> (fun2 * 1 2 :c 3 :d 4 "this""can":be"whatever")
+("this""can":be"whatever")
+24
+
+
+>> (define print-before-eval (lambda(:body expr)
+ (print expr)
+ (eval expr)))
+>> (define r (print-before-eval (+ 1 2 3)))
+(+ 1 2 3)
+>> r
+6
+
+
+
+
+
+Wait this does not work either because you won't be able to pass keywords into
+rest.
+
+
+
+Unrealated Idea: Distinguish between functions and macros
+
+
+
+The difference is really simple:
+
+
+
Function
like a lambda, but no argument is automatically quoted
+
+
Macro
like a lambda, but every argument is automatically quoted
+
+
+
+
+This is important, because a macro should not introduce a new environment, but
+use the one it lives in.
+
positional arguments (always required, no default value)
+
+
keyword arguments (always come after positionals), can be not passed if there
+is a default value
+
+
We want to be able to have a rest paramter, where everything is dumped in to
+that exceeds the other parameters
+
+
+
+
+The problem is, we have no way of knowing if the current keyword we are
+ reading is still part of the keyword block or part of the rest paramter.
+
+
+
+Consider:
+
+
+
+
+
>> (define fun (lambda(a b :key c d :defaults-to 4 :rest) nil))
+>> (fun 1 2 :c 3 :d 5)
+
+
+
+What should this be evaluated to?
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
a
+
b
+
c
+
d
+
rest
+
+
+
+
+
v1
+
1
+
2
+
3
+
5
+
nil
+
+
+
+
v2
+
1
+
2
+
3
+
4
+
(:d 5)
+
+
+
+
+
+It is ambiguous. It is however important to note that this can only happen when
+there are optinal keyword arguments (keyword arguments with default values).
+Otherwise we could always know which variables are set and wich have not been
+set to see if we are in an errornious state.
+
+
+
+
+
+
1.3 Solution
+
+
+We can resolve that problem by always making the the decision of chosing v1
+between v1 and v1. This means, when we are expecting to read kwargs, and we
+encounter an kwarg, try to apply it (this can fail, for two reasons:
+
+
+
+
The keyword has no following value
+
+
The keyword is not a keyword argument in the funciton
+
+
+
+
+), if it fails treat it as part of the rest arguments, and read all the
+following arguments into the rest arguments, or if it works, use it as the
+keyword argument.
+
+
+
+
+
+
1.4 Implementation
+
+
+
+
typedefstruct{
+ char* docstring;
+ char** positional_arguments;
+ struct{
+ char* identifier;
+ Ast_node* default_value; // will be nullptr if no defalut value was declared
+ }* keyword_arguments;
+ char* rest_argument; // will be nullptr if no rest argument is declared
+ Ast_Node_array_listbody;
+}lambda;
+
+
+
+
+
+
+
+
+
+
diff --git a/todo.org b/todo.org
index ca0cceb..af2c616 100644
--- a/todo.org
+++ b/todo.org
@@ -1,3 +1,196 @@
+* Arguments
+
+In Emacs lisp, keyword arguments must be passed in the same order as they were
+defined in, so =((lambda (a &key b c) (+ a b c)) 1 3 :b 2)= will preduce an
+error, because =b= was passed as last argument but was defined as second argument.
+#+begin_src emacs-lisp
+ ((lambda (a b) (+ a b)) 1 2) ;; 3
+ ((lambda (a &key b) (+ a b)) 1 :b 2) ;; 3
+ ((lambda (a &key b) (+ a b)) :b 2 1) ;; error
+#+end_src
+
+
+But if mulitiple keyword arguments are defined, their ordering does not matter.
+#+begin_src emacs-lisp
+ ((lambda (a &key b &key c &key d) (+ a b c d)) 1 :d 4 :c 3 :b 2) ;; 10
+#+end_src
+
+It is even possible to have a non keyword argument bewtween keyword arguments
+and when calling, switch the order.
+#+begin_src emacs-lisp
+ ((lambda (a &key b c &key d) (+ a b c d)) 1 :d 4 3 :b 2) ;; 10
+ ((lambda (a &key b c &key d) (+ a b c d)) 1 :d 4 :b 2 3) ;; error
+#+end_src
+
+However somehow it is not possible to flip the ordering at the first argument
+#+begin_src emacs-lisp
+ ((lambda (a &key b) (+ a b)) 2 :b 1) ;; 3
+ ((lambda (a &key b) (+ a b)) :b 2 1) ;; error
+ ((lambda (&key a b) (+ a b)) :a 2 1) ;; 3
+ ((lambda (&key a b) (+ a b)) 1 :a 2) ;; error
+#+end_src
+
+So it seems, positional arguments have to be at the exact position they were
+defined in, but keyword arguments can appear in any order, but at the places
+where keyword arguments have been defined.
+
+** Idea
+
+Every argument can be set using keys, but keyword arguments always come strictly
+after positional arguments
+
+#+begin_src emacs-lisp
+ ((lambda (a b c d)) 1 2 3 4) ;; ok
+ ((lambda (a b c d)) 1 2 :c 2 :d 1) ;; ok
+ ((lambda (a b c d)) 1 2 :d 1 :c 2) ;; ok
+ ((lambda (a b c d)) :d 1 1 2 :c 2) ;; error, keyword arguments must come last
+ ((lambda (a b c d)) 1 2 :c 2 :d 1 :a 2) ;; error, 'a' was passed two times
+
+ ((lambda (a b c d)) 1 2 :d 1) ;; error, c was not passed, but does not have a default value
+ ((lambda (a b c :default 10 d)) 1 2 :d 1) ;; okay again
+#+end_src
+
+The drawback then is that you can never pass a keyword as a positional argument,
+because it will always try to assign the next argument to the variable defined
+by the keyword. You could still pass it as a keyword argument though. This is
+especially annoying because we want to use keywords as type identifiers.
+
+#+begin_src emacs-lisp
+(= (type "hello") :string)
+#+end_src
+
+
+#+begin_src emacs-lisp
+ ((lambda (i-wanna-get-a-kw)) :hey) ;; error, unmatched value for keyword argument
+ ((lambda (i-wanna-get-a-kw)) :i-wanna-get-a-kw :hey) ;; this would work again
+#+end_src
+
+Other Idea is to mark the place in the parameter list where keyword argumetns
+start and arguments before that are positional and will not be stuffed into
+key-value pairs and arguments after that will be keyword arguments
+
+#+begin_src ebnf
+ -> *
+ [:keys ( [:defaults-to ])*]
+ [(:rest )|(:body )]
+#+end_src
+
+#+begin_src emacs-lisp
+ (= (type "hello") :string) ;; this will work again because (=) does
+ ;; not use keyword arguments
+ ((lambda (i-wanna-get-a-kw)) :hey) ;; would work because of the same reason
+
+ (define fun
+ (lambda (:keys a b c d)
+ (+ a b c d)))
+
+ (fun 1 2 3 4) ;; okay
+ (fun 1 2 :d 3 :c 4) ;; okay
+ (fun 1 2 :d 3 :c 4 :a 2) ;; error, 'a' was passed two times
+ (fun :c 1 :b 2 :d 3 :a 4) ;; okay
+ (fun :c 1 :b 2 :d 3 4) ;; error, positional argument after keyword
+ ;; and fun does not accept rest parameter
+
+ (define fun2
+ (lambda (:quoted op a b :keys c d :rest r)
+ (print r)
+ (op a b c d)))
+
+>> (fun2 * 1 2 :c 3 :d 4 "this" "can" :be "whatever")
+("this" "can" :be "whatever")
+24
+
+
+>> (define print-before-eval (lambda (:body expr)
+ (print expr)
+ (eval expr)))
+>> (define r (print-before-eval (+ 1 2 3)))
+(+ 1 2 3)
+>> r
+6
+
+#+end_src
+
+Wait this does not work either because you won't be able to pass keywords into
+rest.
+
+*Unrealated Idea: Distinguish between functions and macros*
+
+The difference is really simple:
+ - Function :: like a lambda, but no argument is automatically quoted
+ - Macro :: like a lambda, but every argument is automatically quoted
+
+This is important, because a macro should not introduce a new environment, but
+use the one it lives in.
+
+#+begin_src emacs-lisp
+
+>> (define print-before-eval (macro (:rest expr)
+ (print expr)
+ (eval expr))
+
+#+end_src
+
+** the actual problem
+
+We have some requirements to the arguments:
+ - positional arguments (always required, no default value)
+ - keyword arguments (always come after positionals), can be not passed if there
+ is a default value
+ - We want to be able to have a rest paramter, where everything is dumped in to
+ that exceeds the other parameters
+
+*The problem is, we have no way of knowing if the current keyword we are
+ reading is still part of the keyword block or part of the rest paramter.*
+
+Consider:
+
+#+begin_src emacs-lisp
+ >> (define fun (lambda (a b :key c d :defaults-to 4 :rest) nil))
+ >> (fun 1 2 :c 3 :d 5)
+
+
+ (defun haha (par1 par2)
+ (+ 1 2))
+#+end_src
+What should this be evaluated to?
+
+| | a | b | c | d | rest |
+|----+---+---+---+---+--------|
+| v1 | 1 | 2 | 3 | 5 | nil |
+| v2 | 1 | 2 | 3 | 4 | (:d 5) |
+
+It is ambiguous. It is however important to note that this can only happen when
+there are optinal keyword arguments (keyword arguments with default values).
+Otherwise we could always know which variables are set and wich have not been
+set to see if we are in an errornious state.
+
+** Solution
+ We can resolve that problem by always making the the decision of chosing =v1=
+ between =v1= and =v1=. This means, when we are expecting to read kwargs, and we
+ encounter an kwarg, try to apply it (this can fail, for two reasons:
+
+ 1. The keyword has no following value
+ 2. The keyword is not a keyword argument in the funciton
+
+ ), if it fails treat it as part of the rest arguments, and read all the
+ following arguments into the rest arguments, or if it works, use it as the
+ keyword argument.
+
+** Implementation
+
+#+begin_src c
+ typedef struct {
+ char* docstring;
+ char** positional_arguments;
+ struct {
+ char* identifier;
+ Ast_node* default_value; // will be nullptr if no defalut value was declared
+ }* keyword_arguments;
+ char* rest_argument; // will be nullptr if no rest argument is declared
+ Ast_Node_array_list body;
+ } lambda;
+#+end_src
* TODO =assert_equal_type= macro in testing
* DONE use an enum for builtin identifiers
CLOSED: [2018-10-11 Do 17:15]
@@ -25,9 +218,9 @@
CLOSED: [2018-10-05 Fr 22:21]
** DONE not
CLOSED: [2018-10-05 Fr 22:21]
-#+begin_src emacs-lisp
+#+begin_src
(not 1) ;; == (not . (1 . nil))
-(not (expression 1 3)) ;; == (not . ((expression . (1 . (3 . nil))) . nil))
+(not (expression 1 3)) ;; == (not . ((expression . (1 . (3 . nil))) . nil) . nil)
(not) ;; == (not . nil)
(not 1 2) ;; == (not . (1 . (2 . nil)))
#+end_src
@@ -62,14 +255,14 @@
** DONE list
CLOSED: [2018-10-08 Mo 21:06]
-#+begin_src emacs-lisp
+#+begin_src
(quote (cons 2 (cons 3 (cons 4 ()))))
;; (cons 2 (cons 3 (cons 4 nil)))
(quote (2 . (3 . (4 . ()))))
;; (2 3 4)
#+end_src
-#+begin_src emacs-lisp
+#+begin_src
(list (cons 2 (cons 3 (cons 4 ()))))
;; ((2 3 4))
(list (quote(2 . (3 . (4 . ())))))
@@ -91,16 +284,3 @@
- pair (cons-cell)
- lambda function lambda
- built-in function
-
-* Arbeitsweise
-
- 1) Parse to AST
- 2) Start with namespace only containing the built-ins
- 3) every AST node has its own namespace,
- inherting the one from its parent (pointer /
- references) because when a function sets a
- variable within an upper namespace it should
- infact change the environment even if the
- function exited
-
-(progn (setq x 4)(* x 2))
diff --git a/vs/lisp.sln b/vs/slime.sln
similarity index 93%
rename from vs/lisp.sln
rename to vs/slime.sln
index 4ce7f5a..d61dfe6 100644
--- a/vs/lisp.sln
+++ b/vs/slime.sln
@@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.27004.2005
MinimumVisualStudioVersion = 10.0.40219.1
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "lisp", "lisp.vcxproj", "{1A47A3ED-871F-4CB4-875B-8CAA385B1771}"
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "slime", "slime.vcxproj", "{1A47A3ED-871F-4CB4-875B-8CAA385B1771}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
diff --git a/vs/lisp.vcxproj b/vs/slime.vcxproj
similarity index 98%
rename from vs/lisp.vcxproj
rename to vs/slime.vcxproj
index 76f9b77..b74f8cc 100644
--- a/vs/lisp.vcxproj
+++ b/vs/slime.vcxproj
@@ -29,8 +29,9 @@
15.0{1A47A3ED-871F-4CB4-875B-8CAA385B1771}
- lisp
+ slime10.0.16299.0
+ slime