| @@ -31,6 +31,7 @@ | |||||
| ("r" save-and-find-run-script-and-compile "run" :color blue) | ("r" save-and-find-run-script-and-compile "run" :color blue) | ||||
| ("d" save-and-find-debug-script-and-compile "debug" :color blue) | ("d" save-and-find-debug-script-and-compile "debug" :color blue) | ||||
| ("t" save-and-find-test-script-and-compile "test" :color blue) | ("t" save-and-find-test-script-and-compile "test" :color blue) | ||||
| ("o" browse-file-directory "open" :color blue) | |||||
| ("q" nil "quit" :color blue)) | ("q" nil "quit" :color blue)) | ||||
| (define-key context-mode-map (kbd "<f2>") 'hydra-context/body)))))) | (define-key context-mode-map (kbd "<f2>") 'hydra-context/body)))))) | ||||
| @@ -7,3 +7,4 @@ | |||||
| /bin/slime | /bin/slime | ||||
| *.psess | *.psess | ||||
| *.vspx | *.vspx | ||||
| todo.html | |||||
| @@ -1,128 +1,155 @@ | |||||
| (define nil ()) | (define nil ()) | ||||
| (define pe | |||||
| (macro (expr) | |||||
| (print expr) | |||||
| (print " evaluates to -> ") | |||||
| (print (eval expr)) | |||||
| (print " | |||||
| ") | |||||
| nil)) | |||||
| (define defun | |||||
| (macro (@name @params :rest @body) | |||||
| (eval (pair 'define (pair @name (pair (pair 'lambda (pair @params @body)) nil)))))) | |||||
| (define defmacro | |||||
| (macro (@name @params :rest @body) | |||||
| (eval (pair 'define (pair @name (pair (pair 'macro (pair @params @body)) nil)))))) | |||||
| (define apply | |||||
| (lambda (fun seq) | |||||
| (eval (pair fun seq)))) | |||||
| (defmacro pe (@expr) | |||||
| (printf @expr "evaluates to" (eval @expr))) | |||||
| (define defun | |||||
| (macro (name params :rest body) | |||||
| (printf "name" params "params" params "body" body) | |||||
| (define name | |||||
| (lambda (params) body)))) | |||||
| (define when | |||||
| (macro (test :rest body) | |||||
| (if (eval test) | |||||
| (apply prog body) | |||||
| nil))) | |||||
| (define unless | |||||
| (macro (test :rest body) | |||||
| (if (eval test) | |||||
| nil | |||||
| (apply prog body)))) | |||||
| (define nil? | |||||
| (lambda (x) | |||||
| "Checks if the argument is nil." | |||||
| (= x nil))) | |||||
| (define append | |||||
| (lambda (seq elem) | |||||
| (if (nil? seq) | |||||
| nil | |||||
| (if (not (= (type (rest seq)) :pair)) | |||||
| ;; we are on the last element | |||||
| (mutate seq (pair (first seq) elem)) | |||||
| (append (rest seq) elem))))) | |||||
| (define last | |||||
| (lambda (seq) | |||||
| "Returns the last element of the given sequence." | |||||
| (if (nil? seq) | |||||
| seq | |||||
| (if (not (= (type (rest seq)) :pair)) | |||||
| (first seq) | |||||
| (last (rest seq)))))) | |||||
| (define n-times | |||||
| (lambda (times action) | |||||
| (if (<= times 0) | |||||
| nil | |||||
| (prog | |||||
| (eval action) | |||||
| (n-times (- 1 times) action))))) | |||||
| (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 (fun seq) | |||||
| "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? seq) | |||||
| seq | |||||
| (pair (fun (first seq)) | |||||
| (map fun (rest seq)))))) | |||||
| (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." | |||||
| (apply function sequence))) | |||||
| (define reduce-binary | |||||
| (lambda (function sequence) | |||||
| "Takes a sequence and a function as arguments and applies the | |||||
| (defun nil? (x) | |||||
| "Checks if the argument is nil." | |||||
| (= x nil)) | |||||
| (defun number? (x) | |||||
| "Checks if the argument is a number." | |||||
| (= (type x) :number)) | |||||
| (defun symbol? (x) | |||||
| "Checks if the argument is a symbol." | |||||
| (= (type x) :symbol)) | |||||
| (defun keyword? (x) | |||||
| "Checks if the argument is a keyword." | |||||
| (= (type x) :keyword)) | |||||
| (defun pair? (x) | |||||
| "Checks if the argument is a pair." | |||||
| (= (type x) :pair)) | |||||
| (defun string? (x) | |||||
| "Checks if the argument is a string." | |||||
| (= (type x) :string)) | |||||
| (defun dynamic-function? (x) | |||||
| "Checks if the argument is a function." | |||||
| (= (type x) :dynamic-function)) | |||||
| (defun dynamic-macro? (x) | |||||
| "Checks if the argument is a macro." | |||||
| (= (type x) :dynamic-macro)) | |||||
| (defun built-in-function? (x) | |||||
| "Checks if the argument is a built-in function." | |||||
| (= (type x) :built-in-function)) | |||||
| (defun apply (fun seq) | |||||
| "Applies the funciton to the sequence, as in calls the funciton with | |||||
| ithe sequence as arguemens." | |||||
| (eval (pair fun seq))) | |||||
| (defmacro when (@test :rest @body) | |||||
| "Executes the code in :rest if test is true" | |||||
| (if (eval @test) | |||||
| (eval (pair prog @body)) | |||||
| nil)) | |||||
| (defmacro unless (@test :rest @body) | |||||
| "Executes the code in :rest if test is false." | |||||
| (if (eval @test) | |||||
| nil | |||||
| (eval (pair prog @body)))) | |||||
| (defun end (seq) | |||||
| "Returns the last pair in the sqeuence." | |||||
| (if (or (nil? seq) (not (pair? (rest seq)))) | |||||
| seq | |||||
| (end (rest seq)))) | |||||
| (defun last (seq) | |||||
| "Returns the (first) of the last (pair) of the given sequence." | |||||
| (first (end seq))) | |||||
| (defun extend (seq elem) | |||||
| "Extends a list with the given element, by putting it in | |||||
| the (rest) of the last element of the sequence." | |||||
| (when (pair? seq) | |||||
| (define e (end seq)) | |||||
| (mutate e (pair (first e) elem))) | |||||
| seq) | |||||
| (defun append (seq elem) | |||||
| (extend seq (pair elem nil))) | |||||
| (defmacro n-times (@times @action) | |||||
| (unless (<= (eval @times) 0) | |||||
| (eval @action) | |||||
| (macro-define @args (pair (pair - (pair (eval @times) (pair 1 nil))) (pair @action nil))) | |||||
| ;; [o|o] --------------------------> [o|o] -> nil | |||||
| ;; | | | |||||
| ;; V V | |||||
| ;; [o|o] -> [o|o] -> [o|o]-> nil action | |||||
| ;; | | | | |||||
| ;; V V V | |||||
| ;; - times 1 | |||||
| (eval (pair n-times @args)))) | |||||
| (defun range (: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)) | |||||
| (defun map (fun seq) | |||||
| "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? seq) | |||||
| seq | |||||
| (pair (fun (first seq)) | |||||
| (map fun (rest seq))))) | |||||
| (defun reduce (fun seq) | |||||
| "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 fun seq))) | |||||
| (defun reduce-binary (fun seq) | |||||
| "Takes a sequence and a function as arguments and applies the | |||||
| function to the argument sequence. reduce-binary applies the | function to the argument sequence. reduce-binary applies the | ||||
| arguments `pair-wise' which means it works with binary functions | arguments `pair-wise' which means it works with binary functions | ||||
| as compared to `reduce'." | 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-quoted | |||||
| (macro (:keys sep :defaults-to " " end :defaults-to "\n" :rest args) | |||||
| (if (nil? args) | |||||
| (prog (print (eval end)) nil) | |||||
| (prog | |||||
| (print (first args)) | |||||
| (when (not (nil? (rest args))) (print (eval sep))) | |||||
| (define command-args (list :sep (eval sep) :end (eval end))) | |||||
| (append command-args (rest args)) | |||||
| (apply printf-quoted command-args))))) | |||||
| (define printf | |||||
| (lambda (:keys sep :defaults-to " " end :defaults-to "\n" :rest args) | |||||
| (define command-args (list :sep (eval sep) :end (eval end))) | |||||
| (append command-args args) | |||||
| (apply printf-quoted command-args))) | |||||
| (if (nil? (rest seq)) | |||||
| (first seq) | |||||
| (fun (first seq) | |||||
| (reduce-binary fun (rest seq))))) | |||||
| (defun filter (fun seq) | |||||
| (if (nil? seq) | |||||
| nil | |||||
| (if (fun (first seq)) | |||||
| (pair (first seq) | |||||
| (filter fun (rest seq))) | |||||
| (filter fun (rest seq))))) | |||||
| (defmacro printf-quoted (:keys @sep :defaults-to " " @end :defaults-to "\n" :rest @args) | |||||
| (if (nil? @args) | |||||
| (prog (print (eval @end)) nil) | |||||
| (prog | |||||
| (print (first @args)) | |||||
| (when (not (nil? (rest @args))) (print (eval @sep))) | |||||
| (eval | |||||
| (pair printf-quoted | |||||
| (extend (list :@sep (eval @sep) :@end (eval @end)) (rest @args))))))) | |||||
| (defun printf (:keys sep :defaults-to " " end :defaults-to "\n" :rest args) | |||||
| (define command-args (extend (list :@sep (eval sep) :@end (eval end)) args)) | |||||
| (eval (pair printf-quoted command-args))) | |||||
| @@ -1,4 +1,9 @@ | |||||
| (define l '(1 2 3 4)) | |||||
| (define r (rest l)) | |||||
| (mutate r (list 100 200)) | |||||
| (print l) | |||||
| (when 1 (breakpoint)) | |||||
| ;; (if (eval 1) | |||||
| ;; (apply prog ((breakpoint))) | |||||
| ;; nil)) | |||||
| ;; (if (eval 1) | |||||
| ;; (eval (pair prog ((breakpoint))))) | |||||
| ;; nil)) | |||||
| @@ -125,6 +125,9 @@ typedef enum { | |||||
| Built_In_Rest, | Built_In_Rest, | ||||
| Built_In_Load, | Built_In_Load, | ||||
| Built_In_Define, | Built_In_Define, | ||||
| Built_In_Let, | |||||
| Built_In_Macro_Define, | |||||
| Built_In_Breakpoint, | |||||
| Built_In_Mutate, | Built_In_Mutate, | ||||
| Built_In_Lambda, | Built_In_Lambda, | ||||
| Built_In_Macro, | Built_In_Macro, | ||||
| @@ -147,6 +150,8 @@ char* Built_In_Name_to_string(Built_In_Name name) { | |||||
| case Built_In_Addition: return "+"; | case Built_In_Addition: return "+"; | ||||
| case Built_In_And: return "and"; | case Built_In_And: return "and"; | ||||
| case Built_In_Define: return "define"; | case Built_In_Define: return "define"; | ||||
| case Built_In_Macro_Define: return "macro-define"; | |||||
| case Built_In_Breakpoint: return "breakpoint"; | |||||
| case Built_In_Division: return "/"; | case Built_In_Division: return "/"; | ||||
| case Built_In_Equal: return "="; | case Built_In_Equal: return "="; | ||||
| case Built_In_Eval: return "eval"; | case Built_In_Eval: return "eval"; | ||||
| @@ -157,6 +162,7 @@ char* Built_In_Name_to_string(Built_In_Name name) { | |||||
| case Built_In_If: return "if"; | case Built_In_If: return "if"; | ||||
| case Built_In_Info: return "info"; | case Built_In_Info: return "info"; | ||||
| case Built_In_Lambda: return "lambda"; | case Built_In_Lambda: return "lambda"; | ||||
| case Built_In_Let: return "let"; | |||||
| case Built_In_Less: return "<"; | case Built_In_Less: return "<"; | ||||
| case Built_In_Less_Equal: return "<="; | case Built_In_Less_Equal: return "<="; | ||||
| case Built_In_List: return "list"; | case Built_In_List: return "list"; | ||||
| @@ -248,36 +254,39 @@ Ast_Node* create_ast_node_keyword(char* keyword) { | |||||
| Ast_Node* create_ast_node_built_in_function(char* name) { | Ast_Node* create_ast_node_built_in_function(char* name) { | ||||
| Built_In_Name type; | Built_In_Name type; | ||||
| if (string_equal(name, "+")) type = Built_In_Addition; | |||||
| else if (string_equal(name, "-")) type = Built_In_Subtraction; | |||||
| else if (string_equal(name, "*")) type = Built_In_Multiplication; | |||||
| else if (string_equal(name, "/")) type = Built_In_Division; | |||||
| else if (string_equal(name, "=")) type = Built_In_Equal; | |||||
| else if (string_equal(name, ">")) type = Built_In_Greater; | |||||
| else if (string_equal(name, ">=")) type = Built_In_Greater_Equal; | |||||
| else if (string_equal(name, "<")) type = Built_In_Less; | |||||
| else if (string_equal(name, "<=")) type = Built_In_Less_Equal; | |||||
| else if (string_equal(name, "if")) type = Built_In_If; | |||||
| else if (string_equal(name, "and")) type = Built_In_And; | |||||
| else if (string_equal(name, "or")) type = Built_In_Or; | |||||
| else if (string_equal(name, "not")) type = Built_In_Not; | |||||
| else if (string_equal(name, "pair")) type = Built_In_Pair; | |||||
| else if (string_equal(name, "first")) type = Built_In_First; | |||||
| else if (string_equal(name, "rest")) type = Built_In_Rest; | |||||
| else if (string_equal(name, "load")) type = Built_In_Load; | |||||
| else if (string_equal(name, "define")) type = Built_In_Define; | |||||
| else if (string_equal(name, "mutate")) type = Built_In_Mutate; | |||||
| else if (string_equal(name, "lambda")) type = Built_In_Lambda; | |||||
| else if (string_equal(name, "macro")) type = Built_In_Macro; | |||||
| else if (string_equal(name, "eval")) type = Built_In_Eval; | |||||
| else if (string_equal(name, "quote")) type = Built_In_Quote; | |||||
| else if (string_equal(name, "prog")) type = Built_In_Prog; | |||||
| else if (string_equal(name, "list")) type = Built_In_List; | |||||
| else if (string_equal(name, "print")) type = Built_In_Print; | |||||
| else if (string_equal(name, "read")) type = Built_In_Read; | |||||
| else if (string_equal(name, "info")) type = Built_In_Info; | |||||
| else if (string_equal(name, "type")) type = Built_In_Type; | |||||
| else if (string_equal(name, "exit")) type = Built_In_Exit; | |||||
| if (string_equal(name, "+")) type = Built_In_Addition; | |||||
| else if (string_equal(name, "-")) type = Built_In_Subtraction; | |||||
| else if (string_equal(name, "*")) type = Built_In_Multiplication; | |||||
| else if (string_equal(name, "/")) type = Built_In_Division; | |||||
| else if (string_equal(name, "=")) type = Built_In_Equal; | |||||
| else if (string_equal(name, ">")) type = Built_In_Greater; | |||||
| else if (string_equal(name, ">=")) type = Built_In_Greater_Equal; | |||||
| else if (string_equal(name, "<")) type = Built_In_Less; | |||||
| else if (string_equal(name, "<=")) type = Built_In_Less_Equal; | |||||
| else if (string_equal(name, "if")) type = Built_In_If; | |||||
| else if (string_equal(name, "and")) type = Built_In_And; | |||||
| else if (string_equal(name, "or")) type = Built_In_Or; | |||||
| else if (string_equal(name, "not")) type = Built_In_Not; | |||||
| else if (string_equal(name, "pair")) type = Built_In_Pair; | |||||
| else if (string_equal(name, "first")) type = Built_In_First; | |||||
| else if (string_equal(name, "rest")) type = Built_In_Rest; | |||||
| else if (string_equal(name, "load")) type = Built_In_Load; | |||||
| else if (string_equal(name, "let")) type = Built_In_Let; | |||||
| else if (string_equal(name, "define")) type = Built_In_Define; | |||||
| else if (string_equal(name, "macro-define")) type = Built_In_Macro_Define; | |||||
| else if (string_equal(name, "breakpoint")) type = Built_In_Breakpoint; | |||||
| else if (string_equal(name, "mutate")) type = Built_In_Mutate; | |||||
| else if (string_equal(name, "lambda")) type = Built_In_Lambda; | |||||
| else if (string_equal(name, "macro")) type = Built_In_Macro; | |||||
| else if (string_equal(name, "eval")) type = Built_In_Eval; | |||||
| else if (string_equal(name, "quote")) type = Built_In_Quote; | |||||
| else if (string_equal(name, "prog")) type = Built_In_Prog; | |||||
| else if (string_equal(name, "list")) type = Built_In_List; | |||||
| else if (string_equal(name, "print")) type = Built_In_Print; | |||||
| else if (string_equal(name, "read")) type = Built_In_Read; | |||||
| else if (string_equal(name, "info")) type = Built_In_Info; | |||||
| else if (string_equal(name, "type")) type = Built_In_Type; | |||||
| else if (string_equal(name, "exit")) type = Built_In_Exit; | |||||
| else return nullptr; | else return nullptr; | ||||
| Ast_Node* node = new(Ast_Node); | Ast_Node* node = new(Ast_Node); | ||||
| @@ -56,6 +56,78 @@ Ast_Node* built_in_equals(Ast_Node* operands) { | |||||
| return create_ast_node_number(1); | return create_ast_node_number(1); | ||||
| } | } | ||||
| Ast_Node* built_in_greater(Ast_Node* operands) { | |||||
| double last_number = strtod("Inf", NULL); | |||||
| while (operands->type == Ast_Node_Type_Pair) { | |||||
| try { | |||||
| assert_type(operands->value.pair->first, Ast_Node_Type_Number); | |||||
| } | |||||
| if (operands->value.pair->first->value.number->value >= last_number) | |||||
| return create_ast_node_nil(); | |||||
| last_number = operands->value.pair->first->value.number->value; | |||||
| operands = operands->value.pair->rest; | |||||
| } | |||||
| return create_ast_node_number(1); | |||||
| } | |||||
| Ast_Node* built_in_greater_equal(Ast_Node* operands) { | |||||
| double last_number = strtod("Inf", NULL); | |||||
| while (operands->type == Ast_Node_Type_Pair) { | |||||
| try { | |||||
| assert_type(operands->value.pair->first, Ast_Node_Type_Number); | |||||
| } | |||||
| if (operands->value.pair->first->value.number->value > last_number) | |||||
| return create_ast_node_nil(); | |||||
| last_number = operands->value.pair->first->value.number->value; | |||||
| operands = operands->value.pair->rest; | |||||
| } | |||||
| return create_ast_node_number(1); | |||||
| } | |||||
| Ast_Node* built_in_less(Ast_Node* operands) { | |||||
| double last_number = strtod("-Inf", NULL); | |||||
| while (operands->type == Ast_Node_Type_Pair) { | |||||
| try { | |||||
| assert_type(operands->value.pair->first, Ast_Node_Type_Number); | |||||
| } | |||||
| if (operands->value.pair->first->value.number->value <= last_number) | |||||
| return create_ast_node_nil(); | |||||
| last_number = operands->value.pair->first->value.number->value; | |||||
| operands = operands->value.pair->rest; | |||||
| } | |||||
| return create_ast_node_number(1); | |||||
| } | |||||
| Ast_Node* built_in_less_equal(Ast_Node* operands) { | |||||
| double last_number = strtod("-Inf", NULL); | |||||
| while (operands->type == Ast_Node_Type_Pair) { | |||||
| try { | |||||
| assert_type(operands->value.pair->first, Ast_Node_Type_Number); | |||||
| } | |||||
| if (operands->value.pair->first->value.number->value < last_number) | |||||
| return create_ast_node_nil(); | |||||
| last_number = operands->value.pair->first->value.number->value; | |||||
| operands = operands->value.pair->rest; | |||||
| } | |||||
| return create_ast_node_number(1); | |||||
| } | |||||
| Ast_Node* built_in_add(Ast_Node* operands) { | Ast_Node* built_in_add(Ast_Node* operands) { | ||||
| double sum = 0; | double sum = 0; | ||||
| while (operands->type == Ast_Node_Type_Pair) { | while (operands->type == Ast_Node_Type_Pair) { | ||||
| @@ -1,6 +1,13 @@ | |||||
| typedef enum { | |||||
| Environment_Type_Let, | |||||
| Environment_Type_Lambda, | |||||
| Environment_Type_Macro, | |||||
| } Environment_Type; | |||||
| struct Environment { | struct Environment { | ||||
| struct Environment* parent; | struct Environment* parent; | ||||
| Environment_Type type; | |||||
| int capacity; | int capacity; | ||||
| int next_index; | int next_index; | ||||
| // TODO(Felix): Use a hashmap here. | // TODO(Felix): Use a hashmap here. | ||||
| @@ -10,12 +17,12 @@ struct Environment { | |||||
| typedef struct Environment Environment; | typedef struct Environment Environment; | ||||
| Environment* create_child_environment(Environment* parent) { | |||||
| Environment* create_child_environment(Environment* parent, Environment_Type type) { | |||||
| Environment* env = new(Environment); | Environment* env = new(Environment); | ||||
| int start_capacity = 16; | int start_capacity = 16; | ||||
| env->type = type; | |||||
| env->parent = parent; | env->parent = parent; | ||||
| env->capacity = start_capacity; | env->capacity = start_capacity; | ||||
| env->next_index = 0; | env->next_index = 0; | ||||
| @@ -25,11 +32,20 @@ Environment* create_child_environment(Environment* parent) { | |||||
| return env; | return env; | ||||
| } | } | ||||
| Environment* create_empty_environment() { | |||||
| return create_child_environment(nullptr); | |||||
| Environment* create_empty_environment(Environment_Type type) { | |||||
| return create_child_environment(nullptr, type); | |||||
| } | } | ||||
| void define_symbol(Ast_Node* symbol, Ast_Node* value, Environment* env) { | void define_symbol(Ast_Node* symbol, Ast_Node* value, Environment* env) { | ||||
| if (env->type == Environment_Type_Macro) { | |||||
| // NOTE(Felix): we know we have a parent because every | |||||
| // environment has a parent except the top level environment. | |||||
| // However the top level environment is not a let-environment, | |||||
| // so we would not land here | |||||
| define_symbol(symbol, value, env->parent); | |||||
| return; | |||||
| } | |||||
| // NOTE(Felix): right now we are simply adding the symol at the | // NOTE(Felix): right now we are simply adding the symol at the | ||||
| // back of the list without checking if it already exists but are | // back of the list without checking if it already exists but are | ||||
| // also searching for thesymbol from the back, so we will find the | // also searching for thesymbol from the back, so we will find the | ||||
| @@ -47,19 +63,116 @@ void define_symbol(Ast_Node* symbol, Ast_Node* value, Environment* env) { | |||||
| ++env->next_index; | ++env->next_index; | ||||
| } | } | ||||
| Ast_Node* lookup_symbol(Symbol* sym, Environment* env) { | |||||
| void define_macro_symbol(Ast_Node* symbol, Ast_Node* value, Environment* env) { | |||||
| if (!env->type == Environment_Type_Macro) { | |||||
| create_error(Error_Type_Unknown_Error, symbol); | |||||
| return; | |||||
| } | |||||
| env->type = Environment_Type_Lambda; | |||||
| define_symbol(symbol, value, env); | |||||
| env->type = Environment_Type_Macro; | |||||
| } | |||||
| void print_environment(Environment* env); | |||||
| Ast_Node* lookup_symbol_in_this_envt(Symbol* sym, Environment* env) { | |||||
| for (int i = env->next_index - 1; i >= 0; --i) | for (int i = env->next_index - 1; i >= 0; --i) | ||||
| if (string_equal(env->keys[i], sym->identifier)) | if (string_equal(env->keys[i], sym->identifier)) | ||||
| return env->values[i]; | return env->values[i]; | ||||
| return nullptr; | |||||
| } | |||||
| Ast_Node* lookup_symbol_from_lambda_env(Symbol* sym, Environment* env) { | |||||
| Ast_Node* result; | |||||
| do { | |||||
| if (env->type != Environment_Type_Lambda) { | |||||
| result = lookup_symbol_in_this_envt(sym, env); | |||||
| if (result) return result; | |||||
| } | |||||
| env = env->parent; | |||||
| } while (env); | |||||
| return nullptr; | |||||
| } | |||||
| Ast_Node* lookup_symbol_from_let_or_macro_env(Symbol* sym, Environment* env) { | |||||
| Ast_Node* result; | |||||
| do { | |||||
| result = lookup_symbol_in_this_envt(sym, env); | |||||
| if (result) return result; | |||||
| if (env->type == Environment_Type_Lambda) | |||||
| break; | |||||
| env = env->parent; | |||||
| } while (env); | |||||
| if (env->parent) | |||||
| return lookup_symbol(sym, env->parent); | |||||
| if (env) { | |||||
| do { | |||||
| if (env->type != Environment_Type_Lambda) { | |||||
| result = lookup_symbol_in_this_envt(sym, env); | |||||
| if (result) return result; | |||||
| } | |||||
| env = env->parent; | |||||
| } while (env); | |||||
| } | |||||
| return nullptr; | |||||
| } | |||||
| Ast_Node* lookup_symbol(Symbol* sym, Environment* env) { | |||||
| // first check current environment | |||||
| Ast_Node* result; | |||||
| result = lookup_symbol_in_this_envt(sym, env); | |||||
| if (result) | |||||
| return result; | |||||
| if (env->parent) { | |||||
| if (env->type == Environment_Type_Lambda) { | |||||
| result = lookup_symbol_from_lambda_env(sym, env->parent); | |||||
| } else { | |||||
| result = lookup_symbol_from_let_or_macro_env(sym, env->parent); | |||||
| } | |||||
| if (result) | |||||
| return result; | |||||
| } | |||||
| Ast_Node* built_in = create_ast_node_built_in_function(sym->identifier); | |||||
| if (built_in) | |||||
| return built_in; | |||||
| result = create_ast_node_built_in_function(sym->identifier); | |||||
| if (result) | |||||
| return result; | |||||
| 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); | printf("%s\n", sym->identifier); | ||||
| return nullptr; | return nullptr; | ||||
| } | } | ||||
| void print_indent(int indent) { | |||||
| for (int i = 0; i < indent; ++i) { | |||||
| printf(" "); | |||||
| } | |||||
| } | |||||
| void print_environment_indent(Environment* env, int indent) { | |||||
| for (int i = 0; i < env->next_index; ++i) { | |||||
| print_indent(indent); | |||||
| printf("%s -> ", env->keys[i]); | |||||
| print(env->values[i]); | |||||
| printf("\n"); | |||||
| } | |||||
| if (env->parent) { | |||||
| print_indent(indent); | |||||
| printf("parent"); | |||||
| if (env->parent->type == Environment_Type_Lambda) | |||||
| printf(" (lambda)"); | |||||
| else if (env->parent->type == Environment_Type_Macro) | |||||
| printf(" (macro)"); | |||||
| else if (env->parent->type == Environment_Type_Let) | |||||
| printf(" (let)"); | |||||
| printf(":\n"); | |||||
| print_environment_indent(env->parent, indent+4); | |||||
| } | |||||
| } | |||||
| void print_environment(Environment* env) { | |||||
| printf("\n=== Environment ===\n"); | |||||
| print_environment_indent(env, 0); | |||||
| } | |||||
| @@ -31,7 +31,7 @@ void delete_error() { | |||||
| void create_error(Error_Type type, Ast_Node* location) { | void create_error(Error_Type type, Ast_Node* location) { | ||||
| delete_error(); | delete_error(); | ||||
| error = new(Error); | error = new(Error); | ||||
| error->type = type; | error->type = type; | ||||
| error->location = location; | error->location = location; | ||||
| @@ -1,7 +1,12 @@ | |||||
| Ast_Node* eval_expr(Ast_Node* node, Environment* env); | Ast_Node* eval_expr(Ast_Node* node, Environment* env); | ||||
| Ast_Node* apply_arguments_to_function(Ast_Node* arguments, Function* function, Environment* parent) { | Ast_Node* apply_arguments_to_function(Ast_Node* arguments, Function* function, Environment* parent) { | ||||
| Environment* new_env = create_child_environment(parent); | |||||
| // NOTE(Felix): if it is a macro, we will set it later, so we can | |||||
| // use the default "define_symbol" method here instead of | |||||
| // switching between "define_symbol" and "define_macro_symbol" all | |||||
| // the time | |||||
| Environment* new_env = create_child_environment(parent, Environment_Type_Lambda); | |||||
| // 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) { | ||||
| @@ -137,10 +142,16 @@ Ast_Node* apply_arguments_to_function(Ast_Node* arguments, Function* function, E | |||||
| eval_time: | eval_time: | ||||
| Ast_Node* result; | Ast_Node* result; | ||||
| // don't have to check every time if it is macro environment or | |||||
| // not | |||||
| if (function->is_macro) | |||||
| new_env->type = Environment_Type_Macro; | |||||
| try { | try { | ||||
| result = eval_expr(function->body, new_env); | result = eval_expr(function->body, new_env); | ||||
| } | } | ||||
| free(new_env); | |||||
| return result; | return result; | ||||
| } | } | ||||
| @@ -354,7 +365,7 @@ Ast_Node* eval_expr(Ast_Node* node, Environment* env) { | |||||
| create_error(_type, node); \ | create_error(_type, node); \ | ||||
| return nullptr; \ | return nullptr; \ | ||||
| } | } | ||||
| if (error) | if (error) | ||||
| return nullptr; | return nullptr; | ||||
| @@ -392,6 +403,13 @@ Ast_Node* eval_expr(Ast_Node* node, Environment* env) { | |||||
| // check for special form | // check for special form | ||||
| if (operator->type == Ast_Node_Type_Built_In_Function) { | if (operator->type == Ast_Node_Type_Built_In_Function) { | ||||
| switch (operator->value.built_in_function->type) { | switch (operator->value.built_in_function->type) { | ||||
| case Built_In_Breakpoint: { | |||||
| print_environment(env); | |||||
| #ifdef _DEBUG | |||||
| __debugbreak(); | |||||
| #endif | |||||
| return create_ast_node_nil(); | |||||
| } | |||||
| case Built_In_Macro: | case Built_In_Macro: | ||||
| case Built_In_Lambda: { | case Built_In_Lambda: { | ||||
| /* | /* | ||||
| @@ -420,6 +438,10 @@ Ast_Node* eval_expr(Ast_Node* node, Environment* env) { | |||||
| try { | try { | ||||
| parse_argument_list(arguments->value.pair->first, function); | parse_argument_list(arguments->value.pair->first, function); | ||||
| } | } | ||||
| } else { | |||||
| function->positional_arguments = create_positional_argument_list(1); | |||||
| function->keyword_arguments = create_keyword_argument_list(1); | |||||
| function->rest_argument = nullptr; | |||||
| } | } | ||||
| arguments = arguments->value.pair->rest; | arguments = arguments->value.pair->rest; | ||||
| @@ -440,6 +462,63 @@ Ast_Node* eval_expr(Ast_Node* node, Environment* env) { | |||||
| ret->value.function = function; | ret->value.function = function; | ||||
| return ret; | return ret; | ||||
| } | } | ||||
| case Built_In_Let: { | |||||
| // (let ((a 10)(b 20)) (body1) (body2)) | |||||
| try { | |||||
| arguments_length = list_length(arguments); | |||||
| } | |||||
| if (arguments_length < 1) | |||||
| report_error(Error_Type_Wrong_Number_Of_Arguments); | |||||
| Environment* let_env = create_child_environment(env, Environment_Type_Let); | |||||
| Ast_Node* bindings = arguments->value.pair->first; | |||||
| while (true) { | |||||
| if (bindings->type == Ast_Node_Type_Nil) { | |||||
| break; | |||||
| } else if (bindings->type != Ast_Node_Type_Pair) { | |||||
| report_error(Error_Type_Ill_Formed_Arguments); | |||||
| } | |||||
| Ast_Node* sym = bindings->value.pair->first->value.pair->first; | |||||
| if(sym->type != Ast_Node_Type_Symbol) { | |||||
| report_error(Error_Type_Ill_Formed_Arguments); | |||||
| } | |||||
| Ast_Node* rest_sym = bindings->value.pair->first->value.pair->rest; | |||||
| if (rest_sym->type != Ast_Node_Type_Pair) { | |||||
| report_error(Error_Type_Ill_Formed_Arguments); | |||||
| } | |||||
| if (rest_sym->value.pair->rest->type != Ast_Node_Type_Nil) { | |||||
| report_error(Error_Type_Ill_Formed_Arguments); | |||||
| } | |||||
| Ast_Node* value = rest_sym->value.pair->first; | |||||
| define_symbol(sym, value, let_env); | |||||
| bindings = bindings->value.pair->rest; | |||||
| } | |||||
| arguments = arguments->value.pair->rest; | |||||
| Ast_Node* evaluated_arguments; | |||||
| try { | |||||
| evaluated_arguments = eval_arguments(arguments, let_env, &arguments_length); | |||||
| } | |||||
| if (evaluated_arguments->type == Ast_Node_Type_Nil) | |||||
| return evaluated_arguments; | |||||
| // skip to the last evaluated operand and return it, | |||||
| // we use eval_arguments here instead of doing it | |||||
| // manually, because we want to increase code reuse, | |||||
| // but at the cost that we have to find the end of the | |||||
| // list again | |||||
| while (evaluated_arguments->value.pair->rest->type == Ast_Node_Type_Pair) { | |||||
| evaluated_arguments = evaluated_arguments->value.pair->rest; | |||||
| } | |||||
| return evaluated_arguments->value.pair->first; | |||||
| } | |||||
| case Built_In_And: { | case Built_In_And: { | ||||
| bool result = true; | bool result = true; | ||||
| while (arguments->type != Ast_Node_Type_Nil) { | while (arguments->type != Ast_Node_Type_Nil) { | ||||
| @@ -522,6 +601,7 @@ Ast_Node* eval_expr(Ast_Node* node, Environment* env) { | |||||
| } | } | ||||
| return arguments->value.pair->first; | return arguments->value.pair->first; | ||||
| } | } | ||||
| case Built_In_Macro_Define: | |||||
| case Built_In_Define: { | case Built_In_Define: { | ||||
| try { | try { | ||||
| arguments_length = list_length(arguments); | arguments_length = list_length(arguments); | ||||
| @@ -531,15 +611,32 @@ Ast_Node* eval_expr(Ast_Node* node, Environment* env) { | |||||
| } | } | ||||
| Ast_Node* symbol = arguments->value.pair->first; | Ast_Node* symbol = arguments->value.pair->first; | ||||
| if (symbol->type != Ast_Node_Type_Symbol) | |||||
| if (symbol->type == Ast_Node_Type_Pair) { | |||||
| try { | |||||
| symbol = eval_expr(symbol, env); | |||||
| } | |||||
| } | |||||
| if (symbol->type != Ast_Node_Type_Symbol) { | |||||
| report_error(Error_Type_Type_Missmatch); | report_error(Error_Type_Type_Missmatch); | ||||
| } | |||||
| Ast_Node* value = arguments->value.pair->rest->value.pair->first; | Ast_Node* value = arguments->value.pair->rest->value.pair->first; | ||||
| /* print(value); */ | |||||
| try { | try { | ||||
| value = eval_expr(value, env); | value = eval_expr(value, env); | ||||
| } | } | ||||
| /* print(value); */ | |||||
| if (operator->value.built_in_function->type == Built_In_Macro_Define) { | |||||
| /* printf("Defining %s in the macro env to be ", symbol->value.symbol->identifier); */ | |||||
| /* print(value); */ | |||||
| define_symbol(symbol, value, env); | |||||
| define_macro_symbol(symbol, value, env); | |||||
| } | |||||
| else | |||||
| define_symbol(symbol, value, env); | |||||
| return value; | return value; | ||||
| } | } | ||||
| @@ -569,6 +666,18 @@ 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_Greater: { | |||||
| return built_in_greater(evaluated_arguments); | |||||
| } | |||||
| case Built_In_Greater_Equal: { | |||||
| return built_in_greater_equal(evaluated_arguments); | |||||
| } | |||||
| case Built_In_Less: { | |||||
| return built_in_less(evaluated_arguments); | |||||
| } | |||||
| case Built_In_Less_Equal: { | |||||
| return built_in_less_equal(evaluated_arguments); | |||||
| } | |||||
| case Built_In_Mutate: { | case Built_In_Mutate: { | ||||
| if (arguments_length != 2) | if (arguments_length != 2) | ||||
| report_error(Error_Type_Wrong_Number_Of_Arguments); | report_error(Error_Type_Wrong_Number_Of_Arguments); | ||||
| @@ -685,7 +794,11 @@ Ast_Node* eval_expr(Ast_Node* node, Environment* env) { | |||||
| Ast_Node_Type type = evaluated_arguments->value.pair->first->type; | Ast_Node_Type type = evaluated_arguments->value.pair->first->type; | ||||
| switch (type) { | switch (type) { | ||||
| case Ast_Node_Type_Built_In_Function: return create_ast_node_keyword("built-in-function"); | case Ast_Node_Type_Built_In_Function: return create_ast_node_keyword("built-in-function"); | ||||
| case Ast_Node_Type_Function: return create_ast_node_keyword("dynamic-function"); | |||||
| case Ast_Node_Type_Function: { | |||||
| if (evaluated_arguments->value.pair->first->value.function->is_macro) | |||||
| return create_ast_node_keyword("dynamic-macro"); | |||||
| return create_ast_node_keyword("dynamic-function"); | |||||
| } | |||||
| case Ast_Node_Type_Keyword: return create_ast_node_keyword("keyword"); | case Ast_Node_Type_Keyword: return create_ast_node_keyword("keyword"); | ||||
| case Ast_Node_Type_Nil: return create_ast_node_keyword("nil"); | case Ast_Node_Type_Nil: return create_ast_node_keyword("nil"); | ||||
| case Ast_Node_Type_Number: return create_ast_node_keyword("number"); | case Ast_Node_Type_Number: return create_ast_node_keyword("number"); | ||||
| @@ -715,8 +828,6 @@ Ast_Node* eval_expr(Ast_Node* node, Environment* env) { | |||||
| } | } | ||||
| // assume it's lambda function and evaluate the arguments | |||||
| if (operator->type == Ast_Node_Type_Function) { | if (operator->type == Ast_Node_Type_Function) { | ||||
| if (!operator->value.function->is_macro) { | if (!operator->value.function->is_macro) { | ||||
| try { | try { | ||||
| @@ -732,6 +843,9 @@ Ast_Node* eval_expr(Ast_Node* node, Environment* env) { | |||||
| } | } | ||||
| } | } | ||||
| default: { | default: { | ||||
| #ifdef _DEBUG | |||||
| __debugbreak(); | |||||
| #endif | |||||
| report_error(Error_Type_Not_A_Function); | report_error(Error_Type_Not_A_Function); | ||||
| } | } | ||||
| } | } | ||||
| @@ -103,7 +103,8 @@ char* read_entire_file (char* filename) { | |||||
| /* Read the entire file into memory. */ | /* Read the entire file into memory. */ | ||||
| size_t newLen = fread(fileContent, sizeof(char), bufsize, fp); | size_t newLen = fread(fileContent, sizeof(char), bufsize, fp); | ||||
| /* fileContent[bufsize-5] = '\0'; */ | |||||
| fileContent[newLen] = '\0'; | |||||
| if ( ferror( fp ) != 0 ) { | if ( ferror( fp ) != 0 ) { | ||||
| fputs("Error reading file", stderr); | fputs("Error reading file", stderr); | ||||
| } | } | ||||
| @@ -68,8 +68,10 @@ void print(Ast_Node* node) { | |||||
| printf(":%s", node->value.keyword->identifier); | printf(":%s", node->value.keyword->identifier); | ||||
| } break; | } break; | ||||
| case (Ast_Node_Type_Function): { | case (Ast_Node_Type_Function): { | ||||
| printf("[lambda]"); | |||||
| /* print(node->value.function->body); */ | |||||
| if (node->value.function->is_macro) | |||||
| printf("[macro]"); | |||||
| else | |||||
| printf("[lambda]"); | |||||
| } break; | } break; | ||||
| case (Ast_Node_Type_Built_In_Function): { | case (Ast_Node_Type_Built_In_Function): { | ||||
| printf("[built-in-function %s]", Built_In_Name_to_string(node->value.built_in_function->type)); | printf("[built-in-function %s]", Built_In_Name_to_string(node->value.built_in_function->type)); | ||||
| @@ -23,7 +23,7 @@ int interprete_file (char* file_content) { | |||||
| log_error(); | log_error(); | ||||
| return 1; | return 1; | ||||
| } | } | ||||
| Environment* env = create_empty_environment(); | |||||
| Environment* env = create_empty_environment(Environment_Type_Let); | |||||
| built_in_load("pre.slime", env); | built_in_load("pre.slime", env); | ||||
| if (error) { | if (error) { | ||||
| @@ -46,7 +46,7 @@ int interprete_file (char* file_content) { | |||||
| int interprete_stdin () { | 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(Environment_Type_Let); | |||||
| built_in_load("pre.slime", env); | built_in_load("pre.slime", env); | ||||
| if (error) { | if (error) { | ||||
| @@ -27,6 +27,7 @@ void eat_comment_line(char* text, int* index_in_text) { | |||||
| do { | do { | ||||
| ++(*index_in_text); | ++(*index_in_text); | ||||
| } while (text[(*index_in_text)] != '\n' && | } while (text[(*index_in_text)] != '\n' && | ||||
| text[(*index_in_text)] != '\r' && | |||||
| text[(*index_in_text)] != '\0'); | text[(*index_in_text)] != '\0'); | ||||
| } | } | ||||
| @@ -34,7 +35,8 @@ void eat_whitespace(char* text, int* index_in_text) { | |||||
| // skip whitespaces | // skip whitespaces | ||||
| while (text[(*index_in_text)] == ' ' || | while (text[(*index_in_text)] == ' ' || | ||||
| text[(*index_in_text)] == '\t' || | text[(*index_in_text)] == '\t' || | ||||
| text[(*index_in_text)] == '\n') { | |||||
| text[(*index_in_text)] == '\n' || | |||||
| text[(*index_in_text)] == '\r') { | |||||
| ++(*index_in_text); | ++(*index_in_text); | ||||
| } | } | ||||
| } | } | ||||
| @@ -55,6 +57,7 @@ char* read_atom(char* text, int* index_in_text) { | |||||
| text[*index_in_text+atom_length] != '(' && | text[*index_in_text+atom_length] != '(' && | ||||
| text[*index_in_text+atom_length] != '\0' && | text[*index_in_text+atom_length] != '\0' && | ||||
| text[*index_in_text+atom_length] != '\n' && | text[*index_in_text+atom_length] != '\n' && | ||||
| text[*index_in_text+atom_length] != '\r' && | |||||
| text[*index_in_text+atom_length] != '\t') | text[*index_in_text+atom_length] != '\t') | ||||
| { | { | ||||
| ++atom_length; | ++atom_length; | ||||
| @@ -269,7 +272,8 @@ Ast_Node_Array_List* parse_program(char* text) { | |||||
| case ';': | case ';': | ||||
| case ' ': | case ' ': | ||||
| case '\t': | case '\t': | ||||
| case '\n': { | |||||
| case '\n': | |||||
| case '\r': { | |||||
| eat_until_code(text, &index_in_text); | eat_until_code(text, &index_in_text); | ||||
| } break; | } break; | ||||
| default: | default: | ||||
| @@ -83,7 +83,7 @@ testresult test_eval_operands() { | |||||
| char operands_string[] = "((eval 1) (+ 1 2) \"okay\" (eval :haha))"; | char operands_string[] = "((eval 1) (+ 1 2) \"okay\" (eval :haha))"; | ||||
| Ast_Node* operands = parse_single_expression(operands_string); | Ast_Node* operands = parse_single_expression(operands_string); | ||||
| int operands_length; | int operands_length; | ||||
| operands = eval_arguments(operands, create_empty_environment(), &operands_length); | |||||
| operands = eval_arguments(operands, create_empty_environment(Environment_Type_Let), &operands_length); | |||||
| assert_no_error(error); | assert_no_error(error); | ||||
| assert_equal_int(list_length(operands), 4); | assert_equal_int(list_length(operands), 4); | ||||
| @@ -227,7 +227,7 @@ testresult test_parse_expression() { | |||||
| testresult test_built_in_add() { | testresult test_built_in_add() { | ||||
| char exp_string[] = "(+ 10 4)"; | char exp_string[] = "(+ 10 4)"; | ||||
| Ast_Node* expression = parse_single_expression(exp_string); | Ast_Node* expression = parse_single_expression(exp_string); | ||||
| Ast_Node* result = eval_expr(expression, create_empty_environment()); | |||||
| Ast_Node* result = eval_expr(expression, create_empty_environment(Environment_Type_Let)); | |||||
| assert_no_error(error); | assert_no_error(error); | ||||
| assert_not_null(result); | assert_not_null(result); | ||||
| @@ -240,7 +240,7 @@ testresult test_built_in_add() { | |||||
| testresult test_built_in_substract() { | testresult test_built_in_substract() { | ||||
| char exp_string[] = "(- 10 4)"; | char exp_string[] = "(- 10 4)"; | ||||
| Ast_Node* expression = parse_single_expression(exp_string); | Ast_Node* expression = parse_single_expression(exp_string); | ||||
| Ast_Node* result = eval_expr(expression, create_empty_environment()); | |||||
| Ast_Node* result = eval_expr(expression, create_empty_environment(Environment_Type_Let)); | |||||
| assert_no_error(error); | assert_no_error(error); | ||||
| assert_not_null(result); | assert_not_null(result); | ||||
| @@ -254,7 +254,7 @@ testresult test_built_in_substract() { | |||||
| testresult test_built_in_multiply() { | testresult test_built_in_multiply() { | ||||
| char exp_string[] = "(* 10 4)"; | char exp_string[] = "(* 10 4)"; | ||||
| Ast_Node* expression = parse_single_expression(exp_string); | Ast_Node* expression = parse_single_expression(exp_string); | ||||
| Ast_Node* result = eval_expr(expression, create_empty_environment()); | |||||
| Ast_Node* result = eval_expr(expression, create_empty_environment(Environment_Type_Let)); | |||||
| assert_no_error(error); | assert_no_error(error); | ||||
| assert_not_null(result); | assert_not_null(result); | ||||
| @@ -268,7 +268,7 @@ testresult test_built_in_multiply() { | |||||
| testresult test_built_in_divide() { | testresult test_built_in_divide() { | ||||
| char exp_string[] = "(/ 20 4)"; | char exp_string[] = "(/ 20 4)"; | ||||
| Ast_Node* expression = parse_single_expression(exp_string); | Ast_Node* expression = parse_single_expression(exp_string); | ||||
| Ast_Node* result = eval_expr(expression, create_empty_environment()); | |||||
| Ast_Node* result = eval_expr(expression, create_empty_environment(Environment_Type_Let)); | |||||
| assert_null(error); | assert_null(error); | ||||
| assert_not_null(result); | assert_not_null(result); | ||||
| @@ -282,7 +282,7 @@ testresult test_built_in_divide() { | |||||
| testresult test_built_in_if() { | testresult test_built_in_if() { | ||||
| char exp_string1[] = "(if 1 4 5)"; | char exp_string1[] = "(if 1 4 5)"; | ||||
| Ast_Node* expression = parse_single_expression(exp_string1); | Ast_Node* expression = parse_single_expression(exp_string1); | ||||
| Ast_Node* result = eval_expr(expression, create_empty_environment()); | |||||
| Ast_Node* result = eval_expr(expression, create_empty_environment(Environment_Type_Let)); | |||||
| assert_no_error(error); | assert_no_error(error); | ||||
| assert_not_null(result); | assert_not_null(result); | ||||
| @@ -291,7 +291,7 @@ testresult test_built_in_if() { | |||||
| char exp_string2[] = "(if () 4 5)"; | char exp_string2[] = "(if () 4 5)"; | ||||
| expression = parse_single_expression(exp_string2); | expression = parse_single_expression(exp_string2); | ||||
| result = eval_expr(expression, create_empty_environment()); | |||||
| result = eval_expr(expression, create_empty_environment(Environment_Type_Let)); | |||||
| assert_no_error(error); | assert_no_error(error); | ||||
| assert_not_null(result); | assert_not_null(result); | ||||
| @@ -304,7 +304,7 @@ testresult test_built_in_if() { | |||||
| testresult test_built_in_and() { | testresult test_built_in_and() { | ||||
| char exp_string1[] = "(and 1 \"asd\" 4)"; | char exp_string1[] = "(and 1 \"asd\" 4)"; | ||||
| Ast_Node* expression = parse_single_expression(exp_string1); | Ast_Node* expression = parse_single_expression(exp_string1); | ||||
| Ast_Node* result = eval_expr(expression, create_empty_environment()); | |||||
| Ast_Node* result = eval_expr(expression, create_empty_environment(Environment_Type_Let)); | |||||
| assert_no_error(error); | assert_no_error(error); | ||||
| assert_not_null(result); | assert_not_null(result); | ||||
| @@ -314,7 +314,7 @@ testresult test_built_in_and() { | |||||
| // a false case | // a false case | ||||
| char exp_string2[] = "(and () \"asd\" 4)"; | char exp_string2[] = "(and () \"asd\" 4)"; | ||||
| expression = parse_single_expression(exp_string2); | expression = parse_single_expression(exp_string2); | ||||
| result = eval_expr(expression, create_empty_environment()); | |||||
| result = eval_expr(expression, create_empty_environment(Environment_Type_Let)); | |||||
| assert_no_error(error); | assert_no_error(error); | ||||
| assert_not_null(result); | assert_not_null(result); | ||||
| @@ -326,7 +326,7 @@ testresult test_built_in_and() { | |||||
| testresult test_built_in_or() { | testresult test_built_in_or() { | ||||
| char exp_string1[] = "(or \"asd\" nil)"; | char exp_string1[] = "(or \"asd\" nil)"; | ||||
| Ast_Node* expression = parse_single_expression(exp_string1); | Ast_Node* expression = parse_single_expression(exp_string1); | ||||
| Ast_Node* result = eval_expr(expression, create_empty_environment()); | |||||
| Ast_Node* result = eval_expr(expression, create_empty_environment(Environment_Type_Let)); | |||||
| assert_no_error(error); | assert_no_error(error); | ||||
| assert_not_null(result); | assert_not_null(result); | ||||
| @@ -336,7 +336,7 @@ testresult test_built_in_or() { | |||||
| // a false case | // a false case | ||||
| char exp_string2[] = "(or () ())"; | char exp_string2[] = "(or () ())"; | ||||
| expression = parse_single_expression(exp_string2); | expression = parse_single_expression(exp_string2); | ||||
| result = eval_expr(expression, create_empty_environment()); | |||||
| result = eval_expr(expression, create_empty_environment(Environment_Type_Let)); | |||||
| assert_no_error(error); | assert_no_error(error); | ||||
| assert_not_null(result); | assert_not_null(result); | ||||
| @@ -349,7 +349,7 @@ testresult test_built_in_or() { | |||||
| testresult test_built_in_not() { | testresult test_built_in_not() { | ||||
| char exp_string1[] = "(not ())"; | char exp_string1[] = "(not ())"; | ||||
| Ast_Node* expression = parse_single_expression(exp_string1); | Ast_Node* expression = parse_single_expression(exp_string1); | ||||
| Ast_Node* result = eval_expr(expression, create_empty_environment()); | |||||
| Ast_Node* result = eval_expr(expression, create_empty_environment(Environment_Type_Let)); | |||||
| // a true case | // a true case | ||||
| assert_no_error(error); | assert_no_error(error); | ||||
| @@ -360,7 +360,7 @@ testresult test_built_in_not() { | |||||
| // a false case | // a false case | ||||
| char exp_string2[] = "(not \"asd xD\")"; | char exp_string2[] = "(not \"asd xD\")"; | ||||
| expression = parse_single_expression(exp_string2); | expression = parse_single_expression(exp_string2); | ||||
| result = eval_expr(expression, create_empty_environment()); | |||||
| result = eval_expr(expression, create_empty_environment(Environment_Type_Let)); | |||||
| assert_no_error(error); | assert_no_error(error); | ||||
| assert_not_null(result); | assert_not_null(result); | ||||
| @@ -1,3 +1,139 @@ | |||||
| * Issues | |||||
| ** DONE #001 | |||||
| CLOSED: [2018-10-26 Fr 22:36] | |||||
| #+begin_src lisp | |||||
| (define seq (list 1 2 3)) | |||||
| (when 1 (printf seq)) | |||||
| #+end_src | |||||
| The problem is, that =when= calls =apply= and the params of =apply= are named =fun= and =seq=. So | |||||
| when we refer to seq in =printf= then we are refering to the =apply='s argument names. Maybe a good | |||||
| solution to that is, that a child environment can never look up something in a parent macro | |||||
| environment, but will always pass through | |||||
| | stack depth | is macro environment | defined symbols | scope | | |||||
| |-------------+----------------------+--------------------------+---------------------| | |||||
| | 0 | no | seq nil printf apply etc | | | |||||
| | 1 | yes | test body | (when 1 printf seq) | | |||||
| | 2 | yes | fun seq | (apply prog body) | | |||||
| | 3 | no | | (printf seq) | | |||||
| ** DONE #002 | |||||
| CLOSED: [2018-10-27 Sa 13:50] | |||||
| #+begin_src lisp | |||||
| (define l (list 1 2 3)) | |||||
| (extend l 4) | |||||
| ;; === Environment === | |||||
| ;; current: in (apply) | |||||
| ;; fun -> prog | |||||
| ;; seq -> body | |||||
| ;; parent (hidden): in (when) | |||||
| ;; test -> (pair? seq) | |||||
| ;; body -> ((define e (end seq)) (mutate e (pair (first e) elem))) | |||||
| ;; parent: in (extend) | |||||
| ;; seq -> (1.000000 2.000000 3.000000) | |||||
| ;; elem -> 4.000000 | |||||
| ;; parent: (top level) | |||||
| ;; nil -> nil | |||||
| ;; defun -> [macro] | |||||
| ;; defmacro -> [macro] | |||||
| ;; pe -> [macro] | |||||
| ;; nil? -> [lambda] | |||||
| ;; number? -> [lambda] | |||||
| ;; symbol? -> [lambda] | |||||
| ;; keyword? -> [lambda] | |||||
| ;; pair? -> [lambda] | |||||
| ;; string? -> [lambda] | |||||
| ;; dynamic-function? -> [lambda] | |||||
| ;; dynamic-macro? -> [lambda] | |||||
| ;; built-in-function? -> [lambda] | |||||
| ;; apply -> [macro] | |||||
| ;; when -> [macro] | |||||
| ;; unless -> [macro] | |||||
| ;; end -> [lambda] | |||||
| ;; last -> [lambda] | |||||
| ;; extend -> [lambda] | |||||
| ;; append -> [lambda] | |||||
| ;; n-times -> [macro] | |||||
| ;; range -> [lambda] | |||||
| ;; map -> [lambda] | |||||
| ;; reduce -> [lambda] | |||||
| ;; reduce-binary -> [lambda] | |||||
| ;; filter -> [lambda] | |||||
| ;; printf-quoted -> [macro] | |||||
| ;; printf -> [lambda] | |||||
| ;; l -> (1.000000 2.000000 3.000000) | |||||
| ;; M (apply fun -> prog seq -> body) | |||||
| ;; M (when test -> (pair? seq) body -> ((define e (end seq)) (mutate e (pair (first e) elem)))) | |||||
| ;; (extend seq -> (1.000000 2.000000 3.000000) elem -> 4.000000) | |||||
| #+end_src | |||||
| The problem is that a funciton g called by a function f has access to the environment of f. This is | |||||
| a real problem. | |||||
| #+begin_src lisp | |||||
| (defun f () 1) | |||||
| (defun g () (f)) | |||||
| (defun h (f) (g)) | |||||
| (h 4) | |||||
| #+end_src | |||||
| What *should* happen: | |||||
| - (h 1) calls (g) | |||||
| - (g) calls (f) | |||||
| - (f) retunrs 1 | |||||
| What *actually* happens: | |||||
| - (h 1) calls (g) | |||||
| - (g) tries to call (f) but f is not a funciton but =4= | |||||
| ** DONE #003 | |||||
| CLOSED: [2018-10-27 Sa 15:22] | |||||
| #+begin_src lisp | |||||
| (defmacro n-times (times action) | |||||
| (unless (<= (eval times) 0) | |||||
| (breakpoint) | |||||
| (eval action) | |||||
| (macro-define args (pair (pair - (pair (eval times) (pair 1 nil))) (pair action nil))) | |||||
| ;; [o|o] --------------------------> [o|o] -> nil | |||||
| ;; | | | |||||
| ;; V V | |||||
| ;; [o|o] -> [o|o] -> [o|o]-> nil action | |||||
| ;; | | | | |||||
| ;; V V V | |||||
| ;; - times 1 | |||||
| (eval (pair n-times args)))) | |||||
| #+end_src | |||||
| - *let* should never have access to parenting macro because =(while 2 test) -> 2.00000= | |||||
| - Should it have access to the grandad macro? | |||||
| - no, =(when 2 (when 3 test)) -> 2= | |||||
| - in the example, =n-times= is a macro, that calls =unless=, that itself is a macro that calls the | |||||
| content of =n-times= in a let. So of course the contents of =n-times= wont have acess to the | |||||
| parameters to =n-times=, which is bad. | |||||
| So right now the only way I see to fix this is to make maco environments accessable again, but pay | |||||
| attention on how to name macro parameters, to avoid collision, maybe a naming convention would help, | |||||
| maybe prefix with =@= | |||||
| *** Solutions: | |||||
| First instinct to prevent that, is that if a parent environment is a function then just pass it and | |||||
| go for their parent etc. Then we need =let= of a way to create environments that are neither macro- | |||||
| nor funciton environmetns. They will be still different though. | |||||
| *** Defining | |||||
| In a macro environment define will still behave differently than in a function environment. If you | |||||
| use =define= in a macro environment, it will actually be defined in the closest non-macro | |||||
| environment. If you actually want to define something in a macro environment, then use | |||||
| =macro-define=. However =define= will do the same if you are in a function or let environment. | |||||
| *** Looking up | |||||
| If the symbol is found on the active environment then use it. If not: Go up the environments untily | |||||
| ou find a let-environment and recurse on that. | |||||
| * Arguments | * Arguments | ||||
| In Emacs lisp, keyword arguments must be passed in the same order as they were | In Emacs lisp, keyword arguments must be passed in the same order as they were | ||||
| @@ -191,13 +327,23 @@ set to see if we are in an errornious state. | |||||
| Ast_Node_array_list body; | Ast_Node_array_list body; | ||||
| } lambda; | } lambda; | ||||
| #+end_src | #+end_src | ||||
| * TODO =assert_equal_type= macro in testing | |||||
| * DONE use an enum for builtin identifiers | * DONE use an enum for builtin identifiers | ||||
| CLOSED: [2018-10-11 Do 17:15] | CLOSED: [2018-10-11 Do 17:15] | ||||
| * TODO =assert_equal_type= macro in testing | |||||
| * TODO =t= ast node type, universal source of truth | |||||
| * TODO backquoting | |||||
| * TODO dont create new nils or builtins, but store one of each globally | * TODO dont create new nils or builtins, but store one of each globally | ||||
| * TODO make keywords unique (binary tree) | |||||
| * TODO store all ast nodes in a huge arena | |||||
| * TODO source code locations for errors | * TODO source code locations for errors | ||||
| * Build-in forms | |||||
| * TODO String error messages | |||||
| * TODO Rename macro to =special= or something | |||||
| * Build-in forms [29/30] | |||||
| ** TODO info | |||||
| ** DONE let | |||||
| CLOSED: [2018-10-27 Sa 15:30] | |||||
| ** DONE + | ** DONE + | ||||
| CLOSED: [2018-09-18 Di 12:14] | CLOSED: [2018-09-18 Di 12:14] | ||||
| ** DONE - | ** DONE - | ||||
| @@ -206,8 +352,14 @@ set to see if we are in an errornious state. | |||||
| CLOSED: [2018-09-18 Di 12:14] | CLOSED: [2018-09-18 Di 12:14] | ||||
| ** DONE / | ** DONE / | ||||
| CLOSED: [2018-09-18 Di 12:14] | CLOSED: [2018-09-18 Di 12:14] | ||||
| ** TODO > | |||||
| ** TODO < | |||||
| ** DONE > | |||||
| CLOSED: [2018-10-26 Fr 23:30] | |||||
| ** DONE >= | |||||
| CLOSED: [2018-10-26 Fr 23:30] | |||||
| ** DONE < | |||||
| CLOSED: [2018-10-26 Fr 23:30] | |||||
| ** DONE >= | |||||
| CLOSED: [2018-10-26 Fr 23:30] | |||||
| ** DONE = | ** DONE = | ||||
| CLOSED: [2018-10-21 So 00:25] | CLOSED: [2018-10-21 So 00:25] | ||||
| ** DONE if | ** DONE if | ||||
| @@ -239,10 +391,12 @@ set to see if we are in an errornious state. | |||||
| CLOSED: [2018-10-21 So 00:25] | CLOSED: [2018-10-21 So 00:25] | ||||
| ** DONE define | ** DONE define | ||||
| CLOSED: [2018-10-08 Mo 20:28] | CLOSED: [2018-10-08 Mo 20:28] | ||||
| ** TODO mutateW | |||||
| ** DONE mutate | |||||
| CLOSED: [2018-10-25 Do 19:40] | |||||
| ** DONE lambda | ** DONE lambda | ||||
| CLOSED: [2018-10-21 So 00:25] | CLOSED: [2018-10-21 So 00:25] | ||||
| ** TODO macro | |||||
| ** DONE macro | |||||
| CLOSED: [2018-10-25 Do 19:40] | |||||
| ** DONE prog | ** DONE prog | ||||
| CLOSED: [2018-10-21 So 00:25] | CLOSED: [2018-10-21 So 00:25] | ||||
| ** DONE eval | ** DONE eval | ||||
| @@ -279,5 +433,3 @@ set to see if we are in an errornious state. | |||||
| CLOSED: [2018-10-08 Mo 20:28] | CLOSED: [2018-10-08 Mo 20:28] | ||||
| ** DONE type | ** DONE type | ||||
| CLOSED: [2018-10-08 Mo 21:30] | CLOSED: [2018-10-08 Mo 21:30] | ||||
| ** TODO info | |||||