Statements and State
This commit is contained in:
parent
9d05747655
commit
dd11057ca8
19
examples/block.lx
Normal file
19
examples/block.lx
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
var a = "global a";
|
||||
var b = "global b";
|
||||
var c = "global c";
|
||||
{
|
||||
var a = "outer a";
|
||||
var b = "outer b";
|
||||
{
|
||||
var a = "inner a";
|
||||
print a;
|
||||
print b;
|
||||
print c;
|
||||
}
|
||||
print a;
|
||||
print b;
|
||||
print c;
|
||||
}
|
||||
print a;
|
||||
print b;
|
||||
print c;
|
||||
3
examples/print.lx
Normal file
3
examples/print.lx
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
print "one";
|
||||
print true;
|
||||
print 2 + 1;
|
||||
3
examples/var.lx
Normal file
3
examples/var.lx
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
var a = 1;
|
||||
var b = 2;
|
||||
print a + b;
|
||||
2
examples/var1.lx
Normal file
2
examples/var1.lx
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
var a = 1;
|
||||
print a = 2;
|
||||
|
|
@ -1,68 +0,0 @@
|
|||
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));
|
||||
}
|
||||
}
|
||||
52
src/main/java/me/rose/lox/Environment.java
Normal file
52
src/main/java/me/rose/lox/Environment.java
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
package me.rose.lox;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
class Environment
|
||||
{
|
||||
private Environment enclosing;
|
||||
private final Map<String, Object> values = new HashMap<> ();
|
||||
|
||||
Environment () { enclosing = null; }
|
||||
|
||||
Environment (Environment enclosing) { this.enclosing = enclosing; }
|
||||
|
||||
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 + "'.");
|
||||
}
|
||||
|
||||
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 + "'.");
|
||||
}
|
||||
|
||||
void
|
||||
define (String name, Object value)
|
||||
{
|
||||
values.put (name, value);
|
||||
}
|
||||
}
|
||||
|
|
@ -4,10 +4,26 @@ import java.util.List;
|
|||
|
||||
abstract class Expr {
|
||||
interface Visitor<R> {
|
||||
R visitAssignExpr(Assign expr);
|
||||
R visitBinaryExpr(Binary expr);
|
||||
R visitGroupingExpr(Grouping expr);
|
||||
R visitLiteralExpr(Literal 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> R accept(Visitor<R> visitor) {
|
||||
return visitor.visitAssignExpr(this);
|
||||
}
|
||||
|
||||
final Token name;
|
||||
final Expr value;
|
||||
}
|
||||
static class Binary extends Expr {
|
||||
Binary(Expr left, Token operator, Expr right) {
|
||||
|
|
@ -63,6 +79,18 @@ abstract class Expr {
|
|||
final Token operator;
|
||||
final Expr right;
|
||||
}
|
||||
static class Variable extends Expr {
|
||||
Variable(Token name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
<R> R accept(Visitor<R> visitor) {
|
||||
return visitor.visitVariableExpr(this);
|
||||
}
|
||||
|
||||
final Token name;
|
||||
}
|
||||
|
||||
abstract <R> R accept(Visitor<R> visitor);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,14 +1,20 @@
|
|||
package me.rose.lox;
|
||||
|
||||
class Interpreter implements Expr.Visitor<Object>
|
||||
import java.util.List;
|
||||
|
||||
class Interpreter implements Expr.Visitor<Object>, Stmt.Visitor<Void>
|
||||
{
|
||||
private Environment environment = new Environment ();
|
||||
|
||||
void
|
||||
interpret (Expr expression)
|
||||
interpret (List<Stmt> statements)
|
||||
{
|
||||
try
|
||||
{
|
||||
Object value = evaluate (expression);
|
||||
System.out.println (stringify (value));
|
||||
for (Stmt statement : statements)
|
||||
{
|
||||
execute (statement);
|
||||
}
|
||||
}
|
||||
catch (RuntimeError error)
|
||||
{
|
||||
|
|
@ -22,6 +28,79 @@ class Interpreter implements Expr.Visitor<Object>
|
|||
return expr.accept (this);
|
||||
}
|
||||
|
||||
private void
|
||||
execute (Stmt stmt)
|
||||
{
|
||||
stmt.accept (this);
|
||||
}
|
||||
|
||||
void
|
||||
executeBlock (List<Stmt> 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
|
||||
visitPrintStmt (Stmt.Print stmt)
|
||||
{
|
||||
Object value = evaluate (stmt.expression);
|
||||
System.out.println (stringify (value));
|
||||
return null;
|
||||
}
|
||||
|
||||
@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 Object
|
||||
visitAssignExpr (Expr.Assign expr)
|
||||
{
|
||||
Object value = evaluate (expr.value);
|
||||
environment.assign (expr.name, value);
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object
|
||||
visitGroupingExpr (Expr.Grouping expr)
|
||||
|
|
@ -106,6 +185,13 @@ class Interpreter implements Expr.Visitor<Object>
|
|||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object
|
||||
visitVariableExpr (Expr.Variable expr)
|
||||
{
|
||||
return environment.get (expr.name);
|
||||
}
|
||||
|
||||
private void
|
||||
checkNumberOperand (Token operator, Object operand)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -67,12 +67,12 @@ public class Lox
|
|||
Scanner scanner = new Scanner (source);
|
||||
List<Token> tokens = scanner.scanTokens ();
|
||||
Parser parser = new Parser (tokens);
|
||||
Expr expression = parser.parse ();
|
||||
List<Stmt> statements = parser.parse ();
|
||||
|
||||
if (hadError)
|
||||
return;
|
||||
|
||||
interpreter.interpret (expression);
|
||||
interpreter.interpret (statements);
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package me.rose.lox;
|
|||
|
||||
import static me.rose.lox.TokenType.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
class Parser
|
||||
|
|
@ -15,23 +16,117 @@ class Parser
|
|||
|
||||
Parser (List<Token> tokens) { this.tokens = tokens; }
|
||||
|
||||
Expr
|
||||
List<Stmt>
|
||||
parse ()
|
||||
{
|
||||
try
|
||||
List<Stmt> statements = new ArrayList<> ();
|
||||
while (!isAtEnd ())
|
||||
{
|
||||
return expression ();
|
||||
}
|
||||
catch (ParseError error)
|
||||
{
|
||||
return null;
|
||||
statements.add (declaration ());
|
||||
}
|
||||
|
||||
return statements;
|
||||
}
|
||||
|
||||
private Expr
|
||||
expression ()
|
||||
{
|
||||
return equality ();
|
||||
return assignment ();
|
||||
}
|
||||
|
||||
private Stmt
|
||||
declaration ()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (match (VAR))
|
||||
return varDeclaration ();
|
||||
|
||||
return statement ();
|
||||
}
|
||||
catch (ParseError error)
|
||||
{
|
||||
synchronize ();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private Stmt
|
||||
statement ()
|
||||
{
|
||||
if (match (PRINT))
|
||||
return printStatement ();
|
||||
if (match (LEFT_BRACE))
|
||||
return new Stmt.Block (block ());
|
||||
|
||||
return expressionStatement ();
|
||||
}
|
||||
|
||||
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
|
||||
expressionStatement ()
|
||||
{
|
||||
Expr expr = expression ();
|
||||
consume (SEMICOLON, "Expect ';' after expression");
|
||||
return new Stmt.Expression (expr);
|
||||
}
|
||||
|
||||
private List<Stmt>
|
||||
block ()
|
||||
{
|
||||
List<Stmt> statements = new ArrayList<> ();
|
||||
|
||||
while (!check (RIGHT_BRACE) && !isAtEnd ())
|
||||
{
|
||||
statements.add (declaration ());
|
||||
}
|
||||
|
||||
consume (RIGHT_BRACE, "Expect '}' after block.");
|
||||
return statements;
|
||||
}
|
||||
|
||||
private Expr
|
||||
assignment ()
|
||||
{
|
||||
Expr expr = equality ();
|
||||
|
||||
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
|
||||
|
|
@ -122,6 +217,11 @@ class Parser
|
|||
return new Expr.Literal (previous ().literal);
|
||||
}
|
||||
|
||||
if (match (IDENTIFIER))
|
||||
{
|
||||
return new Expr.Variable (previous ());
|
||||
}
|
||||
|
||||
if (match (LEFT_PAREN))
|
||||
{
|
||||
Expr expr = expression ();
|
||||
|
|
|
|||
64
src/main/java/me/rose/lox/Stmt.java
Normal file
64
src/main/java/me/rose/lox/Stmt.java
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
package me.rose.lox;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
abstract class Stmt {
|
||||
interface Visitor<R> {
|
||||
R visitBlockStmt(Block stmt);
|
||||
R visitExpressionStmt(Expression stmt);
|
||||
R visitPrintStmt(Print stmt);
|
||||
R visitVarStmt(Var stmt);
|
||||
}
|
||||
static class Block extends Stmt {
|
||||
Block(List<Stmt> statements) {
|
||||
this.statements = statements;
|
||||
}
|
||||
|
||||
@Override
|
||||
<R> R accept(Visitor<R> visitor) {
|
||||
return visitor.visitBlockStmt(this);
|
||||
}
|
||||
|
||||
final List<Stmt> statements;
|
||||
}
|
||||
static class Expression extends Stmt {
|
||||
Expression(Expr expression) {
|
||||
this.expression = expression;
|
||||
}
|
||||
|
||||
@Override
|
||||
<R> R accept(Visitor<R> visitor) {
|
||||
return visitor.visitExpressionStmt(this);
|
||||
}
|
||||
|
||||
final Expr expression;
|
||||
}
|
||||
static class Print extends Stmt {
|
||||
Print(Expr expression) {
|
||||
this.expression = expression;
|
||||
}
|
||||
|
||||
@Override
|
||||
<R> R accept(Visitor<R> visitor) {
|
||||
return visitor.visitPrintStmt(this);
|
||||
}
|
||||
|
||||
final Expr expression;
|
||||
}
|
||||
static class Var extends Stmt {
|
||||
Var(Token name, Expr initializer) {
|
||||
this.name = name;
|
||||
this.initializer = initializer;
|
||||
}
|
||||
|
||||
@Override
|
||||
<R> R accept(Visitor<R> visitor) {
|
||||
return visitor.visitVarStmt(this);
|
||||
}
|
||||
|
||||
final Token name;
|
||||
final Expr initializer;
|
||||
}
|
||||
|
||||
abstract <R> R accept(Visitor<R> visitor);
|
||||
}
|
||||
|
|
@ -18,9 +18,17 @@ public class GenerateAst
|
|||
String outputDir = args[0];
|
||||
defineAst (
|
||||
outputDir, "Expr",
|
||||
Arrays.asList ("Binary : Expr left, Token operator, Expr right",
|
||||
Arrays.asList ("Assign : Token name, Expr value",
|
||||
"Binary : Expr left, Token operator, Expr right",
|
||||
"Grouping : Expr expression", "Literal : Object value",
|
||||
"Unary : Token operator, Expr right"));
|
||||
"Unary : Token operator, Expr right",
|
||||
"Variable : Token name"));
|
||||
|
||||
defineAst (outputDir, "Stmt",
|
||||
Arrays.asList ("Block : List<Stmt> statements",
|
||||
"Expression : Expr expression",
|
||||
"Print : Expr expression",
|
||||
"Var : Token name, Expr initializer"));
|
||||
}
|
||||
|
||||
private static void
|
||||
|
|
|
|||
Loading…
Reference in a new issue