Bläddra i källkod

Macros actually working

master
FelixBrendel 7 år sedan
förälder
incheckning
5aa92062c3
15 ändrade filer med 702 tillägg och 201 borttagningar
  1. +2
    -1
      .dir-locals.el
  2. +1
    -0
      .gitignore
  3. +147
    -120
      bin/pre.slime
  4. +9
    -4
      bin/test.slime
  5. +39
    -30
      src/ast.c
  6. +72
    -0
      src/built_ins.c
  7. +123
    -10
      src/env.c
  8. +1
    -1
      src/error.c
  9. +121
    -7
      src/eval.c
  10. +2
    -1
      src/helpers.c
  11. +4
    -2
      src/io.c
  12. +2
    -2
      src/main.c
  13. +6
    -2
      src/parse.c
  14. +13
    -13
      src/testing.c
  15. +160
    -8
      todo.org

+ 2
- 1
.dir-locals.el Visa fil

@@ -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))))))

+ 1
- 0
.gitignore Visa fil

@@ -7,3 +7,4 @@
/bin/slime
*.psess
*.vspx
todo.html

+ 147
- 120
bin/pre.slime Visa fil

@@ -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)))

+ 9
- 4
bin/test.slime Visa fil

@@ -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))

+ 39
- 30
src/ast.c Visa fil

@@ -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);


+ 72
- 0
src/built_ins.c Visa fil

@@ -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) {


+ 123
- 10
src/env.c Visa fil

@@ -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);
}

+ 1
- 1
src/error.c Visa fil

@@ -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;


+ 121
- 7
src/eval.c Visa fil

@@ -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);
}
}


+ 2
- 1
src/helpers.c Visa fil

@@ -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);
}


+ 4
- 2
src/io.c Visa fil

@@ -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));


+ 2
- 2
src/main.c Visa fil

@@ -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) {


+ 6
- 2
src/parse.c Visa fil

@@ -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:


+ 13
- 13
src/testing.c Visa fil

@@ -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);


+ 160
- 8
todo.org Visa fil

@@ -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

Laddar…
Avbryt
Spara