Egil Möller:
> I was thinking about the {}-infix notation and implementation, and realized
> a rather sad side-effect - the actual parse-tree, what is fed to eval, does
> not resemble the input any longer, as it does for all other syntax sugar
> (quoting and so on).
> My suggestion is to simplify the reader-macro, so that it only rewrites {1 *
> 2 + 4 * {5 + 6}} into (nfx 1 * 2 + 4 (nfx 5 + 6)) and do the rest with an
> ordinary macro, nfx.
I also prefer seeing the actual parse-tree, but I have the exact opposite
solution: Keep the rules, and I plan to _avoid_ (in most cases) writing code
that invokes "nfx". In other words, I plan to do this:
{3 + {4 * 5}}
so that it will auto-map into (+ 3 (* 4 5)), and avoid doing this:
{3 + 4 * 5}
This means that the actual parse tree DOES represent the input - every new list
is marked with either (...) or {...}. The only thing that's unusual is the
swapping of the argument order.... but hey, that's the WHOLE POINT of having
infix, to have the infix operator NOT in the first position. I've done some
experimentation and find it's actually quite easy to get used to.
There's also a pragmatic reason: if the expression is hidden inside an
execution time/compile time macro and/or special form, the expression _WILL_ be
correct. A reader can do that... a macro like "nfx" cannot.
I actually originally planned to make infix processing produce an ERROR in the
case where it produces (nfx ...). But Gloria's comments made me realize that
at little cost I could do much better - instead of an error, hand it off to a
macro and let the user decide how to handle it. It isn't any more complex to
implement the reader, and it's much more flexible.
> It would be easy to add a printing hook that rewrites anything (nfx ...) into
> {...}, just like it works for quotes.
True, but I think it's reasonable that a _printing_ hook have some "smarts"
that try to make it look pretty. E.G., if a function name is all-punctuation,
"and", "or", or "xor", use the {...} format. So a printing hook can see (+ 4
5) and print {4 + 5} just as easily. The printing hook doesn't need to print
everything EXACTLY how it was input; (quote x) is printed as 'x by many
printers. But I think it needs to be very clear and unambiguous.
I think I'd want (nfx ....) to be printed as (nfx ....) - or better yet,
nfx(....). If it's an argument to a macro, that additional (nfx ....) might
make a key difference, and I DEFINITELY don't want to create debugging problems
by hiding that information. Again, I plan to use the "simple infix" (swapping)
as the NORMAL use of {...}, and only rarely use (nfx ....).
> One way to handle operator precedence would be to call a special function for
> each pair of used operators: {1 * 2 + 4 * {5 + 6}} (precedence_*_+ 1 2 4) ->
> * (precedence_+_* 2 4 11) -> *
You don't want to do it QUITE that way. In any case, precedence-handling is
old stuff, techniques are well-known. You can give each operator a precedence
value, or state orderings for pairs.
But I think that precedence-handling is a suboptimal idea in a reader. It's
not that it can't be done... it CAN be done easily enough, you just configure a
table. The problem is that what an expression means depends on the particular
state of this table. You have to be REAL careful of the exact state of this
table; an expression's interpretation could DIFFER after each change. When
merging people's code this could be especially nasty.
If you're dealing with data that express different sublanguages (this is my
program, this over there is input that is a special-purpose language, etc.),
then it gets worse - they probably have different operators, with possibly
different precedence. Uh oh. And what's more, there are endless ways of
handling precedence, no one can seem to agree. It's also easy to implement it
incorrectly.
I think a basic, SIMPLE rule for reading MOST infix expressions - and then
handling off to a macro for more complex cases - is the better approach. I
wrote the Common Lisp implementation of curly braces in little time, and I
believe it's rock-solid. After all, it's quite simple! Being _certain_ that
you know what the code will do has its own advantages. :-)
--- David A. Wheeler