- Quick summary
This is a Java version of the lox interpreter from the wonderful book Crafting Interpreters
It is written in pure Java, using only 2 external Java libraries for testing: JUnit 5 and AssertJ.
This implementation focuses heavily on unit testing. I tried to unit test as many scenarios as is reasonably possible since I felt this really deepened my understanding of how exactly the interpreter works.
This is also the reason why this implementation contains relatively little acceptance tests.
be.guldentops.geert.lox.LoxMain and be.guldentops.geert.lox.tools.GenerateAbstractSyntaxTree are not tested by design, see their respective Javadocs why.
Maven (3) is used to compile the code and run the tests:
- You can run maven manually using
mvn clean install
from the command line. - You can run the cleanInstall.sh script, e.g.:
> chmod +x cleanInstall.sh
> ./cleanInstall.sh
- Run the project's only pom.xml file using your favourite IDE! (IntelliJ IDEA, Eclipse, Netbeans, etc.)
Lox has 2 modes it can run in:
-
REPL mode: Run the main method in the LoxMain class with exactly 1 program argument, the path to the Lox script you want to run.
-
Script mode: Run the main method in the LoxMain class with no program arguments.
The Expression class and its subtypes are generated by the GenerateAbstractSyntaxTree class.
GenerateAbstractSyntaxTree has a main method which accepts exactly 1 program argument, the output directory of the generated Expression class.
E.g.: ${ABSOLUTE_PATH_TO_JLOX_PROJECT}/jlox/src/main/java/be/guldentops/geert/lox/grammar
Through maven: mvn clean test
Using an IDE, for instance IntelliJ IDEA: Run all JUnit tests in package be.guldentops.geert.lox
Run all the tests except the acceptance tests which are located in the be.guldentops.geert.lox.LoxAcceptanceTest class
Run all the tests in the be.guldentops.geert.lox.LoxAcceptanceTest class
NUMBER → DIGIT+ ( "." DIGIT+ )? ;
STRING → '"' <any char except '"'>* '"' ;
IDENTIFIER → ALPHA ( ALPHA | DIGIT )* ;
ALPHA → 'a' ... 'z' | 'A' ... 'Z' | '_' ;
DIGIT → '0' ... '9' ;
program → declaration* EOF ;
declaration → classDecl
| funDecl
| varDecl
| statement ;
classDecl → "class" IDENTIFIER ( "<" IDENTIFIER )?
"{" function* "}" ;
funDecl → "fun" function ;
function → IDENTIFIER "(" parameters? ")" block ;
parameters → IDENTIFIER ( "," IDENTIFIER )* ;
varDecl → "var" IDENTIFIER ( "=" expression )? ";" ;
statement → exprStmt
| forStmt
| ifStmt
| printStmt
| returnStmt
| whileStmt
| block ;
exprStmt → expression ";" ;
forStmt → "for" "(" ( varDecl | exprStmt | ";" )
expression? ";"
expression? ")" statement ;
ifStmt → "if" "(" expression ")" statement ( "else" statement )? ;
printStmt → "print" expression ";" ;
returnStmt → "return" expression? ";" ;
whileStmt → "while" "(" expression ")" statement ;
block → "{" declaration* "}" ;
expression → assignment ;
assignment → ( call "." )? IDENTIFIER "=" assignment
| logic_or ;
logic_or → logic_and ( "or" logic_and )* ;
logic_and → equality ( "and" equality )* ;
equality → comparison ( ( "!=" | "==" ) comparison )* ;
comparison → addition ( ( ">" | ">=" | "<" | "<=" ) addition )* ;
addition → multiplication ( ( "-" | "+" ) multiplication )* ;
multiplication → unary ( ( "/" | "*" ) unary )* ;
unary → ( "!" | "-" ) unary | call ;
call → primary ( "(" arguments? ")" | "." IDENTIFIER )* ;
arguments → expression ( "," expression )* ;
primary → "true" | "false" | "nil" | "this"
| NUMBER | STRING | IDENTIFIER | "(" expression ")"
| "super" "." IDENTIFIER ;
Name | Operators | Associates |
---|---|---|
Unary | ! - |
Right |
Multiplication | / * |
Left |
Addition | - + |
Left |
Comparison | > >= < <= |
Left |
Equality | == != |
Left |