Scala parser combinators are a Powerful way to build parsers that can be used in everyday programs. But it’s hard to understand the plumbing pieces and how to get started. After you get the first couple of samples to compile and work, the plumbing starts to make sense,this is very easy way to design your own programming language, using its parser library
so lets get started first thing is that we need to extend the regex parser
to use scala parser combinators you need to add following dependencies in your build.sbt
libraryDependencies += "org.scala-lang.modules" %% "scala-parser-combinators" % "1.0.5"
sbt version is 2.11.3
EBNF grammar for this language would look something like this:
def symbol: Parser[Any] = "+" | "-" | "*" def number: Parser[Int] = """(0|[1-9]\d*)""".r ^^ { _.toInt }
symbol is our parser of type any that state that it will contain either the +,-, * symbol using | operator
number is parser of type int looking at it you might get confused ,it states that it will evaluate a regular expression of type int,then you might think what is is ^^ symbol is doing it denotes the operation that needs to be performed when the left hand side expression is evaluated so here it is converting this expression value to integer
def expression = { number ~ symbol ~ number ^^ { case firstOperand ~ operator ~ secondOperand => validateAndExtractFirstValue(firstOperand) + validateAndExtractSecondValue(secondOperand) }
now what is this method is doing
~ symbol in scala parser is used to seperate out the token in scala,it will parse the expression if the expression contain a number followed by a symbol followed by another number,then it simply add those two numbers
to be more concise here is the full code
import scala.util.Try import scala.util.parsing.combinator.RegexParsers class ScalaParser extends RegexParsers { def expression = { number ~ symbol ~ number ^^ { case firstOperand ~ operator ~ secondOperand => validateAndExtractFirstValue(firstOperand) + validateAndExtractSecondValue(secondOperand) } } def symbol: Parser[Any] = "+" | "-" | "*" def number: Parser[Int] = """(0|[1-9]\d*)""".r ^^ { _.toInt } def validateAndExtractFirstValue(firstOperand: Any): Int = { val firstValue: Try[Int] = Try(firstOperand.toString.toInt) firstValue match { case util.Success(value) => value case util.Failure(exception) => throw new Exception("can not convert values to integer") } } def validateAndExtractSecondValue(secondOperand: Any): Int = { val secondValue = Try(secondOperand.toString.toInt) secondValue match { case util.Success(value) => value case util.Failure(exception) => throw new Exception("can not convert values to integer") } } } object TestSimpleParser extends ScalaParser { def main(args: Array[String]) = { parse(expression, "5 + 4") match { case Success(result, _) => println(result) case Failure(msg, _) => println("FAILURE: " + msg) case Error(msg, _) => println("ERROR: " + msg) } } }
to be clear to call the parser you have to call the parse method it takes two arguments one is type of parser and second one is statement to be parsed
i think this blog will help to getting started with scala parser and combinators