我正试图为命令接口编写一个DCG。其想法是读取一个输入字符串,将其拆分为空格,并将生成的令牌列表交给DCG,以将其解析为命令和参数。解析的结果应该是一个术语列表,我可以与=..
一起使用这些术语来构建要调用的目标。然而,我对SWI-Prolog(7.2.3版)中的字符串类型情况感到非常困惑。SWI-Prollog包括一个基本DCG功能库,其中包括一个目标integer//1
,它应该解析一个整数。由于类型错误,它失败了,但更大的问题是,我不知道如何使DCG在SWI-Prolog中使用"令牌列表"良好地工作。
以下是我要做的:
:- 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手册的第5.2节,但它并没有完全回答我的问题:
dcg/basics
库中的integer//1
需要什么类型?错误消息显示"字符",但我找不到任何有用的参考资料来说明这到底意味着什么以及如何为其提供"正确"的输入- 如何将字符串(令牌)列表传递给
phrase/2
,以便使用integer//1
将令牌解析为整数 - 如果没有办法使用
integer//1
原语将一串数字解析为整数,我应该如何实现这一点
我在SWI-Prolog中为double_quote
标志使用了不同的值,再加上不同的输入格式,例如使用原子列表,使用单个字符串作为输入,即"skip 50"
而不是["skip", "50"]
,等等,我做了很多实验,但我觉得对DCG如何工作有一些假设我不理解。
我也研究了这三个页面,其中有很多例子,但没有一个完全解决我的问题(由于我没有足够的声誉来发布所有这些链接,所以省略了一些链接):
- Anne Ogborn的教程"在SWI Prolog中使用确定性子句语法">
- Amzi的教程!关于将命令接口编写为DCG的Prolog
- J.R.Fisher Prolog教程第7.3节
第三个更广泛的问题是,如果需要一个整数,但无法解析为一个整数时,如何生成错误消息,类似于以下内容:
% the user types:
> skip 50x
I didn't understand that number.
一种方法是将上面DCG中的变量X
设置为某种错误值,然后稍后进行检查(比如假设的skip/1
目标,该目标应该由命令调用),但也许有更好/更惯用的方法?我编写解析器的大部分经验来自于使用Haskell的Parsec和Attoparsec库,这两个库是相当声明性的,但工作方式有所不同,尤其是在错误处理方面。
Prolog没有字符串。双引号字符序列的传统表示是代码(整数)列表。出于效率原因,SWI Prolog v>=7引入字符串作为新的原子数据类型:
?- atomic("a string").
true.
和后引号文字现在有了以前由字符串担任的角色:
?- X=`123`.
X = [49, 50, 51].
不用说,这引起了一些混乱,也考虑到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.