summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFelix Perktold <Felix.Perktold@student.uibk.ac.at>2025-12-23 14:29:00 +0100
committerFelix Perktold <Felix.Perktold@student.uibk.ac.at>2025-12-23 14:29:00 +0100
commitc63cbd8db40fbdeccabd218229010cbfb8a93977 (patch)
tree34a9725eb1207a624cb8486420d780bbfb983482
parentb99be4933bcb850cbaf96f89750e925ecf2e8386 (diff)
fix tests, fix empty input behavior
-rw-r--r--lisp.c1
-rw-r--r--lisp.l3
-rw-r--r--lisp.y24
-rw-r--r--lisp/meta_cirular_eval.lisp64
-rw-r--r--lisp/standard_functions.lisp15
-rw-r--r--test.c8
6 files changed, 95 insertions, 20 deletions
diff --git a/lisp.c b/lisp.c
index 6b34bab..a797e8e 100644
--- a/lisp.c
+++ b/lisp.c
@@ -99,6 +99,7 @@ void println_value(value *val){
printf("\n");
}
void print_value(value *val){
+ if(!val) { return; }
switch (val->type) {
case VT_INT:
printf("%d", val->as.i);
diff --git a/lisp.l b/lisp.l
index cb0453a..d5d2c69 100644
--- a/lisp.l
+++ b/lisp.l
@@ -7,9 +7,8 @@ int tcnt = 0;
%}
%%
-[[:space:]&&[^\n]]+ { /* ignore whitespace */ }
+[[:space:]]+ { /* ignore whitespace */ }
";"[^\n]* { /* ignore comment */ }
-\n { return NLINE; }
"(" { return LPAREN; }
")" { return RPAREN; }
"\." { return DOT; }
diff --git a/lisp.y b/lisp.y
index 086ef93..a4ed142 100644
--- a/lisp.y
+++ b/lisp.y
@@ -5,6 +5,13 @@
int yylex(void);
int yyerror(const char *s);
+
+void eval_and_print_REPL(value *v) {
+ if(!v) { return; }
+ value *ve = eval(global_env, v);
+ printf(";> ");
+ println_value(ve);
+}
%}
%union {
@@ -14,7 +21,7 @@ int yyerror(const char *s);
}
%token <dval> NUMBER
%token <strval> STRING SYMBOL
-%token LPAREN RPAREN DOT QUOTE NLINE
+%token LPAREN RPAREN DOT QUOTE
%type <val> expr atom list list_items
@@ -22,24 +29,13 @@ int yyerror(const char *s);
%%
exprs: /* nothing */
- | expr
- {
- value *v = eval(global_env, $1);
- printf(";> ");
- println_value(v);
- }
- | exprs expr
- {
- value *v = eval(global_env, $2);
- printf(";> ");
- println_value(v);
- };
+ | expr { eval_and_print_REPL($1); }
+ | exprs expr { eval_and_print_REPL($2); }
expr: atom
| list
| LPAREN expr DOT expr RPAREN { $$ = cons($2, $4); }
| QUOTE expr { $$ = cons(make_symbol("quote"), cons($2, make_nil())); }
- | NLINE { $$ = make_nil(); };
list: LPAREN list_items RPAREN { $$ = $2; };
diff --git a/lisp/meta_cirular_eval.lisp b/lisp/meta_cirular_eval.lisp
new file mode 100644
index 0000000..60b7bf8
--- /dev/null
+++ b/lisp/meta_cirular_eval.lisp
@@ -0,0 +1,64 @@
+; metacircular evaluator in your Lisp
+(define global-env '())
+
+; helper to extend environment
+(define (extend-env vars vals base)
+ (cons (cons vars vals) base))
+
+; lookup variable
+(define (lookup var env)
+ (cond
+ ((null? env) (print "symbol " var " not found") ())
+ ((eq? var (car (car env))) (cadr (car env)))
+ (else (lookup var (cdr env)))))
+
+; eval sequence (for lambda bodies)
+(define (eval-sequence exps env)
+ (if (null? (cdr exps))
+ (eval (car exps) env)
+ (begin (eval (car exps) env)
+ (eval-sequence (cdr exps) env))))
+
+; eval
+(define (eval exp env)
+ (cond
+ ((number? exp) exp)
+ ((string? exp) exp)
+ ((symbol? exp) (lookup exp env))
+ ((pair? exp)
+ (let ((head (car exp)) (tail (cdr exp)))
+ (cond
+ ((eq? head 'quote) (car tail))
+ ((eq? head 'if)
+ (let ((test (car tail))
+ (then (car (cdr tail)))
+ (else (car (cdr (cdr tail)))))
+ (if (eval test env)
+ (eval then env)
+ (eval else env))))
+ ((eq? head 'define)
+ (let ((var (car tail))
+ (val (eval (car (cdr tail)) env)))
+ (set! global-env (extend-env (list var) (list val) global-env))
+ 1)) ; return 1 for success
+ ((eq? head 'lambda)
+ (list 'closure (car tail) (cdr tail) env))
+ (else ; procedure application
+ (let ((proc (eval head env))
+ (args (map (lambda (x) (eval x env)) tail)))
+ (apply proc args))))))
+ (else (print "Unknown expression type") ()))
+
+)
+
+; apply
+(define (apply proc args)
+ (cond
+ ((procedure? proc) (apply proc args)) ; builtin
+ ((and (pair? proc) (eq? (car proc) 'closure))
+ (let ((params (cadr proc))
+ (body (caddr proc))
+ (env (cadddr proc)))
+ (eval-sequence body (extend-env params args env))))
+ (else (print "Unknown procedure type") ()))
+)
diff --git a/lisp/standard_functions.lisp b/lisp/standard_functions.lisp
new file mode 100644
index 0000000..9658ef9
--- /dev/null
+++ b/lisp/standard_functions.lisp
@@ -0,0 +1,15 @@
+; comparisons
+(define <= (lambda (x y)
+ (if (< x y)
+ 1
+ (if (eq? x y) 1 ()))))
+(define > (lambda (x y) (< y x)))
+(define >= (lambda (x y) (<= y x)))
+
+; boolean funs
+(define and (lambda (x y)
+ (if x
+ (if y 1 ())
+ ())))
+(define or (lambda (x y) (if x 1 y)))
+(define nand (lambda (x y) (null? (and x y))))
diff --git a/test.c b/test.c
index 48b844c..95cce3f 100644
--- a/test.c
+++ b/test.c
@@ -348,7 +348,7 @@ void test_recursive_factorial() {
void test_eq_ints() {
env *e = env_create(NULL);
- env_define(e, "eq", make_builtin(builtin_eq));
+ env_define(e, "eq", make_builtin(builtin_equal));
value *expr =
cons(make_symbol("eq"),
@@ -365,7 +365,7 @@ void test_eq_ints() {
void test_eq_ints_false() {
env *e = env_create(NULL);
- env_define(e, "eq", make_builtin(builtin_eq));
+ env_define(e, "eq", make_builtin(builtin_equal));
value *expr =
cons(make_symbol("eq"),
@@ -380,7 +380,7 @@ void test_eq_ints_false() {
void test_eq_symbols() {
env *e = env_create(NULL);
- env_define(e, "eq", make_builtin(builtin_eq));
+ env_define(e, "eq", make_builtin(builtin_equal));
value *expr =
cons(make_symbol("eq"),
@@ -395,7 +395,7 @@ void test_eq_symbols() {
void test_eq_lists() {
env *e = env_create(NULL);
- env_define(e, "eq", make_builtin(builtin_eq));
+ env_define(e, "eq", make_builtin(builtin_equal));
value *list1 =
cons(make_int(1),