我想知道,如果这是一个错误或行为,那是发明人的意图。
这里我有一个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
答案 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种可能的解析。