我需要实现类似C的解释器。这是语法。
Program ::= Commandlist
Commandlist ::= Command | Command; Commandlist;
Command ::= Left = Expression | while Expression : Commandlist end | print Left
Expression ::= Number | (Expression1 + Expression2) | (Expression1 - Expression2) | Left | &Left
Left ::= Variable | *Left
Number ::= string of digits
Variable ::= string of letters
以下是解释器使用的运算符树。
PTREE ::= [ CTREE+ ]
CTREE ::= Assign(LTREE, ETREE) | While(ETREE, CLIST) | Print(LTREE)
ETREE ::= Num(String) | Add(ETREE, ETREE) | Sub(ETREE, ETREE) | At(LTREE) | Amph(LTREE)
LTREE ::= Var(String) | Star(LTREE)
以下是实现类C解释器的代码。
trait OpTree {
sealed abstract class Ltree
case class Var(x: String) extends Ltree
case class Star(l: Ltree) extends Ltree
sealed abstract class Etree
case class Num(s: String) extends Etree
case class Add(e1: Etree, e2: Etree) extends Etree
case class Sub(e1: Etree, e2: Etree) extends Etree
case At(l: Ltree) extends Etree
case Amph(l: Ltree) extends Etree
sealed abstract class Ctree
case class Assign(l: Ltree, e: Etree) extends Ctree
case class While(e: Etree, c: List[Ctree]) extends Ctree
case class Print(l: Ltree) extends Ctree
}
import scala.util.parsing.combinator.JavaTokenParsers
object MiniC extends JavaTokenParsers with OpTree {
def parse(source: String): List[Ctree] =
parseAll(prog, source) match {
case Success(optree,_) => optree
case _ => throw new Exception("Parse error!")
}
def prog: Parser[List[Ctree]] = commlist
def commlist: Parser[List[Ctree]] = rep1sep(comm, ";")
def comm: Parser[Ctree] = left~("="~>expr) ^^
{ case l~e => Assign(l, e) } |
"print"~>left ^^
{ case l => Print(l) } |
("while"~>expr<~":")~(commlist<~"end") ^^
{ case e~cs => While(e, cs) }
def expr: Parser[Etree] = wholeNumber ^^ (Num(_)) |
"("~>expr~op<~expr")" ^^
{ case e1~"+"~e2 => Add(e1, e2)
case e1~"-"~e2 => Sub(e1, e2) } |
left ^^ (At(_)) |
"&"~>left ^^ (Amph(_))
def left: Parser[Ltree] = ident ^^ (Var(_)) |
"*"~>left ^^ (Star(_))
def op: Parser[String] = "+" | "-"
//Interpreter
val memory = scala.collection.mutable.ArrayBuffer.empty[Int]
var env = Map.empty[String, Int]
def interpretPTREE(p: List[Ctree]): Unit = interpretCLIST(p)
def interpretCLIST(cs: List[Ctree]): Unit =
for(c <- cs) yield interpretCTREE(c)
def interpretCTREE(c: Ctree): Unit = c match {
case Assign(l, e) => env + (interpretLTREE(l).toString -> interpretETREE(e))
case Print(l) =>
if(env.contains(interpretLTREE(l).toString)) println(env(interpretLTREE(l).toString))
else throw new Exception("Error: " + l + " is undefined.")
env
case While(e, cs) =>
if (interpretETREE(e) != 0)
interpretCTREE(c)
else env
}
def interpretETREE(e: Etree): Int = e match {
case Num(s) => s.toInt
case Add(e1, e2) => interpretETREE(e1) + interpretETREE(e2)
case Sub(e1, e2) => interpretETREE(e1) - interpretETREE(e2)
case At(l) => interpretLTREE(l)
case Amph(l) => interpretLTREE(l)
}
def interpretLTREE(l: Ltree): Int = l match {
case Var(x) => if (env.contains(x)) env(x)
else throw new Exception("Error: " + x + " is undefined.")
case Star(l) => if (memory.contains(l)) memory(interpretLTREE(l))
else throw new Exception("Error: " + l + " is undefined.")
}
def main(args: Array[String]): Unit = {
try {
val source = args(0)
println("input : " + source)
val optree = parse(source)
println("optree : " + optree)
interpretPTREE(optree)
println("final memory : " + memory)
println("final namespace : " + env)
}
catch { case e: Exception => println(e)}
}
}
该程序的模糊部分是interpretCTREE,interpretETREE和interpretLTREE的定义部分。如果我用
的参数运行这个程序y = 4; z = &y; x = (7 + *z); *z = (y + 1)
optree的程序结果应如下所示:
List(Assign(Var("x"),Num("4")),
Assign(Var("z"),Amph(Var("y"))),
Assign(Var("x"),Add(Num("7"),Star(Var("z")))),
Assign(Star(Var("z")),Add(Var("y"),Num("1"))))
但是,我的结果看起来像这样:
List(Assign(Var("x"),Num("4")),
Assign(Var("z"),Amph(Var("y"))),
Assign(Var("x"),Add(Num("7"),At(Star(Var("z"))))),
Assign(Star(Var("z")),Add(At(Var("y")),Num("1"))))
另外,我想查看内存和env的值,但结果包含错误消息。
java.lang.Exception: Error: y is undefined.
那么,我怎样才能解决这个问题,以获得正确的选择,内存的最终值和环境?
答案 0 :(得分:1)
问题(一旦明显的代码错误被修复)似乎就在这里
def interpretCTREE(c: Ctree): Unit = c match {
case Assign(l, e) => env + (interpretLTREE(l).toString -> interpretETREE(e))
/* ... */
取决于l
是Var(x)
还是Star(l)
,这应该有不同的行为。
如果l
为Var(x)
,那么,如果环境不包含x
,则应创建新的内存位置,应相应地更新环境,然后更新值{ {1}}应存储在此位置。
如果interpretETREE(e)
为l
,则应评估Star(l)
,并将值l
存储在此评估获得的内存位置。
另外,在我看来,程序创建的optree是正确的,而你提到的第一个(&#34;应该看起来像这样#34;一个)不符合语法。
我试图在下面提出一些固定的代码(没有保证,我还没有对其进行适当的测试)。我所做的更改标有评论。另请注意,您的interpretETREE(e)
及相关方法的编写方式就好像它们会返回一个环境一样,但由于没有使用,我省略了该部分。
interpretCTREE
最后,这是一个计算斐波纳契数的简单测试程序
trait OpTree {
sealed abstract class Ltree
case class Var(x: String) extends Ltree
case class Star(l: Ltree) extends Ltree
sealed abstract class Etree
case class Num(s: String) extends Etree
case class Add(e1: Etree, e2: Etree) extends Etree
case class Sub(e1: Etree, e2: Etree) extends Etree
// typo fixed, "class" forgotten
case class At(l: Ltree) extends Etree
case class Amph(l: Ltree) extends Etree
sealed abstract class Ctree
case class Assign(l: Ltree, e: Etree) extends Ctree
case class While(e: Etree, c: List[Ctree]) extends Ctree
case class Print(l: Ltree) extends Ctree
}
import scala.util.parsing.combinator.JavaTokenParsers
object MiniC extends JavaTokenParsers with OpTree {
// Parser
def parse(source: String): List[Ctree] =
parseAll(prog, source) match {
case Success(optree,_) => optree
case _ => throw new Exception("Parse error!")
}
def prog: Parser[List[Ctree]] = commlist
def commlist: Parser[List[Ctree]] = rep1sep(comm, ";")
def comm: Parser[Ctree] = left~("="~>expr) ^^
{ case l~e => Assign(l, e) } |
"print"~>left ^^
{ case l => Print(l) } |
("while"~>expr<~":")~(commlist<~"end") ^^
{ case e~cs => While(e, cs) }
// typo fixed "~" forgotten in "expr~op~expr"
def expr: Parser[Etree] = wholeNumber ^^ (Num(_)) |
"("~>expr~op~expr<~")" ^^
{ case e1~"+"~e2 => Add(e1, e2)
case e1~"-"~e2 => Sub(e1, e2) } |
left ^^ (At(_)) |
"&"~>left ^^ (Amph(_))
def left: Parser[Ltree] = ident ^^ (Var(_)) |
"*"~>left ^^ (Star(_))
def op: Parser[String] = "+" | "-"
//Interpreter
// new: fix some memory size (for test purposes)
val MEMORY_SIZE = 16
val memory = new Array[Int](MEMORY_SIZE)
// new: represents first unused memory address
var allocatedMemorySize = 0
var env = Map.empty[String, Int]
// new: simple memory allocation
def allocateMemory(variableName : String) {
env += (variableName -> allocatedMemorySize)
allocatedMemorySize += 1
}
def interpretPTREE(p: List[Ctree]) {
interpretCLIST(p)
}
def interpretCLIST(cs: List[Ctree]) {
for(c <- cs) interpretCTREE(c)
}
def interpretCTREE(c: Ctree) {
c match {
case Assign(l, e) =>
// new: allocate memory for variables if necessary
l match {
case Var(x) => if (!env.contains(x)) allocateMemory(x)
case _ =>
}
// new: store evaluated expression at evaluated address
memory(interpretLTREE(l)) = interpretETREE(e)
// new: handling print
case Print(l) =>
println(memory(interpretLTREE(l)))
// new: handling while properly
case While(e, cs) =>
while (interpretETREE(e) != 0)
interpretCLIST(cs)
}
}
def interpretETREE(e: Etree): Int = e match {
case Num(s) => s.toInt
case Add(e1, e2) => interpretETREE(e1) + interpretETREE(e2)
case Sub(e1, e2) => interpretETREE(e1) - interpretETREE(e2)
// new: at returns the value that is stored in memory at the address represented by l
case At(l) => memory(interpretLTREE(l))
// new: amph returns the address that is represented by l
case Amph(l) => interpretLTREE(l)
}
// new: evaluates l to memory address it represents
def interpretLTREE(l: Ltree): Int = l match {
case Var(x) => if (env.contains(x))
env(x)
else
throw new Exception("Error: " + x + " is undefined.")
case Star(l) => val memoryLocation = interpretLTREE(l)
if (0 <= memoryLocation && memoryLocation < MEMORY_SIZE)
memory(memoryLocation)
else
throw new Exception("Error: " + l + " is out of memory bounds.")
}
def main(args: Array[String]): Unit = {
try {
val source = args(0)
println("input : " + source)
val optree = parse(source)
println("optree : " + optree)
interpretPTREE(optree)
// new: nicer printing of memory
println("final memory : " + (memory mkString ","))
println("final namespace : " + env)
}
catch { case e: Exception => println(e)}
}
}