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

Re: Macro hell



I went through this once writing DUIM's 'define frame'.  Perhaps
you should steal it, since it's doing roughly the same sort of thing.

"Carl Gay" <carlgay@attbi.com> wrote in message
301a9102.0209171917.688bef93@posting.google.com">news:301a9102.0209171917.688bef93@posting.google.com...
> Aaargh!  Let's just get this out of the way up front:  Dylan macros are
> hell compared to Common Lisp macros.  Every time I try to write a
non-trivial
> Dylan macro I end up spending inordinate amounts of time and then end up
> compromising for some syntax that's not quite what I want but is pretty
close,
> because it makes the compiler happy.  What am I missing?  Do I have to go
> study that horrific BNF?  I sure hope not.
>
> Here's the example that I'm currently struggling with.  If anyone can
> show me how to fix it that would be wonderful.  Bonus points for figuring
> out how my thinking about macros is broken such that I couldn't figure
> it out myself.
>
> I want to be able to define a class, some of whos slots map to columns
> in a database table:
>
> define record <account> (<database-record>)
>   database slot username :: <string>, column-name: "username";
>   database slot password :: <string>, column-name: "password";
>   slot foo;  // non-database slot
> end;
>
> My basic approach is to have this expand into two other macro
> calls, each passing along all the arguments:
>
> define record-class <account> (<database-record>)
>   database slot username :: <string>, column-name: "username";
>   database slot password :: <string>, column-name: "password";
>   slot foo;  // non-database slot
> end;
>
> define record-slots <account> (<database-record>)
>   database slot username :: <string>, column-name: "username";
>   database slot password :: <string>, column-name: "password";
>   slot foo;  // non-database slot
> end;
>
> "define record-class" then expands to a normal "define class"
> form and "define record-slots" expands to a bunch of code for
> storing information about the mapping between class slots and
> database columns.  Therefore, record-class-definer needs to strip
> out all the stuff unacceptable to class-definer and record-slots-definer
> needs to strip out anything it doesn't need.
>
> Is that basic approach reasonable?  Better way?
>
> I actually managed to get the whole thing working except
> for one little problem: the comma after the slot name and type.
> For some reason i'm unable to use my little "keyvals" trick
> (see *** comment in macros below) to make this comma optional
> in record-slots-definer.  I get a warning about invalid syntax
> in the "slot-name" expansion.
> It seems to work ok in record-class-definer, however.
>
> Any suggestions?
>
> Thanks.
> -Carl
>
>
> define macro record-definer
>   { define ?modifiers:* record ?record-name:name (?superclasses)
>         ?slot-specs:*
>     end }
>   =>
>   { define ?modifiers record-class ?record-name (?superclasses)
>       ?slot-specs
>     end;
>     define record-slots ?record-name (?superclasses)
>       ?slot-specs
>     end }
> superclasses:
>   { } => { }
>   { ?superclass:expression, ... } => { ?superclass, ... }
> end;
>
> // This basically strips out everything that "define class" doesn't
handle.
> //
> define macro record-class-definer
>   { define ?modifiers:* record-class ?record-name:name (?superclasses:*)
>         ?slot-specs
>     end }
>   =>
>   { define ?modifiers class ?record-name (?superclasses) ?slot-specs end }
> slot-specs:
>   { } => { }
>   { ?slot-spec ; ... } => { ?slot-spec ; ... }
> slot-spec:
>   { ?modifiers slot ?slot-name:variable ?keyvals }
>   =>
>   { ?modifiers slot ?slot-name ?keyvals }
> modifiers:
>   { } => { }
>   { database ... } => { ... }                        // strip
>   { ?modifier:name ... } => { ?modifier ... }
> keyvals:
>   { } => { }
>   { , ?keys-and-vals } => { ?keys-and-vals }         // remove leading
comma
> keys-and-vals:
>   { } => { }
>   { column-name:  ?foo:expression, ... } => { ... }  // strip
>   { column-index: ?foo:expression, ... } => { ... }  // strip
>   { ?key:token ?foo:expression, ... } => { ?key ?foo, ... }
> end macro record-class-definer;
>
>
> define macro record-slots-definer
>   { define ?modifiers:* record-slots ?record-name:name (?superclasses:*)
>         ?slot-specs
>     end }
>   =>
>   { begin
>       // This needs to be referenced from auxiliary rule sets below.
>       let _record-name = ?record-name;
>       // Associate all the slot descriptors of the superclasses with
>       // this record class.
>       for (_class in vector(?superclasses))
>         for (_desc in slot-descriptors(_class))
>           add-slot-descriptor(_record-name, _desc);
>         end;
>       end;
>       ?slot-specs
>     end }
> slot-specs:
>   { } => { }
>   { ?slot-spec ; ... } => { ?slot-spec ; ... }
> slot-spec:
>   // ********* had to insert comma below, which makes it required.
>   { ?modifiers slot ?slot-name , ?keys-and-vals }
>   =>
>   { begin
>       let _database? = ?modifiers;
>       let (_name, _type) = ?slot-name;
>       let _args = vector(?keys-and-vals);
>       add-slot-descriptor(_record-name, _name, _type, _database, _args);
>     end }
> modifiers:
>   { } => { #f } // didn't find the 'database' modifier
>   { database ... } => { #t }
>   { ?modifier:name ... } => { ?modifier ... }
> slot-name:
>   { ?:name } => { values(?name, <object>) }
>   { ?:name :: ?type:expression } => { values(?name, ?type) }
> /*
> keyvals:
>   { } => { }
>   { , ?keys-and-vals } => { ?keys-and-vals }         // remove leading
comma
> */
> keys-and-vals:
>   { } => { }
>   { column-name:  ?a:expression, ... } => { column-name: ?a, ... }
>   { column-index: ?b:expression, ... } => { column-index: ?b, ... }
>   { init-keyword: ?c:expression, ... } => { init-keyword: ?c, ... }
>   { ?key:token ?d:expression, ... } => { ... }  // remove everything else
> end macro record-slots-definer;