in Sources/_StringProcessing/Compiler.swift [38:161]
func emit(_ node: AST.Node) throws {
switch node {
case .atom(let a) where a.kind == .any:
try emitAny()
// Single characters we just match
case .atom(let a) where a.singleCharacter != nil :
builder.buildMatch(a.singleCharacter!)
// Alternation: p0 | p1 | ... | pn
// save next_p1
// <code for p0>
// branch done
// next_p1:
// save next_p2
// <code for p1>
// branch done
// next_p2:
// save next_p...
// <code for p2>
// branch done
// ...
// next_pn:
// <code for pn>
// done:
case .alternation(let alt):
let done = builder.makeAddress()
for component in alt.children.dropLast() {
let next = builder.makeAddress()
builder.buildSave(next)
try emit(component)
builder.buildBranch(to: done)
builder.label(next)
}
try emit(alt.children.last!)
builder.label(done)
case .conditional:
throw unsupported(node.renderAsCanonical())
// FIXME: Wait, how does this work?
case .groupTransform(let g, _):
try emit(g.child)
case .concatenation(let concat):
try concat.children.forEach(emit)
case .trivia, .empty:
break
case .absentFunction:
throw unsupported(node.renderAsCanonical())
case .group(let g):
options.beginScope()
defer { options.endScope() }
if let lookaround = g.lookaroundKind {
try emitLookaround(lookaround, g.child)
return
}
switch g.kind.value {
case .lookahead, .negativeLookahead,
.lookbehind, .negativeLookbehind:
fatalError("unreachable")
case .capture, .namedCapture:
let cap = builder.makeCapture()
builder.buildBeginCapture(cap)
try emit(g.child)
builder.buildEndCapture(cap)
case .changeMatchingOptions(let optionSequence, _):
options.apply(optionSequence)
try emit(g.child)
default:
// FIXME: Other kinds...
try emit(g.child)
}
case .quantification(let quant):
try emitQuantification(quant)
// For now, we model sets and atoms as consumers.
// This lets us rapidly expand support, and we can better
// design the actual instruction set with real examples
case _ where try node.generateConsumer(options) != nil:
try builder.buildConsume(by: node.generateConsumer(options)!)
case .quote(let q):
// We stick quoted content into read-only constant strings
builder.buildMatchSequence(q.literal)
case .atom(let a) where a.assertionKind != nil:
try emitAssertion(a.assertionKind!)
case .atom(let a):
switch a.kind {
case .backreference(let r):
if r.recursesWholePattern {
// TODO: A recursive call isn't a backreference, but
// we could in theory match the whole match so far...
throw unsupported(node.renderAsCanonical())
}
switch r.kind {
case .absolute(let i):
// Backreferences number starting at 1
builder.buildBackreference(.init(i-1))
case .relative, .named:
throw unsupported(node.renderAsCanonical())
}
default:
throw unsupported(node.renderAsCanonical())
}
case .customCharacterClass:
throw unsupported(node.renderAsCanonical())
}
}