@@ -589,6 +589,26 @@ extension Source {
589589 return AST . Quote ( str. value, str. location)
590590 }
591591
592+ /// Try to consume an interpolation sequence.
593+ ///
594+ /// Interpolation -> '<{' String '}>'
595+ ///
596+ mutating func lexInterpolation( ) throws -> AST . Interpolation ? {
597+ let contents = try recordLoc { src -> String ? in
598+ try src. tryEating { src in
599+ guard src. tryEat ( sequence: " <{ " ) else { return nil }
600+ _ = src. lexUntil { $0. isEmpty || $0. starts ( with: " }> " ) }
601+ guard src. tryEat ( sequence: " }> " ) else { return nil }
602+
603+ // Not currently supported. We error here instead of during Sema to
604+ // get a better error for something like `(<{)}>`.
605+ throw ParseError . unsupported ( " interpolation " )
606+ }
607+ }
608+ guard let contents = contents else { return nil }
609+ return . init( contents. value, contents. location)
610+ }
611+
592612 /// Try to consume a comment
593613 ///
594614 /// Comment -> '(?#' [^')']* ')'
@@ -1674,9 +1694,10 @@ extension Source {
16741694 break
16751695 }
16761696
1677- // We only allow unknown escape sequences for non-letter ASCII, and
1678- // non-ASCII whitespace.
1679- guard ( char. isASCII && !char. isLetter) ||
1697+ // We only allow unknown escape sequences for non-letter non-number ASCII,
1698+ // and non-ASCII whitespace.
1699+ // TODO: Once we have fix-its, suggest a `0` prefix for octal `[\7]`.
1700+ guard ( char. isASCII && !char. isLetter && !char. isNumber) ||
16801701 ( !char. isASCII && char. isWhitespace)
16811702 else {
16821703 throw ParseError . invalidEscape ( char)
@@ -1981,10 +2002,21 @@ extension Source {
19812002
19822003 case " ] " :
19832004 assert ( !customCC, " parser should have prevented this " )
1984- fallthrough
2005+ break
19852006
1986- default : return . char( char)
2007+ default :
2008+ // Reject non-letter non-number non-`\r\n` ASCII characters that have
2009+ // multiple scalars. These may be confusable for metacharacters, e.g
2010+ // `[\u{301}]` wouldn't be interpreted as a custom character class due
2011+ // to the combining accent (assuming it is literal, not `\u{...}`).
2012+ let scalars = char. unicodeScalars
2013+ if scalars. count > 1 && scalars. first!. isASCII && char != " \r \n " &&
2014+ !char. isLetter && !char. isNumber {
2015+ throw ParseError . confusableCharacter ( char)
2016+ }
2017+ break
19872018 }
2019+ return . char( char)
19882020 }
19892021 guard let kind = kind else { return nil }
19902022 return AST . Atom ( kind. value, kind. location)
0 commit comments