/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * This file is part of SableCC.                             *
 * See the file "LICENSE" for copyright information and the  *
 * terms and conditions for copying, distribution and        *
 * modification of SableCC.                                  *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

Macro:LexerException
/* This file was generated by SableCC (http://www.sablecc.org/). */

package $0$lexer;

@SuppressWarnings({"serial"})
public class LexerException extends Exception
{
    public LexerException(String message)
    {
        super(message);
    }
}

$

Macro:LexerHeader
/* This file was generated by SableCC (http://www.sablecc.org/). */

package $0$lexer;

import java.io.BufferedInputStream;
import java.io.DataInputStream;
import java.io.InputStream;
import java.io.IOException;
import java.io.PushbackReader;

import $0$node.*;

public class Lexer
{
    protected Token token;
    protected State state = State.$1$;

    private final PushbackReader in;
    protected int line;
    protected int pos;
    private boolean cr;
    private boolean eof;
    private final StringBuilder text = new StringBuilder();

    protected void filter() throws LexerException, IOException
    {}

    public Lexer(PushbackReader in)
    {
        this.in = in;
    }

    public Token peek() throws LexerException, IOException
    {
        while(this.token == null)
        {
            token = getToken();
            filter();
        }

        return token;
    }

    public Token next() throws LexerException, IOException
    {
        while(this.token == null)
        {
            token = getToken();
            filter();
        }

        Token result = token;
        token = null;
        return result;
    }

    protected Token getToken() throws IOException, LexerException
    {
        int dfaState = 0;

        int startPos = this.pos;
        int startLine = this.line;

        int acceptToken = -1;
        int acceptLength = -1;
        int acceptPos = -1;
        int acceptLine = -1;
        boolean acceptCr = false;

        int[][][] gotoTableSub = Lexer.gotoTable[this.state.ordinal()];
        int[] acceptSub = Lexer.accept[this.state.ordinal()];
        this.text.setLength(0);

        while(true)
        {
            int c = getChar();

            if(c != -1)
            {
                switch(c)
                {
                    case '\n':
                        if(this.cr)
                        {
                            // If the preceding character was \r (CR),
                            // ignore this \n (LF) character and don't increase the line or column.
                            this.cr = false;
                        }
                        else
                        {
                            // If there was no preceding \r (CR) character,
                            // consider this \n (LF) character an actual newline.
                            this.line++;
                            this.pos = 0;
                        }
                        break;
                    case '\r':
                        // A \r (CR) character is always considered a newline,
                        // but a \n (LF) character following it (if any) will be ignored (see above).
                        this.line++;
                        this.pos = 0;
                        this.cr = true;
                        break;
                    case 0x2028: // Unicode line separator
                    case 0x2029: // Unicode paragraph separator
                        // A Unicode line or paragraph separator is treated like a newline,
                        // but doesn't take part in the special handling for CR+LF.
                        // FIXME This case is a workaround for a limitation in the ProB cliparser prepl protocol, which doesn't support embedded newlines.
                        // TODO Remove this case once that is resolved. Practically nothing else uses the Unicode line separator character.
                        this.line++;
                        this.pos = 0;
                        this.cr = false;
                        break;
                    default:
                        this.pos++;
                        this.cr = false;
                        break;
                }

                this.text.append((char) c);

                do
                {
                    int oldState = (dfaState < -1) ? (-2 -dfaState) : dfaState;

                    dfaState = -1;

                    int[][] tmp1 = gotoTableSub[oldState];
                    int low = 0;
                    int high = tmp1.length - 1;

                    // find next DFA state depending on character c
                    // an entry {Low, Up, Id} -> means if Low <= c <= Up -> goto state Id
                    while(low <= high)
                    {
                        int middle = (low + high) >>> 1;
                        int[] tmp2 = tmp1[middle];

                        if(c < tmp2[0])
                        {
                            high = middle - 1;
                        }
                        else if(c > tmp2[1])
                        {
                            low = middle + 1;
                        }
                        else
                        {
                            dfaState = tmp2[2];
                            break;
                        }
                    }
                } while(dfaState < -1);
            }
            else
            {
                dfaState = -1;
            }

            if(dfaState >= 0)
            {
                if(acceptSub[dfaState] != -1)
                {
                    acceptToken = acceptSub[dfaState];
                    acceptLength = this.text.length();
                    acceptPos = this.pos;
                    acceptLine = this.line;
                    acceptCr = this.cr;
                }
            }
            else
            {
                Token tok;
                switch(acceptToken)
                {
                    case -1:
                        if(this.text.length() > 0)
                        {
                            throw new LexerException("[" + (startLine + 1) + "," + (startPos + 1) + "] Unknown token: " + this.text);
                        }

                        return new EOF(startLine + 1, startPos + 1);


$

Macro:LexerVariableToken
                    case $0$:
                        tok = new $1$(getText(acceptLength), startLine + 1, startPos + 1);

$

Macro:LexerFixedToken
                    case $0$:
                        tok = new $1$(startLine + 1, startPos + 1);

$

Macro:TokenSwitchHeader
                        switch(state.ordinal())
                        {

$

Macro:TokenCase
                            case $0$: state = State.$1$; break;

$

Macro:TokenSwitchTail
                        }

$

Macro:LexerTokenTail
                        break;


$

Macro:LexerBody
                    default:
                        throw new LexerException("[" + (startLine + 1) + "," + (startPos + 1) + "] Internal lexer error: invalid accept table entry " + acceptToken + ", current text: " + this.text);
                }
                pushBack(acceptLength);
                this.pos = acceptPos;
                this.line = acceptLine;
                this.cr = acceptCr;
                return tok;
            }
        }
    }

    private int getChar() throws IOException
    {
        if(this.eof)
        {
            return -1;
        }

        int result = this.in.read();

        if(result == -1)
        {
            this.eof = true;
        }

        return result;
    }

    private void pushBack(int acceptLength) throws IOException
    {
        int length = this.text.length();
        for(int i = length - 1; i >= acceptLength; i--)
        {
            this.eof = false;

            this.in.unread(this.text.charAt(i));
        }
    }

    /**
     * <p>
     * Push the given token's text back onto the input.
     * Note that the lexer state is <i>not</i> restored,
     * so a following {@link #next()}/{@link #peek()} call may result in a different token.
     * </p>
     * <p>
     * <b>Note:</b>
     * If the token text contains newlines,
     * the caller must ensure that CR+LF pairs are unread in their entirety.
     * If only one half of a CR+LF pair is unread,
     * the line numbers will be incorrect when it is lexed again.
     * </p>
     *
     * @param tok the token to push back onto the input
     * @throws IOException when thrown by {@link PushbackReader#unread(int)}
     */
    protected void unread(Token tok) throws IOException
    {
        String tokenText = tok.getText();
        int length = tokenText.length();

        for(int i = length - 1; i >= 0; i--)
        {
            this.eof = false;

            this.in.unread(tokenText.charAt(i));
        }

        this.pos = tok.getPos() - 1;
        this.line = tok.getLine() - 1;
    }

    private String getText(int acceptLength)
    {
        return this.text.substring(0, acceptLength);
    }

    private static final int[][][][] gotoTable;
/*  {

$

Macro:LexerAcceptHeader
    };*/

    private static final int[][] accept;
/*  {

$

Macro:LexerAcceptTail
    };*/


$

Macro:LexerStateHeader
    public enum State
    {

$

Macro:LexerStateBody
        $0$,

$

Macro:LexerStateTail
        ;

        public int id()
        {
            return this.ordinal();
        }
    }

$

Macro:LexerTail

    static
    {
        try
        {
            InputStream resStream = Lexer.class.getResourceAsStream("lexer.dat");
            if(resStream == null)
            {
                throw new RuntimeException("The file \"lexer.dat\" is missing.");
            }
            DataInputStream s = new DataInputStream(new BufferedInputStream(resStream));

            // read gotoTable
            int gotoTableLength1 = s.readInt();
            gotoTable = new int[gotoTableLength1][][][];
            for(int i = 0; i < gotoTableLength1; i++)
            {
                int gotoTableLength2 = s.readInt();
                gotoTable[i] = new int[gotoTableLength2][][];
                for(int j = 0; j < gotoTableLength2; j++)
                {
                    int gotoTableLength3 = s.readInt();
                    gotoTable[i][j] = new int[gotoTableLength3][3];
                    for(int k = 0; k < gotoTableLength3; k++)
                    {
                        for(int l = 0; l < 3; l++)
                        {
                            gotoTable[i][j][k][l] = s.readInt();
                        }
                    }
                }
            }

            // read accept
            int acceptLength1 = s.readInt();
            accept = new int[acceptLength1][];
            for(int i = 0; i < acceptLength1; i++)
            {
                int acceptLength2 = s.readInt();
                accept[i] = new int[acceptLength2];
                for(int j = 0; j < acceptLength2; j++)
                {
                    accept[i][j] = s.readInt();
                }
            }

            s.close();
        }
        catch(IOException e)
        {
            throw new RuntimeException("The file \"lexer.dat\" is either missing or corrupted.", e);
        }
    }
}

$
