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