transform your state \/ err
TRANSCRIPT
@gerferra
Transform your State \/ Err
Transform your State \/ Err
An example of functional parsing with state and error handling
An example of functional parsing with state and error handling
@gerferra
Initial language
Arithmetic expressions over integers
1;
2+2;
3*3+4*4*(1+2);
Functional parsing
Write a parser using “just functions” ...
… within some programming language
… in the form of some embedded DSL
Functional parsingAll of this has a proper name:
Parser combinators
A collection of basic parsing functions that recognise a piece of input
A collection of combinators that build new parsers out of existing oneshttp://www.fing.edu.uy/inco/cursos/pfa/uploads/Main/parsercombinators
Parser combinatorsScala has many parsing combinator libraries
Two common ones:
scala-parser-combinators
parboiled2
Parser combinatorsScala has many parsing combinator libraries
Two common ones:
scala-parser-combinators
parboiled2
The one we will be using
Parser combinatorsScala has many parsing combinator libraries
Two common ones:
scala-parser-combinators
parboiled2 Not really parser combinators ...
but very fast!
Parser combinatorsScala has many parsing combinator libraries
Two common ones:
scala-parser-combinators
parboiled2
New kid on the block: lihaoyi/fastparse
scala-parser-combinators
Part of the Scala standard library but distributed separately
libraryDependencies += "org.scala-lang.modules" %% "scala-parser-combinators" % "1.0.4"
scala-parser-combinators
Slow but good enough in this case
The first (?) parser combinator library for Scala
Many (but not all) of the other parser combinator libraries use similar names/symbols for the combinators
scala-parser-combinatorsimport scala.util.parsing.combinator._
object BasicCombinators extends Parsers
with RegexParsers {
def pA : Parser[Char] = 'A' def pB : Parser[Char] = 'B' def pAlt: Parser[Char] = pA | pB def pSeq: Parser[Char ~ Char] = pA ~ pB def pRep: Parser[List[Char]] = rep(pA) def pOpt: Parser[Option[Char]] = opt(pA)}
import scala.util.parsing.combinator._
object BasicCombinators extends Parsers
with RegexParsers {
def pA : Parser[Char] = 'A' def pB : Parser[Char] = 'B' def pAlt: Parser[Char] = pA | pB def pSeq: Parser[Char ~ Char] = pA ~ pB def pRep: Parser[List[Char]] = rep(pA) def pOpt: Parser[Option[Char]] = opt(pA)}
scala-parser-combinatorsBase type
import scala.util.parsing.combinator._
object BasicCombinators extends Parsers
with RegexParsers {
def pA : Parser[Char] = 'A' def pB : Parser[Char] = 'B' def pAlt: Parser[Char] = pA | pB def pSeq: Parser[Char ~ Char] = pA ~ pB def pRep: Parser[List[Char]] = rep(pA) def pOpt: Parser[Option[Char]] = opt(pA)}
scala-parser-combinators
Useful extensions
import scala.util.parsing.combinator._
object BasicCombinators extends Parsers
with RegexParsers {
def pA : Parser[Char] = 'A' def pB : Parser[Char] = 'B' def pAlt: Parser[Char] = pA | pB def pSeq: Parser[Char ~ Char] = pA ~ pB def pRep: Parser[List[Char]] = rep(pA) def pOpt: Parser[Option[Char]] = opt(pA)}
scala-parser-combinators
alternative, sequence, repetition, optionals ...
scala-parser-combinators
trait Parsers { type Input
sealed trait ParseResult[+T]
trait Parser[+T] extends (Input => ParseResult[T])}
scala-parser-combinators
trait Parsers { type Input
sealed trait ParseResult[+T]
trait Parser[+T] extends (Input => ParseResult[T])}
Success[+T] | NoSuccess...plus the input not consumed
scala-parser-combinators
trait Parsers { type Input
sealed trait ParseResult[+T]
trait Parser[+T] extends (Input => ParseResult[T])}
Success[+T] | NoSuccess...plus the input not consumed
~ Input => (T, Input)
scala-parser-combinators
trait Parsers { type Input
sealed trait ParseResult[+T]
trait Parser[+T] extends (Input => ParseResult[T])} A Parser is a function taking some input and
returning either some value of type T, or a failure
Success[+T] | NoSuccess...plus the input not consumed
scala-parser-combinators
trait Parsers { type Input
sealed trait ParseResult[+T]
trait Parser[+T] extends (Input => ParseResult[T])} A Parser is a function taking some input and
returning either some value of type T, or a failure
Success[+T] | NoSuccess...plus the input not consumed
Returning to our language
1;
2+2;
3*3+4*4*(1+2);
Returning to our languageobject Parser1 extends RegexParsers {
def pL1: Parser[List[Int]] = rep(pExpr <~ ";")
def pExpr = pAdd
def pAdd: Parser[Int] = pProd ~ "+" ~ pAdd ^^ { case left ~ _ ~ right => left + right } | pProd
def pProd: Parser[Int] = pAtom ~ "*" ~ pProd ^^ { case left ~ _ ~ right => left * right } | pAtom
def pAtom: Parser[Int] = """\d+""".r ^^ (_.toInt) | "(" ~> pExpr <~ ")"}
Returning to our languageobject Parser1 extends RegexParsers {
def pL1: Parser[List[Int]] = rep(pExpr <~ ";")
def pExpr = pAdd
def pAdd: Parser[Int] = pProd ~ "+" ~ pAdd ^^ { case left ~ _ ~ right => left + right } | pProd
def pProd: Parser[Int] = pAtom ~ "*" ~ pProd ^^ { case left ~ _ ~ right => left * right } | pAtom
def pAtom: Parser[Int] = """\d+""".r ^^ (_.toInt) | "(" ~> pExpr <~ ")"}
Many things going on
Returning to our languageobject Parser1 extends RegexParsers {
def pL1: Parser[List[Int]] = rep(pExpr <~ ";")
def pExpr = pAdd
def pAdd: Parser[Int] = pProd ~ "+" ~ pAdd ^^ { case left ~ _ ~ right => left + right } | pProd
def pProd: Parser[Int] = pAtom ~ "*" ~ pProd ^^ { case left ~ _ ~ right => left * right } | pAtom
def pAtom: Parser[Int] = """\d+""".r ^^ (_.toInt) | "(" ~> pExpr <~ ")"}
We are evaluating the result directly, avoiding construct an AST
Returning to our languageobject Parser1 extends RegexParsers {
def pL1: Parser[List[Int]] = rep(pExpr <~ ";")
def pExpr = pAdd
def pAdd: Parser[Int] = pProd ~ "+" ~ pAdd ^^ { case left ~ _ ~ right => left + right } | pProd
def pProd: Parser[Int] = pAtom ~ "*" ~ pProd ^^ { case left ~ _ ~ right => left * right } | pAtom
def pAtom: Parser[Int] = """\d+""".r ^^ (_.toInt) | "(" ~> pExpr <~ ")"}
Precedence rules are encoded in the grammar
Returning to our languageobject Parser1 extends RegexParsers {
def pL1: Parser[List[Int]] = rep(pExpr <~ ";")
def pExpr = pAdd
def pAdd: Parser[Int] = pProd ~ "+" ~ pAdd ^^ { case left ~ _ ~ right => left + right } | pProd
def pProd: Parser[Int] = pAtom ~ "*" ~ pProd ^^ { case left ~ _ ~ right => left * right } | pAtom
def pAtom: Parser[Int] = """\d+""".r ^^ (_.toInt) | "(" ~> pExpr <~ ")"}
This is evaluating from right to left.
If you need to control associativity, the rep combinator is your friend.
Returning to our languageobject Parser1 extends RegexParsers {
def pL1: Parser[List[Int]] = rep(pExpr <~ ";")
def pExpr = pAdd
def pAdd: Parser[Int] = pProd ~ "+" ~ pAdd ^^ { case left ~ _ ~ right => left + right } | pProd
def pProd: Parser[Int] = pAtom ~ "*" ~ pProd ^^ { case left ~ _ ~ right => left * right } | pAtom
def pAtom: Parser[Int] = """\d+""".r ^^ (_.toInt) | "(" ~> pExpr <~ ")"}
Regular expressions are converted to Parsers implicitly
Returning to our languageobject Parser1 extends RegexParsers {
def pL1: Parser[List[Int]] = rep(pExpr <~ ";")
def pExpr = pAdd
def pAdd: Parser[Int] = pProd ~ "+" ~ pAdd ^^ { case left ~ _ ~ right => left + right } | pProd
def pProd: Parser[Int] = pAtom ~ "*" ~ pProd ^^ { case left ~ _ ~ right => left * right } | pAtom
def pAtom: Parser[Int] = """\d+""".r ^^ (_.toInt) | "(" ~> pExpr <~ ")"}
^^ is an alias of the map function
Returning to our languageobject Parser1 extends RegexParsers {
def pL1: Parser[List[Int]] = rep(pExpr <~ ";")
def pExpr = pAdd
def pAdd: Parser[Int] = pProd ~ "+" ~ pAdd ^^ { case left ~ _ ~ right => left + right } | pProd
def pProd: Parser[Int] = pAtom ~ "*" ~ pProd ^^ { case left ~ _ ~ right => left * right } | pAtom
def pAtom: Parser[Int] = """\d+""".r ^^ (_.toInt) | "(" ~> pExpr <~ ")"}
Allows to convert the Parser[String] to Parser[Int]^^ is an alias of
the map function
Returning to our languageobject Parser1 extends RegexParsers {
def pL1: Parser[List[Int]] = rep(pExpr <~ ";")
def pExpr = pAdd
def pAdd: Parser[Int] = pProd ~ "+" ~ pAdd ^^ { case left ~ _ ~ right => left + right } | pProd
def pProd: Parser[Int] = pAtom ~ "*" ~ pProd ^^ { case left ~ _ ~ right => left * right } | pAtom
def pAtom: Parser[Int] = """\d+""".r ^^ (_.toInt) | "(" ~> pExpr <~ ")"}
Allows to convert the Parser[String] to Parser[Int]
String => Int
^^ is an alias of the map function
Returning to our languageobject Parser1 extends RegexParsers {
def pL1: Parser[List[Int]] = rep(pExpr <~ ";")
def pExpr = pAdd
def pAdd: Parser[Int] = pProd ~ "+" ~ pAdd ^^ { case left ~ _ ~ right => left + right } | pProd
def pProd: Parser[Int] = pAtom ~ "*" ~ pProd ^^ { case left ~ _ ~ right => left * right } | pAtom
def pAtom: Parser[Int] = """\d+""".r ^^ (_.toInt) | "(" ~> pExpr <~ ")"}
Parse two inputs in sequence discarding the input of the left or the right respectively
Returning to our languageobject Parser1 extends RegexParsers {
def pL1: Parser[List[Int]] = rep(pExpr <~ ";")
def pExpr = pAdd
def pAdd: Parser[Int] = pProd ~ "+" ~ pAdd ^^ { case left ~ _ ~ right => left + right } | pProd
def pProd: Parser[Int] = pAtom ~ "*" ~ pProd ^^ { case left ~ _ ~ right => left * right } | pAtom
def pAtom: Parser[Int] = """\d+""".r ^^ (_.toInt) | "(" ~> pExpr <~ ")"}
Pattern matching anonymous function
Returning to our languageobject Parser1 extends RegexParsers {
def pL1: Parser[List[Int]] = rep(pExpr <~ ";")
def pExpr = pAdd
def pAdd: Parser[Int] = pProd ~ "+" ~ pAdd ^^ { case left ~ _ ~ right => left + right } | pProd
def pProd: Parser[Int] = pAtom ~ "*" ~ pProd ^^ { case left ~ _ ~ right => left * right } | pAtom
def pAtom: Parser[Int] = """\d+""".r ^^ (_.toInt) | "(" ~> pExpr <~ ")"}
Pattern matching anonymous function
Allows to deconstruct the result of this parser
Returning to our languageobject Parser1 extends RegexParsers {
def pL1: Parser[List[Int]] = rep(pExpr <~ ";")
def pExpr = pAdd
def pAdd: Parser[Int] = pProd ~ "+" ~ pAdd ^^ { case left ~ _ ~ right => left + right } | pProd
def pProd: Parser[Int] = pAtom ~ "*" ~ pProd ^^ { case left ~ _ ~ right => left * right } | pAtom
def pAtom: Parser[Int] = """\d+""".r ^^ (_.toInt) | "(" ~> pExpr <~ ")"}
Pattern matching anonymous function
Allows to deconstruct the result of this parser
Int IntString
“+” discarded Int
Returning to our languageobject Parser1 extends RegexParsers {
def pL1: Parser[List[Int]] = rep(pExpr <~ ";") def pExpr = pAdd
def pAdd: Parser[Int] = pProd ~ "+" ~ pAdd ^^ { case left ~ _ ~ right => left + right } | pProd
def pProd: Parser[Int] = pAtom ~ "*" ~ pProd ^^ { case left ~ _ ~ right => left * right } | pAtom
def pAtom: Parser[Int] = """\d+""".r ^^ (_.toInt) | "(" ~> pExpr <~ ")"}
We want a list of all the expressions we encounter separated by “;”
Returning to our languageobject Parser1 extends RegexParsers {
def pL1: Parser[List[Int]] = rep(pExpr <~ ";") def pExpr = pAdd
def pAdd: Parser[Int] = pProd ~ "+" ~ pAdd ^^ { case left ~ _ ~ right => left + right } | pProd
def pProd: Parser[Int] = pAtom ~ "*" ~ pProd ^^ { case left ~ _ ~ right => left * right } | pAtom
def pAtom: Parser[Int] = """\d+""".r ^^ (_.toInt) | "(" ~> pExpr <~ ")"}
All good?
Mini testimport Parser1._
parse(pExpr, "1") //> [1.2] parsed: 1parse(pExpr, "2+2") //> [1.4] parsed: 4parse(pExpr, "3*3+4*4*(1+2)") //> [1.14] parsed: 57
parse(pL1, """1; |2+2; |3*3+4*4*(1+2);""".stripMargin) //> [3.15] parsed: List(1, 4, 57)
parse(pL1, "1; 2+2; 3*3+4*4*(1+2);") //> [1.8] parsed: List(1, 4, 57)
Now …
Now …
What if we want to add variables?
What if we want to add variables?
We need to handle two more cases:
Assignation of variablesReferences to variables
What if we want to add variables?
We need to handle two more cases:
Assignation of variablesReferences to variables
def pRef = pName ^^ { x => get(symtab, x)}
def pVar = "var" ~ pName ~ "=" ~ pExpr ^^ { case _ ~ name ~ _ ~ expr => put(symtab, name, expr) }
We need to handle two more cases:
Assignation of variablesReferences to variables
And we need to carry a symbol table ...
def pVar = "var" ~ pName ~ "=" ~ pExpr ^^ { case _ ~ name ~ _ ~ expr => put(symtab, name, expr) }
What if we want to add variables?
?def pRef = pName ^^ { x => get(symtab, x)}
Carrying a symbol table
On pRef we need to access the symbol table but leave it unmodified
On pVar we need to update the symbol table
Otherwise we just need to pass it untouched
def pRef = pName ^^ (x => get(symtab, x))
def pVar = "var" ~ pName ~ "=" ~ pExpr ^^ { case _ ~ name ~ _ ~ expr => put(symtab, name, expr) }
Enter scalaz.State[S,A]
scalaz has a type that represents state transitions
State[S,A] wraps functions with type:
S => (S, A)Current state
New stateValue computed
Enter scalaz.State[S,A]
We need to change the type of our parsers ...
Enter scalaz.State[S,A]
We need to change the type of our parsers ...
Parser[A]
Parser[State[S,A]]
Parser[State[SymTab, A]]
Parser[S[A]]
P[A]
Enter scalaz.State[S,A]
We need to change the type of our parsers ...We now return A, but in the context of some stateful computationParser[A]
Parser[State[S,A]]
Parser[State[SymTab, A]]
Parser[S[A]]
P[A]
Enter scalaz.State[S,A]
We need to change the type of our parsers ...We now return A, but in the context of some stateful computation
Our state is the symbol table~Map[String, Int]
Parser[A]
Parser[State[S,A]]
Parser[State[SymTab, A]]
Parser[S[A]]
P[A]
Parser[A]
Parser[State[S,A]]
Parser[State[SymTab, A]]
Parser[S[A]]
P[A]
Enter scalaz.State[S,A]
We need to change the type of our parsers ...We now return A, but in the context of some stateful computation
type S[A] = State[SymTab,A]
Our state is the symbol table~Map[String, Int]
Parser[A]
Parser[State[S,A]]
Parser[State[SymTab, A]]
Parser[S[A]]
P[A]
Enter scalaz.State[S,A]
We need to change the type of our parsers ...We now return A, but in the context of some stateful computation
type S[A] = State[SymTab,A]
Our state is the symbol table~Map[String, Int]
type P[A] = Parser[S[A]]
Enter scalaz.State[S,A]
… and change the parsers themselves a bit
Enter scalaz.State[S,A]
… and change the parsers themselves a bitdef pAdd: P[Int] = pProd ~ "+" ~ pAdd ^^ { case leftS ~ _ ~ rightS => for { left <- leftS right <- rightS } yield { left + right } } | pProd
def pAdd: Parser[Int] = pProd ~ "+" ~ pAdd ^^ { case left ~ _ ~ right => left + right } | pProd
Enter scalaz.State[S,A]
… and change the parsers themselves a bitdef pAdd: P[Int] = pProd ~ "+" ~ pAdd ^^ { case leftS ~ _ ~ rightS => for { left <- leftS right <- rightS } yield { left + right } } | pProd
def pAdd: Parser[Int] = pProd ~ "+" ~ pAdd ^^ { case left ~ _ ~ right => left + right } | pProd
P[Int] == Parser[S[Int]]
Enter scalaz.State[S,A]
… and change the parsers themselves a bitdef pAdd: P[Int] = pProd ~ "+" ~ pAdd ^^ { case leftS ~ _ ~ rightS => for { left <- leftS right <- rightS } yield { left + right } } | pProd
def pAdd: Parser[Int] = pProd ~ "+" ~ pAdd ^^ { case left ~ _ ~ right => left + right } | pProd
P[Int] == Parser[S[Int]]
S[Int]
Enter scalaz.State[S,A]
… and change the parsers themselves a bitdef pAdd: P[Int] = pProd ~ "+" ~ pAdd ^^ { case leftS ~ _ ~ rightS => for { left <- leftS right <- rightS } yield { left + right } } | pProd
def pAdd: Parser[Int] = pProd ~ "+" ~ pAdd ^^ { case left ~ _ ~ right => left + right } | pProd
P[Int] == Parser[S[Int]]
S[Int]
leftS and rightS areS[Int] and can’t be added directly
Enter scalaz.State[S,A]
… and change the parsers themselves a bitdef pAdd: P[Int] = pProd ~ "+" ~ pAdd ^^ { case leftS ~ _ ~ rightS => for { left <- leftS right <- rightS } yield { left + right } } | pProd
def pAdd: Parser[Int] = pProd ~ "+" ~ pAdd ^^ { case left ~ _ ~ right => left + right } | pProd
P[Int] == Parser[S[Int]]
S[Int]
But State[S,A] is a Monad[A]
State[SymTab,A]
Enter scalaz.State[S,A]
… and change the parsers themselves a bitdef pAdd: P[Int] = pProd ~ "+" ~ pAdd ^^ { case leftS ~ _ ~ rightS => for { left <- leftS right <- rightS } yield { left + right } } | pProd
def pAdd: Parser[Int] = pProd ~ "+" ~ pAdd ^^ { case left ~ _ ~ right => left + right } | pProd
P[Int] == Parser[S[Int]]
S[Int]
State[SymTab,A]So we can use a for-comprehension to work with the ints
Enter scalaz.State[S,A]
… and change the parsers themselves a bitdef pAdd: P[Int] = pProd ~ "+" ~ pAdd ^^ { case leftS ~ _ ~ rightS => for { left <- leftS right <- rightS } yield { left + right } } | pProd
def pAdd: Parser[Int] = pProd ~ "+" ~ pAdd ^^ { case left ~ _ ~ right => left + right } | pProd
P[Int] == Parser[S[Int]]
S[Int]
State[SymTab,A]When we treatState[S,A] as a Monad[A], we work with the values, and the state transitions happens in the background
Enter scalaz.State[S,A]
… and change the parsers themselves a bitdef pAdd: P[Int] = pProd ~ "+" ~ pAdd ^^ { case leftS ~ _ ~ rightS => for { left <- leftS right <- rightS } yield { left + right } } | pProd
def pAdd: Parser[Int] = pProd ~ "+" ~ pAdd ^^ { case left ~ _ ~ right => left + right } | pProd
P[Int] == Parser[S[Int]]
S[Int]
State[SymTab,A]Here we are chaining three state transitionss0 => (s1, left)s1 => (s2, right)s2 => (s2, left+right)
Enter scalaz.State[S,A]
… and change the parsers themselves a bitdef pAdd: P[Int] = pProd ~ "+" ~ pAdd ^^ { case leftS ~ _ ~ rightS => for { left <- leftS right <- rightS } yield { left + right } } | pProd
def pAdd: Parser[Int] = pProd ~ "+" ~ pAdd ^^ { case left ~ _ ~ right => left + right } | pProd
P[Int] == Parser[S[Int]]
S[Int]
State[SymTab,A]Which can be seen as one big transition:
s0 => ... (s2, left+right)
The State[S,A] we return
Enter scalaz.State[S,A]
… and change the parsers themselves a bitdef pRef: Parser[Int] = pName ^^ { x => get(symtab, x) }
def pRef: P[Int] = pName ^^ { name => for { symtab <- State.get } yield { symtab(name) } }
Enter scalaz.State[S,A]
… and change the parsers themselves a bitdef pRef: Parser[Int] = pName ^^ { x => get(symtab, x) }
def pRef: P[Int] = pName ^^ { name => for { symtab <- State.get } yield { symtab(name) } }
Here we need to access to the symbol table—our state
Enter scalaz.State[S,A]
… and change the parsers themselves a bitdef pRef: Parser[Int] = pName ^^ { x => get(symtab, x) }
def pRef: P[Int] = pName ^^ { name => for { symtab <- State.get } yield { symtab(name) } }
The trick of State.get is to create a new State[S,S] where the state is used as the value
Enter scalaz.State[S,A]
… and change the parsers themselves a bitdef pRef: Parser[Int] = pName ^^ { x => get(symtab, x) }
def pRef: P[Int] = pName ^^ { name => for { symtab <- State.get } yield { symtab(name) } }
The trick of State.get is to create a new State[S,S] where the state is used as the value
Wraps a state transition { s => (s, s) }
Enter scalaz.State[S,A]
… and change the parsers themselves a bitdef pRef: Parser[Int] = pName ^^ { x => get(symtab, x) }
def pRef: P[Int] = pName ^^ { name => for { symtab <- State.get } yield { symtab(name) } }
Wraps a state transition { s => (s, s) }
With such State[S,S] created, now we can use again a for-comprehension to work with the symbol table (the value)
Enter scalaz.State[S,A]
… and change the parsers themselves a bitdef pRef: Parser[Int] = pName ^^ { x => get(symtab, x) }
def pRef: P[Int] = pName ^^ { name => for { symtab <- State.get } yield { symtab(name) } }
Wraps a state transition { s => (s, s) }
We are chaining two state transitions:s => (s, s)s => (s, s(name))
… and change the parsers themselves a bit
Enter scalaz.State[S,A]
def pRef: Parser[Int] = pName ^^ { x => get(symtab, x) }
def pRef: P[Int] = pName ^^ { name => for { symtab <- State.get } yield { symtab(name) } }
Wraps a state transition { s => (s, s) }
Which can be seen as:
s => ... (s, s(name))
The State[SymTab, Int] == S[Int] we return
Enter scalaz.State[S,A]
… and change the parsers themselves a bitdef pVar: Parser[Unit] = "var"~pName~"="~pExpr ^^ { case _ ~ name ~ _ ~ expr => put(symtab,name,expr) }
def pVar: P[Unit] = "var"~pName~"="~pExpr ^^ { case _ ~ name ~ _ ~ exprS => for { expr <- exprS symtab <- State.get unit <-State.put( symtab+(name->expr) ) } yield { unit } }
Enter scalaz.State[S,A]
… and change the parsers themselves a bitdef pVar: Parser[Unit] = "var"~pName~"="~pExpr ^^ { case _ ~ name ~ _ ~ expr => put(symtab,name,expr) }
def pVar: P[Unit] = "var"~pName~"="~pExpr ^^ { case _ ~ name ~ _ ~ exprS => for { expr <- exprS symtab <- State.get unit <-State.put( symtab+(name->expr) ) } yield { unit } }
The last case
Enter scalaz.State[S,A]
… and change the parsers themselves a bitdef pVar: Parser[Unit] = "var"~pName~"="~pExpr ^^ { case _ ~ name ~ _ ~ expr => put(symtab,name,expr) }
def pVar: P[Unit] = "var"~pName~"="~pExpr ^^ { case _ ~ name ~ _ ~ exprS => for { expr <- exprS symtab <- State.get unit <-State.put( symtab+(name->expr) ) } yield { unit } }
We need to modify the state
Enter scalaz.State[S,A]
… and change the parsers themselves a bitdef pVar: Parser[Unit] = "var"~pName~"="~pExpr ^^ { case _ ~ name ~ _ ~ expr => put(symtab,name,expr) }
def pVar: P[Unit] = "var"~pName~"="~pExpr ^^ { case _ ~ name ~ _ ~ exprS => for { expr <- exprS symtab <- State.get unit <-State.put( symtab+(name->expr) ) } yield { unit } }
We need to modify the state
S[Int]
Enter scalaz.State[S,A]
… and change the parsers themselves a bitdef pVar: Parser[Unit] = "var"~pName~"="~pExpr ^^ { case _ ~ name ~ _ ~ expr => put(symtab,name,expr) }
def pVar: P[Unit] = "var"~pName~"="~pExpr ^^ { case _ ~ name ~ _ ~ exprS => for { expr <- exprS symtab <- State.get unit <-State.put( symtab+(name->expr) ) } yield { unit } }
We need to modify the state
S[SymTab]S[Int]
Enter scalaz.State[S,A]
… and change the parsers themselves a bitdef pVar: Parser[Unit] = "var"~pName~"="~pExpr ^^ { case _ ~ name ~ _ ~ expr => put(symtab,name,expr) }
def pVar: P[Unit] = "var"~pName~"="~pExpr ^^ { case _ ~ name ~ _ ~ exprS => for { expr <- exprS symtab <- State.get unit <-State.put( symtab+(name->expr) ) } yield { unit } }
To change the state we use State.put
S[SymTab]S[Int]
Enter scalaz.State[S,A]
… and change the parsers themselves a bitdef pVar: Parser[Unit] = "var"~pName~"="~pExpr ^^ { case _ ~ name ~ _ ~ expr => put(symtab,name,expr) }
Given s, it creates aState[S,Unit] representing this state transition: { _ => (s, ()) }
def pVar: P[Unit] = "var"~pName~"="~pExpr ^^ { case _ ~ name ~ _ ~ exprS => for { expr <- exprS symtab <- State.get unit <-State.put( symtab+(name->expr) ) } yield { unit } }
S[SymTab]S[Int]
Enter scalaz.State[S,A]
… and change the parsers themselves a bitdef pVar: Parser[Unit] = "var"~pName~"="~pExpr ^^ { case _ ~ name ~ _ ~ expr => put(symtab,name,expr) }
This discards any previous state in favor of the one provided, and returns () { _ => (s, ()) }
def pVar: P[Unit] = "var"~pName~"="~pExpr ^^ { case _ ~ name ~ _ ~ exprS => for { expr <- exprS symtab <- State.get unit <-State.put( symtab+(name->expr) ) } yield { unit } }
S[SymTab]S[Int]
Enter scalaz.State[S,A]
… and change the parsers themselves a bitdef pVar: Parser[Unit] = "var"~pName~"="~pExpr ^^ { case _ ~ name ~ _ ~ expr => put(symtab,name,expr) }
def pVar: P[Unit] = "var"~pName~"="~pExpr ^^ { case _ ~ name ~ _ ~ exprS => for { expr <- exprS symtab <- State.get unit <-State.put( symtab+(name->expr) ) } yield { unit } }
So, the value we get in the for-comprehension is the unit
S[Unit]
S[SymTab]S[Int]
Enter scalaz.State[S,A]
… and change the parsers themselves a bitdef pVar: Parser[Unit] = "var"~pName~"="~pExpr ^^ { case _ ~ name ~ _ ~ expr => put(symtab,name,expr) }
def pVar: P[Unit] = "var"~pName~"="~pExpr ^^ { case _ ~ name ~ _ ~ exprS => for { expr <- exprS symtab <- State.get unit <-State.put( symtab+(name->expr) ) } yield { unit } }
We have four state transitions:s0 => (s1, expr)s1 => (s1, s1)_ => (s2=s1+(name->expr), ())s2 => (s2, ())
S[Unit]
S[SymTab]S[Int]
Enter scalaz.State[S,A]
… and change the parsers themselves a bitdef pVar: Parser[Unit] = "var"~pName~"="~pExpr ^^ { case _ ~ name ~ _ ~ expr => put(symtab,name,expr) }
def pVar: P[Unit] = "var"~pName~"="~pExpr ^^ { case _ ~ name ~ _ ~ exprS => for { expr <- exprS symtab <- State.get unit <-State.put( symtab+(name->expr) ) } yield { unit } }
So we are returning:
s0 => … (s1+(name->expr), ())
State[SymTab, Unit] == S[Unit] S[Unit]
S[SymTab]S[Int]
Enter scalaz.State[S,A]
… and change the parsers themselves a bitdef pVar: Parser[Unit] = "var"~pName~"="~pExpr ^^ { case _ ~ name ~ _ ~ expr => put(symtab,name,expr) }
def pVar: P[Unit] = "var"~pName~"="~pExpr ^^ { case _ ~ name ~ _ ~ exprS => for { expr <- exprS symtab <- State.get unit <-State.put( symtab+(name->expr) ) } yield { unit } }
Now we know the basics of the State monad
Running the stateimport Parser2._
parse(pExpr, "1") //> [1.2] parsed: scalaz.package$State$$anon$3@7f560810
Running the stateimport Parser2._
parse(pExpr, "1") //> [1.2] parsed: scalaz.package$State$$anon$3@7f560810
Our parser now returns State[SymTab,A], not directly a value
Running the stateimport Parser2._
parse(pExpr, "1") //> [1.2] parsed: scalaz.package$State$$anon$3@7f560810
To compute the final value, we need to provide an initial state and then all the state transitions can be run
Running the stateimport Parser2._
parse(pExpr, "1").map(state => state.run(Map.empty))//> [1.2] parsed: (Map(),1)
To compute the final value, we need to provide an initial state and then all the state transitions can be run
Running the stateimport Parser2._
parse(pExpr, "1").map(state => state.run(Map.empty))//> [1.2] parsed: (Map(),1)
To compute the final value, we need to provide an initial state and then all the state transitions can be run
map over ParseResult[S[Int]]
Running the stateimport Parser2._
parse(pExpr, "1").map(state => state.run(Map.empty))//> [1.2] parsed: (Map(),1)
To compute the final value, we need to provide an initial state and then all the state transitions can be run
map over ParseResult[S[Int]]Initial state
Running the stateimport Parser2._
parse(pExpr, "1").map(state => state.run(Map.empty))//> [1.2] parsed: (Map(),1)
To compute the final value, we need to provide an initial state and then all the state transitions can be run
map over ParseResult[S[Int]]Initial state
We obtain the final state and the value computed
Running the stateimport Parser2._
parse(pExpr, "1").map(_.run(Map.empty))//> [1.2] parsed: (Map(),1)
parse(pVar, "var x = 1").map(_.run(Map.empty))//> [1.10] parsed: (Map(x -> 1),())
parse(pExpr, "y * 3").map(_.run(Map("y" -> 2)))//> [1.6] parsed: (Map(y -> 2),6)
Running the stateimport Parser2._
parse(pExpr, "1").map(_.run(Map.empty))//> [1.2] parsed: (Map(),1)
parse(pVar, "var x = 1").map(_.run(Map.empty))//> [1.10] parsed: (Map(x -> 1),())
parse(pExpr, "y * 3").map(_.run(Map("y" -> 2)))//> [1.6] parsed: (Map(y -> 2),6)
The final state reflects the binding of 1 to x
Running the stateimport Parser2._
parse(pExpr, "1").map(_.run(Map.empty))//> [1.2] parsed: (Map(),1)
parse(pVar, "var x = 1").map(_.run(Map.empty))//> [1.10] parsed: (Map(x -> 1),())
parse(pExpr, "y * 3").map(_.run(Map("y" -> 2)))//> [1.6] parsed: (Map(y -> 2),6)
The final state reflects the binding of 1 to x
We can provide bindings in the initial state
Running the stateparse(pL2, """var x = 1; |var y = x * 3; |x + y; |(x + 1) * 3 + 1; |var z = 8;""".stripMargin).map(_.run(Map.empty))//> [5.11] parsed: (Map(x -> 1, y -> 3, z -> 8),List(4, 7))
Now ...
Now ...
What if we reference an undeclared variable?parse(pL2, "var x = y;").map(_.run(Map.empty))
Now ...
What if we reference an undeclared variable?parse(pL2, "var x = y;").map(_.run(Map.empty))
Exception in thread "main" java.util.NoSuchElementException: key not found: y
at scala.collection.MapLike$class.default(MapLike.scala:228)at scala.collection.AbstractMap.default(Map.scala:59)at scala.collection.MapLike$class.apply(MapLike.scala:141)at scala.collection.AbstractMap.apply(Map.scala:59)...
Handling errors
If our computation can fail, then our types are wrong. We can’t just return A
Handling errors
If our computation can fail, then our types are wrong. We can’t just return A
We need to handleReferences to undeclared variablesDeclarations of already declared variables
scalaz.\/
\/[A,B] == A\/B, called “or”, “xor”, “either”, “disjoint union”,..., can be used to handle the cases when a computation can fail
A \/ BThe type of the error. By convention on the left
The type of the value we are computing. By convention on the right
scalaz.\/
\/[A,B] is the scalaz version ofscala.Either[A,B]
Handling errors
We will have to change our types again:
type V[A] = Err \/ A
type S[A] = State[SymTab, V[A]]
sealed abstract class Err(override val toString: String)
case class NotDeclared(name: String) extends Err(s"`$name' not declared.")
case class AlreadyDeclared(name: String, value: Int) extends Err(s"`$name' already declared with value `$value'.")
Bad news
Bad news
It gets ugly ...
It gets ugly ...def pVar: P[Unit] = "var" ~ pName ~ "=" ~ pExpr ^^ { case _ ~ name ~ _ ~ exprS => for { exprV <- exprS symtab <- State.get[SymTab] newSymTab = for { expr <- exprV s <- symtab.get(name) match { // if the name exists, creates a left with the error case Some(v) => AlreadyDeclared(name, v).left // if not exists, returns the symtab as a right case None => symtab.right } } yield { s + (name -> expr) // only executes if everything is right } res <- newSymTab match { // if the newSymTab is a left, I need to put the error in the State case -\/(l) => State.state[SymTab, V[Unit]](l.left) // if the newSymTab is a right, I need to use it as the new state, and put the () inside a \/ case \/-(r) => State.put(r).map(_.right) } } yield { res }}
It gets ugly ...def pVar: P[Unit] = "var" ~ pName ~ "=" ~ pExpr ^^ { case _ ~ name ~ _ ~ exprS => for { exprV <- exprS symtab <- State.get[SymTab] newSymTab = for { expr <- exprV s <- symtab.get(name) match { // if the name exists, creates a left with the error case Some(v) => AlreadyDeclared(name, v).left // if not exists, returns the symtab as a right case None => symtab.right } } yield { s + (name -> expr) // only executes if everything is right } res <- newSymTab match { // if the newSymTab is a left, I need to put the error in the State case -\/(l) => State.state[SymTab, V[Unit]](l.left) // if the newSymTab is a right, I need to use it as the new state, and put the () inside a \/ case \/-(r) => State.put(r).map(_.right) } } yield { res }}
This is just one parser
It gets ugly ...def pVar: P[Unit] = "var" ~ pName ~ "=" ~ pExpr ^^ { case _ ~ name ~ _ ~ exprS => for { exprV <- exprS symtab <- State.get[SymTab] newSymTab = for { expr <- exprV s <- symtab.get(name) match { // if the name exists, creates a left with the error case Some(v) => AlreadyDeclared(name, v).left // if not exists, returns the symtab as a right case None => symtab.right } } yield { s + (name -> expr) // only executes if everything is right } res <- newSymTab match { // if the newSymTab is a left, I need to put the error in the State case -\/(l) => State.state[SymTab, V[Unit]](l.left) // if the newSymTab is a right, I need to use it as the new state, and put the () inside a \/ case \/-(r) => State.put(r).map(_.right) } } yield { res }}
exprS now contains a \/[Err,Int]
It gets ugly ...def pVar: P[Unit] = "var" ~ pName ~ "=" ~ pExpr ^^ { case _ ~ name ~ _ ~ exprS => for { exprV <- exprS symtab <- State.get[SymTab] newSymTab = for { expr <- exprV s <- symtab.get(name) match { // if the name exists, creates a left with the error case Some(v) => AlreadyDeclared(name, v).left // if not exists, returns the symtab as a right case None => symtab.right } } yield { s + (name -> expr) // only executes if everything is right } res <- newSymTab match { // if the newSymTab is a left, I need to put the error in the State case -\/(l) => State.state[SymTab, V[Unit]](l.left) // if the newSymTab is a right, I need to use it as the new state, and put the () inside a \/ case \/-(r) => State.put(r).map(_.right) } } yield { res }}
I’m using an extra for-comprehension to deal with it
It gets ugly ...def pVar: P[Unit] = "var" ~ pName ~ "=" ~ pExpr ^^ { case _ ~ name ~ _ ~ exprS => for { exprV <- exprS symtab <- State.get[SymTab] newSymTab = for { expr <- exprV s <- symtab.get(name) match { // if the name exists, creates a left with the error case Some(v) => AlreadyDeclared(name, v).left // if not exists, returns the symtab as a right case None => symtab.right } } yield { s + (name -> expr) // only executes if everything is right } res <- newSymTab match { // if the newSymTab is a left, I need to put the error in the State case -\/(l) => State.state[SymTab, V[Unit]](l.left) // if the newSymTab is a right, I need to use it as the new state, and put the () inside a \/ case \/-(r) => State.put(r).map(_.right) } } yield { res }}
Some juggling to construct a left value if the symbol table already has the name
It gets ugly ...def pVar: P[Unit] = "var" ~ pName ~ "=" ~ pExpr ^^ { case _ ~ name ~ _ ~ exprS => for { exprV <- exprS symtab <- State.get[SymTab] newSymTab = for { expr <- exprV s <- symtab.get(name) match { // if the name exists, creates a left with the error case Some(v) => AlreadyDeclared(name, v).left // if not exists, returns the symtab as a right case None => symtab.right } } yield { s + (name -> expr) // only executes if everything is right } res <- newSymTab match { // if the newSymTab is a left, I need to put the error in the State case -\/(l) => State.state[SymTab, V[Unit]](l.left) // if the newSymTab is a right, I need to use it as the new state, and put the () inside a \/ case \/-(r) => State.put(r).map(_.right) } } yield { res }}
If either exprV or this contains a left, all the for-comprehension will be a left, ie, newSymTab will be a left
It gets ugly ...def pVar: P[Unit] = "var" ~ pName ~ "=" ~ pExpr ^^ { case _ ~ name ~ _ ~ exprS => for { exprV <- exprS symtab <- State.get[SymTab] newSymTab = for { expr <- exprV s <- symtab.get(name) match { // if the name exists, creates a left with the error case Some(v) => AlreadyDeclared(name, v).left // if not exists, returns the symtab as a right case None => symtab.right } } yield { s + (name -> expr) // only executes if everything is right } res <- newSymTab match { // if the newSymTab is a left, I need to put the error in the State case -\/(l) => State.state[SymTab, V[Unit]](l.left) // if the newSymTab is a right, I need to use it as the new state, and put the () inside a \/ case \/-(r) => State.put(r).map(_.right) } } yield { res }}
Here I’m “folding” over the\/[Err, SymTab]. If is a left, I need to put that inside the State. If is a right, I need to use the value as the new state
It gets ugly ...def pVar: P[Unit] = "var" ~ pName ~ "=" ~ pExpr ^^ { case _ ~ name ~ _ ~ exprS => for { exprV <- exprS symtab <- State.get[SymTab] newSymTab = for { expr <- exprV s <- symtab.get(name) match { // if the name exists, creates a left with the error case Some(v) => AlreadyDeclared(name, v).left // if not exists, returns the symtab as a right case None => symtab.right } } yield { s + (name -> expr) // only executes if everything is right } res <- newSymTab match { // if the newSymTab is a left, I need to put the error in the State case -\/(l) => State.state[SymTab, V[Unit]](l.left) // if the newSymTab is a right, I need to use it as the new state, and put the () inside a \/ case \/-(r) => State.put(r).map(_.right) } } yield { res }}
-\/ is the extractor of left values, \/- is the extractor of right values
It gets ugly ...def pVar: P[Unit] = "var" ~ pName ~ "=" ~ pExpr ^^ { case _ ~ name ~ _ ~ exprS => for { exprV <- exprS symtab <- State.get[SymTab] newSymTab = for { expr <- exprV s <- symtab.get(name) match { // if the name exists, creates a left with the error case Some(v) => AlreadyDeclared(name, v).left // if not exists, returns the symtab as a right case None => symtab.right } } yield { s + (name -> expr) // only executes if everything is right } res <- newSymTab match { // if the newSymTab is a left, I need to put the error in the State case -\/(l) => State.state[SymTab, V[Unit]](l.left) // if the newSymTab is a right, I need to use it as the new state, and put the () inside a \/ case \/-(r) => State.put(r).map(_.right) } } yield { res }}
State.state allows to put a value inside a State[S,A]. For a given `a’, represents a transition { s => (s, a) }
It gets ugly ...def pVar: P[Unit] = "var" ~ pName ~ "=" ~ pExpr ^^ { case _ ~ name ~ _ ~ exprS => for { exprV <- exprS symtab <- State.get[SymTab] newSymTab = for { expr <- exprV s <- symtab.get(name) match { // if the name exists, creates a left with the error case Some(v) => AlreadyDeclared(name, v).left // if not exists, returns the symtab as a right case None => symtab.right } } yield { s + (name -> expr) // only executes if everything is right } res <- newSymTab match { // if the newSymTab is a left, I need to put the error in the State case -\/(l) => State.state[SymTab, V[Unit]](l.left) // if the newSymTab is a right, I need to use it as the new state, and put the () inside a \/ case \/-(r) => State.put(r).map(_.right) } } yield { res }}
...
Good news
Good news
Monad transformers
Monad transformers
The only thing I know about them, is that they let you combine the effects of two monads, ...
Monad transformers
The only thing I know about them, is that they let you combine the effects of two monads, ...
… scalaz has a monad transformer, StateT, that let you combine the State monad with any other monad, …
Monad transformers
The only thing I know about them, is that they let you combine the effects of two monads, ...
… scalaz has a monad transformer, StateT, that let you combine the State monad with any other monad, …
… and this solves all our problems
Combining State and \/
This are our final type aliases
type SymTab = Map[String, Int]
type V[A] = Err \/ A
type S[A] = StateT[V, SymTab, A]
type P[A] = Parser[S[A]]
Combining State and \/
This are our final type aliases
type SymTab = Map[String, Int]
type V[A] = Err \/ A
type S[A] = StateT[V, SymTab, A]
type P[A] = Parser[S[A]]
This is the only new one
Combining State and \/
This are our final type aliases
type SymTab = Map[String, Int]
type V[A] = Err \/ A
type S[A] = StateT[V, SymTab, A]
type P[A] = Parser[S[A]]
StateT is parameterized on the monad with which is combined
Combining State and \/
This are our final type aliases
type SymTab = Map[String, Int]
type V[A] = Err \/ A
type S[A] = StateT[V, SymTab, A]
type P[A] = Parser[S[A]]
Here is using the monad of\/[A,B]
Combining State and \/
And one final ingredientval State = StateT.stateTMonadState[SymTab, V]
Combining State and \/
And one final ingredientval State = StateT.stateTMonadState[SymTab, V]
The companion object of StateT does not have the functions get, put, state, etc.
Combining State and \/
And one final ingredientval State = StateT.stateTMonadState[SymTab, V]
Instead we have to instantiate one of this “beasts”
Combining State and \/With that changes, our parser works as before without any modification
Combining State and \/import Parser4._
parse(pExpr, "1").map(_.run(Map.empty))//> [1.2] parsed: \/-((Map(),1))
parse(pVar, "var x = 1").map(_.run(Map.empty))//> [1.10] parsed: \/-((Map(x -> 1),()))
parse(pExpr, "y * 3").map(_.run(Map("y" -> 2)))//> [1.6] parsed: \/-((Map(y -> 2),6))
Combining State and \/parse(pL4, """var x = 1; |var y = x * 3; |x + y; |(x + 1) * 3 + 1; |var z = 8;""".stripMargin).map(_.run(Map.empty))//> [5.11] parsed: \/-((Map(x -> 1, y -> 3, z -> 8),List(4, 7)))
Combining State and \/parse(pL4, """var x = 1; |var y = x * 3; |x + y; |(x + 1) * 3 + 1; |var z = 8;""".stripMargin).map(_.run(Map.empty))//> [5.11] parsed: \/-((Map(x -> 1, y -> 3, z -> 8),List(4, 7)))
The only difference is that now the results are inside a \/, in particular a \/-, ie, a right value, which indicates that everything is OK.
Handling errors
Everything else is working the same, even our exceptions when an undeclared variable is referenced
Handling errors
Everything else is working the same, even our exceptions when an undeclared variable is referenced
We must change pRef y pVar to handle the error cases
Handling errorsdef pRef: P[Int] = pName ^^ { name => for { symtab <- State.get } yield { symtab(name) } }
def pRef: P[Int] = pName ^^ { name => for { symtab <- State.get vErrInt = symtab.get(name) .toRightDisjunction( NotDeclared(name)) res <- StateT[V, SymTab, Int] { s => vErrInt.map(s -> _) } } yield { res } }
Handling errorsdef pRef: P[Int] = pName ^^ { name => for { symtab <- State.get } yield { symtab(name) } }
def pRef: P[Int] = pName ^^ { name => for { symtab <- State.get vErrInt = symtab.get(name) .toRightDisjunction( NotDeclared(name)) res <- StateT[V, SymTab, Int] { s => vErrInt.map(s -> _) } } yield { res } }
We are converting an Option[Int] into \/[Err,Int]
Handling errorsdef pRef: P[Int] = pName ^^ { name => for { symtab <- State.get } yield { symtab(name) } }
def pRef: P[Int] = pName ^^ { name => for { symtab <- State.get vErrInt = symtab.get(name) .toRightDisjunction( NotDeclared(name)) res <- StateT[V, SymTab, Int] { s => vErrInt.map(s -> _) } } yield { res } }
We construct our StateT “by hand” by passing the function directly
Handling errorsdef pRef: P[Int] = pName ^^ { name => for { symtab <- State.get } yield { symtab(name) } }
def pRef: P[Int] = pName ^^ { name => for { symtab <- State.get vErrInt = symtab.get(name) .toRightDisjunction( NotDeclared(name)) res <- StateT[V, SymTab, Int] { s => vErrInt.map(s -> _) } } yield { res } }
This StateT wraps functions of type SymTab => V[(SymTab,A)] == SymTab => \/[Err, (SymTab,A)]
Handling errorsdef pRef: P[Int] = pName ^^ { name => for { symtab <- State.get } yield { symtab(name) } }
def pRef: P[Int] = pName ^^ { name => for { symtab <- State.get vErrInt = symtab.get(name) .toRightDisjunction( NotDeclared(name)) res <- StateT[V, SymTab, Int] { s => vErrInt.map(s -> _) } } yield { res } }
vErrInt is of type\/[Err, Int], we convert it to\/[Err, (SymTab, Int)] with map
Handling errorsdef pRef: P[Int] = pName ^^ { name => for { symtab <- State.get } yield { symtab(name) } }
def pRef: P[Int] = pName ^^ { name => for { symtab <- State.get vErrInt = symtab.get(name) .toRightDisjunction( NotDeclared(name)) res <- StateT[V, SymTab, Int] { s => vErrInt.map(s -> _) } } yield { res } }
This state transition will only be made if vErrInt is a right value. If not, all the computation will return the left value
Handling errorsdef pRef: P[Int] = pName ^^ { name => for { symtab <- State.get } yield { symtab(name) } }
def pRef: P[Int] = pName ^^ { name => for { symtab <- State.get vErrInt = symtab.get(name) .toRightDisjunction( NotDeclared(name)) res <- StateT[V, SymTab, Int] { s => vErrInt.map(s -> _) } } yield { res } }
Here is happening the combination of both monad effects
Handling errorsdef pVar: P[Unit] = "var" ~ pName ~ "=" ~ pExpr ^^ { case _ ~ name ~ _ ~ exprS => for { expr <- exprS symtab <- State.get unit <- symtab.get(name) match { case Some(v) => StateT[V, SymTab, Unit] { s => AlreadyDeclared(name, v).left } case None => State.put(symtab + (name->expr)) } } yield { unit }}
def pVar: P[Unit] = "var"~pName~"="~pExpr ^^ { case _ ~ name ~ _ ~ exprS => for { expr <- exprS symtab <- State.get unit <-State.put( symtab+(name->expr) ) } yield { unit } }
Handling errorsdef pVar: P[Unit] = "var" ~ pName ~ "=" ~ pExpr ^^ { case _ ~ name ~ _ ~ exprS => for { expr <- exprS symtab <- State.get unit <- symtab.get(name) match { case Some(v) => StateT[V, SymTab, Unit] { s => AlreadyDeclared(name, v).left } case None => State.put(symtab + (name->expr)) } } yield { unit }}
def pVar: P[Unit] = "var"~pName~"="~pExpr ^^ { case _ ~ name ~ _ ~ exprS => for { expr <- exprS symtab <- State.get unit <-State.put( symtab+(name->expr) ) } yield { unit } } We check if the declaration
already exists
Handling errorsdef pVar: P[Unit] = "var" ~ pName ~ "=" ~ pExpr ^^ { case _ ~ name ~ _ ~ exprS => for { expr <- exprS symtab <- State.get unit <- symtab.get(name) match { case Some(v) => StateT[V, SymTab, Unit] { s => AlreadyDeclared(name, v).left } case None => State.put(symtab + (name->expr)) } } yield { unit }}
def pVar: P[Unit] = "var"~pName~"="~pExpr ^^ { case _ ~ name ~ _ ~ exprS => for { expr <- exprS symtab <- State.get unit <-State.put( symtab+(name->expr) ) } yield { unit } } If exists, we construct a StateT with
a function returning a left. This stops any further state transitions
Handling errorsdef pVar: P[Unit] = "var" ~ pName ~ "=" ~ pExpr ^^ { case _ ~ name ~ _ ~ exprS => for { expr <- exprS symtab <- State.get unit <- symtab.get(name) match { case Some(v) => StateT[V, SymTab, Unit] { s => AlreadyDeclared(name, v).left } case None => State.put(symtab + (name->expr)) } } yield { unit }}
def pVar: P[Unit] = "var"~pName~"="~pExpr ^^ { case _ ~ name ~ _ ~ exprS => for { expr <- exprS symtab <- State.get unit <-State.put( symtab+(name->expr) ) } yield { unit } } If the value not exists, we proceed as
before, updating the state
Handling errorsdef pVar: P[Unit] = "var" ~ pName ~ "=" ~ pExpr ^^ { case _ ~ name ~ _ ~ exprS => for { expr <- exprS symtab <- State.get unit <- symtab.get(name) match { case Some(v) => StateT[V, SymTab, Unit] { s => AlreadyDeclared(name, v).left } case None => State.put(symtab + (name->expr)) } } yield { unit }}
def pVar: P[Unit] = "var"~pName~"="~pExpr ^^ { case _ ~ name ~ _ ~ exprS => for { expr <- exprS symtab <- State.get unit <-State.put( symtab+(name->expr) ) } yield { unit } } In both cases we are returning
StateT[V, SymTab, Unit]
Now errors are handled properlyimport Parser4._
parse(pL4, "var x = y;").map(_.run(Map.empty))//> [1.11] parsed: -\/(`y' not declared.)
parse(pL4, "var x = 1; x * x; var x = 2;").map(_.run(Map.empty))//> [1.29] parsed: -\/(`x' already declared with value `1'.)
Now errors are handled properlyimport Parser4._
parse(pL4, "var x = y;").map(_.run(Map.empty))//> [1.11] parsed: -\/(`y' not declared.)
parse(pL4, "var x = 1; x * x; var x = 2;").map(_.run(Map.empty))//> [1.29] parsed: -\/(`x' already declared with value `1'.)
Both return left values
github.com/gerferra/parser-stateT
FIN