3

我正在尝试在词法分析/解析语法方面建立一些技能。我正在回顾我为 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

ONand子句是可选的WHERE,可以出现多次。我在解析器中处理这些如下:

%{
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) }

// ...
%%

换句话说,我通过将可选语法分解为单独的规则来处理可选语法,并使用递归处理重复。这行得通,但是它将解析分解为一堆小子例程,并且很难看出语法实际代表什么。

如果我可以在括号内指定可选语法并使用 * 或 + 重复,我认为编写起来会容易得多。这会将我的 whereClause 和 joinList 规则减少到以下内容:

whereClause:
    |                       { None }
//    $1    $2, I envision $2 being return as an (ID * op * ID * cond) list
    | WHERE [ ID op ID { (AND | OR) }]+
        { Some([for name1, op, name2, _ in $1 -> name1, op, name2]) }

joinClause:
    |                       { None }

//    $1, returned as (joinType
//                       * JOIN
//                       * ID
//                       * ((ID * op * ID) list) option) list
    | [ (INNER | LEFT | RIGHT) JOIN ID [ON whereClause] ]*
        { let joinType, _, table, onClause = $1;
          Some(table, joinType, onClause) }

我认为这种形式容易阅读,并且更直观地表达了它试图捕捉的语法。不幸的是,我在 Ocaml 或 F# 文档中找不到任何支持此表示法或类似内容的内容。

在 OcamlYacc 或 FsYacc 中是否有一种简单的方法来表示具有可选或重复标记的语法?

4

2 回答 2

3

当你编写所有的小片段时,你应该得到你想要的东西。代替:

(INNER | RIGHT | LEFT ) 

你只有

inner_right_left

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

您还可以在词法分析器中定义联合。在您定义令牌的方式或使用camlp4 方面,我在这方面做得不多,所以我不建议您采用这些路线。而且我认为它们不会为您工作,而只是到处都有小块。

编辑:

因此,对于camlp4,您可以查看Camlp4 语法模块以及教程更好的教程。请注意,这并不完全是您想要的,但它非常接近。正如最近关于 ocaml groups的讨论所表达的那样,文档非常糟糕,但是对于这个特定领域,我认为您不会遇到太多问题。我做了一点,可以提出更多问题。

于 2009-02-10T20:25:48.753 回答
3

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

option(X): x=X { Some x} 
         | { None }

还有一些语法糖,“令牌?” 相当于'option(token)','token+' 到'nonempty_list(token)'。

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

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

于 2009-06-26T09:28:14.307 回答