Skip to content

Instantly share code, notes, and snippets.

@kishida
Created November 7, 2011 19:23
Show Gist options
  • Star 7 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
  • Save kishida/1345875 to your computer and use it in GitHub Desktop.
Save kishida/1345875 to your computer and use it in GitHub Desktop.
Mini parser with parser combinators of scala.
package miniparser
import scala.util.parsing.combinator.RegexParsers
import scala.collection.mutable.Map
object Main {
def main(args: Array[String]): Unit = {
val expr = """
val aa = 23;
println("pre block:" + aa);
{
val aa = 4;
println("in block:" + aa);
val bb = 3;
println(bb);
};
println("post block:" + aa);
println(bb);
"""
val parser = new MiniParser
val ast = parser.parse(expr).get
val visitor = new ExprVisitor
var result = visitor.eval(new Environment(None), ast);
}
}
class Environment(parent:Option[Environment]){
val variables = Map[String, Any]()
def get(key:String):Any = {
if(variables.contains(key)){
variables(key)
}else{
parent match{
case Some(p) => p.get(key)
case None => throw new Exception("symbol'%s' not found".format(key))
}
}
}
def set(key:String, value:Any){
variables(key) = value
}
}
class ExprVisitor{
def eval(env:Environment, ast:AST):Any = {
def visit(ast:AST):Any = {
ast match{
case Lines(exprs) =>{
val local = new Environment(Some(env))
exprs.foldLeft(Unit:Any){(result, x) => eval(local, x)}
}
case IfExpr(cond, pos, neg) =>{
visit(cond) match{
case true => visit(pos)
case false => visit(neg)
}
}
case LessOp(left, right) =>{
(visit(left), visit(right)) match{
case (lval:Int, rval:Int) => lval < rval
}
}
case AddOp(left, right) =>{
(visit(left), visit(right)) match{
case (lval:Int, rval:Int) => lval + rval
case (lval:String, rval) => lval + rval
case (lval, rval:String) => lval + rval
}
}
case SubOp(left, right) =>{
(visit(left), visit(right)) match{
case (lval:Int, rval:Int) => lval - rval
}
}
case MulOp(left, right) =>{
(visit(left), visit(right)) match{
case (lval:Int, rval:Int) => lval * rval
}
}
case IntVal(value) => value
case StringVal(value) => value
case PrintLine(value) => {
val v = visit(value);
println(v);
v
}
case Ident(name) => {
env.get(name)
}
case Assignment(vr, value) =>{
val v = visit(value)
env.set(vr.name, v)
}
}
}
visit(ast)
}
}
trait AST
case class Lines(exprs:List[AST]) extends AST
case class IfExpr(cond:AST, pos:AST, neg:AST) extends AST
case class LessOp(left: AST, right:AST) extends AST
case class AddOp(left: AST, right:AST) extends AST
case class SubOp(left: AST, right:AST) extends AST
case class MulOp(left: AST, right:AST) extends AST
case class StringVal(value: String) extends AST
case class PrintLine(value: AST) extends AST
case class IntVal(value: Int) extends AST
case class Ident(name: String) extends AST
case class Assignment(variable: Ident, value: AST) extends AST
class MiniParser extends RegexParsers{
//lines ::= expr {";" expr} [";"]
def lines: Parser[AST] = repsep(line, ";")<~opt(";")^^Lines
def line: Parser[AST] = expr | assignment
//expr ::= cond | if | printLine
def expr: Parser[AST] = condOp|ifExpr|printLine
//if ::= "if" "(" expr ")" expr "else" expr
def ifExpr: Parser[AST] = "if"~"("~>expr~")"~expr~"else"~expr^^{
case cond~_~pos~_~neg => IfExpr(cond, pos, neg)
}
//cond ::= add {"<" add}
def condOp: Parser[AST] = chainl1(add,
"<"^^{op => (left:AST, right:AST) => LessOp(left, right)})
//add ::= term {"+" term | "-" term}.
def add: Parser[AST] = chainl1(term,
"+"^^{op => (left:AST, right:AST) => AddOp(left, right)}|
"-"^^{op => (left:AST, right:AST) => SubOp(left, right)})
//term ::= factor {"*" factor}
def term : Parser[AST] = chainl1(factor,
"*"^^{op => (left:AST, right:AST) => MulOp(left, right)})
//factor ::= intLiteral | stringLiteral | "(" expr ")" | "{" lines "}"
def factor: Parser[AST] = intLiteral | stringLiteral | ident |
"("~>expr<~")" | "{"~>lines<~"}"
//intLiteral ::= ["1"-"9"] {"0"-"9"}
def intLiteral : Parser[AST] = """[1-9][0-9]*|0""".r^^{
value => IntVal(value.toInt)}
//stringLiteral ::= "\"" {"a-zA-Z0-9.."} "\""
def stringLiteral : Parser[AST] = "\""~>"""[a-zA-Z0-9:*/+\- ]*""".r<~"\""^^StringVal
def ident :Parser[Ident] = """[A-Za-z_][a-zA-Z0-9]*""".r^?{
case n if n != "if" && n!= "val" && n!= "println" => n}^^Ident
def assignment:Parser[Assignment] = "val"~>ident~"="~expr^^{
case v~_~value => Assignment(v, value)
}
// printLine ::= "printLn" "(" expr ")"
def printLine: Parser[AST] = "println"~"("~>expr<~")"^^PrintLine
def parse(str:String) = parseAll(lines, str)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment