first pieces
This commit is contained in:
		
							
								
								
									
										4
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,4 @@
 | 
			
		||||
.DS_Store
 | 
			
		||||
.idea
 | 
			
		||||
*.iml
 | 
			
		||||
out
 | 
			
		||||
							
								
								
									
										41
									
								
								src/lox/Expr.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								src/lox/Expr.java
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,41 @@
 | 
			
		||||
package lox;
 | 
			
		||||
 | 
			
		||||
abstract class Expr {
 | 
			
		||||
  static class Binary extends Expr {
 | 
			
		||||
    Binary(Expr left, Token operator, Expr right) {
 | 
			
		||||
      this.left = left;
 | 
			
		||||
      this.operator = operator;
 | 
			
		||||
      this.right = right;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    final Expr left;
 | 
			
		||||
    final Token operator;
 | 
			
		||||
    final Expr right;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static class Grouping extends Expr {
 | 
			
		||||
    Grouping(Expr expression) {
 | 
			
		||||
      this.expression = expression;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    final Expr expression;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static class Literal extends Expr {
 | 
			
		||||
    Literal(Object value) {
 | 
			
		||||
      this.value = value;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    final Object value;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static class Unary extends Expr {
 | 
			
		||||
    Unary(Token operator, Expr right) {
 | 
			
		||||
      this.operator = operator;
 | 
			
		||||
      this.right = right;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    final Token operator;
 | 
			
		||||
    final Expr right;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										67
									
								
								src/lox/Lox.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								src/lox/Lox.java
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,67 @@
 | 
			
		||||
package 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()));
 | 
			
		||||
 | 
			
		||||
    // whoops
 | 
			
		||||
    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();
 | 
			
		||||
 | 
			
		||||
    for (Token token : tokens) {
 | 
			
		||||
      System.out.println(token);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  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;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										230
									
								
								src/lox/Scanner.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										230
									
								
								src/lox/Scanner.java
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,230 @@
 | 
			
		||||
package lox;
 | 
			
		||||
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.HashMap;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
 | 
			
		||||
import static lox.TokenType.*;
 | 
			
		||||
 | 
			
		||||
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(TokenType.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('/')) {
 | 
			
		||||
          // comments run to the end of the line
 | 
			
		||||
          while (peek() != '\n' && !isAtEnd()) {
 | 
			
		||||
            advance();
 | 
			
		||||
          }
 | 
			
		||||
        } else {
 | 
			
		||||
          addToken(SLASH);
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
      case ' ':
 | 
			
		||||
      case '\r':
 | 
			
		||||
      case '\t':
 | 
			
		||||
        break; // skip whitespace
 | 
			
		||||
      case '\n':
 | 
			
		||||
        line++;
 | 
			
		||||
        break; // EOL
 | 
			
		||||
      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.getOrDefault(text, 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()) { // ran off the end of the line
 | 
			
		||||
      return false;
 | 
			
		||||
    }
 | 
			
		||||
    if (source.charAt(current) != expected) { // next char is not our expected extra
 | 
			
		||||
      return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // consume second char
 | 
			
		||||
    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));
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										19
									
								
								src/lox/Token.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								src/lox/Token.java
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,19 @@
 | 
			
		||||
package 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;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										47
									
								
								src/lox/TokenType.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								src/lox/TokenType.java
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,47 @@
 | 
			
		||||
package 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
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										69
									
								
								src/tool/GenerateAST.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								src/tool/GenerateAST.java
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,69 @@
 | 
			
		||||
package tool;
 | 
			
		||||
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.io.PrintWriter;
 | 
			
		||||
import java.nio.charset.Charset;
 | 
			
		||||
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, Charset.defaultCharset());
 | 
			
		||||
 | 
			
		||||
    writer.println("package lox;");
 | 
			
		||||
    writer.println();
 | 
			
		||||
    writer.println("import java.util.List;");
 | 
			
		||||
    writer.println();
 | 
			
		||||
    writer.println("abstract class " + baseName + " {");
 | 
			
		||||
 | 
			
		||||
    for (String type : types) {
 | 
			
		||||
      String className = type.split(":")[0].trim();
 | 
			
		||||
      String fields = type.split(":")[1].trim();
 | 
			
		||||
      defineType(writer, baseName, className, fields);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    writer.println("}");
 | 
			
		||||
    writer.close();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  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();
 | 
			
		||||
 | 
			
		||||
    for (String field : fields) {
 | 
			
		||||
      writer.println("    final " + field + ";");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    writer.println("  }");
 | 
			
		||||
    writer.println();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user