Skip to content

Instantly share code, notes, and snippets.

@DylanLukes
Last active November 7, 2017 22:21
Show Gist options
  • Save DylanLukes/aa5790554c3b4b453b8741d009838c85 to your computer and use it in GitHub Desktop.
Save DylanLukes/aa5790554c3b4b453b8741d009838c85 to your computer and use it in GitHub Desktop.
An implementation of PositionAdjustingLexer (for the Swift runtime) which does not require inline @members definitions.
lexer grammar Example;
options {
language=Swift;
superClass=MyBaseLexer;
}
@header {
// All that is needed here is some very simple conformance boilerplate.
// No inlined method definitions, no editing Swift source in a .g4 file.
extension Example : GeneratedLexer {
public var generated_ATN: ATN { return Example._ATN }
public var generated_decisionToDFA: [DFA] { return Example._decisionToDFA }
public var generated_sharedContextCache: PredictionContextCache { return Example._sharedContextCache }
}
}
// ... your lexer grammar goes here ...
tokens {
UNQUOTED_STRING
}
KEYWORD : 'foo' | 'bar' | 'baz' ;
WS : [ \t\r\n] ;
// KEYWORD will always take precedence over this rule, as it only matches 1 character.
UnquotedString_Start: UNQUOTED_STRING_LEAD_CHAR -> more, pushMode(UnquotedString);
fragment UNQUOTED_STRING_LEAD_CHAR : [a-zA-Z] ;
fragment UNQUOTED_STRING_CHAR : UNQUOTED_STRING_LEAD_CHAR | [0-9] ;
mode UnquotedString;
// Match terminating whitespace, but don't consume it.
UnquotedString_End : WS { adjust(by: -1) } -> type(UNQUOTED_STRING), popMode ;
UnquotedString_Char : UNQUOTED_STRING_CHAR -> more ;
import Foundation
import Antlr4
open class MyBaseLexer : PositionAdjustingLexer {
/**
* Adjusts the accept position, accepts negative numbers.
*/
func adjustAcceptPosition(by: Int) -> Bool {
let interp = (getInterpreter() as! PositionAdjustingLexerATNSimulator)
let (start, stop) = (_tokenStartCharIndex, getCharIndex())
let textLength = stop - start
let lastMatchLength = getText().utf8.count
if (getInputStream()!.index() > start + textLength - lastMatchLength) {
let offset = textLength + by - 1
interp.resetAcceptPosition(
getInputStream()!,
_tokenStartCharIndex + offset,
_tokenStartLine,
_tokenStartCharPositionInLine + offset)
return true
}
return false
}
/**
* Shortcut for ease of writing in a lexer.
*/
func adjust(by offset: Int) -> Void {
let _ = adjustAcceptPosition(by: offset)
}
}
//
// LexerUtils.swift
// CIFKit
//
// Created by Dylan Lukes on 11/7/17.
// Copyright © 2017 Dylan Lukes. All rights reserved.
//
import Foundation
import Antlr4
/**
* Defines properties of generated lexers which are not present
* in Lexer, so that they be referenced before the concrete subtype
* is known.
*/
public protocol GeneratedLexer {
var generated_ATN: ATN { get }
var generated_decisionToDFA: [DFA] { get}
var generated_sharedContextCache: PredictionContextCache { get }
}
extension GeneratedLexer {
public var generated_ATN: ATN {
fatalError("generated_ATN is abstract.")
}
public var generated_decisionToDFA: [DFA] {
fatalError("generated_decisionToDFA is abstract.")
}
public var generated_sharedContextCache: PredictionContextCache {
fatalError("generated_sharedContextCache is abstract.")
}
}
/**
* Base class for position adjusting lexers. Note that CIF2Lexer _must_ provide
* a conformance for GeneratedLexer. (This would normally be expressed
* throw a constrained type parameter, but cannot because Antlr syntax does
* not accept a generic instantiation (Foo<T>) as a superClass, (< >).
*
* This unpleasantry is accepted as it allows us to only write the
* minimum required conformance in @header, without implementing there.
*/
open class PositionAdjustingLexer : Lexer {
override open func nextToken() throws -> Token {
if (!(_interp is PositionAdjustingLexerATNSimulator)) {
_interp = PositionAdjustingLexerATNSimulator(
self,
(self as! GeneratedLexer).generated_ATN,
(self as! GeneratedLexer).generated_decisionToDFA,
(self as! GeneratedLexer).generated_sharedContextCache)
}
return try super.nextToken()
}
}
open class PositionAdjustingLexerATNSimulator: LexerATNSimulator {
init(_ recog: Lexer,_ atn: ATN,
_ decisionToDFA: [DFA],
_ sharedContextCache:PredictionContextCache)
{
super.init(recog, atn, decisionToDFA, sharedContextCache)
}
func resetAcceptPosition(_ input: CharStream,_ index: Int,_ line: Int,_ charPositionInLine: Int) {
try! input.seek(index)
self.line = line
self.charPositionInLine = charPositionInLine
try! consume(input)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment