diff --git a/src/Main.java b/src/Main.java
deleted file mode 100644
index 930198c..0000000
--- a/src/Main.java
+++ /dev/null
@@ -1,15 +0,0 @@
-//TIP To Run code, press or
-// click the icon in the gutter.
-public class Main {
- public static void main(String[] args) {
- //TIP Press with your caret at the highlighted text
- // to see how IntelliJ IDEA suggests fixing it.
- System.out.printf("Hello and welcome!");
-
- for (int i = 1; i <= 5; i++) {
- //TIP Press to start debugging your code. We have set one breakpoint
- // for you, but you can always add more by pressing .
- System.out.println("i = " + i);
- }
- }
-}
\ No newline at end of file
diff --git a/src/lox/Lox.java b/src/lox/Lox.java
new file mode 100644
index 0000000..8262a47
--- /dev/null
+++ b/src/lox/Lox.java
@@ -0,0 +1,62 @@
+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()));
+
+ 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 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;
+ }
+}
diff --git a/src/lox/Scanner.java b/src/lox/Scanner.java
new file mode 100644
index 0000000..8cc64f9
--- /dev/null
+++ b/src/lox/Scanner.java
@@ -0,0 +1,218 @@
+package lox;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static lox.TokenType.*;
+
+public class Scanner {
+ private final String source;
+ private final List tokens = new ArrayList<>();
+ private int start = 0;
+ private int current = 0;
+ private int line = 1;
+
+ private static final Map 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 scanTokens() {
+ while (!isAtEnd()) {
+ start = current;
+ scanToken();
+ }
+
+ tokens.add(new Token(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('/')) {
+ while (peek() != '\n' && !isAtEnd()) advance();
+ } else {
+ addToken(SLASH);
+ }
+ break;
+
+ case ' ':
+ case '\r':
+ case '\t':
+ break;
+
+ case '\n':
+ line++;
+ break;
+
+ 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.get(text);
+ if (type == null) type = IDENTIFIER;
+ addToken(type);
+ }
+
+ private void number() {
+ while (isDigit(peek())) advance();
+
+ if (peek() == '.' && isDigit(peekNext())) {
+
+ do advance();
+ while (isDigit(peek()));
+ }
+
+ 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()) return false;
+ if (source.charAt(current) != expected) return false;
+
+ 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));
+ }
+}
diff --git a/src/lox/Token.java b/src/lox/Token.java
new file mode 100644
index 0000000..a4abf08
--- /dev/null
+++ b/src/lox/Token.java
@@ -0,0 +1,19 @@
+package lox;
+
+public 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;
+ }
+}
diff --git a/src/lox/TokenType.java b/src/lox/TokenType.java
new file mode 100644
index 0000000..3d7e281
--- /dev/null
+++ b/src/lox/TokenType.java
@@ -0,0 +1,18 @@
+package lox;
+
+public 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
+}