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
object Main {
def main(args: Array[String]): Unit = {
val expr = """
println("result: "+(3+(if ({
println("cond");
3 < 5
}) println(12 * 2) else if(3+2) 15 + 3 else 0)));
12+6;
"""
val parser = new MiniParser
val ast = parser.parse(expr).get
val visitor = new ExprVisitor
var result = visitor.visit(ast);
println(result)
}
}
class ExprVisitor{
def visit(ast:AST):Any = {
ast match{
case Lines(exprs) =>{
var result:Any = Unit
for(x <- exprs){
result = visit(x)
}
result
}
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
}
}
}
}
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
class MiniParser extends RegexParsers{
//lines ::= expr {";" expr} [";"]
def lines: Parser[AST] = repsep(expr, ";")<~opt(";")^^{
values => Lines(values)
}
//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 |
"("~>expr<~")"^^{ x=>x } | "{"~>lines<~"}"^^{x=>x}
//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<~"\""^^{
value => StringVal(value)
}
// printLine ::= "printLn" "(" expr ")"
def printLine: Parser[AST] = "println"~"("~>expr<~")"^^{
value => PrintLine(value)
}
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