diff --git a/java/.envrc b/.envrc similarity index 100% rename from java/.envrc rename to .envrc diff --git a/rust/rox/Cargo.lock b/Cargo.lock similarity index 100% rename from rust/rox/Cargo.lock rename to Cargo.lock diff --git a/rust/rox/Cargo.toml b/Cargo.toml similarity index 100% rename from rust/rox/Cargo.toml rename to Cargo.toml diff --git a/README.md b/README.md index d18e422..494cee4 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # Crafting Interpreters -Implementing [Crafting interpreters](https://craftinginterpreters.com) for the +Using rust to implement the first part of +[Crafting interpreters](https://craftinginterpreters.com) for the [FHNW](https://www.fhnw.ch) [PL-Circle](https://github.com/fhnw-pl-circle). All dependencies for running the respective implementations are specified in the @@ -8,19 +9,6 @@ All dependencies for running the respective implementations are specified in the Though my recommendation is to use the provided [nix flakes](https://nixos.wiki/wiki/Flakes). -## [Java Implementation](./java/lox-interpreter/) - -Implementation of the tree-walk interpreter in java after the book. - -Run `mvn package` to create an executable jar file in the `target` directory and -`mvn exec:java@jlox` to get a repl. - -Run `mvn exec:java@generate-ast` to generate the expression class. - -## [Rust implementation](./rust/rox/) - -Implementation of the tree-walk interpreter in rust. - Use `cargo run -- ` to run the interpreter directly from the source code. Specify needed arguments in place of ``. @@ -28,7 +16,7 @@ Specify needed arguments in place of ``. Usage: rox Commands: - compile Compile a Lox source file + run Run a Lox source file repl Run a Lox REPL help Print this message or the help of the given subcommand(s) diff --git a/rust/flake.lock b/flake.lock similarity index 100% rename from rust/flake.lock rename to flake.lock diff --git a/rust/flake.nix b/flake.nix similarity index 94% rename from rust/flake.nix rename to flake.nix index 40b9b6b..d287364 100644 --- a/rust/flake.nix +++ b/flake.nix @@ -19,7 +19,7 @@ fx = fenix.packages.${system}; rust = fx.combine [ (fx.fromToolchainFile { - file = ./rox/rust-toolchain.toml; + file = ./rust-toolchain.toml; sha256 = "sha256-AJ6LX/Q/Er9kS15bn9iflkUwcgYqRQxiOIL2ToVAXaU="; }) ]; diff --git a/java/flake.lock b/java/flake.lock deleted file mode 100644 index f293d28..0000000 --- a/java/flake.lock +++ /dev/null @@ -1,61 +0,0 @@ -{ - "nodes": { - "flake-utils": { - "inputs": { - "systems": "systems" - }, - "locked": { - "lastModified": 1731533236, - "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", - "owner": "numtide", - "repo": "flake-utils", - "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", - "type": "github" - }, - "original": { - "owner": "numtide", - "repo": "flake-utils", - "type": "github" - } - }, - "nixpkgs": { - "locked": { - "lastModified": 1738680400, - "narHash": "sha256-ooLh+XW8jfa+91F1nhf9OF7qhuA/y1ChLx6lXDNeY5U=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "799ba5bffed04ced7067a91798353d360788b30d", - "type": "github" - }, - "original": { - "owner": "NixOS", - "ref": "nixos-unstable", - "repo": "nixpkgs", - "type": "github" - } - }, - "root": { - "inputs": { - "flake-utils": "flake-utils", - "nixpkgs": "nixpkgs" - } - }, - "systems": { - "locked": { - "lastModified": 1681028828, - "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", - "owner": "nix-systems", - "repo": "default", - "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", - "type": "github" - }, - "original": { - "owner": "nix-systems", - "repo": "default", - "type": "github" - } - } - }, - "root": "root", - "version": 7 -} diff --git a/java/flake.nix b/java/flake.nix deleted file mode 100644 index 97fc0f0..0000000 --- a/java/flake.nix +++ /dev/null @@ -1,29 +0,0 @@ -{ - inputs = { - nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; - flake-utils.url = "github:numtide/flake-utils"; - }; - - outputs = - { - nixpkgs, - flake-utils, - ... - }: - flake-utils.lib.eachDefaultSystem ( - system: - let - pkgs = import nixpkgs { inherit system; }; - buildInputs = with pkgs; [ - jdk21 - jdt-language-server - maven - ]; - in - { - devShells.default = pkgs.mkShell { - buildInputs = buildInputs; - }; - } - ); -} diff --git a/java/lox-interpreter/.classpath b/java/lox-interpreter/.classpath deleted file mode 100644 index 4e7f670..0000000 --- a/java/lox-interpreter/.classpath +++ /dev/null @@ -1,57 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/java/lox-interpreter/.mvn/jvm.config b/java/lox-interpreter/.mvn/jvm.config deleted file mode 100644 index e69de29..0000000 diff --git a/java/lox-interpreter/.mvn/maven.config b/java/lox-interpreter/.mvn/maven.config deleted file mode 100644 index e69de29..0000000 diff --git a/java/lox-interpreter/.project b/java/lox-interpreter/.project deleted file mode 100644 index f9bbc23..0000000 --- a/java/lox-interpreter/.project +++ /dev/null @@ -1,34 +0,0 @@ - - - lox-interpreter - - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.m2e.core.maven2Builder - - - - - - org.eclipse.jdt.core.javanature - org.eclipse.m2e.core.maven2Nature - - - - 1738828672243 - - 30 - - org.eclipse.core.resources.regexFilterMatcher - node_modules|\.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__ - - - - diff --git a/java/lox-interpreter/.settings/org.eclipse.core.resources.prefs b/java/lox-interpreter/.settings/org.eclipse.core.resources.prefs deleted file mode 100644 index f9fe345..0000000 --- a/java/lox-interpreter/.settings/org.eclipse.core.resources.prefs +++ /dev/null @@ -1,4 +0,0 @@ -eclipse.preferences.version=1 -encoding//src/main/java=UTF-8 -encoding//src/test/java=UTF-8 -encoding/=UTF-8 diff --git a/java/lox-interpreter/.settings/org.eclipse.jdt.apt.core.prefs b/java/lox-interpreter/.settings/org.eclipse.jdt.apt.core.prefs deleted file mode 100644 index d4313d4..0000000 --- a/java/lox-interpreter/.settings/org.eclipse.jdt.apt.core.prefs +++ /dev/null @@ -1,2 +0,0 @@ -eclipse.preferences.version=1 -org.eclipse.jdt.apt.aptEnabled=false diff --git a/java/lox-interpreter/.settings/org.eclipse.jdt.core.prefs b/java/lox-interpreter/.settings/org.eclipse.jdt.core.prefs deleted file mode 100644 index e96c048..0000000 --- a/java/lox-interpreter/.settings/org.eclipse.jdt.core.prefs +++ /dev/null @@ -1,9 +0,0 @@ -eclipse.preferences.version=1 -org.eclipse.jdt.core.compiler.codegen.targetPlatform=21 -org.eclipse.jdt.core.compiler.compliance=21 -org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled -org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning -org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=ignore -org.eclipse.jdt.core.compiler.processAnnotations=disabled -org.eclipse.jdt.core.compiler.release=enabled -org.eclipse.jdt.core.compiler.source=21 diff --git a/java/lox-interpreter/.settings/org.eclipse.m2e.core.prefs b/java/lox-interpreter/.settings/org.eclipse.m2e.core.prefs deleted file mode 100644 index f897a7f..0000000 --- a/java/lox-interpreter/.settings/org.eclipse.m2e.core.prefs +++ /dev/null @@ -1,4 +0,0 @@ -activeProfiles= -eclipse.preferences.version=1 -resolveWorkspaceProjects=true -version=1 diff --git a/java/lox-interpreter/pom.xml b/java/lox-interpreter/pom.xml deleted file mode 100644 index 9bf0b34..0000000 --- a/java/lox-interpreter/pom.xml +++ /dev/null @@ -1,151 +0,0 @@ - - - 4.0.0 - - ch.vanwa.lox_interpreter - lox-interpreter - 1.0-SNAPSHOT - - lox-interpreter - https://code.vanwa.ch - - - UTF-8 - 21 - - - - - - org.junit - junit-bom - 5.11.0 - pom - import - - - - - - - org.junit.jupiter - junit-jupiter-api - test - - - - org.junit.jupiter - junit-jupiter-params - test - - - - - - - - maven-clean-plugin - 3.4.0 - - - maven-resources-plugin - 3.3.1 - - - maven-compiler-plugin - 3.13.0 - - - maven-surefire-plugin - 3.3.0 - - - maven-jar-plugin - 3.4.2 - - - maven-install-plugin - 3.1.2 - - - maven-deploy-plugin - 3.1.2 - - - maven-site-plugin - 3.12.1 - - - maven-project-info-reports-plugin - 3.6.1 - - - - - - maven-assembly-plugin - 3.7.1 - - - jar-with-dependencies - - - - ch.vanwa.lox_interpreter.App - - - - - - make-assembly - package - - single - - - - - - org.codehaus.mojo - exec-maven-plugin - 3.5.0 - - - jlox - - java - - - ch.vanwa.lox_interpreter.App - - ${script} - - - - - jlox-repl - - java - - - ch.vanwa.lox_interpreter.App - - - - generate-ast - - java - - - ch.vanwa.tool.GenerateAst - - src/main/java/ch/vanwa/lox_interpreter - - - - - - - - diff --git a/java/lox-interpreter/src/main/java/ch/vanwa/lox_interpreter/App.java b/java/lox-interpreter/src/main/java/ch/vanwa/lox_interpreter/App.java deleted file mode 100644 index bb48da0..0000000 --- a/java/lox-interpreter/src/main/java/ch/vanwa/lox_interpreter/App.java +++ /dev/null @@ -1,19 +0,0 @@ -package ch.vanwa.lox_interpreter; - -import java.io.IOException; - -public class App { - /** - * Entry point for the Lox interpreter cli. - */ - public static void main(String[] args) throws IOException { - if (args.length > 1) { - System.out.println("Usage: jlox [script]"); - System.exit(64); - } else if (args.length == 1) { - Lox.runFile(args[0]); - } else { - Lox.runPrompt(); - } - } -} diff --git a/java/lox-interpreter/src/main/java/ch/vanwa/lox_interpreter/Environment.java b/java/lox-interpreter/src/main/java/ch/vanwa/lox_interpreter/Environment.java deleted file mode 100644 index fe327bc..0000000 --- a/java/lox-interpreter/src/main/java/ch/vanwa/lox_interpreter/Environment.java +++ /dev/null @@ -1,105 +0,0 @@ -package ch.vanwa.lox_interpreter; - -import java.util.HashMap; -import java.util.Map; - -/** - * Environment mapping variable names to their values. - */ -class Environment { - /** - * Enclosing parent environment, can be null (only used for the outermost global - * environment). - */ - final Environment enclosing; - - /** - * Mapping of variable names to their values. - */ - private final Map values = new HashMap<>(); - - /** - * Initialise environment without an enclosing environment. - */ - Environment() { - enclosing = null; - } - - /** - * Initialise environment with an enclosing environment. - */ - Environment(Environment enclosing) { - this.enclosing = enclosing; - } - - /** - * Get the value of a variable. Throw an error if that variable is not known in - * this environment or in any of the enclosing ones. - * - * @param name Token with the variable lexeme used as variable name. - */ - Object get(Token name) { - if (values.containsKey(name.lexeme())) { - return values.get(name.lexeme()); - } - - if (enclosing != null) { - return enclosing.get(name); - } - - throw new RuntimeError(name, - "Undefined variable '" + name.lexeme() + "'."); - } - - /** - * Assign a value to a variable. Throw an error if that variable is not known in - * this environment or in any of the enclosing ones. - */ - void assign(Token name, Object value) { - if (values.containsKey(name.lexeme())) { - values.put(name.lexeme(), value); - return; - } - - if (enclosing != null) { - enclosing.assign(name, value); - return; - } - - throw new RuntimeError(name, - "Undefined variable '" + name.lexeme() + "'."); - } - - /** - * Define a new variable in this environment. - */ - void define(String name, Object value) { - values.put(name, value); - } - - /** - * Get a variable value at the n-th parent environment of the current one. - */ - Object getAt(int distance, String name) { - return ancestor(distance).values.get(name); - } - - /** - * Assign a variable value at the n-th parent environment of the current one. - */ - void assignAt(int distance, Token name, Object value) { - ancestor(distance).values.put(name.lexeme(), value); - } - - /** - * Get the n-th parent environment of the current one. - */ - Environment ancestor(int distance) { - Environment environment = this; - for (int i = 0; i < distance; i++) { - environment = environment.enclosing; - } - - return environment; - } -} diff --git a/java/lox-interpreter/src/main/java/ch/vanwa/lox_interpreter/Expr.java b/java/lox-interpreter/src/main/java/ch/vanwa/lox_interpreter/Expr.java deleted file mode 100644 index 4106d02..0000000 --- a/java/lox-interpreter/src/main/java/ch/vanwa/lox_interpreter/Expr.java +++ /dev/null @@ -1,130 +0,0 @@ -package ch.vanwa.lox_interpreter; - -import java.util.List; - -abstract class Expr { - interface Visitor { - R visitAssignExpr(Assign expr); - R visitBinaryExpr(Binary expr); - R visitCallExpr(Call expr); - R visitGroupingExpr(Grouping expr); - R visitLiteralExpr(Literal expr); - R visitLogicalExpr(Logical expr); - R visitUnaryExpr(Unary expr); - R visitVariableExpr(Variable expr); - } - static class Assign extends Expr { - Assign(Token name, Expr value) { - this.name = name; - this.value = value; - } - - @Override - R accept(Visitor visitor) { - return visitor.visitAssignExpr(this); - } - - final Token name; - final Expr value; - } - static class Binary extends Expr { - Binary(Expr left, Token operator, Expr right) { - this.left = left; - this.operator = operator; - this.right = right; - } - - @Override - R accept(Visitor visitor) { - return visitor.visitBinaryExpr(this); - } - - final Expr left; - final Token operator; - final Expr right; - } - static class Call extends Expr { - Call(Expr callee, Token paren, List arguments) { - this.callee = callee; - this.paren = paren; - this.arguments = arguments; - } - - @Override - R accept(Visitor visitor) { - return visitor.visitCallExpr(this); - } - - final Expr callee; - final Token paren; - final List arguments; - } - static class Grouping extends Expr { - Grouping(Expr expression) { - this.expression = expression; - } - - @Override - R accept(Visitor visitor) { - return visitor.visitGroupingExpr(this); - } - - final Expr expression; - } - static class Literal extends Expr { - Literal(Object value) { - this.value = value; - } - - @Override - R accept(Visitor visitor) { - return visitor.visitLiteralExpr(this); - } - - final Object value; - } - static class Logical extends Expr { - Logical(Expr left, Token operator, Expr right) { - this.left = left; - this.operator = operator; - this.right = right; - } - - @Override - R accept(Visitor visitor) { - return visitor.visitLogicalExpr(this); - } - - final Expr left; - final Token operator; - final Expr right; - } - static class Unary extends Expr { - Unary(Token operator, Expr right) { - this.operator = operator; - this.right = right; - } - - @Override - R accept(Visitor visitor) { - return visitor.visitUnaryExpr(this); - } - - final Token operator; - final Expr right; - } - static class Variable extends Expr { - Variable(Token name) { - this.name = name; - } - - @Override - R accept(Visitor visitor) { - return visitor.visitVariableExpr(this); - } - - final Token name; - } - - abstract R accept(Visitor visitor); -} diff --git a/java/lox-interpreter/src/main/java/ch/vanwa/lox_interpreter/Interpreter.java b/java/lox-interpreter/src/main/java/ch/vanwa/lox_interpreter/Interpreter.java deleted file mode 100644 index 90ba9b2..0000000 --- a/java/lox-interpreter/src/main/java/ch/vanwa/lox_interpreter/Interpreter.java +++ /dev/null @@ -1,345 +0,0 @@ -package ch.vanwa.lox_interpreter; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * Evaluate Lox expressions. - */ -class Interpreter implements Expr.Visitor, Stmt.Visitor { - final Environment globals = new Environment(); - private Environment environment = globals; - private final Map locals = new HashMap<>(); - - Interpreter() { - globals.define("clock", new LoxCallable() { - @Override - public int arity() { - return 0; - } - - @Override - public Object call(Interpreter interpreter, - List arguments) { - return (double) System.currentTimeMillis() / 1000.0; - } - - @Override - public String toString() { - return ""; - } - }); - } - - void interpret(List statements) { - try { - for (Stmt statement : statements) { - execute(statement); - } - } catch (RuntimeError error) { - Lox.runtimeError(error); - } - } - - @Override - public Object visitLiteralExpr(Expr.Literal expr) { - return expr.value; - } - - @Override - public Object visitLogicalExpr(Expr.Logical expr) { - Object left = evaluate(expr.left); - - if (expr.operator.type() == TokenType.OR) { - if (isTruthy(left)) - return left; - } else { - if (!isTruthy(left)) - return left; - } - - return evaluate(expr.right); - } - - @Override - public Object visitGroupingExpr(Expr.Grouping expr) { - return evaluate(expr.expression); - } - - private Object evaluate(Expr expr) { - return expr.accept(this); - } - - private void execute(Stmt stmt) { - stmt.accept(this); - } - - void resolve(Expr expr, int depth) { - locals.put(expr, depth); - } - - /** - * Execute all statements within a block while using lexical scoping for - * variables. - */ - void executeBlock(List statements, - Environment environment) { - Environment previous = this.environment; - try { - this.environment = environment; - - for (Stmt statement : statements) { - execute(statement); - } - } finally { - this.environment = previous; - } - } - - @Override - public Void visitBlockStmt(Stmt.Block stmt) { - executeBlock(stmt.statements, new Environment(environment)); - return null; - } - - @Override - public Void visitExpressionStmt(Stmt.Expression stmt) { - evaluate(stmt.expression); - return null; - } - - @Override - public Void visitFunctionStmt(Stmt.Function stmt) { - LoxFunction function = new LoxFunction(stmt, environment); - environment.define(stmt.name.lexeme(), function); - return null; - } - - @Override - public Void visitIfStmt(Stmt.If stmt) { - if (isTruthy(evaluate(stmt.condition))) { - execute(stmt.thenBranch); - } else if (stmt.elseBranch != null) { - execute(stmt.elseBranch); - } - return null; - } - - @Override - public Void visitPrintStmt(Stmt.Print stmt) { - Object value = evaluate(stmt.expression); - System.out.println(stringify(value)); - return null; - } - - @Override - public Void visitReturnStmt(Stmt.Return stmt) { - Object value = null; - if (stmt.value != null) - value = evaluate(stmt.value); - - throw new Return(value); - } - - @Override - public Void visitVarStmt(Stmt.Var stmt) { - Object value = null; - if (stmt.initializer != null) { - value = evaluate(stmt.initializer); - } - - environment.define(stmt.name.lexeme(), value); - return null; - } - - @Override - public Void visitWhileStmt(Stmt.While stmt) { - while (isTruthy(evaluate(stmt.condition))) { - execute(stmt.body); - } - return null; - } - - @Override - public Object visitAssignExpr(Expr.Assign expr) { - Object value = evaluate(expr.value); - - Integer distance = locals.get(expr); - if (distance != null) { - environment.assignAt(distance, expr.name, value); - } else { - globals.assign(expr.name, value); - } - - return value; - } - - @Override - public Object visitBinaryExpr(Expr.Binary expr) { - Object left = evaluate(expr.left); - Object right = evaluate(expr.right); - - switch (expr.operator.type()) { - case GREATER: - checkNumberOperands(expr.operator, left, right); - return (double) left > (double) right; - case GREATER_EQUAL: - checkNumberOperands(expr.operator, left, right); - return (double) left >= (double) right; - case LESS: - checkNumberOperands(expr.operator, left, right); - return (double) left < (double) right; - case LESS_EQUAL: - checkNumberOperands(expr.operator, left, right); - return (double) left <= (double) right; - case BANG_EQUAL: - return !isEqual(left, right); - case EQUAL_EQUAL: - return isEqual(left, right); - case MINUS: - checkNumberOperands(expr.operator, left, right); - return (double) left - (double) right; - case PLUS: - if (left instanceof Double && right instanceof Double) { - return (double) left + (double) right; - } - - if (left instanceof String && right instanceof String) { - return (String) left + (String) right; - } - throw new RuntimeError(expr.operator, - "Operands must be two numbers or two strings."); - case SLASH: - checkNumberOperands(expr.operator, left, right); - return (double) left / (double) right; - case STAR: - checkNumberOperands(expr.operator, left, right); - return (double) left * (double) right; - } - - // Unreachable. - return null; - } - - @Override - public Object visitCallExpr(Expr.Call expr) { - Object callee = evaluate(expr.callee); - - List arguments = new ArrayList<>(); - for (Expr argument : expr.arguments) { - arguments.add(evaluate(argument)); - } - - if (!(callee instanceof LoxCallable)) { - throw new RuntimeError(expr.paren, - "Can only call functions and classes."); - } - - LoxCallable function = (LoxCallable) callee; - if (arguments.size() != function.arity()) { - throw new RuntimeError(expr.paren, "Expected " + - function.arity() + " arguments but got " + - arguments.size() + "."); - } - - return function.call(this, arguments); - } - - @Override - public Object visitUnaryExpr(Expr.Unary expr) { - Object right = evaluate(expr.right); - - switch (expr.operator.type()) { - case BANG: - return !isTruthy(right); - case MINUS: - checkNumberOperand(expr.operator, right); - return -(double) right; - } - - // Unreachable. - return null; - } - - @Override - public Object visitVariableExpr(Expr.Variable expr) { - return lookUpVariable(expr.name, expr); - } - - private Object lookUpVariable(Token name, Expr expr) { - Integer distance = locals.get(expr); - if (distance != null) { - return environment.getAt(distance, name.lexeme()); - } else { - return globals.get(name); - } - } - - /** - * Throw a runtime error if the operand is not an instance of Double. - */ - private void checkNumberOperand(Token operator, Object operand) { - if (operand instanceof Double) - return; - throw new RuntimeError(operator, "Operand must be a number."); - } - - /** - * Throw a runtime error if left and right are not both instances of Double. - */ - private void checkNumberOperands(Token operator, - Object left, Object right) { - if (left instanceof Double && right instanceof Double) - return; - - throw new RuntimeError(operator, "Operands must be numbers."); - } - - /** - * Return true if the object is a boolean with value true, false otherwise. - */ - private boolean isTruthy(Object object) { - if (object == null) - return false; - if (object instanceof Boolean) - return (boolean) object; - return true; - } - - /** - * Return true if object a and b are not null and a equals be, otherwise return - * false. - */ - private boolean isEqual(Object a, Object b) { - if (a == null && b == null) - return true; - if (a == null) - return false; - - return a.equals(b); - } - - /** - * Convert an object to a string. - * - * If the object is null, return "nil", if it is a Double properly print it as a - * decimal, otherwise just call toString. - */ - private String stringify(Object object) { - if (object == null) { - return "nil"; - } - - if (object instanceof Double) { - String text = object.toString(); - if (text.endsWith(".0")) { - text = text.substring(0, text.length() - 2); - } - return text; - } - - return object.toString(); - } -} diff --git a/java/lox-interpreter/src/main/java/ch/vanwa/lox_interpreter/Lox.java b/java/lox-interpreter/src/main/java/ch/vanwa/lox_interpreter/Lox.java deleted file mode 100644 index e5add4a..0000000 --- a/java/lox-interpreter/src/main/java/ch/vanwa/lox_interpreter/Lox.java +++ /dev/null @@ -1,134 +0,0 @@ -package ch.vanwa.lox_interpreter; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Paths; -import java.util.List; - -/** - * A Lox interpreter. - */ -public class Lox { - private static final Interpreter interpreter = new Interpreter(); - - /** - * True if a run produced an error, false otherwise. - */ - static boolean hadError = false; - - /** - * True if a run produced a runtime error, false otherwise. - */ - static boolean hadRuntimeError = false; - - /** - * Run a file with Lox source code. - * - * @param path Path of the source code file. - */ - public static void runFile(String path) throws IOException { - byte[] bytes = Files.readAllBytes(Paths.get(path)); - run(new String(bytes, StandardCharsets.UTF_8)); - if (hadError) { - System.exit(65); - } - if (hadRuntimeError) { - System.exit(70); - } - } - - /** - * Run a Lox REPL. - */ - public static void runPrompt() throws IOException { - var input = new InputStreamReader(System.in); - var reader = new BufferedReader(input); - - for (;;) { - System.out.print("> "); - String line = reader.readLine(); - if (line == null) { - break; - } - run(line); - hadError = false; - } - } - - /** - * Run Lox source code. - * - * @param source Lox source code. - * - * @return List of scanned tokens. - */ - public static void run(String source) { - var scanner = new Scanner(source); - List tokens = scanner.scanTokens(); - Parser parser = new Parser(tokens); - List statements = parser.parse(); - - if (hadError) { - return; - } - - Resolver resolver = new Resolver(interpreter); - resolver.resolve(statements); - - // Stop if there was a resolution error. - if (hadError) { - return; - } - - interpreter.interpret(statements); - } - - /** - * Print a simple error message. - * - * @param line Line number where the error ocurred. - * @param message Error message. - */ - static void error(int line, String message) { - report(line, "", message); - } - - /** - * Print an error message for a token. - * - * @param line Line number where the error ocurred. - * @param message Error message. - */ - static void error(Token token, String message) { - if (token.type() == TokenType.EOF) { - report(token.line(), " at end", message); - } else { - report(token.line(), " at '" + token.lexeme() + "'", message); - } - } - - /** - * Print a runtime error. - * - * @param error Runtime error to print. - */ - static void runtimeError(RuntimeError error) { - System.err.println(String.format("%s\n[line %d]", error.getMessage(), error.token.line())); - hadRuntimeError = true; - } - - /** - * Print a message to stderr. - * - * @param line Line number where the error ocurred. - * @param where Where the error occurred. - * @param message Error message. - */ - private static void report(int line, String where, String message) { - System.err.println(String.format("[line %d] Error%s: %s", line, where, message)); - hadError = true; - } -} diff --git a/java/lox-interpreter/src/main/java/ch/vanwa/lox_interpreter/LoxCallable.java b/java/lox-interpreter/src/main/java/ch/vanwa/lox_interpreter/LoxCallable.java deleted file mode 100644 index b0ac838..0000000 --- a/java/lox-interpreter/src/main/java/ch/vanwa/lox_interpreter/LoxCallable.java +++ /dev/null @@ -1,9 +0,0 @@ -package ch.vanwa.lox_interpreter; - -import java.util.List; - -interface LoxCallable { - int arity(); - - Object call(Interpreter interpreter, List arguments); -} diff --git a/java/lox-interpreter/src/main/java/ch/vanwa/lox_interpreter/LoxFunction.java b/java/lox-interpreter/src/main/java/ch/vanwa/lox_interpreter/LoxFunction.java deleted file mode 100644 index c3d3dca..0000000 --- a/java/lox-interpreter/src/main/java/ch/vanwa/lox_interpreter/LoxFunction.java +++ /dev/null @@ -1,41 +0,0 @@ -package ch.vanwa.lox_interpreter; - -import java.util.List; - -class LoxFunction implements LoxCallable { - private final Stmt.Function declaration; - private final Environment closure; - - LoxFunction(Stmt.Function declaration, Environment closure) { - this.closure = closure; - this.declaration = declaration; - } - - @Override - public String toString() { - return ""; - } - - @Override - public int arity() { - return declaration.params.size(); - } - - @Override - public Object call(Interpreter interpreter, - List arguments) { - Environment environment = new Environment(closure); - for (int i = 0; i < declaration.params.size(); i++) { - environment.define(declaration.params.get(i).lexeme(), - arguments.get(i)); - } - - try { - interpreter.executeBlock(declaration.body, environment); - } catch (Return returnValue) { - return returnValue.value; - } - - return null; - } -} diff --git a/java/lox-interpreter/src/main/java/ch/vanwa/lox_interpreter/Parser.java b/java/lox-interpreter/src/main/java/ch/vanwa/lox_interpreter/Parser.java deleted file mode 100644 index 2d06a9d..0000000 --- a/java/lox-interpreter/src/main/java/ch/vanwa/lox_interpreter/Parser.java +++ /dev/null @@ -1,458 +0,0 @@ -package ch.vanwa.lox_interpreter; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import static ch.vanwa.lox_interpreter.TokenType.*; - -/** - * Parse tokens into an AST. - * - * Implement the grammar specified at - * https://craftinginterpreters.com/appendix-i.html#expressions - */ -class Parser { - /** - * Thrown if a parsing error occurs. - */ - private static class ParseError extends RuntimeException { - } - - /** - * List of tokens to parse. - */ - private final List tokens; - - /** - * Index of the current token. - */ - private int current = 0; - - /** - * Initialize a new parser with the given tokens. - * - * @param tokens List of tokens to parse. - */ - Parser(List tokens) { - this.tokens = tokens; - } - - /** - * Start parsing. - * - * @return Expression signifying the whole program or null, if parsing fails. - */ - List parse() { - List statements = new ArrayList<>(); - while (!isAtEnd()) { - statements.add(declaration()); - } - - return statements; - } - - private Expr expression() { - return assignment(); - } - - private Stmt declaration() { - try { - if (match(FUN)) { - return function("function"); - } - if (match(VAR)) { - return varDeclaration(); - } - - return statement(); - } catch (ParseError error) { - synchronize(); - return null; - } - } - - private Stmt statement() { - if (match(FOR)) { - return forStatement(); - } - if (match(IF)) { - return ifStatement(); - } - if (match(PRINT)) { - return printStatement(); - } - if (match(RETURN)) { - return returnStatement(); - } - if (match(WHILE)) { - return whileStatement(); - } - if (match(LEFT_BRACE)) { - return new Stmt.Block(block()); - } - - return expressionStatement(); - } - - private Stmt forStatement() { - consume(LEFT_PAREN, "Expect '(' after 'for'."); - - Stmt initializer; - if (match(SEMICOLON)) { - initializer = null; - } else if (match(VAR)) { - initializer = varDeclaration(); - } else { - initializer = expressionStatement(); - } - - Expr condition = null; - if (!check(SEMICOLON)) { - condition = expression(); - } - consume(SEMICOLON, "Expect ';' after loop condition."); - - Expr increment = null; - if (!check(RIGHT_PAREN)) { - increment = expression(); - } - consume(RIGHT_PAREN, "Expect ')' after for clauses."); - - Stmt body = statement(); - - if (increment != null) { - body = new Stmt.Block( - Arrays.asList( - body, - new Stmt.Expression(increment))); - } - - if (condition == null) { - condition = new Expr.Literal(true); - } - body = new Stmt.While(condition, body); - - if (initializer != null) { - body = new Stmt.Block(Arrays.asList(initializer, body)); - } - - return body; - } - - private Stmt ifStatement() { - consume(LEFT_PAREN, "Expect '(' after 'if'."); - Expr condition = expression(); - consume(RIGHT_PAREN, "Expect ')' after if condition."); - - Stmt thenBranch = statement(); - Stmt elseBranch = null; - if (match(ELSE)) { - elseBranch = statement(); - } - - return new Stmt.If(condition, thenBranch, elseBranch); - } - - private Stmt printStatement() { - Expr value = expression(); - consume(SEMICOLON, "Expect ';' after value."); - return new Stmt.Print(value); - } - - private Stmt varDeclaration() { - Token name = consume(IDENTIFIER, "Expect variable name."); - - Expr initializer = null; - if (match(EQUAL)) { - initializer = expression(); - } - - consume(SEMICOLON, "Expect ';' after variable declaration."); - return new Stmt.Var(name, initializer); - } - - private Stmt returnStatement() { - Token keyword = previous(); - Expr value = null; - if (!check(SEMICOLON)) { - value = expression(); - } - - consume(SEMICOLON, "Expect ';' after return value."); - return new Stmt.Return(keyword, value); - } - - private Stmt whileStatement() { - consume(LEFT_PAREN, "Expect '(' after 'while'."); - Expr condition = expression(); - consume(RIGHT_PAREN, "Expect ')' after condition."); - Stmt body = statement(); - - return new Stmt.While(condition, body); - } - - private Stmt expressionStatement() { - Expr expr = expression(); - consume(SEMICOLON, "Expect ';' after expression."); - return new Stmt.Expression(expr); - } - - private Stmt.Function function(String kind) { - Token name = consume(IDENTIFIER, "Expect " + kind + " name."); - consume(LEFT_PAREN, "Expect '(' after " + kind + " name."); - List parameters = new ArrayList<>(); - if (!check(RIGHT_PAREN)) { - do { - if (parameters.size() >= 255) { - error(peek(), "Can't have more than 255 parameters."); - } - - parameters.add( - consume(IDENTIFIER, "Expect parameter name.")); - } while (match(COMMA)); - } - consume(RIGHT_PAREN, "Expect ')' after parameters."); - - consume(LEFT_BRACE, "Expect '{' before " + kind + " body."); - List body = block(); - return new Stmt.Function(name, parameters, body); - } - - private List block() { - List statements = new ArrayList<>(); - - while (!check(RIGHT_BRACE) && !isAtEnd()) { - statements.add(declaration()); - } - - consume(RIGHT_BRACE, "Expect '}' after block."); - return statements; - } - - private Expr assignment() { - Expr expr = or(); - - if (match(EQUAL)) { - Token equals = previous(); - Expr value = assignment(); - - if (expr instanceof Expr.Variable) { - Token name = ((Expr.Variable) expr).name; - return new Expr.Assign(name, value); - } - - error(equals, "Invalid assignment target."); - } - - return expr; - } - - private Expr or() { - Expr expr = and(); - - while (match(OR)) { - Token operator = previous(); - Expr right = and(); - expr = new Expr.Logical(expr, operator, right); - } - - return expr; - } - - private Expr and() { - Expr expr = equality(); - - while (match(AND)) { - Token operator = previous(); - Expr right = equality(); - expr = new Expr.Logical(expr, operator, right); - } - - return expr; - } - - private Expr equality() { - Expr expr = comparison(); - - while (match(BANG_EQUAL, EQUAL_EQUAL)) { - Token operator = previous(); - Expr right = comparison(); - expr = new Expr.Binary(expr, operator, right); - } - - return expr; - } - - private Expr comparison() { - Expr expr = term(); - - while (match(GREATER, GREATER_EQUAL, LESS, LESS_EQUAL)) { - Token operator = previous(); - Expr right = term(); - expr = new Expr.Binary(expr, operator, right); - } - - return expr; - } - - private Expr term() { - Expr expr = factor(); - - while (match(MINUS, PLUS)) { - Token operator = previous(); - Expr right = factor(); - expr = new Expr.Binary(expr, operator, right); - } - - return expr; - } - - private Expr factor() { - Expr expr = unary(); - - while (match(SLASH, STAR)) { - Token operator = previous(); - Expr right = unary(); - expr = new Expr.Binary(expr, operator, right); - } - - return expr; - } - - private Expr unary() { - if (match(BANG, MINUS)) { - Token operator = previous(); - Expr right = unary(); - return new Expr.Unary(operator, right); - } - - return call(); - } - - private Expr call() { - Expr expr = primary(); - - while (true) { - if (match(LEFT_PAREN)) { - expr = finishCall(expr); - } else { - break; - } - } - - return expr; - } - - private Expr finishCall(Expr callee) { - List arguments = new ArrayList<>(); - if (!check(RIGHT_PAREN)) { - do { - if (arguments.size() >= 255) { - error(peek(), "Can't have more than 255 arguments."); - } - arguments.add(expression()); - } while (match(COMMA)); - } - - Token paren = consume(RIGHT_PAREN, - "Expect ')' after arguments."); - - return new Expr.Call(callee, paren, arguments); - } - - private Expr primary() { - if (match(FALSE)) - return new Expr.Literal(false); - if (match(TRUE)) - return new Expr.Literal(true); - if (match(NIL)) - return new Expr.Literal(null); - - if (match(NUMBER, STRING)) { - return new Expr.Literal(previous().literal()); - } - - if (match(IDENTIFIER)) { - return new Expr.Variable(previous()); - } - - if (match(LEFT_PAREN)) { - Expr expr = expression(); - consume(RIGHT_PAREN, "Expect ')' after expression."); - return new Expr.Grouping(expr); - } - - throw error(peek(), "Expect expression."); - } - - private boolean match(TokenType... types) { - for (TokenType type : types) { - if (check(type)) { - advance(); - return true; - } - } - - return false; - } - - private Token consume(TokenType type, String message) { - if (check(type)) - return advance(); - - throw error(peek(), message); - } - - private boolean check(TokenType type) { - if (isAtEnd()) - return false; - return peek().type() == type; - } - - private Token advance() { - if (!isAtEnd()) - current++; - return previous(); - } - - private boolean isAtEnd() { - return peek().type() == EOF; - } - - private Token peek() { - return tokens.get(current); - } - - private Token previous() { - return tokens.get(current - 1); - } - - private ParseError error(Token token, String message) { - Lox.error(token, message); - return new ParseError(); - } - - private void synchronize() { - advance(); - - while (!isAtEnd()) { - if (previous().type() == SEMICOLON) - return; - - switch (peek().type()) { - case CLASS: - case FUN: - case VAR: - case FOR: - case IF: - case WHILE: - case PRINT: - case RETURN: - return; - } - - advance(); - } - } -} diff --git a/java/lox-interpreter/src/main/java/ch/vanwa/lox_interpreter/Resolver.java b/java/lox-interpreter/src/main/java/ch/vanwa/lox_interpreter/Resolver.java deleted file mode 100644 index 818b887..0000000 --- a/java/lox-interpreter/src/main/java/ch/vanwa/lox_interpreter/Resolver.java +++ /dev/null @@ -1,215 +0,0 @@ -package ch.vanwa.lox_interpreter; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Stack; - -class Resolver implements Expr.Visitor, Stmt.Visitor { - private final Interpreter interpreter; - private final Stack> scopes = new Stack<>(); - private FunctionType currentFunction = FunctionType.NONE; - - Resolver(Interpreter interpreter) { - this.interpreter = interpreter; - } - - private enum FunctionType { - NONE, - FUNCTION - } - - @Override - public Void visitBlockStmt(Stmt.Block stmt) { - beginScope(); - resolve(stmt.statements); - endScope(); - return null; - } - - @Override - public Void visitExpressionStmt(Stmt.Expression stmt) { - resolve(stmt.expression); - return null; - } - - @Override - public Void visitFunctionStmt(Stmt.Function stmt) { - declare(stmt.name); - define(stmt.name); - - resolveFunction(stmt, FunctionType.FUNCTION); - return null; - } - - @Override - public Void visitIfStmt(Stmt.If stmt) { - resolve(stmt.condition); - resolve(stmt.thenBranch); - if (stmt.elseBranch != null) - resolve(stmt.elseBranch); - return null; - } - - @Override - public Void visitPrintStmt(Stmt.Print stmt) { - resolve(stmt.expression); - return null; - } - - @Override - public Void visitReturnStmt(Stmt.Return stmt) { - if (currentFunction == FunctionType.NONE) { - Lox.error(stmt.keyword, "Can't return from top-level code."); - } - - if (stmt.value != null) { - resolve(stmt.value); - } - - return null; - } - - @Override - public Void visitVariableExpr(Expr.Variable expr) { - if (!scopes.isEmpty() && - scopes.peek().get(expr.name.lexeme()) == Boolean.FALSE) { - Lox.error(expr.name, - "Can't read local variable in its own initializer."); - } - - resolveLocal(expr, expr.name); - return null; - } - - @Override - public Void visitVarStmt(Stmt.Var stmt) { - declare(stmt.name); - if (stmt.initializer != null) { - resolve(stmt.initializer); - } - define(stmt.name); - return null; - } - - @Override - public Void visitWhileStmt(Stmt.While stmt) { - resolve(stmt.condition); - resolve(stmt.body); - return null; - } - - @Override - public Void visitAssignExpr(Expr.Assign expr) { - resolve(expr.value); - resolveLocal(expr, expr.name); - return null; - } - - @Override - public Void visitBinaryExpr(Expr.Binary expr) { - resolve(expr.left); - resolve(expr.right); - return null; - } - - @Override - public Void visitCallExpr(Expr.Call expr) { - resolve(expr.callee); - - for (Expr argument : expr.arguments) { - resolve(argument); - } - - return null; - } - - @Override - public Void visitGroupingExpr(Expr.Grouping expr) { - resolve(expr.expression); - return null; - } - - @Override - public Void visitLiteralExpr(Expr.Literal expr) { - return null; - } - - @Override - public Void visitLogicalExpr(Expr.Logical expr) { - resolve(expr.left); - resolve(expr.right); - return null; - } - - @Override - public Void visitUnaryExpr(Expr.Unary expr) { - resolve(expr.right); - return null; - } - - void resolve(List statements) { - for (Stmt statement : statements) { - resolve(statement); - } - } - - private void resolve(Stmt stmt) { - stmt.accept(this); - } - - private void resolve(Expr expr) { - expr.accept(this); - } - - private void beginScope() { - scopes.push(new HashMap()); - } - - private void endScope() { - scopes.pop(); - } - - private void declare(Token name) { - if (scopes.isEmpty()) { - return; - } - - Map scope = scopes.peek(); - if (scope.containsKey(name.lexeme())) { - Lox.error(name, - "Already a variable with this name in this scope."); - } - - scope.put(name.lexeme(), false); - } - - private void define(Token name) { - if (scopes.isEmpty()) - return; - scopes.peek().put(name.lexeme(), true); - } - - private void resolveLocal(Expr expr, Token name) { - for (int i = scopes.size() - 1; i >= 0; i--) { - if (scopes.get(i).containsKey(name.lexeme())) { - interpreter.resolve(expr, scopes.size() - 1 - i); - return; - } - } - } - - private void resolveFunction(Stmt.Function function, FunctionType type) { - FunctionType enclosingFunction = currentFunction; - currentFunction = type; - beginScope(); - for (Token param : function.params) { - declare(param); - define(param); - } - resolve(function.body); - endScope(); - - currentFunction = enclosingFunction; - } -} diff --git a/java/lox-interpreter/src/main/java/ch/vanwa/lox_interpreter/Return.java b/java/lox-interpreter/src/main/java/ch/vanwa/lox_interpreter/Return.java deleted file mode 100644 index 45adeb6..0000000 --- a/java/lox-interpreter/src/main/java/ch/vanwa/lox_interpreter/Return.java +++ /dev/null @@ -1,10 +0,0 @@ -package ch.vanwa.lox_interpreter; - -class Return extends RuntimeException { - final Object value; - - Return(Object value) { - super(null, null, false, false); - this.value = value; - } -} diff --git a/java/lox-interpreter/src/main/java/ch/vanwa/lox_interpreter/RuntimeError.java b/java/lox-interpreter/src/main/java/ch/vanwa/lox_interpreter/RuntimeError.java deleted file mode 100644 index 3f933d5..0000000 --- a/java/lox-interpreter/src/main/java/ch/vanwa/lox_interpreter/RuntimeError.java +++ /dev/null @@ -1,13 +0,0 @@ -package ch.vanwa.lox_interpreter; - -/** - * Represent an error at runtime (evaluating). - */ -class RuntimeError extends RuntimeException { - final Token token; - - RuntimeError(Token token, String message) { - super(message); - this.token = token; - } -} diff --git a/java/lox-interpreter/src/main/java/ch/vanwa/lox_interpreter/Scanner.java b/java/lox-interpreter/src/main/java/ch/vanwa/lox_interpreter/Scanner.java deleted file mode 100644 index 8070783..0000000 --- a/java/lox-interpreter/src/main/java/ch/vanwa/lox_interpreter/Scanner.java +++ /dev/null @@ -1,342 +0,0 @@ -package ch.vanwa.lox_interpreter; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -import static ch.vanwa.lox_interpreter.TokenType.*; - -/** - * Convert source code into tokens for the Lox programming language. - */ -class Scanner { - /** - * Source code getting scanned. - */ - private final String source; - - /** - * List of already scanned tokens. - */ - private final List tokens = new ArrayList<>(); - - /** - * Starting index of the current lexeme. - */ - private int start = 0; - - /** - * Index of current character in the source code. - */ - private int current = 0; - - /** - * Which line the scanneris on in the source code. - */ - private int line = 1; - - /** - * Mapping of keyword string to {@link TokenType}. - */ - private static final Map keywords = Map.ofEntries( - Map.entry("and", AND), - Map.entry("class", CLASS), - Map.entry("else", ELSE), - Map.entry("false", FALSE), - Map.entry("for", FOR), - Map.entry("fun", FUN), - Map.entry("if", IF), - Map.entry("nil", NIL), - Map.entry("or", OR), - Map.entry("print", PRINT), - Map.entry("return", RETURN), - Map.entry("super", SUPER), - Map.entry("this", THIS), - Map.entry("true", TRUE), - Map.entry("var", VAR), - Map.entry("while", WHILE)); - - /** - * Initialize scanner. - * - * @param source Source code to scan. - */ - Scanner(final String source) { - this.source = source; - } - - /** - * Scan lexemes to tokens until the end is reached. - * - * @return List of scanned tokens + EOF. - */ - List scanTokens() { - while (!isAtEnd()) { - start = current; - scanToken(); - } - - tokens.add(new Token(EOF, "", null, line)); - return tokens; - } - - /** - * Scan the next lexeme to a token. - */ - private void scanToken() { - char c = advance(); - switch (c) { - case '(': - addToken(LEFT_PAREN); - break; - case ')': - addToken(RIGHT_PAREN); - break; - case '{': - addToken(LEFT_BRACE); - break; - case '}': - addToken(RIGHT_BRACE); - break; - case ',': - addToken(COMMA); - break; - case '.': - addToken(DOT); - break; - case '-': - addToken(MINUS); - break; - case '+': - addToken(PLUS); - break; - case ';': - addToken(SEMICOLON); - break; - case '*': - addToken(STAR); - break; - case '!': - addToken(match('=') ? BANG_EQUAL : BANG); - break; - case '=': - addToken(match('=') ? EQUAL_EQUAL : EQUAL); - break; - case '<': - addToken(match('=') ? LESS_EQUAL : LESS); - break; - case '>': - addToken(match('=') ? GREATER_EQUAL : GREATER); - break; - case '/': - if (match('/')) { - // A comment goes until the end of the line. - while (peek() != '\n' && !isAtEnd()) - advance(); - } else { - addToken(SLASH); - } - break; - case ' ': - case '\r': - case '\t': - // Ignore whitespace. - break; - case '\n': - line++; - break; - case '"': - string(); - break; - default: - if (isDigit(c)) { - number(); - } else if (isAlpha(c)) { - identifier(); - } else { - Lox.error(line, "Unexpected character."); - } - break; - } - } - - /** - * Consume an identifier. - */ - private void identifier() { - while (isAlphaNumeric(peek())) { - advance(); - } - - String text = source.substring(start, current); - TokenType type = keywords.get(text); - if (type == null) { - type = IDENTIFIER; - } - addToken(type); - } - - /** - * Consume a number literal. - */ - private void number() { - while (isDigit(peek())) { - advance(); - } - - // Look for a fractional part. - if (peek() == '.' && isDigit(peekNext())) { - // Consume the "." - advance(); - - while (isDigit(peek())) { - advance(); - } - } - - addToken(NUMBER, - Double.parseDouble(source.substring(start, current))); - } - - /** - * Consume a string literal. - */ - private void string() { - while (peek() != '"' && !isAtEnd()) { - if (peek() == '\n') { - line++; - } - advance(); - } - - if (isAtEnd()) { - Lox.error(line, "Unterminated string."); - return; - } - - // The closing ". - advance(); - - // Trim the surrounding quotes. - String value = source.substring(start + 1, current - 1); - addToken(STRING, value); - } - - /** - * Check if a character matches the one at the current pointer. - * - * @param expected Character used for matching against the current character. - * - * @return True if the expected character matches the current one, false - * otherwise. - */ - private boolean match(final char expected) { - if (isAtEnd()) { - return false; - } - if (source.charAt(current) != expected) { - return false; - } - - current++; - return true; - } - - /** - * Peek at the current character without consuming it. - * - * @return The character peeked at. - */ - private char peek() { - if (isAtEnd()) { - return '\0'; - } - return source.charAt(current); - } - - /** - * Peek at the next character without consuming it or moving the pointer. - * - * @return The character peeked at. - */ - private char peekNext() { - if (current + 1 >= source.length()) { - return '\0'; - } - return source.charAt(current + 1); - } - - /** - * Check if a character is a letter or an underscore (a-z,A-Z,_). - * - * @param c Character to check. - * - * @return True if the character is a letter or underscore, false otherwise. - */ - private boolean isAlpha(final char c) { - return (c >= 'a' && c <= 'z') || - (c >= 'A' && c <= 'Z') || - c == '_'; - } - - /** - * Check if a character is alphanumeric (a-z,A-Z,_,0-9). - * - * @param c Character to check. - * - * @return True if the character is alphanumeric, false otherwise. - */ - private boolean isAlphaNumeric(final char c) { - return isAlpha(c) || isDigit(c); - } - - /** - * Check if a character is a digit (0-9). - * - * @param c Character to check. - * - * @return True if the character is a digit, false otherwise. - */ - private boolean isDigit(final char c) { - return c >= '0' && c <= '9'; - } - - /** - * Check if the scanner reached the end of the source code. - * - * @return True if the scaner reached the end of the source code, false - * otherwise. - */ - private boolean isAtEnd() { - return current >= source.length(); - } - - /** - * Advance the source code pointer by one. - * - * @return The character under the advanced pointer. - */ - private char advance() { - return source.charAt(current++); - } - - /** - * Add a token without a literal to the list of scanned tokens. - * - * @param type Type of token. - */ - private void addToken(final TokenType type) { - addToken(type, null); - } - - /** - * Add a token to the list of scanned tokens. - * - * @param type Type of token. - * @param literal Value of a literal (can be null). - */ - private void addToken(final TokenType type, final Object literal) { - String text = source.substring(start, current); - tokens.add(new Token(type, text, literal, line)); - } -} diff --git a/java/lox-interpreter/src/main/java/ch/vanwa/lox_interpreter/Stmt.java b/java/lox-interpreter/src/main/java/ch/vanwa/lox_interpreter/Stmt.java deleted file mode 100644 index 7381854..0000000 --- a/java/lox-interpreter/src/main/java/ch/vanwa/lox_interpreter/Stmt.java +++ /dev/null @@ -1,128 +0,0 @@ -package ch.vanwa.lox_interpreter; - -import java.util.List; - -abstract class Stmt { - interface Visitor { - R visitBlockStmt(Block stmt); - R visitExpressionStmt(Expression stmt); - R visitFunctionStmt(Function stmt); - R visitIfStmt(If stmt); - R visitPrintStmt(Print stmt); - R visitReturnStmt(Return stmt); - R visitVarStmt(Var stmt); - R visitWhileStmt(While stmt); - } - static class Block extends Stmt { - Block(List statements) { - this.statements = statements; - } - - @Override - R accept(Visitor visitor) { - return visitor.visitBlockStmt(this); - } - - final List statements; - } - static class Expression extends Stmt { - Expression(Expr expression) { - this.expression = expression; - } - - @Override - R accept(Visitor visitor) { - return visitor.visitExpressionStmt(this); - } - - final Expr expression; - } - static class Function extends Stmt { - Function(Token name, List params, List body) { - this.name = name; - this.params = params; - this.body = body; - } - - @Override - R accept(Visitor visitor) { - return visitor.visitFunctionStmt(this); - } - - final Token name; - final List params; - final List body; - } - static class If extends Stmt { - If(Expr condition, Stmt thenBranch, Stmt elseBranch) { - this.condition = condition; - this.thenBranch = thenBranch; - this.elseBranch = elseBranch; - } - - @Override - R accept(Visitor visitor) { - return visitor.visitIfStmt(this); - } - - final Expr condition; - final Stmt thenBranch; - final Stmt elseBranch; - } - static class Print extends Stmt { - Print(Expr expression) { - this.expression = expression; - } - - @Override - R accept(Visitor visitor) { - return visitor.visitPrintStmt(this); - } - - final Expr expression; - } - static class Return extends Stmt { - Return(Token keyword, Expr value) { - this.keyword = keyword; - this.value = value; - } - - @Override - R accept(Visitor visitor) { - return visitor.visitReturnStmt(this); - } - - final Token keyword; - final Expr value; - } - static class Var extends Stmt { - Var(Token name, Expr initializer) { - this.name = name; - this.initializer = initializer; - } - - @Override - R accept(Visitor visitor) { - return visitor.visitVarStmt(this); - } - - final Token name; - final Expr initializer; - } - static class While extends Stmt { - While(Expr condition, Stmt body) { - this.condition = condition; - this.body = body; - } - - @Override - R accept(Visitor visitor) { - return visitor.visitWhileStmt(this); - } - - final Expr condition; - final Stmt body; - } - - abstract R accept(Visitor visitor); -} diff --git a/java/lox-interpreter/src/main/java/ch/vanwa/lox_interpreter/Token.java b/java/lox-interpreter/src/main/java/ch/vanwa/lox_interpreter/Token.java deleted file mode 100644 index a8d9630..0000000 --- a/java/lox-interpreter/src/main/java/ch/vanwa/lox_interpreter/Token.java +++ /dev/null @@ -1,16 +0,0 @@ -package ch.vanwa.lox_interpreter; - -/** - * Single token. - * - * @param type Type of the token. - * @param lexeme Lexeme that produced this token. - * @param literal Literal value of the token (can be null). - * @param line Line number where the token was scannedin the source code. - */ -public record Token(TokenType type, String lexeme, Object literal, int line) { - @Override - public String toString() { - return String.format("%d: %s %s %s", line, type, lexeme, literal); - } -} diff --git a/java/lox-interpreter/src/main/java/ch/vanwa/lox_interpreter/TokenType.java b/java/lox-interpreter/src/main/java/ch/vanwa/lox_interpreter/TokenType.java deleted file mode 100644 index d056d1d..0000000 --- a/java/lox-interpreter/src/main/java/ch/vanwa/lox_interpreter/TokenType.java +++ /dev/null @@ -1,25 +0,0 @@ -package ch.vanwa.lox_interpreter; - -/** - * Enumeration of all token types for Lox. - */ -enum TokenType { - // Single-character tokens. - LEFT_PAREN, RIGHT_PAREN, LEFT_BRACE, RIGHT_BRACE, - COMMA, DOT, MINUS, PLUS, SEMICOLON, SLASH, STAR, - - // One or two character tokens. - BANG, BANG_EQUAL, - EQUAL, EQUAL_EQUAL, - GREATER, GREATER_EQUAL, - LESS, LESS_EQUAL, - - // Literals. - IDENTIFIER, STRING, NUMBER, - - // Keywords. - AND, CLASS, ELSE, FALSE, FUN, FOR, IF, NIL, OR, - PRINT, RETURN, SUPER, THIS, TRUE, VAR, WHILE, - - EOF -} diff --git a/java/lox-interpreter/src/main/java/ch/vanwa/tool/GenerateAst.java b/java/lox-interpreter/src/main/java/ch/vanwa/tool/GenerateAst.java deleted file mode 100644 index 3023e6e..0000000 --- a/java/lox-interpreter/src/main/java/ch/vanwa/tool/GenerateAst.java +++ /dev/null @@ -1,115 +0,0 @@ -package ch.vanwa.tool; - -import java.io.IOException; -import java.io.PrintWriter; -import java.util.Arrays; -import java.util.List; - -public class GenerateAst { - public static void main(String[] args) throws IOException { - if (args.length != 1) { - System.err.println("Usage: generate_ast "); - System.exit(64); - } - String outputDir = args[0]; - - defineAst(outputDir, "Expr", Arrays.asList( - "Assign : Token name, Expr value", - "Binary : Expr left, Token operator, Expr right", - "Call : Expr callee, Token paren, List arguments", - "Grouping : Expr expression", - "Literal : Object value", - "Logical : Expr left, Token operator, Expr right", - "Unary : Token operator, Expr right", - "Variable : Token name")); - - defineAst(outputDir, "Stmt", Arrays.asList( - "Block : List statements", - "Expression : Expr expression", - "Function : Token name, List params," + - " List body", - "If : Expr condition, Stmt thenBranch," + - " Stmt elseBranch", - "Print : Expr expression", - "Return : Token keyword, Expr value", - "Var : Token name, Expr initializer", - "While : Expr condition, Stmt body")); - } - - private static void defineAst( - String outputDir, String baseName, List types) - throws IOException { - String path = outputDir + "/" + baseName + ".java"; - PrintWriter writer = new PrintWriter(path, "UTF-8"); - - writer.println("package ch.vanwa.lox_interpreter;"); - writer.println(); - writer.println("import java.util.List;"); - writer.println(); - writer.println("abstract class " + baseName + " {"); - - defineVisitor(writer, baseName, types); - - // The AST classes. - for (String type : types) { - String className = type.split(":")[0].trim(); - String fields = type.split(":")[1].trim(); - defineType(writer, baseName, className, fields); - } - - // The base accept() method. - writer.println(); - writer.println(" abstract R accept(Visitor visitor);"); - - writer.println("}"); - writer.close(); - } - - private static void defineType( - PrintWriter writer, String baseName, - String className, String fieldList) { - writer.println(" static class " + className + " extends " + - baseName + " {"); - - // Constructor. - writer.println(" " + className + "(" + fieldList + ") {"); - - // Store parameters in fields. - String[] fields = fieldList.split(", "); - for (String field : fields) { - String name = field.split(" ")[1]; - writer.println(" this." + name + " = " + name + ";"); - } - - writer.println(" }"); - - // Visitor pattern. - writer.println(); - writer.println(" @Override"); - writer.println(" R accept(Visitor visitor) {"); - writer.println(" return visitor.visit" + - className + baseName + "(this);"); - writer.println(" }"); - - // Fields. - writer.println(); - for (String field : fields) { - writer.println(" final " + field + ";"); - } - - writer.println(" }"); - } - - private static void defineVisitor( - PrintWriter writer, String baseName, List types) { - writer.println(" interface Visitor {"); - - for (String type : types) { - String typeName = type.split(":")[0].trim(); - writer.println(" R visit" + typeName + baseName + "(" + - typeName + " " + baseName.toLowerCase() + ");"); - } - - writer.println(" }"); - } -} diff --git a/java/lox-interpreter/src/test/java/ch/vanwa/lox_interpreter/AppTest.java b/java/lox-interpreter/src/test/java/ch/vanwa/lox_interpreter/AppTest.java deleted file mode 100644 index 4b8dc4a..0000000 --- a/java/lox-interpreter/src/test/java/ch/vanwa/lox_interpreter/AppTest.java +++ /dev/null @@ -1,28 +0,0 @@ -package ch.vanwa.lox_interpreter; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -import org.junit.jupiter.api.Test; - -/** - * Unit test for simple App. - */ -public class AppTest { - - /** - * Simple scanner test. - */ - @Test - public void scanOneLine() { - final var scanner = new Scanner("var language = \"lox\""); - final var tokens = scanner.scanTokens(); - - assertEquals(5, tokens.size()); - - assertEquals(new Token(TokenType.VAR, "var", null, 1), tokens.get(0)); - assertEquals(new Token(TokenType.IDENTIFIER, "language", null, 1), tokens.get(1)); - assertEquals(new Token(TokenType.EQUAL, "=", null, 1), tokens.get(2)); - assertEquals(new Token(TokenType.STRING, "\"lox\"", "lox", 1), tokens.get(3)); - assertEquals(new Token(TokenType.EOF, "", null, 1), tokens.get(4)); - } -} diff --git a/rust/rox/rust-toolchain.toml b/rust-toolchain.toml similarity index 100% rename from rust/rox/rust-toolchain.toml rename to rust-toolchain.toml diff --git a/rust/.envrc b/rust/.envrc deleted file mode 100644 index 3550a30..0000000 --- a/rust/.envrc +++ /dev/null @@ -1 +0,0 @@ -use flake diff --git a/rust/rox/src/callable.rs b/src/callable.rs similarity index 100% rename from rust/rox/src/callable.rs rename to src/callable.rs diff --git a/rust/rox/src/class.rs b/src/class.rs similarity index 100% rename from rust/rox/src/class.rs rename to src/class.rs diff --git a/rust/rox/src/cli.rs b/src/cli.rs similarity index 100% rename from rust/rox/src/cli.rs rename to src/cli.rs diff --git a/rust/rox/src/environment.rs b/src/environment.rs similarity index 100% rename from rust/rox/src/environment.rs rename to src/environment.rs diff --git a/rust/rox/src/expression.rs b/src/expression.rs similarity index 100% rename from rust/rox/src/expression.rs rename to src/expression.rs diff --git a/rust/rox/src/function.rs b/src/function.rs similarity index 100% rename from rust/rox/src/function.rs rename to src/function.rs diff --git a/rust/rox/src/instance.rs b/src/instance.rs similarity index 100% rename from rust/rox/src/instance.rs rename to src/instance.rs diff --git a/rust/rox/src/interpreter.rs b/src/interpreter.rs similarity index 100% rename from rust/rox/src/interpreter.rs rename to src/interpreter.rs diff --git a/rust/rox/src/keywords.rs b/src/keywords.rs similarity index 100% rename from rust/rox/src/keywords.rs rename to src/keywords.rs diff --git a/rust/rox/src/lib.rs b/src/lib.rs similarity index 100% rename from rust/rox/src/lib.rs rename to src/lib.rs diff --git a/rust/rox/src/main.rs b/src/main.rs similarity index 100% rename from rust/rox/src/main.rs rename to src/main.rs diff --git a/rust/rox/src/native_functions.rs b/src/native_functions.rs similarity index 100% rename from rust/rox/src/native_functions.rs rename to src/native_functions.rs diff --git a/rust/rox/src/parser.rs b/src/parser.rs similarity index 100% rename from rust/rox/src/parser.rs rename to src/parser.rs diff --git a/rust/rox/src/resolver.rs b/src/resolver.rs similarity index 100% rename from rust/rox/src/resolver.rs rename to src/resolver.rs diff --git a/rust/rox/src/scanner.rs b/src/scanner.rs similarity index 100% rename from rust/rox/src/scanner.rs rename to src/scanner.rs diff --git a/rust/rox/src/statement.rs b/src/statement.rs similarity index 100% rename from rust/rox/src/statement.rs rename to src/statement.rs diff --git a/rust/rox/src/token.rs b/src/token.rs similarity index 100% rename from rust/rox/src/token.rs rename to src/token.rs diff --git a/rust/rox/src/tokenizer/comment.rs b/src/tokenizer/comment.rs similarity index 100% rename from rust/rox/src/tokenizer/comment.rs rename to src/tokenizer/comment.rs diff --git a/rust/rox/src/tokenizer/identifier.rs b/src/tokenizer/identifier.rs similarity index 100% rename from rust/rox/src/tokenizer/identifier.rs rename to src/tokenizer/identifier.rs diff --git a/rust/rox/src/tokenizer/interface.rs b/src/tokenizer/interface.rs similarity index 100% rename from rust/rox/src/tokenizer/interface.rs rename to src/tokenizer/interface.rs diff --git a/rust/rox/src/tokenizer/lookahead.rs b/src/tokenizer/lookahead.rs similarity index 100% rename from rust/rox/src/tokenizer/lookahead.rs rename to src/tokenizer/lookahead.rs diff --git a/rust/rox/src/tokenizer/newline.rs b/src/tokenizer/newline.rs similarity index 100% rename from rust/rox/src/tokenizer/newline.rs rename to src/tokenizer/newline.rs diff --git a/rust/rox/src/tokenizer/number.rs b/src/tokenizer/number.rs similarity index 100% rename from rust/rox/src/tokenizer/number.rs rename to src/tokenizer/number.rs diff --git a/rust/rox/src/tokenizer/single_char.rs b/src/tokenizer/single_char.rs similarity index 100% rename from rust/rox/src/tokenizer/single_char.rs rename to src/tokenizer/single_char.rs diff --git a/rust/rox/src/tokenizer/string.rs b/src/tokenizer/string.rs similarity index 100% rename from rust/rox/src/tokenizer/string.rs rename to src/tokenizer/string.rs diff --git a/rust/rox/src/tokenizer/whitespace.rs b/src/tokenizer/whitespace.rs similarity index 100% rename from rust/rox/src/tokenizer/whitespace.rs rename to src/tokenizer/whitespace.rs diff --git a/rust/rox/src/value.rs b/src/value.rs similarity index 100% rename from rust/rox/src/value.rs rename to src/value.rs