Kaynağa Gözat

finished scanner

runningwater 2 yıl önce
ebeveyn
işleme
6350646c6c

+ 104 - 1
src/main/java/com/craftinginterpreters/lox/Scanner.java

@@ -3,7 +3,9 @@
 package com.craftinginterpreters.lox;
 
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
 import static com.craftinginterpreters.lox.TokenType.*;
 
@@ -18,6 +20,26 @@ public class Scanner {
     private int start = 0;  //  points to the first character in the lexeme being scanned
     private int current = 0; // points at the character currently being considered.
     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("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);
+    }
 
     public Scanner(String source) {
         this.source = source;
@@ -67,8 +89,70 @@ public class Scanner {
             }
             case '\n' -> line++;
 
-            default -> Lox.error(line, "Unexpected character: " + c);
+            case '"' -> string();
+
+            default -> {
+                if (isDigit(c)) {
+                    number();
+                } else if (isAlpha(c)) {
+                    identifier();
+                } else {
+                    Lox.error(line, "Unexpected character: " + c);
+                }
+            }
+        } // end switch
+    }
+
+    /**
+     * keyword or user-defined identifier
+     */
+    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);
+    }
+
+    /**
+     * Number literals
+     * 1234
+     * 12.34
+     */
+    private void number() {
+        while (isDigit(peek())) advance();
+
+        // Look for a fractional part.
+        if (peek() == '.' && isDigit(peekNext())) {
+            // consume .
+            advance();
+
+            while (isDigit(peek())) advance();
         }
+
+        addToken(NUMBER, Double.parseDouble(source.substring(start, current)));
+    }
+
+    /**
+     * String literals
+     */
+    private void string() {
+        while (peek() != '"' && !isAtEnd()) {
+            if (peek() == '\n') line++;
+            advance();
+        }
+        if (isAtEnd()) {
+            Lox.error(line, "Unterminated string.");
+            return;
+        }
+
+        // 读取最后的 “
+        advance();
+
+        // Trim the surrounding quotes.
+        String value = source.substring(start + 1, current - 1);
+        addToken(STRING, value);
     }
 
     private char peek() {
@@ -76,6 +160,11 @@ public class Scanner {
         return source.charAt(current);
     }
 
+    private char peekNext() {
+        if (current + 1 >= source.length()) return '\0';
+        return source.charAt(current + 1);
+    }
+
     private void addToken(TokenType type) {
         addToken(type, null);
     }
@@ -105,4 +194,18 @@ public class Scanner {
         current++;
         return true;
     }
+
+    private boolean isDigit(char c) {
+        return c >= '0' && c <= '9';
+    }
+
+    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);
+    }
 }

+ 1 - 1
src/main/java/com/craftinginterpreters/lox/Token.java

@@ -22,6 +22,6 @@ class Token {
 
     @Override
     public String toString() {
-        return type + " " + lexeme + " " + literal;
+        return type + " [" + lexeme + "] " + (literal == null ? "" : literal);
     }
 }