可能无限递归循环:用DCG解析规则

问题描述

我正在努力为命令式语言开发一个小型解析器,但我坚持不使用递归语法的定义。

我阅读了关于解决方案的 this post,但我无法意识到它,我将终端移动到我的声明之上,但我对用于表达相同内容的不同语法感到困惑意义。

我的输入令牌如下[word(x),punct(:),punct(=),number(1)]

这是我用来解析这个列表的代码

parse(ListTokens,ListStmt) :-
    phrase(commands(ListStmt),ListTokens).

commands([]) --> [].
commands(Command) --> command(Command).
commands(Commands) --> [punct("(")],commands(Commands),[punct(")")].
commands([Command | Commands]) --> command(Command),[punct(";")],commands(Commands).

command(Skip) --> [keyword("skip")],{Skip = skip()}.

command(Assign) --> a_expr(X),!,[punct(":")],[punct("=")],a_expr(Y),{Assign = assign(X,Y)}.

command(While) --> [keyword("while")],b_expr(X),[keyword("do")],command(Commands),{While = while(X,Commands)}.

a_expr(word(Name)) --> [var(Name)].
a_expr(number(Val)) --> [Val].

a_expr(Expr) --> a_expr(X),[punct("+")],{Expr = increment(X,Y)}.
a_expr(Expr) --> a_expr(X),[punct("-")],{Expr = decrement(X,[punct("*")],{Expr = multipl(X,Y)}.

b_expr(Expr) --> [punct("(")],b_expr(Expr),[punct(")")].
b_expr(True) --> [keyword("true")],{True = bool(1)}.
b_expr(False) --> [keyword("false")],{False = bool(0)}.
b_expr(Cmp) --> a_expr(X),{Cmp = cmp(X,Y)}.
b_expt(Gt) --> a_expr(X),[punct(">")],{Gt = gt(X,Y)}.

结果报告如下,但我想要以下一个assign(x,1)

assign(number(word(x)),number(number(1)))

我认为问题在于以下几行,但我无法找到错误,因为这是来自使用不同语法的 this post解决方

a_expr(word(Name)) --> [var(Name)].
a_expr(number(Val)) --> [Val].

你能帮我找到解决这个问题的方法吗?

解决方法

基本上,您的代码有两个问题。

一个是停止回溯的 bang 运算符 !。这是一个反模式,所以我建议不要使用它。

我是可读代码的忠实粉丝,我认为您需要接受语法中的左递归,主要是因为您使用的语法非常易读。

我建议改变

parse(ListTokens,ListStmt) :-
    phrase(commands(ListStmt),ListTokens).

commands([]) --> [].
commands(Command) --> command(Command).
commands(Commands) --> [punct("(")],commands(Commands),[punct(")")].
commands([Command | Commands]) --> command(Command),[punct(";")],commands(Commands).

command(Skip) --> [keyword("skip")],{Skip = skip()}.

command(Assign) --> a_expr(X),!,[punct(":")],[punct("=")],a_expr(Y),{Assign = assign(X,Y)}.

command(While) --> [keyword("while")],b_expr(X),[keyword("do")],command(Commands),{While = while(X,Commands)}.

a_expr(word(Name)) --> [var(Name)].
a_expr(number(Val)) --> [Val].

a_expr(Expr) --> a_expr(X),[punct("+")],{Expr = increment(X,Y)}.
a_expr(Expr) --> a_expr(X),[punct("-")],{Expr = decrement(X,[punct("*")],{Expr = multipl(X,Y)}.

b_expr(Expr) --> [punct("(")],b_expr(Expr),[punct(")")].
b_expr(True) --> [keyword("true")],{True = bool(1)}.
b_expr(False) --> [keyword("false")],{False = bool(0)}.
b_expr(Cmp) --> a_expr(X),{Cmp = cmp(X,Y)}.
b_expt(Gt) --> a_expr(X),[punct(">")],{Gt = gt(X,Y)}.

对此,基本上,添加以下行 :- table a_expr//1. 以使用制表并接受左递归。 (提醒,您需要删除所有的 bang 运算符)

:- table a_expr//1.

% other rules

% add this one,it is left recursion,but tanks to tabling
a_expr(Var)   --> [word(Name)],{Var = var(Name)}.
a_expr(Val)   --> [number(X)],{Val = X}.

a_expr(Expr) --> a_expr(X),Y)}.

P.S:正如一条评论所暗示的那样,您需要按照以下规则输入错误

b_expt(Gt) --> a_expr(X),Y)}.

它是b_expr