【问题标题】:Is this handling of ambiguities in dypgen normal or is it not?这种处理 dypgen 中的歧义是否正常?
【发布时间】:2016-09-15 11:36:37
【问题描述】:

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

这里我有一个 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) }

这个例子可以处理这样的语法:

想想 ?和 * 作为正则表达式运算符,'s' 和 'm' 和 'K' 作为词法。

  s = m? n* K

“K”、“m”和“n”也可以替换为这些字母和后面的 1-9 之间的数字 或者它们可以替换为由“|”分隔的列表作为

  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

现在有趣的一点: 在语法中还有另一个特点。在自然语言的风格中,有一个带有“u”或“y”的“协调绑定”。

这可以将这些“短语”列表(一个带有可选前置“m”和可选数字“n”的“K”字母)绑定到“K1 和 K2”之类的东西。 语法可以解析:

 K1|K2 u K3|K4

 K1|K2 y K3|K4

正如我所想,它应该有相同的结果。 但是“协调绑定”之间的区别是: lexem 'u' 被定义为歧义列表,其方式与 m、n、K 相同,也可以与 'K's、'm's、'n's 混合 lexem 'y' 是在没有这个列表元素的情况下定义的。

这产生了(令人惊讶的)不同:

 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 和 n's,因为如果 'n's 的规则被忽略,它就可以工作。)

最好的问候,并感谢您考虑

gwf


最小的例子是 dypgen-demos 的风格,尝试在演示中创建一个文件夹“abc”并将所有提到的、完全引用的文件放在那里。 “parse_tree”:

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

一个文件“printit.ml”: 打开 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

和“主”程序: 打开 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

和“Makefile”

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

【问题讨论】:

  • 我编辑了我的重要文本行。我在输出的描述中混淆了'u'和'y':“K1|K2 y K3|K4” -> koo { K1 y K3 }, koo { K2 y K3 }, koo { K1 y K4 }, koo { K2 y K4 } 和 "K1|K2 u K3|K4" -> koo { K1 u K4 }, koo { K2 u K4 }

标签: parsing nlp ocaml lexer ambiguity


【解决方案1】:

我使用 dypgen 但不使用歧义处理。

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

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

我不确定我是否理解您的语法,但是您的语法中有一个棘手的问题可以解释您的问题。

Dypgen 是 GLR,但它不能做正确的 GLR。如果你有像

这样的递归

x = A x |一个

y = y B |乙

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

在回溯解析器中,如果您有像 A*A 这样的语法,它首先在尾随 A 上失败,因为前导 A* 吃掉了输入中的所有 A,所以它回溯。 GLR 不会回溯,而是分叉一个新的解析。但如果它有尾部或头部优化递归到循环,它就不能这样做。

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

【讨论】:

  • 感谢您的考虑。我给 Manu Onzon 写了一封电子邮件,然后他说:“正如所承诺的那样,我会用你的 abc 语法示例回复你关于缺少解析树的问题。原来问题的根源是 dypgen 中的一个错误。我现在已经修复它并将修复推送到(全新的)dypgen repo:github.com/manu291/dypgen“这是我在此处发布的相同问题和示例。然后它做到了。
  • 酷!我希望他还为 Ocaml 6 升级了 Dypgen(字节等)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-04-06
  • 2012-01-15
  • 1970-01-01
  • 2013-06-02
  • 1970-01-01
  • 2015-02-14
  • 2018-04-28
相关资源
最近更新 更多