【问题标题】:Prolog - DCG parser with input from fileProlog - 带有文件输入的 DCG 解析器
【发布时间】:2012-12-11 00:34:13
【问题描述】:

作为项目的一部分,我需要编写一个解析器,它可以读取文件并解析成我可以在我的程序中使用的事实。

文件结构如下:

property = { el1 , el2 , ... }.  

我到底想要的是:

property(el1).
property(el2).
...

我这样读我的文件:

main :-
       open('myFile.txt', read, Str),
       read_file(Str,Lines),
       close(Str),
       write(Lines), nl.

read_file(Stream,[]) :-
                       at_end_of_stream(Stream).

read_file(Stream,[X|L]) :-
                          \+ at_end_of_stream(Stream),
                          read(Stream,X),
                          parse(X),            % Here I call upon my parser.
                          read_file(Stream,L).

现在我已经阅读了几本关于 DCG 的书籍和在线内容,但它们都解释了相同的简单示例,您可以在这些示例中生成诸如“猫吃蝙蝠”之类的句子......当我想在上面的示例中使用它时我失败得很惨。

我所做的是“解析”下一行:

property = el1.

到

property(el1).

用这个:

parse(X) :-
           X =.. List,    % Reason I do this is because X is one atom and not a list.
           phrase(sentence(Statement), List),
           asserta(Statement).

sentence(Statement) --> ['=', Gender, Person] , { Statement =.. [Gender, Person] }.

我什至不知道我是否在这里以正确的方式使用了 dcg,因此我们将不胜感激。现在我遇到的问题是,如何处理列表中的多个元素,以及如何处理“{”和“}”。
我真正想要的是一个可以处理这些类型的句子(超过2个元素)的dcg:

现在我知道这里的很多人在谈到 dcgs 时都会参考 dcg_basics 和 pio 库。但是,我还有一个问题,当我尝试使用该库时,我收到了错误:

ERROR: (c:/users/ldevriendt/documents/prolog/file3.pl:3):
      Type error: `text' expected, found `http/dcg_basics'
Warning: (c:/users/ldevriendt/documents/prolog/file3.pl:3):
      Goal (directive) failed: user:[library(http/dcg_basics)]

当我这样做时:

:- [library(http/dcg_basics)].

附加信息:

对此的任何帮助将不胜感激!

编辑:这个问题的目的是了解更多关于 DCG 及其在解析器中的使用。

【问题讨论】:

  • 尝试替换为:- use_module(library(dcg/basics)).
  • 但我认为您的 SWI-Prolog 可能太旧了
  • 我笔记本电脑上安装的版本似乎确实是旧版本的 SWI-Prolog。我在桌面上安装了所有东西,现在可以正常使用库。谢谢!
  • 感谢 Floris 的问题 +1。如果你能接受我的回答,如果有用的话,我想会很好。
  • 如前所述,如果可能的话,我首先想试试 DCG 解析器。我正在尝试使用我的课程(DCG)的元素来完成这项工作。虽然您对另一个问题的解决方案适用于该示例,但在输入不同的其他情况下将不起作用。例如,有时首选项将给出为: m1: w1 > {w2 , w3} > w4(不一定按此顺序)。此外,列表可能不完整,也需要处理。我想我在使用 DCG 时会有更多的可能性。

标签: parsing prolog swi-prolog dcg


【解决方案1】:

只要您的文件是纯 Prolog 语法,建议您使用 Prolog 术语 IO。通过 single 调用读取完全结构化的术语。使用 DCG 的方式更复杂,效率也更低(此处不确定,应该测量,但 read(Term) 调用用 C 实现的 Prolog 解析器...)参见另一个 question,它使用相同格式(至少,你可以检查一下其他人是否在这里得到了关于你相同作业的答案......)

编辑在cmets之后...

你说得对,DCG 是在 Prolog 中处理一般解析的正确方法。 DCG 产生中的参数可以看作是语义属性,因此 DCG 编程可以看作是对输入进行工作语义分析(参见Attribute Grammar,这是语言工程中的一项重要技术)。

确实,所提供的示例可以很好地解决,而无需术语 IO 所需的 hack。

这里是:

:- use_module(library(pio)).  % autoload(ed), added just for easy browsing
:- use_module(library(dcg/basics)).

property(P) -->
    b, "my props", b, "=", b, "{", elS(Es) , b, "}", b,
    { P =.. [property|Es] }.

elS([E|Es]) --> el(E), b, ("," -> elS(Es) ; {Es = []}).
el(N) --> number(N).
el(S) --> csym(S). % after Jeremy Knees comment...
b --> blanks.

%   parse a C symbol
csym(S) -->
    [F], { code_type(F, csymf) },
    csym1(Cs),
    !, { atom_codes(S, [F|Cs]) }.

csym1([C|Cs]) -->
    [C], { code_type(C, csym) },
    csym1(Cs).
csym1([]) --> [].

这样,我们就有了

?- phrase(property(P), "my props = {1,2,3}").
P = property(1, 2, 3).

感谢库(pureio),我们可以将语义编程应用于 Prolog 流,并获得与短语/2 相同的行为的奖励。

更多

other answer 展示了一种实用的方法来实现具有运算符解析和惰性求值的表达式计算器。

【讨论】:

  • 啊,好像确实有人问过同样的问题,而且他很可能是同班的人(虽然我不认识他)。他似乎没有使用 DCG 的想法(我认为这是我们必须使用的)。理想情况下,我希望它在 DCG 中,但如果我无法弄清楚,我会尝试你的解决方案。
  • 这里假设您总是有一个数字列表 {1,2,3}。但是如果你想把它读成原子呢?例如 {el1, el2} ?
【解决方案2】:

嗯,家庭作业的目的是为了学习。用 DCG 来做这件事会教给你一个比骑马操作员更普遍有用的技能。

我认为您的问题与 DCG 本身有关的问题比字符串处理问题要少。

您有很多地方可以使用 univ(=.. 运算符)在列表和字符串之间进行转换。大学可能不是你想要的。 Univ 将术语与列表统一起来。

foo(bar, baz)  =..  [foo, bar, baz]

你需要了解的是,Prolog 中的字符串可以有几种不同的形式 字符串 'hi Flores' 可以是

'嗨弗洛雷斯' - 这是一个原子 - 一个“固体块”的东西。某些字符序列不需要单引号(请参阅您的书),因此 hi_flores 是一个非常好的原子 没有单引号。

[104,105,32,70,108,111,114,101,115] - a list of ASCII codes.  This is likely what you want. These can be written with double quotes, "hi Floris"  in prolog code.

To save your sanity, put

:- portray_text(true).  

在您的文件中,以便在调试时打印出“hi Floris”,而不是一堆数字。

还有一个字符原子列表

[h, i, ' ', 'F', l, o, r, i, s]

但你可能不想要这些。

您可能会发现 SICTUS 兼容性 pred read_line 很有用。

现在,在 DCG 中,您有时想要匹配“文字”——字面意思就是那个东西。 如果是这样,请将其放入列表中。 这是一些模糊的 VBish 语言中 if 语句的 DCG

if_statement  --> "if", wh, "(", condition, ")", wh, 
                  "then", wh, body, wh, "else", wh,
                  else_body, wh, "endif".

% whitespace
wh -->  [].
wh -->  " ", wh.
wh --> [10], wh.   % handle newline and cr
wh --> [12], wh.

wh 到处都是可选的空格。

现在,对于整体策略,您可以一次读取一行,也可以读取整个文件。对于一行,使用 read_line,它返回代码列表。 read_file_to_codes 将获取整个文件。

如果您使用整个文件策略,并且换行符很重要,您显然需要将它们从空格的定义中删除。

当然,所有这一切都引出了一个问题,为什么关于这个问题的问题充斥着 SO 而不是讲师的收件箱。

【讨论】:

    【解决方案3】:

    我将字符串解析为一个列表,然后操作该列表。 使用 DCG 你可以转换

    T = (saf>{saf, as13s}>a32s>asf).
    

    到

    S = [saf-0, saf-1, as13s-1, a32s-2, asf-3] .
    

    注意事项:

    1. parseLine(<<Yourpattern>>,Position) --> parseLine(L,Position), parseLine(R,NewPosition)
    2. parseLine(Item,Pos) --> [Item-Pos].
    

    这里有 2 个模式可以处理,即 (L>R) 和 {L,R}。这不会很复杂,而且很容易阅读。

    【讨论】:

      【解决方案4】:

      恕我直言,DCG 语法规则在标记化方面非常难看,我真的认为 DCG 甚至不应该被提议用于该任务;与 DCG 的真正交易是解析令牌,因为 prolog 使用符号,所以我可以说,最好的选择是创建一个对 a 的外部调用,比如 C 令牌生成器,它将与普通令牌列表统一,然后让 DCG做它一直以来的事情。这样实现更干净,不用担心解析cr、空格...

      假设您有一种假设语言,其语句如下所示:

      object:
             object in a yields b,
             object in b yields C.
      

      我什至不想在 DCG 中将其标记化;我懒得学习如何使用不是为此类任务设计的工具来做到这一点。更好的是对谓词进行外部调用,该谓词将为我提供简单的令牌列表。

       tokenize(A,ListOfTokens), phrase(yourDGCstartRule(Information), ListOfTokens, _).
      

      我们正在运行的示例的列表如下所示:

      ListOfTokens = [object,:,object,in,a,yields,b,',',object,in,b,yields,c].
      

      我认为这要优雅得多,并且您的规则会相应地映射。我的想法可能是错误的,但归根结底这是一个品味问题,对我来说,DCG 不是标记器,除非严格要求,否则我永远不会使用它。诚然,我可以发现一些应用程序也可以将其用作标记器,但我仍然认为任务应该分开。

      请注意,我并不是说 prolog 没有很好的功能,您总是可以在 prolog 中进行标记化,但您应该将任务分开,让 DCG 只处理符号和其他一些严格需要的字符或字符串(如大写字符串,如专有名称或其他字符)。

      最后,在我看来,人们可能忘记了标记化和解析是两个独立的任务;更多在 prolog 中,因为令牌是 prolog 擅长的符号,而 DCG 更擅长解析令牌/符号(而不是字符),因为嵌入语义接口 prolog 这是理想的场景。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-09-02
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多