How to do a parser in Prolog?

放肆的年华 提交于 2019-12-05 18:33:00

Here's an enhancement of your parser as written which can get you started. It's an elaboration of the notions that @CapelliC indicated.

parser([]) --> [].
parser(Tree) --> assign(Tree).

assign([assignment, ident(X), '=', Exp]) --> id(X), [=], expr(Exp), [;].

id(X) --> [X], { atom(X) }.

expr([expression, Term]) --> term(Term).
expr([expression, Term, Op, Exp]) --> term(Term), add_sub(Op), expr(Exp).

term([term, F]) --> factor(F).
term([term, F, Op, Term]) --> factor(F), mul_div(Op), term(Term).

factor([factor, int(N)]) --> num(N).
factor([factor, Exp]) --> ['('], expr(Exp), [')'].

add_sub(Op) --> [Op], { memberchk(Op, ['+', '-']) }.
mul_div(Op) --> [Op], { memberchk(Op, ['*', '/']) }.

num(N) --> [N], { number(N) }.

I might have a couple of niggles in here, but the key elements I've added to your code are:

  • Replaced digit with num which accepts any Prolog term N for which number(N) is true
  • Used atom(X) to identify a valid identifier
  • Added an argument to hold the result of parsing the given expression item

As an example:

| ?- phrase(parser(Tree), [a, =, 3, +, '(', 6, *, 11, ')', ;]).

Tree = [assignment,ident(a),=,[expression,[term,[factor,int(3)]],+,[expression,[term,[factor,[expression,[term,[factor,int(6)],*,[term,[factor,int(11)]]]]]]]]] ? ;

This may not be an ideal representation of the parse tree. It may need some adjustment per your needs, which you can do by modifying what I've shown a little. And then you can write a predicate which formats the parse tree as you like.

You could also consider, instead of a list structure, an embedded Prolog term structure as follows:

parser([]) --> [].
parser(Tree) --> assign(Tree).

assign(assignment(ident(X), '=', Exp)) --> id(X), [=], expr(Exp), [;].

id(X) --> [X], { atom(X) }.

expr(expression(Term)) --> term(Term).
expr(expression(Term, Op, Exp)) --> term(Term), add_sub(Op), expr(Exp).

term(term(F)) --> factor(F).
term(term(F, Op, Term)) --> factor(F), mul_div(Op), term(Term).

factor(factor(int(N))) --> num(N).
factor(factor(Exp)) --> ['('], expr(Exp), [')'].

add_sub(Op) --> [Op], { memberchk(Op, ['+', '-']) }.
mul_div(Op) --> [Op], { memberchk(Op, ['*', '/']) }.

num(N) --> [N], { number(N) }.

Which results in something like this:

| ?- phrase(parser(T), [a, =, 3, +, '(', 6, *, 11, ')', ;]).

T = assignment(ident(a),=,expression(term(factor(int(3))),+,expression(term(factor(expression(term(factor(int(6)),*,term(factor(int(11)))))))))) ? ;

A recursive rule for id//0, made a bit more generic:

id --> [First], {char_type(First,lower)}, id ; [].

Building the tree could be done 'by hand', augmenting each non terminal with the proper term, like

...
assign(assign(Id, Expr)) --> id(Id), [=], expr(Expr), [;].
...

id//0 could become id//1

id(id([First|Rest])) --> [First], {memberchk(First, [a,b])}, id(Rest) ; [], {Rest=[]}.

If you're going to code such parsers frequently, a rewrite rule can be easily implemented...

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!