MOO-cows Mailing List Archive

[Prev][Next][Index][Thread]

Macros! (was Re: [wish list]: perl stuff)



> It'd also be nice to have 
>
>        "blah" if (foo);
>

so um... what's wrong with

   foo ? "blah" | 0;

arguably the |0 part is annoying, but I'd say it's the most readable of
the bunch.

I will, in fact, admit to being a fan of WHEN and UNLESS, combined, of
course, with the stipulation that all IF statements be required to have
ELSE clauses.  It would even be trivial to modify the unparser to spit
things out this way.  Note that I'm not actually proposing this since

(1) What I happen to like syntaxwise is not necessarily what Quinn
likes, etc.
    There's just too much room here for religious wars.

(2) Much of this is do-able (albeit very painfully) in-db, so it seems
that a better
    answer is for the server to provide tools so that this may be done
much more easily.

Really, what everything boils down to IMHO is having a macro system.
With a macro system, if there's some syntactic construct you really
like, you just write a macro for it and away you go.  

To be sure, this can and has been horribly abused --- I only need look
at some of the nasty
things people have done with CPP (the C preprocessor), crippled as it
is, that I'm wary of proposing a macro system for MOO.  Once you have
macros in the world, you have the potential for linguistic chaos; just
*forget* about being able to read other people's code.

On the other hand, people are now writing unreadable code anyway, and
one can legitimately ask if the spectre of some nutcase writing even
*worse* code is worth the price of keeping the handcuffs on the rest of
us.  One can argue that people wouldn't do nasty things with CPP if C
had a decent macro system.  Likewise, if MOO had a decent macro system,
various constructs (e.g., security checks) that currently need to be
written in a convoluted manner, things people tend to omit or do wrong,
would become much less painful.

In some sense, this argument is moot, since it is indeed ALREADY
possible to do macros (or any kind of funky syntax) entirely in-db.
Here's how:

(1) set some convention for how to derive/print the pre-macro-expanded
version of a given verb given its expanded version.  Possibilities here
include annotating the expanded verb code with comments indicating where
macro-expansions occurred, or, at worst, putting the entire
pre-macro-expansion version of the verb in a big comment at the end.

(2) write a macro-expander.  This is something that takes
pre-macro-expanded code (i.e., code containing instances of WHEN,
UNLESS, REPEAT/UNTIL and other macro invocations), as might be held in
an editing buffer, finds all macro invocations and expands them, putting
in the required comments/annotations.

(3) take ALL of the bozos who might be inclined to edit
post-macro-expanded verbs directly with set_verb_code() and kill them.

For example, your editing buffer might have

   @program foo:bar this none this
   MUST_BE_OWNER() || raise(E_PERM);
   hell = random:stuff();
   REPEAT
     hell = random:more_stuff();
   UNTIL (hell.frozen);
   ...

@program would takes this, doing $macro:expand($read_lines())),
producing something like

   "#@EXPANDER VERSION 1.5";
   "#@EXPRMACRO MUST_BE_OWNER 1 61";
   ($perm_utils:controls(caller_perms(),this)
    || caller != this)
    || raise(E_PERM);
   hell = random:stuff();
   "#@STMTMACRO REPEAT";
   WHILE (1)
     hell = random:more_stuff();
     IF (hell.frozen)
       BREAK;
     ENDIF
   ENDWHILE
   ...

which would then be stored as the verb code on foo:bar.  @list, in turn,
would know how to render this as what was originally in your editing
buffer.

The only problem is that it's currently REALLY HARD to write a fast
macro-expander.  JHM has done some things along these lines with its
hack to put in //-style comments and such, but for true syntax macros,
you essentially have to duplicate in-db what the builtin MOO parser
does.  This is, in fact, possible though incredibly painful/slow, as
anyone who ever used #5803:@prettylist on LambdaMOO can attest.

Which brings up MY wish list w.r.t. syntax:

(1) verb_code_ast    (OBJECT, VERBNAME)      => ast
    set_verb_code_ast(OBJECT, VERBNAME, AST)

which would traffic in the abstract syntax tree (AST) version of the
verb code rather than the flat-text list-of-strings version.  The idea
is to be able to handle the verb code in program-manageable form rather
than user-readable form.  E.g., if

    verb_code(#37,"foo")
     => {"IF (a < b)","raise(E_RANGE);","ENDIF")

then

    verb_code_ast(#37,"foo")
     => {{"IF",{"<",{"VAR","a"},{"VAR","b"}},
               {{"EXPR",{"BICALL","raise",{E_RANGE}}}},{}}})

though I'd have token-numbers in place of "IF","<","EXPR","VAR","BICALL"
to save memory/time.  Note that the AST form doesn't have to be
user-friendly.

Just this much would open the possibility of decent pretty-printers.

Also necessary for a macro system would be

(2) parse(LIST_OF_STRINGS, TEMPLATES) => ast
    unparse(AST, FORMATTERS) => list of strings

where the lists of strings are (or are expected to be) verb code in the
usual format.  The idea here is that

  verb_code(obj,vb)
   == unparse(verb_code_AST(obj,vb),{})

  set_verb_code(obj,vb,text)
   == set_verb_code_AST(obj,vb,parse(text,{}))

Admittedly, unparse(), like toliteral(), could be written relatively
cheaply in-db, but there might yet be some win to having it in the
server.

Meanwhile, the point of the 2nd arguments to parse()/unparse() is to
have a place to allow the parser to handle macro keywords and for the
unparser to call user-defined formatting routines.  TEMPLATES, in
particular, would be a list of elements of the form

  {keyword, kind, format}

that basically tell the parser what keywords to look out for and what
parsing rules to invoke when it sees them, e.g.,

  {"REPEAT", STMT, {STMTS, "UNTIL", EXPR, ";"}}

which says that on seeing "REPEAT" the parser should
(*) bomb if it's not expecting a statement at this point,
and otherwise
(*) suck up the "REPEAT", a sequence of statements, an "UNTIL", an
expression and a semicolon.  The ultimate result of the parse will be an
AST in which the particular subtree corresponding to the REPEAT
statement is something like {"REPEAT", {sucked_stmts...}, sucked_expr}.

(3) Oh yeah, I want backquote, too.

This is so that when writing the code to actually *produce* the
expansion for the REPEAT/UNTIL, I can write something readable, like

  expansion = &STMT_BQ
    WHILE (1)
      &@stmts
      IF (&,expr)
        BREAK;
      ENDIF
    ENDWHILE
  &END_STMT_BQ;

rather than be forced to write something horrible, like

  expansion = 
   $ast:make_while_stmt(
     $ast:make_const_expr(1),
     {@stmts,
      $ast:make_if_stmt(expr,
                   {$ast:make_break_stmt(1)},
                   {})});




Home | Subject Index | Thread Index