3

目前我的前端可以解析诸如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的公式。我希望不会,因为只对整个程序进行一次 lex 是一种逻辑,不是吗?另外,我可以直接将公式语法添加到现有的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 fsparser_formula.mlyparser.mly

4

2 回答 2

4

问题出在你的线路上

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

而不是(String.sub ...), 它有 type string,你应该返回一个 type 的值Syntax.formula。如果你有一个parse_formula : string -> Syntax.formula函数,你可以在这里写

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

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

编辑:在您自己编辑之后:

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

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

如果你能得到第二种方法来工作,我认为这确实是一个更简单的想法。

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

于 2012-12-19T19:51:38.860 回答
2

继续@gasche 的回答。

您想在parser中包含新的语法规则,这意味着您需要更改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完全相同。如果您在相同的词法分析器规则中使用它们(如在您的代码片段中),它将不会像您期望的那样工作。词法分析器不知道解析器中发生了什么,它不能选择在解析器方便填写特定规则时提供 aSTRING或 a 。使用 ocamlyacc(以及它从中汲取灵感的工具),它可以工作FORMULA_STRING反之亦然:解析器接收词法分析器从文本流中识别的标记,并尝试根据他之前已经弄清楚的内容找到与它们对应的规则。

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

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

如果是这样,您能否根据表达式和新标记重新制定公式的定义?

于 2012-12-20T02:37:41.973 回答