/** * 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.regex.lexer; import com.intellij.lexer.LexerBase; import com.intellij.psi.tree.IElementType; import org.jetbrains.annotations.NotNull; import org.textmapper.lapg.regex.RegexDefLexer; import org.textmapper.lapg.regex.RegexDefLexer.Span; import org.textmapper.lapg.regex.RegexDefLexer.Tokens; import java.io.IOException; /** * evgeny, 3/4/12 */ public class RegexLexerAdapter extends LexerBase implements RegexTokenTypes { private CharSequence myText; private RegexDefLexer lexer; private Span token; private int fDocumentLength; private int fTokenOffset; private int fState; private int fTokenLength; private int fRegexpStartOffset; private IElementType current; public RegexLexerAdapter() { } @Override public void start(@NotNull final CharSequence buffer, int startOffset, int endOffset, int initialState) { myText = buffer; fDocumentLength = endOffset; CharSequence input = buffer.subSequence(startOffset, endOffset); try { if (lexer == null) { lexer = new IdeaRegexDefLexer(input); } else { lexer.reset(input); } } catch (IOException ex) { /* never happens */ } lexer.setOffset(startOffset); fTokenOffset = startOffset; fRegexpStartOffset = initialState == 0 ? startOffset : -1; lexer.setState(initialState > 0 ? initialState - 1 : 0); fState = initialState; fTokenLength = 0; token = null; current = null; } @Override public int getState() { locateToken(); if (fTokenOffset == fRegexpStartOffset) { return 0; } 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; } @NotNull @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() + 1; readNext(); } if (fTokenOffset < token.offset) { fTokenLength = token.offset - fTokenOffset; if (fTokenOffset == fRegexpStartOffset && myText.charAt(fTokenOffset) == '/') { fTokenLength = 1; return RE_DELIMITERS; } if (token.symbol == Tokens.eoi && fTokenLength > 1 && myText.charAt(token.offset - 1) == '/') { fTokenLength--; return RE_BAD; } if (token.symbol == Tokens.eoi && fTokenLength == 1 && myText.charAt(fTokenOffset) == '/') { return RE_DELIMITERS; } return RE_BAD; } int symbol = token.symbol; fTokenLength = token.endoffset - fTokenOffset; Span currentToken = token; token = null; switch (symbol) { case Tokens._char: return RE_CHAR; case Tokens.escaped: return RE_ESCAPED; case Tokens.charclass: return RE_CHARCLASS; case Tokens.Dot: return RE_DOT; case Tokens.Mult: return RE_MULT; case Tokens.Plus: return RE_PLUS; case Tokens.Quest: return RE_QUESTIONMARK; case Tokens.quantifier: return RE_QUANTFIER; case Tokens.Lparen: return RE_LPAREN; case Tokens.LparenQuest: return RE_LPARENQMARK; case Tokens.Or: return RE_OR; case Tokens.Rparen: return RE_RPAREN; case Tokens.expand: return RE_EXPAND; case Tokens.Lbrack: return RE_LSQUARE; case Tokens.LbrackXor: return RE_LSQUAREXOR; case Tokens.Minus: return RE_MINUS; case Tokens.Rbrack: return RE_RSQUARE; case Tokens.kw_eoi: return RE_EOI; case Tokens.op_intersect: return RE_INTERSECT; case Tokens.op_minus: return RE_SETDIFF; case Tokens.op_union: return RE_SETUNION; } /* default, eoi */ token = currentToken; assert token.symbol == Tokens.eoi && token.endoffset == fDocumentLength; return null; } private void readNext() { try { token = lexer.next(); } catch (IOException e) { /* never happens */ } } private static class IdeaRegexDefLexer extends RegexDefLexer { public IdeaRegexDefLexer(CharSequence input) throws IOException { super(input, (message, offset, endoffset) -> { }); } @Override protected boolean createToken(Span token, int ruleIndex) throws IOException { super.createToken(token, ruleIndex); return true; } } }