Re: Attempt at approximating Lisp's "macroexpansion"
Alan Baljeu <[email protected]>
| Newsgroups | gmane.comp.ai.prolog.swi |
|---|---|
| Message-ID | <[email protected]> |
I think you're missing something in your understanding of Prolog.
The Prolog compiler will read one line at a time from a source file and then compile that line. The first step of compiling is read/N. If the line can't be read and converted to a term, we have an error. term_expansion happens to the term after it's read.
So when you see this:
?- expand_term(:- record arec(afield:atom=''),Res).
ERROR: Syntax error: Operator expected
ERROR: system:expand_term(:- record
ERROR: ** here **
ERROR: arec(afield:atom=''),Res) .
It's because record is not a defined operator. And that's because you have not imported the module that defines it.
20 ?- [library(record)]
21 ?- expand_term(:- record arec(afield:atom=''),Res).
Res = [ (:-record'<compiled>'), default_arec(arec('')), arec_afield(arec(_G620493), _G620493), arec_data(afield, arec(_G620510), _G620510), (set_afield_of_arec(_G620558, arec(_G620555), arec(_G620558)):-must_be(atom, _G620558)), (set_afield_of_arec(_G620558, _G620593):-must_be(atom, _G620558), setarg(1, _G620593, _G620558)), (nb_set_afield_of_arec(_G620558, _G620593):-must_be(..., ...), nb_setarg(..., ..., ...)), (set_arec_field(..., ..., ...):-must_be(..., ...)), (... :- ...)|...].
Alan Baljeu
----- Original Message -----
From: Ian Tegebo <[email protected]>
To: "[email protected]" <[email protected]>
Cc:
Sent: Wednesday, July 17, 2013 12:30:00 AM
Subject: [SWIPL] Attempt at approximating Lisp's "macroexpansion"
Firstly, this email is longer than I'd like. I ask and answer some of my
own questions. I didn't edit as much as I could because I think it helps
explain my confusion and may be of some use to those seeking to improve
docs or otherwise teach others.
----
I was trying to use expand_term/2 like one might use "macroexpand" in a
Lisp when I ran into trouble. Consider expanding an example using the
library(record):
?- expand_term(:- record arec(afield:atom=''),Res).
ERROR: Syntax error: Operator expected
ERROR: system:expand_term(:- record
ERROR: ** here **
ERROR: arec(afield:atom=''),Res) .
After much puzzling and eventually re-reading library/record.pl, I saw that
the term_expansion definition didn't refer to "record" as an operator, but
as a predicate. So,
?- expand_term(:- record(arec(afield:atom='')),Res).
Res = [default_arec(arec('')), ...
And then I got what I expected. But this left me with two questions:
1) At what point does "record" go from an operator to a predicate during
compilation and/or execution as a directive?
2) What's the deal with the "system" module qualification in the definition
of term_expansion? Here's the definition:
system:term_expansion((:- record(Record)), Clauses) :-
compile_records(Record, Clauses).
The documentation for term_expansion doesn't mention the "system" module:
http://www.swi-prolog.org/pldoc/man?predicate=term_expansion/2
Assuming something special is going on, should it?
Also, the issue surrounding "record" as operator is unsettling; at the
top-level, I can query current_op while within my module to see that
"record" is in fact an operator. Operator handling seems to happen before
expand_term. If so, is there another predicate I should use before
expanding it? In the Lisp world, I'd have thought "read" would do the job.
After a bit of manual reading,
arec_mod ?- open_chars_stream(":- record
arec(afield:atom='').",S),read(S,T),expand_term(T,Res).
ERROR: Stream <stream>(0x38db590):0:1 Syntax error: Operator expected
More manual reading revealed read_term/3 accepts a module option but
read_clause/3 is supposed to set it automatically based on the "current
compilation module". Sadly, it didn't work whereas explicitly handing the
module over to read_term/3 does:
arec_mod ?- open_chars_stream(":- record
arec(afield:atom='').",S),read_clause(S,T,[]),expand_term(T,Res).
ERROR: Stream <stream>(0x3b702f0):0:1 Syntax error: Operator expected
arec_mod ?- open_chars_stream(":- record
arec(afield:atom='').",S),read_term(S,T,[module(arec_mod)]),expand_term(T,Res).
(this one worked as expected)
Okay, let's follow the manual to see what the "current compilation module"
is supposed to be:
arec_mod ?- prolog_load_context(module,V).
V = user.
This is surprising because I'd already done "?- module(arec_mod)". Looking
around for more "context", I found,
arec_mod: ?- context_module(M).
M = arec_mod.
At this point, I have something that seems to work for my use case. That
said, I think I've only scratched the surface of all the issues at play.
Here's what I have so far:
:- meta_predicate expand_chars_term(:,-).
expand_chars_term(Mod:Chars,Term) :- expand_chars_term(Chars,Mod,Term).
expand_chars_term(Chars,Module,Term) :-
open_chars_stream(Chars,S),
read_term(S,T,[module(Module)]),
expand_term(T,Term).
Where the following is my pretty-printer:
:- meta_predicate portray_expansion(:).
portray_expansion(Mod:Chars) :-
expand_chars_term(Chars,Mod,Term),
is_list(Term) -> maplist(portray_clause,Term)
; portray_clause(Term).
Now I can see a pretty-printed expansion:
arec_mod ?- portray_expansion(":- record arec(afield:atom='').").
I'm assuming the caller is at the top-level, "inside" the module where
predicates/operators that may occur in Chars have already been loaded. I
can imagine a few improvements, but I'd like some feedback given that I'm
still a Prolog newbie.
Thanks,
--
Ian Tegebo
-------------- next part --------------
HTML attachment scrubbed and removed
_______________________________________________
SWI-Prolog mailing list
[email protected]
https://lists.iai.uni-bonn.de/mailman/listinfo.cgi/swi-prolog