Scalaの勉強をはじめたので、とりあえず簡単なパーサーを作ってみてます。
http://d.hatena.ne.jp/nowokay/20111101#1320102262
前回はif式で条件分岐してみました。
http://d.hatena.ne.jp/nowokay/20111106#1320542912
ここいらで計算過程を表示できるように、文字列とprintlnを導入しておきます。「+」演算子で文字列の連結ができるようにします。
それぞれの構文要素のためのケースクラスを用意します。
case class StringVal(value: String) extends AST case class PrintLine(value: AST) extends AST
こんな感じで、さも「ケースクラスについてもう理解したぜ」風に書いてますが、まだ言葉に振り回されてる気もします。
それはいいとして。
それぞれ構文規則を定義します。今回、文字列はダブルクオートでくくりますが、使える文字はアルファベットと数字と記号いくつかくらいにしておきます。めんどいから。
def stringLiteral : Parser[AST] = "\""~>"""[a-zA-Z0-9:*/+\- ]*""".r<~"\""^^{ value => StringVal(value) } def printLine: Parser[AST] = "println"~"("~>expr<~")"^^{ value => PrintLine(value) }
文字列リテラルはfactorのところに入れときます。
//factor ::= intLiteral | "(" expr ")" def factor: Parser[AST] = intLiteral | stringLiteral | "("~>expr<~")"^^{
printlnは一番最初に入れときます。
def expr: Parser[AST] = condOp|ifExpr|printLine
あとは、それぞれの評価の処理を行います。printlnは、表示した値をそのまま返すようにしておきます。
case StringVal(value) => value case PrintLine(value) => { val v = visit(value); println(v); v }
それと、「+」演算子を文字列に対応させておきます。どちらかが文字列なら文字列の連結です。けど、まあそのまま足し算になりますね。
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 } }
さてこんな感じで、式を与えてみます。
val expr = "\"result: \"+(3+(if (3 < 5) println(12 * 2) else if(3+2) 15 + 3 else 0))"
表示されました!
24 result: 27
というかなんか、表示されると面白い!
副作用ばんざい!
ソースはこんな感じになってます。
そろそろどっかに置いたほうがいいですかね。
package miniparser import scala.util.parsing.combinator.RegexParsers object Main { def main(args: Array[String]): Unit = { val expr = "\"result: \"+(3+(if (3 < 5) println(12 * 2) else if(3+2) 15 + 3 else 0))"//"12-5*3+7*(2+5)*1+0" 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 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 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{ //expr ::= cond | if 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 | "(" expr ")" def factor: Parser[AST] = intLiteral | stringLiteral | "("~>expr<~")"^^{ x=>x} //intLiteral ::= ["1"-"9"] {"0"-"9"} def intLiteral : Parser[AST] = """[1-9][0-9]*|0""".r^^{ value => IntVal(value.toInt)} def stringLiteral : Parser[AST] = "\""~>"""[a-zA-Z0-9:*/+\- ]*""".r<~"\""^^{ value => StringVal(value) } def printLine: Parser[AST] = "println"~"("~>expr<~")"^^{ value => PrintLine(value) } def parse(str:String) = parseAll(expr, str) }