【问题标题】:DCG in Prolog — stringsProlog 中的 DCG — 字符串
【发布时间】:2010-12-08 15:53:59
【问题描述】:

我正在使用 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(Z) --> "(", "+", spaces, expr(M), spaces, expr(N), ")", {swritef(Z, "%s + %s", [M, N])}.
expr(E) --> number(N), {swritef(E, "%d", [N])}.

但我明白了:

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

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

如何让它发挥作用?

【问题讨论】:

  • @Bobby:SWI-Prolog 版本 5.6.64

标签: parsing lisp prolog grammar dcg


【解决方案1】:

问题在于 %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 --> [].

谓词 lexpr 只是将解析后的表达式转换为字符列表。

编辑:03/07/2016:从 SWI 7.0 版开始,用双引号括起来的文本不再被解释为字符代码列表。 您可以使用反引号 (`) 更改双引号或添加指令;

:-set_prolog_flag(double_quotes, codes).

在代码的开头。

【讨论】:

  • 这个演示似乎与 SWI-Prolog 不兼容。我应该使用哪个版本的 Prolog 来运行它?
  • @AndersonGreen:在从版本 7 开始的 SWI 中,默认情况下,双引号之间的文本不会被解释为字符代码列表。我已经编辑了答案以显示应该打开哪个 prolog 标志才能使代码正常工作。或者,您可以使用反引号 (`) 更改双引号
【解决方案2】:

在您的swritef 中使用%t or %w, not %d。注意,%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.

【讨论】:

  • 我在 SWI-Prolog 中运行了这个演示,但它只打印了 E = "(%t * %t)" . 我应该使用哪个版本的 Prolog 来运行这个示例?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多