fix: load negative integer literals as numbers (#307) - #311
Merged
kkozik-amplify merged 3 commits intoAug 24, 2026
Merged
Conversation
`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.
The bool-operand test asserted "${-True}", which no HCL input can
produce: UnaryOpRule serializes its operand with inside_dollar_string
set, and LiteralValueRule yields the string "true" in that context.
Assert the helper contract directly instead, and add TestNegatedKeywords
for the path a parse actually takes.
-1e10 lexes as a single FLOAT_LITERAL and never reaches the unary path.
Add the spaced form, which does, and whose operand is a string under
preserve_scientific_notation.
Pin -0 normalization, and add -0 and -true to the integers round-trip
suite. The spaced forms stay out of that fixture because the direct
pipeline normalizes the space away, which would break the byte-exact
direct-reconstruct assertion.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
kkozik-amplify
approved these changes
Aug 24, 2026
amplify-education#312 landed the empty-heredoc fix, which touched the same two append-only spots as this branch. Both conflicts keep both sides: CHANGELOG.md - both entries, amplify-education#309's landed one first so this branch's diff is a pure append test_api.py - TestEmptyHeredocs from main, then this branch's TestNegativeIntegerLiterals and TestNegatedKeywords No source overlap: this branch only touches hcl2/rules/expressions.py, amplify-education#312 only touched hcl2/hcl2.lark. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
kkozik-amplify
added a commit
to livingstaccato/python-hcl2
that referenced
this pull request
Aug 24, 2026
amplify-education#312 and amplify-education#311 have landed since this branch was opened. Both conflicts are append-only and keep both sides: CHANGELOG.md - landed entries first, so this branch's two lines are a pure append test_api.py - TestEmptyHeredocs, TestNegativeIntegerLiterals and TestNegatedKeywords from main, then this branch's TestStripStringQuotes No source overlap: this branch touches hcl2/rules/strings.py and hcl2/utils.py, which neither amplify-education#311 (expressions.py) nor amplify-education#312 (hcl2.lark) went near. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
kkozik-amplify
added a commit
to agu2347/python-hcl2
that referenced
this pull request
Aug 24, 2026
amplify-education#312, amplify-education#311 and amplify-education#313 have landed. Two conflicts: hcl2/hcl2.lark - real overlap with amplify-education#312, both editing the heredoc terminals. Resolved as the union: amplify-education#312's lazy optional body group plus this branch's \r?, giving /<<MARKER\r?\n(?:(?:.|\n)*?\r?\n)??\s*MARKER\r?\n/. Verified an empty heredoc in a CRLF file parses, two consecutive empty CRLF heredocs stay separate, and a line merely ending in the marker still does not terminate the body. CHANGELOG.md - landed entries first, this branch's appended. hcl2/rules/strings.py and hcl2/utils.py auto-merged against amplify-education#313, which touched both. Checked rather than assumed: strip_string_quotes on CRLF source, escapes resolved through CRLF line endings, a literal CR inside a string surviving, and amplify-education#313's out-of-range and lone-surrogate escape guards all still behave. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
kkozik-amplify
added a commit
to agu2347/python-hcl2
that referenced
this pull request
Aug 24, 2026
amplify-education#312, amplify-education#311 and amplify-education#313 have landed. Only CHANGELOG.md conflicted, resolved with the landed entries first and this branch's appended. hcl2/rules/strings.py auto-merged against amplify-education#313, which rewrote the same file's string-serialization path. Verified the two coexist: amplify-education#313's process_escape_sequences and lark_name() dispatch sit alongside this branch's _strip_closing_marker_line, and the full suite passes. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #307.
x = -3serialized to the expression string${-3}rather than the int-3, whilex = -3.5still 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_LITERALalready permits a leading minus, so the tempting fix is to let it win. That breaks subtraction:MINUSis also the binary operator, and10 -3has to keep parsing as10 - 3— which it does in both 7.2.1 and 8.1.2 today.FLOAT_LITERALescapes 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.counthas no literal value,!flagis not arithmetic,1 + -3is a larger expression whose operand must stay concatenable text,-1e10serializes to a string underpreserve_scientific_notation, and aforce_operation_parenthesesresult cannot carry its parentheses as a bare number.Tests
integersround-trip suite mirroring the existingfloatsone, exercising negative literals bare, in tuples and in objects, alongside subtraction and negated references.UnaryOpRuleand 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 checkandruff format --checkclean. 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.