@@ -16,16 +16,21 @@ extension TokenConsumer {
1616 /// Returns `true` if the current token represents the start of a statement
1717 /// item.
1818 ///
19+ /// - Parameters:
20+ /// - allowRecovery: Whether to attempt to perform recovery.
21+ /// - preferExpr: If an expression or statement could be parsed, returns
22+ /// `false` if this parameter is set to `true`.
23+ ///
1924 /// - Note: This function must be kept in sync with `parseStatement()`.
2025 /// - Seealso: ``Parser/parseStatement()``
21- func atStartOfStatement( allowRecovery: Bool = false ) -> Bool {
26+ func atStartOfStatement( allowRecovery: Bool = false , preferExpr : Bool = false ) -> Bool {
2227 var lookahead = self . lookahead ( )
2328 if allowRecovery {
2429 // Attributes are not allowed on statements. But for recovery, skip over
2530 // misplaced attributes.
2631 _ = lookahead. consumeAttributeList ( )
2732 }
28- return lookahead. atStartOfStatement ( allowRecovery: allowRecovery)
33+ return lookahead. atStartOfStatement ( allowRecovery: allowRecovery, preferExpr : preferExpr )
2934 }
3035}
3136
@@ -105,6 +110,8 @@ extension Parser {
105110 return label ( self . parseDoStatement ( doHandle: handle) , with: optLabel)
106111 case ( . yield, let handle) ? :
107112 return label ( self . parseYieldStatement ( yieldHandle: handle) , with: optLabel)
113+ case ( . then, let handle) ? :
114+ return label ( self . parseThenStatement ( handle: handle) , with: optLabel)
108115 case nil :
109116 let missingStmt = RawStmtSyntax ( RawMissingStmtSyntax ( arena: self . arena) )
110117 return label ( missingStmt, with: optLabel)
@@ -630,7 +637,7 @@ extension Parser {
630637 if self . at ( anyIn: IfOrSwitch . self) != nil {
631638 return true
632639 }
633- if self . atStartOfStatement ( ) || self . atStartOfDeclaration ( ) {
640+ if self . atStartOfStatement ( preferExpr : true ) || self . atStartOfDeclaration ( ) {
634641 return false
635642 }
636643 return true
@@ -723,6 +730,35 @@ extension Parser {
723730 }
724731}
725732
733+ extension Parser {
734+ /// Parse a `then` statement.
735+ mutating func parseThenStatement( handle: RecoveryConsumptionHandle ) -> RawStmtSyntax {
736+ guard experimentalFeatures. contains ( . thenStatements) else {
737+ return RawStmtSyntax ( RawMissingStmtSyntax ( arena: self . arena) )
738+ }
739+ let ( unexpectedBeforeThen, then) = self . eat ( handle)
740+ let hasMisplacedTry = unexpectedBeforeThen? . containsToken ( where: { TokenSpec ( . try ) ~= $0 } ) ?? false
741+
742+ var expr = self . parseExpression ( )
743+ if hasMisplacedTry && !expr. is ( RawTryExprSyntax . self) {
744+ expr = RawExprSyntax (
745+ RawTryExprSyntax (
746+ tryKeyword: missingToken ( . try ) ,
747+ questionOrExclamationMark: nil ,
748+ expression: expr,
749+ arena: self . arena
750+ )
751+ )
752+ }
753+ return RawStmtSyntax ( RawThenStmtSyntax (
754+ unexpectedBeforeThen,
755+ thenKeyword: then,
756+ expression: expr,
757+ arena: self . arena
758+ ) )
759+ }
760+ }
761+
726762extension Parser {
727763 struct StatementLabel {
728764 var label : RawTokenSyntax
@@ -791,7 +827,7 @@ extension Parser {
791827 }
792828
793829 guard
794- self . at ( . identifier) && !self . atStartOfStatement ( ) && !self . atStartOfDeclaration ( )
830+ self . at ( . identifier) && !self . atStartOfStatement ( preferExpr : true ) && !self . atStartOfDeclaration ( )
795831 else {
796832 return nil
797833 }
@@ -806,9 +842,20 @@ extension Parser.Lookahead {
806842 /// Returns `true` if the current token represents the start of a statement
807843 /// item.
808844 ///
845+ /// - Parameters:
846+ /// - allowRecovery: Whether to attempt to perform recovery.
847+ /// - preferExpr: If `true`, if either an expression or statement could be
848+ /// parsed, returns `false` such that an expression can be parsed.
849+ ///
809850 /// - Note: This function must be kept in sync with `parseStatement()`.
810851 /// - Seealso: ``Parser/parseStatement()``
852+ < <<<<<< HEAD
811853 mutating func atStartOfStatement( allowRecovery: Bool = false ) -> Bool {
854+ ||||||| parent of a2 c4 f722 ( Introduce `then` statements)
855+ mutating func isStartOfStatement( allowRecovery: Bool = false ) -> Bool {
856+ =======
857+ mutating func isStartOfStatement( allowRecovery: Bool = false , preferExpr: Bool = false ) -> Bool {
858+ >>>>>>> a2c4f722 ( Introduce `then` statements)
812859 if ( self . at ( anyIn: SwitchCaseStart . self) != nil || self . at ( . atSign) ) && withLookahead ( { $0. atStartOfSwitchCaseItem ( ) } ) {
813860 // We consider SwitchCaseItems statements so we don't parse the start of a new case item as trailing parts of an expression.
814861 return true
@@ -877,11 +924,66 @@ extension Parser.Lookahead {
877924 // For example, could be the function call "discard()".
878925 return false
879926 }
927+
928+ case . then:
929+ return isStartOfThenStatement ( preferExpr: preferExpr)
930+
880931 case nil :
881932 return false
882933 }
883934 }
884935
936+ /// Whether we're currently at a `then` token that should be parsed as a
937+ /// `then` statement.
938+ mutating func isStartOfThenStatement( preferExpr: Bool ) -> Bool {
939+ // If the feature is disabled, don't parse it.
940+ guard self . experimentalFeatures. contains ( . thenStatements) else {
941+ return false
942+ }
943+ guard self . at ( . keyword( . then) ) else {
944+ return false
945+ }
946+
947+ // If we want to prefer an expr, and aren't at the start of a newline, then
948+ // don't parse a ThenStmt.
949+ if preferExpr && !self . atStartOfLine {
950+ return false
951+ }
952+
953+ let next = peek ( )
954+
955+ // If 'then' is followed by a binary or postfix operator, prefer to parse as
956+ // an expr.
957+ if BinaryOperatorLike ( lexeme: next) != nil || PostfixOperatorLike ( lexeme: next) != nil {
958+ return false
959+ }
960+
961+ switch next. rawTokenKind {
962+ case . keyword:
963+ // Treat 'is' and 'as' like the binary operator case, and parse as an
964+ // expr.
965+ switch Keyword ( next. tokenText) {
966+ case . as, . is:
967+ return false
968+ default :
969+ break
970+ }
971+
972+ case . leftBrace:
973+ // This is a trailing closure.
974+ return false
975+
976+ case . leftParen, . leftSquare, . period:
977+ // These are handled based on whether there is trivia between the 'then'
978+ // and the token. If so, it's a 'then' statement. Otherwise it should
979+ // be treated as an expression, e.g `then(...)`, `then[...]`, `then.foo`.
980+ return !self . currentToken. trailingTriviaText. isEmpty || !next. leadingTriviaText. isEmpty
981+ default :
982+ break
983+ }
984+ return true
985+ }
986+
885987 /// Returns whether the parser's current position is the start of a switch case,
886988 /// given that we're in the middle of a switch already.
887989 mutating func atStartOfSwitchCase( allowRecovery: Bool = false ) -> Bool {
0 commit comments