Использование 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")]

0 ответов

Другие вопросы по тегам