in Sources/_StringProcessing/Legacy/LegacyCompile.swift [23:272]
func compileNode(_ ast: AST.Node) throws {
if let cc = ast.characterClass {
instructions.append(.characterClass(cc))
return
}
switch ast {
case .trivia, .empty: return
case .quote(let s):
s.literal.forEach { instructions.append(.character($0)) }
return
case .atom(let a):
switch a.kind {
case .char(let c):
instructions.append(.character(c))
return
case .scalar(let u):
instructions.append(.unicodeScalar(u))
return
case .any:
instructions.append(.any)
return
default:
throw unsupported("Unsupported: \(a._dumpBase)")
}
case .group(let g):
switch g.kind.value {
case .nonCapture:
instructions.append(.beginGroup)
try compileNode(g.child)
instructions.append(.endGroup)
return
case .capture:
instructions.append(.beginCapture)
try compileNode(g.child)
instructions.append(.endCapture())
return
default:
throw unsupported("Unsupported group \(g.kind.value) \(g)")
}
case let .groupTransform(g, transform: t) where g.kind.value == .capture:
instructions.append(.beginCapture)
try compileNode(g.child)
instructions.append(.endCapture(transform: t))
return
case .groupTransform(let g, _):
throw unsupported("Unsupported group \(g)")
case .concatenation(let concat):
let children = concat.children
let childrenHaveCaptures = children.any(\.hasCapture)
if childrenHaveCaptures {
instructions.append(.beginGroup)
}
try children.forEach { try compileNode($0) }
if childrenHaveCaptures {
instructions.append(.endGroup)
}
return
case .quantification(let quant):
let child = quant.child
switch (quant.amount.value, quant.kind.value) {
case (.zeroOrMore, .eager):
// a* ==> L_START, <split L_DONE>, a, goto L_START, L_DONE
let childHasCaptures = child.hasCapture
if childHasCaptures {
instructions.append(.beginGroup)
}
let start = createLabel()
instructions.append(start)
let done = createLabel()
instructions.append(.split(disfavoring: done.label!))
try compileNode(child)
instructions.append(.goto(label: start.label!))
instructions.append(done)
if childHasCaptures {
instructions.append(.captureArray(childType: child.captureStructure.type))
instructions.append(.endGroup)
}
return
case (.zeroOrMore, .reluctant):
// a*? ==> L_START, <split L_ELEMENT>, goto L_DONE,
// L_ELEMENT, a, goto L_START, L_DONE
let childHasCaptures = child.hasCapture
if childHasCaptures {
instructions.append(.beginGroup)
}
let start = createLabel()
let element = createLabel()
let done = createLabel()
instructions.append(start)
instructions.append(.split(disfavoring: element.label!))
instructions.append(.goto(label: done.label!))
instructions.append(element)
try compileNode(child)
instructions.append(.goto(label: start.label!))
instructions.append(done)
if childHasCaptures {
instructions.append(.captureArray(childType: child.captureStructure.type))
instructions.append(.endGroup)
}
return
case (.zeroOrOne, .eager):
// a? ==> <split L_DONE> a, L_DONE
if child.hasCapture {
instructions.append(.beginGroup)
let nilCase = createLabel()
let done = createLabel()
instructions.append(.split(disfavoring: nilCase.label!))
try compileNode(child)
instructions += [
.captureSome,
.goto(label: done.label!),
nilCase,
.captureNil(childType: child.captureStructure.type),
done,
.endGroup
]
} else {
let done = createLabel()
instructions.append(.split(disfavoring: done.label!))
try compileNode(child)
instructions.append(done)
}
return
case (.zeroOrOne, .reluctant):
// a?? ==> <split L_ELEMENT>, goto L_DONE, L_ELEMENT, a, L_DONE
if child.hasCapture {
instructions.append(.beginGroup)
let element = createLabel()
let nilCase = createLabel()
let done = createLabel()
instructions.append(.split(disfavoring: element.label!))
instructions.append(.goto(label: nilCase.label!))
instructions.append(element)
try compileNode(child)
instructions += [
.captureSome,
.goto(label: done.label!),
nilCase,
.captureNil(childType: child.captureStructure.type),
done,
.endGroup
]
} else {
let element = createLabel()
let done = createLabel()
instructions.append(.split(disfavoring: element.label!))
instructions.append(.goto(label: done.label!))
instructions.append(element)
try compileNode(child)
instructions.append(done)
}
return
case (.oneOrMore, .eager):
// a+ ==> L_START, a, <split L_DONE>, goto L_START, L_DONE
let childHasCaptures = child.hasCapture
if childHasCaptures {
instructions.append(.beginGroup)
}
let start = createLabel()
let done = createLabel()
instructions.append(start)
try compileNode(child)
instructions.append(.split(disfavoring: done.label!))
instructions.append(.goto(label: start.label!))
instructions.append(done)
if childHasCaptures {
instructions.append(.captureArray(childType: child.captureStructure.type))
instructions.append(.endGroup)
}
return
case (.oneOrMore, .reluctant):
// a+? ==> L_START, a, <split L_START>
let childHasCaptures = child.hasCapture
if childHasCaptures {
instructions.append(.beginGroup)
}
let start = createLabel()
instructions.append(start)
try compileNode(child)
instructions.append(.split(disfavoring: start.label!))
if childHasCaptures {
instructions.append(.captureArray(childType: child.captureStructure.type))
instructions.append(.endGroup)
}
return
default:
throw unsupported("Unsupported: \(quant._dumpBase)")
}
case .alternation(let alt):
// a|b ==> <split L_B>, a, goto L_DONE, L_B, b, L_DONE
// a|b|c ==> <split L_B>, a, goto L_DONE,
// L_B, <split L_C>, b, goto L_DONE, L_C, c, L_DONE
// a|b|c|... ==> <split L_B>, a, goto L_DONE,
// L_B, <split L_C>, b, goto L_DONE,
// L_C, <split L_...>, c, goto L_DONE, ...,
// L_DONE
//
// NOTE: Can be optimized for more efficient layout.
// E.g. `a` falls-through to the rest of the program and the
// other cases branch back.
//
let children = alt.children
assert(!children.isEmpty)
guard children.count > 1 else {
return try compileNode(children[0])
}
let last = children.last!
let middle = children.dropLast()
let done = createLabel()
for child in middle {
let nextLabel = createLabel()
instructions.append(.split(disfavoring: nextLabel.label!))
try compileNode(child)
instructions.append(.goto(label: done.label!))
instructions.append(nextLabel)
}
try compileNode(last)
instructions.append(done)
return
case .conditional:
throw unsupported(ast.renderAsCanonical())
case .absentFunction:
throw unsupported(ast.renderAsCanonical())
case .customCharacterClass:
fatalError("unreachable")
case .atom(let a) where a.characterClass != nil:
fatalError("unreachable")
}
}