| @@ -31,6 +31,7 @@ | |||
| ("r" save-and-find-run-script-and-compile "run" :color blue) | |||
| ("d" save-and-find-debug-script-and-compile "debug" :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)) | |||
| (define-key context-mode-map (kbd "<f2>") 'hydra-context/body)))))) | |||
| @@ -7,3 +7,4 @@ | |||
| /bin/slime | |||
| *.psess | |||
| *.vspx | |||
| todo.html | |||
| @@ -1,128 +1,155 @@ | |||
| (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 | |||
| 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-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_Load, | |||
| Built_In_Define, | |||
| Built_In_Let, | |||
| Built_In_Macro_Define, | |||
| Built_In_Breakpoint, | |||
| Built_In_Mutate, | |||
| Built_In_Lambda, | |||
| 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_And: return "and"; | |||
| 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_Equal: return "="; | |||
| 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_Info: return "info"; | |||
| case Built_In_Lambda: return "lambda"; | |||
| case Built_In_Let: return "let"; | |||
| case Built_In_Less: return "<"; | |||
| case Built_In_Less_Equal: return "<="; | |||
| 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) { | |||
| 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; | |||
| Ast_Node* node = new(Ast_Node); | |||
| @@ -56,6 +56,78 @@ Ast_Node* built_in_equals(Ast_Node* operands) { | |||
| 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) { | |||
| double sum = 0; | |||
| 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* parent; | |||
| Environment_Type type; | |||
| int capacity; | |||
| int next_index; | |||
| // TODO(Felix): Use a hashmap here. | |||
| @@ -10,12 +17,12 @@ struct Environment { | |||
| typedef struct Environment Environment; | |||
| Environment* create_child_environment(Environment* parent) { | |||
| Environment* create_child_environment(Environment* parent, Environment_Type type) { | |||
| Environment* env = new(Environment); | |||
| int start_capacity = 16; | |||
| env->type = type; | |||
| env->parent = parent; | |||
| env->capacity = start_capacity; | |||
| env->next_index = 0; | |||
| @@ -25,11 +32,20 @@ Environment* create_child_environment(Environment* parent) { | |||
| 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) { | |||
| 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 | |||
| // back of the list without checking if it already exists but are | |||
| // 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; | |||
| } | |||
| 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) | |||
| if (string_equal(env->keys[i], sym->identifier)) | |||
| 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()); | |||
| printf("%s\n", sym->identifier); | |||
| 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) { | |||
| delete_error(); | |||
| error = new(Error); | |||
| error->type = type; | |||
| error->location = location; | |||
| @@ -1,7 +1,12 @@ | |||
| Ast_Node* eval_expr(Ast_Node* node, Environment* env); | |||
| 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 | |||
| 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: | |||
| 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 { | |||
| result = eval_expr(function->body, new_env); | |||
| } | |||
| free(new_env); | |||
| return result; | |||
| } | |||
| @@ -354,7 +365,7 @@ Ast_Node* eval_expr(Ast_Node* node, Environment* env) { | |||
| create_error(_type, node); \ | |||
| return nullptr; \ | |||
| } | |||
| if (error) | |||
| return nullptr; | |||
| @@ -392,6 +403,13 @@ Ast_Node* eval_expr(Ast_Node* node, Environment* env) { | |||
| // check for special form | |||
| if (operator->type == Ast_Node_Type_Built_In_Function) { | |||
| switch (operator->value.built_in_function->type) { | |||
| case Built_In_Breakpoint: { | |||
| print_environment(env); | |||
| #ifdef _DEBUG | |||
| __debugbreak(); | |||
| #endif | |||
| return create_ast_node_nil(); | |||
| } | |||
| case Built_In_Macro: | |||
| case Built_In_Lambda: { | |||
| /* | |||
| @@ -420,6 +438,10 @@ Ast_Node* eval_expr(Ast_Node* node, Environment* env) { | |||
| try { | |||
| 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; | |||
| @@ -440,6 +462,63 @@ Ast_Node* eval_expr(Ast_Node* node, Environment* env) { | |||
| ret->value.function = function; | |||
| 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: { | |||
| bool result = true; | |||
| 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; | |||
| } | |||
| case Built_In_Macro_Define: | |||
| case Built_In_Define: { | |||
| try { | |||
| 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; | |||
| 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); | |||
| } | |||
| Ast_Node* value = arguments->value.pair->rest->value.pair->first; | |||
| /* print(value); */ | |||
| try { | |||
| 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; | |||
| } | |||
| @@ -569,6 +666,18 @@ Ast_Node* eval_expr(Ast_Node* node, Environment* env) { | |||
| case Built_In_Equal: { | |||
| 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: { | |||
| if (arguments_length != 2) | |||
| 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; | |||
| switch (type) { | |||
| 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_Nil: return create_ast_node_keyword("nil"); | |||
| 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->value.function->is_macro) { | |||
| try { | |||
| @@ -732,6 +843,9 @@ Ast_Node* eval_expr(Ast_Node* node, Environment* env) { | |||
| } | |||
| } | |||
| default: { | |||
| #ifdef _DEBUG | |||
| __debugbreak(); | |||
| #endif | |||
| report_error(Error_Type_Not_A_Function); | |||
| } | |||
| } | |||
| @@ -103,7 +103,8 @@ char* read_entire_file (char* filename) { | |||
| /* Read the entire file into memory. */ | |||
| size_t newLen = fread(fileContent, sizeof(char), bufsize, fp); | |||
| /* fileContent[bufsize-5] = '\0'; */ | |||
| fileContent[newLen] = '\0'; | |||
| if ( ferror( fp ) != 0 ) { | |||
| fputs("Error reading file", stderr); | |||
| } | |||
| @@ -68,8 +68,10 @@ void print(Ast_Node* node) { | |||
| printf(":%s", node->value.keyword->identifier); | |||
| } break; | |||
| case (Ast_Node_Type_Function): { | |||
| printf("[lambda]"); | |||
| /* print(node->value.function->body); */ | |||
| if (node->value.function->is_macro) | |||
| printf("[macro]"); | |||
| else | |||
| printf("[lambda]"); | |||
| } break; | |||
| case (Ast_Node_Type_Built_In_Function): { | |||
| 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(); | |||
| return 1; | |||
| } | |||
| Environment* env = create_empty_environment(); | |||
| Environment* env = create_empty_environment(Environment_Type_Let); | |||
| built_in_load("pre.slime", env); | |||
| if (error) { | |||
| @@ -46,7 +46,7 @@ int interprete_file (char* file_content) { | |||
| int interprete_stdin () { | |||
| printf("Welcome to the lispy interpreter.\n"); | |||
| char* line; | |||
| Environment* env = create_empty_environment(); | |||
| Environment* env = create_empty_environment(Environment_Type_Let); | |||
| built_in_load("pre.slime", env); | |||
| if (error) { | |||
| @@ -27,6 +27,7 @@ void eat_comment_line(char* text, int* index_in_text) { | |||
| do { | |||
| ++(*index_in_text); | |||
| } while (text[(*index_in_text)] != '\n' && | |||
| text[(*index_in_text)] != '\r' && | |||
| text[(*index_in_text)] != '\0'); | |||
| } | |||
| @@ -34,7 +35,8 @@ void eat_whitespace(char* text, int* index_in_text) { | |||
| // skip whitespaces | |||
| while (text[(*index_in_text)] == ' ' || | |||
| text[(*index_in_text)] == '\t' || | |||
| text[(*index_in_text)] == '\n') { | |||
| text[(*index_in_text)] == '\n' || | |||
| text[(*index_in_text)] == '\r') { | |||
| ++(*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] != '\0' && | |||
| text[*index_in_text+atom_length] != '\n' && | |||
| text[*index_in_text+atom_length] != '\r' && | |||
| text[*index_in_text+atom_length] != '\t') | |||
| { | |||
| ++atom_length; | |||
| @@ -269,7 +272,8 @@ Ast_Node_Array_List* parse_program(char* text) { | |||
| case ';': | |||
| case ' ': | |||
| case '\t': | |||
| case '\n': { | |||
| case '\n': | |||
| case '\r': { | |||
| eat_until_code(text, &index_in_text); | |||
| } break; | |||
| default: | |||
| @@ -83,7 +83,7 @@ testresult test_eval_operands() { | |||
| char operands_string[] = "((eval 1) (+ 1 2) \"okay\" (eval :haha))"; | |||
| Ast_Node* operands = parse_single_expression(operands_string); | |||
| 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_equal_int(list_length(operands), 4); | |||
| @@ -227,7 +227,7 @@ testresult test_parse_expression() { | |||
| testresult test_built_in_add() { | |||
| char exp_string[] = "(+ 10 4)"; | |||
| 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_not_null(result); | |||
| @@ -240,7 +240,7 @@ testresult test_built_in_add() { | |||
| testresult test_built_in_substract() { | |||
| char exp_string[] = "(- 10 4)"; | |||
| 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_not_null(result); | |||
| @@ -254,7 +254,7 @@ testresult test_built_in_substract() { | |||
| testresult test_built_in_multiply() { | |||
| char exp_string[] = "(* 10 4)"; | |||
| 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_not_null(result); | |||
| @@ -268,7 +268,7 @@ testresult test_built_in_multiply() { | |||
| testresult test_built_in_divide() { | |||
| char exp_string[] = "(/ 20 4)"; | |||
| 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_not_null(result); | |||
| @@ -282,7 +282,7 @@ testresult test_built_in_divide() { | |||
| testresult test_built_in_if() { | |||
| char exp_string1[] = "(if 1 4 5)"; | |||
| 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_not_null(result); | |||
| @@ -291,7 +291,7 @@ testresult test_built_in_if() { | |||
| char exp_string2[] = "(if () 4 5)"; | |||
| 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_not_null(result); | |||
| @@ -304,7 +304,7 @@ testresult test_built_in_if() { | |||
| testresult test_built_in_and() { | |||
| char exp_string1[] = "(and 1 \"asd\" 4)"; | |||
| 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_not_null(result); | |||
| @@ -314,7 +314,7 @@ testresult test_built_in_and() { | |||
| // a false case | |||
| char exp_string2[] = "(and () \"asd\" 4)"; | |||
| 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_not_null(result); | |||
| @@ -326,7 +326,7 @@ testresult test_built_in_and() { | |||
| testresult test_built_in_or() { | |||
| char exp_string1[] = "(or \"asd\" nil)"; | |||
| 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_not_null(result); | |||
| @@ -336,7 +336,7 @@ testresult test_built_in_or() { | |||
| // a false case | |||
| char exp_string2[] = "(or () ())"; | |||
| 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_not_null(result); | |||
| @@ -349,7 +349,7 @@ testresult test_built_in_or() { | |||
| testresult test_built_in_not() { | |||
| char exp_string1[] = "(not ())"; | |||
| 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 | |||
| assert_no_error(error); | |||
| @@ -360,7 +360,7 @@ testresult test_built_in_not() { | |||
| // a false case | |||
| char exp_string2[] = "(not \"asd xD\")"; | |||
| 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_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 | |||
| 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; | |||
| } lambda; | |||
| #+end_src | |||
| * TODO =assert_equal_type= macro in testing | |||
| * DONE use an enum for builtin identifiers | |||
| 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 make keywords unique (binary tree) | |||
| * TODO store all ast nodes in a huge arena | |||
| * 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 + | |||
| CLOSED: [2018-09-18 Di 12:14] | |||
| ** DONE - | |||
| @@ -206,8 +352,14 @@ set to see if we are in an errornious state. | |||
| CLOSED: [2018-09-18 Di 12:14] | |||
| ** DONE / | |||
| 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 = | |||
| CLOSED: [2018-10-21 So 00:25] | |||
| ** DONE if | |||
| @@ -239,10 +391,12 @@ set to see if we are in an errornious state. | |||
| CLOSED: [2018-10-21 So 00:25] | |||
| ** DONE define | |||
| CLOSED: [2018-10-08 Mo 20:28] | |||
| ** TODO mutateW | |||
| ** DONE mutate | |||
| CLOSED: [2018-10-25 Do 19:40] | |||
| ** DONE lambda | |||
| CLOSED: [2018-10-21 So 00:25] | |||
| ** TODO macro | |||
| ** DONE macro | |||
| CLOSED: [2018-10-25 Do 19:40] | |||
| ** DONE prog | |||
| CLOSED: [2018-10-21 So 00:25] | |||
| ** DONE eval | |||
| @@ -279,5 +433,3 @@ set to see if we are in an errornious state. | |||
| CLOSED: [2018-10-08 Mo 20:28] | |||
| ** DONE type | |||
| CLOSED: [2018-10-08 Mo 21:30] | |||
| ** TODO info | |||