Parsing Prolog中的DCG—;串

Parsing Prolog中的DCG—;串,parsing,lisp,prolog,grammar,dcg,Parsing,Lisp,Prolog,Grammar,Dcg,我正在使用Prolog的内置DCG功能编写一个Lisp-to-C转换器。这就是我处理算术的方式: expr(Z) --> "(", "+", spaces, expr(M), spaces, expr(N), ")", {swritef(Z, "%d + %d", [M, N])}. expr(Z) --> "(", "-", spaces, expr(M), spaces, expr(N), ")", {swritef(Z, "%d - %d", [M, N])}. expr(Z)

我正在使用Prolog的内置DCG功能编写一个Lisp-to-C转换器。这就是我处理算术的方式:

expr(Z) --> "(", "+", spaces, expr(M), spaces, expr(N), ")", {swritef(Z, "%d + %d", [M, N])}.
expr(Z) --> "(", "-", spaces, expr(M), spaces, expr(N), ")", {swritef(Z, "%d - %d", [M, N])}.
expr(Z) --> "(", "*", spaces, expr(M), spaces, expr(N), ")", {swritef(Z, "%d * %d", [M, N])}.
expr(Z) --> "(", "/", spaces, expr(M), spaces, expr(N), ")", {swritef(Z, "%d / %d", [M, N])}.
expr(E) --> number(E).

number(C) --> "-", digits(X), {C is -X}.
number(C) --> digits(C).
digits(D) --> digit(D);digit(A),digits(B), {number_codes(B,Cs),length(Cs,L), D is A*(10^L)+B}.
digit(D) --> [C], {"0"=<C, C=<"9", D is C - "0"}.
但我明白了:

?- expr(E, "42", []).
E = "42" %all OK

?- expr(E, "(+ 3 (* 2 2))", []).
E = "%s + %s" %not OK

如何使其工作?

问题是%s格式说明符需要参数是字符列表。 所以你可以这样做:

:-set_prolog_flag(double_quotes, codes).  % This is for SWI 7+ to revert to the prior interpretation of quoted strings.

expr(Z) --> "(", "+", spaces, lexpr(M), spaces, lexpr(N), ")", {swritef(Z, "%s + %s", [M, N])}.
expr(Z) --> "(", "-", spaces, lexpr(M), spaces, lexpr(N), ")", {swritef(Z, "%s - %s", [M, N])}.
expr(Z) --> "(", "*", spaces, lexpr(M), spaces, lexpr(N), ")", {swritef(Z, "%s * %s", [M, N])}.
expr(Z) --> "(", "/", spaces, lexpr(M), spaces, lexpr(N), ")", {swritef(Z, "%s / %s", [M, N])}.
expr(N) --> number(N).

lexpr(Z) --> expr(M), {atom_chars(M, Z)}.

number(C) --> "-", digits(X), {C is -X}.
number(C) --> digits(C).

digits(D) --> digit(D);digit(A),digits(B), {number_codes(B,Cs),length(Cs,L), D is A*(10^L)+B}.
digit(D) --> [C], {"0"=<C, C=<"9", D is C - "0"}.

spaces --> " ", spaces.
spaces --> [].
:-set_prolog_标志(双引号,代码)。%这是为了让SWI 7+恢复到引用字符串的先前解释。
expr(Z)->“(“,“+”,空格,lexpr(M),空格,lexpr(N),”,{swritef(Z,“%s+%s”,[M,N])”。
expr(Z)->“(“,“-”,空格,lexpr(M),空格,lexpr(N),”,{swritef(Z,,%s-%s),[M,N])”。
expr(Z)->“(”,“*”,空格,lexpr(M),空格,lexpr(N),”,{swritef(Z,“%s*%s”,[M,N])”。
expr(Z)->“(“,“/”,空格,lexpr(M),空格,lexpr(N),”,{swritef(Z,,%s/%s),[M,N])”。
expr(N)->编号(N)。
lexpr(Z)->expr(M),{atom_chars(M,Z)}。
数字(C)->“-”,数字(X),{C是-X}。
数字(C)->数字(C)。
数字(D)->数字(D);数字(A),数字(B),{数字代码(B,Cs),长度(Cs,L),D是A*(10^L)+B}。
数字(D)->[C],{“0”=[]。
谓词lexpr只是将解析后的表达式转换为字符列表

编辑:2016年7月3日:自SWI 7.0版起,双引号内的文本不再解释为字符代码列表。 您可以使用反引号(`)更改双引号或添加指令

:-设置prolog标志(双引号,代码)。

在代码开头。

在您的
swritef
中使用。注意,%d不是C的printf格式

如果您只是将lisp-like转换为C-like,那么实际上不需要转换字符串 将数字表示为一个数字。只需将其保留为字符串即可。(当然,这取决于 否则,上级规则会在需要字符串的地方找到一个数字

将生成的C代码放入括号中,以便结果中的优先级和关联性是正确的

expr(Z) --> "(", "-", spaces, expr(M), spaces, expr(N), ")", {swritef(Z, "(%t - %t)", [M, N])}.
expr(Z) --> "(", "*", spaces, expr(M), spaces, expr(N), ")", {swritef(Z, "(%t * %t)", [M, N])}.
expr(Z) --> "(", "/", spaces, expr(M), spaces, expr(N), ")", {swritef(Z, "(%t / %t)", [M, N])}.
expr(Z) --> "(", "+", spaces, expr(M), spaces, expr(N), ")", {swritef(Z, "(%t + %t)", [M, N])}.
expr(E) --> number(N), {swritef(E, "%s", [N])}.

spaces --> " ".

number([C|Cs]) --> "-", {C = "-"}, digits(Cs).
number(C) --> digits(C).

digits([D|[]]) --> digit(D).

digits([D|Ds]) --> digit(D), digits(Ds).
digit(D) --> [D], {code_type(D, digit)}.
这就是它的工作原理

?- expr(E, "(* 1342 (/ 44 -17))", []).
E = "(1342 * (44 / -17))" ;
false.

@Bobby:SWI Prolog版本5.6.64I在SWI Prolog中运行了这个演示,但它只打印了
E=“(%t*%t)”.
我应该使用哪个版本的Prolog来运行这个示例?这个演示似乎与SWI Prolog不兼容。我应该使用哪个版本的Prolog来运行它?@AndersonGreen:在从版本7开始的SWI中,默认情况下,双引号之间的文本不会被解释为字符代码列表。我已经编辑了答案,以显示哪个prol应该打开og标志,代码才能工作。或者,您可以使用反引号(`)更改双引号
?- expr(E, "(* 1342 (/ 44 -17))", []).
E = "(1342 * (44 / -17))" ;
false.