Skip to content

fix: load negative integer literals as numbers (#307) - #311

Open
livingstaccato wants to merge 1 commit into
amplify-education:mainfrom
livingstaccato:fix/negative-int-literals
Open

fix: load negative integer literals as numbers (#307)#311
livingstaccato wants to merge 1 commit into
amplify-education:mainfrom
livingstaccato:fix/negative-int-literals

Conversation

@livingstaccato

Copy link
Copy Markdown

Fixes #307.

x = -3 serialized to the expression string ${-3} rather than the int -3, while x = -3.5 still produced a float. Code reading numbers out of a parsed configuration got a string whenever the value happened to be integral.

Verified in clean per-version environments (all on lark 1.3.1): correct in 7.2.1 and 7.3.1, wrong in 8.0.0rc1, 8.1.0 and 8.1.2 — so it arrived with the v8 rewrite, and it undoes the grammar fix from #182.

Why not fix the lexer

INT_LITERAL already permits a leading minus, so the tempting fix is to let it win. That breaks subtraction: MINUS is also the binary operator, and 10 -3 has to keep parsing as 10 - 3 — which it does in both 7.2.1 and 8.1.2 today. FLOAT_LITERAL escapes the ambiguity only because its pattern cannot be confused with an operator followed by a digit.

So the fix goes where the parse has already settled the question. In UnaryOpRule.serialize, when a unary - applies to something that serialized to a number and the operation is the whole value, emit the negated number.

Everything else keeps the ${...} form: -var.count has no literal value, !flag is not arithmetic, 1 + -3 is a larger expression whose operand must stay concatenable text, -1e10 serializes to a string under preserve_scientific_notation, and a force_operation_parentheses result cannot carry its parentheses as a bare number.

Tests

  • integers round-trip suite mirroring the existing floats one, exercising negative literals bare, in tuples and in objects, alongside subtraction and negated references.
  • Unit tests on UnaryOpRule and API-level tests covering both halves of the trade-off.

Without the source change, 12 of the new tests fail. nose2 --config tox.ini: 1410 tests, OK. ruff check and ruff format --check clean. No change to default behaviour beyond the reported bug.


This pull request, and the investigation behind it, were produced by an AI assistant (Claude) working on behalf of the author. Every reproduction, test run and benchmark cited was executed rather than inferred, but please review with that provenance in mind.

`x = -3` serialized to the expression string `${-3}` rather than the int
`-3`, while `x = -3.5` still produced a float. Downstream code reading
numbers out of a parsed configuration got a string whenever the value
happened to be integral, and the 7.x behaviour (fixed in amplify-education#182) was lost.

MINUS is both the unary sign and the binary subtraction operator, so the
sign cannot simply be folded into INT_LITERAL: `10 -3` has to keep
parsing as a subtraction. FLOAT_LITERAL escapes this only because its
pattern cannot be confused with an operator followed by a digit.

Recombine the two at serialization instead, where the parse has already
settled the question: when a unary `-` is applied to something that
serialized to a number, and the operation is the whole value, emit the
negated number. Everything else keeps the `${...}` form -- `-var.count`
has no literal value, `!flag` is not arithmetic, `1 + -3` is a larger
expression whose operand must stay concatenable text, and a
`force_operation_parentheses` result cannot carry its parentheses as a
bare number.

Add an `integers` round-trip suite mirroring the existing `floats` one,
plus unit tests covering both halves of the trade-off. Without the fix,
12 of the new tests fail.
@livingstaccato
livingstaccato requested a review from a team as a code owner August 18, 2026 18:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Regression in 8.x: negative integer literals load as ${-N} expression strings (regression of #102)

1 participant