A Typescript interpreter for Lox (from Crafting Interpreters)
-
Build the project
yarn build
-
Run the interactive shell
yarn start
or run a source file
yarn start [FILE]
I should probably pack this app for one to be able to just run tslox [FILE] but if you are reading this you probably have no intention of running this or you are actually coding your own version of an interpreter for Lox, so... yeah.
Inside the /examples folder there are multiple (huh) examples of valid* programs you can run.
* Except for the runtimeError.lox file which is designed to throw a Runtime Error.
In the same spirit of the original jlox, I also coded an AST generator in javascript (What!?) that generates the classes for both Expr and Stmt. To execute the generator you have to run:
yarn gen:astThis will write (and overwrite) the syntax/ast/Expr.ts and syntax/ast/Stmt.ts files.
-
Concatenation operator: I don't like to use the same operator (
+) for both number sums and string concatenation. So, differently from the original version of Lox, I implemented the (explicit) string concatenation by using the++operator. -
Everything comes from the
Object: I implemented a simple (empty) global classObjectfrom which every class inherits by default. -
Syntactic sugar: In the original implementation of Lox (jlox) syntactic sugar is implemented by both the parser and the runtime engine (Interpreter), by allowing, for example, a certain field of an AST node to be null, which later would be (null-)checked by the interpreter or other runtime actors to behave as if the field did exist. I prefer the syntactic sugar to be desugared explicitly in the parser so the runtime actors don't have to be checking everytime. An example of this would be implicit null declaration of variables
var a;where a is implicitly bound to the valuenil. In my interpreter the parser has the job of inserting thenilvalue in the AST node. In my opinion, this way the code is better organized but of course it has drawbacks. For example, while developing theResolverclass, in order to differentiate between empty return declarations and explicitnilreturns I had to add theempty: boolfield in theReturnAST node. -
Values type: In the original Java implementation, the (Java) class
Objectis used to represent the type of the Lox values. Since Typescript has sum types, I implemented a more accurate type called (wait for it...)Value, defined as:type Value = number | boolean | string | null | LoxCallable | LoxInstance;
Note that the last two differences have no impact in the end-user of the interpreter and they are just development decisions.