in Sources/_StringProcessing/ConsumerInterface.swift [124:211]
func generateConsumer(
_ opts: MatchingOptions
) throws -> Program<String>.ConsumeFunction {
switch self {
case .custom(let ccc):
return try ccc.generateConsumer(opts)
case .range(let r):
guard let lhs = r.lhs.literalCharacterValue else {
throw unsupported("\(r.lhs) in range")
}
guard let rhs = r.rhs.literalCharacterValue else {
throw unsupported("\(r.rhs) in range")
}
return { input, bounds in
// TODO: check for out of bounds?
let curIdx = bounds.lowerBound
if (lhs...rhs).contains(input[curIdx]) {
// TODO: semantic level
return input.index(after: curIdx)
}
return nil
}
case .atom(let atom):
guard let gen = try atom.generateConsumer(opts) else {
throw unsupported("TODO")
}
return gen
case .quote(let q):
// TODO: Not optimal.
let consumers = try q.literal.map {
try AST.Atom(.char($0), .fake).generateConsumer(opts)!
}
return { input, bounds in
for consumer in consumers {
if let idx = consumer(input, bounds) {
return idx
}
}
return nil
}
case .setOperation(let lhs, let op, let rhs):
// TODO: We should probably have a component type
// instead of a members array... for now we reconstruct
// an AST node...
let start = AST.Located(
faking: AST.CustomCharacterClass.Start.normal)
let lhs = try AST.CustomCharacterClass(
start, lhs, .fake
).generateConsumer(opts)
let rhs = try AST.CustomCharacterClass(
start, rhs, .fake
).generateConsumer(opts)
return { input, bounds in
// NOTE: Easy way to implement, not performant
let lhsIdxOpt = lhs(input, bounds)
let rhsIdxOpt = rhs(input, bounds)
// TODO: What if lengths don't line up?
assert(lhsIdxOpt == rhsIdxOpt || lhsIdxOpt == nil
|| rhsIdxOpt == nil)
switch op.value {
case .subtraction:
guard rhsIdxOpt == nil else { return nil }
return lhsIdxOpt
case .intersection:
if let idx = lhsIdxOpt {
return rhsIdxOpt == nil ? nil : idx
}
return nil
case .symmetricDifference:
if let idx = lhsIdxOpt {
return rhsIdxOpt == nil ? idx : nil
}
return rhsIdxOpt
}
}
}
}