|
Hi everyone, I continued my work on the Scala back-end for SableCC, and the code created is much more usable. [*] Each custom token is a case class which allows to extract the text, the line and the position [*] Each alternative is a normal class with a companion object declaring the unapply method for pattern matching. The difference is that the unapply method only extracts the public children (custom tokens and alternatives), so that there is no problem with package protected classes. Moreover, an alternative-class declares a copy method in the same way as case classes (see http://programming-scala.labs.oreilly.com/ch06.html#TheCopyMethodInScalaVersion28). This method can be used for AST rewrites in a Transformer. So for the moment for the _expression_ example grammar, we could write a simple interpreter using either the Traverser or the Transformer as show below: import java.io._ import language_exp._ import scala.collection.mutable.Stack object Main { object int { def unapply(s: String) = try { Some(s.toInt) } catch { case _ => None } } def main(args: Array[String]) { val in = new FileReader(args(0)) val syntaxTree = new Parser(in).parse println("=== With Traverser ===") new Traverser { val stack = new Stack[Int] override def traverse(node: Node) = node match { case NProgram(_, _, e) => traverse(e) println(stack) case NNum(int(i), _, _) => stack push i case NExp_Add(_, _, l, r) => traverse(l) traverse(r) stack.push(stack.pop + stack.pop) case NExp_Mul(_, _, l, r) => traverse(l) traverse(r) stack.push(stack.pop * stack.pop) case _ => super.traverse(node) } }.traverse(syntaxTree) println("=== With Transformer ===") println(new Transformer { override def transform(node: Node): Node = node match { case NProgram(_, _, e) => transform(e) case NExp_Num(_, _, n: NNum) => n case NExp_Add(line, pos, l, r) => (transform(l), transform(r)) match { case (NNum(int(l), line, pos), NNum(int(r), _, _)) => NNum((l + r).toString, line, pos) case p => throw new Exception("NNum expected but found: " + p) } case NExp_Mul(line, pos, l, r) => (transform(l), transform(r)) match { case (NNum(int(l), line, pos), NNum(int(r), _, _)) => NNum((l * r).toString, line, pos) case p => throw new Exception("NNum expected but found: " + p) } case _ => super.transform(node) } }.transform(syntaxTree)) } }The output for input "1 + 5 * 2;" is: === With Traverser === Stack(11) === With Transformer === "11"@(1,1)Any feedback on this is welcome. I also thought about the pertinence of extracting the line and position, but I am not sure about this yet. As you can see in the small example, we often ignore them (the two first parameters for alternatives and the two last ones for tokens). They are always available through their accessors so maybe we could remove them from the extractor method to keep only children (tokens or alternatives). For people interested in this work, I forked the git repository on GitHub at this address: http://github.com/gnieh/sablecc All the best Lucas |