Scanner.java 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  1. /* Copyright (C) 2019-2023 Hangzhou HSH Co. Ltd.
  2. * All right reserved.*/
  3. package com.craftinginterpreters.lox;
  4. import java.util.ArrayList;
  5. import java.util.List;
  6. import static com.craftinginterpreters.lox.TokenType.*;
  7. /**
  8. * @author simon
  9. * @date 2023-06-04 16:30
  10. * @desc
  11. */
  12. public class Scanner {
  13. private final String source;
  14. private final List<Token> tokens = new ArrayList<>();
  15. private int start = 0; // points to the first character in the lexeme being scanned
  16. private int current = 0; // points at the character currently being considered.
  17. private int line = 1;
  18. public Scanner(String source) {
  19. this.source = source;
  20. }
  21. public List<Token> scanTokens() {
  22. while (!isAtEnd()) {
  23. // We are at the beginning of the next lexeme.
  24. start = current;
  25. scanToken();
  26. }
  27. // add finish token to the end of list
  28. tokens.add(new Token(EOF, "", null, line));
  29. return tokens;
  30. }
  31. private void scanToken() {
  32. char c = advance();
  33. switch (c) {
  34. case '(' -> addToken(LEFT_PARAM);
  35. case ')' -> addToken(RIGHT_PARAM);
  36. case '{' -> addToken(LEFT_BRACE);
  37. case '}' -> addToken(RIGHT_BRACE);
  38. case ',' -> addToken(COMMA);
  39. case '.' -> addToken(DOT);
  40. case '-' -> addToken(MINUS);
  41. case '+' -> addToken(PLUS);
  42. case ';' -> addToken(SEMICOLON);
  43. case '*' -> addToken(STAR);
  44. // 两个字符 != == >= <=
  45. case '!' -> addToken(match('=') ? BANG_EQUAL : BANG);
  46. case '=' -> addToken(match('=') ? EQUAL_EQUAL : EQUAL);
  47. case '<' -> addToken(match('=') ? LESS_EQUAL : LESS);
  48. case '>' -> addToken(match('=') ? GRATER_EQUAL : GRATER);
  49. case '/' -> {
  50. if (match('/')) {
  51. // A comment goes until the end of the line.
  52. while (peek() != '\n' && !isAtEnd()) advance();
  53. } else {
  54. addToken(SLASH);
  55. }
  56. }
  57. // Ignore whitespace
  58. case ' ', '\r', '\t' -> {
  59. }
  60. case '\n' -> line++;
  61. default -> Lox.error(line, "Unexpected character: " + c);
  62. }
  63. }
  64. private char peek() {
  65. if (isAtEnd()) return '\0';
  66. return source.charAt(current);
  67. }
  68. private void addToken(TokenType type) {
  69. addToken(type, null);
  70. }
  71. private void addToken(TokenType type, Object literal) {
  72. String text = source.substring(start, current);
  73. tokens.add(new Token(type, text, literal, line));
  74. }
  75. /**
  76. * consumes the next character in the source file and returns it
  77. *
  78. * @return
  79. */
  80. private char advance() {
  81. return source.charAt(current++);
  82. }
  83. private boolean isAtEnd() {
  84. return current >= source.length();
  85. }
  86. private boolean match(char expected) {
  87. if (isAtEnd()) return false;
  88. if (source.charAt(current) != expected) return false;
  89. current++;
  90. return true;
  91. }
  92. }