for review: EDCGs and insts
Peter Schachte
pets at students.cs.mu.OZ.AU
Tue Mar 3 13:25:57 AEDT 1998
On Mon, 2 Mar 1998, Peter Nicholas MALKIN wrote:
> > > The hmode declaration is of the following form:
> > >
> > > :- hmode(Hidden, [changed(Mode, Mode),] [passed(Mode),]
> > > [consumed(Mode),] [produced(Mode)]).
> > >
> > > Where Mode is a Mercury mode.
> >
> > Shouldn't they be insts? If not, why does changed need two modes?
>
> No. I assume you mean that, say if a hidden argument of form `passed'
> was given an instantiation of INST then the mode MODE would be inferred to
> be;
>
> :- mode MODE :: INST -> INST.
>
> BUT, the hidden argument forms are not reflections of variable
> instantiations but instead of the flow of the hidden argument. Do not
> think of hidden arguments in terms of variable instantiation.
>
> It is possible to have a hidden argument of form passed with the mode out.
I can see that `consumed' with mode out would make sense, but I would
expect `passed' with mode out would be a problem when the next goal that
used that hidden argument was reached.
The modes for these uses (we need a word for the set of possibilities
{changed, passed, consumed, produced}; for now I'll call them `uses')
have to be compatible. The final inst of `produced' and `changed' has
to be compatible with the initial inst of `changed', `passed', and
`consumed', doesn't it? As far as I can see, you really only need to
specify three insts: the initial inst for `produced', the final inst
for `consumed', and the common inst for everything else. Call them
`first', `middle', and `last'. Then the modes for the introduced
arguments in the generated code would be
produced: first->middle
passed: middle->middle
changed: middle->middle, free->middle
consumed: middle->last
I also think first will usually be free, middle will usually be
ground, and last will usually be dead(whatever middle is), so I think
it makes sense to have these as defaults. Why make users write, and
maintainers read, lots of text that has little information other than
"yup, it's the usual"?
> The forms make the most sense when thought of in terms of predicate calls
> within the body of a clause:
>
> Passed indicates that the predicate call recieves a hidden argument and
> passes on the same hidden argument, to the next goal.
Do you mean by this that the call winds up with two extra arguments?
I would expect that one would be enough for `passed'.
> I probably was not very clear in my explanation of EDCGs, if this makes it
> clearer then please let me know so that I can change the documentation.
I think that was a clearer explaination; it would be a good idea to
put it in the doc.
> I suggest you have a look at the example;
>
> ~pnmalk/MERCURYDEVELOPERS/calculator.m
>
> Which replaces DCG notation with EDCG notation using a changed hidden
> argument.
FWIW, I've appended a version of the same program using my global
variable implementation. Note that this is for Prolog, so there are
no declarations; the arguments that need to be added are inferred.
I've tested this code a bit and it seems to work.
-Peter Schachte | "Under capitalism, man exploits man. Under
pets at cs.mu.OZ.AU | communism, it's just the opposite." - John
http://www.cs.mu.oz.au/~pets/ | Kenneth Galbraith
PGP key available on request |
================================================================
% File : calculator.pl
% RCS : $Id: calculator.pl,v 1.1 1998/03/03 01:35:03 pets Exp pets $
% Author : Peter Schachte, based on work by Fergus Henderson
% Origin : Tue Mar 3 11:08:56 1998
% Purpose : Example use of global variables
% Copyright: (c) 1998 University of Melbourne. \%\
%
% A simpler calculator --- parses and evaluates integer expressions using
% global variables instead of the more obvious DCG implementation.
:- module(calculator, [
main/0
]).
:- load_files(library(globals), [when(compile_time),if(changed)]).
:- use_module(library(lineio)).
:- use_module(library(ctypes)).
user:runtime_entry(start) :- main. % define main for Quintus
main ::-
get_line(Line, Term),
$line := Line,
( is_endfile(Term) ->
true
; expr(Expr),
$line = [] ->
evalexpr(Expr, Num),
write(Num),
nl,
main
; write('invalid expression'),
nl,
main
).
evalexpr(number(Num), Num).
evalexpr(plus(X,Y), Z) :- evalexpr(X,A), evalexpr(Y,B), Z is A + B.
evalexpr(minus(X,Y), Z) :- evalexpr(X,A), evalexpr(Y,B), Z is A - B.
evalexpr(times(X,Y), Z) :- evalexpr(X,A), evalexpr(Y,B), Z is A * B.
evalexpr(div(X,Y), Z) :- evalexpr(X,A), evalexpr(Y,B), Z is A // B.
% Simple recursive-descent parser.
expr(Expr) ::-
factor(Factor),
expr_tail(Factor, Expr).
expr_tail(Factor, Expr) ::-
( nextchar(C),
expr_op(C, Factor, Factor2, Factor3) ->
factor(Factor2),
expr_tail(Factor3, Expr)
; Expr = Factor
).
expr_op(0'+, Factor1, Factor2, plus(Factor1, Factor2)).
expr_op(0'-, Factor1, Factor2, minus(Factor1, Factor2)).
factor(Factor) ::-
term(Term),
factor_tail(Term, Factor).
factor_tail(Term, Factor) ::-
( nextchar(C),
factor_op(C, Term, Term2, Term3) ->
term(Term2),
factor_tail(Term3, Factor)
; Factor = Term
).
factor_op(0'*, Term1, Term2, times(Term1, Term2)).
factor_op(0'/, Term1, Term2, div(Term1, Term2)).
term(Term) ::-
( num(Num) ->
Term = number(Num)
; nextchar(0'(),
expr(Term),
nextchar(0'))
).
num(Num) ::-
nextchar(Digit),
is_digit(Digit, 10, Value0),
num_tail(Value0, Num).
num_tail(Value0, Value) ::-
( nextchar(Digit),
is_digit(Digit, 10, Value1),
Value2 is Value0*10+Value1,
num_tail(Value2, Value)
; Value = Value0
).
nextchar(C) ::-
$line = [C|Tail], % this could instead be written
$line := Tail. % $line = [C | $:=line]
More information about the developers
mailing list