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
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.