This commit is contained in:
Ashley Rose 2025-11-01 19:02:37 -05:00
commit 5dac9efd38
Signed by untrusted user who does not match committer: rosyowo
GPG key ID: 999CEF726392D8F9
25 changed files with 1189 additions and 0 deletions

2
.clang-format Normal file
View file

@ -0,0 +1,2 @@
---
BasedOnStyle: GNU

57
.classpath Normal file
View 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
View file

@ -0,0 +1,2 @@
build/
target/

0
.mvn/jvm.config Normal file
View file

0
.mvn/maven.config Normal file
View file

34
.project Normal file
View 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>

View 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

View file

@ -0,0 +1,2 @@
eclipse.preferences.version=1
org.eclipse.jdt.apt.aptEnabled=false

View 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

View file

@ -0,0 +1,4 @@
activeProfiles=
eclipse.preferences.version=1
resolveWorkspaceProjects=true
version=1

16
LICENSE Normal file
View 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
View 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
View file

@ -0,0 +1,17 @@
# JLox
Lox interpreter written in Java.
## Linux Install
Requirements:
- JDK 17+
- Apache Maven
- GNU Make
```sh
\# make install
```
---
![ISC Logo](./images/isc-logo.png)

BIN
images/isc-logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

98
pom.xml Normal file
View 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>

View 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));
}
}

View 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);
}

View 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);
}
}
}

View 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 ();
}
}
}

View 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));
}
}

View 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;
}
}

View 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
}

View 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 (" }");
}
}

View 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);
}
}

9
stub.sh Normal file
View file

@ -0,0 +1,9 @@
#!/usr/bin/env sh
MYSELF=`which "$0" 2>/dev/null`
[ $? -gt 0 -a -f "$0" ] && MYSELF="./$0"
java=java
if test -n "$JAVA_HOME"; then
java="$JAVA_HOME/bin/java"
fi
exec "$java" $java_args -jar $MYSELF "$@"
exit 1