package xhl.core;

import java.io.IOException;
import java.io.Reader;
import java.util.regex.Pattern;

import xhl.core.Token.TokenType;
import xhl.core.elements.CodeElement.CodePosition;

/**
 * Lexical analyzer for XHL
 *
 * @author Sergej Chodarev
 */
public class Lexer {
    private static final String PUNCTUATION = "+-*/_=<>.:?!";
    private static final String numberRegex = "[+-]?\\d+(\\.\\d*)?";

    private final Reader input;
    private int ch;
    private int line = 1;
    private int column = 0;

    public Lexer(Reader input) throws IOException {
        this.input = input;
        ch = input.read();
    }

    public Token nextToken() throws IOException {
        while (true) {
            switch (ch) {
            case ' ':
            case '\t':
            case '\n':
            case '\r':
                ch = nextChar();
                break;
            case ';':
                do
                    ch = nextChar();
                while (ch != '\n');
                break;
            case '(':
                ch = nextChar();
                return new Token(TokenType.PAR_OPEN, getPosition());
            case ')':
                ch = nextChar();
                return new Token(TokenType.PAR_CLOSE, getPosition());
            case '[':
                ch = nextChar();
                return new Token(TokenType.BRACKET_OPEN, getPosition());
            case ']':
                ch = nextChar();
                return new Token(TokenType.BRACKET_CLOSE, getPosition());
            case '"':
                return readString();
            default:
                if (Character.isLetterOrDigit(ch)
                        || PUNCTUATION.indexOf(ch) != -1)
                    return readSymbol();
                else
                    return null;
            }
        }
    }

    private int nextChar() throws IOException {
        int ch = input.read();
        column++;
        if (ch == '\n') {
            line++;
            column = 0; // FIXME
        }
        return ch;
    }

    private Token readString() throws IOException {
        StringBuilder sb = new StringBuilder();
        ch = nextChar(); // "
        while (ch != '"') {
            sb.append((char) ch);
            ch = nextChar();
        }
        ch = nextChar(); // "
        return new Token(TokenType.STRING, sb.toString(), getPosition());
    }

    private Token readSymbol() throws IOException {
        CodePosition position = getPosition();

        // Read whole symbol
        StringBuilder sb = new StringBuilder();
        while (Character.isLetterOrDigit(ch) || PUNCTUATION.indexOf(ch) != -1) {
            sb.append((char) ch);
            ch = nextChar();
        }
        String symbol = sb.toString();

        // Check symbol type
        if (symbol.equals("true"))
            return new Token(TokenType.TRUE, position);
        if (symbol.equals("false"))
            return new Token(TokenType.FALSE, position);
        if (Pattern.matches(numberRegex, symbol))
            return new Token(TokenType.NUMBER, Double.valueOf(symbol), position);
        else
            return new Token(TokenType.SYMBOL, symbol, position);
    }

    private CodePosition getPosition() {
        return new CodePosition("input", line, column); // FIXME filename
    }
}

@@ -63,16 +63,64 @@
 Reader;%0A
+import java.util.HashMap;%0Aimport java.util.Map;%0A
 import j
@@ -446,16 +446,319 @@
 %5C%5Cd*)?%22;
+%0A    private static final Map%3CCharacter, TokenType%3E simpleTokens =%0A        new HashMap%3CCharacter, TokenType%3E() %7B%7B%0A            put('(', TokenType.PAR_OPEN);%0A            put(')', TokenType.PAR_CLOSE);%0A            put('%5B', TokenType.BRACKET_OPEN);%0A            put('%5D', TokenType.BRACKET_CLOSE);%0A        %7D%7D;
 %0A%0A    pr
@@ -1092,16 +1092,31 @@
 ase ' ':
+ // White space
 %0A       
@@ -1254,16 +1254,27 @@
 ase ';':
+ // Comment
 %0A       
@@ -1403,111 +1403,56 @@
 se '
-(':%0A                ch = nextChar();%0A                return new Token(TokenType.PAR_OPEN, getPosition()
+%22': // String%0A                return readString(
 );%0A 
@@ -1466,16 +1466,15 @@
     
-case ')'
+default
 :%0A  
@@ -1491,245 +1491,120 @@
     
-ch = nextChar();%0A                return new Token(TokenType.PAR_CLOSE, getPosition());%0A            case '%5B':%0A                ch = nextChar();%0A                return new Token(TokenType.BRACKET_OPEN, getPosition());%0A            case '%5D':%0A
+if (simpleTokens.containsKey((char)ch)) %7B%0A                    TokenType type = simpleTokens.get((char)ch);%0A     
     
@@ -1606,33 +1606,32 @@
                 
- 
 ch = nextChar();
@@ -1623,32 +1623,36 @@
 h = nextChar();%0A
+    
                 
@@ -1668,39 +1668,20 @@
 w Token(
-TokenType.BRACKET_CLOSE
+type
 , getPos
@@ -1706,91 +1706,18 @@
     
-case '%22':%0A                return readString();%0A            default:%0A               
+    %7D else
  if 

Generalize processing of simple tokens