Last active
November 7, 2017 22:21
-
-
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 ; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// 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