/* * Copyright (c) 2012 Sam Harwell, Tunnel Vision Laboratories LLC * All rights reserved. * * The source code of this document is proprietary work, and is not licensed for * distribution. For information about licensing, contact Sam Harwell at: * sam@tunnelvisionlabs.com */ package org.antlr.works.editor.st4.highlighter; import java.util.HashMap; import java.util.IdentityHashMap; import java.util.Map; import org.antlr.v4.runtime.CharStream; import org.antlr.v4.runtime.Lexer; import org.antlr.v4.runtime.Token; import org.antlr.v4.runtime.atn.ATN; import org.antlr.v4.runtime.atn.ATNDeserializer; import org.antlr.v4.runtime.atn.ATNState; import org.antlr.v4.runtime.atn.AtomTransition; import org.antlr.v4.runtime.atn.LexerATNSimulator; import org.antlr.v4.runtime.atn.NotSetTransition; import org.antlr.v4.runtime.atn.RangeTransition; import org.antlr.v4.runtime.atn.RuleTransition; import org.antlr.v4.runtime.atn.SetTransition; import org.antlr.v4.runtime.atn.Transition; import org.antlr.v4.runtime.misc.Interval; import org.antlr.v4.runtime.misc.IntervalSet; import org.antlr.works.editor.st4.highlighter.generated.AbstractGroupHighlighterLexer; /** * * @author Sam Harwell */ public class GroupHighlighterLexer extends AbstractGroupHighlighterLexer { public static final char DEFAULT_OPEN_DELIMITER = '<'; public static final char DEFAULT_CLOSE_DELIMITER = '>'; private static final Map<Integer, ATN> delimiterToATN = new HashMap<>(); private static final int OPEN_DELIMITER_PLACEHOLDER = '\uFFF0'; private static final int CLOSE_DELIMITER_PLACEHOLDER = '\uFFF1'; private final Map<ATN, GroupHighlighterATNSimulator> atnToSimulator = new IdentityHashMap<>(); public GroupHighlighterLexer(CharStream input) { this(input, DEFAULT_OPEN_DELIMITER, DEFAULT_CLOSE_DELIMITER); } @SuppressWarnings("LeakingThisInConstructor") public GroupHighlighterLexer(CharStream input, char openDelimiter, char closeDelimiter) { super(input); _interp = getSimulatorForDelimiters(openDelimiter, closeDelimiter); } @Override public Token emit() { switch (_type) { case LBRACE: if (handleAcceptPositionForKeyword("[")) { pushMode(AnonymousTemplateParameters); } break; case DELIMITERS: if (handleAcceptPositionForKeyword("delimiters")) { pushMode(DelimitersOpenSpec); } break; case FIRST: handleAcceptPositionForKeyword("first"); break; case LAST: handleAcceptPositionForKeyword("last"); break; case REST: handleAcceptPositionForKeyword("rest"); break; case TRUNC: handleAcceptPositionForKeyword("trunc"); break; case STRIP: handleAcceptPositionForKeyword("strip"); break; case TRIM: handleAcceptPositionForKeyword("trim"); break; case LENGTH: handleAcceptPositionForKeyword("length"); break; case STRLEN: handleAcceptPositionForKeyword("strlen"); break; case REVERSE: handleAcceptPositionForKeyword("reverse"); break; case DelimitersOpenSpec_DELIMITER_STRING: setDelimiters(getText().charAt(1), getCloseDelimiter()); _type = STRING; break; case DelimitersCloseSpec_DELIMITER_STRING: setDelimiters(getOpenDelimiter(), getText().charAt(1)); _type = STRING; break; default: break; } return super.emit(); } private boolean handleAcceptPositionForKeyword(String keyword) { if (getInputStream().index() > _tokenStartCharIndex + keyword.length()) { int offset = keyword.length() - 1; getInterpreter().resetAcceptPosition(getInputStream(), _tokenStartCharIndex + offset, _tokenStartLine, _tokenStartCharPositionInLine + offset); return true; } return false; } @Override public GroupHighlighterATNSimulator getInterpreter() { return (GroupHighlighterATNSimulator)super.getInterpreter(); } public char getOpenDelimiter() { return getInterpreter().openDelimiter; } public char getCloseDelimiter() { return getInterpreter().closeDelimiter; } public void setDelimiters(char openDelimiter, char closeDelimiter) { GroupHighlighterATNSimulator interpreter = getInterpreter(); if (interpreter.openDelimiter == openDelimiter && interpreter.closeDelimiter == closeDelimiter) { return; } _interp = getSimulatorForDelimiters(openDelimiter, closeDelimiter); getInterpreter().copyState(interpreter); } @Override protected boolean inStringTemplateMode() { if (_modeStack.size() < 2) { return false; } // index 0 is DEFAULT_MODE, StringTemplate is always 1 inside that return _modeStack.get(1) == StringTemplate; } private GroupHighlighterATNSimulator getSimulatorForDelimiters(char openDelimiter, char closeDelimiter) { ATN atn = getATNForDelimiters(openDelimiter, closeDelimiter); synchronized (atnToSimulator) { GroupHighlighterATNSimulator simulator = atnToSimulator.get(atn); if (simulator == null) { simulator = new GroupHighlighterATNSimulator(this, atn, openDelimiter, closeDelimiter); atnToSimulator.put(atn, simulator); } return simulator; } } private static synchronized ATN getATNForDelimiters(char openDelimiter, char closeDelimiter) { int key = (openDelimiter << 16) + (closeDelimiter & 0xFFFF); ATN atn = delimiterToATN.get(key); if (atn != null) { return atn; } atn = new ATNDeserializer().deserialize(_serializedATN.toCharArray()); for (ATNState state : atn.states) { if (state == null) { continue; } for (int i = 0; i < state.getNumberOfTransitions(); i++) { Transition t = state.transition(i); Transition updated = updateTransition(t, openDelimiter, closeDelimiter); if (updated != null) { state.setTransition(i, updated); } } if (!state.isOptimized()) { continue; } for (int i = 0; i < state.getNumberOfOptimizedTransitions(); i++) { Transition t = state.getOptimizedTransition(i); Transition updated = updateTransition(t, openDelimiter, closeDelimiter); if (updated != null) { state.setOptimizedTransition(i, updated); } } } delimiterToATN.put(key, atn); return atn; } private static Transition updateTransition(Transition t, char openDelimiter, char closeDelimiter) { Transition updated = null; if (t instanceof RuleTransition) { return null; } else if (t instanceof AtomTransition) { AtomTransition atomTransition = (AtomTransition)t; int newLabel; if (atomTransition.label == OPEN_DELIMITER_PLACEHOLDER) { newLabel = openDelimiter; } else if (atomTransition.label == CLOSE_DELIMITER_PLACEHOLDER) { newLabel = closeDelimiter; } else { return null; } updated = new AtomTransition(t.target, newLabel); } else if (t instanceof NotSetTransition) { NotSetTransition notSetTransition = (NotSetTransition)t; int removeLabel; int addLabel; if (notSetTransition.set.contains(OPEN_DELIMITER_PLACEHOLDER)) { removeLabel = OPEN_DELIMITER_PLACEHOLDER; addLabel = openDelimiter; } else if (notSetTransition.set.contains(CLOSE_DELIMITER_PLACEHOLDER)) { removeLabel = CLOSE_DELIMITER_PLACEHOLDER; addLabel = closeDelimiter; } else { return null; } IntervalSet set = new IntervalSet(notSetTransition.set); set.remove(removeLabel); set.add(addLabel); set.setReadonly(true); updated = new NotSetTransition(t.target, set); } else if (t instanceof SetTransition) { SetTransition setTransition = (SetTransition)t; int removeLabel; int addLabel; if (setTransition.set.contains(OPEN_DELIMITER_PLACEHOLDER)) { removeLabel = OPEN_DELIMITER_PLACEHOLDER; addLabel = openDelimiter; } else if (setTransition.set.contains(CLOSE_DELIMITER_PLACEHOLDER)) { removeLabel = CLOSE_DELIMITER_PLACEHOLDER; addLabel = closeDelimiter; } else { return null; } IntervalSet set = new IntervalSet(setTransition.set); set.remove(removeLabel); set.add(addLabel); set.setReadonly(true); updated = createSetTransition(t.target, set); } else if (t instanceof RangeTransition) { RangeTransition rangeTransition = (RangeTransition)t; int removeLabel; int addLabel; if (rangeTransition.from <= OPEN_DELIMITER_PLACEHOLDER && rangeTransition.to >= OPEN_DELIMITER_PLACEHOLDER) { removeLabel = OPEN_DELIMITER_PLACEHOLDER; addLabel = openDelimiter; } else if (rangeTransition.from <= CLOSE_DELIMITER_PLACEHOLDER && rangeTransition.to >= CLOSE_DELIMITER_PLACEHOLDER) { removeLabel = CLOSE_DELIMITER_PLACEHOLDER; addLabel = closeDelimiter; } else { return null; } IntervalSet set = IntervalSet.of(rangeTransition.from, rangeTransition.to); set.remove(removeLabel); set.add(addLabel); set.setReadonly(true); updated = createSetTransition(t.target, set); } return updated; } private static Transition createSetTransition(ATNState target, IntervalSet set) { if (set.getIntervals().size() == 1) { Interval interval = set.getIntervals().get(0); if (interval.a == interval.b) { return new AtomTransition(target, interval.a); } else { return new RangeTransition(target, interval.a, interval.b); } } else { return new SetTransition(target, set); } } protected static class GroupHighlighterATNSimulator extends LexerATNSimulator { private final char openDelimiter; private final char closeDelimiter; public GroupHighlighterATNSimulator(Lexer recog, ATN atn, char openDelimiter, char closeDelimiter) { super(recog, atn); this.openDelimiter = openDelimiter; this.closeDelimiter = closeDelimiter; } protected void resetAcceptPosition(CharStream input, int index, int line, int charPositionInLine) { input.seek(index); this.line = line; this.charPositionInLine = charPositionInLine; consume(input); } } }