这种对dypgen的歧义的处理是正常还是不是?

时间:2016-09-15 11:36:38

标签: parsing nlp ocaml lexer ambiguity

我想知道,如果这是一个错误或行为,那是发明人的意图。

这里我有一个dypgen语法的最小例子:

{
open Parse_tree
let dyp_merge = Dyp.keep_all
}

%start main
%layout [' ' '\t']

%%

main:
  | a  "\n"                                                             { $1 }

a:
  | ms b                                                                { Mt ($1,$2) }
  | b <Mt(_,_)> kon1 b                                                  { Koo ($1, $2, $3) }
  | b <Mt(_,_)> kon2 b                                                  { Koo ($1, $2, $3) }
  | b                                                                   { $1 }

b:
  | k                                                                   { $1 }
  | ns b                                                                { Nt ($1,$2) } 
  /* If you comment this line out, it will work with the permutation, but I need the 'n' ! */


   /* | b <Nt(_,_)> kon1 b                                                 { Koo ($1, $2, $3) }
   | b <Nt(_,_)> kon2 b                                                 { Koo ($1, $2, $3) }*/

k:
  | k kon1 k                                                            { Koo ($1, $2, $3) }
  | k kon2 k                                                            { Koo ($1, $2, $3) }
  | ks                                                                  { $1 }

ms:
  | words <M(_)>                                                        { $1 }
ns:
  | words <N(_)>                                                        { $1 }
ks:
  | words <K(_)>                                                        { $1 }


kon1:
  | words <U(_)>                                                        { $1 }

kon2:
  | 'y'                                                                 { Y($1) }


words:
  | chain                                                               { $1 }
throw_away:
  | word  "|" throw_away                                                { $3 }
  | word                                                                { $1 }
chain:
  | word "|" throw_away                                                 { $1 }
  | word "|" chain                                                      { $3 }
  | word                                                                { $1 }


word:
  | ('m' ['1'-'9']?)                                                    { M ($1) }
  | ('n' ['1'-'9']?)                                                    { N ($1) }

  | ('K' ['1'-'9']?)                                                    { K ($1) }

  | ('u' ['1'-'9']?)                                                    { U ($1) }

该示例可以处理此类语法:

想一想?和*作为正则表达式运算符,&#39;&#39;和&#39; m&#39;和&#39; K&#39;作为词汇。

  s = m? n* K
&#39; K&#39;,&#39; m&#39;和&#39; n&#39;也可以用这些字母和1-9之间的后续数字代替 或者可以用&#39; |&#39;分隔的列表替换它们。如

  m1
  n1|n2
  K|K|K or K1|K2|K3

这些列表也可以混合为

  m1|n1|K1

所有这些列表都被解析为可能的歧义,全局合并 - 在已知的dypgen意义上 - 与

  let dyp_merge = Dyp.keep_all

如果您输入:

m1|n1|K1  m1|n1|K1 m1|n1|K1      

你得到了结果:

m1  > n1  > K1
n1  > n1  > K1

如果您输入

 K1|K2

你得到了

 K1
 K2

现在有趣的一点: 在语法中还有另一个特征。有一个&#34; koordination binding&#34;以自然语言的风格与“你好”或者与&#39; y&#39;。

这可以绑定这些短语&#34;短语&#34; (&#39; K&#39;带有可选前面的字母&#39;以及&#39; n&#39;以及&#39; n&#39;和#34; K1和K2&#34; 。 语法可以解析:

 K1|K2 u K3|K4

 K1|K2 y K3|K4

正如我所想,它应该有相同的结果。 但是&#34; koordination bindings&#34;是: lexem&#39; u&#39;被定义为与m,n,K相同的歧义列表,也可以与“K”,“m”,“&#39; n&#39;&#39;&#39;&#39; n&#39; ; S lexem&#39; y&#39;是没有这个列表的定义。

这会产生(令人惊讶的)差异:

 K1|K2 u K3|K4

被解析为:

 koo { K1 u K4 }
 koo { K2 u K4 }

 K1|K2 y K3|K4

被解析为:

 koo2 { K1 y K3 }
 koo2 { K2 y K3 }
 koo2 { K1 y K4 }
 koo2 { K2 y K4 }

在第一种情况下,u-协调的第二部分不是排列的。 在第二种情况下,协调的第二部分是置换的(因为dypgen通常会模糊不清)。

为什么会有所不同?

(它必须以某种方式连接到m和's',因为如果省略了规则,那就行了。)

致以最诚挚的问候,并感谢您的思考

GWF

最小的例子是dypgen-demos的风格,尝试制作一个文件夹&#34; abc&#34;在演示中并将所有提到的,完全引用的文件放在那里。 &#34; parse_tree&#34;:

type tree = 
  | M           of string
  | Mt          of tree * tree
  | N           of string
  | Nt          of tree * tree
  | K           of string
  | U           of string
  | Y           of string
  | Koo         of tree * tree * tree
  | Koo2        of tree * tree * tree * tree

文件&#34; printit.ml&#34;:     打开Parse_tree

let print_abc abc=
  let rec aux1 t = match t with
    | Koo(x1, k, x2) -> (
        print_string "\x1b[1m\x1b[31mkoo {\x1b[21m\027[0m ";
        aux1 x1;
        print_string "";
        aux1 k;
        print_string "";
        aux1 x2;
        print_string "\x1b[1m\x1b[31m}\x1b[21m\027[0m")
    | Koo2(k1, x1, k2, x2) -> (
        print_string "\x1b[1m\x1b[31mkoo2 {\x1b[21m\027[0m ";
        aux1 k1;
        print_string " ";
        aux1 x1;
        print_string "";
        aux1 k2;
        print_string "";
        aux1 x2;
        print_string "\x1b[1m\x1b[31m}\x1b[21m\027[0m")
    | Word (w) -> print_string (w ^ " ")
    | M (w) -> print_string (w ^ " ")
    | K (w) -> print_string (w ^ " ")
    | N (w) -> print_string (w ^ " ")
    | U (w) -> print_string (w ^ " ")
    | Y (w) -> print_string (w ^ " ")
    | Nt (p, l)
    | Mt (p, l) -> (
        print_string "";
        aux1 p;
        print_string " > ";
        aux1 l;)
  in
    let aux2 t = aux1 t; print_newline () in
  List.iter aux2 abc

和&#34; main&#34;程序:     打开Parse_tree     打开Printit

let () = print_endline "
please try:
  K1|K2 u K3|K4
and
  K1|K2 y K3|K4
"

let lexbuf = Dyp.from_channel (Abc_parser.pp ()) stdin

let _ =
  try
    while true do
      (Dyp.flush_input lexbuf;
      try
        let pf = Abc_parser.main lexbuf in
        print_abc (List.map (fun (x,_) -> x) pf)
      with
        Dyp.Syntax_error -> Printf.printf "Syntax error\n\n"
      );
      flush stdout
    done
  with Failure _ -> exit 0

和&#34; Makefile&#34;

SOURCES = printit.ml abc_parser.dyp abc.ml
REP = -I ../../dyplib
CAMLC = ocamlc $(REP)
DYPGEN = ../../dypgen/dypgen --ocamlc "-I ../../dyplib"
LIBS=dyp.cma

all: abc

SOURCES1 = $(SOURCES:.mll=.ml)
SOURCES2 = $(SOURCES1:.dyp=.ml)
OBJS = $(SOURCES2:.ml=.cmo)

abc: parse_tree.cmi $(OBJS)
    $(CAMLC) -o abc $(LIBS) $(OBJS)

.SUFFIXES: .ml .mli .cmo .cmi .dyp

.ml.cmo:
    $(CAMLC) -c $<

.mli.cmi:
    $(CAMLC) -c $<

.dyp.ml:
    $(DYPGEN) $<
    $(CAMLC) -c $*.mli

clean:
    rm -f *.cm[iox] *~ .*~ *.o
    rm -f abc
    rm -f *.extract_type *_temp.ml
    rm -f *parser.ml *parser.mli

1 个答案:

答案 0 :(得分:0)

我使用dypgen而不是模糊处理。

“合并点”是输入流中的一个点,其中相同非终结符的两个解析完成。如果此时由您的操作代码构造的AST是相同的,您可以安全地丢弃两个解析中的任何一个:可能有两种方法来解析非终端,但两者的结果相同。

如果结果不同,默认情况下dypgen会抛出一个,除非你告诉它保留所有替代品(你有)。

我不完全确定我理解你的语法,但是你的语法中有一个棘手的东西可以解释你的问题。

Dypgen是GLR,但它没有做适当的GLR。如果你有像

这样的递归

x = A x |甲

y = y B |乙

dypgen执行尾部和头部优化并将递归转换为循环。你在“一次性”中拥有它。一个真正的LR解析器只能处理左递归并且会在右递归时进行barf。 Dypgen处理两者。

在回溯解析器中,如果你有像A * A这样的语法作为语法,它首先在尾随A上失败,因为前导A *在输入中占用所有A,所以它回溯。 GLR没有回溯,而是提出新的解析。但如果它有尾部或头部优化递归到循环,它就不能这样做。

我怀疑这与你的问题有关。如果您尝试在AAAAA输入上解析A * A *,它应该提供6种可能的解析。