Statements and State

This commit is contained in:
Ashley Rose 2025-11-02 03:35:37 -06:00
parent 9d05747655
commit dd11057ca8
Signed by untrusted user who does not match committer: rosyowo
GPG key ID: 999CEF726392D8F9
12 changed files with 381 additions and 84 deletions

19
examples/block.lx Normal file
View 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
View file

@ -0,0 +1,3 @@
print "one";
print true;
print 2 + 1;

3
examples/var.lx Normal file
View file

@ -0,0 +1,3 @@
var a = 1;
var b = 2;
print a + b;

2
examples/var1.lx Normal file
View file

@ -0,0 +1,2 @@
var a = 1;
print a = 2;

View file

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

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

View file

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

View file

@ -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)
{

View file

@ -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

View file

@ -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 ();

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

View file

@ -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