Advertisement
Guest User

StateLexer.java

a guest
Jan 31st, 2018
16
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Java 7.43 KB | None | 0 0
  1. import static net.pyrite.lexer.TokenType.*;
  2.  
  3. import net.pyrite.exception.SyntaxError;
  4.  
  5. public class StateLexer {
  6.    
  7.     private String stream;
  8.     private int line = 1, column = 1;
  9.    
  10.     public StateLexer(String stream) {
  11.         this.stream = stream;
  12.     }
  13.    
  14.     public Token match() {
  15.         int lline = line, lcolumn = column;
  16.         TokenType state = null;
  17.         StringBuilder token = new StringBuilder();
  18.         if (peek() == '\0') return null;
  19.         if (peek('_') || alphabetical()) state = IDENTIFIER;
  20.         else if (digit()) state = INTEGER;
  21.         else if (peek(' ') || peek('\n') || peek('\t')) state = WHITESPACE;
  22.         else if (peek('~')) state = C_BLOCK;
  23.         else if (peek('"')) state = STRING_LITERAL;
  24.         else if (peek('\'')) state = CHAR_LITERAL;
  25.         else if (peek('/') && (npeek(1) == '/' || npeek(1) == '*')) {
  26.             state = COMMENT;
  27.             token.append(pop());
  28.         } else {
  29.             token.append(pop());
  30.             boolean found = false;
  31.             for (TokenType t : TokenType.values()) {
  32.                 if (!t.match.equals("") && t.match.equals(token.toString())) found = true;
  33.             }
  34.             if (!found) unexpected(token.toString());
  35.             state = SYMBOL;
  36.         }
  37.         if (state != SYMBOL) token.append(pop());
  38.         while (true) {
  39.             switch (state) {
  40.                 case IDENTIFIER:
  41.                     if (peek('_') || alphabetical() || digit()) {
  42.                         token.append(pop());
  43.                     } else if (peek('.')) {
  44.                         if (token.toString().endsWith(".")) unexpected(token.toString());
  45.                         token.append(pop());
  46.                     } else {
  47.                         if (token.toString().endsWith(".")) {
  48.                             column--;
  49.                             unexpected(".");
  50.                         } else {
  51.                             if (token.toString().indexOf('.') > -1) state = QUALIFIED;
  52.                             for (TokenType t : TokenType.values()) {
  53.                                 if (t.match.equals("")) continue;
  54.                                 if (t.match.equals(token.toString())) state = t;
  55.                             }
  56.                             return new Token(state, token.toString(), lline, lcolumn);
  57.                         }
  58.                     }
  59.                     break;
  60.                 case INTEGER:
  61.                     if (peek('x') || peek('X')) {
  62.                         if (token.length() == 1 && token.toString().charAt(0) == '0') {
  63.                             token.append(pop());
  64.                         } else unexpected(Character.toString(pop()));
  65.                     } else {
  66.                         if (token.toString().startsWith("0x")) {
  67.                             if (hex()) token.append(pop());
  68.                             else if (alphabetical()) unexpected(Character.toString(pop()));
  69.                             else if (token.length() == 2) {
  70.                                 expected("hex char", Character.toString(pop()));
  71.                             } else {
  72.                                 return new Token(state, token.toString(), lline, lcolumn);
  73.                             }
  74.                         } else if (peek('.')) {
  75.                             state = DECIMAL;
  76.                             token.append(pop());
  77.                         } else if (digit()) token.append(pop());
  78.                         else if (alphabetical()) unexpected(Character.toString(pop()));
  79.                         else return new Token(state, token.toString(), lline, lcolumn);
  80.                     }
  81.                     break;
  82.                 case DECIMAL:
  83.                     if (digit()) {
  84.                         token.append(pop());
  85.                     } else if (alphabetical()) {
  86.                         unexpected(Character.toString(pop()));
  87.                     } else {
  88.                         if (token.toString().endsWith(".")) {
  89.                             column--;
  90.                             unexpected(".");
  91.                         } else return new Token(state, token.toString(), lline, lcolumn);
  92.                     }
  93.                     break;
  94.                 case WHITESPACE: return new Token(WHITESPACE, token.toString(), lline, lcolumn);
  95.                 case C_BLOCK:
  96.                     if (peek('~')) {
  97.                         token.append(pop());
  98.                         return new Token(C_BLOCK, token.toString(), lline, lcolumn);
  99.                     } else if (peek() == '\0') {
  100.                         expected("~", "EOF");
  101.                     } else token.append(pop());
  102.                     break;
  103.                 case STRING_LITERAL:
  104.                     if (peek('\\')) {
  105.                         token.append(pop());
  106.                         if (escape()) {
  107.                             token.append(pop());
  108.                         } else error("Syntax error, invalid escape '\\" + pop() + "' at " + line + ":" + column + ". Valid escapes are \\' \\\" \\n \\r \\t \\b");
  109.                     } else if (peek('"')) {
  110.                         token.append(pop());
  111.                         return new Token(state, token.toString(), lline, lcolumn);
  112.                     } else if (peek() == '\0') expected("\"", "EOF");
  113.                     else if (peek('\n')) expected("\"", "\\n");
  114.                     else token.append(pop());
  115.                     break;
  116.                 case CHAR_LITERAL:
  117.                     if (peek('\\')) {
  118.                         token.append(pop());
  119.                         if (escape()) {
  120.                             token.append(pop());
  121.                         } else error("Syntax error, invalid escape '\\" + pop() + "' at " + line + ":" + column + ". Valid escapes are \\' \\\" \\n \\r \\t \\b");
  122.                     } else if (peek('\'')) error("Syntax error, character literal at " + line + ":" + column + " must contain a character.");
  123.                     else token.append(pop());
  124.                     if (!peek('\'')) expected("'", Character.toString(pop()));
  125.                     else {
  126.                         token.append(pop());
  127.                         return new Token(state, token.toString(), lline, lcolumn);
  128.                     }
  129.                     error("Critical error, code should never reach here. CHAR_LITERAL.");
  130.                     break;
  131.                 case COMMENT:
  132.                     if (token.toString().startsWith("//")) {
  133.                         if (peek('\n') || peek() == '\0') return new Token(COMMENT, token.toString(), lline, lcolumn);
  134.                         else token.append(pop());
  135.                     } else if (peek('*') && npeek(1) == '/') {
  136.                         token.append(pop());
  137.                         token.append(pop());
  138.                         return new Token(state, token.toString(), lline, lcolumn);
  139.                     } else {
  140.                         if (peek() == '\0') expected("*/", "EOF");
  141.                         token.append(pop());
  142.                     }
  143.                     break;
  144.                 case SYMBOL:
  145.                     boolean match = false;
  146.                     for (TokenType t : TokenType.values()) {
  147.                         if (t.match.equals("")) continue;
  148.                         if (t.match.contains(token.toString() + peek())) {
  149.                             token.append(pop());
  150.                             match = true;
  151.                             break;
  152.                         }
  153.                     }
  154.                     if (match) break;
  155.                     for (TokenType t : TokenType.values()) {
  156.                         if (t.match.equals("")) continue;
  157.                         if (t.match.equals(token.toString())) {
  158.                             return new Token(t, token.toString(), lline, lcolumn);
  159.                         }
  160.                     }
  161.                     error("Critical error, code should never reach here. SYMBOL: '" + token.toString() + "'");
  162.                     break;
  163.                 default:
  164.                     error("Critical error, unexpected state '" + state.name().toLowerCase() + "' found on matched token.");
  165.             }
  166.         }
  167.     }
  168.    
  169.     private final boolean alphabetical() {
  170.         return Character.isAlphabetic(peek());
  171.     }
  172.    
  173.     private final boolean digit() {
  174.         return Character.isDigit(peek());
  175.     }
  176.    
  177.     private final boolean escape() {
  178.         if (peek('\'') || peek('"') || peek('n') || peek('r') || peek('t') || peek('b') || peek('0')) return true;
  179.         return false;
  180.     }
  181.    
  182.     private final boolean hex() {
  183.         if (digit() ||
  184.             peek('a') || peek('A') || peek('b') || peek('B') || peek('c') || peek('C') ||
  185.             peek('d') || peek('D') || peek('e') || peek('E') || peek('f') || peek('F')) return true;
  186.         return false;
  187.     }
  188.    
  189.     private final boolean peek(char c) {
  190.         if (c == '\0' || stream.length() == 0) return false;
  191.         char first = stream.charAt(0);
  192.         if (first == '\0') return false;
  193.         return c == first;
  194.     }
  195.    
  196.     private final char peek() {
  197.         if (stream.length() == 0) return '\0';
  198.         return stream.charAt(0);
  199.     }
  200.    
  201.     private final char npeek(int n) {
  202.         if (stream.length() < n + 1) return '\0';
  203.         return stream.charAt(n);
  204.     }
  205.    
  206.     private final char pop() {
  207.         if (stream.length() > 0) {
  208.             char c = stream.charAt(0);
  209.             if (c == '\n') {
  210.                 line++;
  211.                 column = 1;
  212.             } else column++;
  213.             stream = stream.substring(1);
  214.             return c;
  215.         } return '\0';
  216.     }
  217.    
  218.     private final void expected(String expected, String found) {
  219.         error("Syntax error, expected token '" + expected + "' at " + line + ":" + column + " but found '" + found + "'.");
  220.     }
  221.    
  222.     private final void unexpected(String token) {
  223.         error("Syntax error, unexpected token '" + token + "' at " + line + ":" + column + ". Delete this token.");
  224.     }
  225.    
  226.     private final void error(String message) {
  227.         throw new SyntaxError(message);
  228.     }
  229.  
  230. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement