Skip to content

Commit ecb689d

Browse files
authored
Fix string literal argument repetition in grammar (#181)
* Fix string literal argument repetition in grammar Fixes #180 by no longer allowing an empty string literal to match the `#ExpressionString` context. * Fix precedence test for sin expression (#182) Updated test to check for correct precedence in expressions after fixing grammar error. The order-of-operations precedence was broken previously due to the possibility to insert an empty string literal turning a unary operation into a binary one.
1 parent 3d343e5 commit ecb689d

File tree

2 files changed

+15
-21
lines changed

2 files changed

+15
-21
lines changed

src/grammar/McCommon.g4

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ expr
3030
: '0' #ExpressionZero
3131
| IntegerLiteral #ExpressionInteger
3232
| FloatingLiteral #ExpressionFloat
33-
| (args+=StringLiteral)* #ExpressionString
33+
| (args+=StringLiteral)+ #ExpressionString
3434
| Identifier '->' expr #ExpressionPointerAccess
3535
| Identifier '.' expr #ExpressionStructAccess
3636
| Identifier '[' expr ']' #ExpressionArrayAccess
@@ -116,4 +116,4 @@ Symbol: 'symbol'; // McCode ???? type ????!?!?!
116116
UnparsedBlock: '%{' (.)*? '%}'; // Used for raw C code blocks and metadata, etc.
117117
Include: '%include';
118118

119-
Null: 'NULL'; // remove if we switch to underlying C grammar?
119+
Null: 'NULL'; // remove if we switch to underlying C grammar?

tests/test_expression.py

Lines changed: 13 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -373,28 +373,22 @@ def test_parse_Expr(self):
373373
pi = Expr.id('PI')
374374
x = Expr.id('x')
375375
sin = Value('sin', _object=ObjectType.function)
376-
# These two expressions should be identical, but details of the Lexer/Parser pick the second
377-
equiv_expr = BinaryOp(
378-
DataType.undefined, OpStyle.C, '__call__', [sin], [
379-
BinaryOp(
380-
DataType.undefined, OpStyle.C, '/', [
381-
BinaryOp(DataType.undefined, OpStyle.C, '*', [
382-
UnaryOp(DataType.undefined, OpStyle.C, '-', pi)
383-
], [x])
384-
], [Value.float(2)]
385-
)
386-
]
387-
)
388-
expr = BinaryOp(DataType.undefined, OpStyle.C, '__call__', [sin],[
389-
UnaryOp(DataType.undefined, OpStyle.C, '-', [
390-
BinaryOp(DataType.undefined, OpStyle.C, '/', [
391-
BinaryOp(DataType.undefined, OpStyle.C, '*', [pi], [x])], [Value.float(2)])
392-
])
376+
uop, bop, u, c = UnaryOp, BinaryOp, DataType.undefined, OpStyle.C
377+
two = Value.float(2)
378+
# These two expressions have identical string representations,
379+
# but an error in the grammar caused the Lexer/Parser pick the second.
380+
# Now that the error is fixed, the Unary operation of negation has higher
381+
# precedence than either the multiplication or division.
382+
expr = bop(u, c, '__call__', [sin], [
383+
bop(u, c, '/', [bop(u, c, '*', [uop(u, c, '-', pi)], [x])], [two])
384+
])
385+
wrong_precedence_expr = bop(u, c, '__call__', [sin], [
386+
uop(u, c, '-', [bop(u, c, '/', [bop(u, c, '*', [pi], [x])], [two])])
393387
])
394388

395389
self.assertEqual(sin_minus_pi_x_over_2, expr)
396390
self.assertEqual(str(sin_minus_pi_x_over_2), str(expr))
397-
self.assertEqual(str(sin_minus_pi_x_over_2), str(equiv_expr))
391+
self.assertEqual(str(sin_minus_pi_x_over_2), str(wrong_precedence_expr))
398392

399393
# One reason that Expr has a list of expressions:
400394
atan2_y_x = Expr.parse('arctan2(y, x)')
@@ -506,4 +500,4 @@ def test_is_vector(self):
506500
expr = -ex / BinaryOp(DataType.undefined, OpStyle.C, '__getitem__', [values], Expr.int(0).expr)
507501
self.assertEqual(f'{expr}', '-ex / values[0]')
508502

509-
self.assertFalse(expr.is_vector) # because values[0] is a scalar
503+
self.assertFalse(expr.is_vector) # because values[0] is a scalar

0 commit comments

Comments
 (0)