/
user_guide.txt
240 lines (173 loc) · 7.01 KB
/
user_guide.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
Erlog
=====
DESCRIPTION
Erlog is a Prolog interpreter implemented in Erlang and integrated
with the Erlang runtime system. It follows the Prolog standard and the
following subset of the built-ins have been implemented:
Logic and control
call/1, ','/2, '!'/0, ';'/2, fail/0, '->'/2 (if-then),
( -> ; )(if-then-else), '\\+'/1, once/1, repeat/0, true/0
Term creation and decomposition
arg/3, copy_term/2, functor/3, '=..'/2
Clause creation and destruction
abolish/1, assert/1, asserta/1, assertz/1, retract/1, retractall/1.
Clause retrieval and information
clause/2, current_predicate/1, predicate_property/2
Term unification and comparison
'@>'/2, '@>='/2, '=='/2, '\\=='/2, '@>'/2, '@=<'/2, '='/2, '\\='/2
Arithmetic evaluation and comparison
'>'/2, '>='/2, '=:='/2, '=\\='/2, '<'/2, '=<'/2, is/2
Type testing
atom/1, atomic/1, compound/1, integer/1, float/1, number/1,
nonvar/1, var/1
Erlang interface
ecall/2
Useful but non-standard
expand_term/2, sort/2, 'C'/3, phrase/2, phrase/3
Common library
append/3, insert/3, member/2
The following arithmetic operators are implemented:
+/1, -/1, +/2, -/2, */2, //2, **/2, ///2, mod/2, abs/1,
float/1, truncate/1
Prolog terms in Erlog have a very direct representation in Erlang:
Prolog Erlang
------ ------
Structures Tuples where the first element
is the functor name (an atom)
Lists Lists
Variables Tuple {VariableName} where VariableName
is an atom
Atomic Atomic
Note there is no problem with this representation of variables as
structures without arguments, a(), are illegal in Prolog. For example
the Prolog term:
Goal = insert([1,2,3], atom, Es), call(Goal)
is represeted in Erlang by:
{',',{'=',{'Goal'},{insert,[1,2,3],atom,{'Es'}}},{call,{'Goal'}}}
The clauses of the standard append/3 defined by
append([], L, L).
append([H|T], L, [H|T1]) :-
append(T, L, T1).
are represented in Erlang by the terms:
{append,[],{'L'},{'L'}}.
{':-',{append,[{'H'}|{'T'}],{'L'},[{'H'}|{'T1'}]},
{append,{'T'},{'L'},{'T1'}}}.
Limited checking is done at run-time, basically only of input terms.
Currently this is done for the top level when clauses are added to the
database and a goal is entered.
ERLANG INTERFACE
The interface to Erlang is through the ecall/2 predicate, which
provides a back-trackable interface to Erlang. It has the form:
ecall(ErlangFunctionCall, ReturnValue)
It calls the Erlang function and unifies the result with ReturnValue.
For example
ecall(mymod:onefunc(A1, A2), Ret)
ecall(mymod:otherfunc, Ret)
where the second form calls a function of no arguments
(funcname() is illegal syntax in Prolog).
The Erlang function must return:
{succeed,Value,Continuation}
The function hass succeeded and returns Value which is unified
with the output argument of ecall/2. Continuation will be
called on backtracking to generate the next value.
{succeed_last,Value}
This is the last time the function will succeed so no
continuation is returned. It is an optimisation of returning a
continuation which will fail the next time.
fail
The function cannot generate more solutions and fails.
The first example is a simple function which calls an Erlang function
and returns the value:
efunc(Fcall) ->
%% This is what the operators will generate.
Val = case Fcall of
{':',M,F} when is_atom(M), is_atom(F) -> M:F();
{':',M,{F,A}} when is_atom(M), is_atom(F) -> M:F(A);
{':',M,T} when is_atom(M), is_tuple(T), size(T) >= 2,
is_atom(element(1, T)) ->
apply(M,element(1, T),tl(tuple_to_list(T)))
end,
{succeed_last,Val}. %Optimisation
The second example is a function which returns the keys in an Ets
table on backtracking:
ets_keys(Tab) ->
%% Solution with no look-ahead, get keys when requested.
%% This fun returns next key and itself for continuation.
F = fun (F1, Tab1, Last1) ->
case ets:next(Tab1, Last1) of
'$end_of_table' -> fail; %No more elements
Key1 -> {succeed,Key1,
fun () -> F1(F1, Tab1, Key1) end}
end
end,
case ets:first(Tab) of
'$end_of_table' -> fail; %No elements
Key -> {succeed,Key, fun () -> F(F, Tab, Key) end}
end.
The third example calls a function which returns a list and retuns
elements from this list on backtracking. I KNOW we could just return
the whole list and use member/2 to generate elements from it, but this
is more fun.
get_list(ListGen) ->
%% This is what the operators will generate.
Vals = case ListGen of
{':',M,F} when is_atom(M), is_atom(F) -> M:F();
{':',M,{F,A}} when is_atom(M), is_atom(F) -> M:F(A);
{':',M,T} when is_atom(M), is_tuple(T), size(T) >= 2,
is_atom(element(1, T)) ->
apply(M,element(1, T),tl(tuple_to_list(T)))
end,
%% This fun will return head and itself for continuation.
Fun = fun (F1, Es0) ->
case Es0 of
[E] -> {succeed_last,E}; %Optimisation
[E|Es] -> {succeed,E,fun () -> F1(F1, Es) end};
[] -> fail %No more elements
end
end,
%Call with list of values to return first element.
Fun(Fun, Vals).
For example the Erlog goal:
ecall(erlog_demo:get_list(ets:all),Tab),
ecall(erlog_demo:ets_keys(Tab),Key).
will on backtracking generate the names of all ETS tables which have
keys and their keys.
It is a great pity that the implementation of ETS loses greatly if you
want to do more complex selection of elements that just simple
matching.
DEFINTE CLAUSE GRAMMERS (DCGs)
Erlog supports DCGs. Expansion of -->/2 terms is done through the
procedure expand_term/2 which can be called explicitly and is called
automatically when consulting files. At present there is no support
for a user defined term_expansion/2 procedure. The expansion uses
'C'/3 to match tokens and phrase/3 to handle variable terms. These are
defined by:
'C'([Token|Tokens], Token, Tokens).
phrase(Term, S0, S1) :-
Term =.. L, append(L, [S0,S1], L1), Call =.. L1, Call.
Both are interpreted procedures and can be redefined as needed.
PROLOG SYNTAX
There is a simple Prolog parser, based on a Leex scanner and a
Standard Prolog parser, which will parse most Prolog terms. It
recognises all the standard operators, which have the default
priorites, but does not allow adding new operators.
Files containing Prolog predicates can be consulted, however
directives and queries in the file are ignored.
NOTES
This is only a simple interpreter without a true garbage collector so
for larger evaluations you should adopt a failure driven style.
There is no smart clause indexing on the first argument in a procedure
in Erlog.
Yes, there are no I/O predicates provided.
There is partial support for the equivalence of list notation and
'.'/2 terms, but it might go away later.
We use the standard Erlang ordering of terms which means that
variables do not have the lowest ordering as they should.
We use the Erlang definition of arithmetic operators, not standard
Prolog.
Sometimes the description of the error returned from the parser can be
a little "cryptic".
AUTHOR
Robert Virding - robert.virding@telia.com
(with thanks to Richard O'Keefe for explaining some finer points of
the Prolog standard)