-
Notifications
You must be signed in to change notification settings - Fork 82
Closed
Description
This describes and discusses the issue: swiftlang/swift#62978
It seems a design choice was made to make resultBuilders stricter in how they compile causing some of the behaviour swift-parsing was relying on to no longer be valid.
I've made the example provided in the discussion work for the new toolchain (2023-01-09) by making the ParserBuilder
generic over the accepted Input
, see solution below.
I've tried but haven't succeeded in creating a fix for the library as a whole however. Would love to hear your thoughts on this cause I'm a little stuck at the moment unfortunately.
struct ParsingError: Error { }
//MARK: Parsers
@rethrows public protocol Parser<Input, Output> {
associatedtype Input
associatedtype Output
func parse(input: inout Input) throws -> Output
}
extension String: Parser {
public typealias Input = Substring
public func parse(input: inout Substring) throws {
guard input.starts(with: self) else {
throw ParsingError()
}
input.removeFirst(self.count)
}
}
extension Int {
static func parser(
of inputType: Substring.Type = Substring.self
) -> FromSubstringToUTF8<IntParser<Substring.UTF8View>> {
FromSubstringToUTF8 { IntParser<Substring.UTF8View>() }
}
static func parser(
of inputType: Substring.UTF8View.Type = Substring.UTF8View.self
) -> IntParser<Substring.UTF8View> {
.init()
}
}
struct FromSubstringToUTF8<P: Parser>: Parser where P.Input == Substring.UTF8View {
typealias Input = Substring
let parser: P
init(_ parser: P) {
self.parser = parser
}
init(@ParserBuilder<P.Input> _ build: () -> P) { self.parser = build() }
func parse(input: inout Substring) throws -> P.Output {
var transformedInput = input.utf8
let result = try parser.parse(input: &transformedInput)
input = Substring(transformedInput)
return result
}
}
struct IntParser<Input: Collection>: Parser where Input.SubSequence == Input, Input.Element == UTF8.CodeUnit {
public init() { }
func parse(input: inout Input) throws -> Int {
// some int parsing logic
return 4
}
}
//MARK: ParserBuilder
struct Parse<Input, Parsers: Parser>: Parser where Input == Parsers.Input {
let parsers: Parsers
init(@ParserBuilder<Parsers.Input> with build: () -> Parsers) { self.parsers = build() }
func parse(input: inout Parsers.Input) throws -> Parsers.Output {
try parsers.parse(input: &input)
}
}
@resultBuilder
struct ParserBuilder<Input> {
public static func buildPartialBlock<P: Parser>(first: P) -> P where P.Input == Input { first }
public static func buildPartialBlock<P0, P1>(accumulated: P0, next: P1) -> SkipFirst<P0, P1> where P0.Input == Input, P1.Input == Input {
.init(p0: accumulated, p1: next)
}
public static func buildExpression<P: Parser>(_ expression: P) -> P where P.Input == Input {
expression
}
}
struct SkipFirst<P0: Parser, P1: Parser>: Parser where P0.Input == P1.Input {
let p0: P0
let p1: P1
func parse(input: inout P0.Input) throws -> P1.Output {
let _ = try p0.parse(input: &input)
return try p1.parse(input: &input)
}
}
//MARK: main
let parser = Parse {
","
Int.parser()
}
var input = ",4"[...]
let result = try parser.parse(input: &input)
print(result)
bjschoenfeld
Metadata
Metadata
Assignees
Labels
No labels