Skip to content

Commit e24aa16

Browse files
authored
fix incorrect 'forgot new' errors for operator-like specifiers (#426)
1 parent f1f33ec commit e24aa16

File tree

2 files changed

+46
-3
lines changed

2 files changed

+46
-3
lines changed

src/scenic/syntax/scenic.gram

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1721,18 +1721,23 @@ scenic_specifiers: ss=','.scenic_specifier+ { ss }
17211721
scenic_specifier:
17221722
| scenic_valid_specifier
17231723
| invalid_scenic_specifier
1724+
# Split into non-operator-like vs operator-like instance specifiers.
17241725
scenic_valid_specifier:
1725-
| 'with' p=NAME v=expression { s.WithSpecifier(prop=p.string, value=v, LOCATIONS) }
1726+
| scenic_instance_specifier_nonoperator
1727+
| scenic_instance_specifier_operator_like
1728+
scenic_instance_specifier_operator_like:
17261729
| 'at' position=expression { s.AtSpecifier(position=position, LOCATIONS) }
17271730
| "offset" 'by' o=expression { s.OffsetBySpecifier(offset=o, LOCATIONS) }
17281731
| "offset" "along" d=expression 'by' o=expression { s.OffsetAlongSpecifier(direction=d, offset=o, LOCATIONS) }
1732+
| 'in' r=expression { s.InSpecifier(region=r, LOCATIONS) }
1733+
scenic_instance_specifier_nonoperator:
1734+
| 'with' p=NAME v=expression { s.WithSpecifier(prop=p.string, value=v, LOCATIONS) }
17291735
| direction=scenic_specifier_position_direction position=expression distance=['by' e=expression { e }] {
17301736
s.DirectionOfSpecifier(direction=direction, position=position, distance=distance, LOCATIONS)
17311737
}
17321738
| "beyond" v=expression 'by' o=expression b=['from' a=expression {a}] { s.BeyondSpecifier(position=v, offset=o, base=b) }
17331739
| "visible" b=['from' r=expression { r }] { s.VisibleSpecifier(base=b, LOCATIONS) }
17341740
| 'not' "visible" b=['from' r=expression { r }] { s.NotVisibleSpecifier(base=b, LOCATIONS) }
1735-
| 'in' r=expression { s.InSpecifier(region=r, LOCATIONS) }
17361741
| 'on' r=expression { s.OnSpecifier(region=r, LOCATIONS) }
17371742
| "contained" 'in' r=expression { s.ContainedInSpecifier(region=r, LOCATIONS) }
17381743
| "following" f=expression b=['from' e=expression {e}] 'for' d=expression {
@@ -2492,7 +2497,7 @@ invalid_kwarg[NoReturn]:
24922497
}
24932498

24942499
invalid_scenic_instance_creation[NoReturn]:
2495-
| n=NAME s=scenic_valid_specifier {
2500+
| n=NAME s=scenic_instance_specifier_nonoperator {
24962501
self.raise_syntax_error_known_range("invalid syntax. Perhaps you forgot 'new'?", n, s)
24972502
}
24982503
invalid_scenic_specifier[NoReturn]:

tests/syntax/test_parser.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1518,6 +1518,44 @@ def test_missing_new(self):
15181518
parse_string_helper("Object facing x")
15191519
assert "forgot 'new'" in e.value.msg
15201520

1521+
def test_in_operator_not_missing_new(self):
1522+
# Regression test for issue #356: "in" is also a specifier keyword, but
1523+
# `x in [0]` must not trigger the "forgot 'new'" error message.
1524+
with pytest.raises(ScenicSyntaxError) as e:
1525+
parse_string_helper(
1526+
"""
1527+
x = 0
1528+
x in [0]
1529+
=
1530+
"""
1531+
)
1532+
err = e.value
1533+
assert "forgot 'new'" not in err.msg
1534+
assert "invalid syntax" in err.msg
1535+
assert err.lineno == 3
1536+
1537+
def test_invalid_specifier_line_number(self):
1538+
# Regression test for issue #345: a malformed position specifier like
1539+
# `offset left` should not trigger the "forgot 'new'" error or be attributed
1540+
# to an earlier valid `... until self in ...` expression.
1541+
with pytest.raises(ScenicSyntaxError) as e:
1542+
parse_string_helper(
1543+
"""
1544+
behavior AdvBehavior():
1545+
do CrossingBehavior(ego) until self in ego.lane
1546+
while True:
1547+
take SetWalkingSpeedAction(0)
1548+
1549+
SHIFT = Vector(1, 2)
1550+
AdvAgent = new Pedestrian at Truck offset left Truck.heading by SHIFT,
1551+
with heading Truck.heading
1552+
"""
1553+
)
1554+
err = e.value
1555+
assert "forgot 'new'" not in err.msg
1556+
assert "invalid syntax" in err.msg
1557+
assert err.lineno == 7
1558+
15211559
def test_invalid_specifier(self):
15221560
with pytest.raises(ScenicSyntaxError) as e:
15231561
parse_string_helper("new Object blobbing")

0 commit comments

Comments
 (0)