Skip to content
Bill Hails edited this page Oct 28, 2024 · 9 revisions

There is now some very basic macro-like support implemented in the lambda conversion step (where we go from the abstract syntax tree to al lambda representation). It isn't a true macro facility, in fact what I'm calling "macros" are really just lazy functions, but still has some usefulness. For example in the preamble we now have:

macro AND(a, b) { if (a) { b } else { false } }

infix left 30 "and" AND;

Ans similar for the other logical operators. Note this preserves the short circuiting of the and, e.g.

let
    fn a() { print "a called"; false }
    fn b() { print "b called"; true }
    fn c() { print "c called"; true }
in
    a() and b() or c();

prints:

a called
c called

How this works is, the macro is parsed and converted to a function and a note made that it is a macro, then if it is encountered later, each of it's arguments is wrapped in a thunk before passing them to the function. Likewise within the function, any reference to a formal argument is replaced with a call to that argument. To return to the AND example above,

macro AND(a, b) { if (a) { b } else { false } }

is rewritten to

fn AND(a, b) { if (a()) { b() } else { false } }

and likewise AND(a(), b()) becomes AND(fn () {a()}, fn () {b()}) which a subsequent generic optimization step will transform to AND(a, b), because fn () { a () } can be reduced to just a. In summary

AND(a(), b()) => if (a()) { b() } else { false }

Which is just what is required.

Clone this wiki locally