目前我正在努力解释Scala中的Blocks。我的代码看起来像这样:
AST:
case class Braces(value: Node) extends Node
case class Block(value: List[Node]) extends Node
case class VariableDeclaration(name: String, value: Node) extends Node
case class VariableAssignment(name: String, value: Node) extends Node
case class CallFunction(name: String, value: List[Node]) extends Node
case class Variable(name: String) extends Node
case class Integer(int: Int) extends Node
解释器: (我不想更改签名或解释函数的参数。我的首选解决方案包括全局环境堆栈)
sealed trait iValue
type VarEnv = Map[String, iValue]
def interpret(env: VarEnv, body: Node): iValue = body match
{
case Block(value: List[Node]) => {
???
}
case VariableDeclaration(....)
case VariableAssignment(....)
case CallFunction(....)
}
示例测试用例应如下所示:
{
$a = 5;
$b = 4;
{
$a = 2;
b = add(a, b);
};
add(b, { a; });
}
iValue(11))
如何实现这一点,包括隐藏变量?
答案 0 :(得分:2)
我刚刚完成了一个功能很强的翻译代码,这里有一个简短的演练。
您想重新分配变量,因此它看起来像是一种命令式语言。因此,我们将分别处理语句和表达。以下是与示例相关的AST元素:
sealed trait Stmt
case class DeclAsgn(varName: String, rhs: Expr) extends Stmt
case class Asgn(varName: String, rhs: Expr) extends Stmt
case class Print(expr: Expr) extends Stmt
case class Block(stmts: List[Stmt]) extends Stmt
sealed trait Expr
case class Num(n: Int) extends Expr
case class Sub(a: Expr, b: Expr) extends Expr
case class Var(name: String) extends Expr
我们希望评估表达式。因此,我们必须定义值是什么。目前,只有整数值(iValue
?)和默认None
- 值(如Python中的None
,而不是Scala - None
):
sealed trait Value
case object NoneValue extends Value
case class IntValue(i: Int) extends Value
现在有趣的部分:环境。请注意区分"环境"和"阻止环境"是不够的:块可以嵌套到任意深度,所以你需要一个完整的堆栈变量绑定:
case class Env(stack: List[Map[String, Value]]) extends (String => Value) {
def apply(varName: String) = stack match {
case Nil => throw new Error("Undefined var: " + varName)
case h :: t => h.getOrElse(varName, Env(t)(varName))
}
def enterBlock: Env = Env(Map.empty[String, Value] :: stack)
def exitBlock: Env = Env(stack.tail)
def withDeclaredVar(name: String): Env = {
val h :: t = stack
if (h contains name) throw new Error(s"Variable $name already declared")
else Env(h.updated(name, NoneValue) :: t)
}
def updated(name: String, value: Value): Env = stack match {
case Nil => throw new Error("Could not set variable " + name)
case h :: t =>
if (h contains name) Env(h.updated(name, value) :: t)
else Env(h :: Env(t).updated(name, value).stack)
}
def withDeclaredVar(name: String, value: Value): Env = {
this.withDeclaredVar(name).updated(name, value)
}
}
现在,每次输入一个块,你只需在这个堆栈的顶部放一个新的空Map[String, Value]
,这样就可以将所有新声明的变量插入到这个地图中(遮蔽以前的级别),并且你完成了这个块,你可以简单地丢弃最顶层的地图(取消之前阴影层的阴影)。
这里还有一个小辅助方法,用于实例化整个程序运行的初始空环境:
object Env {
def empty = Env(List(Map.empty[String, Value]))
}
一旦正确设置了所有数据结构,表达式的评估和副作用语句的解释就很简单:
def eval(env: Env, expr: Expr): Value = expr match {
case Var(v) => env(v)
case Sub(a, b) => (eval(env, a), eval(env, b)) match {
case (IntValue(va), IntValue(vb)) => IntValue(va - vb)
case sthElse => throw new Error("`Sub` not applicable to " + sthElse)
}
case Num(n) => IntValue(n)
}
def interpret(env: Env, stmt: Stmt): Env = stmt match {
case Block(stmts) =>
stmts.foldLeft(env.enterBlock)(interpret).exitBlock
case Print(e) => {
println(eval(env, e))
env
}
case DeclAsgn(v, r) => {
val rhsVal = eval(env, r)
env.withDeclaredVar(v, rhsVal)
}
case Asgn(v, r) => {
var rhsVal = eval(env, r)
env.updated(v, rhsVal)
}
}
为了看到一些东西,让我们快速定义漂亮的打印功能:
def prettyPrint(prog: Stmt): String = prog match {
case DeclAsgn(v, r) => s"$$${v} = ${prettyPrint(r)}"
case Asgn(v, r) => s"${v} = ${prettyPrint(r)}"
case Print(e) => s"print(${prettyPrint(e)})"
case Block(xs) => xs
.map(prettyPrint)
.mkString(";\n")
.split("\n")
.map(" " + _)
.mkString("{\n", "\n", "\n}")
}
def prettyPrint(expr: Expr): String = expr match {
case Num(n) => n.toString
case Sub(a, b) => s"sub(${prettyPrint(a)},${prettyPrint(b)})"
case Var(v) => v
}
现在举个例子。我已经插入了一些print
- 语句,因此我们可以观察到中间结果。这是AST代码:
/* example */ {
import scala.language.implicitConversions
val c = Var("c")
val d = Var("d")
implicit def intToNum(i: Int): Expr = Num(i)
val ast = Block(List(
DeclAsgn("c", 5),
DeclAsgn("d", 4),
Block(List(
Asgn("c", 3),
DeclAsgn("d", 45),
Print(c),
Print(d),
Print(Sub(d, c))
)),
Print(c),
Print(d),
Print(Sub(c, d))
))
println(prettyPrint(ast))
interpret(Env.empty, ast)
}
这是漂亮的印刷版本:
{
$c = 5;
$d = 4;
{
c = 3;
$d = 45;
print(c);
print(d);
print(sub(d,c))
};
print(c);
print(d);
print(sub(c,d))
}
这是输出:
IntValue(3)
IntValue(45)
IntValue(42)
IntValue(3)
IntValue(4)
IntValue(-1)
正如您所看到的,c
的值被遮蔽,然后没有被遮蔽,内部块中设置的d
的值被简单地丢弃。