/* * 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.tvl.goworks.editor.go.parser; import org.antlr.v4.runtime.CharStream; import org.antlr.v4.runtime.Token; import org.antlr.v4.runtime.atn.ATNState; import org.antlr.v4.runtime.atn.SetTransition; import org.antlr.v4.runtime.atn.Transition; import org.antlr.v4.runtime.misc.IntervalSet; import org.tvl.goworks.editor.go.parser.generated.AbstractGoLexer; /** * * @author Sam Harwell */ public class GoLexer extends AbstractGoLexer { private boolean insertSemicolonAtEol; private Token deferredEol; private static final char unicodeDigitPlaceholder = '\u0660'; private static final char unicodeLetterPlaceholder = '\u0101'; static { IntervalSet digitChars = new IntervalSet(); IntervalSet letterChars = new IntervalSet(); for (char c = 0; c < Character.MAX_VALUE; c++) { if (Character.isDigit(c)) { digitChars.add(c); } else if (Character.isLetter(c)) { letterChars.add(c); } } for (ATNState state : _ATN.states) { if (state == null || state.onlyHasEpsilonTransitions()) { continue; } for (int i = 0; i < state.getNumberOfTransitions(); i++) { Transition updated = patchTransition(state.transition(i), digitChars, letterChars); if (updated != null) { state.setTransition(i, updated); } } if (!state.isOptimized()) { continue; } for (int i = 0; i < state.getNumberOfOptimizedTransitions(); i++) { Transition updated = patchTransition(state.getOptimizedTransition(i), digitChars, letterChars); if (updated != null) { state.setOptimizedTransition(i, updated); } } } } private static Transition patchTransition(Transition transition, IntervalSet digitChars, IntervalSet letterChars) { switch (transition.getSerializationType()) { case Transition.ATOM: case Transition.RANGE: case Transition.SET: break; default: return null; } IntervalSet label = transition.label(); if (label == null) { return null; } IntervalSet updated = null; if (label.contains(unicodeDigitPlaceholder)) { updated = new IntervalSet(label); updated.addAll(digitChars); if (updated.size() == digitChars.size()) { updated = digitChars; } } if (label.contains(unicodeLetterPlaceholder)) { if (updated != null) { updated.addAll(label); } else { updated = new IntervalSet(label); } updated.addAll(letterChars); if (updated.size() == letterChars.size()) { updated = letterChars; } } if (updated == null) { return null; } return new SetTransition(transition.target, updated); } public GoLexer(CharStream input) { super(input); } @Override public void reset() { insertSemicolonAtEol = false; deferredEol = null; super.reset(); } @Override public Token nextToken() { Token result; if (deferredEol != null) { result = deferredEol; deferredEol = null; } else { result = super.nextToken(); } switch (result.getType()) { case IDENTIFIER: case INT_LITERAL: case FLOAT_LITERAL: case IMAGINARY_LITERAL: case CharLiteral: case StringLiteral: case Break: case Continue: case Fallthrough: case Return: case Inc: case Dec: case RightParen: case RightBrack: case RightBrace: insertSemicolonAtEol = true; break; default: if (result.getChannel() == Token.DEFAULT_CHANNEL) { insertSemicolonAtEol = false; } break; } return result; } @Override public Token emit() { Token semicolonToken = null; if (_type == NEWLINE || _type == COMMENT) { if (insertSemicolonAtEol) { // emit the virtual semicolon token first semicolonToken = _factory.create(_tokenFactorySourcePair, Semi, ";", DEFAULT_TOKEN_CHANNEL, _tokenStartCharIndex, _tokenStartCharIndex - 1, _tokenStartLine, _tokenStartCharPositionInLine); emit(semicolonToken); } // reset for the next line insertSemicolonAtEol = false; } Token result = super.emit(); if (semicolonToken != null) { deferredEol = result; _token = semicolonToken; // Lexer.nextToken ignores the return value from emit() return semicolonToken; } return result; } @Override public Token emitEOF() { Token semicolonToken = null; if (insertSemicolonAtEol) { // emit the virtual semicolon token first semicolonToken = _factory.create(_tokenFactorySourcePair, Semi, ";", DEFAULT_TOKEN_CHANNEL, _tokenStartCharIndex, _tokenStartCharIndex - 1, _tokenStartLine, _tokenStartCharPositionInLine); emit(semicolonToken); // don't want to emit it twice insertSemicolonAtEol = false; } Token result = super.emitEOF(); if (semicolonToken != null) { deferredEol = result; return semicolonToken; } return result; } }