在特殊情况下进一步解析表达式

时间:2012-12-19 18:15:56

标签: parsing ocaml frontend lexer

目前,我的前端可以解析123"abcd""=123""=TRUE+123"这样的正常表达式......以下是相关代码:

(* in `syntax.ml`: *)
and expression =
  | E_integer of int  
  | E_string of string

(* in `parser.mly`: *)
expression:
  | INTEGER { E_integer $1 }
  | STRING { E_string $1 }

现在我想改进解析器,这样,当我们遇到以=开头的字符串时,我们会尝试将其评估为公式,而不是文字字符串。所以syntax.ml变成了:

(* in `syntax.ml`: *)
and expression =
  | E_integer of int  
  | E_string of string
  | E_formula of formula

and formula =
  | F_integer of int  
  | F_boolean of bool
  | F_Add of formula * formula

问题是我不确定如何更改parser.mly,我尝试过这个不起作用(This expression has type string but an expression was expected of type Syntax.formula):

(* in `parser.mly`: *)
expression:
  | INTEGER { E_integer $1 }
  | STRING { 
    if String.sub $1 1 1 <> "="
    then E_string $1
    else E_formula (String.sub $1 2 ((String.length $1) - 1)) }

我不知道如何让解析器知道,对于以=开头的字符串,我需要根据formula的规则进一步解析它...任何人都可以帮助?

关注gasche的评论

我同意我需要一个公式的解析器。现在问题是我是否需要单独的lexer.mll公式。我希望不是,因为整个程序只有一次,不是吗?另外,我可以直接将公式语法添加到现有的parser.mly吗?

在当前lexer.mll中,我有:

let STRING = double_quote ([^ '\x0D' '\x0A' '\x22'])* double_quote
rule token = parse
  | STRING as s { STRING s }

我想我可以直接在这里做点什么:

let STRING = double_quote ([^ '\x0D' '\x0A' '\x22'])* double_quote    
let FORMULA_STRING = double_quote = ([^ '\x0D' '\x0A' '\x22'])* double_quote
rule token = parse
  | FORMULA_STRING as fs { XXXXX }
  | STRING as s { STRING s }

我不确定在XXXXX的地方我应该写什么,如果是Parser_formula.formula token fs,我是否单独parser_formula.mly?如果我只有parser.mly包含所有语法,包括公式?

,该怎么办?

2 个答案:

答案 0 :(得分:4)

问题在于您的行

    else E_formula (String.sub $1 2 ((String.length $1) - 1))

而不是类型为(String.sub ...)的{​​{1}},您应该返回类型为string的值。如果您有Syntax.formula函数,可以在这里写

parse_formula : string -> Syntax.formula

我认为你可以通过首先将公式语法定义为单独的解析器来定义这样的函数。

您拥有编辑后

编辑

  • 如果你去为公式调用不同的解析器,你不需要定义不同的词法分析器

  • 如果您选择在词法分析器级别处理字符串和公式之间的区别(您确定这是正确的吗?那么以'='开头的真实字符串怎么样?),那么您不需要公式的单独解析器,您可以将它们作为当前语法中的规则。但要做到这一点,你需要你的词法分析器在公式上采用更细粒度的方式:而不是仅仅将 else E_formula (parse_formula (String.sub $1 2 ((String.length $1) - 1))) 识别为单个标记,你应该将"=.*"识别为公式的开头,和 lex 公式的其余部分,直到您遇到结束"=。为避免冲突,您可能希望使用lexing规则处理简单字符串,而不是简单的正则表达式。

如果你能够采用第二种方法,我认为这确实是一个更简单的想法。

PS:一旦变量不连续(因为中间终端),或者你需要多次重复它,请使用menhir变量命名工具而不是"

答案 1 :(得分:2)

继续@gasche的回答。

您希望在解析器中包含新的语法规则,这意味着您需要更改 parser.mly 中的语法规则以适应这些新规则。

String.sub方法在某种程度上正确,但您实际上是手动执行mly文件可以让您自动化的方法。

考虑您的formula类型:F_Add数据类型可让您对二进制和公式进行编码,从而包含2个公式。 在mly文件中,您可以将其描述为:

formula:
   INTEGER                              { F_Integer $1 }
  | BOOL                                   { F_Bool $1 }
  | formula PLUS formula   { F_Add ($1, $3) }
;

请注意语法规则定义如何镜像formula类型定义。 正如您所看到的,公式的递归属性很适合您的语法规则。

关于lexer.mll,正则表达式STRINGFORMULA_STRING完全相同。如果您在相同的词法分析器规则中使用它们(如在代码段中),它将无法按预期工作。词法分析器不知道解析器中发生了什么,当强制解析器填充特定规则时,无法选择提供STRINGFORMULA_STRING使用ocamlyacc(以及从中汲取灵感的工具),它以相反的方式工作:解析器接收词法分析器从文本流中识别的标记,并尝试根据什么找到与它们对应的规则他以前已经想过了。

请注意,BOOL终端必须由_lexer.mll (just like INTEGER`进行重新编号,因此您需要使用正确的正则表达式对其进行修改。

另外,您应该问自己以下问题: 在=5公式中,是否有某个表达式等待被发现?

如果是这样,你能用表达式和新标记重新表述公式的定义吗?