[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Scala back-end for SableCC 4



Hello everybody,

I wanted to write a parser in Scala and wrote the grammar with SableCC. Even if I could use the generated Java classes without any problem, I wanted to use the Scala specific features such as pattern matching and case classes. So I wrote an objectmacro file to generate Scala code instead of Java code. It is still the first version but the generated code allows to use the parser and two new Scala classes (replacing the Walker class in Java) [*] Traverser: traverses the produced AST, by default, only propagates to the children [*] Transformer: traverses the produced AST and may transform the visited node and return the transformed one. These two elements are mostly inspired by the strategy used in the Scala compiler itself (actually I wanted to write a "sub-parser" with transformations in the Scala compiler...) The generated alternatives are case classes which allows the use of pattern matching on AST nodes

For the Exp example the generated code is as follows:

Traverser.scala: simply traverse the children
class Traverser {
  def traverse(node: Node): Unit = node match {
    case NProgram(line, pos, eExp, e$0) =>
      traverse(eExp)
      traverse(e$0)
    case NExp_Add(line, pos, eExp$1, eOp, eExp$2) =>
      traverse(eExp$1)
      traverse(eOp)
      traverse(eExp$2)
    case NExp_Mul(line, pos, eExp$1, eOp, eExp$2) =>
      traverse(eExp$1)
      traverse(eOp)
      traverse(eExp$2)
    case NExp_Num(line, pos, eNum) =>
      traverse(eNum)
    case _ => // do nothing
  }
}

Transformer.scala: creates a new node with the transformed children
class Transformer {
  def transform(node: Node): Node = node match {
    case NProgram(line, pos, eExp, e$0) =>
NProgram(line, pos, transform(eExp).asInstanceOf[NExp], transform(e$0).asInstanceOf[N$0])
    case NExp_Add(line, pos, eExp$1, eOp, eExp$2) =>
NExp_Add(line, pos, transform(eExp$1).asInstanceOf[NExp], transform(eOp).asInstanceOf[N$1], transform(eExp$2).asInstanceOf[NExp])
    case NExp_Mul(line, pos, eExp$1, eOp, eExp$2) =>
NExp_Mul(line, pos, transform(eExp$1).asInstanceOf[NExp], transform(eOp).asInstanceOf[N$2], transform(eExp$2).asInstanceOf[NExp])
    case NExp_Num(line, pos, eNum) =>
      NExp_Num(line, pos, transform(eNum).asInstanceOf[NNum])
    case _ => node
  }
}

Test.scala: shows an example on how to use the Traverser class
object Test {

  def main(args: Array[String]) {

    val in =
      if(args.length > 0)
        new FileReader(args(0))
      else
        new InputStreamReader(System.in)

    val syntaxTree = new Parser(in).parse
    println()
    new Traverser() {
      override def traverse(node: Node) = node match {
        case _: Token =>
println(node.tpe + ":\"" + node.text + "\"@(" + node.line + "," + node.pos + ")");
        case _ => super.traverse(node)
      }
    }.traverse(syntaxTree)
  }
}

The main problem for the moment is the access to non public types (N$0, N$1, ...) which should probably be replaced by Token instead. I would like to have your opinion on this first version. You can find the git patch attached to generate Scala code. I added a class named ScalaGenerator to generate the Scala code (it is not really clean yet, I just copied the Java code generation and adapted it to my needs. Any feedback/idea is welcome. I am also looking how a specific option could be set (for the Scala target) to generate classes that can be used with kiama (http://code.google.com/p/kiama/). You can find a git patch attached for the current master available on GitHub.

All the best
Lucas

PS: @Etienne if you want I can open a ticket in Trac where I could submit improvement patches.
diff --git a/macros/sablecc-scala.objectmacro b/macros/sablecc-scala.objectmacro
new file mode 100644
index 0000000..773c458
--- /dev/null
+++ b/macros/sablecc-scala.objectmacro
@@ -0,0 +1,881 @@
+$comment$
+This file is part of SableCC ( http://sablecc.org ).
+
+See the NOTICE file distributed with this work for copyright information.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+$end$
+
+$comment: ========== header ========== $
+
+$text: header $
+/* This file was generated by SableCC ( http://sablecc.org ). */
+$end: header $
+
+$comment: ========== default_package ========== $
+
+$macro: default_package(language_name) $
+package language_$language_name
+$end: default_package $
+
+$comment: ========== specified_package ========== $
+
+$macro: specified_package(language_name, package) $
+package $package.language_$language_name
+$end: specified_package $
+
+$comment: ========== node ========== $
+
+$macro: node(pkg) $
+$insert: header $
+$expand: default_package, specified_package, before_first="\n" $
+
+abstract class Node {
+
+  import Node._
+
+  import Type._
+
+  def tpe: Type
+
+  import ProductionType._
+
+  def productionType: ProductionType
+
+  def text: String
+  def line: Int
+  def pos: Int
+
+  var parent: Node = _
+
+  override def toString =
+    "\\"" + text + "\\"@(" + line + "," + pos + ")";
+
+  import InternalType._
+
+  protected[$pkg] def internalType: InternalType
+}
+
+object Node {
+
+  object Type extends Enumeration {
+    type Type = Value
+    val
+$macro: node_type_enum_entry(name) $
+    T_$name,
+$end: node_type_enum_entry $
+    TEnd,
+    TAnonymous = Value
+  }
+
+  object ProductionType extends Enumeration {
+    type ProductionType = Value
+    val
+$macro: node_production_type_enum_entry(name) $
+    T_$name,
+$end: node_production_type_enum_entry $
+    TNotAProduction = Value
+  }
+
+  object InternalType extends Enumeration {
+    type InternalType = Value
+    val
+$macro: node_internal_type_enum_entry(name) $
+    T_$name,
+$end: node_internal_type_enum_entry $
+    TEnd = Value
+  }
+  
+}
+$end: node $
+
+$comment: ========== token ========== $
+
+$macro: token $
+$insert: header $
+$expand: default_package, specified_package, before_first="\n" $
+
+abstract class Token(val text: String, val line: Int, val pos: Int)
+    extends Node {
+
+  import Node._
+
+  override def productionType = ProductionType.TNotAProduction
+
+}
+$end: token $
+
+$comment: ========== custom_token ========== $
+
+$macro: custom_token(name) $
+$insert: header $
+$expand: default_package, specified_package, before_first="\n" $
+
+case class N$name(override val text: String, override val line: Int, override val pos: Int)
+    extends Token(text, line, pos) {
+
+  import Node._ 
+
+  override def tpe = Type.T_$name
+
+  override def internalType = InternalType.T_$name
+
+}
+$end: custom_token $
+
+$comment: ========== anonymous_token ========== $
+
+$macro: anonymous_token(name) $
+$insert: header $
+$expand: default_package, specified_package, before_first="\n" $
+
+case class N$name protected (override val text: String, override val line: Int, override val pos: Int)
+    extends Token(text, line, pos) {
+
+  import Node._
+
+  override def tpe() = Type.TAnonymous
+
+  override def internalType = InternalType.T_$name
+}
+$end: anonymous_token $
+
+$comment: ========== lexer ========== $
+
+$macro: lexer(pkg) $
+$insert: header $
+$expand: default_package, specified_package, before_first="\n" $
+
+import java.io._
+import Symbol._
+
+class Lexer(reader: Reader) {
+
+  private val br = new BufferedReader(reader)
+  private val buffer = new StringBuilder
+  private var eof = false
+  private var line = 1
+  private var pos = 1
+  private var last_is_cr = false
+  private var sb: StringBuilder = null
+  private var acceptLine_ = 0
+  private var acceptPos_ = 0
+  private var current_sb_length = 0
+  private var sb_contains_eof = false
+$macro: marker_declaration(name) $
+  private var marker$name = 0
+$end: marker_declaration $
+
+$macro: set_marker_declaration(name) $
+  protected[$pkg] def setMarker$name {
+    this.marker$name = this.current_sb_length
+  }
+$end: set_marker_declaration $
+
+  def next: Token = {
+    var token: Token = null
+    do {
+      token = internalNext
+    } while (token == null)
+    token
+  }
+
+  protected def internalNext: Token = {
+
+    var first = true
+
+    val sb = new StringBuilder
+    var state: State = S_0
+
+    while(state.stateType == State.StateType.TRANSITION) {
+      val transitionState = state.asInstanceOf[TransitionState]
+
+      this.current_sb_length = sb.length
+      transitionState.marker(this)
+
+      var symbol: Symbol = null
+      if(buffer.length > 0) {
+        val c = buffer.charAt(0)
+        buffer.deleteCharAt(0)
+
+        sb.append(c)
+        symbol = Symbol.symbol(c)
+      }
+      else if(this.eof) {
+        if(sb.length == 0) {
+          return new End(this.line, this.pos)
+        }
+        this.sb_contains_eof = true
+        symbol = Symbol.Symbol_end
+      }
+      else {
+        val i = br.read
+        if(i == -1) {
+          this.eof = true
+          br.close
+
+          if(sb.length == 0) {
+            return new End(this.line, this.pos)
+          }
+          this.sb_contains_eof = true
+          symbol = Symbol.Symbol_end
+        }
+        else {
+          val c = i.toChar
+
+          sb.append(c)
+          symbol = Symbol.symbol(c)
+        }
+      }
+
+      if(symbol == null) {
+        throw new LexerException(sb.charAt(0), this.line, this.pos)
+      }
+
+      state = transitionState.target(symbol)
+
+      if(state == null) {
+        throw new LexerException(sb.charAt(0), this.line, this.pos)
+      }
+    }
+
+    val finalState = state.asInstanceOf[FinalState]
+
+    this.sb = sb
+
+    finalState.token(this)
+  }
+
+  protected[$pkg] def acceptLine = this.acceptLine_
+  private def acceptLine_=(i: Int) = this.acceptLine_ = i
+
+  protected[$pkg] def acceptPos = this.acceptPos_
+  private def acceptPos_=(i: Int) = this.acceptPos_ = i
+
+  private def updateLinePos(text: String) {
+    if(this.last_is_cr) {
+      if(text.length > 0) {
+        if(text.charAt(0) != 10) {
+          this.last_is_cr = false
+          this.line += 1
+          this.pos = 1
+        }
+      }
+      else if(this.buffer.length > 0) {
+        if(this.buffer.charAt(0) != 10) {
+          this.last_is_cr = false
+          this.line += 1
+          this.pos = 1
+        }
+      }
+    }
+
+    this.acceptLine = line
+    this.acceptPos = pos
+
+    for(i <- 0 until text.length) {
+      val c = text.charAt(i)
+
+      if(c == 10) {
+        this.line += 1
+        this.pos = 1
+      }
+      else {
+        if(last_is_cr) {
+          this.line += 1
+          this.pos = 2
+        }
+        else {
+          this.pos += 1
+        }
+      }
+
+      this.last_is_cr = c == 13
+    }
+  }
+
+  protected[$pkg] def accept(backCount: Int) = {
+    var bc = backCount
+    if(this.sb_contains_eof) {
+      this.sb_contains_eof = false
+      bc -= 1
+    }
+    val text = this.sb.toString.substring(0, this.sb.length - bc)
+    val leftover = this.sb.toString.substring(this.sb.length - bc, this.sb.length)
+    this.buffer.insert(0, leftover)
+    updateLinePos(text)
+    text
+  }
+
+$macro: accept_marker_declaration(name) $
+  protected[$pkg] def acceptWithMarker$name(backCount: Int) = {
+    var bc = backCount
+    if(this.sb_contains_eof) {
+      this.sb_contains_eof = false
+      bc -= 1
+    }
+    val text = this.sb.toString.substring(0, this.marker$name - bc)
+    val leftover = this.sb.toString.substring(this.marker$name - bc, this.sb.length)
+    this.buffer.insert(0, leftover)
+    updateLinePos(text)
+    text
+  }
+$end: accept_marker_declaration $
+
+}
+$end: lexer $
+
+$comment: ========== state ========== $
+
+$macro: state(pkg) $
+$insert: header $
+$expand: default_package, specified_package, before_first="\n" $
+
+abstract class State {
+
+  import State.StateType._
+
+  protected[$pkg] def stateType: StateType
+}
+object State {
+  object StateType extends Enumeration {
+    type StateType = Value
+    val TRANSITION, FINAL = Value
+  }
+}
+$end: state $
+
+$comment: ========== transition_state ========== $
+
+$macro: transition_state(pkg) $
+$insert: header $
+$expand: default_package, specified_package, before_first="\n" $
+
+abstract class TransitionState
+    extends State {
+
+  import State._
+  import Symbol._
+
+  protected[$pkg] def marker(lexer: Lexer): Unit
+  protected[$pkg] def target(symbol: Symbol): State
+
+  protected[$pkg] def stateType = StateType.TRANSITION
+}
+$end: transition_state $
+
+$comment: ========== final_state ========== $
+
+$macro: final_state(pkg) $
+$insert: header $
+$expand: default_package, specified_package, before_first="\n" $
+
+abstract class FinalState
+    extends State {
+
+  import State._
+
+  protected[$pkg] def token(lexer: Lexer): Token
+
+  protected[$pkg] def stateType = StateType.FINAL
+}
+$end: final_state $
+
+$comment: ========== transition_state_singleton ========== $
+
+$macro: transition_state_singleton(number,pkg) $
+$insert: header $
+$expand: default_package, specified_package, before_first="\n" $
+
+object S_$number
+    extends TransitionState {
+
+  import Symbol._
+
+$macro: set_marker(name) $
+  protected[$pkg] def marker(lexer: Lexer) {
+    lexer.setMarker$name
+  }
+$end: set_marker $
+$macro: no_marker $
+  protected[$pkg] def marker(lexer: Lexer) {
+  }
+$end: no_marker $
+
+  protected[$pkg] def target(symbol: Symbol) = symbol match {
+$macro: transition_target(symbol, target) $
+    case Symbol.Symbol_$(symbol) => S_$target
+$end: transition_target $
+    case _ => null
+  }
+
+}
+$end: transition_state_singleton $
+
+$comment: ========== final_state_singleton ========== $
+
+$macro: final_state_singleton(number,back_count,pkg) $
+$insert: header $
+$expand: default_package, specified_package, before_first="\n" $
+
+object S_$number
+    extends FinalState {
+
+  protected[$pkg] def token(lexer: Lexer) = {
+$macro: accept_token_no_marker $
+    val text = lexer.accept($back_count)
+$end: accept_token_no_marker $
+$macro: accept_token_with_marker(marker_name) $
+    val text = lexer.acceptWithMarker$marker_name($back_count)
+$end: accept_token_with_marker $
+    val line = lexer.acceptLine
+    val pos = lexer.acceptPos
+
+$macro: accept_normal_token(token_name) $
+    new N$token_name(text, line, pos)
+$end: accept_normal_token $
+$macro: accept_ignored_token $
+    null
+$end: accept_ignored_token $
+  }
+}
+$end: final_state_singleton $
+
+$comment: ========== symbol ========== $
+
+$macro: symbol $
+$insert: header $
+$expand: default_package, specified_package, before_first="\n" $
+
+object Symbol extends Enumeration {
+  type Symbol = Value
+  val
+$macro: symbol_declaration(name) $
+  Symbol_$name,
+$end: symbol_declaration $
+  Symbol_end = Value
+
+  def symbol(c: Char): Symbol = c match {
+$macro: single_char(bound, name) $
+    case $(bound) => Symbol_$name
+$end: single_char $
+$macro: open_interval(name) $
+      case _ => Symbol_$name
+$end: open_interval $
+$macro: open_left_interval(upper_bound, name) $
+      case _ if(c <= $upper_bound) => Symbol_$name
+$end: open_left_interval $
+$macro: open_right_interval(lower_bound, name) $
+      case _ if(c >= $lower_bound) => Symbol_$name
+$end: open_right_interval $
+$macro: interval(lower_bound, upper_bound, name) $
+      case _ if(c >= $lower_bound && c <= $upper_bound) => Symbol_$name
+$end: interval $
+      case _ => null
+  }
+}
+$end: symbol $
+
+$comment: ========== lexer_exception ========== $
+
+$macro: lexer_exception $
+$insert: header $
+$expand: default_package, specified_package, before_first="\n" $
+
+class LexerException(val char: Char, val line: Int, val pos: Int)
+    extends Exception() {
+
+  override def getMessage = "unrecognized char '" + char + "' on line " + line + ", pos " + pos
+}
+$end: lexer_exception $
+
+$comment: ========== parser_exception ========== $
+
+$macro: parser_exception $
+$insert: header $
+$expand: default_package, specified_package, before_first="\n" $
+
+class ParserException(val token: Token)
+    extends Exception() {
+
+  override def getMessage = "unexpected token '" + token.text + "' on line " + token.line + ", pos " + token.pos
+}
+$end: parser_exception $
+
+$comment: ========== test ========== $
+
+$macro: test $
+$insert: header $
+$expand: default_package, specified_package, before_first="\n" $
+
+import java.io._
+object Test {
+
+  def main(args: Array[String]) {
+
+    val in = 
+      if(args.length > 0)
+	new FileReader(args(0))
+      else
+        new InputStreamReader(System.in)
+
+    val syntaxTree = new Parser(in).parse
+    println()
+    new Traverser() {
+      override def traverse(node: Node) = node match {
+        case _: Token =>
+          println(node.tpe + ":\\"" + node.text + "\\"@(" + node.line + "," + node.pos + ")");
+        case _ => super.traverse(node)
+      }
+    }.traverse(syntaxTree)
+  }
+}
+$end: test $
+
+$comment: ========== end ========== $
+
+$macro: end(pkg) $
+$insert: header $
+$expand: default_package, specified_package, before_first="\n" $
+
+case class End(line: Int, pos: Int)
+    extends Token("", line, pos) {
+
+  import Node._
+
+  override def tpe = Type.TEnd
+
+  protected[$pkg] override def internalType = InternalType.TEnd
+}
+$end: end $
+
+$comment: ========== production ========== $
+
+$macro: production(name) $
+$insert: header $
+$expand: default_package, specified_package, before_first="\n" $
+
+$macro: named_production_header $
+abstract class N$name
+$end: named_production_header $
+$macro: anonymous_production_header(pkg) $
+protected[$pkg] protected abstract class N$name
+$end: anonymous_production_header $
+    extends Node
+$end: production $
+
+$comment: ========== package_protected ========== $
+
+$macro: package_protected(pkg) $
+protected[$pkg] $no_eol$
+$end: package_protected $
+
+$comment: ========== alternative ========== $
+
+$macro: alternative(name) $
+$insert: header $
+$expand: default_package, specified_package, before_first="\n" $
+
+$expand: package_protected $
+case class N$(name)$expand: protected_constructor $ (val line: Int, val pos: Int$expand: normal_constructor_parameter, end_constructor_parameter, before_first=", ", separator=", " $)
+    extends $expand: alternative_named_parent, alternative_node_parent $ {
+$macro: protected_constructor(pkg) $
+ protected[$pkg]$no_eol$
+$end: protected_constructor $
+$macro: alternative_named_parent(parent) $
+N$(parent)$no_eol$
+$end: alternative_named_parent $
+$macro: alternative_node_parent $
+Node$no_eol$
+$end: alternative_node_parent $
+$macro: normal_constructor_parameter(element_type, element_name) $
+private val e$(element_name): N$(element_type)$no_eol$
+$end: normal_constructor_parameter $
+$macro: end_constructor_parameter $
+private val e\$end: End$no_eol$
+$end: end_constructor_parameter $
+
+  import Node._
+
+  override def text = null
+
+  override def tpe =
+$macro: named_alt_type $
+    Type.T_$name
+$end: named_alt_type $
+$macro: anonymous_alt_type $
+    Type.TAnonymous
+$end: anonymous_alt_type $
+
+  override def productionType = 
+$macro: alt_prod_type(prod_name) $
+    ProductionType.T_$prod_name
+$end: alt_prod_type $
+
+  override def internalType =
+    InternalType.T_$name
+
+$expand: public_element_accessor, before_first="\n", separator="\n" $
+$expand: normal_element_accessor, end_element_accessor, before_first="\n", separator="\n" $
+$macro: public_element_accessor(element_name) $
+  def $(element_name): $expand: public_element_type, token_element_type $ = 
+$macro: public_element_type(element_type) $
+N$(element_type)$no_eol$
+$end: public_element_type $
+$macro: token_element_type $
+Token$no_eol$
+$end: token_element_type $
+    this.e$element_name
+$end: public_element_accessor$
+$macro: normal_element_accessor(element_type, element_name,pkg) $
+  protected[$pkg] def internalGet$(element_name): N$element_type = this.e$element_name
+$end: normal_element_accessor$
+
+$macro: end_element_accessor(pkg) $
+  protected[$pkg] def internalGet\$end: End = this.e\$end
+$end: end_element_accessor$
+
+}
+$end: alternative $
+
+$comment: ========== traverser ========== $
+
+$macro: traverser $
+$insert: header $
+$expand: default_package, specified_package, before_first="\n" $
+
+class Traverser {
+  def traverse(node: Node): Unit = node match {
+$expand: normal_traverser_case $
+$macro: normal_traverser_case(case_type) $
+    case $case_type(line, pos$expand: case_constructor_parameter, end_case_constructor_parameter, before_first=", ", separator=", " $) =>
+$expand: child_traverse $
+$macro: case_constructor_parameter(case_name) $
+e$(case_name)$no_eol$
+$end: case_constructor_parameter $
+$macro: end_case_constructor_parameter $
+e\$end$no_eol$
+$end: end_case_constructor_parameter $
+$macro: child_traverse(element_name) $
+      traverse(e$element_name)
+$end: child_traverse $
+$end: normal_traverser_case $
+    case _ => // do nothing
+  }
+}
+$end: traverser $
+
+$comment: ========== transformer ========== $
+
+$macro: transformer $
+$insert: header $
+$expand: default_package, specified_package, before_first="\n" $
+
+class Transformer {
+  def transform(node: Node): Node = node match {
+$expand: normal_transformer_case $
+$macro: normal_transformer_case(case_type) $
+    case $case_type(line, pos$expand: transform_case_constructor_parameter, transform_end_case_constructor_parameter, before_first=", ", separator=", " $) =>
+      $case_type(line, pos$expand: child_transform, before_first=", ", separator=", " $)
+$macro: transform_case_constructor_parameter(case_name) $
+e$(case_name)$no_eol$
+$end: transform_case_constructor_parameter $
+$macro: transform_end_case_constructor_parameter $
+e\$end$no_eol$
+$end: transform_end_case_constructor_parameter $
+$macro: child_transform(element_name, element_type) $
+transform(e$element_name).asInstanceOf[$element_type]$no_eol$
+$end: child_transform $
+$end: normal_transformer_case $
+    case _ => node
+  }
+}
+$end: transformer $
+
+$comment: ========== parser ========== $
+
+$macro: parser(pkg) $
+$insert: header $
+$expand: default_package, specified_package, before_first="\n" $
+
+import java.io._
+import java.util._
+
+class Parser(reader: Reader) {
+
+  private val lexer = new Lexer(reader)
+  protected[$pkg] val stack = new ParseStack
+
+  def parse: Node = {
+
+    var tree: Node = null
+    while(tree == null) {
+      tree = stack.state(this)
+    }
+
+    tree
+  }
+
+  private val lookList = new LinkedList[Token]
+
+  protected[$pkg] def look(distance: Int) = {
+    while(lookList.size < distance) {
+      lookList.addLast(lexer.next)
+    }
+    lookList.get(distance - 1)
+  }
+
+  protected[$pkg] def shift {
+    val token =
+      if(lookList.size > 0)
+	lookList.removeFirst
+      else
+	lexer.next
+    stack.push(token, stack.state.target(token))
+  }
+}
+
+protected[$pkg] class ParseStack {
+
+  import ParseStack._
+
+  private var stack = new ParseStackEntry(null, null, L_0)
+  private var freeList: ParseStackEntry = _
+
+  protected[$pkg] def push(node: Node, state: LRState) {
+    var entry: ParseStackEntry = null
+    if(freeList != null) {
+      entry = freeList
+      freeList = freeList.previous
+      entry.set(stack, node, state)
+    }
+    else {
+      entry = new ParseStackEntry(stack, node, state)
+    }
+    stack = entry
+  }
+
+  protected[$pkg] def state: LRState = stack.state
+
+  protected[$pkg] def pop = {
+    val node = stack.node
+    val entry = stack
+    stack = stack.previous
+    entry.set(freeList, null, null)
+    freeList = entry
+    node
+  }
+}
+
+protected[$pkg] object ParseStack {
+
+  class ParseStackEntry(var previous: ParseStackEntry, var node: Node, var state: LRState) {
+
+    def set(
+        previous: ParseStackEntry,
+        node: Node,
+        state: LRState) {
+     this.previous = previous;
+     this.node = node;
+     this.state = state;
+   }
+  }
+}
+
+protected[$pkg] abstract class LRState {
+
+  protected[$pkg] def apply(parser: Parser): Node
+  protected[$pkg] def target(node: Node): LRState
+}
+$macro: lr_state_singleton(number) $
+
+protected[$pkg] object L_$number
+    extends LRState {
+
+  override def apply(parser: Parser): Node = {
+
+$macro: distance(distance) $
+    // LR($distance) decisions
+$macro: action $
+    $expand: normal_group, false_group, before_first="if (", separator=" && ", after_last=") " ${
+$macro: normal_group$
+$expand: normal_condition, end_condition, before_many="(", separator=" || ", after_many=")" $
+$end: normal_group $
+$macro: normal_condition(ahead,token_type) $
+parser.look($ahead).internalType == Node.InternalType.T_$(token_type)$no_eol$
+$end: normal_condition $
+$macro: end_condition(ahead) $
+parser.look($ahead).internalType == Node.InternalType.TEnd$no_eol$
+$end: end_condition $
+$macro: false_group $
+false$no_eol$
+$end: false_group $
+$macro: shift $
+      parser.shift
+      return null
+$end: shift $
+$macro: reduce(alternative) $
+      val stack = parser.stack
+
+$expand: reduce_normal_pop, reduce_end_pop $
+$macro: reduce_normal_pop(element_type, element_name) $
+      val l$element_name = stack.pop.asInstanceOf[N$element_type]
+$end: reduce_normal_pop $
+$macro: reduce_end_pop $
+      val l\$end = stack.pop.asInstanceOf[End]
+$end: reduce_end_pop $
+
+$macro: normal_parameter(element_name) $
+l$(element_name)$no_eol$
+$end: normal_parameter $
+$macro: end_parameter $
+l\$end$no_eol$
+$end: end_parameter $
+$macro: reduce_decision $
+      val l$alternative = new N$alternative(-1, -1$expand: normal_parameter, end_parameter, before_first=", ", separator=", " $)
+      stack.push(l$alternative, stack.state.target(l$alternative))
+      return null
+$end: reduce_decision $
+$macro: accept_decision(element_name) $
+      return l$element_name
+$end: accept_decision $
+$end: reduce $
+    }
+$end: action $
+$end: distance $
+$macro: lr1_or_more $
+    parser.shift
+    return null
+$end: lr1_or_more $
+  }
+
+  import Node._
+  
+  protected[$pkg] override def target(node: Node): LRState = node.productionType match {
+$macro: production_lr_transition_target(node_type, target) $
+    case ProductionType.T_$(node_type) => L_$target
+$end: production_lr_transition_target $
+    case _ => node.internalType match {
+$macro: normal_token_lr_transition_target(node_type, target) $
+        case InternalType.T_$(node_type) => L_$target
+$end: normal_token_lr_transition_target $
+$macro: end_token_lr_transition_target(target) $
+        case InternalType.TEnd => L_$target
+$end: end_token_lr_transition_target $
+        case _ =>
+          throw new ParserException(node.asInstanceOf[Token])
+      }
+    }
+}
+$end: lr_state_singleton $
+$end: parser $
diff --git a/src/org/sablecc/sablecc/codegeneration/scala/ScalaGenerator.java b/src/org/sablecc/sablecc/codegeneration/scala/ScalaGenerator.java
new file mode 100644
index 0000000..7b48048
--- /dev/null
+++ b/src/org/sablecc/sablecc/codegeneration/scala/ScalaGenerator.java
@@ -0,0 +1,1006 @@
+/* This file is part of SableCC ( http://sablecc.org ).
+ *
+ * See the NOTICE file distributed with this work for copyright information.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.sablecc.sablecc.codegeneration.scala;
+
+import static org.sablecc.sablecc.util.Utils.to_CamelCase;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.Map.Entry;
+
+import org.sablecc.sablecc.alphabet.Bound;
+import org.sablecc.sablecc.alphabet.Interval;
+import org.sablecc.sablecc.alphabet.RichSymbol;
+import org.sablecc.sablecc.alphabet.Symbol;
+import org.sablecc.sablecc.automaton.Acceptation;
+import org.sablecc.sablecc.automaton.Automaton;
+import org.sablecc.sablecc.automaton.Marker;
+import org.sablecc.sablecc.automaton.State;
+import org.sablecc.sablecc.codegeneration.scala.macro.MAction;
+import org.sablecc.sablecc.codegeneration.scala.macro.MAlternative;
+import org.sablecc.sablecc.codegeneration.scala.macro.MAnonymousToken;
+import org.sablecc.sablecc.codegeneration.scala.macro.MCustomToken;
+import org.sablecc.sablecc.codegeneration.scala.macro.MDistance;
+import org.sablecc.sablecc.codegeneration.scala.macro.MEnd;
+import org.sablecc.sablecc.codegeneration.scala.macro.MFinalState;
+import org.sablecc.sablecc.codegeneration.scala.macro.MFinalStateSingleton;
+import org.sablecc.sablecc.codegeneration.scala.macro.MLexer;
+import org.sablecc.sablecc.codegeneration.scala.macro.MLexerException;
+import org.sablecc.sablecc.codegeneration.scala.macro.MLrStateSingleton;
+import org.sablecc.sablecc.codegeneration.scala.macro.MNode;
+import org.sablecc.sablecc.codegeneration.scala.macro.MNormalGroup;
+import org.sablecc.sablecc.codegeneration.scala.macro.MNormalTransformerCase;
+import org.sablecc.sablecc.codegeneration.scala.macro.MNormalTraverserCase;
+import org.sablecc.sablecc.codegeneration.scala.macro.MParser;
+import org.sablecc.sablecc.codegeneration.scala.macro.MParserException;
+import org.sablecc.sablecc.codegeneration.scala.macro.MProduction;
+import org.sablecc.sablecc.codegeneration.scala.macro.MPublicElementAccessor;
+import org.sablecc.sablecc.codegeneration.scala.macro.MReduce;
+import org.sablecc.sablecc.codegeneration.scala.macro.MReduceDecision;
+import org.sablecc.sablecc.codegeneration.scala.macro.MState;
+import org.sablecc.sablecc.codegeneration.scala.macro.MSymbol;
+import org.sablecc.sablecc.codegeneration.scala.macro.MTest;
+import org.sablecc.sablecc.codegeneration.scala.macro.MToken;
+import org.sablecc.sablecc.codegeneration.scala.macro.MTransformer;
+import org.sablecc.sablecc.codegeneration.scala.macro.MTransitionState;
+import org.sablecc.sablecc.codegeneration.scala.macro.MTransitionStateSingleton;
+import org.sablecc.sablecc.codegeneration.scala.macro.MTraverser;
+import org.sablecc.sablecc.exception.CompilerException;
+import org.sablecc.sablecc.lrautomaton.Action;
+import org.sablecc.sablecc.lrautomaton.ActionType;
+import org.sablecc.sablecc.lrautomaton.Alternative;
+import org.sablecc.sablecc.lrautomaton.Element;
+import org.sablecc.sablecc.lrautomaton.Item;
+import org.sablecc.sablecc.lrautomaton.LRAutomaton;
+import org.sablecc.sablecc.lrautomaton.LRState;
+import org.sablecc.sablecc.lrautomaton.Production;
+import org.sablecc.sablecc.lrautomaton.ProductionElement;
+import org.sablecc.sablecc.lrautomaton.ReduceAction;
+import org.sablecc.sablecc.lrautomaton.Token;
+import org.sablecc.sablecc.lrautomaton.TokenElement;
+import org.sablecc.sablecc.oldstructure.AnonymousToken;
+import org.sablecc.sablecc.oldstructure.Context;
+import org.sablecc.sablecc.oldstructure.GlobalIndex;
+import org.sablecc.sablecc.oldstructure.MatchedToken;
+import org.sablecc.sablecc.oldstructure.NameToken;
+
+/**
+ * The main class of SableCC.
+ */
+public class ScalaGenerator {
+
+    public static void generateCode(
+            File destinationDirectory,
+            String destinationPackage,
+            GlobalIndex globalIndex,
+            Automaton lexer,
+            LRAutomaton parser) {
+
+        String languagePackageName = "language_"
+                + globalIndex.getLanguage().get_camelCaseName();
+        File packageDirectory;
+        MNode mNode = new MNode(languagePackageName);
+        MToken mToken = new MToken();
+        MState mState = new MState(languagePackageName);
+        MTransitionState mTransitionState = new MTransitionState(
+                languagePackageName);
+        MFinalState mFinalState = new MFinalState(languagePackageName);
+        MSymbol mSymbol = new MSymbol();
+        MLexer mLexer = new MLexer(languagePackageName);
+        MLexerException mLexerException = new MLexerException();
+        MParserException mParserException = new MParserException();
+        MTest mTest = new MTest();
+        MEnd mEnd = new MEnd(languagePackageName);
+        MTransformer mTransformer = new MTransformer();
+        MTraverser mTraverser = new MTraverser();
+        MParser mParser = new MParser(languagePackageName);
+
+        if (destinationPackage.equals("")) {
+            packageDirectory = new File(destinationDirectory,
+                    languagePackageName);
+            mNode.newDefaultPackage(globalIndex.getLanguage()
+                    .get_camelCaseName());
+            mToken.newDefaultPackage(globalIndex.getLanguage()
+                    .get_camelCaseName());
+            mState.newDefaultPackage(globalIndex.getLanguage()
+                    .get_camelCaseName());
+            mTransitionState.newDefaultPackage(globalIndex.getLanguage()
+                    .get_camelCaseName());
+            mFinalState.newDefaultPackage(globalIndex.getLanguage()
+                    .get_camelCaseName());
+            mSymbol.newDefaultPackage(globalIndex.getLanguage()
+                    .get_camelCaseName());
+            mLexer.newDefaultPackage(globalIndex.getLanguage()
+                    .get_camelCaseName());
+            mLexerException.newDefaultPackage(globalIndex.getLanguage()
+                    .get_camelCaseName());
+            mParserException.newDefaultPackage(globalIndex.getLanguage()
+                    .get_camelCaseName());
+            mTest.newDefaultPackage(globalIndex.getLanguage()
+                    .get_camelCaseName());
+            mEnd.newDefaultPackage(globalIndex.getLanguage()
+                    .get_camelCaseName());
+            mTransformer.newDefaultPackage(globalIndex.getLanguage()
+                    .get_camelCaseName());
+            mTraverser.newDefaultPackage(globalIndex.getLanguage()
+                    .get_camelCaseName());
+            mParser.newDefaultPackage(globalIndex.getLanguage()
+                    .get_camelCaseName());
+        }
+        else {
+            packageDirectory = new File(destinationDirectory,
+                    destinationPackage.replace('.', '/') + "/"
+                            + languagePackageName);
+            mNode.newSpecifiedPackage(globalIndex.getLanguage()
+                    .get_camelCaseName(), destinationPackage);
+            mToken.newSpecifiedPackage(globalIndex.getLanguage()
+                    .get_camelCaseName(), destinationPackage);
+            mState.newSpecifiedPackage(globalIndex.getLanguage()
+                    .get_camelCaseName(), destinationPackage);
+            mTransitionState.newSpecifiedPackage(globalIndex.getLanguage()
+                    .get_camelCaseName(), destinationPackage);
+            mFinalState.newSpecifiedPackage(globalIndex.getLanguage()
+                    .get_camelCaseName(), destinationPackage);
+            mSymbol.newSpecifiedPackage(globalIndex.getLanguage()
+                    .get_camelCaseName(), destinationPackage);
+            mLexer.newSpecifiedPackage(globalIndex.getLanguage()
+                    .get_camelCaseName(), destinationPackage);
+            mLexerException.newSpecifiedPackage(globalIndex.getLanguage()
+                    .get_camelCaseName(), destinationPackage);
+            mParserException.newSpecifiedPackage(globalIndex.getLanguage()
+                    .get_camelCaseName(), destinationPackage);
+            mTest.newSpecifiedPackage(globalIndex.getLanguage()
+                    .get_camelCaseName(), destinationPackage);
+            mEnd.newSpecifiedPackage(globalIndex.getLanguage()
+                    .get_camelCaseName(), destinationPackage);
+            mTransformer.newSpecifiedPackage(globalIndex.getLanguage()
+                    .get_camelCaseName(), destinationPackage);
+            mTraverser.newSpecifiedPackage(globalIndex.getLanguage()
+                    .get_camelCaseName(), destinationPackage);
+            mParser.newSpecifiedPackage(globalIndex.getLanguage()
+                    .get_camelCaseName(), destinationPackage);
+        }
+
+        packageDirectory.mkdirs();
+
+        Context context = globalIndex.getContexts().iterator().next();
+
+        for (MatchedToken matchedToken : context.getMatchedTokens()) {
+            if (!matchedToken.isIgnored()) {
+                if (matchedToken instanceof NameToken) {
+                    NameToken nameToken = (NameToken) matchedToken;
+
+                    mNode.newNodeTypeEnumEntry(nameToken.get_CamelCaseName());
+                    mNode.newNodeInternalTypeEnumEntry(nameToken
+                            .get_CamelCaseName());
+
+                    MCustomToken mCustomToken = new MCustomToken(nameToken
+                            .get_CamelCaseName());
+
+                    if (destinationPackage.equals("")) {
+                        mCustomToken.newDefaultPackage(globalIndex
+                                .getLanguage().get_camelCaseName());
+                    }
+                    else {
+                        mCustomToken.newSpecifiedPackage(globalIndex
+                                .getLanguage().get_camelCaseName(),
+                                destinationPackage);
+                    }
+
+                    try {
+                        BufferedWriter bw = new BufferedWriter(new FileWriter(
+                                new File(packageDirectory, "N"
+                                        + nameToken.get_CamelCaseName()
+                                        + ".scala")));
+
+                        bw.write(mCustomToken.toString());
+                        bw.close();
+                    }
+                    catch (IOException e) {
+                        throw CompilerException.outputError("N"
+                                + nameToken.get_CamelCaseName() + ".scala", e);
+                    }
+                }
+                else {
+                    AnonymousToken anonymousToken = (AnonymousToken) matchedToken;
+
+                    mNode.newNodeInternalTypeEnumEntry(""
+                            + anonymousToken.get_CamelCaseName());
+
+                    MAnonymousToken mAnonymousToken = new MAnonymousToken(""
+                            + anonymousToken.get_CamelCaseName());
+
+                    if (destinationPackage.equals("")) {
+                        mAnonymousToken.newDefaultPackage(globalIndex
+                                .getLanguage().get_camelCaseName());
+                    }
+                    else {
+                        mAnonymousToken.newSpecifiedPackage(globalIndex
+                                .getLanguage().get_camelCaseName(),
+                                destinationPackage);
+                    }
+
+                    try {
+                        BufferedWriter bw = new BufferedWriter(new FileWriter(
+                                new File(packageDirectory, "N"
+                                        + anonymousToken.get_CamelCaseName()
+                                        + ".scala")));
+
+                        bw.write(mAnonymousToken.toString());
+                        bw.close();
+                    }
+                    catch (IOException e) {
+                        throw CompilerException.outputError(
+                                "N" + anonymousToken.get_CamelCaseName()
+                                        + ".scala", e);
+                    }
+                }
+            }
+        }
+
+        for (Symbol symbol : lexer.getAlphabet().getSymbols()) {
+            mSymbol.newSymbolDeclaration(symbol.getSimpleName());
+        }
+
+        for (Map.Entry<Interval, Symbol> entry : lexer.getAlphabet()
+                .getIntervalToSymbolMap().entrySet()) {
+            Interval interval = entry.getKey();
+            Symbol symbol = entry.getValue();
+
+            if (interval.getLowerBound() == Bound.MIN) {
+                if (interval.getUpperBound() == Bound.MAX) {
+                    mSymbol.newOpenInterval(symbol.getSimpleName());
+                }
+                else {
+                    mSymbol.newOpenLeftInterval(interval.getUpperBound()
+                            .getValue().toString(), symbol.getSimpleName());
+                }
+            }
+            else if (interval.getUpperBound() == Bound.MAX) {
+                mSymbol.newOpenRightInterval(interval.getLowerBound()
+                        .getValue().toString(), symbol.getSimpleName());
+            }
+            else if (interval.getLowerBound().equals(interval.getUpperBound())) {
+                mSymbol.newSingleChar(interval.getLowerBound().getValue()
+                        .toString(), symbol.getSimpleName());
+            }
+            else {
+                mSymbol.newInterval(interval.getLowerBound().getValue()
+                        .toString(), interval.getUpperBound().getValue()
+                        .toString(), symbol.getSimpleName());
+            }
+        }
+
+        for (State state : lexer.getStates()) {
+            if (state.isAcceptState()) {
+                Acceptation acceptation = state.getAcceptations().first();
+                MFinalStateSingleton mFinalStateSingleton = new MFinalStateSingleton(
+                        "" + state.getId(), "" + acceptation.getBackCount(),
+                        languagePackageName);
+
+                if (destinationPackage.equals("")) {
+                    mFinalStateSingleton.newDefaultPackage(globalIndex
+                            .getLanguage().get_camelCaseName());
+                }
+                else {
+                    mFinalStateSingleton.newSpecifiedPackage(globalIndex
+                            .getLanguage().get_camelCaseName(),
+                            destinationPackage);
+                }
+
+                Marker marker = acceptation.getMarker();
+
+                if (marker == null) {
+                    mFinalStateSingleton.newAcceptTokenNoMarker();
+                }
+                else {
+                    mFinalStateSingleton.newAcceptTokenWithMarker(marker
+                            .getName());
+                }
+
+                MatchedToken matchedToken = context.getMatchedToken(acceptation
+                        .getName());
+
+                if (matchedToken.isIgnored()) {
+                    mFinalStateSingleton.newAcceptIgnoredToken();
+                }
+                else {
+                    if (matchedToken instanceof NameToken) {
+                        NameToken nameToken = (NameToken) matchedToken;
+
+                        mFinalStateSingleton.newAcceptNormalToken(nameToken
+                                .get_CamelCaseName());
+                    }
+                    else {
+                        AnonymousToken anonymousToken = (AnonymousToken) matchedToken;
+
+                        mFinalStateSingleton.newAcceptNormalToken(""
+                                + anonymousToken.get_CamelCaseName());
+                    }
+                }
+
+                try {
+                    BufferedWriter bw = new BufferedWriter(new FileWriter(
+                            new File(packageDirectory, "S_" + state.getId()
+                                    + ".scala")));
+
+                    bw.write(mFinalStateSingleton.toString());
+                    bw.close();
+                }
+                catch (IOException e) {
+                    throw CompilerException.outputError("S_" + state.getId()
+                            + ".scala", e);
+                }
+            }
+            else {
+                MTransitionStateSingleton mTransitionStateSingleton = new MTransitionStateSingleton(
+                        "" + state.getId(), languagePackageName);
+
+                if (destinationPackage.equals("")) {
+                    mTransitionStateSingleton.newDefaultPackage(globalIndex
+                            .getLanguage().get_camelCaseName());
+                }
+                else {
+                    mTransitionStateSingleton.newSpecifiedPackage(globalIndex
+                            .getLanguage().get_camelCaseName(),
+                            destinationPackage);
+                }
+
+                Marker marker = state.getMarker();
+
+                if (marker == null) {
+                    mTransitionStateSingleton.newNoMarker();
+                }
+                else {
+                    mTransitionStateSingleton.newSetMarker(marker.getName());
+                }
+
+                for (Entry<RichSymbol, SortedSet<State>> entry : state
+                        .getTransitions().entrySet()) {
+                    RichSymbol richSymbol = entry.getKey();
+                    State target = state.getSingleTarget(richSymbol);
+                    String symbolName = richSymbol == RichSymbol.END ? "end"
+                            : richSymbol.getSymbol().getSimpleName();
+
+                    mTransitionStateSingleton.newTransitionTarget(symbolName,
+                            "" + target.getId());
+                }
+
+                try {
+                    BufferedWriter bw = new BufferedWriter(new FileWriter(
+                            new File(packageDirectory, "S_" + state.getId()
+                                    + ".scala")));
+
+                    bw.write(mTransitionStateSingleton.toString());
+                    bw.close();
+                }
+                catch (IOException e) {
+                    throw CompilerException.outputError("S_" + state.getId()
+                            + ".scala", e);
+                }
+            }
+        }
+
+        for (Marker marker : lexer.getMarkers()) {
+            mLexer.newMarkerDeclaration(marker.getName());
+            mLexer.newSetMarkerDeclaration(marker.getName());
+            mLexer.newAcceptMarkerDeclaration(marker.getName());
+        }
+
+        for (Production production : parser.getGrammar().getProductions()) {
+
+            String production_CamelCaseName = to_CamelCase(production.getName());
+
+            mNode.newNodeProductionTypeEnumEntry(production_CamelCaseName);
+
+            // if production is not a single anonymous alternative
+            if (production.getAlternatives().size() > 1
+                    || !production.getAlternatives().iterator().next()
+                            .getName().equals("")) {
+
+                MProduction mProduction = new MProduction(
+                        production_CamelCaseName);
+
+                if (destinationPackage.equals("")) {
+                    mProduction.newDefaultPackage(globalIndex.getLanguage()
+                            .get_camelCaseName());
+                }
+                else {
+                    mProduction.newSpecifiedPackage(globalIndex.getLanguage()
+                            .get_camelCaseName(), destinationPackage);
+                }
+
+                if (production_CamelCaseName.indexOf('$') == -1) {
+                    mProduction.newNamedProductionHeader();
+                }
+                else {
+                    mProduction
+                            .newAnonymousProductionHeader(languagePackageName);
+                }
+
+                try {
+                    BufferedWriter bw = new BufferedWriter(new FileWriter(
+                            new File(packageDirectory, "N"
+                                    + production_CamelCaseName + ".scala")));
+
+                    bw.write(mProduction.toString());
+                    bw.close();
+                }
+                catch (IOException e) {
+                    throw CompilerException.outputError("N"
+                            + production_CamelCaseName + ".scala", e);
+                }
+            }
+
+            for (Alternative alternative : production.getAlternatives()) {
+                String alt_CamelCaseName = to_CamelCase(alternative.getName());
+                String alt_CamelCaseFullName = production_CamelCaseName
+                        + (alt_CamelCaseName.equals("") ? "" : "_"
+                                + alt_CamelCaseName);
+                boolean altIsPublic = alt_CamelCaseFullName.indexOf('$') == -1;
+                boolean altExtendsNode = alt_CamelCaseFullName.indexOf('_') == -1;
+
+                MAlternative mAlternative = new MAlternative(
+                        alt_CamelCaseFullName);
+
+                // XXX new case in the traverser
+                MNormalTraverserCase mTraverserCase = null;
+                MNormalTransformerCase mTransformerCase = null;
+                if (altIsPublic) {
+                    mTraverserCase = mTraverser.newNormalTraverserCase("N"
+                            + alt_CamelCaseFullName);
+                    mTransformerCase = mTransformer
+                            .newNormalTransformerCase("N"
+                                    + alt_CamelCaseFullName);
+                }
+
+                mAlternative.newAltProdType(production_CamelCaseName);
+
+                if (destinationPackage.equals("")) {
+                    mAlternative.newDefaultPackage(globalIndex.getLanguage()
+                            .get_camelCaseName());
+                }
+                else {
+                    mAlternative.newSpecifiedPackage(globalIndex.getLanguage()
+                            .get_camelCaseName(), destinationPackage);
+                }
+
+                mNode.newNodeInternalTypeEnumEntry(alt_CamelCaseFullName);
+                if (altIsPublic) {
+                    mNode.newNodeTypeEnumEntry(alt_CamelCaseFullName);
+                    // mAlternative.newPublic();
+                    mAlternative.newNamedAltType();
+                }
+                else {
+                    mAlternative.newPackageProtected(languagePackageName);
+                    mAlternative.newAnonymousAltType();
+                }
+
+                if (altExtendsNode) {
+                    mAlternative.newAlternativeNodeParent();
+                }
+                else {
+                    mAlternative
+                            .newAlternativeNamedParent(production_CamelCaseName);
+                }
+
+                boolean altHasPublicConstructor = true;
+                for (Element element : alternative.getElements()) {
+                    String element_CamelCaseName = to_CamelCase(element
+                            .getName());
+                    String element_CamelCaseType = null;
+                    boolean elementIsEndToken;
+                    boolean elementIsPublicReadable;
+                    boolean elementIsPublicWritable;
+                    if (element instanceof TokenElement) {
+                        TokenElement tokenElement = (TokenElement) element;
+                        if (tokenElement.getToken().getName().equals("$end")) {
+                            elementIsEndToken = true;
+                            elementIsPublicReadable = false;
+                            elementIsPublicWritable = false;
+                        }
+                        else {
+                            MatchedToken matchedToken = context
+                                    .getMatchedToken(tokenElement.getToken()
+                                            .getName());
+                            if (matchedToken instanceof NameToken) {
+                                NameToken nameToken = (NameToken) matchedToken;
+                                element_CamelCaseType = nameToken
+                                        .get_CamelCaseName();
+                            }
+                            else {
+                                AnonymousToken anonymousToken = (AnonymousToken) matchedToken;
+
+                                element_CamelCaseType = ""
+                                        + anonymousToken.get_CamelCaseName();
+                            }
+
+                            elementIsEndToken = false;
+                            elementIsPublicReadable = altIsPublic
+                                    && element_CamelCaseName.indexOf('$') == -1;
+                            elementIsPublicWritable = elementIsPublicReadable
+                                    && element_CamelCaseType.indexOf('$') == -1;
+                        }
+                    }
+                    else {
+                        ProductionElement productionElement = (ProductionElement) element;
+                        element_CamelCaseType = to_CamelCase(productionElement
+                                .getProduction().getName());
+
+                        elementIsEndToken = false;
+                        elementIsPublicReadable = altIsPublic
+                                && element_CamelCaseName.indexOf('$') == -1;
+                        elementIsPublicWritable = elementIsPublicReadable
+                                && element_CamelCaseType.indexOf('$') == -1;
+                    }
+
+                    if (!elementIsPublicWritable) {
+                        altHasPublicConstructor = false;
+                    }
+
+                    if (elementIsEndToken) {
+                        mAlternative.newEndConstructorParameter();
+                        if (altIsPublic) {
+                            // XXX extract the end parameter
+                            mTraverserCase.newEndCaseConstructorParameter();
+                            mTransformerCase
+                                    .newTransformEndCaseConstructorParameter();
+                        }
+                        // mAlternative.newEndContructorInitialization();
+
+                        // mAlternative.newEndElementDeclaration();
+                        mAlternative.newEndElementAccessor(languagePackageName);
+
+                    }
+                    else {
+                        mAlternative.newNormalConstructorParameter(
+                                element_CamelCaseType, element_CamelCaseName);
+
+                        // mAlternative
+                        // .newNormalContructorInitialization(element_CamelCaseName);
+
+                        // mAlternative.newNormalElementDeclaration(
+                        // element_CamelCaseType, element_CamelCaseName);
+                        mAlternative.newNormalElementAccessor(
+                                element_CamelCaseType, element_CamelCaseName,
+                                languagePackageName);
+
+                        if (elementIsPublicReadable) {
+                            MPublicElementAccessor publicElementAccessor = mAlternative
+                                    .newPublicElementAccessor(element_CamelCaseName);
+                            if (elementIsPublicWritable) {
+                                publicElementAccessor
+                                        .newPublicElementType(element_CamelCaseType);
+                            }
+                            else {
+                                publicElementAccessor.newTokenElementType();
+                            }
+
+                        }
+
+                        if (altIsPublic) {
+                            // XXX extract a normal parameter
+                            mTraverserCase
+                                    .newCaseConstructorParameter(element_CamelCaseName);
+                            mTransformerCase
+                                    .newTransformCaseConstructorParameter(element_CamelCaseName);
+                            // and the child to traverse
+                            mTraverserCase
+                                    .newChildTraverse(element_CamelCaseName);
+                            mTransformerCase.newChildTransform(
+                                    element_CamelCaseName, "N"
+                                            + element_CamelCaseType);
+                        }
+                    }
+                }
+
+                if (!altHasPublicConstructor) {
+                    mAlternative.newProtectedConstructor(languagePackageName);
+                }
+
+                try {
+                    BufferedWriter bw = new BufferedWriter(new FileWriter(
+                            new File(packageDirectory, "N"
+                                    + alt_CamelCaseFullName + ".scala")));
+
+                    bw.write(mAlternative.toString());
+                    bw.close();
+                }
+                catch (IOException e) {
+                    throw CompilerException.outputError("N"
+                            + alt_CamelCaseFullName + ".scala", e);
+                }
+            }
+        }
+
+        for (LRState state : parser.getStates()) {
+            MLrStateSingleton mLrStateSingleton = mParser
+                    .newLrStateSingleton(state.getName());
+
+            for (Entry<Token, LRState> entry : state.getTokenTransitions()
+                    .entrySet()) {
+                Token token = entry.getKey();
+                LRState target = entry.getValue();
+
+                if (token.getName().equals("$end")) {
+                    mLrStateSingleton.newEndTokenLrTransitionTarget(target
+                            .getName());
+                }
+                else {
+                    MatchedToken matchedToken = context.getMatchedToken(token
+                            .getName());
+                    String element_CamelCaseType;
+                    if (matchedToken instanceof NameToken) {
+                        NameToken nameToken = (NameToken) matchedToken;
+                        element_CamelCaseType = nameToken.get_CamelCaseName();
+                    }
+                    else {
+                        AnonymousToken anonymousToken = (AnonymousToken) matchedToken;
+
+                        element_CamelCaseType = ""
+                                + anonymousToken.get_CamelCaseName();
+                    }
+
+                    mLrStateSingleton.newNormalTokenLrTransitionTarget(
+                            element_CamelCaseType, target.getName());
+                }
+            }
+
+            for (Entry<Production, LRState> entry : state
+                    .getProductionTransitions().entrySet()) {
+                Production production = entry.getKey();
+                LRState target = entry.getValue();
+
+                String production_CamelCaseName = to_CamelCase(production
+                        .getName());
+                mLrStateSingleton.newProductionLrTransitionTarget(
+                        production_CamelCaseName, target.getName());
+            }
+
+            Map<Integer, MDistance> distanceMap = new LinkedHashMap<Integer, MDistance>();
+            boolean isLr1OrMore = false;
+            for (Action action : state.getActions()) {
+                int maxLookahead = action.getMaxLookahead();
+                while (maxLookahead > distanceMap.size() - 1) {
+                    int distance = distanceMap.size();
+                    distanceMap.put(distance, mLrStateSingleton.newDistance(""
+                            + distance));
+                }
+
+                MDistance mDistance = distanceMap.get(maxLookahead);
+                MAction mAction = mDistance.newAction();
+                if (maxLookahead > 0) {
+                    isLr1OrMore = true;
+                    for (Entry<Integer, Set<Item>> entry : action
+                            .getDistanceToItemSetMap().entrySet()) {
+                        String ahead = "" + entry.getKey();
+                        Set<Item> items = entry.getValue();
+                        Set<Token> tokens = new LinkedHashSet<Token>();
+                        for (Item item : items) {
+                            tokens.add(item.getTokenElement().getToken());
+                        }
+
+                        if (tokens.size() == 0) {
+                            mAction.newFalseGroup();
+                        }
+                        else {
+                            MNormalGroup mNormalGroup = mAction
+                                    .newNormalGroup();
+
+                            for (Token token : tokens) {
+                                if (token.getName().equals("$end")) {
+                                    mNormalGroup.newEndCondition(ahead);
+                                }
+                                else {
+                                    MatchedToken matchedToken = context
+                                            .getMatchedToken(token.getName());
+                                    String element_CamelCaseType;
+                                    if (matchedToken instanceof NameToken) {
+                                        NameToken nameToken = (NameToken) matchedToken;
+                                        element_CamelCaseType = nameToken
+                                                .get_CamelCaseName();
+                                    }
+                                    else {
+                                        AnonymousToken anonymousToken = (AnonymousToken) matchedToken;
+
+                                        element_CamelCaseType = ""
+                                                + anonymousToken
+                                                        .get_CamelCaseName();
+                                    }
+
+                                    mNormalGroup.newNormalCondition(ahead,
+                                            element_CamelCaseType);
+                                }
+                            }
+                        }
+                    }
+                }
+
+                if (action.getType() == ActionType.SHIFT) {
+                    mAction.newShift();
+                }
+                else {
+                    ReduceAction reduceAction = (ReduceAction) action;
+                    Alternative alternative = reduceAction.getAlternative();
+                    Production production = alternative.getProduction();
+                    String production_CamelCaseName = to_CamelCase(production
+                            .getName());
+                    String alt_CamelCaseName = to_CamelCase(alternative
+                            .getName());
+                    String alt_CamelCaseFullName = production_CamelCaseName
+                            + (alt_CamelCaseName.equals("") ? "" : "_"
+                                    + alt_CamelCaseName);
+
+                    MReduce mReduce = mAction.newReduce(alt_CamelCaseFullName);
+
+                    ArrayList<Element> elements = alternative.getElements();
+                    int elementCount = elements.size();
+                    for (int i = elementCount - 1; i >= 0; i--) {
+                        Element element = elements.get(i);
+                        String element_CamelCaseName = to_CamelCase(element
+                                .getName());
+                        String element_CamelCaseType = null;
+                        boolean elementIsEndToken;
+                        if (element instanceof TokenElement) {
+                            TokenElement tokenElement = (TokenElement) element;
+                            if (tokenElement.getToken().getName()
+                                    .equals("$end")) {
+                                elementIsEndToken = true;
+                            }
+                            else {
+                                MatchedToken matchedToken = context
+                                        .getMatchedToken(tokenElement
+                                                .getToken().getName());
+                                if (matchedToken instanceof NameToken) {
+                                    NameToken nameToken = (NameToken) matchedToken;
+                                    element_CamelCaseType = nameToken
+                                            .get_CamelCaseName();
+                                }
+                                else {
+                                    AnonymousToken anonymousToken = (AnonymousToken) matchedToken;
+
+                                    element_CamelCaseType = ""
+                                            + anonymousToken
+                                                    .get_CamelCaseName();
+                                }
+
+                                elementIsEndToken = false;
+                            }
+                        }
+                        else {
+                            ProductionElement productionElement = (ProductionElement) element;
+                            element_CamelCaseType = to_CamelCase(productionElement
+                                    .getProduction().getName());
+
+                            elementIsEndToken = false;
+                        }
+
+                        if (elementIsEndToken) {
+                            mReduce.newReduceEndPop();
+                        }
+                        else {
+                            mReduce.newReduceNormalPop(element_CamelCaseType,
+                                    element_CamelCaseName);
+                        }
+                    }
+
+                    if (alt_CamelCaseFullName.equals("$Start")) {
+                        mReduce.newAcceptDecision(to_CamelCase(elements.get(0)
+                                .getName()));
+                    }
+                    else {
+                        MReduceDecision mReduceDecision = mReduce
+                                .newReduceDecision();
+
+                        for (Element element : elements) {
+                            String element_CamelCaseName = to_CamelCase(element
+                                    .getName());
+                            boolean elementIsEndToken;
+                            if (element instanceof TokenElement) {
+                                TokenElement tokenElement = (TokenElement) element;
+                                if (tokenElement.getToken().getName().equals(
+                                        "$end")) {
+                                    elementIsEndToken = true;
+                                }
+                                else {
+                                    elementIsEndToken = false;
+                                }
+                            }
+                            else {
+                                elementIsEndToken = false;
+                            }
+                            if (elementIsEndToken) {
+                                mReduceDecision.newEndParameter();
+                            }
+                            else {
+                                mReduceDecision
+                                        .newNormalParameter(element_CamelCaseName);
+                            }
+                        }
+                    }
+                }
+            }
+
+            if (isLr1OrMore) {
+                mLrStateSingleton.newLr1OrMore();
+            }
+        }
+
+        try {
+            BufferedWriter bw = new BufferedWriter(new FileWriter(new File(
+                    packageDirectory, "Node.scala")));
+
+            bw.write(mNode.toString());
+            bw.close();
+        }
+        catch (IOException e) {
+            throw CompilerException.outputError("Node.scala", e);
+        }
+
+        try {
+            BufferedWriter bw = new BufferedWriter(new FileWriter(new File(
+                    packageDirectory, "Token.scala")));
+
+            bw.write(mToken.toString());
+            bw.close();
+        }
+        catch (IOException e) {
+            throw CompilerException.outputError("Token.scala", e);
+        }
+
+        try {
+            BufferedWriter bw = new BufferedWriter(new FileWriter(new File(
+                    packageDirectory, "State.scala")));
+
+            bw.write(mState.toString());
+            bw.close();
+        }
+        catch (IOException e) {
+            throw CompilerException.outputError("State.scala", e);
+        }
+
+        try {
+            BufferedWriter bw = new BufferedWriter(new FileWriter(new File(
+                    packageDirectory, "TransitionState.scala")));
+
+            bw.write(mTransitionState.toString());
+            bw.close();
+        }
+        catch (IOException e) {
+            throw CompilerException.outputError("TransitionState.scala", e);
+        }
+
+        try {
+            BufferedWriter bw = new BufferedWriter(new FileWriter(new File(
+                    packageDirectory, "FinalState.scala")));
+
+            bw.write(mFinalState.toString());
+            bw.close();
+        }
+        catch (IOException e) {
+            throw CompilerException.outputError("FinalState.scala", e);
+        }
+
+        try {
+            BufferedWriter bw = new BufferedWriter(new FileWriter(new File(
+                    packageDirectory, "Symbol.scala")));
+
+            bw.write(mSymbol.toString());
+            bw.close();
+        }
+        catch (IOException e) {
+            throw CompilerException.outputError("Symbol.scala", e);
+        }
+
+        try {
+            BufferedWriter bw = new BufferedWriter(new FileWriter(new File(
+                    packageDirectory, "Lexer.scala")));
+
+            bw.write(mLexer.toString());
+            bw.close();
+        }
+        catch (IOException e) {
+            throw CompilerException.outputError("Lexer.scala", e);
+        }
+
+        try {
+            BufferedWriter bw = new BufferedWriter(new FileWriter(new File(
+                    packageDirectory, "LexerException.scala")));
+
+            bw.write(mLexerException.toString());
+            bw.close();
+        }
+        catch (IOException e) {
+            throw CompilerException.outputError("LexerException.scala", e);
+        }
+
+        try {
+            BufferedWriter bw = new BufferedWriter(new FileWriter(new File(
+                    packageDirectory, "ParserException.scala")));
+
+            bw.write(mParserException.toString());
+            bw.close();
+        }
+        catch (IOException e) {
+            throw CompilerException.outputError("ParserException.scala", e);
+        }
+
+        try {
+            BufferedWriter bw = new BufferedWriter(new FileWriter(new File(
+                    packageDirectory, "Test.scala")));
+
+            bw.write(mTest.toString());
+            bw.close();
+        }
+        catch (IOException e) {
+            throw CompilerException.outputError("Test.scala", e);
+        }
+
+        try {
+            BufferedWriter bw = new BufferedWriter(new FileWriter(new File(
+                    packageDirectory, "End.scala")));
+
+            bw.write(mEnd.toString());
+            bw.close();
+        }
+        catch (IOException e) {
+            throw CompilerException.outputError("End.scala", e);
+        }
+
+        try {
+            BufferedWriter bw = new BufferedWriter(new FileWriter(new File(
+                    packageDirectory, "Transformer.scala")));
+
+            bw.write(mTransformer.toString());
+            bw.close();
+        }
+        catch (IOException e) {
+            throw CompilerException.outputError("Transformer.scala", e);
+        }
+
+        try {
+            BufferedWriter bw = new BufferedWriter(new FileWriter(new File(
+                    packageDirectory, "Traverser.scala")));
+
+            bw.write(mTraverser.toString());
+            bw.close();
+        }
+        catch (IOException e) {
+            throw CompilerException.outputError("Traverser.scala", e);
+        }
+
+        try {
+            BufferedWriter bw = new BufferedWriter(new FileWriter(new File(
+                    packageDirectory, "Parser.scala")));
+
+            bw.write(mParser.toString());
+            bw.close();
+        }
+        catch (IOException e) {
+            throw CompilerException.outputError("Parser.scala", e);
+        }
+    }
+}
diff --git a/src/org/sablecc/sablecc/launcher/SableCC.java b/src/org/sablecc/sablecc/launcher/SableCC.java
index 55a958c..e3322da 100644
--- a/src/org/sablecc/sablecc/launcher/SableCC.java
+++ b/src/org/sablecc/sablecc/launcher/SableCC.java
@@ -136,6 +136,7 @@ public class SableCC {
             case LIST_TARGETS:
                 System.out.println("Available targets:");
                 System.out.println(" java (default)");
+                System.out.println(" scala");
                 return;
 
             case TARGET:
@@ -217,7 +218,7 @@ public class SableCC {
         }
 
         // check target
-        if (!targetLanguage.equals("java")) {
+        if (!(targetLanguage.equals("java") || targetLanguage.equals("scala"))) {
             throw CompilerException.unknownTarget(targetLanguage);
         }
 
@@ -323,6 +324,10 @@ public class SableCC {
                 generateJavaCode(destinationDirectory, destinationPackage,
                         globalIndex, lexer, parser);
             }
+            else if(targetLanguage.equals("scala")) {
+                ScalaGenerator.generateCode(destinationDirectory, destinationPackage,
+                        globalIndex, lexer, parser);
+            }
             else {
                 throw new InternalException("unimplemented");
             }
diff --git a/utils/regenerate-macros b/utils/regenerate-macros
index ca0b344..db26f36 100755
--- a/utils/regenerate-macros
+++ b/utils/regenerate-macros
@@ -23,6 +23,7 @@ rm -f src/org/sablecc/objectmacro/codegeneration/intermediate/macro/*.java
 rm -f src/org/sablecc/objectmacro/errormessage/*.java
 
 rm -f src/org/sablecc/sablecc/codegeneration/java/macro/*.java
+rm -f src/org/sablecc/sablecc/codegeneration/scala/macro/*.java
 rm -f src/org/sablecc/sablecc/errormessage/*.java
 
 java -jar lib/objectmacro.jar -d src -p org.sablecc.objectmacro.codegeneration.java.macro macros/objectmacro-java.objectmacro
@@ -32,4 +33,5 @@ java -jar lib/objectmacro.jar -d src -p org.sablecc.objectmacro.codegeneration.i
 java -jar lib/objectmacro.jar -d src -p org.sablecc.objectmacro.errormessage macros/objectmacro-errormessage.objectmacro
 
 java -jar lib/objectmacro.jar -d src -p org.sablecc.sablecc.codegeneration.java.macro macros/sablecc-java.objectmacro
+java -jar lib/objectmacro.jar -d src -p org.sablecc.sablecc.codegeneration.scala.macro macros/sablecc-scala.objectmacro
 java -jar lib/objectmacro.jar -d src -p org.sablecc.sablecc.errormessage macros/sablecc-errormessage.objectmacro