Repository navigation
feat(spec): the canonical form prints each condition from its parsed tree, so two conditions that differ only in spelling write one text - #927
Merged
FBumann merged 2 commits intoOct 23, 2026
Conversation
…tree, so two conditions that differ only in spelling write one text A `where:`, `when:`, `condition:` or `holds:` string, and a condition or an assumption written as a bare string, is parsed and printed rather than kept as written. `AND`, `OR` and `NOT` print in upper case, the operands of a chain of `AND` or of `OR` are sorted, a bracket stays only where the grammar needs it or around a comparison under `NOT`, and each side of a comparison is normalised as an expression is. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019j1TmBS3MvzmuarWjhCHHu
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019j1TmBS3MvzmuarWjhCHHu
Contributor
Author
Note The following content was generated by AI. Superseded by #924. This branch was fast-forwarded into #924 ( Generated by Claude Code |
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.
Note
The following content was generated by AI.
The canonical form now prints every
where:,when:,condition:andholds:from its parsed tree. Conditions that differ only in the order ofANDorOR, keyword case, brackets or spacing write one text.Stacked on #924.
p_max > 0 and cost > 0cost > 0 AND p_max > 0(load < 0) or load>1load < 0 OR load > 1(b or a) and c(a OR b) AND cnot a > 0NOT (a > 0)p_max + cost > 0cost + p_max > 0p_max > 0and0 < p_maxMethod
printed_conditionin_canonical.pyprints a parsed condition. A chain of one connective is flattened, and its operands are sorted by the text each prints as, as the form already sorts the terms of a sum.AND,ORandNOTprint in upper case. A bracket stays around anORunder anAND, and underNOTaround anything but a name, a literal or a call. A side of a comparison goes throughnormalised, as an expression does. A quoted label keeps its escape._CONDITION_KEYSnames the four fields, next to_EXPRESSION_KEYS._BAREnames what an entry written as a bare string is, by section: an expression, a condition or an assumption. It replaces thebare_is_expressionflag.ANDorORin order. Resolution and the exclusivity proof treat both sides alike, so sorting them changes no program.docs/reference/reading.mdlists what the form does to a condition, and no longer says conditions are left as written.Tests, gates and mutation table
Tests.
test_a_condition_is_written_one_wayhas six cases, one per place a condition sits: a variable's and a constraint'swhere:, the side of a comparison, a condition written as a bare string, an assumption'sholds:andwhere:, and thewhen:of a case. Each one failed before the fix: two texts, not one.test_a_condition_is_printed_one_wayhas twelve cases for the printer, among them every bracket rule, a count, a column of a relation and an escaped label.test_a_condition_in_the_form_holds_where_the_one_it_came_from_holdsgenerates 500 conditions over four flags, with any case and any brackets. It checks each one on all 16 assignments, and checks that the form is a fixed point.the-conditions-of-a-wherecase oftest_an_order_the_form_keeps_is_a_difference_in_the_textasserted the old behaviour. It is nowthe-sides-of-a-comparison-in-a-condition, the one order the form still keeps in a condition.test_the_form_loads_and_is_already_in_the_formalready runs on every example. With this change, it also checks that every condition there prints into text that loads, and that a second pass changes nothing.Gates on
52bb202.abe796cadds only the changelog line.pixi run lint: passed.pixi run test: 3556 passed, 1 skipped.pixi run docs-build: passed.compile-tex: not run, because Tectonic cannot download its bundle in this sandbox. CI runs it.Mutation table on
52bb202. A script made each change, dropped__pycache__before and after, ran the full suite, restored withgit checkout --, and checked that the tree came back clean each time.test_a_condition_is_written_one_way[the-operands-of-and-and-the-case-of-the-keyword]test_a_condition_is_written_one_way[a-condition-entry-written-as-a-string]ANDandORin the order writtentest_a_condition_is_written_one_way[the-operands-of-or-the-brackets-and-the-spacing]test_a_condition_in_the_form_holds_where_the_one_it_came_from_holdsORunder anANDtest_the_form_loads_and_is_already_in_the_form[pypsa]NOTtest_the_form_loads_and_is_already_in_the_form[generator_commitment]test_a_condition_is_printed_one_way[a-calls-kwargs-are-sorted]test_a_condition_is_printed_one_way[a-quoted-label-keeps-its-escape]Not done
0 < p_maxtop_max > 0). The form does not do that for an expression's comparison either, and doing it for both is a separate decision.to_yaml()still writes a condition as the file wrote it. Only the canonical form reprints it, as for an expression.🤖 Generated with Claude Code
https://claude.ai/code/session_019j1TmBS3MvzmuarWjhCHHu