[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;