From 488b47f08f16d63e94fe4a1c6562b7f55f0c1d49 Mon Sep 17 00:00:00 2001 From: FelixBrendel Date: Sat, 20 Oct 2018 23:27:51 +0200 Subject: [PATCH] We have the beginning of a stdlib and can parse keyword and rest args --- bin/pre.slime | 73 ++++++++++++++++++++++ bin/test.slime | 1 - build.bat | 2 +- src/ast.c | 18 +++--- src/built_ins.c | 23 +++++++ src/env.c | 1 + src/eval.c | 161 ++++++++++++++++++++++++++++++++++++++++++++++-- src/helpers.c | 29 +++++++++ src/io.c | 2 +- src/main.c | 17 ++++- 10 files changed, 306 insertions(+), 21 deletions(-) create mode 100644 bin/pre.slime diff --git a/bin/pre.slime b/bin/pre.slime new file mode 100644 index 0000000..f091e27 --- /dev/null +++ b/bin/pre.slime @@ -0,0 +1,73 @@ +(define nil ()) + +(define nil? + (lambda (x) + "Checks if the argument is nil." + (= x nil))) + +(define append + (lambda (obj sequence) + (if (nil? sequence) + (list obj) + (if (nil? (rest sequence)) + )))) + +(define range + (lambda (:keys from :defaults-to 0 to) + "Returns a sequence of numbers starting with the number +defined by the key 'from' and ends with the number defined in +'to'." + (if (<= from to) + (pair from (range :from (+ 1 from) :to to)) + nil))) + +(define map + (lambda (function sequence) + "Takes a sequence and a function as arguments and returns a +new sequence which contains the results of using the first +sequences elemens as argument to that function." + (if (nil? sequence) + sequence + (pair (function (first sequence)) + (map (rest sequence) function))))) + +(define reduce + (lambda (function sequence) + "Takes a sequence and a function as arguments and applies the +function to the argument sequence. This only works correctly if the +given function accepts a variable amount of parameters. If your +funciton is limited to two arguments, use `reduce-binary' instead." + (eval (pair function sequence)))) + +(define reduce-binary + (lambda (function sequence) + "Takes a sequence and a function as arguments and applies the +function to the argument sequence. reduce-binary applies the +arguments `pair-wise' which means it works with binary functions +as compared to `reduce'." + (if (nil? (rest sequence)) + (first sequence) + (function (first sequence) + (reduce-binary (rest sequence) function))))) + +(define filter + (lambda (function sequence) + (if (nil? sequence) + nil + (if (function (first sequence)) + (pair (first sequence) + (filter (rest sequence) function)) + (filter (rest sequence) function))))) + +(define printf + (lambda (:keys sep :defaults-to " " end :defaults-to "\n" :rest args) + (if (and (nil? (first args)) (nil? (rest args))) + (print end) + (prog + (print (first args)) + (if (not (nil? (rest args))) + (print sep)) + ;; TODO(Felix): later we should use `extend' here: + ;; (eval (extend (quote (printf :sep sep :end end)) (rest args))) + (eval (pair printf (pair :sep (pair sep (pair :end (pair end (rest args))))))) + nil)))) diff --git a/bin/test.slime b/bin/test.slime index 87d1138..b0dee1e 100644 --- a/bin/test.slime +++ b/bin/test.slime @@ -1,2 +1 @@ (define operators (list + - * /)) -(print ((first operators) 1 2 3)) diff --git a/build.bat b/build.bat index 9099ce9..40bdcbc 100644 --- a/build.bat +++ b/build.bat @@ -11,7 +11,7 @@ pushd quickbuild taskkill /F /IM %exeName% > NUL 2> NUL echo ---------- Compiling ---------- -call timecmd cl ../src/main.c /Fe%exeName% /W3 /TC /nologo /EHsc /Z7 /link /incremental /debug:fastlink +call timecmd cl ../src/main.c /Fe%exeName% /W3 /Ox /O2 /Oi /TC /nologo /EHsc /link if %errorlevel% == 0 ( echo. diff --git a/src/ast.c b/src/ast.c index aff1bb4..057932a 100644 --- a/src/ast.c +++ b/src/ast.c @@ -1,4 +1,5 @@ struct Ast_Node; +define_array_list(struct Ast_Node*, Ast_Node); typedef enum { Ast_Node_Type_Nil, @@ -36,13 +37,8 @@ typedef struct { } Pair; typedef struct { - struct Ast_Node** data; - int length; - int next_index; -} Ast_Node_Array_List; - - -typedef struct { + // TODO(Felix) use Ast_Node_symbols here instead, so we don't have + // to convert them to strings and back to symbols char** identifiers; int next_index; int length; @@ -58,8 +54,8 @@ typedef struct { } 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); +/* 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); @@ -80,7 +76,7 @@ void append_to_positional_argument_list(Positional_Arguments* args, char* identi 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->values = create_Ast_Node_array_list(initial_capacity); ret->next_index = 0; ret->length = initial_capacity; return ret; @@ -96,7 +92,7 @@ void append_to_keyword_argument_list(Keyword_Arguments* args, } args->identifiers[args->next_index++] = identifier; - append_to_Ast_Node_Array_List(args->values, default_value); + append_to_Ast_Node_array_list(args->values, default_value); } diff --git a/src/built_ins.c b/src/built_ins.c index 259eba0..1641705 100644 --- a/src/built_ins.c +++ b/src/built_ins.c @@ -1,3 +1,5 @@ +Ast_Node* eval_expr(Ast_Node* node, Environment* env); + bool ast_node_equal(Ast_Node* n1, Ast_Node* n2) { if (n1 == n2) return true; @@ -120,3 +122,24 @@ Ast_Node* built_in_divide(Ast_Node* operands) { } return create_ast_node_number(quotient); } + + +Ast_Node* built_in_load(char* file_name, Environment* env) { + char* file_content = read_entire_file(file_name); + if (file_content) { + Ast_Node* result = create_ast_node_nil(); + Ast_Node_Array_List* program; + try { + program = parse_program(file_content); + } + for (int i = 0; i < program->next_index; ++i) { + try { + result = eval_expr(program->data[i], env); + } + } + return result; + } else { + create_error(Error_Type_Unknown_Error, create_ast_node_nil()); + return nullptr; + } +} diff --git a/src/env.c b/src/env.c index 03a3ee6..99c97b5 100644 --- a/src/env.c +++ b/src/env.c @@ -60,5 +60,6 @@ Ast_Node* lookup_symbol(Symbol* sym, Environment* env) { return built_in; create_error(Error_Type_Symbol_Not_Defined, create_ast_node_nil()); + printf("%s\n", sym->identifier); return nullptr; } diff --git a/src/eval.c b/src/eval.c index 023ce6f..5e4951e 100644 --- a/src/eval.c +++ b/src/eval.c @@ -6,6 +6,9 @@ Ast_Node* apply_arguments_to_function(Ast_Node* arguments, Function* function, E // positional arguments for (int i = 0; i < function->positional_arguments->next_index; ++i) { if (arguments->type == Ast_Node_Type_Pair) { + // TODO(Felix): here we create new ast_node_symbols from + // their identifiers but before we converted them to + // strings from symbols... Wo maybe just use the symbols? define_symbol( create_ast_node_symbol(function->positional_arguments->identifiers[i]), arguments->value.pair->first, new_env); @@ -17,6 +20,122 @@ Ast_Node* apply_arguments_to_function(Ast_Node* arguments, Function* function, E arguments = arguments->value.pair->rest; } + + if (arguments->type == Ast_Node_Type_Nil) + goto eval_time; + + String_Array_List* read_in_keywords = create_String_array_list(16); + // 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 (arguments->value.pair->first->type == Ast_Node_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.keyword->identifier, + function->keyword_arguments->identifiers[i])) + { + accepted = true; + break; + } + } + if (!accepted) { + create_error(Error_Type_Ill_Formed_Arguments, arguments); + 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.keyword->identifier, + read_in_keywords->data[i])) + { + // 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_error(Error_Type_Ill_Formed_Arguments, arguments); + return nullptr; + } + } + + // 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 (arguments->value.pair->rest->type != Ast_Node_Type_Pair) { + create_error(Error_Type_Ill_Formed_Arguments, arguments); + return nullptr; + } + + // if not set it and then add it to the array list + define_symbol( + create_ast_node_symbol(arguments->value.pair->first->value.keyword->identifier), + arguments->value.pair->rest->value.pair->first, new_env); + + append_to_String_array_list(read_in_keywords, arguments->value.pair->first->value.keyword->identifier); + + // overstep both for next one + arguments = arguments->value.pair->rest->value.pair->rest; + + if (arguments->type == Ast_Node_Type_Nil) { + break; + } + } + + + // check if all necessary keywords have been read in + for (int i = 0; i < function->keyword_arguments->next_index; ++i) { + char* 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_error(Error_Type_Ill_Formed_Arguments, arguments); + 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) { + define_symbol( + create_ast_node_symbol(defined_keyword), + function->keyword_arguments->values->data[i], new_env); + } + } + } + + + if (arguments->type == Ast_Node_Type_Nil) { + if (function->rest_argument) { + define_symbol( + create_ast_node_symbol(function->rest_argument), + create_ast_node_nil(), new_env); + } + } else { + if (function->rest_argument) { + define_symbol( + create_ast_node_symbol(function->rest_argument), + arguments, new_env); + } else { + // rest was not declared but additional arguments were found + create_error(Error_Type_Ill_Formed_Arguments, arguments); + return nullptr; + } + } + + eval_time: Ast_Node* result; try { @@ -26,7 +145,16 @@ Ast_Node* apply_arguments_to_function(Ast_Node* arguments, Function* function, E return result; } -/* (define type (lambda (e) (if (and (= (old-type e) :pair) (= (first e) :my-type)) :my-type (old-type e)))) */ +/* + (prog + (define type--before type) + (define type + (lambda (e) + (if (and (= (type--before e) :pair) (= (first e) :my-type)) + :my-type + (type--before e)))) + ) +*/ /** This parses the argument specification of funcitons into their @@ -281,7 +409,9 @@ Ast_Node* eval_expr(Ast_Node* node, Environment* env) { return node; case Ast_Node_Type_Pair: { Ast_Node* operator; - if (node->value.pair->first->type != Ast_Node_Type_Built_In_Function) { + if (node->value.pair->first->type != Ast_Node_Type_Built_In_Function && + node->value.pair->first->type != Ast_Node_Type_Function) + { try { operator = eval_expr(node->value.pair->first, env); } @@ -465,6 +595,23 @@ Ast_Node* eval_expr(Ast_Node* node, Environment* env) { case Built_In_Equal: { return built_in_equals(evaluated_arguments); } + case Built_In_Load: { + try { + arguments_length = list_length(arguments); + } + if (arguments_length != 1) + report_error(Error_Type_Wrong_Number_Of_Arguments); + + if (evaluated_arguments->value.pair->first->type != Ast_Node_Type_String) + report_error(Error_Type_Type_Missmatch); + + Ast_Node* result; + try { + result = built_in_load( + evaluated_arguments->value.pair->first->value.string->value, env); + } + return result; + } case Built_In_Pair: { if (arguments_length != 2) { report_error(Error_Type_Wrong_Number_Of_Arguments); @@ -525,7 +672,7 @@ Ast_Node* eval_expr(Ast_Node* node, Environment* env) { report_error(Error_Type_Wrong_Number_Of_Arguments); } print(evaluated_arguments->value.pair->first); - printf("\n"); + /* printf("\n"); */ return arguments->value.pair->first; } case Built_In_Read: { @@ -582,7 +729,9 @@ 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); + try { + arguments = eval_arguments(arguments, env, &arguments_length); + } if (operator->type == Ast_Node_Type_Function) { Ast_Node* result; try { @@ -592,9 +741,11 @@ Ast_Node* eval_expr(Ast_Node* node, Environment* env) { } } - default: + default: { + printf("alskjdalskdjaldskjalk"); report_error(Error_Type_Not_A_Function); } + } #undef report_error } diff --git a/src/helpers.c b/src/helpers.c index ddfb08e..dc98085 100644 --- a/src/helpers.c +++ b/src/helpers.c @@ -14,6 +14,35 @@ } \ else label(body,__LINE__): +#define define_array_list(type, name) \ + typedef struct { \ + type* data; \ + int length; \ + int next_index; \ + } name##_Array_List; \ + \ + \ + void append_to_##name##_array_list(name##_Array_List* arraylist, type element) { \ + if (arraylist->next_index == arraylist->length) { \ + arraylist->length *= 2; \ + arraylist->data = \ + (type*)realloc(arraylist->data, arraylist->length * sizeof(type)); \ + } \ + arraylist->data[arraylist->next_index++] = element; \ + } \ + \ + \ + name##_Array_List* create_##name##_array_list(int initial_capacity) { \ + name##_Array_List* ret = new(name##_Array_List); \ + ret->data = (type*)malloc(initial_capacity * sizeof(type)); \ + ret->next_index = 0; \ + ret->length = initial_capacity; \ + return ret; \ + } + +define_array_list(char*, String); + + typedef enum { false, true } bool; int string_equal(char* a, char* b) { diff --git a/src/io.c b/src/io.c index 2668954..b6515e1 100644 --- a/src/io.c +++ b/src/io.c @@ -59,7 +59,7 @@ void print(Ast_Node* node) { printf("%f", node->value.number->value); } break; case (Ast_Node_Type_String): { - printf("\"%s\"", node->value.string->value); + printf("%s", node->value.string->value); } break; case (Ast_Node_Type_Symbol): { printf("%s", node->value.symbol->identifier); diff --git a/src/main.c b/src/main.c index c2275bc..49c494c 100644 --- a/src/main.c +++ b/src/main.c @@ -24,6 +24,13 @@ int interprete_file (char* file_content) { return 1; } Environment* env = create_empty_environment(); + + built_in_load("pre.slime", env); + if (error) { + log_error(); + return 1; + } + Ast_Node* result = create_ast_node_nil(); for (int i = 0; i < program->next_index; ++i) { result = eval_expr(program->data[i], env); @@ -32,8 +39,7 @@ int interprete_file (char* file_content) { return 1; } } - /* print(result); */ - /* printf("\n"); */ + return 0; } @@ -41,6 +47,13 @@ int interprete_stdin () { printf("Welcome to the lispy interpreter.\n"); char* line; Environment* env = create_empty_environment(); + + built_in_load("pre.slime", env); + if (error) { + log_error(); + delete_error(); + } + Ast_Node* parsed, * evaluated; while (true) { printf(">");