package qls.parser; import java.io.IOException; import java.io.Reader; import java.util.HashMap; import java.util.Map; import ql.ast.QLNode; import ql.ast.expression.Identifier; import qls.ast.expression.literal.FloatLiteral; import qls.ast.expression.literal.IntegerLiteral; import qls.ast.expression.literal.StringLiteral; public class QLSLexer implements QLSTokens { private static final Map<String, Integer> KEYWORDS; static { KEYWORDS = new HashMap<String, Integer>(); KEYWORDS.put("stylesheet", STYLESHEET); KEYWORDS.put("page", PAGE); KEYWORDS.put("section", SECTION); KEYWORDS.put("question", QUESTION); KEYWORDS.put("default", DEFAULT); KEYWORDS.put("widget", WIDGET); KEYWORDS.put("boolean", BOOLEAN); KEYWORDS.put("integer", INTEGER); KEYWORDS.put("float", FLOAT); KEYWORDS.put("money", MONEY); KEYWORDS.put("string", STRING); KEYWORDS.put("checkbox", CHECKBOX); KEYWORDS.put("spinbox", SPINBOX); KEYWORDS.put("radio", RADIO); KEYWORDS.put("slider", SLIDER); KEYWORDS.put("text", TEXT); KEYWORDS.put("dropdown", DROPDOWN); KEYWORDS.put("width", WIDTH); KEYWORDS.put("height", HEIGHT); KEYWORDS.put("font", FONT); KEYWORDS.put("fontsize", FONTSIZE); KEYWORDS.put("color", COLOR); } private int token; private int c = ' '; private QLNode yylval; private final Reader input; public QLSLexer(Reader input) { this.input = input; nextChar(); } private void nextChar() { if (c >= 0) { try { c = input.read(); } catch (IOException e) { c = -1; } } } public int nextToken() { boolean inComment = false; for (;;) { if (inComment) { while (c != '*' && c != -1) { nextChar(); } if (c == '*') { nextChar(); if (c == '/') { nextChar(); inComment = false; } continue; } } while (c == ' ' || c == '\t' || c == '\n' || c == '\r') { nextChar(); } if (c < 0) { return token = ENDINPUT; } switch (c) { case '/': { nextChar(); if (c == '*') { inComment = true; nextChar(); continue; } else if (c == '/') { while (c != '\n') { nextChar(); } continue; } return token = '/'; } case ':': nextChar(); return token = ':'; case '}': nextChar(); return token = '}'; case '{': nextChar(); return token = '{'; case ')': nextChar(); return token = ')'; case '(': nextChar(); return token = '('; case ',': nextChar(); return token = ','; case ';': nextChar(); return token = ';'; case '"': { StringBuilder sb = new StringBuilder(); // Skip opening quote. nextChar(); // Build a string from everything between the quotes. while (c != '"') { // Detected escaped quotes. if(c == '\\') { nextChar(); if(c == '"') { sb.append((char)c); nextChar(); continue; } } sb.append((char)c); nextChar(); } // Skip closing quote. nextChar(); String string = sb.toString(); yylval = new StringLiteral(string); return token = STRINGLITERAL; } case '#': { StringBuilder sb = new StringBuilder("0x"); nextChar(); while(Character.isDigit(c) || Character.isLetter(c)) { sb.append((char) c); nextChar(); } // Number is 8: 2 for "0x" and 2 for r, g, and b respectively. if(sb.length() != 8) { throw new RuntimeException("Only hexadecimals of 6 numbers allowed: " + sb.toString() + "."); } try { yylval = new IntegerLiteral(Integer.decode(sb.toString())); } catch(NumberFormatException exception) { throw new NumberFormatException("Hexadecimal is illegal: " + sb.toString()); } return token = INTEGERLITERAL; } default: { if (Character.isDigit(c)) { boolean isFloat = false; double n = 0; int decs = 1; do { if(c == '.') { isFloat = true; nextChar(); continue; } if(isFloat) { n = n + (1.0 / (10 * decs)) * (c - '0'); decs *= 10; } else { n = 10 * n + (c - '0'); } yylval = isFloat ? new FloatLiteral((float)n) : new IntegerLiteral((int)n); nextChar(); } while (Character.isDigit(c) || (c == '.' && !isFloat)); return token = isFloat ? FLOATLITERAL : INTEGERLITERAL; } if (Character.isLetter(c)) { StringBuilder sb = new StringBuilder(); do { sb.append((char)c); nextChar(); } while (Character.isLetterOrDigit(c)); String name = sb.toString(); if (KEYWORDS.containsKey(name)) { return token = KEYWORDS.get(name); } yylval = new Identifier(name); return token = IDENTIFIER; } throw new RuntimeException("Unexpected character: " + (char)c); } } } } public int getToken() { return token; } public QLNode getSemantic() { return yylval; } }