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:ast
This 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 classObject
from 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 thenil
value in the AST node. In my opinion, this way the code is better organized but of course it has drawbacks. For example, while developing theResolver
class, in order to differentiate between empty return declarations and explicitnil
returns I had to add theempty: bool
field in theReturn
AST node. -
Values type: In the original Java implementation, the (Java) class
Object
is 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.