【问题标题】:How do I construct a parse tree from a series of S-expression tokens in Prolog?如何从 Prolog 中的一系列 S 表达式标记构造解析树?
【发布时间】:2020-12-18 15:38:59
【问题描述】:

我对 Prolog 作为解析器很好奇,所以我正在做一个小的 Lisp 前端。我已经做了一个分词器,你可以在这里看到:

base_tokenize([], Buffer, [Buffer]).
base_tokenize([Char | Chars], Buffer, Tokens) :-
    (Char = '(' ; Char = ')') ->
        base_tokenize(Chars, '', Tail_Tokens),
        Tokens = [Buffer, Char | Tail_Tokens];

    Char = ' ' ->
        base_tokenize(Chars, '', Tail_Tokens),
        Tokens = [Buffer | Tail_Tokens];

    atom_concat(Buffer, Char, New_Buffer),
    base_tokenize(Chars, New_Buffer, Tokens).

filter_empty_blank([], []).
filter_empty_blank([Head | Tail], Result) :-
    filter_empty_blank(Tail, Tail_Result),
    ((Head = [] ; Head = '') ->
        Result = Tail_Result;
        Result = [Head | Tail_Result]).

tokenize(Expr, Tokens) :-
    atom_chars(Expr, Chars),
    base_tokenize(Chars, '', Dirty_Tokens),
    filter_empty_blank(Dirty_Tokens, Tokens).

我现在有一个新的挑战:从这里构造一个解析树。首先,我试着做一个没有语法的,但结果真的很乱。所以我正在使用DCG。 it 上的维基百科页面不是很清楚 - 尤其是 Parsing with DCGs 部分。也许有人可以让我更清楚地了解如何构建一棵树?我很高兴知道 Prolog 的列表是无类型的,所以现在不需要 sum 类型就容易多了。我只是对 sentence(s(NP,VP))verb(v(eats))(在 Wiki 上)等语法子句的输入感到非常困惑,为什么这些参数有如此深奥的名称,以及我如何能够轻松地开始使用我的解析器。

expr --> [foo].
expr --> list.

seq --> expr, seq.
seq --> expr.

list --> ['('], seq, [')'].

【问题讨论】:

  • 一个不错的 dcg 介绍,metalevel.at/prolog/dcg。它有一个很好的树构造示例。
  • 是的,维基百科页面不像习惯那样优秀。

标签: parsing prolog grammar dcg parse-tree


【解决方案1】:

这是一个开始:解析 LISP 原子列表,起初是非结构化的令牌列表:

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

首先,接受它。

直接写下语法:

so_list         --> ['('], so_list_content, [')'].

so_list_content --> [].
so_list_content --> so_atom, so_list_content.
so_list_content --> so_list, so_list_content.

so_atom         --> [X], { \+ member(X,['(',')']),atom(X) }.

添加一些测试用例(GNU Prolog 中有plunit 吗?)

:- begin_tests(accept_list).

test(1,[fail])        :- phrase(so_list,[]).
test(2,[true,nondet]) :- phrase(so_list,['(',')']).
test(3,[true,nondet]) :- phrase(so_list,['(',foo,')']).
test(4,[true,nondet]) :- phrase(so_list,['(',foo,'(',bar,')',')']).
test(5,[true,nondet]) :- phrase(so_list,['(','(',bar,')',foo,')']).
test(6,[fail])        :- phrase(so_list,['(',foo,'(',bar,')']).

:- end_tests(accept_list).

所以:

?- run_tests.
% PL-Unit: accept_list ...... done
% All 6 tests passed
true.

酷。看起来我们可以接受令牌列表。

现在构建一个解析树。这是通过“DCG 谓词”的参数增长 Prolog 术语来完成的。头部中的词条(或多个词条)很自然地将出现在主体中的词条(或多个词条)收集到一个更大的结构中。一旦到达终端标记,结构就会开始填充实际内容:

so_list(list(Stuff))   --> ['('], so_list_content(Stuff), [')'].

so_list_content([])        --> [].
so_list_content([A|Stuff]) --> so_atom(A), so_list_content(Stuff).
so_list_content([L|Stuff]) --> so_list(L), so_list_content(Stuff).

so_atom(X) --> [X], { \+ member(X,['(',')']),atom(X) }.

是的,测试(将预期的结果移出测试头,因为视觉噪音太大)

:- begin_tests(parse_list).

test(1,[fail]) :-
   phrase(so_list(_),[]).

test(2,[true(L==Result),nondet]) :-
   phrase(so_list(L),['(',')']),
   Result = list([]).
   
test(3,[true(L==Result),nondet]) :-
   phrase(so_list(L),['(',foo,')']),
   Result = list([foo]).
   
test(4,[true(L==Result),nondet]) :-
   phrase(so_list(L),['(',foo,'(',bar,')',')']),
   Result = list([foo,list([bar])]).
   
test(5,[true(L==Result),nondet]) :-
   phrase(so_list(L),['(','(',bar,')',foo,')']),
   Result = list([list([bar]),foo]).
   
test(6,[fail]) :-
   phrase(so_list(_),['(',foo,'(',bar,')']).

:- end_tests(parse_list).

所以:

?- run_tests.
% PL-Unit: parse_list ...... done
% All 6 tests passed
true.

【讨论】:

  • 我找不到 GNU Prolog 的 plunit,但可能还有其他一些单元测试系统。实际上,我已经想出了如何验证一系列标记是否有效——构建树是让我绊倒的原因。你能详细说明一下这一步吗?
  • 这太有帮助了!谢谢。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多