commit 29ff4cf2d8e4b3b8212cc6f6d2cd33a59260663b Author: Dane Johnson Date: Thu Nov 25 11:09:29 2021 -0600 Init commit diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..abf58de --- /dev/null +++ b/Makefile @@ -0,0 +1,18 @@ +.PHONY: all clean +all: deelang + +lexer.o lexer.h: parser.h lexer.l + flex -o lexer.c --header-file=lexer.h lexer.l + $(CC) -c lexer.c + rm lexer.c + +parser.h parser.o: parser.y + bison -o parser.c --header=parser.h parser.y + $(CC) -c parser.c + rm parser.c + +deelang: deelang.c lexer.o parser.o + $(CC) -o $@ $^ + +clean: + rm -rf parser.o lexer.o parser.h lexer.h deelang diff --git a/blockexample.dee b/blockexample.dee new file mode 100644 index 0000000..9ca2e1b --- /dev/null +++ b/blockexample.dee @@ -0,0 +1,15 @@ +fun <- -> + do-something() + do-something-else() + + +foo <- -> + bar <- -> + baz <- -> + print("Deep in here") + print ("Inside foo but not in baz or bar") + +foo() ## Inside foo but not in baz or bar + +add <- x -> y -> + return x + y \ No newline at end of file diff --git a/deelang.c b/deelang.c new file mode 100644 index 0000000..9f94509 --- /dev/null +++ b/deelang.c @@ -0,0 +1,7 @@ +#include "lexer.h" +#include "parser.h" + +void main() { + yydebug = 1; + yyparse(); +} diff --git a/example.dee b/example.dee new file mode 100644 index 0000000..9c86291 --- /dev/null +++ b/example.dee @@ -0,0 +1,57 @@ +## By the end of the book I want to have an interpreter +## for this little language, maybe a compiler too + +print(1 + 1) ## 2 +print("Hello world!") ## Hello world + +name <- read() +print("Hi there $name") ## Hi there + +have <- 10 +want <- 11 +need <- have - want ## This is subtraction +have-want <- 1 ## This is a variable named "have-want" + +print HaVe-WAnt ## 1 (case doesn't matter) +%%option meaningful-casing +Apples <- 1 +print apples ## (or maybe it does) + +print("one fish"); print("two fish") ## Two variables on the same line + +say-hi <- -> print("Hi from inside a function") +say-hi() ## Hi from inside a function + +say-hi-again <- say-hi +say-hi-again() ## "Hi from inside a function" (First class functions!) + +duck <- { + bills <- 1 + talk <- -> print("Quack") + eats? <- food -> food = "bread" +} ## Objects created on the fly + +print duck.eats?("bread") ## true +print duck.eats?("corn") ## false + +cow <- { + talk <- print("Moo") + eats? <- food -> food oneof "grass", "corn" +} + +human <- { + eats? <- _ -> true +} + +print cow.eats?("grass") ## true +print cow.eats?("corn") ## true + +talk-or-pass-wind <- character -> + if character has talk then + character.talk() + else + print("*Fart*") + +talk-or-pass-wind(duck) ## "Quack" +talk-or-pass-wind(cow) ## "Moo" +talk-or-pass-wind(human) ## "*Fart*" \ No newline at end of file diff --git a/lexdemo.c b/lexdemo.c new file mode 100644 index 0000000..c80f449 --- /dev/null +++ b/lexdemo.c @@ -0,0 +1,11 @@ +#include "lexer.h" +#include "parser.h" + +void main() { + int t; + while ((t = yylex()) != 0) { + printf("yylex=0x%x\n", t); + printf("yylval=%s\n", yylval.sym); + } + printf("Byebye!"); +} diff --git a/lexer.l b/lexer.l new file mode 100644 index 0000000..7801203 --- /dev/null +++ b/lexer.l @@ -0,0 +1,66 @@ +%{ +#include "parser.h" +int stack[100]; // Max indentation depth +int sp = -1; +int peek(); +void push(int a); +void pop(); +#define YY_USER_INIT push(0); BEGIN(freshline); +%} + +%option noyywrap + +%x freshline + +digit [0-9] +letter [A-Za-z] + +%% +""/[^\t ] { +if (peek() == 0) { + BEGIN(0); +} else { + pop(); yyless(0); return DEDENT; +} + } +[ \t]+ { +if (peek() == yyleng) { + BEGIN(0); // Same indentation, continue +} else if (peek() > yyleng) { + pop(); yyless(0); return DEDENT; + // Same rule again until the stack is even +} else { + push(yyleng); BEGIN(0); return INDENT; +} + } +#[^\n]* // Eat comments +\"[^"]*\" yylval.sym = yytext; return STRING; +if return IF; +else return ELSE; +elif return ELIF; +{digit}+|{digit}*\.{digit}+ yylval.num = atof(yytext); return NUM; +{letter}({letter}|{digit}|[?-])* yylval.sym = yytext; return ID; +"<-" return GETS; +"->" return MAPS; +[().,*/+-] return yytext[0]; +[\t ] // Eat whitespace not first on a line +"/"\n // Eat newlines ending in / +[\n;] BEGIN(freshline); return STOP; +. fprintf(stderr, "Scanning error!\nOffender: %s\n", yytext); exit(1); +%% + +int peek() { + printf("Peek @ %d\n", stack[sp]); + return stack[sp]; +} + +void push(int a) { + printf("Push @ %d\n", a); + sp++; + stack[sp] = a; +} + +void pop() { + printf("Pop! Sp @ %d\n", sp); + sp--; +} diff --git a/parser.y b/parser.y new file mode 100644 index 0000000..f00d78b --- /dev/null +++ b/parser.y @@ -0,0 +1,52 @@ +%{ +#include +int yylex(); +int yyerror(const char* p) { fprintf(stderr, p); } +%} + +%define parse.trace + +%union { + char* sym; + float num; +} + +%token ID +%token STOP +%token NUM +%token STRING +%token INDENT DEDENT +%token IF ELIF ELSE + +%right GETS +%left '+' '-' +%left '/' '*' +%left MAPS + +%% +program: statements | statements statement; +statements: statements statement STOP + | statements STOP + | // null production ; +statement: assignment | funcall | conditional; +assignment: ID GETS expr; +expr: funcdef + | funcall + | expr '+' expr + | expr '-' expr + | expr '*' expr + | expr '/' expr + | '(' expr ')' + | ID | NUM | STRING; + +funcdef: param MAPS expr | param MAPS block; +param: ID | '_' | // null production; +funcall: ID '(' exprlist ')' +exprlist: exprlist ',' expr | expr | // null production; + +conditional: IF expr block elifs + | IF expr block elifs ELSE block; +elifs: ELIF expr block elifs| // null production; + +block: STOP INDENT statement statements DEDENT +%% \ No newline at end of file