【问题标题】:Prolog type errors with DCG library functionsDCG 库函数的 Prolog 类型错误
【发布时间】:2016-05-22 23:23:38
【问题描述】:

我正在尝试为命令接口编写 DCG。这个想法是读取输入字符串,将其拆分为空格,然后将生成的令牌列表交给 DCG 以将其解析为命令和参数。解析的结果应该是一个术语列表,我可以将其与=.. 一起使用来构建要调用的目标。但是,我对 SWI-Prolog(版本 7.2.3)中的字符串类型情况感到非常困惑。 SWI-Prolog 包含一个basic DCG functionality 库,其中包括一个目标integer//1,它应该解析一个整数。它由于类型错误而失败,但更大的问题是我无法弄清楚如何使用“令牌列表”在 SWI-Prolog 中使 DCG 正常工作。

这是我想要做的:

:- use_module(library(dcg/basics)).

% integer//1 is from the dcg/basics lib
amount(X) --> integer(X), { X > 0 }.
cmd([show,all]) --> ["show"],["all"].
cmd([show,false]) --> ["show"].
cmd([skip,X]) --> ["skip"], amount(X).

% now in the interpreter:
?- phrase(cmd(L), ["show","all"]).
L = [show, all].
% what is the problem with this next query?
?- phrase(cmd(L), ["skip", "50"]).
ERROR: code_type/2: Type error: `character' expected, found `"50"' (a string)

我已经阅读了 SWI 手册的Section 5.2,但它并没有完全回答我的问题:

  1. integer//1dcg/basics 库中需要什么类型?错误消息显示“字符”,但我找不到任何有用的参考来说明这究竟意味着什么以及如何为其提供“正确”输入。
  2. 如何将字符串(令牌)列表传递给phrase/2,以便我可以使用integer//1 将令牌解析为整数?
  3. 如果无法使用integer//1 原语将一串数字解析为整数,我应该如何完成?

我做了很多实验,在 SWI-Prolog 中为 double_quote 标志使用不同的值,以及不同的输入格式,例如使用原子列表,使用单个字符串作为输入,即 "skip 50"而不是["skip", "50"],等等,但我觉得关于DCG如何工作的假设我不明白。

我也研究了这三个页面,其中有很多示例,但没有一个完全解决我的问题(一些链接被省略,因为我没有足够的声誉来发布所有这些):

  1. Anne Ogborn 的教程“在 SWI-Prolog 中使用定句语法”
  2. Amzi 的教程!关于将命令接口编写为 DCG 的 Prolog。
  3. J. R. Fisher 的 Prolog 教程第 7.3 节

第三个更广泛的问题是,如果需要整数但无法解析为整数,如何生成错误消息,如下所示:

% the user types:
> skip 50x
I didn't understand that number.

一种方法是将上面 DCG 中的变量 X 设置为某种错误值,然后稍后检查(如假设的 skip/1 目标应该由命令调用),但是也许有更好/更惯用的方式?我编写解析器的大部分经验来自使用 Haskell 的 Parsec 和 Attoparsec 库,它们具有相当的声明性,但工作方式略有不同,尤其是在错误处理方面。

【问题讨论】:

  • 您的 DCG 中如何定义integer
  • 它是 SWI-Prolog 附带的库的一部分,记录在 here。我将使用此信息编辑帖子。
  • 根据文档,该谓词 (integer(X)) 需要一个整数参数,但您正在尝试用它解析一个原子 ('50')(我假设您的 SWI 序言配置为解释"50" 作为原子 '50')。

标签: prolog dcg


【解决方案1】:

Prolog 没有字符串。双引号字符序列的传统表示是代码列表(整数)。对于efficiency reasons,SWI-Prolog 版本。 >= 7 引入了字符串作为新的 atomic 数据类型:

?- atomic("a string").
true.

和反引号的文字现在具有以前由字符串担任的角色:

?- X=`123`.
X = [49, 50, 51].

不用说,这导致了some confusion,同时考虑到 Prolog 的弱类型特性......

无论如何,DCG 仍然可以处理(差异)字符代码列表,只是翻译器已扩展为接受字符串作为终端。您的代码可能是

cmd([show,all]) --> whites,"show",whites,"all",blanks_to_nl.
cmd([show,false]) --> whites,"show",blanks_to_nl.
cmd([skip,X]) --> whites,"skip",whites,amount(X),blanks_to_nl.

可以这样称呼

?- phrase(cmd(C), ` skip 2300 `).
C = [skip, 2300].

编辑

如果需要整数,如何生成错误消息

我会尝试:

...
cmd([skip,X]) --> whites,"skip",whites,amount(X).

% integer//1 is from the dcg/basics lib
amount(X) --> integer(X), { X > 0 }, blanks_to_nl, !.
amount(unknown) --> string(S), eos, {print_message(error, invalid_int_arg(S))}.

prolog:message(invalid_int_arg(_)) --> ['I didn\'t understand that number.'].

测试:

?- phrase(cmd(C), ` skip 2300x `).
ERROR: I didn't understand that number.
C = [skip, unknown] ;
false.

【讨论】:

  • 所以空格必须包含在phrase/2的输入中?我希望有一种方法可以直接处理令牌列表。问题的一部分似乎是integer//1 需要一个字符代码列表(如“123”),但正在获取 [“123”](假设双引号字符串正在生成字符代码列表;这是可用的SWI-Prolog 7 中的标志)。对吗?
  • 是的,整数//1 需要(或生成)字符代码列表。实际上,对于普通的 DCG,令牌只是一个字符代码。要将字符串转换为数字,您可以使用?- number_string(X,"123").。也许我缺少的一点是:你的令牌从哪里进来,为什么它们是字符串?
  • 这个想法是从某个流中读取一行输入,将其分解为标记(例如“do this”-> [“do”,“this”]),然后将其交给 DCG用于解析成术语列表。正如 herehere 所指出的,这似乎与 DCG 相关。前一个例子在 Amzi prolog 中,但几乎正是我想要做的,但是,如果可能的话,我想使用 SWI-Prolog 的 DCG 原语(如integer//1)来提供帮助。这些类型似乎不起作用,我'不知道如何解决。
  • 是的,我已经阅读了您的问题。为什么不使用read_line_to_codes/2 并直接申请 DCG ?我想这会是一种简化,但我当然不知道你的工作流程 - 所以我问了这个问题。
猜你喜欢
  • 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
相关资源
最近更新 更多