有没有办法在我的递归函数中强制使用Variant类型

时间:2019-02-15 14:20:30

标签: types ocaml utop

我已完成作业的最后一个问题,要求以下内容

“添加本地定义构造“在v中设置v = e” ,其中v是变量,e和f是表达式。这意味着v在f中具有e值,并且覆盖环境中v的任何值(或也包含封闭的let)。与前面的示例一样,您需要考虑一种语法,可以轻松地使用现有解析器的扩展来解析该语法。 / p>

我的问题是,在执行let语句时,无法识别给定代码库中的解析器和编译器在下面概述的预定义expr类型,并且收到以下错误:

错误:此表达式的类型为expr,但期望的表达式类型为char

我已经向解析器添加了功能,该功能可以转换形式为“ 〜var1 = exp1> exp2 ”的let语句,其中解析器的输出形式为Bes“ ( v1,e1,e2)”。这一切都已验证并且可以正常工作。我的问题出现在我向编译器match语句添加了一个case的情况下,它可以识别刚才提到的第二种形式;匹配我的“预处理器”功能后,也会如下所示。在返回表达式e2的这种更新形式进行进一步编译之前,应该在编译器中将v1,e1和e2匹配,并在e2上进行递归匹配,用表达式e1替换变量v1的任何实例。相反,我收到上面的匹配错误。

表达定义

type expr =
    Num of int
  | Var of char
  | Add of expr*expr
  | Mul of expr*expr
  | Con of expr*expr*expr
  | Bes of expr*expr*expr

说明定义

type instr =
  | Push of int
  | Fetch of char
  | Add2
  | Mul2
  | Con2

其他类型

type program = instr list

type stack = int list

我的预处理器功能

let rec preprocessor eo2 eo1 vo1  : expr =

    match eo2 with
    | Num n -> eo2
    | Var v -> if (v = vo1) then eo1 else eo2
    | Add (e1, e2) -> Add ((preprocessor e1 eo1 vo1),(preprocessor e2 eo1 vo1))
    | Mul (e1, e2) -> Mul ((preprocessor e1 eo1 vo1),(preprocessor e2  eo1 vo1))
    | Con (e1,e2,e3) -> Con ((preprocessor e1 eo1 vo1), (preprocessor e2     eo1 vo1), (preprocessor e3 eo1 vo1))
(*  | Bes (v1,e1,e2) -> (preprocessor e2 e1 v1) *)

编译器功能

(*
compile : expr -> instr list
*)

let rec compile e =


  match e with
  | Num n -> [Push n]
  | Var v -> [Fetch v]
  | Add (e1,e2) -> compile e2 @ compile e1 @ [Add2]
  | Mul (e1,e2) -> compile e2 @ compile e1 @ [Mul2]
  | Con (e1,e2,e3) -> compile e3 @ compile e2 @ compile e1 @ [Con2]
  | Bes (v1,e1,e2) -> compile (preprocessor e2 e1 v1) @ [] (*ERROR SOURCE LINE*)

我希望预处理器函数将子表达式e2中的任何变量更改为表达式e1,然后可以在编译过程中继续进行该操作。相反,我收到错误消息,告诉我提供了expr而不是预期的char。我已经尝试了一些事情,例如使用let语句将e2 e1和v1分配给新变量,在这些变量中我为它们明确提供了expr类型(e2:expr)等,我也试图从预处理器中显式返回expr,但我仍然遇到问题。代码库似乎太大了,不能仅将其转储到这里,所以如果我应该发布功能解析器,请告诉我。谢谢您的帮助

1 个答案:

答案 0 :(得分:0)

  1. 错误来自v1类型为expr的错误,而您需要一个char,因为您正在将v1与每个v进行比较,或者Var v,其类型为char。问题的根源在于您的Bes构造函数类型错误,应该为Bes of char * expr * expr,因为您语言的v构造中的let v = x in y应该是变量,在您的实现中以char类型表示。

  2. 使用预处理来实现let不是一个好主意:

    2.1。它将使您的代码爆炸,请考虑以下示例:

    let x = <very-big-expr> in
    let y = x + x + x in
    y + y
    

    最终将复制<very-big-exp> 6次。一般来说,这将是指数级爆炸,将导致AST的千兆字节

    2.2。复制表达式并不总是有效的,例如,在某些语言中可能会有副作用的语言,这会破坏程序的语义,请考虑以下示例

    let x = read_int () in
    let y = read_int () in
    x*x + y
    

    假设输入为"3\n4\n",则正确的实现应返回3 * 3 + 4 = 13,而您的实现将导致产生代码,

    read_int () * read_int () + read_int ()
    

    ,它将读取两个整数并将它们相乘并要求第三个整数,并以通道错误结尾失败。

    这意味着,您必须保留Bes作为您的语言的原始语言并进行正确的编译。

  3. 如果您决定坚持预处理阶段,那么您不应该直接将Bes添加到原语集,并在每次解析器时执行AST-> AST转换看到let语句。这样一来,您将永远不会在编译器代码中看到Bes。本质上,这将使let成为语法糖或宏,这再次是Let的不正确语义,请参见上面的(1)和(2)。