diff --git a/docs/design/expressions/README.md b/docs/design/expressions/README.md index a6dccf90ea25..aa411906c558 100644 --- a/docs/design/expressions/README.md +++ b/docs/design/expressions/README.md @@ -85,6 +85,9 @@ graph BT complement["^x"] click complement "https://github.com/carbon-language/carbon-lang/blob/trunk/docs/design/expressions/bitwise.md" + incDec["++x;
--x;"] + click incDec "https://github.com/carbon-language/carbon-lang/blob/trunk/docs/design/assignment.md" + unary((" ")) as["x as T"] @@ -136,7 +139,12 @@ graph BT if>"if x then y else z"] click if "https://github.com/carbon-language/carbon-lang/blob/trunk/docs/design/expressions/if.md" - expressionEnd["x;"] + insideParens["(...)"] + + assignment["x = y;
x $= y;"] + click assignment "https://github.com/carbon-language/carbon-lang/blob/trunk/docs/design/assignment.md" + + expressionStatement["x;"] top --> parens & braces & unqualifiedName @@ -146,8 +154,7 @@ graph BT memberAccess --> top pointer --> memberAccess - negation --> pointer - complement --> pointer + negation & complement & incDec --> pointer unary --> negation & complement %% Use a longer arrow here to put `not` next to `and` and `or`. not -------> memberAccess @@ -157,7 +164,8 @@ graph BT logicalOperand --> comparison & not and & or --> logicalOperand logicalExpression --> and & or - if & expressionEnd --> logicalExpression + if & expressionStatement --> logicalExpression + insideParens & assignment --> if ``` The diagram's attributes are: diff --git a/toolchain/parser/precedence.cpp b/toolchain/parser/precedence.cpp index 96a312d99f67..d1432274ea04 100644 --- a/toolchain/parser/precedence.cpp +++ b/toolchain/parser/precedence.cpp @@ -17,6 +17,7 @@ enum PrecedenceLevel : int8_t { // Terms. TermPrefix, // Numeric. + IncrementDecrement, NumericPrefix, Modulo, Multiplicative, @@ -53,7 +54,8 @@ struct OperatorPriorityTable { // Start with a list of , // relationships. MarkHigherThan({Highest}, {TermPrefix, LogicalPrefix}); - MarkHigherThan({TermPrefix}, {NumericPrefix, BitwisePrefix}); + MarkHigherThan({TermPrefix}, + {NumericPrefix, BitwisePrefix, IncrementDecrement}); MarkHigherThan({NumericPrefix, BitwisePrefix}, {As, Multiplicative, Modulo, BitwiseAnd, BitwiseOr, BitwiseXor, BitShift}); @@ -64,7 +66,7 @@ struct OperatorPriorityTable { MarkHigherThan({Relational, LogicalPrefix}, {LogicalAnd, LogicalOr}); MarkHigherThan({LogicalAnd, LogicalOr}, {If}); MarkHigherThan({If}, {Assignment}); - MarkHigherThan({Assignment}, {Lowest}); + MarkHigherThan({Assignment, IncrementDecrement}, {Lowest}); // Types are mostly a separate precedence graph. MarkHigherThan({Highest}, {TypePrefix}); @@ -208,7 +210,7 @@ auto PrecedenceGroup::ForLeading(TokenKind kind) case TokenKind::MinusMinus: case TokenKind::PlusPlus: - return PrecedenceGroup(Assignment); + return PrecedenceGroup(IncrementDecrement); case TokenKind::Caret: return PrecedenceGroup(BitwisePrefix); diff --git a/toolchain/parser/testdata/operators/fail_precedence_assign.carbon b/toolchain/parser/testdata/operators/fail_precedence_assign.carbon index 253dfe243cf0..68f75145fcad 100644 --- a/toolchain/parser/testdata/operators/fail_precedence_assign.carbon +++ b/toolchain/parser/testdata/operators/fail_precedence_assign.carbon @@ -23,6 +23,10 @@ fn F() { // CHECK:STDERR: a + ++a; // CHECK:STDERR: ^ a + ++a; + // CHECK:STDERR: fail_precedence_assign.carbon:[[@LINE+3]]:5: Parentheses are required around this unary `if` operator. + // CHECK:STDERR: ++if c then a else b; + // CHECK:STDERR: ^ + ++if c then a else b; } // CHECK:STDOUT: [ @@ -56,6 +60,14 @@ fn F() { // CHECK:STDOUT: {kind: 'PrefixOperator', text: '++', subtree_size: 2}, // CHECK:STDOUT: {kind: 'InfixOperator', text: '+', subtree_size: 4}, // CHECK:STDOUT: {kind: 'ExpressionStatement', text: ';', subtree_size: 5}, -// CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 31}, +// CHECK:STDOUT: {kind: 'NameExpression', text: 'c'}, +// CHECK:STDOUT: {kind: 'IfExpressionIf', text: 'if', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'NameExpression', text: 'a'}, +// CHECK:STDOUT: {kind: 'IfExpressionThen', text: 'then', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'NameExpression', text: 'b'}, +// CHECK:STDOUT: {kind: 'IfExpressionElse', text: 'else', subtree_size: 6}, +// CHECK:STDOUT: {kind: 'PrefixOperator', text: '++', subtree_size: 7}, +// CHECK:STDOUT: {kind: 'ExpressionStatement', text: ';', subtree_size: 8}, +// CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 39}, // CHECK:STDOUT: {kind: 'FileEnd', text: ''}, // CHECK:STDOUT: ] diff --git a/toolchain/parser/testdata/operators/precedence_assign.carbon b/toolchain/parser/testdata/operators/precedence_assign.carbon index 151724b993e2..9b539d8642b4 100644 --- a/toolchain/parser/testdata/operators/precedence_assign.carbon +++ b/toolchain/parser/testdata/operators/precedence_assign.carbon @@ -9,9 +9,8 @@ fn F(c: bool) { var b: i32; var p: i32*; *p = if c then 1 else 2; - // These are valid to _parse_ even though rejected semantically. + // This is valid to _parse_ even though rejected semantically. (if c then a else b) += if c then 1 else 2; - ++if c then a else b; } // CHECK:STDOUT: [ @@ -65,14 +64,6 @@ fn F(c: bool) { // CHECK:STDOUT: {kind: 'IfExpressionElse', text: 'else', subtree_size: 6}, // CHECK:STDOUT: {kind: 'InfixOperator', text: '+=', subtree_size: 15}, // CHECK:STDOUT: {kind: 'ExpressionStatement', text: ';', subtree_size: 16}, -// CHECK:STDOUT: {kind: 'NameExpression', text: 'c'}, -// CHECK:STDOUT: {kind: 'IfExpressionIf', text: 'if', subtree_size: 2}, -// CHECK:STDOUT: {kind: 'NameExpression', text: 'a'}, -// CHECK:STDOUT: {kind: 'IfExpressionThen', text: 'then', subtree_size: 2}, -// CHECK:STDOUT: {kind: 'NameExpression', text: 'b'}, -// CHECK:STDOUT: {kind: 'IfExpressionElse', text: 'else', subtree_size: 6}, -// CHECK:STDOUT: {kind: 'PrefixOperator', text: '++', subtree_size: 7}, -// CHECK:STDOUT: {kind: 'ExpressionStatement', text: ';', subtree_size: 8}, -// CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 59}, +// CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 51}, // CHECK:STDOUT: {kind: 'FileEnd', text: ''}, // CHECK:STDOUT: ]