Related
I have an issue with evaluating a parsetree derived from a grammar. The parsetree is derived from this pice of code:
parse(block(LEFT_CURLY, STMTS, RIGHT_CURLY)) -->
left_curly(LEFT_CURLY),
statements(STMTS),
right_curly(RIGHT_CURLY).
statements(statements) -->
[].
statements(statements(ASSIGNMENT, STMTS)) -->
assignment(ASSIGNMENT),
statements(STMTS).
assignment(assignment(ID, ASSIGN_OP, EXPR, SEMICOLON)) -->
ident(ID),
assign_op(ASSIGN_OP),
expression(EXPR),
semicolon(SEMICOLON).
expression(expression(TERM)) -->
term(TERM).
expression(expression(TERM, SUB_OP, EXPR)) -->
term(TERM),
sub_op(SUB_OP),
expression(EXPR).
expression(expression(TERM, ADD_OP, EXPR)) -->
term(TERM),
add_op(ADD_OP),
expression(EXPR).
term(term(TERM)) -->
factor(TERM).
term(term(FACTOR, MULT_OP ,TERM)) -->
factor(FACTOR),
mult_op(MULT_OP),
term(TERM).
term(term(FACTOR, DIV_OP ,TERM)) -->
factor(FACTOR),
div_op(DIV_OP),
term(TERM).
factor(factor(FACTOR)) -->
int(FACTOR).
factor(factor(FACTOR)) -->
ident(FACTOR).
factor(factor(LEFT_PAR, EXPR, RIGHT_PAR)) -->
left_par(LEFT_PAR),
expression(EXPR),
right_par(RIGHT_PAR).
assign_op(assign_op) --> [=].
mult_op(mult_op) --> [*].
add_op(add_op) --> [+].
sub_op(sub_op) --> [-].
div_op(div_op) --> [/].
left_par(left_paren) --> ['('].
right_par(right_paren) --> [')'].
left_curly(left_curly) --> ['{'].
right_curly(right_curly) --> ['}'].
semicolon(semicolon) --> [;].
ident(ident(Y)) --> [Y] , {atom(Y)}.
int(int(X)) --> [X], {integer(X)}.
The resulting parseTree from parse/3 looks like this (ex. inpt { b = 4 - 2 - 1; }:)
T = block(left_curly,statements(assignment(ident(b),assign_op,expression(term(factor(int(4))),sub_op,expression(term(factor(int(2))),sub_op,expression(term(factor(int(1)))))),semicolon),statements),right_curly)
I've had some success with evaluating the expression, and saving variable results. But I am for now evaluating "bottom up" resulting in a right associative evaluation (3), which is not how math works. 4 - 2 - 1 != 3.
An example of the evaluation which evaluates 4 - 2 - 1 to 3:
evaluate(expression(TERM, SUBOP, EXPR), OtherVariables, RESULT) :-
SUBOP = sub_op,
!,
evaluate(TERM, OtherVariables, LHSResult),
evaluate(EXPR, OtherVariables, RHSResult),
RESULT is LHSResult - RHSResult.
evaluate(term(FACTOR), OtherVariables, RESULT) :-
evaluate(FACTOR, RESULT).
evaluate(factor(INT), RESULT) :-
evaluate(INT, RESULT).
evaluate(int(X), X).
Is there anyone who could give me a hint on how to move forward with this issue? I have been able to do this in Java, but my Prolog knowledge is not as good. Unfortunately I am not allowed to change the grammar or the parsing.
Your parse tree is very explicit about whether the input contains parentheses. For example:
?- phrase(expression(Expr), [4, -, 2, -, 1]).
Expr = expression(term(factor(int(4))), sub_op, expression(term(factor(int(2))), sub_op, expression(term(factor(int(1)))))) .
?- phrase(expression(Expr), [4, -, '(', 2, -, 1, ')']).
Expr = expression(term(factor(int(4))), sub_op, expression(term(factor(left_paren, expression(term(factor(int(2))), sub_op, expression(term(factor(int(1))))), right_paren)))) .
This is good, because we can tell where the user really wanted us to evaluate 4 - (2 - 1), or whether the input was 4 - 2 - 1 and should really be interpreted as (4 - 2) - 1.
By far the simplest way of doing this is by thinking about the problem not as "left-associative evaluation of a right-associative tree", but about the two separate problems of "evaluation of a tree" and "getting a left-associative tree into right-associative form". That is, don't try to be clever inside your evaluate predicate, but first reassociate the tree and then evaluate that new tree.
A sketch:
op_associativity(add_op, right).
op_associativity(sub_op, left).
op_associativity(mult_op, right).
op_associativity(div_op, left).
expression_reassociated(expression(X, Op, expression(Y, Op, Z)),
expression(expression(X, Op, Y), Op, Z)) :-
op_associativity(Op, left),
!.
expression_reassociated(Expression, Expression).
With a slightly massaged version of your evaluate, this already gives:
?- phrase(expression(Expr), [4, -, 2, -, 1]), expression_reassociated(Expr, Reassociated), evaluate(Reassociated, Result).
Expr = expression(term(factor(int(4))), sub_op, expression(term(factor(int(2))), sub_op, expression(term(factor(int(1)))))),
Reassociated = expression(expression(term(factor(int(4))), sub_op, term(factor(int(2)))), sub_op, expression(term(factor(int(1))))),
Result = 1 ;
false.
Note that expression_reassociated needs more work: It must reassociate sub-expressions as well. Once you have a complete working solution, you can think about reassociating "on the fly" during evaluation without building the intermediate tree. But it's probably not worth it, unless explicitly requested by your professor.
All that said, it would really be best if the DCG produced the correctly associated parse tree from the start, but I understand that you have constraints.
As a follow up to this question which poses the problem
Return count of items in a list but if two identical items are next to each other then don't increment the count.
This code is the closest I came to solving this with DCG and semicontext.
lookahead(C),[C] -->
[C].
% empty list
% No lookahead needed because last item in list.
count_dcg(N,N) --> [].
% single item in list
% No lookahead needed because only one in list.
count_dcg(N0,N) -->
[_],
\+ [_],
{ N is N0 + 1 }.
% Lookahead needed because two items in list and
% only want to remove first item.
count_dcg(N0,N) -->
[C1],
lookahead(C2),
{ C1 == C2 },
count_dcg(N0,N).
% Lookahead needed because two items in list and
% only want to remove first item.
count_dcg(N0,N) -->
[C1],
lookahead(C2),
{
C1 \== C2,
N1 is N0 + 1
},
count_dcg(N1,N).
count(L,N) :-
DCG = count_dcg(0,N),
phrase(DCG,L).
What is the correct way to solve the problem using DCG with semicontext on the clause head?
Would like to know if the variation with the semicontext on the clause head is possible or not. If possible then working example code is desired, if not possible then an explanation is desired.
I think this is using semi context notation correctly. I am counting using 0,s(0),...
% Constraint Logic Programming
:- use_module(library(dif)). % Sound inequality
:- use_module(library(clpfd)). % Finite domain constraints
list([]) --> [].
list([L|Ls]) --> [L], list(Ls).
state(S), [state(S)] --> [state(S)].
state(S, s(S)), [state(s(S))] --> [state(S)].
keep_state(S,I),[state(S)] --> [state(S)],[I].
end_state(S) -->[state(S)],[].
lookahead(C),[S,C] -->
[S,C].
count_dcg(S,S) -->
state(S), %might not need this
end_state(S).
/* Can be used get the length of a list
count_dcg(S,S2) -->
state(S,S1),
keep_state(S1,_),
count_dcg(S1,S2),
{}.
*/
%last item.
count_dcg(S,S1) -->
state(S,S1),
keep_state(S1,_C),
list(R),
{R = [state(_)]}.
%Two the same dont increase state
count_dcg(S,S1) -->
state(S), %might not need this
keep_state(S,C1),
lookahead(C1),
count_dcg(S,S1).
%Two different increase state
count_dcg(S,S2) -->
state(S,S1),
keep_state(S1,C1),
lookahead(C2),
{
dif(C1,C2)
},
count_dcg(S1,S2).
count(L,S) :-
phrase(count_dcg(0,S),[state(0)|L]).
This does not work as well as I hoped for cases like:
65 ?- count([a,b,X,c],L).
X = b,
L = s(s(s(0))) ;
;
X = c,
L = s(s(s(0))) .
You can convert peano with:
natsx_int(0, 0).
natsx_int(s(N), I1) :-
I1 #> 0,
I2 #= I1 - 1,
natsx_int(N, I2).
or you can change the state predicates:
state(S), [state(S)] --> [state(S)].
state(S, S2), [state(S2)] --> [state(S)],{S2#=S+1}.
How about:
:-use_module(library(clpfd)).
list([]) --> [].
list([L|Ls]) --> [L], list(Ls).
lookahead(C),[C] -->
[C].
count_dcg(N,N) --> [].
count_dcg(N0,N) --> %last item.
[_],
list(R),
{R = [], N #=N0+1}.
count_dcg(N0,N) -->
[C1],
lookahead(C1),
count_dcg(N0,N).
count_dcg(N0,N) -->
[C1],
lookahead(C2),
{
dif(C1,C2),
N1 #= N0 + 1
},
count_dcg(N1,N).
count(L,N) :-
phrase(count_dcg(0,N),L).
I am trying to convert a Prolog predicate into DCG code. Even if I am familiar with grammar langage I have some troubles to understand how DCG works with lists and how I am supposed to use it.
Actually, this is my predicate :
cleanList([], []).
cleanList([H|L], [H|LL]) :-
number(H),
cleanList(L, LL),
!.
cleanList([_|L], LL) :-
cleanList(L, LL).
It is a simple predicate which removes non-numeric elements.
I would like to have the same behaviour writes in DCG.
I tried something like that (which does not work obviously) :
cleanList([]) --> [].
cleanList([H]) --> {number(H)}.
cleanList([H|T]) --> [H|T], {number(H)}, cleanList(T).
Is it possible to explain me what is wrong or what is missing ?
Thank you !
The purpose of DCG notation is exactly to hide, or better, make implicit, the tokens list. So, your code should look like
cleanList([]) --> [].
cleanList([H|T]) --> [H], {number(H)}, cleanList(T).
cleanList(L) --> [H], {\+number(H)}, cleanList(L).
that can be made more efficient:
cleanList([]) --> [].
cleanList([H|T]) --> [H], {number(H)}, !, cleanList(T).
cleanList(L) --> [_], cleanList(L).
A style note: Prologgers do prefers to avoid camels :)
clean_list([]) --> [].
etc...
Also, I would prefer more compact code:
clean_list([]) --> [].
clean_list(R) --> [H], {number(H) -> R = [H|T] ; R = T}, clean_list(T).
The following meta-predicate is often useful. Note that it cannot be called maplist//2, because its expansion would collide with maplist/4.
maplistDCG(_P_2, []) -->
[].
maplistDCG(P_2, [A|As]) -->
{call(P_2, A, B)},
[B],
maplistDCG(P_2, As).
There are several issues here. Certainly the name. But also the terminal [B]: should it be explicitly disconnected from the connecting goal?
Without above definition, one has to write either one of the following - both having serious termination issues.
maplistDCG1(P_2, As) -->
{maplist(P_2, As, Bs)},
seq(Bs).
maplistDCG2(P_2, As) -->
seq(Bs),
{maplist(P_2, As, Bs)}.
seq([]) -->
[].
seq([E|Es]) -->
[E],
seq(Es).
Does {call(P_2,A,B)}, [B] have advantages over [B], {call(P_2,A,B)}?
(And, if so, shouldn't maplist/3 get something like that, too?)
Let's put corresponding dcg and non-dcg variants side-by-side1:
dcg [B],{call(P_2,A,B)} and non-dcg Bs0 = [B|Bs], call(P_2,A,B)
maplistDCG(_,[]) --> []. % maplist(_,[],[]).
maplistDCG(P_2,[A|As]) --> % maplist(P_2,[A|As],Bs0) :-
[B], % Bs0 = [B|Bs],
{call(P_2,A,B)}, % call(P_2,A,B),
maplistDCG(P_2,As). % maplist(P_2,As,Bs).
dcg {call(P_2,A,B)},[B] and non-dcg call(P_2,A,B), Bs0 = [B|Bs]
maplistDCG(_,[]) --> []. % maplist(_,[],[]).
maplistDCG(P_2,[A|As]) --> % maplist(P_2,[A|As],Bs0) :-
{call(P_2,A,B)}, % call(P_2,A,B),
[B], % Bs0 = [B|Bs],
maplistDCG(P_2,As). % maplist(P_2,As,Bs).
Above, we highlighted the goal ordering in use now:
by maplist/3, as defined in this answer on SO and in the Prolog prologue
by maplistDCG//2, as defined in this question by the OP
If we consider that ...
... termination properties need to be taken into account and ...
... dcg and non-dcg variants should better behave the same2 ...
... we find that the variable should not be explicitly disconnected from the connecting goal. The natural DCG analogue of maplist/3 is maplistDCG//2 defined as follows:
maplistDCG(_,[]) -->
[].
maplistDCG(P_2,[A|As]) -->
[B],
{call(P_2,A,B)},
maplistDCG(P_2,As).
Footnote 1: To emphasize commonalities, we adapted variable names, code layout, and made some unifications explicit.
Footnote 2: ... unless we have really good reasons for their divergent behaviour ...
I need some help in prolog, which is pretty new to me. I have to design a small arithmetic computer. The expression to be evaluated will be represented as a list for example:
?-evaluate([2,+,4,*,5,+,1,*,2,*,3],R).
I am trying to do this by designing two predicates one called parse to transform my list for example:
?-parse([1,+,2,*,3],PF).
PF=[+,1,[*,2,3]]
and another one to evaluate the new expression.
?-evpf([+,1,[*,2,3]],R).
R=7
I have problems with the first part, can anyone help my with the code?
Parsing (= converting a list to an abstract syntax tree) is easy with DCGs:
list_ast(Ls, AST) :- phrase(expression(AST), Ls).
expression(E) --> term(T), expression_r(T, E).
expression_r(E0, E) --> [+], term(T), expression_r(E0+T, E).
expression_r(E0, E) --> [-], term(T), expression_r(E0-T, E).
expression_r(E, E) --> [].
term(T) --> power(P), term_r(P, T).
term_r(T0, T) --> [*], power(P), term_r(T0*P, T).
term_r(T0, T) --> [/], power(P), term_r(T0/P, T).
term_r(T, T) --> [].
power(P) --> factor(F), power_r(F, P).
power_r(P0, P0^P) --> [^], factor(P1), power_r(P1, P).
power_r(P, P) --> [].
factor(N) --> [N], { number(N) }.
factor(E) --> ['('], expression(E), [')'].
To actually evaluate the expression, you can then use the built-in predicate is/2. Sample query:
?- list_ast([2,+,4,+,5,+,1,+,2,*,3], Ast), V is Ast.
Ast = 2+4+5+1+2*3,
V = 18 ;
false.