12

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

文件结构如下所示:

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 及其在解析器中的使用。

4

4 回答 4

13

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

评论后编辑...

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

事实上,所提供的示例可以很好地解决,而无需使用术语 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).

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

更多的

这个另一个答案显示了一种实现具有运算符解析和惰性求值的表达式计算器的实用方法。

于 2012-12-24T18:01:02.800 回答
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]

但你可能不想要那些。

您可能会发现 SIICSTUS 兼容性 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.

w 到处都是可选的空格。

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

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

而且,当然,所有这些都导致了一个问题,为什么关于这个问题的问题充斥着 SO 而不是讲师的盒子。

于 2012-12-26T04:45:31.173 回答
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].

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

于 2013-01-04T07:58:20.127 回答
0

恕我直言,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 这是理想的场景。

于 2013-04-28T09:06:52.343 回答