| @@ -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)))) | |||||
| @@ -1,2 +1 @@ | |||||
| (define operators (list + - * /)) | (define operators (list + - * /)) | ||||
| (print ((first operators) 1 2 3)) | |||||
| @@ -11,7 +11,7 @@ pushd quickbuild | |||||
| taskkill /F /IM %exeName% > NUL 2> NUL | taskkill /F /IM %exeName% > NUL 2> NUL | ||||
| echo ---------- Compiling ---------- | 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 ( | if %errorlevel% == 0 ( | ||||
| echo. | echo. | ||||
| @@ -1,4 +1,5 @@ | |||||
| struct Ast_Node; | struct Ast_Node; | ||||
| define_array_list(struct Ast_Node*, Ast_Node); | |||||
| typedef enum { | typedef enum { | ||||
| Ast_Node_Type_Nil, | Ast_Node_Type_Nil, | ||||
| @@ -36,13 +37,8 @@ typedef struct { | |||||
| } Pair; | } Pair; | ||||
| typedef struct { | 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; | char** identifiers; | ||||
| int next_index; | int next_index; | ||||
| int length; | int length; | ||||
| @@ -58,8 +54,8 @@ typedef struct { | |||||
| } Keyword_Arguments; | } 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* create_positional_argument_list(int initial_capacity) { | ||||
| Positional_Arguments* ret = new(Positional_Arguments); | 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* create_keyword_argument_list(int initial_capacity) { | ||||
| Keyword_Arguments* ret = new(Keyword_Arguments); | Keyword_Arguments* ret = new(Keyword_Arguments); | ||||
| ret->identifiers = (char**)malloc(initial_capacity * sizeof(char*)); | 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->next_index = 0; | ||||
| ret->length = initial_capacity; | ret->length = initial_capacity; | ||||
| return ret; | return ret; | ||||
| @@ -96,7 +92,7 @@ void append_to_keyword_argument_list(Keyword_Arguments* args, | |||||
| } | } | ||||
| args->identifiers[args->next_index++] = identifier; | 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); | |||||
| } | } | ||||
| @@ -1,3 +1,5 @@ | |||||
| Ast_Node* eval_expr(Ast_Node* node, Environment* env); | |||||
| bool ast_node_equal(Ast_Node* n1, Ast_Node* n2) { | bool ast_node_equal(Ast_Node* n1, Ast_Node* n2) { | ||||
| if (n1 == n2) | if (n1 == n2) | ||||
| return true; | return true; | ||||
| @@ -120,3 +122,24 @@ Ast_Node* built_in_divide(Ast_Node* operands) { | |||||
| } | } | ||||
| return create_ast_node_number(quotient); | 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; | |||||
| } | |||||
| } | |||||
| @@ -60,5 +60,6 @@ Ast_Node* lookup_symbol(Symbol* sym, Environment* env) { | |||||
| return built_in; | return built_in; | ||||
| create_error(Error_Type_Symbol_Not_Defined, create_ast_node_nil()); | create_error(Error_Type_Symbol_Not_Defined, create_ast_node_nil()); | ||||
| printf("%s\n", sym->identifier); | |||||
| return nullptr; | return nullptr; | ||||
| } | } | ||||
| @@ -6,6 +6,9 @@ Ast_Node* apply_arguments_to_function(Ast_Node* arguments, Function* function, E | |||||
| // positional arguments | // positional arguments | ||||
| for (int i = 0; i < function->positional_arguments->next_index; ++i) { | for (int i = 0; i < function->positional_arguments->next_index; ++i) { | ||||
| if (arguments->type == Ast_Node_Type_Pair) { | 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( | define_symbol( | ||||
| create_ast_node_symbol(function->positional_arguments->identifiers[i]), | create_ast_node_symbol(function->positional_arguments->identifiers[i]), | ||||
| arguments->value.pair->first, new_env); | 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; | 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; | Ast_Node* result; | ||||
| try { | try { | ||||
| @@ -26,7 +145,16 @@ Ast_Node* apply_arguments_to_function(Ast_Node* arguments, Function* function, E | |||||
| return result; | 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 | This parses the argument specification of funcitons into their | ||||
| @@ -281,7 +409,9 @@ Ast_Node* eval_expr(Ast_Node* node, Environment* env) { | |||||
| return node; | return node; | ||||
| case Ast_Node_Type_Pair: { | case Ast_Node_Type_Pair: { | ||||
| Ast_Node* operator; | 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 { | try { | ||||
| operator = eval_expr(node->value.pair->first, env); | 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: { | case Built_In_Equal: { | ||||
| return built_in_equals(evaluated_arguments); | 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: { | case Built_In_Pair: { | ||||
| if (arguments_length != 2) { | if (arguments_length != 2) { | ||||
| report_error(Error_Type_Wrong_Number_Of_Arguments); | 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); | report_error(Error_Type_Wrong_Number_Of_Arguments); | ||||
| } | } | ||||
| print(evaluated_arguments->value.pair->first); | print(evaluated_arguments->value.pair->first); | ||||
| printf("\n"); | |||||
| /* printf("\n"); */ | |||||
| return arguments->value.pair->first; | return arguments->value.pair->first; | ||||
| } | } | ||||
| case Built_In_Read: { | 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 | // 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) { | if (operator->type == Ast_Node_Type_Function) { | ||||
| Ast_Node* result; | Ast_Node* result; | ||||
| try { | 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); | report_error(Error_Type_Not_A_Function); | ||||
| } | } | ||||
| } | |||||
| #undef report_error | #undef report_error | ||||
| } | } | ||||
| @@ -14,6 +14,35 @@ | |||||
| } \ | } \ | ||||
| else label(body,__LINE__): | 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; | typedef enum { false, true } bool; | ||||
| int string_equal(char* a, char* b) { | int string_equal(char* a, char* b) { | ||||
| @@ -59,7 +59,7 @@ void print(Ast_Node* node) { | |||||
| printf("%f", node->value.number->value); | printf("%f", node->value.number->value); | ||||
| } break; | } break; | ||||
| case (Ast_Node_Type_String): { | case (Ast_Node_Type_String): { | ||||
| printf("\"%s\"", node->value.string->value); | |||||
| printf("%s", node->value.string->value); | |||||
| } break; | } break; | ||||
| case (Ast_Node_Type_Symbol): { | case (Ast_Node_Type_Symbol): { | ||||
| printf("%s", node->value.symbol->identifier); | printf("%s", node->value.symbol->identifier); | ||||
| @@ -24,6 +24,13 @@ int interprete_file (char* file_content) { | |||||
| return 1; | return 1; | ||||
| } | } | ||||
| Environment* env = create_empty_environment(); | Environment* env = create_empty_environment(); | ||||
| built_in_load("pre.slime", env); | |||||
| if (error) { | |||||
| log_error(); | |||||
| return 1; | |||||
| } | |||||
| Ast_Node* result = create_ast_node_nil(); | Ast_Node* result = create_ast_node_nil(); | ||||
| for (int i = 0; i < program->next_index; ++i) { | for (int i = 0; i < program->next_index; ++i) { | ||||
| result = eval_expr(program->data[i], env); | result = eval_expr(program->data[i], env); | ||||
| @@ -32,8 +39,7 @@ int interprete_file (char* file_content) { | |||||
| return 1; | return 1; | ||||
| } | } | ||||
| } | } | ||||
| /* print(result); */ | |||||
| /* printf("\n"); */ | |||||
| return 0; | return 0; | ||||
| } | } | ||||
| @@ -41,6 +47,13 @@ int interprete_stdin () { | |||||
| printf("Welcome to the lispy interpreter.\n"); | printf("Welcome to the lispy interpreter.\n"); | ||||
| char* line; | char* line; | ||||
| Environment* env = create_empty_environment(); | Environment* env = create_empty_environment(); | ||||
| built_in_load("pre.slime", env); | |||||
| if (error) { | |||||
| log_error(); | |||||
| delete_error(); | |||||
| } | |||||
| Ast_Node* parsed, * evaluated; | Ast_Node* parsed, * evaluated; | ||||
| while (true) { | while (true) { | ||||
| printf(">"); | printf(">"); | ||||