On 2025-04-04, Stefan Monnier <
monnier@iro.umontreal.ca> wrote:
1> (parse-infix '(sin a + b + c + d))
(sin (+ (+ (+ a b) c)
d))
>
But, if we put parentheses on the operand of sin, then it's no longer
the unary sin, but a function call with that argument:
>
2> (parse-infix '(sin (a) + b + c + d))
(+ (+ (+ (sin a) b)
c)
d)
>
For someone coming from "statically typed functional programming", this
is *very* weird.
All those languages (Haskell/OCaml/SML/Coq/Idris/Agda/...) treat
>
(sin a + b + c + d)
>
the same as
>
(sin (a) + b + c + d)
Ah, but how about higher precedence operators?
Would you want
sin a * b * c
to be
sin (a) * b *c
If not, you're asking for the operator version of sin to be
a unary operator with a higher precedence than plus or minus,
but lower than multiplication.
In math boox and papers, you certainly see expression like
sin 2cx
I believe I have also seen
sin t + 1
But also:
(sin t - cos t)**2
It seems that a useful behavior to have would be for the
range of the unary operator to stop when we encounter
another such unary operator; i.e.
sin a + b + c + sin d + e + f
^ ^
Suppose that when the binary + is adjacent to a unary operator as above,
we pretend that the + has a lower precedence.
Ooh, that seems to nicely for all unary operators.
I put into the parser a couple of hack lines which look like this:
(iflet ((o3 [ifx-uops (car rest)]))
(if (eq o1.arity :infix)
(set o1 (copy o1)
o1.prec (pred o3.prec))))
When about to process an infix operator o1, we first peek at the rest of the
input. If it starts with a prefix operator, which we call o3, (since o2 is
being used for a previous operator pushed onto the operator stack) then we
duplicate the current o1 operator object, and give it a precedence that is one
lower than the infix.
(A better implementation would be to have a lexical variable here holding
a copy of the precedence, and just adjust the lexical, so as not to copy
the operator object.)
Bam:
What we want works:
2> (parse-infix '(sin x + x + cos y + y))
(+ (sin (+ x x))
(cos (+ y y)))
Also the higher precedence unary minus now does
the right thing, syntactically, FWIW:
3> (parse-infix '(- x * x * - y * y))
(* (- (* x x))
(- (* y y)))
The rule is easy to understand and leads to intuitive, symmetric treatment of
symmetric-looking formulas.
A lower precedence prefix operator suddenly appearing in a chain of higher
precedence operators should interrupt that chain, and begin a new chain piece
at the same level of depth.
Say, I wonder whether this logic could be nicely expressed in LAR(1) anymore.
I have a feeling that not, because LALR(1) makes only simple shift or reduce
Next question: should the precedence-lowering propagate thorugh stacks
of prefix operators?
Like here:
5> (parse-infix '(sin x + x + - cos y + y))
(sin (+ x (+ x (- (cos (+ y y))))))
When we are processing (+ - cos y ...), looking at the binarry +, should we
take into consideration all the consecutive unary operators that prefix the
rest of the input? In this case (- cos). And should we then take their lowest
precedence? The idea being that cos propagates its low precedence to the higher
precedence -, which propagates it to + (decremeted by 1).
Then we could get the parse
(+ (sin (+ x x))
(- (cos (+ y y))))
-- TXR Programming Language: http://nongnu.org/txrCygnal: Cygwin Native Application Library: http://kylheku.com/cygnalMastodon: @Kazinator@mstdn.ca