/* Copyright (C) 2019-2023 Hangzhou HSH Co. Ltd. * All right reserved.*/ package com.craftinginterpreters.lox; import java.util.ArrayList; import java.util.List; import static com.craftinginterpreters.lox.TokenType.*; /** * @author simon * @date 2023-06-04 16:30 * @desc */ public class Scanner { private final String source; private final List tokens = new ArrayList<>(); 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; public Scanner(String source) { this.source = source; } public List scanTokens() { while (!isAtEnd()) { // We are at the beginning of the next lexeme. start = current; scanToken(); } // add finish token to the end of list tokens.add(new Token(EOF, "", null, line)); return tokens; } private void scanToken() { char c = advance(); switch (c) { case '(' -> addToken(LEFT_PARAM); case ')' -> addToken(RIGHT_PARAM); case '{' -> addToken(LEFT_BRACE); case '}' -> addToken(RIGHT_BRACE); case ',' -> addToken(COMMA); case '.' -> addToken(DOT); case '-' -> addToken(MINUS); case '+' -> addToken(PLUS); case ';' -> addToken(SEMICOLON); case '*' -> addToken(STAR); // 两个字符 != == >= <= case '!' -> addToken(match('=') ? BANG_EQUAL : BANG); case '=' -> addToken(match('=') ? EQUAL_EQUAL : EQUAL); case '<' -> addToken(match('=') ? LESS_EQUAL : LESS); case '>' -> addToken(match('=') ? GRATER_EQUAL : GRATER); case '/' -> { if (match('/')) { // A comment goes until the end of the line. while (peek() != '\n' && !isAtEnd()) advance(); } else { addToken(SLASH); } } // Ignore whitespace case ' ', '\r', '\t' -> { } case '\n' -> line++; default -> Lox.error(line, "Unexpected character: " + c); } } private char peek() { if (isAtEnd()) return '\0'; 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)); } /** * consumes the next character in the source file and returns it * * @return */ private char advance() { return source.charAt(current++); } private boolean isAtEnd() { return current >= source.length(); } private boolean match(char expected) { if (isAtEnd()) return false; if (source.charAt(current) != expected) return false; current++; return true; } }