如何在 Prolog 中创建与另一个相反的 DCG 规则?

问题描述

我正在 Prolog 中编写 Commodore BASIC 解释器,并且正在编写一些 DCG 来解析它。我已验证以下 DCG 可以正常工作,但 variable 除外。我的目标是:对于任何不是布尔值、整数、浮点数或字符串的东西,它都是一个变量。但是,我通过 phrase 提供的任何内容都只会导致 no

bool --> [true].
bool --> [false].
integer --> [1]. % how to match nums?
float --> [0.1].
string --> [Str],{atom_chars(Str,['"' | Chars]),last(Chars,'"')}.
literal --> bool; integer; float; string.

variable --> \+ literal.

我运行了这样的堆栈跟踪(使用 gprolog

main :- trace,phrase(variable,[bar]).

看着这个,我无法弄清楚为什么 variable 失败,因为它在 literal 中的每个案例都失败了。我猜这个错误很简单,但我仍然很难过,所以任何擅长 Prolog 的人都知道我做错了什么吗?

| ?- main.
The debugger will first creep -- showing everything (trace)
      1    1  Call: phrase(variable,[bar]) ? 
      2    2  Call: variable([bar],_321) ? 
      3    3  Call: \+literal([bar],_348) ? 
      4    4  Call: literal([bar],_348) ? 
      5    5  Call: bool([bar],_348) ? 
      5    5  Fail: bool([bar],_348) ? 
      5    5  Call: integer([bar],_348) ? 
      5    5  Fail: integer([bar],_348) ? 
      5    5  Call: float([bar],_348) ? 
      5    5  Fail: float([bar],_348) ? 
      5    5  Call: string([bar],_348) ? 
      6    6  Call: atom_chars(bar,['"'|_418]) ? 
      6    6  Fail: atom_chars(bar,['"'|_418]) ? 
      5    5  Fail: string([bar],_348) ? 
      4    4  Fail: literal([bar],_348) ? 
      3    3  Exit: \+literal([bar],_348) ? 
      2    2  Exit: variable([bar],[bar]) ? 
      1    1  Fail: phrase(variable,[bar]) ? 

(2 ms) no
{trace}

解决方法

您可以检测这样的整数字符串(我添加了一个参数来收集数字):

integer([H|T]) --> digit(H),integer(T).
integer([]) -->  [].
digit(0) --> "0".
digit(1) --> "1".
    ...
digit(9) --> "9".

至于 variable -- 它需要使用文本,因此您需要类似于上面的 integer 的内容,但将 digit(H) 更改为能够识别属于“变量”。

如果你想要更多的线索(虽然有时会使用稍微高级的技巧):https://www.swi-prolog.org/pldoc/man?section=basics 代码在这里:https://github.com/SWI-Prolog/swipl-devel/blob/master/library/dcg/basics.pl

,

稍微扩展另一个答案,关键问题是像 \+ literal 这样的 DCG 规则不消耗输入中的项目。它仅检查下一项(如果有)不是 literal

要实际消费一个项目,您需要使用列表目标,这与您使用目标 [1] 来消费 1 元素的方式类似。所以:

variable -->
    \+ literal,% if there is a next element,it's not a literal
    [_Variable]. % consume this next element,which is a variable

例如:

?- phrase(variable,[bar]).
true.

?- phrase((integer,variable,float),[I,bar,F]).
I = 1,F = 0.1.

拥有单个变量 _Variable 有点奇怪——当你像这样解析时,你会丢失变量的名称。当您的解析器稍微扩展时,您将需要使用 DCG 规则的参数来传达规则之外的信息:

variable(Variable) -->
    \+ literal,[Variable].

例如:

?- phrase((integer,variable(Var1),float,variable(Var2)),F,foo]).
Var1 = bar,Var2 = foo,I = 1,F = 0.1.