我已完成作业的最后一个问题,要求以下内容
“添加本地定义构造“在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,但我仍然遇到问题。代码库似乎太大了,不能仅将其转储到这里,所以如果我应该发布功能解析器,请告诉我。谢谢您的帮助
答案 0 :(得分:0)
错误来自v1
类型为expr
的错误,而您需要一个char
,因为您正在将v1
与每个v
进行比较,或者Var v
,其类型为char
。问题的根源在于您的Bes
构造函数类型错误,应该为Bes of char * expr * expr
,因为您语言的v
构造中的let v = x in y
应该是变量,在您的实现中以char
类型表示。
使用预处理来实现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
作为您的语言的原始语言并进行正确的编译。
如果您决定坚持预处理阶段,那么您不应该直接将Bes
添加到原语集,并在每次解析器时执行AST-> AST转换看到let语句。这样一来,您将永远不会在编译器代码中看到Bes
。本质上,这将使let
成为语法糖或宏,这再次是Let的不正确语义,请参见上面的(1)和(2)。