swiftlang / swift-experimental-string-processing

An early experimental general-purpose pattern matching engine for Swift.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Match.subscript fails to properly cast non-matched Capture

jasonbobier opened this issue · comments

This code should produce an Optional<Int>. Instead it produces an Optional<Substring> and crashes.

import RegexBuilder

let ref = Reference(Int.self)

let regex = Regex {
	"test"
	Optionally {
		TryCapture(as: ref) {
			OneOrMore(.digit)
		} transform: {
			Int($0)
		}
	}
	"a"
}

let match = "testa".wholeMatch(of: regex)

print("Int \(match![ref])")

This aborts with:

Could not cast value of type 'Swift.Optional<Swift.Substring>' (0x1f2db92c0) to 'Swift.Int' (0x1f3da0bf0).

There a few things to notice here:

  • By capture of Int being Optionally that means that an Int for the capture may not happen at all, rendering the result nil so I think what you want is a reference for an optional Int?e.g. let ref = Reference(Int?.self) which makes total sense meaning for "testa" for example no digit(which is optional) was captured meaning nil.
  • Perhaps what you want instead of Optionally with TryCapture/OneOrMore is simply TryCapture/ZeroOrMore

With that said, I don't think there is a bug here(perhaps a better runtime error message), but I'm don't know much about regex feature so I'll let @hamishknight, @natecook1000, @Azoy take a look.

Nevermind... it works... I had tried:

let ref = Reference(Optional(Int).self)

and it didn't work.

Notice the typo... Sorry for the false bug. Grrrr...

@LucianoPAlmeida but it won't compile for Substring. For example this fails to compile:

import RegexBuilder

let ref = Reference(Substring?.self)

let regex = Regex {
	"test"
	Optionally {
		Capture(as: ref) {
			OneOrMore(.digit)
		}
	}
	"a"
}

let match = "testa".wholeMatch(of: regex)

print("Substring \(match![ref])")

with an error of:

error: initializer 'init(as:_:)' requires the types 'Substring?' and 'Regex<OneOrMore<Substring>.RegexOutput>.RegexOutput' (aka 'Substring') be equivalent

and if I change the ref to:

let ref = Reference(Substring.self)

I get a runtime casting error:

Could not cast value of type 'Swift.Optional<Swift.Substring>' (0x1f2db10e0) to 'Swift.Substring' (0x1f3d9f530).

@LucianoPAlmeida but it won't compile for Substring. For example this fails to compile:

import RegexBuilder

let ref = Reference(Substring?.self)

let regex = Regex {
	"test"
	Optionally {
		Capture(as: ref) {
			OneOrMore(.digit)
		}
	}
	"a"
}

let match = "testa".wholeMatch(of: regex)

print("Substring \(match![ref])")

with an error of:

error: initializer 'init(as:_:)' requires the types 'Substring?' and 'Regex<OneOrMore<Substring>.RegexOutput>.RegexOutput' (aka 'Substring') be equivalent

and if I change the ref to:

let ref = Reference(Substring.self)

I get a runtime casting error:

Could not cast value of type 'Swift.Optional<Swift.Substring>' (0x1f2db10e0) to 'Swift.Substring' (0x1f3d9f530).

Have to look in more depth into it but initially applying a transform hack makes it compile.

import RegexBuilder

let ref = Reference(Substring?.self)

let regex = Regex {
  "test"
  Optionally {
    Capture(as: ref) {
      OneOrMore(.digit)
    } transform: { $0 }
  }
  "a"
}

let match = "testa".wholeMatch(of: regex)

print("Substring \(match![ref])")

Looks like a type checker problem on matching Substring and Substring?.

Looks like a similar issue to #623, cc @milseman

This still fails on Xcode Version 15.0 beta (15A5160n).

This still fails with Xcode 15 final.