Использование Swift RegexBuilder в лексере – свертывание выходного кортежа соответствия ChoiceOf типу (SubString, TypeOfMatch)
Я реализую парсер, используя SwiftRegexBuilder
для стадии лексера. Я хотел бы понять, можно ли улучшить мое выражение регулярного выражения.
В частности, в примере кода ниже, когдаtokenRegex
соответствует, он создает кортеж типа(SubString, Token?, Token?, ...)
, где количествоToken?
члены будут равны количеству членов регулярного выражения в конструкции. Предполагая, что все члены регулярного выражения вChoiceOf
тип возвращаемого блокаToken
, можно ли как-нибудь выразить это так, чтобы тип возвращаемого значения был(SubString, Token?)
.
В настоящее время я решаю эту проблему, сворачивая выходной кортеж соответствия с помощью функции зеркала, но хотел бы пропустить этот шаг, если это возможно:
let token = Mirror(reflecting: match.output).children.compactMap({ $0.value as? Token }).first
Вот полный пример кода.
import Foundation
import RegexBuilder
enum Token {
case number(Double)
case text(String)
case error(String)
static let tokenRegex = Regex {
ChoiceOf {
numberRegex
textRegex
// Further regex patterns, all returning type Token...
}
}
static let numberRegex = Regex {
Capture {
.localizedDouble(locale: Locale(identifier: "en-US"))
} transform: {
Token.number($0)
}}
static let textRegex = Regex {
Capture {
OneOrMore(.word)
} transform: {
Token.text(String($0))
}}
static func tokenise(_ text: String) -> [Token] {
var stringToParse = text
var tokens: [Token] = []
while !stringToParse.isEmpty {
let (token, matchEndIndex) = findNextToken(in: stringToParse)
tokens.append(token)
stringToParse = String(stringToParse[matchEndIndex...])
}
return tokens
}
static func findNextToken(in string: String) -> (Token, String.Index) {
do {
return if
let match = try tokenRegex.firstMatch(in: string),
let token = Mirror(reflecting: match.output).children.compactMap({ $0.value as? Token }).first
{
(token, match.0.endIndex)
} else {
(.error("Parse error"), string.endIndex)
}
} catch {
return (.error(error.localizedDescription), string.endIndex)
}
}
}
Token.tokenise("123MyDogIsHappy") // Produces -> [Token.double(123.0), Token.text("MyDogIsHappy")]