F# 用OcamlYacc/FsYacc表示可选语法和重复

F# 用OcamlYacc/FsYacc表示可选语法和重复,f#,parsing,ocaml,ocamlyacc,fsyacc,F#,Parsing,Ocaml,Ocamlyacc,Fsyacc,我正在努力培养一些语法分析的技能。我回顾了我为SQL编写的一个简单解析器,我并不完全满意它——似乎应该有一种更简单的方法来编写解析器 SQL把我绊倒了,因为它有很多可选标记和重复。例如: SELECT * FROM t1 INNER JOIN t2 INNER JOIN t3 WHERE t1.ID = t2.ID and t1.ID = t3.ID 相当于: SELECT * FROM t1 INNER JOIN t2 ON t1.ID = t2.ID INNER JOIN t3 on t1

我正在努力培养一些语法分析的技能。我回顾了我为SQL编写的一个简单解析器,我并不完全满意它——似乎应该有一种更简单的方法来编写解析器

SQL把我绊倒了,因为它有很多可选标记和重复。例如:

SELECT *
FROM t1
INNER JOIN t2
INNER JOIN t3
WHERE t1.ID = t2.ID and t1.ID = t3.ID
相当于:

SELECT *
FROM t1
INNER JOIN t2 ON t1.ID = t2.ID
INNER JOIN t3 on t1.ID = t3.ID
上的
中的
子句是可选的,可以出现多次。我在解析器中处理了这些问题,如下所示:

%{
open AST
%}

// ...    
%token <string> ID   
%token AND OR COMMA
%token EQ LT LE GT GE
%token JOIN INNER LEFT RIGHT ON
// ...

%%

op: EQ { Eq } | LT { Lt } | LE { Le } | GT { Gt } | GE { Ge }

// WHERE clause is optional    
whereClause:
    |                       { None }
    | WHERE whereList       { Some($2) }        

whereList:
    | ID op ID                    { Cond($1, $2, $3) }
    | ID op ID AND whereList      { And(Cond($1, $2, $3), $5) }
    | ID op ID OR whereList       { Or(Cond($1, $2, $3), $5) }


// Join clause is optional    
joinList:
    |                       { [] }
    | joinClause            { [$1] }
    | joinClause joinList   { $1 :: $2 }

joinClause:
    | INNER JOIN ID joinOnClause    { $3, Inner, $4 }
    | LEFT JOIN ID joinOnClause     { $3, Left, $4 }
    | RIGHT JOIN ID joinOnClause    { $3, Right, $4 }

// "On" clause is optional    
joinOnClause:
    |                       { None }
    | ON whereList          { Some($2) }

// ...
%%
我认为这种形式更易于阅读,并且更直观地表达了它试图捕捉的语法。不幸的是,我在Ocaml或F#文档中都找不到任何支持这种符号或类似符号的东西


在OcamlYacc或FsYacc中,有没有一种简单的方法可以用可选或重复的标记来表示语法?

当你编写所有的小片段时,你应该会得到你想要的东西。而不是:

(INNER | RIGHT | LEFT ) 
你只是

inner_right_left
并将其定义为这三个关键字的并集

还可以在lexer中定义并集。在您定义令牌的方式中,或者使用camlp4,我在这方面做得不多,因此我不能建议您采用这些路线。我不认为他们会为你工作,就像到处都有小碎片一样

编辑:

因此,对于camlp4,您可以查看和a和a。提醒你,这并不是你想要的,但很接近。文档非常糟糕,如中所述,但是对于这个特定领域,我认为您不会有太多问题。我对它做了一些修改,可以输入更多的问题。

允许通过其他符号参数化非终端符号,并提供标准快捷方式库,如选项和列表,您可以创建自己的快捷方式。例如:

option(X): x=X { Some x} 
         | { None }
还有一些语法,比如“token”相当于“option(token)”,“token+”相当于“nonempty_list(token)”

所有这些确实缩短了语法定义。此外,它还受ocamlbuild支持,可以作为ocamlyacc的替代品。强烈推荐


有趣的是,我也用它来解析SQL:)

我的SQL已经过时了,但我似乎记得ON或WHERE的用法在所有情况下都不是严格等价的(如果我没记错的话,对于外部联接)。
option(X): x=X { Some x} 
         | { None }