summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile25
-rw-r--r--README.ORG7
-rw-r--r--lisp.c95
-rw-r--r--lisp.h37
-rw-r--r--lisp.l25
-rw-r--r--lisp.y59
6 files changed, 248 insertions, 0 deletions
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..2dc8a02
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,25 @@
+LEX = flex
+YACC = bison
+CC = gcc -g
+CFLAGS = -DYYDEBUG=1
+LIBS = -lreadline -lfl -lm
+
+lisp: lisp.c lisp.tab.o lex.yy.o
+ $(CC) $(CFLAGS) -o $@ lisp.c lisp.tab.o lex.yy.o $(LIBS)
+
+lisp.tab.c lisp.tab.h: lisp.y
+ $(YACC) -d lisp.y
+
+lex.yy.c: lisp.l lisp.tab.h
+ $(LEX) --header-file=lex.yy.h lisp.l
+
+lisp.tab.o: lisp.tab.c
+ $(CC) $(CFLAGS) -c lisp.tab.c
+
+lex.yy.o: lex.yy.c
+ $(CC) $(CFLAGS) -c lex.yy.c
+
+clean:
+ rm -f *.o lisp lisp.tab.c lisp.tab.h lex.yy.c lex.yy.h
+
+.PHONY: clean
diff --git a/README.ORG b/README.ORG
new file mode 100644
index 0000000..500e08b
--- /dev/null
+++ b/README.ORG
@@ -0,0 +1,7 @@
+* Lisp Interpreter
+using lex and yacc
+
+** Resources:
++ [[https://github.com/AdhamAfis/yacc-parser-tutorial][yacc-parser-tutorial]]
++ [[https://en.wikipedia.org/wiki/S-expression][s-expressions]]
++ [[https://www.bowaggoner.com/writeups/jumpstart/flexbison/jumpstart_flexbison.pdf?utm_source][flex / bison jumpstart]]
diff --git a/lisp.c b/lisp.c
new file mode 100644
index 0000000..e6dc02e
--- /dev/null
+++ b/lisp.c
@@ -0,0 +1,95 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <readline/readline.h>
+#include <readline/history.h>
+#include "lisp.h"
+#include "lex.yy.h"
+#include "lisp.tab.h"
+
+value *make_int(int i) {
+ value *val = malloc(sizeof(value));
+ val->type = VALTYPE_INT;
+ val->as.i = i;
+ return val;
+}
+
+value *make_double(double d) {
+ value *val = malloc(sizeof(value));
+ val->type = VALTYPE_DOUBLE;
+ val->as.d = d;
+ return val;
+}
+
+value *make_symbol(const char *s) {
+ value *val = malloc(sizeof(value));
+ val->type = VALTYPE_SYMBOL;
+ val->as.sym = s;
+ return val;
+}
+
+value *make_string(const char *s) {
+ value *val = malloc(sizeof(value));
+ val->type = VALTYPE_STRING;
+ val->as.str = s;
+ return val;
+}
+
+value *make_nil() {
+ value *val = malloc(sizeof(value));
+ val->type = VALTYPE_NIL;
+ return val;
+}
+
+value *cons(value *car, value *cdr) {
+ value *val = malloc(sizeof(value));
+ val->type = VALTYPE_PAIR;
+ val->as.pair.car = car;
+ val->as.pair.cdr = cdr;
+ return val;
+}
+
+void *print_val(value *val){
+ char *str;
+ switch (val->type) {
+ case VALTYPE_INT:
+ printf("%d", val->as.i);
+ break;
+
+ case VALTYPE_DOUBLE:
+ printf("%f", val->as.d);
+ break;
+
+ case VALTYPE_SYMBOL:
+ printf("%s", val->as.sym);
+ break;
+
+ case VALTYPE_STRING:
+ printf("\"%s\"", val->as.str);
+ break;
+
+ case VALTYPE_NIL:
+ printf("()");
+ break;
+
+ case VALTYPE_PAIR:
+ printf("(");
+ print_val(val->as.pair.car);
+ printf(" . ");
+ print_val(val->as.pair.cdr);
+ printf(")");
+ default:
+ }
+
+ return val;
+}
+
+int main(void) {
+ char* line;
+ while ((line = readline("λ > ")) != NULL) {
+ if (*line) add_history(line);
+ yy_scan_string(line); // feed input to Flex/Bison
+ yyparse();
+ free(line);
+ }
+ return 0;
+}
diff --git a/lisp.h b/lisp.h
new file mode 100644
index 0000000..535bc50
--- /dev/null
+++ b/lisp.h
@@ -0,0 +1,37 @@
+#ifndef LISP_H
+#define LISP_H
+
+typedef enum {
+ VALTYPE_INT,
+ VALTYPE_DOUBLE,
+ VALTYPE_SYMBOL,
+ VALTYPE_STRING,
+ VALTYPE_NIL,
+ VALTYPE_PAIR
+} val_type;
+
+typedef struct value value;
+struct value {
+ val_type type;
+ union {
+ int i;
+ double d;
+ const char *sym;
+ const char *str;
+ struct {
+ value *car;
+ value *cdr;
+ } pair;
+ } as;
+};
+
+value *make_int(int i);
+value *make_double(double d);
+value *make_symbol(const char *sym);
+value *make_string(const char *str);
+value *make_nil();
+value *cons(value *car, value *cdr);
+
+void *print_val(value *val);
+
+#endif
diff --git a/lisp.l b/lisp.l
new file mode 100644
index 0000000..329fc0f
--- /dev/null
+++ b/lisp.l
@@ -0,0 +1,25 @@
+%{
+#include <stdlib.h>
+#include "lisp.h"
+#include "lisp.tab.h"
+
+int tcnt = 0;
+%}
+
+%%
+[+-]?([0-9]+|([0-9]*\.[0-9]+))([eE][-+]?[0-9]+)? {
+ yylval.dval = atof(yytext);
+ printf("%d: NUMBER: %f\n", ++tcnt, yylval.dval);
+ return NUMBER;
+ }
+[ \t\n]+ { printf("%d: WHITESPACE\n", tcnt); return WHITESPACE; }
+"(" { printf("%d: LPAREN\n", ++tcnt); return LPAREN; }
+")" { printf("%d: RPAREN\n", ++tcnt); return RPAREN; }
+"\." { printf("%d: DOT\n", ++tcnt); return DOT; }
+"+" { printf("%d: PLUS\n", ++tcnt); return PLUS; }
+"-" { printf("%d: MINUS\n", ++tcnt); return MINUS; }
+"*" { printf("%d: MULT\n", ++tcnt); return MULT; }
+"/" { printf("%d: DIV\n", ++tcnt); return DIV; }
+\".*\" { printf("%d: STRING\n", ++tcnt); return STRING; }
+. ; // do nothing
+%%
diff --git a/lisp.y b/lisp.y
new file mode 100644
index 0000000..29031a4
--- /dev/null
+++ b/lisp.y
@@ -0,0 +1,59 @@
+%{
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include "lisp.h"
+
+int yylex(void);
+int yyerror(const char *s);
+
+static int is_integer(double x) {
+ return floor(x) == x && isfinite(x);
+}
+%}
+
+%union {
+ int ival;
+ double dval;
+ const char *strval;
+ value *val;
+}
+%token <dval> NUMBER
+%token <strval> STRING
+%token <ival> LPAREN RPAREN DOT WHITESPACE
+%token <ival> PLUS MINUS MULT DIV
+
+%type <val> sexpr atom sexprs
+
+%start sexprs
+
+%%
+opt_ws: /* matches no whitespace */
+ | WHITESPACE;
+
+sexprs: sexpr
+ | sexprs opt_ws sexpr;
+
+sexpr: atom
+ | LPAREN opt_ws sexpr opt_ws DOT opt_ws sexpr opt_ws RPAREN
+ {
+ $$ = cons($3, $7);
+ print_val($$);
+ printf("\n");
+ };
+
+atom: NUMBER
+ {
+ if(is_integer($1)) {
+ $$ = make_int($1);
+ } else {
+ $$ = make_double($1);
+ }
+ }
+ | LPAREN opt_ws RPAREN { $$ = make_nil(); }
+ | STRING { $$ = make_string($1); };
+%%
+
+int yyerror(const char *s) {
+ printf("%s\n",s);
+}