Init
This commit is contained in:
commit
5dac9efd38
2
.clang-format
Normal file
2
.clang-format
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
---
|
||||||
|
BasedOnStyle: GNU
|
||||||
57
.classpath
Normal file
57
.classpath
Normal file
|
|
@ -0,0 +1,57 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<classpath>
|
||||||
|
<classpathentry kind="src" output="target/classes" path="src/main/java">
|
||||||
|
<attributes>
|
||||||
|
<attribute name="optional" value="true"/>
|
||||||
|
<attribute name="maven.pomderived" value="true"/>
|
||||||
|
</attributes>
|
||||||
|
</classpathentry>
|
||||||
|
<classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources">
|
||||||
|
<attributes>
|
||||||
|
<attribute name="maven.pomderived" value="true"/>
|
||||||
|
<attribute name="optional" value="true"/>
|
||||||
|
</attributes>
|
||||||
|
</classpathentry>
|
||||||
|
<classpathentry kind="src" output="target/test-classes" path="src/test/java">
|
||||||
|
<attributes>
|
||||||
|
<attribute name="optional" value="true"/>
|
||||||
|
<attribute name="maven.pomderived" value="true"/>
|
||||||
|
<attribute name="test" value="true"/>
|
||||||
|
</attributes>
|
||||||
|
</classpathentry>
|
||||||
|
<classpathentry excluding="**" kind="src" output="target/test-classes" path="src/test/resources">
|
||||||
|
<attributes>
|
||||||
|
<attribute name="maven.pomderived" value="true"/>
|
||||||
|
<attribute name="test" value="true"/>
|
||||||
|
<attribute name="optional" value="true"/>
|
||||||
|
</attributes>
|
||||||
|
</classpathentry>
|
||||||
|
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-17">
|
||||||
|
<attributes>
|
||||||
|
<attribute name="maven.pomderived" value="true"/>
|
||||||
|
</attributes>
|
||||||
|
</classpathentry>
|
||||||
|
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
|
||||||
|
<attributes>
|
||||||
|
<attribute name="maven.pomderived" value="true"/>
|
||||||
|
</attributes>
|
||||||
|
</classpathentry>
|
||||||
|
<classpathentry kind="src" path="target/generated-sources/annotations">
|
||||||
|
<attributes>
|
||||||
|
<attribute name="optional" value="true"/>
|
||||||
|
<attribute name="maven.pomderived" value="true"/>
|
||||||
|
<attribute name="ignore_optional_problems" value="true"/>
|
||||||
|
<attribute name="m2e-apt" value="true"/>
|
||||||
|
</attributes>
|
||||||
|
</classpathentry>
|
||||||
|
<classpathentry kind="src" output="target/test-classes" path="target/generated-test-sources/test-annotations">
|
||||||
|
<attributes>
|
||||||
|
<attribute name="optional" value="true"/>
|
||||||
|
<attribute name="maven.pomderived" value="true"/>
|
||||||
|
<attribute name="ignore_optional_problems" value="true"/>
|
||||||
|
<attribute name="m2e-apt" value="true"/>
|
||||||
|
<attribute name="test" value="true"/>
|
||||||
|
</attributes>
|
||||||
|
</classpathentry>
|
||||||
|
<classpathentry kind="output" path="target/classes"/>
|
||||||
|
</classpath>
|
||||||
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
build/
|
||||||
|
target/
|
||||||
0
.mvn/jvm.config
Normal file
0
.mvn/jvm.config
Normal file
0
.mvn/maven.config
Normal file
0
.mvn/maven.config
Normal file
34
.project
Normal file
34
.project
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<projectDescription>
|
||||||
|
<name>jlox</name>
|
||||||
|
<comment></comment>
|
||||||
|
<projects>
|
||||||
|
</projects>
|
||||||
|
<buildSpec>
|
||||||
|
<buildCommand>
|
||||||
|
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||||
|
<arguments>
|
||||||
|
</arguments>
|
||||||
|
</buildCommand>
|
||||||
|
<buildCommand>
|
||||||
|
<name>org.eclipse.m2e.core.maven2Builder</name>
|
||||||
|
<arguments>
|
||||||
|
</arguments>
|
||||||
|
</buildCommand>
|
||||||
|
</buildSpec>
|
||||||
|
<natures>
|
||||||
|
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||||
|
<nature>org.eclipse.m2e.core.maven2Nature</nature>
|
||||||
|
</natures>
|
||||||
|
<filteredResources>
|
||||||
|
<filter>
|
||||||
|
<id>1761899067174</id>
|
||||||
|
<name></name>
|
||||||
|
<type>30</type>
|
||||||
|
<matcher>
|
||||||
|
<id>org.eclipse.core.resources.regexFilterMatcher</id>
|
||||||
|
<arguments>node_modules|\.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__</arguments>
|
||||||
|
</matcher>
|
||||||
|
</filter>
|
||||||
|
</filteredResources>
|
||||||
|
</projectDescription>
|
||||||
4
.settings/org.eclipse.core.resources.prefs
Normal file
4
.settings/org.eclipse.core.resources.prefs
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
eclipse.preferences.version=1
|
||||||
|
encoding//src/main/java=UTF-8
|
||||||
|
encoding//src/test/java=UTF-8
|
||||||
|
encoding/<project>=UTF-8
|
||||||
2
.settings/org.eclipse.jdt.apt.core.prefs
Normal file
2
.settings/org.eclipse.jdt.apt.core.prefs
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
eclipse.preferences.version=1
|
||||||
|
org.eclipse.jdt.apt.aptEnabled=false
|
||||||
9
.settings/org.eclipse.jdt.core.prefs
Normal file
9
.settings/org.eclipse.jdt.core.prefs
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
eclipse.preferences.version=1
|
||||||
|
org.eclipse.jdt.core.compiler.codegen.targetPlatform=17
|
||||||
|
org.eclipse.jdt.core.compiler.compliance=17
|
||||||
|
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=17
|
||||||
4
.settings/org.eclipse.m2e.core.prefs
Normal file
4
.settings/org.eclipse.m2e.core.prefs
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
activeProfiles=
|
||||||
|
eclipse.preferences.version=1
|
||||||
|
resolveWorkspaceProjects=true
|
||||||
|
version=1
|
||||||
16
LICENSE
Normal file
16
LICENSE
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
ISC License
|
||||||
|
|
||||||
|
Copyright (c) 2025 Ashley Rose <ashleyrose@incel.email>
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
purpose with or without fee is hereby granted, provided that the above
|
||||||
|
copyright notice and this permission notice appear in all copies.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||||
|
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||||
|
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||||
|
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||||
|
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||||
|
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
|
PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
|
||||||
15
Makefile
Normal file
15
Makefile
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
VERSION=1.0-SNAPSHOT
|
||||||
|
|
||||||
|
jar:
|
||||||
|
mvn package
|
||||||
|
|
||||||
|
linux-build: jar
|
||||||
|
mkdir -p build
|
||||||
|
cat ./stub.sh ./target/jlox-$(VERSION).jar > ./build/jlox
|
||||||
|
chmod +x ./build/jlox
|
||||||
|
|
||||||
|
install: linux-build
|
||||||
|
mv ./build/jlox /usr/local/bin/jlox
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -r build target
|
||||||
17
README.md
Normal file
17
README.md
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
# JLox
|
||||||
|
|
||||||
|
Lox interpreter written in Java.
|
||||||
|
|
||||||
|
## Linux Install
|
||||||
|
Requirements:
|
||||||
|
- JDK 17+
|
||||||
|
- Apache Maven
|
||||||
|
- GNU Make
|
||||||
|
|
||||||
|
```sh
|
||||||
|
\# make install
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|

|
||||||
BIN
images/isc-logo.png
Normal file
BIN
images/isc-logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.8 KiB |
98
pom.xml
Normal file
98
pom.xml
Normal file
|
|
@ -0,0 +1,98 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<groupId>me.rose.lox</groupId>
|
||||||
|
<artifactId>jlox</artifactId>
|
||||||
|
<version>1.0-SNAPSHOT</version>
|
||||||
|
|
||||||
|
<name>jlox</name>
|
||||||
|
<!-- FIXME change it to the project's website -->
|
||||||
|
<url>http://www.example.com</url>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
<maven.compiler.release>17</maven.compiler.release>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<dependencyManagement>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.junit</groupId>
|
||||||
|
<artifactId>junit-bom</artifactId>
|
||||||
|
<version>5.11.0</version>
|
||||||
|
<type>pom</type>
|
||||||
|
<scope>import</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</dependencyManagement>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.junit.jupiter</groupId>
|
||||||
|
<artifactId>junit-jupiter-api</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<!-- Optionally: parameterized tests support -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.junit.jupiter</groupId>
|
||||||
|
<artifactId>junit-jupiter-params</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
|
||||||
|
<plugins>
|
||||||
|
<!-- clean lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#clean_Lifecycle -->
|
||||||
|
<plugin>
|
||||||
|
<artifactId>maven-clean-plugin</artifactId>
|
||||||
|
<version>3.4.0</version>
|
||||||
|
</plugin>
|
||||||
|
<!-- default lifecycle, jar packaging: see https://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_jar_packaging -->
|
||||||
|
<plugin>
|
||||||
|
<artifactId>maven-resources-plugin</artifactId>
|
||||||
|
<version>3.3.1</version>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
|
<version>3.13.0</version>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<artifactId>maven-surefire-plugin</artifactId>
|
||||||
|
<version>3.3.0</version>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<artifactId>maven-jar-plugin</artifactId>
|
||||||
|
<version>3.4.2</version>
|
||||||
|
<configuration>
|
||||||
|
<archive>
|
||||||
|
<manifest>
|
||||||
|
<addClasspath>true</addClasspath>
|
||||||
|
<mainClass>me.rose.lox.Lox</mainClass>
|
||||||
|
</manifest>
|
||||||
|
</archive>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<artifactId>maven-install-plugin</artifactId>
|
||||||
|
<version>3.1.2</version>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<artifactId>maven-deploy-plugin</artifactId>
|
||||||
|
<version>3.1.2</version>
|
||||||
|
</plugin>
|
||||||
|
<!-- site lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#site_Lifecycle -->
|
||||||
|
<plugin>
|
||||||
|
<artifactId>maven-site-plugin</artifactId>
|
||||||
|
<version>3.12.1</version>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<artifactId>maven-project-info-reports-plugin</artifactId>
|
||||||
|
<version>3.6.1</version>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</pluginManagement>
|
||||||
|
</build>
|
||||||
|
</project>
|
||||||
68
src/main/java/me/rose/lox/AstPrinter.java
Normal file
68
src/main/java/me/rose/lox/AstPrinter.java
Normal file
|
|
@ -0,0 +1,68 @@
|
||||||
|
package me.rose.lox;
|
||||||
|
|
||||||
|
class AstPrinter implements Expr.Visitor<String>
|
||||||
|
{
|
||||||
|
String
|
||||||
|
print (Expr expr)
|
||||||
|
{
|
||||||
|
return expr.accept (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String
|
||||||
|
visitBinaryExpr (Expr.Binary expr)
|
||||||
|
{
|
||||||
|
return parenthesize (expr.operator.lexeme, expr.left, expr.right);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String
|
||||||
|
visitGroupingExpr (Expr.Grouping expr)
|
||||||
|
{
|
||||||
|
return parenthesize ("group", expr.expression);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String
|
||||||
|
visitLiteralExpr (Expr.Literal expr)
|
||||||
|
{
|
||||||
|
if (expr.value == null)
|
||||||
|
return "nil";
|
||||||
|
return expr.value.toString ();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String
|
||||||
|
visitUnaryExpr (Expr.Unary expr)
|
||||||
|
{
|
||||||
|
return parenthesize (expr.operator.lexeme, expr.right);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String
|
||||||
|
parenthesize (String name, Expr... exprs)
|
||||||
|
{
|
||||||
|
StringBuilder builder = new StringBuilder ();
|
||||||
|
|
||||||
|
builder.append ("(").append (name);
|
||||||
|
for (Expr expr : exprs)
|
||||||
|
{
|
||||||
|
builder.append (" ");
|
||||||
|
builder.append (expr.accept (this));
|
||||||
|
}
|
||||||
|
builder.append (")");
|
||||||
|
|
||||||
|
return builder.toString ();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void
|
||||||
|
main (String[] args)
|
||||||
|
{
|
||||||
|
Expr expression = new Expr.Binary (
|
||||||
|
new Expr.Unary (new Token (TokenType.MINUS, "-", null, 1),
|
||||||
|
new Expr.Literal (123)),
|
||||||
|
new Token (TokenType.STAR, "*", null, 1),
|
||||||
|
new Expr.Grouping (new Expr.Literal (45.67)));
|
||||||
|
|
||||||
|
System.out.println (new AstPrinter ().print (expression));
|
||||||
|
}
|
||||||
|
}
|
||||||
68
src/main/java/me/rose/lox/Expr.java
Normal file
68
src/main/java/me/rose/lox/Expr.java
Normal file
|
|
@ -0,0 +1,68 @@
|
||||||
|
package me.rose.lox;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
abstract class Expr {
|
||||||
|
interface Visitor<R> {
|
||||||
|
R visitBinaryExpr(Binary expr);
|
||||||
|
R visitGroupingExpr(Grouping expr);
|
||||||
|
R visitLiteralExpr(Literal expr);
|
||||||
|
R visitUnaryExpr(Unary expr);
|
||||||
|
}
|
||||||
|
static class Binary extends Expr {
|
||||||
|
Binary(Expr left, Token operator, Expr right) {
|
||||||
|
this.left = left;
|
||||||
|
this.operator = operator;
|
||||||
|
this.right = right;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
<R> R accept(Visitor<R> visitor) {
|
||||||
|
return visitor.visitBinaryExpr(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
final Expr left;
|
||||||
|
final Token operator;
|
||||||
|
final Expr right;
|
||||||
|
}
|
||||||
|
static class Grouping extends Expr {
|
||||||
|
Grouping(Expr expression) {
|
||||||
|
this.expression = expression;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
<R> R accept(Visitor<R> visitor) {
|
||||||
|
return visitor.visitGroupingExpr(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
final Expr expression;
|
||||||
|
}
|
||||||
|
static class Literal extends Expr {
|
||||||
|
Literal(Object value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
<R> R accept(Visitor<R> visitor) {
|
||||||
|
return visitor.visitLiteralExpr(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
final Object value;
|
||||||
|
}
|
||||||
|
static class Unary extends Expr {
|
||||||
|
Unary(Token operator, Expr right) {
|
||||||
|
this.operator = operator;
|
||||||
|
this.right = right;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
<R> R accept(Visitor<R> visitor) {
|
||||||
|
return visitor.visitUnaryExpr(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
final Token operator;
|
||||||
|
final Expr right;
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract <R> R accept(Visitor<R> visitor);
|
||||||
|
}
|
||||||
97
src/main/java/me/rose/lox/Lox.java
Normal file
97
src/main/java/me/rose/lox/Lox.java
Normal file
|
|
@ -0,0 +1,97 @@
|
||||||
|
package me.rose.lox;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class Lox
|
||||||
|
{
|
||||||
|
static boolean hadError = false;
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
runFile (args[0]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
runPrompt ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void
|
||||||
|
runFile (String path) throws IOException
|
||||||
|
{
|
||||||
|
byte[] bytes = Files.readAllBytes (Paths.get (path));
|
||||||
|
run (new String (bytes, Charset.defaultCharset ()));
|
||||||
|
|
||||||
|
if (hadError)
|
||||||
|
System.exit (65);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void
|
||||||
|
runPrompt () throws IOException
|
||||||
|
{
|
||||||
|
InputStreamReader input = new InputStreamReader (System.in);
|
||||||
|
BufferedReader reader = new BufferedReader (input);
|
||||||
|
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
System.out.print ("> ");
|
||||||
|
String line = reader.readLine ();
|
||||||
|
if (line == null)
|
||||||
|
break;
|
||||||
|
run (line);
|
||||||
|
hadError = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void
|
||||||
|
run (String source)
|
||||||
|
{
|
||||||
|
Scanner scanner = new Scanner (source);
|
||||||
|
List<Token> tokens = scanner.scanTokens ();
|
||||||
|
Parser parser = new Parser (tokens);
|
||||||
|
Expr expression = parser.parse ();
|
||||||
|
|
||||||
|
if (hadError)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
error (int line, String message)
|
||||||
|
{
|
||||||
|
report (line, "", message);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void
|
||||||
|
report (int line, String where, String message)
|
||||||
|
{
|
||||||
|
System.err.println ("[line " + line + "] Error" + where + ": " + message);
|
||||||
|
hadError = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
226
src/main/java/me/rose/lox/Parser.java
Normal file
226
src/main/java/me/rose/lox/Parser.java
Normal file
|
|
@ -0,0 +1,226 @@
|
||||||
|
package me.rose.lox;
|
||||||
|
|
||||||
|
import static me.rose.lox.TokenType.*;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
class Parser
|
||||||
|
{
|
||||||
|
private static class ParseError extends RuntimeException
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
private final List<Token> tokens;
|
||||||
|
private int current = 0;
|
||||||
|
|
||||||
|
Parser (List<Token> tokens) { this.tokens = tokens; }
|
||||||
|
|
||||||
|
Expr
|
||||||
|
parse ()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return expression ();
|
||||||
|
}
|
||||||
|
catch (ParseError error)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Expr
|
||||||
|
expression ()
|
||||||
|
{
|
||||||
|
return equality ();
|
||||||
|
}
|
||||||
|
|
||||||
|
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 primary ();
|
||||||
|
}
|
||||||
|
|
||||||
|
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 (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 ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
268
src/main/java/me/rose/lox/Scanner.java
Normal file
268
src/main/java/me/rose/lox/Scanner.java
Normal file
|
|
@ -0,0 +1,268 @@
|
||||||
|
package me.rose.lox;
|
||||||
|
|
||||||
|
import static me.rose.lox.TokenType.*;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
class Scanner
|
||||||
|
{
|
||||||
|
private final String source;
|
||||||
|
private final List<Token> tokens = new ArrayList<> ();
|
||||||
|
private int start = 0;
|
||||||
|
private int current = 0;
|
||||||
|
private int line = 1;
|
||||||
|
|
||||||
|
private static final Map<String, TokenType> keywords;
|
||||||
|
|
||||||
|
static
|
||||||
|
{
|
||||||
|
keywords = new HashMap<> ();
|
||||||
|
keywords.put ("and", AND);
|
||||||
|
keywords.put ("class", CLASS);
|
||||||
|
keywords.put ("else", ELSE);
|
||||||
|
keywords.put ("false", FALSE);
|
||||||
|
keywords.put ("for", FOR);
|
||||||
|
keywords.put ("fun", FUN);
|
||||||
|
keywords.put ("if", IF);
|
||||||
|
keywords.put ("nil", NIL);
|
||||||
|
keywords.put ("or", OR);
|
||||||
|
keywords.put ("print", PRINT);
|
||||||
|
keywords.put ("return", RETURN);
|
||||||
|
keywords.put ("super", SUPER);
|
||||||
|
keywords.put ("this", THIS);
|
||||||
|
keywords.put ("true", TRUE);
|
||||||
|
keywords.put ("var", VAR);
|
||||||
|
keywords.put ("while", WHILE);
|
||||||
|
}
|
||||||
|
|
||||||
|
Scanner (String source) { this.source = source; }
|
||||||
|
|
||||||
|
List<Token>
|
||||||
|
scanTokens ()
|
||||||
|
{
|
||||||
|
while (!isAtEnd ())
|
||||||
|
{
|
||||||
|
start = current;
|
||||||
|
scanToken ();
|
||||||
|
}
|
||||||
|
|
||||||
|
tokens.add (new Token (EOF, "", null, line));
|
||||||
|
return tokens;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 ('/'))
|
||||||
|
{
|
||||||
|
while (peek () != '\n' && !isAtEnd ())
|
||||||
|
advance ();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
addToken (SLASH);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ' ':
|
||||||
|
case '\r':
|
||||||
|
case '\t':
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void
|
||||||
|
number ()
|
||||||
|
{
|
||||||
|
while (isDigit (peek ()))
|
||||||
|
advance ();
|
||||||
|
|
||||||
|
if (peek () == '.' && isDigit (peekNext ()))
|
||||||
|
{
|
||||||
|
advance ();
|
||||||
|
|
||||||
|
while (isDigit (peek ()))
|
||||||
|
advance ();
|
||||||
|
}
|
||||||
|
|
||||||
|
addToken (NUMBER, Double.parseDouble (source.substring (start, current)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void
|
||||||
|
string ()
|
||||||
|
{
|
||||||
|
while (peek () != '"' && !isAtEnd ())
|
||||||
|
{
|
||||||
|
if (peek () == '\n')
|
||||||
|
line++;
|
||||||
|
advance ();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isAtEnd ())
|
||||||
|
{
|
||||||
|
Lox.error (line, "Unterminated string.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
advance ();
|
||||||
|
|
||||||
|
String value = source.substring (start + 1, current - 1);
|
||||||
|
addToken (STRING, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean
|
||||||
|
match (char expected)
|
||||||
|
{
|
||||||
|
if (isAtEnd ())
|
||||||
|
return false;
|
||||||
|
if (source.charAt (current) != expected)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
current++;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private char
|
||||||
|
peek ()
|
||||||
|
{
|
||||||
|
if (isAtEnd ())
|
||||||
|
return '\0';
|
||||||
|
return source.charAt (current);
|
||||||
|
}
|
||||||
|
|
||||||
|
private char
|
||||||
|
peekNext ()
|
||||||
|
{
|
||||||
|
if (current + 1 >= source.length ())
|
||||||
|
return '\0';
|
||||||
|
return source.charAt (current + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean
|
||||||
|
isAlpha (char c)
|
||||||
|
{
|
||||||
|
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_';
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean
|
||||||
|
isAlphaNumeric (char c)
|
||||||
|
{
|
||||||
|
return isAlpha (c) || isDigit (c);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean
|
||||||
|
isDigit (char c)
|
||||||
|
{
|
||||||
|
return c >= '0' && c <= '9';
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean
|
||||||
|
isAtEnd ()
|
||||||
|
{
|
||||||
|
return current >= source.length ();
|
||||||
|
}
|
||||||
|
|
||||||
|
private char
|
||||||
|
advance ()
|
||||||
|
{
|
||||||
|
return source.charAt (current++);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void
|
||||||
|
addToken (TokenType type)
|
||||||
|
{
|
||||||
|
addToken (type, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void
|
||||||
|
addToken (TokenType type, Object literal)
|
||||||
|
{
|
||||||
|
String text = source.substring (start, current);
|
||||||
|
tokens.add (new Token (type, text, literal, line));
|
||||||
|
}
|
||||||
|
}
|
||||||
23
src/main/java/me/rose/lox/Token.java
Normal file
23
src/main/java/me/rose/lox/Token.java
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
package me.rose.lox;
|
||||||
|
|
||||||
|
class Token
|
||||||
|
{
|
||||||
|
final TokenType type;
|
||||||
|
final String lexeme;
|
||||||
|
final Object literal;
|
||||||
|
final int line;
|
||||||
|
|
||||||
|
Token (TokenType type, String lexeme, Object literal, int line)
|
||||||
|
{
|
||||||
|
this.type = type;
|
||||||
|
this.lexeme = lexeme;
|
||||||
|
this.literal = literal;
|
||||||
|
this.line = line;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String
|
||||||
|
toString ()
|
||||||
|
{
|
||||||
|
return type + " " + lexeme + " " + literal;
|
||||||
|
}
|
||||||
|
}
|
||||||
48
src/main/java/me/rose/lox/TokenType.java
Normal file
48
src/main/java/me/rose/lox/TokenType.java
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
package me.rose.lox;
|
||||||
|
|
||||||
|
enum TokenType
|
||||||
|
{
|
||||||
|
LEFT_PAREN,
|
||||||
|
RIGHT_PAREN,
|
||||||
|
LEFT_BRACE,
|
||||||
|
RIGHT_BRACE,
|
||||||
|
COMMA,
|
||||||
|
DOT,
|
||||||
|
MINUS,
|
||||||
|
PLUS,
|
||||||
|
SEMICOLON,
|
||||||
|
SLASH,
|
||||||
|
STAR,
|
||||||
|
|
||||||
|
BANG,
|
||||||
|
BANG_EQUAL,
|
||||||
|
EQUAL,
|
||||||
|
EQUAL_EQUAL,
|
||||||
|
GREATER,
|
||||||
|
GREATER_EQUAL,
|
||||||
|
LESS,
|
||||||
|
LESS_EQUAL,
|
||||||
|
|
||||||
|
IDENTIFIER,
|
||||||
|
STRING,
|
||||||
|
NUMBER,
|
||||||
|
|
||||||
|
AND,
|
||||||
|
CLASS,
|
||||||
|
ELSE,
|
||||||
|
FALSE,
|
||||||
|
FUN,
|
||||||
|
FOR,
|
||||||
|
IF,
|
||||||
|
NIL,
|
||||||
|
OR,
|
||||||
|
PRINT,
|
||||||
|
RETURN,
|
||||||
|
SUPER,
|
||||||
|
THIS,
|
||||||
|
TRUE,
|
||||||
|
VAR,
|
||||||
|
WHILE,
|
||||||
|
|
||||||
|
EOF
|
||||||
|
}
|
||||||
103
src/main/java/me/rose/tool/GenerateAst.java
Normal file
103
src/main/java/me/rose/tool/GenerateAst.java
Normal file
|
|
@ -0,0 +1,103 @@
|
||||||
|
package me.rose.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 <output directory>");
|
||||||
|
System.exit (64);
|
||||||
|
}
|
||||||
|
String outputDir = args[0];
|
||||||
|
defineAst (
|
||||||
|
outputDir, "Expr",
|
||||||
|
Arrays.asList ("Binary : Expr left, Token operator, Expr right",
|
||||||
|
"Grouping : Expr expression", "Literal : Object value",
|
||||||
|
"Unary : Token operator, Expr right"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void
|
||||||
|
defineAst (String outputDir, String baseName, List<String> types)
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
String path = outputDir + "/" + baseName + ".java";
|
||||||
|
PrintWriter writer = new PrintWriter (path, "UTF-8");
|
||||||
|
|
||||||
|
writer.println ("package me.rose.lox;");
|
||||||
|
writer.println ();
|
||||||
|
writer.println ("import java.util.List;");
|
||||||
|
writer.println ();
|
||||||
|
writer.println ("abstract class " + baseName + " {");
|
||||||
|
|
||||||
|
defineVisitor (writer, baseName, types);
|
||||||
|
|
||||||
|
for (String type : types)
|
||||||
|
{
|
||||||
|
String className = type.split (":")[0].trim ();
|
||||||
|
String fields = type.split (":")[1].trim ();
|
||||||
|
defineType (writer, baseName, className, fields);
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.println ();
|
||||||
|
writer.println (" abstract <R> R accept(Visitor<R> visitor);");
|
||||||
|
|
||||||
|
writer.println ("}");
|
||||||
|
writer.close ();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void
|
||||||
|
defineVisitor (PrintWriter writer, String baseName, List<String> types)
|
||||||
|
{
|
||||||
|
writer.println (" interface Visitor<R> {");
|
||||||
|
|
||||||
|
for (String type : types)
|
||||||
|
{
|
||||||
|
String typeName = type.split (":")[0].trim ();
|
||||||
|
writer.println (" R visit" + typeName + baseName + "(" + typeName
|
||||||
|
+ " " + baseName.toLowerCase () + ");");
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.println (" }");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void
|
||||||
|
defineType (PrintWriter writer, String baseName, String className,
|
||||||
|
String fieldList)
|
||||||
|
{
|
||||||
|
writer.println (" static class " + className + " extends " + baseName
|
||||||
|
+ " {");
|
||||||
|
|
||||||
|
writer.println (" " + className + "(" + fieldList + ") {");
|
||||||
|
|
||||||
|
String[] fields = fieldList.split (", ");
|
||||||
|
for (String field : fields)
|
||||||
|
{
|
||||||
|
String name = field.split (" ")[1];
|
||||||
|
writer.println (" this." + name + " = " + name + ";");
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.println (" }");
|
||||||
|
|
||||||
|
writer.println ();
|
||||||
|
writer.println (" @Override");
|
||||||
|
writer.println (" <R> R accept(Visitor<R> visitor) {");
|
||||||
|
writer.println (" return visitor.visit" + className + baseName
|
||||||
|
+ "(this);");
|
||||||
|
writer.println (" }");
|
||||||
|
|
||||||
|
writer.println ();
|
||||||
|
for (String field : fields)
|
||||||
|
{
|
||||||
|
writer.println (" final " + field + ";");
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.println (" }");
|
||||||
|
}
|
||||||
|
}
|
||||||
19
src/test/java/me/rose/lox/AppTest.java
Normal file
19
src/test/java/me/rose/lox/AppTest.java
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
package me.rose.lox;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unit test for simple App.
|
||||||
|
*/
|
||||||
|
public class AppTest {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rigorous Test :-)
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void shouldAnswerWithTrue() {
|
||||||
|
assertTrue(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue