Prolog - 带有文件输入的DCG解析器

时间:2012-12-24 16:50:44

标签: parsing prolog swi-prolog dcg

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

文件结构如下所示:

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:Sentence split in parts

现在我知道这里有很多人在涉及到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及其在解析器中的使用的更多信息。

4 个答案:

答案 0 :(得分:9)

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

评论后

编辑 ...

你是对的,DCG是正确的方式来处理Prolog中的常规解析。 DCG制作中的参数可以被视为语义属性,因此编程DCG可以被视为在输入上提供工作语义分析(参见Attribute Grammar,这是一种重要的技术 - 在语言工程中也是如此)。

事实上,所提出的例子可以很好地解决,而不需要术语IO所需的黑客攻击。

这是:

:- 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显示了一种实用的方法来实现具有运算符分辨率和惰性求值的表达式计算器。

答案 1 :(得分:1)

好的,家庭作业问题的目的是学习。使用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]

但你可能不想要那些。

您可能会发现SICSTUS兼容性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而不是教师的框。

答案 2 :(得分:1)

我将字符串解析为列表然后操作列表。 使用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].

这里有两种模式可以处理(L> R)和{L,R}。这不会太复杂,也很容易阅读。

答案 3 :(得分:0)

恕我直言,DCG语法规则在令牌化方面相当丑陋,我真的应该从未为这项任务提出过DCG;与DCG真正的交易是解析令牌,因为prolog使用符号,所以我可以说,最好的选择是创建一个外部调用,比如C tokenizer,它将与简单的令牌列表统一,然后让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是理想的场景。