Просмотр исходного кода

Add precedence rules for assignment operators to the precedence diagram. (#3083)

Also indicate what can appear within parentheses.

This is intended to be a clarification, not a design change. Note that
while we previously described the operand of `++` or `--` as being
simply an expression, the operand can never be anything other than the
kinds of expression the diagram now shows due to the expression category
rules added in #2006.

Fixes #3079.
Richard Smith 2 лет назад
Родитель
Сommit
7b22173cba

+ 12 - 4
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;<br>--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;<br>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:

+ 5 - 3
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 <higher precedence>, <lower precedence>
     // 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);

+ 13 - 1
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: ]

+ 2 - 11
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: ]