--- date: 2025-04-24 url: https://hackmd.io/HjxQO3QqTVKHxR-jx8fCxA --- # let chain grammar Attempt by encoding precedence This doens't entirely capture the precedence rules... `if let true == true && x == true || false` (which is an error because the precedence of `||` is too low), need to somehow say when parsing the ConditionExpression that the precedence rules change. ```rust IfExpression -> `if` IfConditions BlockExpression (`else` ( BlockExpression | IfExpression ) )? IfConditions -> IfCondition ( `&&` IfCondition )* | Expression _except [StructExpression]_ // Changed: previously was Expression or `let` // Includes exception because that is recursive, and I don't want to fork the // entire expression grammar. IfCondition -> ConditionExpression _except [StructExpression]_ Expression -> ExpressionWithoutBlock | ExpressionWithBlock // Changed: Factored out expressions shared with ConditionExpression ExpressionWithoutBlock -> OuterAttribute* ( CommonExpression | OperatorExpression | RangeExpression | StructExpression ) // New: Expressions shared with ConditionExpression // (no LetExpression or OperatorExpression or StructExpression) CommonExpression -> LiteralExpression | PathExpression | GroupedExpression | ArrayExpression | AwaitExpression | IndexExpression | TupleExpression | TupleIndexingExpression | CallExpression | MethodCallExpression | FieldExpression | ClosureExpression | AsyncBlockExpression | ContinueExpression | BreakExpression | ReturnExpression | UnderscoreExpression | MacroInvocation ExpressionWithBlock -> OuterAttribute* ( BlockExpression | ConstBlockExpression | UnsafeBlockExpression | LoopExpression | IfExpression | MatchExpression ) // New: Everything allowed in a condition expression // Forks OperatorExpression to exclude `||` or `&&` or `a..` or `a..b` or `a..=b` // Excludes StructExpression // Adds LetExpression ConditionExpression -> OuterAttribute* ( CommonExpression | CommonOperatorExpression | NoPrefixRangeExpression | LetExpression ) // New LetExpression -> `let` Pattern `=` LetScrutinee // New: Expression that excludes `||` or `&&` or `a..` or `a..b` or `a..=b` or StructExpression LetScrutinee -> CommonExpression | CommonOperatorExpression | NoPrefixRangeExpression // Changed: Factored out expressions shared with ConditionExpression OperatorExpression -> CommonOperatorExpression | LazyBooleanExpression | AssignmentExpression | CompoundAssignmentExpression // New: Factored out things. CommonOperatorExpression -> BorrowExpression | DereferenceExpression | ErrorPropagationExpression | NegationExpression | ArithmeticOrLogicalExpression | ComparisonExpression | TypeCastExpression // Changed: Factored out NoPrefixRangeExpression RangeExpression -> RangeExpr | RangeFromExpr | RangeInclusiveExpr | NoPrefixRangeExpression // New: Range expressions that don't start with an expression NoPrefixRangeExpression -> | RangeToExpr | RangeFullExpr | RangeToInclusiveExpr ``` --- New attempt where precedence is specified using _except_. ```rust IfExpression -> `if` Conditions BlockExpression (`else` ( BlockExpression | IfExpression ) )? Conditions -> Expression _except [StructExprStruct]_ | LetChain LetChain -> LetChainCondition ( `&&` LetChainCondition )* LetChainCondition -> Expression _except [ExcludedConditions]_ | `let` Pattern `=` Scrutinee _except [ExcludedConditions]_ // Would need to somehow say "these exclusions are in effect until ...parse_expr_bottom?" ExcludedConditions -> StructExprStruct | LazyBooleanExpression | NoPrefixRangeExpression | AssignmentExpression | CompoundAssignmentExpression NoPrefixRangeExpression -> | RangeToExpr | RangeFullExpr | RangeToInclusiveExpr ``` TC: This is probably what we should do. It leans into infinite lookahead, but that's OK for our purposes here, at least for the moment. --- TC: With respect to precedence handling, one very direct way to encode this might be to, in the DFA, separate out the binary operators at each level of precedence, and parse each separately, making sure to more tightly loop over the higher precedence ones. That's what my draft grammar did in a limited way, but it could be extended. TC: (Or we just, over an edition, switch Rust to s-exprs. /s) --- Use this instead of _except_. Note that this does not explain the StructExprStruct restriction, which is quite complicated (and seems buggy to me). See also expr.struct.brace-restricted-positions. This also doesn't explain the ambiguity between the two options in `Conditions`. We should probably define how those ambiguities are resolved in general for the entire grammar. ```rust IfExpression -> `if` Conditions BlockExpression (`else` ( BlockExpression | IfExpression ) )? Conditions -> Expression _except [StructExpression]_ | LetChain LetChain -> LetChainCondition ( `&&` LetChainCondition )* LetChainCondition -> Expression _see [expr.if.chains.parse-restriction]_ | OuterAttribute* `let` Pattern `=` Scrutinee _see [expr.if.chains.parse-restriction]_ ``` [expr.if.chains.parse-restriction] When parsing a chain of conditions separated with `&&`, the following expressions are not allowed: - [StructExpression] - [LazyBooleanExpression] - [RangeExpr] - [RangeFromExpr] - [RangeInclusiveExpr] - [AssignmentExpression] - [CompoundAssignmentExpression]