implies parses left-associatively — the grammar's implies_expression is a (...)* loop — but
Impliesf.Stringize parenthesises the left operand at equal priority and the right one only at
strictly lower priority. The guards are on the wrong sides, so a right-nested implication prints as
something the parser reads as the left-nested one:
var e = MathS.FromString("a implies (b implies c)");
e.Stringize() // "a implies b implies c"
MathS.FromString(e.Stringize()) // (a implies b) implies c
Implication is not associative, so this is a change of value and not only of shape. Measured on
v2.3.0 (6b93b40) by substituting:
| a |
b |
c |
a implies (b implies c) |
what it prints and reads back as |
| False |
True |
False |
True |
False |
| False |
False |
False |
True |
False |
| True |
True |
False |
False |
False |
This is the same defect as the (2^3)^2 printed as 2^3^2 case AGENTS.md names: a wrong reading
that is still a valid expression, so nothing raises.
-> prints through the same method, so a -> (b -> c) has it too.
Providedf has the mirror of it. provided_expression is right-associative on purpose (the grammar
says so in a comment), and the printer parenthesises the right operand rather than the left:
var e = MathS.FromString("(a provided b) provided c");
e.Stringize() // "a provided b provided c"
MathS.FromString(e.Stringize()) // a provided (b provided c)
That one looks like shape only — the grammar comment argues provided is associative — but it is the
same guard on the same wrong side.
The fix is one character on each side of Impliesf.Stringize, in
Functions/Output/ToString/ToString.Discrete.Classes.cs:
// now
$"{Assumption.Stringize(Assumption.Priority <= Priority)} implies {Conclusion.Stringize(Conclusion.Priority < Priority)}"
// should be
$"{Assumption.Stringize(Assumption.Priority < Priority)} implies {Conclusion.Stringize(Conclusion.Priority <= Priority)}"
which is what Minusf, Divf and Modf next door already do for the same reason — left-associative
and not associative, so an equal-priority right operand needs the brackets. Latexize should be
checked at the same time; CSharpMath.Evaluation reads LaTeX back (#822).
Deliberately not the same case: Sumf, Mulf, Andf, Orf and Xorf also drop a right nesting —
1 + (2 + 3) prints as 1 + 2 + 3 and reads back as (1 + 2) + 3 — but those operators are
associative, so the value survives and only the node shape changes. That is a defensible choice about
output; implies is not one.
StringizeRoundTripsToTheSameNode in EveryNodeSurvivesEveryPipelineTest misses all of it because
every sample it builds is one level deep. A right-nested sample per binary node type would catch this
class, and is worth adding whatever is decided about the associative ones.
Found while measuring what the printed form does carry, for #323 / #1031.
impliesparses left-associatively — the grammar'simplies_expressionis a(...)*loop — butImpliesf.Stringizeparenthesises the left operand at equal priority and the right one only atstrictly lower priority. The guards are on the wrong sides, so a right-nested implication prints as
something the parser reads as the left-nested one:
Implication is not associative, so this is a change of value and not only of shape. Measured on
v2.3.0(6b93b40) by substituting:a implies (b implies c)This is the same defect as the
(2^3)^2printed as2^3^2case AGENTS.md names: a wrong readingthat is still a valid expression, so nothing raises.
->prints through the same method, soa -> (b -> c)has it too.Providedfhas the mirror of it.provided_expressionis right-associative on purpose (the grammarsays so in a comment), and the printer parenthesises the right operand rather than the left:
That one looks like shape only — the grammar comment argues
providedis associative — but it is thesame guard on the same wrong side.
The fix is one character on each side of
Impliesf.Stringize, inFunctions/Output/ToString/ToString.Discrete.Classes.cs:which is what
Minusf,DivfandModfnext door already do for the same reason — left-associativeand not associative, so an equal-priority right operand needs the brackets.
Latexizeshould bechecked at the same time;
CSharpMath.Evaluationreads LaTeX back (#822).Deliberately not the same case:
Sumf,Mulf,Andf,OrfandXorfalso drop a right nesting —1 + (2 + 3)prints as1 + 2 + 3and reads back as(1 + 2) + 3— but those operators areassociative, so the value survives and only the node shape changes. That is a defensible choice about
output;
impliesis not one.StringizeRoundTripsToTheSameNodeinEveryNodeSurvivesEveryPipelineTestmisses all of it becauseevery sample it builds is one level deep. A right-nested sample per binary node type would catch this
class, and is worth adding whatever is decided about the associative ones.
Found while measuring what the printed form does carry, for #323 / #1031.