@@ -149,6 +149,14 @@ extension Source {
149
149
return result
150
150
}
151
151
152
+ /// Perform a lookahead using a temporary source. Within the body of the
153
+ /// lookahead, any modifications to the source will not be reflected outside
154
+ /// the body.
155
+ func lookahead< T> ( _ body: ( inout Source ) throws -> T ) rethrows -> T {
156
+ var src = self
157
+ return try body ( & src)
158
+ }
159
+
152
160
/// Attempt to eat the given character, returning its source location if
153
161
/// successful, `nil` otherwise.
154
162
mutating func tryEatWithLoc( _ c: Character ) -> SourceLocation ? {
@@ -1240,8 +1248,9 @@ extension Source {
1240
1248
1241
1249
private func canLexPOSIXCharacterProperty( ) -> Bool {
1242
1250
do {
1243
- var src = self
1244
- return try src. lexPOSIXCharacterProperty ( ) != nil
1251
+ return try lookahead { src in
1252
+ try src. lexPOSIXCharacterProperty ( ) != nil
1253
+ }
1245
1254
} catch {
1246
1255
// We want to tend on the side of lexing a POSIX character property, so
1247
1256
// even if it is invalid in some way (e.g invalid property names), still
@@ -1394,10 +1403,11 @@ extension Source {
1394
1403
1395
1404
/// Checks whether a numbered reference can be lexed.
1396
1405
private func canLexNumberedReference( ) -> Bool {
1397
- var src = self
1398
- _ = src. tryEat ( anyOf: " + " , " - " )
1399
- guard let next = src. peek ( ) else { return false }
1400
- return RadixKind . decimal. characterFilter ( next)
1406
+ lookahead { src in
1407
+ _ = src. tryEat ( anyOf: " + " , " - " )
1408
+ guard let next = src. peek ( ) else { return false }
1409
+ return RadixKind . decimal. characterFilter ( next)
1410
+ }
1401
1411
}
1402
1412
1403
1413
/// Eat a named reference up to a given closing delimiter.
@@ -1587,53 +1597,55 @@ extension Source {
1587
1597
1588
1598
/// Whether we can lex a group-like reference after the specifier '(?'.
1589
1599
private func canLexGroupLikeReference( ) -> Bool {
1590
- var src = self
1591
- if src. tryEat ( " P " ) {
1592
- return src. tryEat ( anyOf: " = " , " > " ) != nil
1593
- }
1594
- if src. tryEat ( anyOf: " & " , " R " ) != nil {
1595
- return true
1600
+ lookahead { src in
1601
+ if src. tryEat ( " P " ) {
1602
+ return src. tryEat ( anyOf: " = " , " > " ) != nil
1603
+ }
1604
+ if src. tryEat ( anyOf: " & " , " R " ) != nil {
1605
+ return true
1606
+ }
1607
+ return src. canLexNumberedReference ( )
1596
1608
}
1597
- return src. canLexNumberedReference ( )
1598
1609
}
1599
1610
1600
1611
private func canLexMatchingOptionsAsAtom( context: ParsingContext ) -> Bool {
1601
- var src = self
1602
-
1603
- // See if we can lex a matching option sequence that terminates in ')'. Such
1604
- // a sequence is an atom. If an error is thrown, there are invalid elements
1605
- // of the matching option sequence. In such a case, we can lex as a group
1606
- // and diagnose the invalid group kind.
1607
- guard ( try ? src. lexMatchingOptionSequence ( context: context) ) != nil else {
1608
- return false
1612
+ lookahead { src in
1613
+ // See if we can lex a matching option sequence that terminates in ')'.
1614
+ // Such a sequence is an atom. If an error is thrown, there are invalid
1615
+ // elements of the matching option sequence. In such a case, we can lex as
1616
+ // a group and diagnose the invalid group kind.
1617
+ guard ( try ? src. lexMatchingOptionSequence ( context: context) ) != nil else {
1618
+ return false
1619
+ }
1620
+ return src. tryEat ( " ) " )
1609
1621
}
1610
- return src. tryEat ( " ) " )
1611
1622
}
1612
1623
1613
1624
/// Whether a group specifier should be lexed as an atom instead of a group.
1614
1625
private func shouldLexGroupLikeAtom( context: ParsingContext ) -> Bool {
1615
- var src = self
1616
- guard src. tryEat ( " ( " ) else { return false }
1626
+ lookahead { src in
1627
+ guard src. tryEat ( " ( " ) else { return false }
1617
1628
1618
- if src. tryEat ( " ? " ) {
1619
- // The start of a reference '(?P=', '(?R', ...
1620
- if src. canLexGroupLikeReference ( ) { return true }
1629
+ if src. tryEat ( " ? " ) {
1630
+ // The start of a reference '(?P=', '(?R', ...
1631
+ if src. canLexGroupLikeReference ( ) { return true }
1621
1632
1622
- // The start of a PCRE callout.
1623
- if src. tryEat ( " C " ) { return true }
1633
+ // The start of a PCRE callout.
1634
+ if src. tryEat ( " C " ) { return true }
1624
1635
1625
- // The start of an Oniguruma 'of-contents' callout.
1626
- if src. tryEat ( " { " ) { return true }
1636
+ // The start of an Oniguruma 'of-contents' callout.
1637
+ if src. tryEat ( " { " ) { return true }
1627
1638
1628
- // A matching option atom (?x), (?i), ...
1629
- if src. canLexMatchingOptionsAsAtom ( context: context) { return true }
1639
+ // A matching option atom (?x), (?i), ...
1640
+ if src. canLexMatchingOptionsAsAtom ( context: context) { return true }
1641
+
1642
+ return false
1643
+ }
1644
+ // The start of a backreference directive or Oniguruma named callout.
1645
+ if src. tryEat ( " * " ) { return true }
1630
1646
1631
1647
return false
1632
1648
}
1633
- // The start of a backreference directive or Oniguruma named callout.
1634
- if src. tryEat ( " * " ) { return true }
1635
-
1636
- return false
1637
1649
}
1638
1650
1639
1651
/// Consume an escaped atom, starting from after the backslash
0 commit comments