| @@ -1,102 +1,117 @@ | |||||
| (define defmacro | |||||
| (macro (@name @params :rest @body) | |||||
| "Macro for creating macros with a more concise syntax." | |||||
| (eval (pair 'define-upwards (pair @name (pair (pair 'macro (pair @params @body)) nil)))))) | |||||
| (define defun | |||||
| (macro (@name @params :rest @body) | |||||
| "Macro for creating functions with a more concise syntax." | |||||
| (eval (pair 'define-upwards (pair @name (pair (pair 'lambda (pair @params @body)) nil)))))) | |||||
| ;; (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 function | |||||
| ;; 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 incr (val) | |||||
| ;; "Adds one to the argument." | |||||
| ;; (+ val 1)) | |||||
| ;; (defun decr (val) | |||||
| ;; "Subtracts one from the argument." | |||||
| ;; (- val 1)) | |||||
| ;; (defun append (seq elem) | |||||
| ;; "Appends an element to a sequence, by extendeing the list | |||||
| ;; with (pair elem nil)." | |||||
| ;; (extend seq (pair elem nil))) | |||||
| ;; (defun length (seq) | |||||
| ;; "Returns the length of the given sequence." | |||||
| ;; (if (nil? seq) | |||||
| ;; 0 | |||||
| ;; (incr (length (rest seq))))) | |||||
| (define-syntax when (condition :rest body) | |||||
| (list 'if condition (pair 'prog body) nil)) | |||||
| (define-syntax unless (condition :rest body) | |||||
| (list 'if condition nil (pair 'prog body))) | |||||
| (define-syntax defun (name arguments :rest body) | |||||
| ;; (type-assert arguments :pair) | |||||
| ;; `(define ,name (lambda ,arguments ,body)) | |||||
| ;; see if we have a docstring | |||||
| (if (and (= (type (first body)) :string) (not (= (type (rest body)) :nil))) | |||||
| (list 'define name (list 'lambda arguments (first body) (pair 'prog (rest body)))) | |||||
| (list 'define name (list 'lambda arguments (pair 'prog body))))) | |||||
| (define-syntax defspecial (name arguments :rest body) | |||||
| ;; (type-assert arguments :pair) | |||||
| ;; `(define ,name (lambda ,arguments ,body)) | |||||
| ;; see if we have a docstring | |||||
| (if (and (= (type (first body)) :string) (not (= (type (rest body)) :nil))) | |||||
| (list 'define name (list 'special-lambda arguments (first body) (pair 'prog (rest body)))) | |||||
| (list 'define name (list 'special-lambda arguments (pair 'prog body))))) | |||||
| ;; (cond | |||||
| ;; (p1 v1) | |||||
| ;; (p2 v2)) | |||||
| (define-syntax cond (:rest clauses) | |||||
| (defun rec (clauses) | |||||
| (if (= nil clauses) | |||||
| nil | |||||
| (list 'if (first (first clauses)) | |||||
| (pair 'prog (rest (first clauses))) | |||||
| (rec (rest clauses))))) | |||||
| (rec clauses)) | |||||
| (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." | |||||
| (or (= (type x) :pair) (= (type x) :nil))) | |||||
| (defun string? (x) | |||||
| "Checks if the argument is a string." | |||||
| (= (type x) :string)) | |||||
| (defun lambda? (x) | |||||
| "Checks if the argument is a function." | |||||
| (= (type x) :dynamic-function)) | |||||
| (defun special-lambda? (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 function | |||||
| with ithe sequence as arguemens." | |||||
| (eval (pair fun seq))) | |||||
| (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) | |||||
| "Appends an element to a sequence, by extendeing the list | |||||
| with (pair elem nil)." | |||||
| (extend seq (pair elem nil))) | |||||
| (defun length (seq) | |||||
| "Returns the length of the given sequence." | |||||
| (if (nil? seq) | |||||
| 0 | |||||
| (incr (length (rest seq))))) | |||||
| (defun increment (val) | |||||
| "Adds one to the argument." | |||||
| (+ val 1)) | |||||
| (defun decrement (val) | |||||
| "Subtracts one from the argument." | |||||
| (- val 1)) | |||||
| ;; (defmacro n-times (@times @action) | ;; (defmacro n-times (@times @action) | ||||
| ;; "Executes @action @times times." | ;; "Executes @action @times times." | ||||
| @@ -119,79 +134,79 @@ | |||||
| ;; (eval (pair prog @for-body)) | ;; (eval (pair prog @for-body)) | ||||
| ;; (eval (extend (list for @symbol (@op @from) @to) @for-body)))) | ;; (eval (extend (list for @symbol (@op @from) @to) @for-body)))) | ||||
| ;; (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'." | |||||
| ;; (when (< from to) | |||||
| ;; (pair from (range :from (+ 1 from) :to to)))) | |||||
| ;; (defun range-while (: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'." | |||||
| ;; (define result (list (copy from))) | |||||
| ;; (define head result) | |||||
| ;; (mutate from (incr from)) | |||||
| ;; (while (< from to) | |||||
| ;; (prog | |||||
| ;; (mutate head (pair (first head) (pair (copy from) nil))) | |||||
| ;; (define head (rest head)) | |||||
| ;; (mutate from (incr from)))) | |||||
| ;; result) | |||||
| ;; (defun map (fun seq) | |||||
| ;; "Takes a function and a sequence 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 function and a sequence 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 fun seq)) | |||||
| ;; (defun reduce-binary (fun seq) | |||||
| ;; "Takes a function and a sequence 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 seq)) | |||||
| ;; (first seq) | |||||
| ;; (fun (first seq) | |||||
| ;; (reduce-binary fun (rest seq))))) | |||||
| ;; (defun filter (fun seq) | |||||
| ;; "Takes a function and a sequence as arguments and applies the | |||||
| ;; function to every value in the sequence. If the result of that | |||||
| ;; funciton application returns a truthy value, the original value is | |||||
| ;; added to a list, which in the end is returned." | |||||
| ;; (when seq | |||||
| ;; (if (fun (first seq)) | |||||
| ;; (pair (first seq) | |||||
| ;; (filter fun (rest seq))) | |||||
| ;; (filter fun (rest seq))))) | |||||
| ;; (defun printf (:keys sep :defaults-to " " end :defaults-to "\n" :rest args) | |||||
| ;; "A wrapper for the built-in (print) that accepts a variable number | |||||
| ;; of arguments and also provides keywords for specifying the printed | |||||
| ;; separators between the arguments and what should be printed after the | |||||
| ;; las argument." | |||||
| ;; (defmacro printf-quoted (:keys @sep @end :rest @args) | |||||
| ;; (if (nil? @args) | |||||
| ;; (prog (print (eval @end)) nil) | |||||
| ;; (prog | |||||
| ;; (print (first @args)) | |||||
| ;; (unless (nil? (rest @args)) | |||||
| ;; (print (eval @sep))) | |||||
| ;; (eval (pair printf-quoted | |||||
| ;; (extend (list :@sep (eval @sep) :@end (eval @end)) (rest @args))))))) | |||||
| ;; (eval (pair printf-quoted (extend (list :@sep (eval sep) :@end (eval end)) 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'." | |||||
| (when (< from to) | |||||
| (pair from (range :from (+ 1 from) :to to)))) | |||||
| (defun range-while (: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'." | |||||
| (define result (list (copy from))) | |||||
| (define head result) | |||||
| (mutate from (increment from)) | |||||
| (while (< from to) | |||||
| (prog | |||||
| (mutate head (pair (first head) (pair (copy from) nil))) | |||||
| (define head (rest head)) | |||||
| (mutate from (increment from)))) | |||||
| result) | |||||
| (defun map (fun seq) | |||||
| "Takes a function and a sequence 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 function and a sequence 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 fun seq)) | |||||
| (defun reduce-binary (fun seq) | |||||
| "Takes a function and a sequence 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 seq)) | |||||
| (first seq) | |||||
| (fun (first seq) | |||||
| (reduce-binary fun (rest seq))))) | |||||
| (defun filter (fun seq) | |||||
| "Takes a function and a sequence as arguments and applies the | |||||
| function to every value in the sequence. If the result of that | |||||
| funciton application returns a truthy value, the original value is | |||||
| added to a list, which in the end is returned." | |||||
| (when seq | |||||
| (if (fun (first seq)) | |||||
| (pair (first seq) | |||||
| (filter fun (rest seq))) | |||||
| (filter fun (rest seq))))) | |||||
| (defun printf (:keys sep :defaults-to " " end :defaults-to "\n" :rest args) | |||||
| "A wrapper for the built-in (print) that accepts a variable number | |||||
| of arguments and also provides keywords for specifying the printed | |||||
| separators between the arguments and what should be printed after the | |||||
| las argument." | |||||
| (defspecial printf-quoted (:keys @sep @end :rest @args) | |||||
| (if (nil? @args) | |||||
| (prog (print (eval @end)) nil) | |||||
| (prog | |||||
| (print (first @args)) | |||||
| (unless (nil? (rest @args)) | |||||
| (print (eval @sep))) | |||||
| (eval (pair printf-quoted | |||||
| (extend (list :@sep (eval @sep) :@end (eval @end)) (rest @args))))))) | |||||
| (eval (pair printf-quoted (extend (list :@sep (eval sep) :@end (eval end)) args)))) | |||||
| ;; (defmacro pe (@expr) | ;; (defmacro pe (@expr) | ||||
| ;; (printf @expr "evaluates to" (eval @expr))) | ;; (printf @expr "evaluates to" (eval @expr))) | ||||
| @@ -1,29 +1,80 @@ | |||||
| ;; (define-syntax defclass (name members :rest functions) | |||||
| ;; (list 'defun (string->symbol (concat-strings "make-" (symbol->string name))) | |||||
| (defun make-vector (x y z) | |||||
| (let ((local-x x) | |||||
| (local-y y) | |||||
| (local-z z) | |||||
| (defun (macro (@name @params :rest @body) | |||||
| (eval (pair 'define-upwards (pair @name (pair (pair 'lambda (pair @params @body)) nil))))))) | |||||
| (defun set-x (new-x) (mutate local-x new-x)) | |||||
| (defun set-y (new-y) (mutate local-y new-y)) | |||||
| (defun set-z (new-z) (mutate local-z new-z)) | |||||
| ;; )) | |||||
| (defun dispatch (message) | |||||
| (if (= message ::get-x) | |||||
| local-x | |||||
| (if (= message ::set-x) | |||||
| set-x | |||||
| nil))) | |||||
| ;; (defclass vector3 (x y z) | |||||
| ;; ;; getters and setters will be auto generated | |||||
| (break) | |||||
| dispatch)) | |||||
| ;; (defun ->length () | |||||
| ;; (** (+ (* x x) (* y y) (* z z)) 0.5)) | |||||
| (define v (make-vector 1 2 3)) | |||||
| ;; (defun ->scale (fac) | |||||
| ;; (mutate x (* fac x)) | |||||
| ;; (mutate y (* fac y)) | |||||
| ;; (mutate z (* fac z))) | |||||
| (print (v ::get-x)) | |||||
| ((v ::set-x) 19) | |||||
| (print (v ::get-x)) | |||||
| ;; ) | |||||
| (defun make-vector3 (x-coord y-coord z-coord) | |||||
| (let ((x x-coord) | |||||
| (y y-coord) | |||||
| (z z-coord)) | |||||
| (defun ->get-x () x) | |||||
| (defun ->get-y () y) | |||||
| (defun ->get-z () z) | |||||
| (defun ->set-x (new-x) (mutate x new-x)) | |||||
| (defun ->set-y (new-y) (mutate y new-y)) | |||||
| (defun ->set-z (new-z) (mutate z new-z)) | |||||
| (defun ->length () | |||||
| (** (+ (* x x) (* y y) (* z z)) 0.5)) | |||||
| (defun ->scale (fac) | |||||
| (mutate x (* fac x)) | |||||
| (mutate y (* fac y)) | |||||
| (mutate z (* fac z))) | |||||
| (defun ->print () | |||||
| (print "[vector3] (") | |||||
| (print x) | |||||
| (print " ") | |||||
| (print y) | |||||
| (print " ") | |||||
| (print z) | |||||
| (print ")\n")) | |||||
| (defun ->+ (other) | |||||
| (make-vector | |||||
| (+ x ((other ->get-x))) | |||||
| (+ y ((other ->get-y))) | |||||
| (+ z ((other ->get-z))))) | |||||
| (defun ->- (other) | |||||
| (make-vector | |||||
| (- x ((other ->get-x))) | |||||
| (- y ((other ->get-y))) | |||||
| (- z ((other ->get-z))))) | |||||
| (defun ->scalar-product (other) | |||||
| (+ (* x ((other ->get-x))) | |||||
| (* y ((other ->get-y))) | |||||
| (* z ((other ->get-z))))) | |||||
| (defun ->cross-product (other) | |||||
| (make-vector | |||||
| (- (* y ((other ->get-z))) (* z ((other ->get-y)))) | |||||
| (- (* z ((other ->get-x))) (* x ((other ->get-z)))) | |||||
| (- (* x ((other ->get-y))) (* y ((other ->get-x)))))) | |||||
| (special-lambda (message) (eval message)))) | |||||
| (define v1 (make-vector3 1 2 3)) | |||||
| (define v2 (make-vector3 3 2 1)) | |||||
| (print ((v1 ->length))) | |||||
| (print ((v2 ->length))) | |||||
| (read " ") | (read " ") | ||||
| @@ -1,5 +1,32 @@ | |||||
| struct Ast_Node; | struct Ast_Node; | ||||
| define_array_list(struct Ast_Node*, Ast_Node); | |||||
| // #define define_array_list(type, name) \ | |||||
| // struct name##_Array_List { \ | |||||
| // type* data; \ | |||||
| // int length; \ | |||||
| // int next_index; \ | |||||
| // }; \ | |||||
| // \ | |||||
| // \ | |||||
| // void append_to_##name##_array_list(name##_Array_List* arraylist, type element) { \ | |||||
| // if (arraylist->next_index == arraylist->length) { \ | |||||
| // arraylist->length *= 2; \ | |||||
| // arraylist->data = \ | |||||
| // (type*)realloc(arraylist->data, arraylist->length * sizeof(type)); \ | |||||
| // } \ | |||||
| // arraylist->data[arraylist->next_index++] = element; \ | |||||
| // } \ | |||||
| // \ | |||||
| // \ | |||||
| // name##_Array_List* create_##name##_array_list(int initial_capacity) { \ | |||||
| // name##_Array_List* ret = new(name##_Array_List); \ | |||||
| // ret->data = (type*)malloc(initial_capacity * sizeof(type)); \ | |||||
| // ret->next_index = 0; \ | |||||
| // ret->length = initial_capacity; \ | |||||
| // return ret; \ | |||||
| // } | |||||
| define_array_list(Ast_Node*, Ast_Node); | |||||
| enum struct Ast_Node_Type { | enum struct Ast_Node_Type { | ||||
| Nil, | Nil, | ||||
| @@ -112,7 +139,7 @@ void append_to_keyword_argument_list(Keyword_Arguments* args, | |||||
| struct Environment; | struct Environment; | ||||
| struct Function { | struct Function { | ||||
| bool is_macro; | |||||
| bool is_special_form; | |||||
| char* docstring; | char* docstring; | ||||
| Positional_Arguments* positional_arguments; | Positional_Arguments* positional_arguments; | ||||
| Keyword_Arguments* keyword_arguments; | Keyword_Arguments* keyword_arguments; | ||||
| @@ -247,6 +247,34 @@ Ast_Node* built_in_divide(Ast_Node* arguments, Environment* env) { | |||||
| return create_ast_node_number(quotient); | return create_ast_node_number(quotient); | ||||
| } | } | ||||
| Ast_Node* built_in_exponentiate(Ast_Node* arguments, Environment* env) { | |||||
| int arguments_length; | |||||
| try { | |||||
| arguments = eval_arguments(arguments, env, &arguments_length); | |||||
| } | |||||
| if (arguments_length != 2) { | |||||
| create_error(Error_Type::Wrong_Number_Of_Arguments, arguments->sourceCodeLocation); | |||||
| return nullptr; | |||||
| } | |||||
| try { | |||||
| assert_type(arguments->value.pair->first, Ast_Node_Type::Number); | |||||
| } | |||||
| double base = arguments->value.pair->first->value.number->value; | |||||
| arguments = arguments->value.pair->rest; | |||||
| try { | |||||
| assert_type(arguments->value.pair->first, Ast_Node_Type::Number); | |||||
| } | |||||
| double exponent = arguments->value.pair->first->value.number->value; | |||||
| return create_ast_node_number(pow(base, exponent)); | |||||
| } | |||||
| Ast_Node* built_in_load(char* file_name, Environment* env) { | Ast_Node* built_in_load(char* file_name, Environment* env) { | ||||
| char* file_content = read_entire_file(file_name); | char* file_content = read_entire_file(file_name); | ||||
| @@ -254,7 +282,7 @@ Ast_Node* built_in_load(char* file_name, Environment* env) { | |||||
| Ast_Node* result = create_ast_node_nil(); | Ast_Node* result = create_ast_node_nil(); | ||||
| Ast_Node_Array_List* program; | Ast_Node_Array_List* program; | ||||
| try { | try { | ||||
| program = parse_program(file_name, file_content); | |||||
| program = Parser::parse_program(file_name, file_content); | |||||
| } | } | ||||
| for (int i = 0; i < program->next_index; ++i) { | for (int i = 0; i < program->next_index; ++i) { | ||||
| try { | try { | ||||
| @@ -294,6 +322,7 @@ void load_built_ins_into_environment(Environment* env) { | |||||
| defun("-", built_in_substract); | defun("-", built_in_substract); | ||||
| defun("*", built_in_multiply); | defun("*", built_in_multiply); | ||||
| defun("/", built_in_divide); | defun("/", built_in_divide); | ||||
| defun("**", built_in_exponentiate); | |||||
| defun("define", cLambda { | defun("define", cLambda { | ||||
| try { | try { | ||||
| arguments_length = list_length(arguments); | arguments_length = list_length(arguments); | ||||
| @@ -359,36 +388,6 @@ void load_built_ins_into_environment(Environment* env) { | |||||
| return value; | return value; | ||||
| }); | }); | ||||
| // defun("macro-define", cLambda { | |||||
| // try { | |||||
| // arguments_length = list_length(arguments); | |||||
| // } | |||||
| // if (arguments_length != 2) { | |||||
| // report_error(Error_Type::Wrong_Number_Of_Arguments); | |||||
| // } | |||||
| // Ast_Node* symbol = arguments->value.pair->first; | |||||
| // 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; | |||||
| // try { | |||||
| // value = eval_expr(value, env); | |||||
| // } | |||||
| // define_macro_symbol(symbol, value, env); | |||||
| // return value; | |||||
| // }); | |||||
| defun("mutate", cLambda { | defun("mutate", cLambda { | ||||
| try { | try { | ||||
| evaluated_arguments = eval_arguments(arguments, env, &arguments_length); | evaluated_arguments = eval_arguments(arguments, env, &arguments_length); | ||||
| @@ -596,12 +595,8 @@ void load_built_ins_into_environment(Environment* env) { | |||||
| report_error(Error_Type::Wrong_Number_Of_Arguments); | report_error(Error_Type::Wrong_Number_Of_Arguments); | ||||
| Function* function = new(Function); | Function* function = new(Function); | ||||
| /* if (lispOperator->value.built_in_function->type == Built_In_Macro) { */ | |||||
| /* function->is_macro = true; */ | |||||
| /* } else { */ | |||||
| function->parent_environment = env; | function->parent_environment = env; | ||||
| function->is_macro = false; | |||||
| /* } */ | |||||
| function->is_special_form = false; | |||||
| // if parameters were specified | // if parameters were specified | ||||
| if (arguments->value.pair->first->type != Ast_Node_Type::Nil) { | if (arguments->value.pair->first->type != Ast_Node_Type::Nil) { | ||||
| @@ -637,10 +632,10 @@ void load_built_ins_into_environment(Environment* env) { | |||||
| ret->value.function = function; | ret->value.function = function; | ||||
| return ret; | return ret; | ||||
| }); | }); | ||||
| defun("macro", cLambda { | |||||
| defun("special-lambda", cLambda { | |||||
| /* | /* | ||||
| * (macro ()) | |||||
| * (macro (x d) (+ 1 2) (- 1 2) (* 1 2)) | |||||
| * (special-lambda ()) | |||||
| * (special-lambda (x d) (+ 1 2) (- 1 2) (* 1 2)) | |||||
| */ | */ | ||||
| try { | try { | ||||
| arguments_length = list_length(arguments); | arguments_length = list_length(arguments); | ||||
| @@ -651,7 +646,7 @@ void load_built_ins_into_environment(Environment* env) { | |||||
| Function* function = new(Function); | Function* function = new(Function); | ||||
| function->parent_environment = env; | function->parent_environment = env; | ||||
| function->is_macro = true; | |||||
| function->is_special_form = true; | |||||
| // if parameters were specified | // if parameters were specified | ||||
| if (arguments->value.pair->first->type != Ast_Node_Type::Nil) { | if (arguments->value.pair->first->type != Ast_Node_Type::Nil) { | ||||
| @@ -734,8 +729,9 @@ void load_built_ins_into_environment(Environment* env) { | |||||
| // BUG(Felix): Why is arguments_length for '(1 (2)) == 3 and not 2?? | // BUG(Felix): Why is arguments_length for '(1 (2)) == 3 and not 2?? | ||||
| // BUG(Felix): Why is arguments_length for '(1 (2)) == 3 and not 2?? | // BUG(Felix): Why is arguments_length for '(1 (2)) == 3 and not 2?? | ||||
| // BUG(Felix): Why is arguments_length for '(1 (2)) == 3 and not 2?? | // BUG(Felix): Why is arguments_length for '(1 (2)) == 3 and not 2?? | ||||
| /* if (arguments_length != 2) { */ | |||||
| if (list_length(evaluated_arguments) != 2) { | |||||
| // if (list_length(evaluated_arguments) != 2) { | |||||
| if (arguments_length != 2) { | |||||
| report_error(Error_Type::Wrong_Number_Of_Arguments); | report_error(Error_Type::Wrong_Number_Of_Arguments); | ||||
| } | } | ||||
| return create_ast_node_pair(evaluated_arguments->value.pair->first, evaluated_arguments->value.pair->rest->value.pair->first); | return create_ast_node_pair(evaluated_arguments->value.pair->first, evaluated_arguments->value.pair->rest->value.pair->first); | ||||
| @@ -780,7 +776,7 @@ void load_built_ins_into_environment(Environment* env) { | |||||
| switch (type) { | switch (type) { | ||||
| case Ast_Node_Type::CFunction: return create_ast_node_keyword("cfunction"); | case Ast_Node_Type::CFunction: return create_ast_node_keyword("cfunction"); | ||||
| case Ast_Node_Type::Function: { | case Ast_Node_Type::Function: { | ||||
| if (evaluated_arguments->value.pair->first->value.function->is_macro) | |||||
| if (evaluated_arguments->value.pair->first->value.function->is_special_form) | |||||
| return create_ast_node_keyword("dynamic-macro"); | return create_ast_node_keyword("dynamic-macro"); | ||||
| return create_ast_node_keyword("dynamic-function"); | return create_ast_node_keyword("dynamic-function"); | ||||
| } | } | ||||
| @@ -822,7 +818,7 @@ void load_built_ins_into_environment(Environment* env) { | |||||
| string_equal(type->value.keyword->identifier, "dynamic-macro"))) | string_equal(type->value.keyword->identifier, "dynamic-macro"))) | ||||
| { | { | ||||
| Ast_Node* fun = eval_expr(arguments->value.pair->first, env); | Ast_Node* fun = eval_expr(arguments->value.pair->first, env); | ||||
| printf("\nMacro? %s\n", (fun->value.function->is_macro) ? "yes" : "no"); | |||||
| printf("\nspecial-lambda? %s\n", (fun->value.function->is_special_form) ? "yes" : "no"); | |||||
| if (fun->value.function->docstring) | if (fun->value.function->docstring) | ||||
| printf("Docstring:\n==========\n%s\n\n", fun->value.function->docstring); | printf("Docstring:\n==========\n%s\n\n", fun->value.function->docstring); | ||||
| else | else | ||||
| @@ -876,7 +872,7 @@ void load_built_ins_into_environment(Environment* env) { | |||||
| report_error(Error_Type::Wrong_Number_Of_Arguments); | report_error(Error_Type::Wrong_Number_Of_Arguments); | ||||
| } | } | ||||
| print(evaluated_arguments->value.pair->first); | print(evaluated_arguments->value.pair->first); | ||||
| printf("\n"); | |||||
| // printf("\n"); | |||||
| return create_ast_node_nil(); | return create_ast_node_nil(); | ||||
| }); | }); | ||||
| defun("read", cLambda { | defun("read", cLambda { | ||||
| @@ -27,15 +27,6 @@ Environment* create_empty_environment() { | |||||
| } | } | ||||
| 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 | ||||
| @@ -52,17 +43,6 @@ void define_symbol(Ast_Node* symbol, Ast_Node* value, Environment* env) { | |||||
| ++env->next_index; | ++env->next_index; | ||||
| } | } | ||||
| // 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->sourceCodeLocation); | |||||
| // return; | |||||
| // } | |||||
| // env->type = Environment_Type::Lambda; | |||||
| // define_symbol(symbol, value, env); | |||||
| // env->type = Environment_Type::Macro; | |||||
| // } | |||||
| void print_environment(Environment* env); | void print_environment(Environment* env); | ||||
| Ast_Node* lookup_symbol_in_this_envt(Symbol* sym, Environment* env) { | Ast_Node* lookup_symbol_in_this_envt(Symbol* sym, Environment* env) { | ||||
| @@ -72,42 +52,6 @@ Ast_Node* lookup_symbol_in_this_envt(Symbol* sym, Environment* env) { | |||||
| return nullptr; | 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) { | |||||
| // 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(Ast_Node* node, Environment* env) { | Ast_Node* lookup_symbol(Ast_Node* node, Environment* env) { | ||||
| // first check current environment | // first check current environment | ||||
| Symbol* sym = node->value.symbol; | Symbol* sym = node->value.symbol; | ||||
| @@ -150,12 +94,6 @@ void print_environment_indent(Environment* env, int indent) { | |||||
| if (env->parent) { | if (env->parent) { | ||||
| print_indent(indent); | print_indent(indent); | ||||
| printf("parent"); | 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"); | printf(":\n"); | ||||
| print_environment_indent(env->parent, indent+4); | print_environment_indent(env->parent, indent+4); | ||||
| } | } | ||||
| @@ -1,12 +1,6 @@ | |||||
| Ast_Node* eval_expr(Ast_Node* node, Environment* env); | |||||
| Ast_Node* eval_expr(Ast_Node*, Environment*); | |||||
| Ast_Node* apply_arguments_to_function(Ast_Node* arguments, Function* function, 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); | |||||
| Ast_Node* apply_arguments_to_function(Ast_Node* arguments, Function* function) { | |||||
| Environment* new_env = create_child_environment(function->parent_environment); | Environment* new_env = create_child_environment(function->parent_environment); | ||||
| // positional arguments | // positional arguments | ||||
| @@ -143,10 +137,6 @@ 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 | |||||
| try { | try { | ||||
| result = eval_expr(function->body, new_env); | result = eval_expr(function->body, new_env); | ||||
| } | } | ||||
| @@ -331,7 +321,7 @@ Ast_Node* extract_keyword_value(char* keyword, Parsed_Arguments* args) { | |||||
| } | } | ||||
| Ast_Node* eval_arguments(Ast_Node* arguments, Environment* env, int *out_arguments_length) { | Ast_Node* eval_arguments(Ast_Node* arguments, Environment* env, int *out_arguments_length) { | ||||
| *out_arguments_length = 0; | |||||
| int my_out_arguments_length = 0; | |||||
| if (arguments->type == Ast_Node_Type::Nil) { | if (arguments->type == Ast_Node_Type::Nil) { | ||||
| return arguments; | return arguments; | ||||
| } | } | ||||
| @@ -355,8 +345,9 @@ Ast_Node* eval_arguments(Ast_Node* arguments, Environment* env, int *out_argumen | |||||
| create_error(Error_Type::Ill_Formed_Arguments, arguments->sourceCodeLocation); | create_error(Error_Type::Ill_Formed_Arguments, arguments->sourceCodeLocation); | ||||
| return nullptr; | return nullptr; | ||||
| } | } | ||||
| ++(*out_arguments_length); | |||||
| ++my_out_arguments_length; | |||||
| } | } | ||||
| *(out_arguments_length) = my_out_arguments_length; | |||||
| return evaluated_arguments; | return evaluated_arguments; | ||||
| } | } | ||||
| @@ -408,7 +399,7 @@ Ast_Node* eval_expr(Ast_Node* node, Environment* env) { | |||||
| // check for list function | // check for list function | ||||
| if (lispOperator->type == Ast_Node_Type::Function) { | if (lispOperator->type == Ast_Node_Type::Function) { | ||||
| if (!lispOperator->value.function->is_macro) { | |||||
| if (!lispOperator->value.function->is_special_form) { | |||||
| try { | try { | ||||
| arguments = eval_arguments(arguments, env, &arguments_length); | arguments = eval_arguments(arguments, env, &arguments_length); | ||||
| } | } | ||||
| @@ -416,7 +407,7 @@ Ast_Node* eval_expr(Ast_Node* node, Environment* env) { | |||||
| Ast_Node* result; | Ast_Node* result; | ||||
| try { | try { | ||||
| result = apply_arguments_to_function(arguments, lispOperator->value.function, env); | |||||
| result = apply_arguments_to_function(arguments, lispOperator->value.function); | |||||
| } | } | ||||
| return result; | return result; | ||||
| } | } | ||||
| @@ -1,6 +1,14 @@ | |||||
| #define new(type) new type | #define new(type) new type | ||||
| #define nullptr NULL | #define nullptr NULL | ||||
| #ifdef _DEBUG | |||||
| #define assert(cond) \ | |||||
| if (!cond) \ | |||||
| __debugbreak(); | |||||
| #else | |||||
| #define assert(cond) | |||||
| #endif | |||||
| #define concat_( a, b) a##b | #define concat_( a, b) a##b | ||||
| #define label(prefix, lnum) concat_(prefix,lnum) | #define label(prefix, lnum) concat_(prefix,lnum) | ||||
| #define try \ | #define try \ | ||||
| @@ -55,10 +63,23 @@ | |||||
| define_array_list(char*, String); | define_array_list(char*, String); | ||||
| int string_equal(char* a, char* b) { | |||||
| return !strcmp(a, b); | |||||
| // int string_equal(char* a, char* b) { | |||||
| // return !strcmp(a, b); | |||||
| // } | |||||
| int string_equal(char input[],char check[]) | |||||
| { | |||||
| int i,result=1; | |||||
| for(i=0; input[i]!='\0' || check[i]!='\0'; i++) { | |||||
| if(input[i] != check[i]) { | |||||
| result=0; | |||||
| break; | |||||
| } | |||||
| } | |||||
| return result; | |||||
| } | } | ||||
| // asprintf implementation | // asprintf implementation | ||||
| int _vscprintf_so(const char * format, va_list pargs) { | int _vscprintf_so(const char * format, va_list pargs) { | ||||
| int retval; | int retval; | ||||
| @@ -59,8 +59,8 @@ 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): { | ||||
| if (node->value.function->is_macro) | |||||
| printf("[macro]"); | |||||
| if (node->value.function->is_special_form) | |||||
| printf("[special-lambda]"); | |||||
| else | else | ||||
| printf("[lambda]"); | printf("[lambda]"); | ||||
| } break; | } break; | ||||
| @@ -13,8 +13,8 @@ | |||||
| #include "./error.cpp" | #include "./error.cpp" | ||||
| #include "./io.cpp" | #include "./io.cpp" | ||||
| #include "./assert.cpp" | #include "./assert.cpp" | ||||
| #include "./parse.cpp" | |||||
| #include "./env.cpp" | #include "./env.cpp" | ||||
| #include "./parse.cpp" | |||||
| #include "./built_ins.cpp" | #include "./built_ins.cpp" | ||||
| #include "./eval.cpp" | #include "./eval.cpp" | ||||
| #include "./testing.cpp" | #include "./testing.cpp" | ||||
| @@ -25,18 +25,20 @@ Ast_Node* interprete_file (char* file_name) { | |||||
| create_error(Error_Type::Unknown_Error, nullptr); | create_error(Error_Type::Unknown_Error, nullptr); | ||||
| } | } | ||||
| Ast_Node_Array_List* program; | |||||
| try { | |||||
| program = parse_program(file_name, file_content); | |||||
| } | |||||
| Environment* env = create_empty_environment(); | Environment* env = create_empty_environment(); | ||||
| load_built_ins_into_environment(env); | load_built_ins_into_environment(env); | ||||
| Parser::init(env); | |||||
| try { | try { | ||||
| built_in_load("pre.slime", env); | built_in_load("pre.slime", env); | ||||
| } | } | ||||
| Ast_Node_Array_List* program; | |||||
| try { | |||||
| program = Parser::parse_program(file_name, file_content); | |||||
| } | |||||
| Ast_Node* result = create_ast_node_nil(); | Ast_Node* result = create_ast_node_nil(); | ||||
| for (int i = 0; i < program->next_index; ++i) { | for (int i = 0; i < program->next_index; ++i) { | ||||
| try { | try { | ||||
| @@ -53,6 +55,8 @@ int interprete_stdin () { | |||||
| Environment* env = create_empty_environment(); | Environment* env = create_empty_environment(); | ||||
| load_built_ins_into_environment(env); | load_built_ins_into_environment(env); | ||||
| Parser::init(env); | |||||
| built_in_load("pre.slime", env); | built_in_load("pre.slime", env); | ||||
| if (error) { | if (error) { | ||||
| log_error(); | log_error(); | ||||
| @@ -63,7 +67,7 @@ int interprete_stdin () { | |||||
| while (true) { | while (true) { | ||||
| printf(">"); | printf(">"); | ||||
| line = read_expression(); | line = read_expression(); | ||||
| parsed = parse_single_expression(line); | |||||
| parsed = Parser::parse_single_expression(line); | |||||
| if (error) { | if (error) { | ||||
| log_error(); | log_error(); | ||||
| delete_error(); | delete_error(); | ||||
| @@ -1,358 +1,476 @@ | |||||
| char* parser_file; | |||||
| int parser_line; | |||||
| int parser_col; | |||||
| #define inject_scl(_ret) \ | |||||
| ret->sourceCodeLocation = new(Source_Code_Location); \ | |||||
| ret->sourceCodeLocation->file = parser_file; \ | |||||
| ret->sourceCodeLocation->line = parser_line; \ | |||||
| ret->sourceCodeLocation->column = parser_col | |||||
| // forward decls -- start | |||||
| void load_built_ins_into_environment(Environment*); | |||||
| int list_length(Ast_Node*); | |||||
| void parse_argument_list(Ast_Node*, Function*); | |||||
| Ast_Node* eval_expr(Ast_Node*, Environment*); | |||||
| // forward decls -- end | |||||
| // TODO(Felix): use the array list macro here? | |||||
| Ast_Node_Array_List* create_Ast_Node_Array_List(int initial_length) { | |||||
| Ast_Node_Array_List* ret = new (Ast_Node_Array_List); | |||||
| // create one with 16 entries first | |||||
| ret->length = initial_length; | |||||
| ret->data = (struct Ast_Node**)malloc(initial_length * sizeof(struct Ast_Node)); | |||||
| ret->next_index = 0; | |||||
| namespace Parser { | |||||
| return ret; | |||||
| } | |||||
| #define inject_scl(_ret) \ | |||||
| ret->sourceCodeLocation = new(Source_Code_Location); \ | |||||
| ret->sourceCodeLocation->file = parser_file; \ | |||||
| ret->sourceCodeLocation->line = parser_line; \ | |||||
| ret->sourceCodeLocation->column = parser_col | |||||
| void append_to_Ast_Node_Array_List(Ast_Node_Array_List* list, struct Ast_Node* node) { | |||||
| if (list->next_index == list->length) { | |||||
| list->length *= 2; | |||||
| list->data = (struct Ast_Node**)realloc(list->data, list->length * sizeof(struct Ast_Node)); | |||||
| char* parser_file; | |||||
| int parser_line; | |||||
| int parser_col; | |||||
| // NOTE(Felix): In this environment, the build in vunctions will | |||||
| // be loaded, and the macros will be stroed in form of | |||||
| // special-lambdas, that get executed in this environment at | |||||
| // read-time | |||||
| Environment* environment_for_macros; | |||||
| void init(Environment* env) { | |||||
| // if we already initialized it, then skip | |||||
| if (environment_for_macros) | |||||
| return; | |||||
| // NOTE(Felix): For now we just allow executing built-ins at | |||||
| // read-time (while creating macros). If later we want to | |||||
| // change that, we have to define some funcions in this | |||||
| // environment. | |||||
| environment_for_macros = env; | |||||
| } | } | ||||
| list->data[list->next_index++] = node; | |||||
| } | |||||
| void eat_comment_line(char* text, int* index_in_text) { | |||||
| // safety check if we are actually starting a comment here | |||||
| if (text[*index_in_text] != ';') | |||||
| return; | |||||
| void eat_comment_line(char* text, int* index_in_text) { | |||||
| // safety check if we are actually starting a comment here | |||||
| if (text[*index_in_text] != ';') | |||||
| return; | |||||
| // eat the comment line | |||||
| do { | |||||
| ++(*index_in_text); | |||||
| ++parser_col; | |||||
| } while (text[(*index_in_text)] != '\n' && | |||||
| text[(*index_in_text)] != '\r' && | |||||
| text[(*index_in_text)] != '\0'); | |||||
| } | |||||
| 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)] == '\r') | |||||
| { | |||||
| if (text[(*index_in_text)] == '\n') { | |||||
| ++parser_line; | |||||
| parser_col = 0; | |||||
| } | |||||
| ++parser_col; | |||||
| ++(*index_in_text); | |||||
| // eat the comment line | |||||
| do { | |||||
| ++(*index_in_text); | |||||
| ++parser_col; | |||||
| } while (text[(*index_in_text)] != '\n' && | |||||
| text[(*index_in_text)] != '\r' && | |||||
| text[(*index_in_text)] != '\0'); | |||||
| } | } | ||||
| } | |||||
| 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)] == '\r') | |||||
| { | |||||
| if (text[(*index_in_text)] == '\n') { | |||||
| ++parser_line; | |||||
| parser_col = 0; | |||||
| } | |||||
| ++parser_col; | |||||
| ++(*index_in_text); | |||||
| } | |||||
| void eat_until_code(char* text, int* index_in_text) { | |||||
| int position_before; | |||||
| do { | |||||
| position_before = *index_in_text; | |||||
| eat_comment_line(text, index_in_text); | |||||
| eat_whitespace(text, index_in_text); | |||||
| } while (position_before != *index_in_text); | |||||
| } | |||||
| } | |||||
| char* read_atom(char* text, int* index_in_text) { | |||||
| int atom_length = 0; | |||||
| while (text[*index_in_text+atom_length] != ' ' && | |||||
| 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] != '\n' && | |||||
| text[*index_in_text+atom_length] != '\r' && | |||||
| text[*index_in_text+atom_length] != '\t') | |||||
| { | |||||
| ++atom_length; | |||||
| void eat_until_code(char* text, int* index_in_text) { | |||||
| int position_before; | |||||
| do { | |||||
| position_before = *index_in_text; | |||||
| eat_comment_line(text, index_in_text); | |||||
| eat_whitespace(text, index_in_text); | |||||
| } while (position_before != *index_in_text); | |||||
| } | } | ||||
| // let's mark the end of the atom there quickly, so the string can | |||||
| // be copied from there easily and then put the char that was | |||||
| // before there back | |||||
| char before = text[*index_in_text+atom_length]; | |||||
| text[*index_in_text+atom_length] = '\0'; | |||||
| char* read_atom(char* text, int* index_in_text) { | |||||
| int atom_length = 0; | |||||
| while (text[*index_in_text+atom_length] != ' ' && | |||||
| 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] != '\n' && | |||||
| text[*index_in_text+atom_length] != '\r' && | |||||
| text[*index_in_text+atom_length] != '\t') | |||||
| { | |||||
| ++atom_length; | |||||
| } | |||||
| // get the atom | |||||
| char* atom = (char*)malloc(atom_length*sizeof(char)+1); // plus null char | |||||
| strcpy(atom, text+(*index_in_text)); | |||||
| // let's mark the end of the atom there quickly, so the string can | |||||
| // be copied from there easily and then put the char that was | |||||
| // before there back | |||||
| char before = text[*index_in_text+atom_length]; | |||||
| text[*index_in_text+atom_length] = '\0'; | |||||
| // restore the original string | |||||
| text[*index_in_text+atom_length] = before; | |||||
| // get the atom | |||||
| char* atom = (char*)malloc(atom_length*sizeof(char)+1); // plus null char | |||||
| strcpy(atom, text+(*index_in_text)); | |||||
| // update the index to point to the character after the atom | |||||
| // ended | |||||
| *index_in_text += atom_length; | |||||
| // restore the original string | |||||
| text[*index_in_text+atom_length] = before; | |||||
| return atom; | |||||
| } | |||||
| // update the index to point to the character after the atom | |||||
| // ended | |||||
| *index_in_text += atom_length; | |||||
| Ast_Node* parse_number(char* text, int* index_in_text) { | |||||
| double number; | |||||
| char* str_number = read_atom(text, index_in_text); | |||||
| sscanf(str_number, "%lf", &number); | |||||
| Ast_Node* ret = create_ast_node_number(number); | |||||
| inject_scl(ret); | |||||
| return ret; | |||||
| } | |||||
| Ast_Node* parse_keyword(char* text, int* index_in_text) { | |||||
| // we are now on the colon | |||||
| ++(*index_in_text); | |||||
| ++parser_col; | |||||
| char* str_keyword = read_atom(text, index_in_text); | |||||
| Ast_Node* ret = create_ast_node_keyword(str_keyword); | |||||
| inject_scl(ret); | |||||
| return ret; | |||||
| } | |||||
| return atom; | |||||
| } | |||||
| Ast_Node* parse_symbol(char* text, int* index_in_text) { | |||||
| // we are now at the first char of the symbol | |||||
| char* str_symbol = read_atom(text, index_in_text); | |||||
| Ast_Node* ret = create_ast_node_symbol(str_symbol); | |||||
| inject_scl(ret); | |||||
| return ret; | |||||
| } | |||||
| Ast_Node* parse_number(char* text, int* index_in_text) { | |||||
| double number; | |||||
| char* str_number = read_atom(text, index_in_text); | |||||
| sscanf(str_number, "%lf", &number); | |||||
| Ast_Node* ret = create_ast_node_number(number); | |||||
| inject_scl(ret); | |||||
| return ret; | |||||
| } | |||||
| Ast_Node* parse_string(char* text, int* index_in_text) { | |||||
| // the first character is the '"' | |||||
| ++(*index_in_text); | |||||
| ++parser_col; | |||||
| // now we are at the first letter, if this is the closing '"' then | |||||
| // it's easy | |||||
| if (text[*index_in_text] == '"') { | |||||
| char* str = new(char); | |||||
| *str = '\0'; | |||||
| Ast_Node* ret = create_ast_node_string(str, 0); | |||||
| Ast_Node* parse_keyword(char* text, int* index_in_text) { | |||||
| // we are now on the colon | |||||
| ++(*index_in_text); | |||||
| ++parser_col; | |||||
| char* str_keyword = read_atom(text, index_in_text); | |||||
| Ast_Node* ret = create_ast_node_keyword(str_keyword); | |||||
| inject_scl(ret); | inject_scl(ret); | ||||
| return ret; | return ret; | ||||
| } | } | ||||
| // okay so the first letter was not actually closing the string... | |||||
| int string_length = 0; | |||||
| while (text[*index_in_text+string_length] != '"' || | |||||
| text[*index_in_text+string_length] == '\\') | |||||
| { | |||||
| ++string_length; | |||||
| Ast_Node* parse_symbol(char* text, int* index_in_text) { | |||||
| // we are now at the first char of the symbol | |||||
| char* str_symbol = read_atom(text, index_in_text); | |||||
| Ast_Node* ret = create_ast_node_symbol(str_symbol); | |||||
| inject_scl(ret); | |||||
| return ret; | |||||
| } | } | ||||
| // we found the end of the string | |||||
| text[*index_in_text+string_length] = '\0'; | |||||
| Ast_Node* parse_string(char* text, int* index_in_text) { | |||||
| // the first character is the '"' | |||||
| ++(*index_in_text); | |||||
| ++parser_col; | |||||
| char* string = (char*)malloc(string_length*sizeof(char)+1); // plus null char | |||||
| // now we are at the first letter, if this is the closing '"' then | |||||
| // it's easy | |||||
| if (text[*index_in_text] == '"') { | |||||
| char* str = new(char); | |||||
| *str = '\0'; | |||||
| Ast_Node* ret = create_ast_node_string(str, 0); | |||||
| inject_scl(ret); | |||||
| return ret; | |||||
| } | |||||
| if (!unescape_string(text+(*index_in_text))) { | |||||
| create_error( | |||||
| Error_Type::Unknown_Error, | |||||
| create_source_code_location(parser_file, parser_line, parser_col)); | |||||
| return nullptr; | |||||
| } | |||||
| strcpy(string, text+(*index_in_text)); | |||||
| /* manually copy to parse control sequences correctly */ | |||||
| /* int temp_index = 0; */ | |||||
| /* while (text+(temp_index+(*index_in_text)) != '\0') { */ | |||||
| /* string[temp_index++] = text[temp_index+(*index_in_text)]; */ | |||||
| /* } */ | |||||
| /* string[temp_index++] = '\0'; */ | |||||
| text[*index_in_text+string_length] = '"'; | |||||
| *index_in_text += string_length +1; // plus one because we want to | |||||
| // go after the quotes | |||||
| Ast_Node* ret = create_ast_node_string(string, string_length); | |||||
| inject_scl(ret); | |||||
| return ret; | |||||
| } | |||||
| // okay so the first letter was not actually closing the string... | |||||
| int string_length = 0; | |||||
| while (text[*index_in_text+string_length] != '"' || | |||||
| text[*index_in_text+string_length] == '\\') | |||||
| { | |||||
| ++string_length; | |||||
| } | |||||
| Ast_Node* parse_atom(char* text, int* index_in_text) { | |||||
| // numbers | |||||
| if ((text[*index_in_text] <= 57 && // if number | |||||
| text[*index_in_text] >= 48) | |||||
| || | |||||
| ((text[*index_in_text] == '+' || // or if sign and then number | |||||
| text[*index_in_text] == '-') | |||||
| && | |||||
| (text[*index_in_text +1] <= 57 && | |||||
| text[*index_in_text +1] >= 48)) | |||||
| || | |||||
| ((text[*index_in_text] == '.') // or if . and then number | |||||
| && | |||||
| (text[*index_in_text +1] <= 57 && | |||||
| text[*index_in_text +1] >= 48))) | |||||
| return parse_number(text, index_in_text); | |||||
| // keywords | |||||
| if (text[*index_in_text] == ':') | |||||
| return parse_keyword(text, index_in_text); | |||||
| // strings | |||||
| if (text[*index_in_text] == '"') | |||||
| return parse_string(text, index_in_text); | |||||
| return parse_symbol(text, index_in_text); | |||||
| } | |||||
| // we found the end of the string | |||||
| text[*index_in_text+string_length] = '\0'; | |||||
| Ast_Node* parse_expression(char* text, int* index_in_text) { | |||||
| if (text[*index_in_text] == '\'') { | |||||
| ++(*index_in_text); | |||||
| ++parser_col; | |||||
| Ast_Node* result; | |||||
| if (text[*index_in_text] == '(' || text[*index_in_text] == '\'' ) { | |||||
| try { | |||||
| result = parse_expression(text, index_in_text); | |||||
| } | |||||
| } else { | |||||
| try { | |||||
| result = parse_atom(text, index_in_text); | |||||
| } | |||||
| char* string = (char*)malloc(string_length*sizeof(char)+1); // plus null char | |||||
| if (!unescape_string(text+(*index_in_text))) { | |||||
| create_error( | |||||
| Error_Type::Unknown_Error, | |||||
| create_source_code_location(parser_file, parser_line, parser_col)); | |||||
| return nullptr; | |||||
| } | } | ||||
| return create_ast_node_pair( | |||||
| create_ast_node_symbol("quote"), | |||||
| create_ast_node_pair(result, create_ast_node_nil())); | |||||
| } | |||||
| ++(*index_in_text); | |||||
| ++parser_col; | |||||
| strcpy(string, text+(*index_in_text)); | |||||
| /* manually copy to parse control sequences correctly */ | |||||
| /* int temp_index = 0; */ | |||||
| /* while (text+(temp_index+(*index_in_text)) != '\0') { */ | |||||
| /* string[temp_index++] = text[temp_index+(*index_in_text)]; */ | |||||
| /* } */ | |||||
| /* string[temp_index++] = '\0'; */ | |||||
| eat_whitespace(text, index_in_text); | |||||
| text[*index_in_text+string_length] = '"'; | |||||
| // if there was actually nothing in the list, return nil | |||||
| if (text[(*index_in_text)] == ')') { | |||||
| ++(*index_in_text); | |||||
| ++parser_col; | |||||
| return create_ast_node_nil(); | |||||
| *index_in_text += string_length +1; // plus one because we want to | |||||
| // go after the quotes | |||||
| Ast_Node* ret = create_ast_node_string(string, string_length); | |||||
| inject_scl(ret); | |||||
| return ret; | |||||
| } | } | ||||
| // okay there is something | |||||
| Ast_Node* head = new(Ast_Node); | |||||
| head->type = Ast_Node_Type::Pair; | |||||
| head->value.pair = new(Pair); | |||||
| Ast_Node* expression = head; | |||||
| Ast_Node* parse_atom(char* text, int* index_in_text) { | |||||
| // numbers | |||||
| if ((text[*index_in_text] <= 57 && // if number | |||||
| text[*index_in_text] >= 48) | |||||
| || | |||||
| ((text[*index_in_text] == '+' || // or if sign and then number | |||||
| text[*index_in_text] == '-') | |||||
| && | |||||
| (text[*index_in_text +1] <= 57 && | |||||
| text[*index_in_text +1] >= 48)) | |||||
| || | |||||
| ((text[*index_in_text] == '.') // or if . and then number | |||||
| && | |||||
| (text[*index_in_text +1] <= 57 && | |||||
| text[*index_in_text +1] >= 48))) | |||||
| return parse_number(text, index_in_text); | |||||
| // keywords | |||||
| if (text[*index_in_text] == ':') | |||||
| return parse_keyword(text, index_in_text); | |||||
| // strings | |||||
| if (text[*index_in_text] == '"') | |||||
| return parse_string(text, index_in_text); | |||||
| return parse_symbol(text, index_in_text); | |||||
| } | |||||
| while (true) { | |||||
| if (text[(*index_in_text)] == '(' || text[(*index_in_text)] == '\'' ) { | |||||
| try { | |||||
| head->value.pair->first = parse_expression(text, index_in_text); | |||||
| } | |||||
| } else { | |||||
| try { | |||||
| head->value.pair->first = parse_atom(text, index_in_text); | |||||
| Ast_Node* parse_expression(char* text, int* index_in_text) { | |||||
| // if it is quoted | |||||
| if (text[*index_in_text] == '\'') { | |||||
| ++(*index_in_text); | |||||
| ++parser_col; | |||||
| Ast_Node* result; | |||||
| if (text[*index_in_text] == '(' || text[*index_in_text] == '\'' ) { | |||||
| try { | |||||
| result = parse_expression(text, index_in_text); | |||||
| } | |||||
| } else { | |||||
| try { | |||||
| result = parse_atom(text, index_in_text); | |||||
| } | |||||
| } | } | ||||
| return create_ast_node_pair( | |||||
| create_ast_node_symbol("quote"), | |||||
| create_ast_node_pair(result, create_ast_node_nil())); | |||||
| } | } | ||||
| eat_until_code(text, index_in_text); | |||||
| if (text[(*index_in_text)] == '\0') { | |||||
| create_error(Error_Type::Unexpected_Eof, create_source_code_location(parser_file, parser_line, parser_col)); | |||||
| return nullptr; | |||||
| } | |||||
| // if it is not quoted | |||||
| ++(*index_in_text); | |||||
| ++parser_col; | |||||
| eat_whitespace(text, index_in_text); | |||||
| // if there was actually nothing in the list, we define here, | |||||
| // that that means nil | |||||
| if (text[(*index_in_text)] == ')') { | if (text[(*index_in_text)] == ')') { | ||||
| head->value.pair->rest = create_ast_node_nil(); | |||||
| ++parser_col; | |||||
| ++(*index_in_text); | ++(*index_in_text); | ||||
| break; | |||||
| } else if (text[(*index_in_text)] == '.') { | |||||
| ++parser_col; | ++parser_col; | ||||
| ++(*index_in_text); | |||||
| eat_until_code(text, index_in_text); | |||||
| return create_ast_node_nil(); | |||||
| } | |||||
| if (text[(*index_in_text)] == '(') | |||||
| head->value.pair->rest = parse_expression(text, index_in_text); | |||||
| else | |||||
| head->value.pair->rest = parse_atom(text, index_in_text); | |||||
| // okay there is something | |||||
| Ast_Node* head = new(Ast_Node); | |||||
| head->type = Ast_Node_Type::Pair; | |||||
| head->value.pair = new(Pair); | |||||
| Ast_Node* expression = head; | |||||
| while (true) { | |||||
| if (text[(*index_in_text)] == '(' || text[(*index_in_text)] == '\'' ) { | |||||
| try { | |||||
| head->value.pair->first = parse_expression(text, index_in_text); | |||||
| } | |||||
| } else { | |||||
| try { | |||||
| head->value.pair->first = parse_atom(text, index_in_text); | |||||
| } | |||||
| } | |||||
| eat_until_code(text, index_in_text); | eat_until_code(text, index_in_text); | ||||
| if (text[(*index_in_text)] == '\0') { | |||||
| create_error(Error_Type::Unexpected_Eof, create_source_code_location(parser_file, parser_line, parser_col)); | |||||
| return nullptr; | |||||
| } | |||||
| if (text[(*index_in_text)] != ')') | |||||
| create_error(Error_Type::Syntax_Error, create_source_code_location(parser_file, parser_line, parser_col)); | |||||
| ++parser_col; | |||||
| ++(*index_in_text); | |||||
| break; | |||||
| } else { | |||||
| head->value.pair->rest = create_ast_node_pair(nullptr, nullptr); | |||||
| head = head->value.pair->rest; | |||||
| } | |||||
| } | |||||
| return expression; | |||||
| } | |||||
| Ast_Node* parse_single_expression(char* text) { | |||||
| parser_file = "stdin"; | |||||
| parser_line = 1; | |||||
| parser_col = 1; | |||||
| int index_in_text = 0; | |||||
| Ast_Node* result; | |||||
| eat_until_code(text, &index_in_text); | |||||
| if (text[(index_in_text)] == '\0') | |||||
| return create_ast_node_nil(); | |||||
| if (text[(index_in_text)] == '(' || text[(index_in_text)] == '\'' ) | |||||
| try { | |||||
| result = parse_expression(text, &index_in_text); | |||||
| if (text[(*index_in_text)] == ')') { | |||||
| head->value.pair->rest = create_ast_node_nil(); | |||||
| ++parser_col; | |||||
| ++(*index_in_text); | |||||
| break; | |||||
| } else if (text[(*index_in_text)] == '.') { | |||||
| ++parser_col; | |||||
| ++(*index_in_text); | |||||
| eat_until_code(text, index_in_text); | |||||
| if (text[(*index_in_text)] == '(') | |||||
| head->value.pair->rest = parse_expression(text, index_in_text); | |||||
| else | |||||
| head->value.pair->rest = parse_atom(text, index_in_text); | |||||
| eat_until_code(text, index_in_text); | |||||
| if (text[(*index_in_text)] != ')') | |||||
| create_error(Error_Type::Syntax_Error, create_source_code_location(parser_file, parser_line, parser_col)); | |||||
| ++parser_col; | |||||
| ++(*index_in_text); | |||||
| break; | |||||
| } else { | |||||
| head->value.pair->rest = create_ast_node_pair(nullptr, nullptr); | |||||
| head = head->value.pair->rest; | |||||
| } | |||||
| } | } | ||||
| else | |||||
| try { | |||||
| result = parse_atom(text, &index_in_text); | |||||
| // check if we have to create or delete or run macros | |||||
| if (expression->value.pair->first->type == Ast_Node_Type::Symbol) { | |||||
| if (string_equal("define-syntax", expression->value.pair->first->value.symbol->identifier)) { | |||||
| // create a new macro | |||||
| Ast_Node* arguments = expression->value.pair->rest; | |||||
| int arguments_length; | |||||
| // HACK(Felix): almost code duplicate from | |||||
| // `built_ins.cpp`: special-lambda | |||||
| try { | |||||
| arguments_length = list_length(arguments); | |||||
| } | |||||
| // (define-syntax defun (name args :rest body) (...)) | |||||
| if (arguments_length < 2) { | |||||
| create_error(Error_Type::Wrong_Number_Of_Arguments, expression->sourceCodeLocation); | |||||
| return nullptr; | |||||
| } | |||||
| if (arguments->value.pair->first->type != Ast_Node_Type::Symbol) { | |||||
| create_error(Error_Type::Type_Missmatch, expression->sourceCodeLocation); | |||||
| return nullptr; | |||||
| } | |||||
| // extract the name | |||||
| Ast_Node* symbol_for_macro = arguments->value.pair->first; | |||||
| arguments = arguments->value.pair->rest; | |||||
| Function* function = new(Function); | |||||
| function->parent_environment = environment_for_macros; | |||||
| function->is_special_form = true; | |||||
| // if parameters were specified | |||||
| if (arguments->value.pair->first->type != Ast_Node_Type::Nil) { | |||||
| try { | |||||
| assert_type(arguments->value.pair->first, Ast_Node_Type::Pair); | |||||
| } | |||||
| try { | |||||
| parse_argument_list(arguments->value.pair->first, function); | |||||
| } | |||||
| } 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; | |||||
| // if there is a docstring, use it | |||||
| if (arguments->value.pair->first->type == Ast_Node_Type::String) { | |||||
| function->docstring = arguments->value.pair->first->value.string->value; | |||||
| arguments = arguments->value.pair->rest; | |||||
| } else { | |||||
| function->docstring = nullptr; | |||||
| } | |||||
| // we are now in the function body, just wrap it in an | |||||
| // implicit prog | |||||
| function->body = create_ast_node_pair( | |||||
| create_ast_node_symbol("prog"), | |||||
| arguments); | |||||
| Ast_Node* macro = new(Ast_Node); | |||||
| macro->type = Ast_Node_Type::Function; | |||||
| macro->value.function = function; | |||||
| define_symbol(symbol_for_macro, macro, environment_for_macros); | |||||
| // print_environment(environment_for_macros); | |||||
| return create_ast_node_nil(); | |||||
| } else if (string_equal("delete-syntax", expression->value.pair->first->value.symbol->identifier)) { | |||||
| /* --- deleting an existing macro --- */ | |||||
| // TODO(Felix): this is a hard one because when | |||||
| // environments will be made from hashmaps, how can we | |||||
| // delete stuff from hashmaps? If we do probing on | |||||
| // collision and then delte the first colliding entry, | |||||
| // how can we find the second one? How many probes do | |||||
| // we have to do to know for sure that an elemenet is | |||||
| // not in the hashmap? It would be much easier if we | |||||
| // never deleted any elements from the hashmap, so | |||||
| // that, when an entry is not found immidiately, we | |||||
| // know for sure that it does not exist in the table. | |||||
| create_error(Error_Type::Not_Yet_Implemented, expression->sourceCodeLocation); | |||||
| return nullptr; | |||||
| } else { | |||||
| // if threre is a macro named like this, then macroexpand | |||||
| // if not it is regular code, dont touch. | |||||
| for (int i = 0; i < environment_for_macros->next_index; ++i) { | |||||
| if (string_equal(expression->value.pair->first->value.symbol->identifier, environment_for_macros->keys[i]) && | |||||
| environment_for_macros->values[i]->type == Ast_Node_Type::Function) | |||||
| { | |||||
| // every `Function` that is defined in this | |||||
| // environment _has_ to be a macro because | |||||
| // only built_ins are in there otherwise, | |||||
| // which are `CFunction`s. | |||||
| try { | |||||
| expression = eval_expr(expression, environment_for_macros); | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| } | } | ||||
| eat_until_code(text, &index_in_text); | |||||
| if (text[(index_in_text)] == '\0') | |||||
| return result; | |||||
| create_error(Error_Type::Trailing_Garbage, create_source_code_location(parser_file, parser_line, parser_col)); | |||||
| return nullptr; | |||||
| } | |||||
| Ast_Node_Array_List* parse_program(char* file_name, char* text) { | |||||
| parser_file = (char*)malloc(strlen(file_name) * sizeof(char) + 1); | |||||
| strcpy(parser_file, file_name); | |||||
| parser_line = 1; | |||||
| parser_col = 0; | |||||
| Ast_Node_Array_List* program = create_Ast_Node_Array_List(16); | |||||
| return expression; | |||||
| } | |||||
| int index_in_text = 0; | |||||
| Ast_Node* parse_single_expression(char* text) { | |||||
| parser_file = "stdin"; | |||||
| parser_line = 1; | |||||
| parser_col = 1; | |||||
| while (text[index_in_text] != '\0') { | |||||
| switch (text[index_in_text]) { | |||||
| case '(': { | |||||
| Ast_Node* parsed; | |||||
| int index_in_text = 0; | |||||
| Ast_Node* result; | |||||
| eat_until_code(text, &index_in_text); | |||||
| if (text[(index_in_text)] == '\0') | |||||
| return create_ast_node_nil(); | |||||
| if (text[(index_in_text)] == '(' || text[(index_in_text)] == '\'' ) | |||||
| try { | try { | ||||
| parsed = parse_expression(text, &index_in_text); | |||||
| result = parse_expression(text, &index_in_text); | |||||
| } | |||||
| else | |||||
| try { | |||||
| result = parse_atom(text, &index_in_text); | |||||
| } | |||||
| eat_until_code(text, &index_in_text); | |||||
| if (text[(index_in_text)] == '\0') | |||||
| return result; | |||||
| create_error(Error_Type::Trailing_Garbage, create_source_code_location(parser_file, parser_line, parser_col)); | |||||
| return nullptr; | |||||
| } | |||||
| Ast_Node_Array_List* parse_program(char* file_name, char* text) { | |||||
| parser_file = (char*)malloc(strlen(file_name) * sizeof(char) + 1); | |||||
| strcpy(parser_file, file_name); | |||||
| parser_line = 1; | |||||
| parser_col = 0; | |||||
| Ast_Node_Array_List* program = create_Ast_Node_array_list(16); | |||||
| int index_in_text = 0; | |||||
| while (text[index_in_text] != '\0') { | |||||
| switch (text[index_in_text]) { | |||||
| case '(': { | |||||
| Ast_Node* parsed; | |||||
| try { | |||||
| parsed = parse_expression(text, &index_in_text); | |||||
| } | |||||
| append_to_Ast_Node_array_list(program, parsed); | |||||
| } break; | |||||
| case ';': | |||||
| case ' ': | |||||
| case '\t': | |||||
| case '\n': | |||||
| case '\r': { | |||||
| eat_until_code(text, &index_in_text); | |||||
| } break; | |||||
| default: | |||||
| /* syntax error */ | |||||
| create_error(Error_Type::Syntax_Error, create_source_code_location(parser_file, parser_line, parser_col)); | |||||
| return nullptr; | |||||
| } | } | ||||
| append_to_Ast_Node_Array_List(program, parsed); | |||||
| } break; | |||||
| case ';': | |||||
| case ' ': | |||||
| case '\t': | |||||
| case '\n': | |||||
| case '\r': { | |||||
| eat_until_code(text, &index_in_text); | |||||
| } break; | |||||
| default: | |||||
| /* syntax error */ | |||||
| create_error(Error_Type::Syntax_Error, create_source_code_location(parser_file, parser_line, parser_col)); | |||||
| return nullptr; | |||||
| } | } | ||||
| return program; | |||||
| } | } | ||||
| return program; | |||||
| } | |||||
| #undef inject_scl | #undef inject_scl | ||||
| } | |||||
| @@ -87,7 +87,7 @@ | |||||
| testresult test_eval_operands() { | 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 = Parser::parse_single_expression(operands_string); | |||||
| int operands_length; | int operands_length; | ||||
| operands = eval_arguments(operands, create_built_ins_environment(), &operands_length); | operands = eval_arguments(operands, create_built_ins_environment(), &operands_length); | ||||
| @@ -128,47 +128,47 @@ testresult test_parse_atom() { | |||||
| "sym +"; // symbols | "sym +"; // symbols | ||||
| // test numbers | // test numbers | ||||
| Ast_Node* result = parse_atom(string, &index_in_text); | |||||
| Ast_Node* result = Parser::parse_atom(string, &index_in_text); | |||||
| assert_equal_type(result, Ast_Node_Type::Number); | assert_equal_type(result, Ast_Node_Type::Number); | ||||
| assert_equal_double(result->value.number->value, 123); | assert_equal_double(result->value.number->value, 123); | ||||
| ++index_in_text; | ++index_in_text; | ||||
| result = parse_atom(string, &index_in_text); | |||||
| result = Parser::parse_atom(string, &index_in_text); | |||||
| assert_equal_type(result, Ast_Node_Type::Number); | assert_equal_type(result, Ast_Node_Type::Number); | ||||
| assert_equal_double(result->value.number->value, -1.23e-2); | assert_equal_double(result->value.number->value, -1.23e-2); | ||||
| // test strings | // test strings | ||||
| ++index_in_text; | ++index_in_text; | ||||
| result = parse_atom(string, &index_in_text); | |||||
| result = Parser::parse_atom(string, &index_in_text); | |||||
| assert_equal_type(result, Ast_Node_Type::String); | assert_equal_type(result, Ast_Node_Type::String); | ||||
| assert_equal_string(result->value.string->value, "asd"); | assert_equal_string(result->value.string->value, "asd"); | ||||
| // test keywords | // test keywords | ||||
| ++index_in_text; | ++index_in_text; | ||||
| result = parse_atom(string, &index_in_text); | |||||
| result = Parser::parse_atom(string, &index_in_text); | |||||
| assert_equal_type(result, Ast_Node_Type::Keyword); | assert_equal_type(result, Ast_Node_Type::Keyword); | ||||
| assert_equal_string(result->value.keyword->identifier, "key1"); | assert_equal_string(result->value.keyword->identifier, "key1"); | ||||
| ++index_in_text; | ++index_in_text; | ||||
| result = parse_atom(string, &index_in_text); | |||||
| result = Parser::parse_atom(string, &index_in_text); | |||||
| assert_equal_type(result, Ast_Node_Type::Keyword); | assert_equal_type(result, Ast_Node_Type::Keyword); | ||||
| assert_equal_string(result->value.keyword->identifier, "key:2"); | assert_equal_string(result->value.keyword->identifier, "key:2"); | ||||
| // test symbols | // test symbols | ||||
| ++index_in_text; | ++index_in_text; | ||||
| result = parse_atom(string, &index_in_text); | |||||
| result = Parser::parse_atom(string, &index_in_text); | |||||
| assert_equal_type(result, Ast_Node_Type::Symbol); | assert_equal_type(result, Ast_Node_Type::Symbol); | ||||
| assert_equal_string(result->value.symbol->identifier, "sym"); | assert_equal_string(result->value.symbol->identifier, "sym"); | ||||
| ++index_in_text; | ++index_in_text; | ||||
| result = parse_atom(string, &index_in_text); | |||||
| result = Parser::parse_atom(string, &index_in_text); | |||||
| assert_equal_type(result, Ast_Node_Type::Symbol); | assert_equal_type(result, Ast_Node_Type::Symbol); | ||||
| assert_equal_string(result->value.symbol->identifier, "+"); | assert_equal_string(result->value.symbol->identifier, "+"); | ||||
| @@ -179,7 +179,7 @@ testresult test_parse_expression() { | |||||
| int index_in_text = 0; | int index_in_text = 0; | ||||
| char string[] = "(fun + 12)"; | char string[] = "(fun + 12)"; | ||||
| Ast_Node* result = parse_expression(string, &index_in_text); | |||||
| Ast_Node* result = Parser::parse_expression(string, &index_in_text); | |||||
| assert_no_error(error); | assert_no_error(error); | ||||
| assert_equal_type(result, Ast_Node_Type::Pair); | assert_equal_type(result, Ast_Node_Type::Pair); | ||||
| @@ -205,7 +205,7 @@ testresult test_parse_expression() { | |||||
| char string2[] = "(define fun (lambda (x) (+ 5 (* x x ))))"; | char string2[] = "(define fun (lambda (x) (+ 5 (* x x ))))"; | ||||
| index_in_text = 0; | index_in_text = 0; | ||||
| result = parse_expression(string2, &index_in_text); | |||||
| result = Parser::parse_expression(string2, &index_in_text); | |||||
| assert_no_error(error); | assert_no_error(error); | ||||
| assert_equal_type(result, Ast_Node_Type::Pair); | assert_equal_type(result, Ast_Node_Type::Pair); | ||||
| @@ -232,7 +232,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 = Parser::parse_single_expression(exp_string); | |||||
| Ast_Node* result = eval_expr(expression, create_built_ins_environment()); | Ast_Node* result = eval_expr(expression, create_built_ins_environment()); | ||||
| assert_no_error(error); | assert_no_error(error); | ||||
| @@ -245,7 +245,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 = Parser::parse_single_expression(exp_string); | |||||
| Ast_Node* result = eval_expr(expression, create_built_ins_environment()); | Ast_Node* result = eval_expr(expression, create_built_ins_environment()); | ||||
| assert_no_error(error); | assert_no_error(error); | ||||
| @@ -259,7 +259,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 = Parser::parse_single_expression(exp_string); | |||||
| Ast_Node* result = eval_expr(expression, create_built_ins_environment()); | Ast_Node* result = eval_expr(expression, create_built_ins_environment()); | ||||
| assert_no_error(error); | assert_no_error(error); | ||||
| @@ -273,7 +273,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 = Parser::parse_single_expression(exp_string); | |||||
| Ast_Node* result = eval_expr(expression, create_built_ins_environment()); | Ast_Node* result = eval_expr(expression, create_built_ins_environment()); | ||||
| assert_null(error); | assert_null(error); | ||||
| @@ -287,7 +287,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 = Parser::parse_single_expression(exp_string1); | |||||
| Ast_Node* result = eval_expr(expression, create_built_ins_environment()); | Ast_Node* result = eval_expr(expression, create_built_ins_environment()); | ||||
| assert_no_error(error); | assert_no_error(error); | ||||
| @@ -296,7 +296,7 @@ testresult test_built_in_if() { | |||||
| assert_equal_double(result->value.number->value, 4); | assert_equal_double(result->value.number->value, 4); | ||||
| char exp_string2[] = "(if () 4 5)"; | char exp_string2[] = "(if () 4 5)"; | ||||
| expression = parse_single_expression(exp_string2); | |||||
| expression = Parser::parse_single_expression(exp_string2); | |||||
| result = eval_expr(expression, create_built_ins_environment()); | result = eval_expr(expression, create_built_ins_environment()); | ||||
| assert_no_error(error); | assert_no_error(error); | ||||
| @@ -309,7 +309,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 = Parser::parse_single_expression(exp_string1); | |||||
| Ast_Node* result = eval_expr(expression, create_built_ins_environment()); | Ast_Node* result = eval_expr(expression, create_built_ins_environment()); | ||||
| assert_no_error(error); | assert_no_error(error); | ||||
| @@ -318,7 +318,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 = Parser::parse_single_expression(exp_string2); | |||||
| result = eval_expr(expression, create_built_ins_environment()); | result = eval_expr(expression, create_built_ins_environment()); | ||||
| assert_no_error(error); | assert_no_error(error); | ||||
| @@ -330,7 +330,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 = Parser::parse_single_expression(exp_string1); | |||||
| Ast_Node* result = eval_expr(expression, create_built_ins_environment()); | Ast_Node* result = eval_expr(expression, create_built_ins_environment()); | ||||
| assert_no_error(error); | assert_no_error(error); | ||||
| @@ -339,7 +339,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 = Parser::parse_single_expression(exp_string2); | |||||
| result = eval_expr(expression, create_built_ins_environment()); | result = eval_expr(expression, create_built_ins_environment()); | ||||
| assert_no_error(error); | assert_no_error(error); | ||||
| @@ -352,7 +352,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 = Parser::parse_single_expression(exp_string1); | |||||
| Ast_Node* result = eval_expr(expression, create_built_ins_environment()); | Ast_Node* result = eval_expr(expression, create_built_ins_environment()); | ||||
| // a true case | // a true case | ||||
| @@ -362,7 +362,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 = Parser::parse_single_expression(exp_string2); | |||||
| result = eval_expr(expression, create_built_ins_environment()); | result = eval_expr(expression, create_built_ins_environment()); | ||||
| assert_no_error(error); | assert_no_error(error); | ||||
| @@ -374,6 +374,7 @@ testresult test_built_in_not() { | |||||
| void run_all_tests() { | void run_all_tests() { | ||||
| log_level = Log_Level::None; | log_level = Log_Level::None; | ||||
| Parser::init(create_built_ins_environment()); | |||||
| printf("-- Parsing --\n"); | printf("-- Parsing --\n"); | ||||
| invoke_test(test_parse_atom); | invoke_test(test_parse_atom); | ||||