/** * Copyright 2010-2017 Evgeny Gryaznov * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see http://www.gnu.org/licenses/. */ package org.textmapper.idea.lang.syntax.lexer; import com.intellij.lexer.LexerBase; import com.intellij.psi.TokenType; import com.intellij.psi.tree.IElementType; import org.textmapper.tool.parser.TMLexer; import org.textmapper.tool.parser.TMLexer.Span; import org.textmapper.tool.parser.TMLexer.Tokens; import java.io.IOException; public class TMLexerAdapter extends LexerBase implements TMTokenTypes { public static final int STATE_AFTER_COLONCOLON = 1000; private CharSequence myText; private TMLexer lexer; private Span token; private int fDocumentLength; private int fTokenOffset; private int fTokenLength; private int fState; private IElementType current; public TMLexerAdapter() { } @Override public void start(final CharSequence buffer, int startOffset, int endOffset, int initialState) { myText = buffer; fDocumentLength = endOffset; String input = buffer.toString().substring(startOffset, endOffset); try { if (lexer == null) { lexer = new IdeaLapgLexer(input); } else { lexer.reset(input); } } catch (IOException ex) { /* never happens */ } lexer.setOffset(startOffset); fTokenOffset = startOffset; lexer.setState(initialState); fState = initialState; fTokenLength = 0; token = null; current = null; } @Override public int getState() { locateToken(); return fState; } @Override public IElementType getTokenType() { locateToken(); return current; } @Override public int getTokenStart() { locateToken(); return fTokenOffset; } @Override public int getTokenEnd() { locateToken(); return fTokenOffset + fTokenLength; } @Override public void advance() { locateToken(); current = null; } @Override public CharSequence getBufferSequence() { return myText; } @Override public int getBufferEnd() { return fDocumentLength; } private void locateToken() { if (current == null) { current = nextToken(); } } public IElementType nextToken() { fTokenOffset += fTokenLength; if (token == null) { fState = lexer.getState(); readNext(); } if (fTokenOffset < token.offset) { fTokenLength = token.offset - fTokenOffset; return TokenType.BAD_CHARACTER; } int symbol = token.symbol; fTokenLength = token.endoffset - fTokenOffset; Span currentToken = token; token = null; switch (symbol) { case Tokens.code: return TOKEN_ACTION; case Tokens._skip: return WHITESPACE; case Tokens._skip_comment: return COMMENT; case Tokens._skip_multiline: return ML_COMMENT; case Tokens.scon: return STRING; case Tokens.icon: return ICON; case Tokens.ID: return ID; case Tokens.regexp: return REGEXP; // operators case Tokens.Rem: return OP_PERCENT; case Tokens.Dollar: return OP_DOLLAR; case Tokens.Or: return OP_OR; case Tokens.OrOr: return OP_OROR; case Tokens.Assign: return OP_EQ; case Tokens.AssignAssign: return OP_EQEQ; case Tokens.ExclAssign: return OP_EXCLEQ; case Tokens.Semicolon: return OP_SEMICOLON; case Tokens.Dot: return OP_DOT; case Tokens.Comma: return OP_COMMA; case Tokens.Colon: return OP_COLON; case Tokens.ColonColon: return OP_COLONCOLON; case Tokens.Lbrack: return OP_LBRACKET; case Tokens.Rbrack: return OP_RBRACKET; case Tokens.Lparen: return OP_LPAREN; case Tokens.LparenQuestAssign: return OP_LPAREN_QA; case Tokens.Rparen: return OP_RPAREN; case Tokens.Lbrace: return OP_LCURLY; case Tokens.Rbrace: return OP_RCURLY; case Tokens.Lt: return OP_LT; case Tokens.Gt: return OP_GT; case Tokens.Mult: return OP_STAR; case Tokens.Plus: return OP_PLUS; case Tokens.PlusAssign: return OP_PLUSEQ; case Tokens.Quest: return OP_QMARK; case Tokens.Excl: return OP_EMARK; case Tokens.MinusGt: return OP_ARROW; case Tokens.And: return OP_AND; case Tokens.AndAnd: return OP_ANDAND; case Tokens.Atsign: return OP_AT; case Tokens.Tilde: return OP_TILDE; case Tokens.Div: return OP_DIV; // keywords case Tokens.Ltrue: return KW_TRUE; case Tokens.Lfalse: return KW_FALSE; case Tokens.Lnew: return KW_NEW; case Tokens.Lseparator: return KW_SEPARATOR; case Tokens.Las: return KW_AS; case Tokens.Limport: return KW_IMPORT; case Tokens.Lset: return KW_SET; case Tokens.Limplements: return KW_IMPLEMENTS; // soft keywords case Tokens.Lbrackets: return KW_BRACKETS; case Tokens.Ls: return KW_S; case Tokens.Lx: return KW_X; case Tokens.Linline: return KW_INLINE; case Tokens.Lprec: return KW_PREC; case Tokens.Lshift: return KW_SHIFT; case Tokens.Lreturns: return KW_RETURNS; case Tokens.Linput: return KW_INPUT; case Tokens.Lleft: return KW_LEFT; case Tokens.Lright: return KW_RIGHT; case Tokens.Lnonassoc: return KW_NONASSOC; case Tokens.Lgenerate: return KW_GENERATE; case Tokens.Lassert: return KW_ASSERT; case Tokens.Lempty: return KW_EMPTY; case Tokens.Lnonempty: return KW_NONEMPTY; case Tokens.Lexplicit: return KW_EXPLICIT; case Tokens.Lglobal: return KW_GLOBAL; case Tokens.Llookahead: return KW_LOOKAHEAD; case Tokens.Lparam: return KW_PARAM; case Tokens.Lflag: return KW_FLAG; case Tokens.Lnoeoi: return KW_NOEOI; case Tokens.Lsoft: return KW_SOFT; case Tokens.Lclass: return KW_CLASS; case Tokens.Linterface: return KW_INTERFACE; case Tokens.Lvoid: return KW_VOID; case Tokens.Lspace: return KW_SPACE; case Tokens.Llayout: return KW_LAYOUT; case Tokens.Llanguage: return KW_LANGUAGE; case Tokens.Llalr: return KW_LALR; case Tokens.Llexer: return fState == STATE_AFTER_COLONCOLON ? KW_LEXER_ACC : KW_LEXER; case Tokens.Lparser: return fState == STATE_AFTER_COLONCOLON ? KW_PARSER_ACC : KW_PARSER; } /* default, eoi */ token = currentToken; assert token.symbol == Tokens.eoi; if (token.offset < fDocumentLength) { fTokenLength = fDocumentLength - fTokenOffset; token.offset = token.endoffset = fDocumentLength; return TEMPLATES; } return null; } private void readNext() { try { token = lexer.next(); } catch (IOException e) { /* never happens */ } } private static class IdeaLapgLexer extends TMLexer { private boolean fAfterColonColon = false; public IdeaLapgLexer(CharSequence input) throws IOException { super(input, (message, line, offset, endoffset) -> { }); } @Override public void setState(int state) { fAfterColonColon = (state == STATE_AFTER_COLONCOLON); if (fAfterColonColon) { state = States.initial; } inStatesSelector = (state&16) != 0; if (inStatesSelector) { state &= ~16; } super.setState(state); } @Override public int getState() { if (fAfterColonColon) return STATE_AFTER_COLONCOLON; return super.getState() + (inStatesSelector ? 16 : 0); } @Override public void reset(CharSequence input) throws IOException { fAfterColonColon = false; super.reset(input); } @Override protected boolean createToken(Span token, int ruleIndex) throws IOException { super.createToken(token, ruleIndex); return true; } @Override public Span next() throws IOException { Span next = super.next(); if (next.symbol != Tokens._skip && next.symbol != Tokens._skip_comment && next.symbol != Tokens._skip_multiline) { fAfterColonColon = (next.symbol == Tokens.ColonColon && super.getState() == States.initial && !inStatesSelector); } return next; } } }