使用Scala构建类似C的解释器

时间:2014-04-01 12:21:01

标签: c scala

我需要实现类似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.

那么,我怎样才能解决这个问题,以获得正确的选择,内存的最终值和环境?

1 个答案:

答案 0 :(得分:1)

问题(一旦明显的代码错误被修复)似乎就在这里

def interpretCTREE(c: Ctree): Unit = c match {
    case Assign(l, e) => env + (interpretLTREE(l).toString -> interpretETREE(e))
    /* ... */

取决于lVar(x)还是Star(l),这应该有不同的行为。

如果lVar(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)}
  }
}