statements
This commit is contained in:
		
							
								
								
									
										153
									
								
								src/lox/Interpreter.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										153
									
								
								src/lox/Interpreter.java
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,153 @@
 | 
				
			|||||||
 | 
					package lox;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.util.List;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Interpreter implements Expr.Visitor<Object>, Stmt.Visitor<Void> {
 | 
				
			||||||
 | 
					  void interpret(List<Stmt> 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 visitGroupingExpr(Expr.Grouping expr) {
 | 
				
			||||||
 | 
					    return evaluate(expr.expression);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @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;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    // actually unreachable
 | 
				
			||||||
 | 
					    return null;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Override
 | 
				
			||||||
 | 
					  public Object visitBinaryExpr(Expr.Binary expr) {
 | 
				
			||||||
 | 
					    Object left = evaluate(expr.left);
 | 
				
			||||||
 | 
					    Object right = evaluate(expr.right);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    switch (expr.operator.type) {
 | 
				
			||||||
 | 
					      case MINUS:
 | 
				
			||||||
 | 
					        checkNumberOperands(expr.operator, left, right);
 | 
				
			||||||
 | 
					        return (double) left - (double) right;
 | 
				
			||||||
 | 
					      case SLASH:
 | 
				
			||||||
 | 
					        checkNumberOperands(expr.operator, left, right);
 | 
				
			||||||
 | 
					        return (double) left / (double) right;
 | 
				
			||||||
 | 
					      case STAR:
 | 
				
			||||||
 | 
					        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 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);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    // actually unreachable
 | 
				
			||||||
 | 
					    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;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private void checkNumberOperand(Token operator, Object operand) {
 | 
				
			||||||
 | 
					    if (operand instanceof Double) {
 | 
				
			||||||
 | 
					      return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    throw new RuntimeError(operator, "Operator must be a number.");
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private void checkNumberOperands(Token operator, Object left, Object right) {
 | 
				
			||||||
 | 
					    if (left instanceof Double && right instanceof Double) {
 | 
				
			||||||
 | 
					      return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    throw new RuntimeError(operator, "Operands must be a numbers.");
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private boolean isTruthy(Object object) {
 | 
				
			||||||
 | 
					    if (object == null) {
 | 
				
			||||||
 | 
					      return false; // null is false
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (object instanceof Boolean) {
 | 
				
			||||||
 | 
					      return (boolean) object; // booleans are their own truthiness
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return true; // everything else is true
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private boolean isEqual(Object left, Object right) {
 | 
				
			||||||
 | 
					    if (left == null && right == null) {
 | 
				
			||||||
 | 
					      return true; // null is equal to another null
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (left == null) {
 | 
				
			||||||
 | 
					      return false; // null is never equal to anything else
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return left.equals(right);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  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();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private Object evaluate(Expr expr) {
 | 
				
			||||||
 | 
					    return expr.accept(this);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private void execute(Stmt stmt) {
 | 
				
			||||||
 | 
					    stmt.accept(this);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -11,7 +11,9 @@ import java.util.List;
 | 
				
			|||||||
import static lox.TokenType.EOF;
 | 
					import static lox.TokenType.EOF;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public class Lox {
 | 
					public class Lox {
 | 
				
			||||||
 | 
					  private static final Interpreter interpreter = new Interpreter();
 | 
				
			||||||
  static boolean hadError = false;
 | 
					  static boolean hadError = false;
 | 
				
			||||||
 | 
					  static boolean hadRuntimeError = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public static void main(String[] args) throws IOException {
 | 
					  public static void main(String[] args) throws IOException {
 | 
				
			||||||
    if (args.length > 1) {
 | 
					    if (args.length > 1) {
 | 
				
			||||||
@@ -32,6 +34,9 @@ public class Lox {
 | 
				
			|||||||
    if (hadError) {
 | 
					    if (hadError) {
 | 
				
			||||||
      System.exit(65);
 | 
					      System.exit(65);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    if (hadRuntimeError) {
 | 
				
			||||||
 | 
					      System.exit(70);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private static void runPrompt() throws IOException {
 | 
					  private static void runPrompt() throws IOException {
 | 
				
			||||||
@@ -53,19 +58,24 @@ public class Lox {
 | 
				
			|||||||
    Scanner scanner = new Scanner(source);
 | 
					    Scanner scanner = new Scanner(source);
 | 
				
			||||||
    List<Token> tokens = scanner.scanTokens();
 | 
					    List<Token> tokens = scanner.scanTokens();
 | 
				
			||||||
    Parser parser = new Parser(tokens);
 | 
					    Parser parser = new Parser(tokens);
 | 
				
			||||||
    Expr expression = parser.parse();
 | 
					    List<Stmt> statements = parser.parse();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (hadError) {
 | 
					    if (hadError) {
 | 
				
			||||||
      return;
 | 
					      return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    System.out.println(new ASTPrinter().print(expression));
 | 
					    interpreter.interpret(statements);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  static void error(int line, String message) {
 | 
					  static void error(int line, String message) {
 | 
				
			||||||
    report(line, "", message);
 | 
					    report(line, "", message);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  static void runtimeError(RuntimeError error) {
 | 
				
			||||||
 | 
					    System.err.println(error.getMessage() + "\n[line " + error.token.line + "]");
 | 
				
			||||||
 | 
					    hadRuntimeError = true;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private static void report(int line, String where, String message) {
 | 
					  private static void report(int line, String where, String message) {
 | 
				
			||||||
    System.err.println("[line " + line + "] Error" + where + ": " + message);
 | 
					    System.err.println("[line " + line + "] Error" + where + ": " + message);
 | 
				
			||||||
    hadError = true;
 | 
					    hadError = true;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,6 @@
 | 
				
			|||||||
package lox;
 | 
					package lox;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.util.ArrayList;
 | 
				
			||||||
import java.util.List;
 | 
					import java.util.List;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import static lox.TokenType.*;
 | 
					import static lox.TokenType.*;
 | 
				
			||||||
@@ -14,12 +15,31 @@ class Parser {
 | 
				
			|||||||
    this.tokens = tokens;
 | 
					    this.tokens = tokens;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  Expr parse() {
 | 
					  List<Stmt> parse() {
 | 
				
			||||||
    try {
 | 
					    List<Stmt> statements = new ArrayList<>();
 | 
				
			||||||
      return expression();
 | 
					    while (!isAtEnd()) {
 | 
				
			||||||
    } catch (ParseError error) {
 | 
					      statements.add(statement());
 | 
				
			||||||
      return null;
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    return statements;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private Stmt statement() {
 | 
				
			||||||
 | 
					    if (match(PRINT)) {
 | 
				
			||||||
 | 
					      return printStatement();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return expressionStatement();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private Stmt printStatement() {
 | 
				
			||||||
 | 
					    Expr value = expression();
 | 
				
			||||||
 | 
					    consume(SEMICOLON, "Expect ';' after value.");
 | 
				
			||||||
 | 
					    return new Stmt.Print(value);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private Stmt expressionStatement() {
 | 
				
			||||||
 | 
					    Expr value = expression();
 | 
				
			||||||
 | 
					    consume(SEMICOLON, "Expect ';' after value.");
 | 
				
			||||||
 | 
					    return new Stmt.Expression(value);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private Expr expression() {
 | 
					  private Expr expression() {
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										10
									
								
								src/lox/RuntimeError.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								src/lox/RuntimeError.java
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,10 @@
 | 
				
			|||||||
 | 
					package lox;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class RuntimeError extends RuntimeException {
 | 
				
			||||||
 | 
					  final Token token;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  RuntimeError(Token token, String message) {
 | 
				
			||||||
 | 
					    super(message);
 | 
				
			||||||
 | 
					    this.token = token;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										37
									
								
								src/lox/Stmt.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								src/lox/Stmt.java
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,37 @@
 | 
				
			|||||||
 | 
					package lox;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					abstract class Stmt {
 | 
				
			||||||
 | 
					  interface Visitor<R> {
 | 
				
			||||||
 | 
					    R visitExpressionStmt(Expression stmt);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    R visitPrintStmt(Print stmt);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  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;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  abstract <R> R accept(Visitor<R> visitor);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -21,6 +21,11 @@ public class GenerateAST {
 | 
				
			|||||||
            "Grouping : Expr expression",
 | 
					            "Grouping : Expr expression",
 | 
				
			||||||
            "Literal : Object value",
 | 
					            "Literal : Object value",
 | 
				
			||||||
            "Unary : Token operator, Expr right"));
 | 
					            "Unary : Token operator, Expr right"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    defineAST(
 | 
				
			||||||
 | 
					        outputDir,
 | 
				
			||||||
 | 
					        "Stmt",
 | 
				
			||||||
 | 
					        Arrays.asList("Expression : Expr expression", "Print : Expr expression"));
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private static void defineAST(String outputDir, String baseName, List<String> types)
 | 
					  private static void defineAST(String outputDir, String baseName, List<String> types)
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user