Skip to content

Instantly share code, notes, and snippets.

@lambdaofgod
Last active April 29, 2019 17:15
Show Gist options
  • Save lambdaofgod/0a3d516afa16e5ae6be01cdbc5069018 to your computer and use it in GitHub Desktop.
Save lambdaofgod/0a3d516afa16e5ae6be01cdbc5069018 to your computer and use it in GitHub Desktop.
While language interpreter
/*
Kuba 'lambdaofgod Bartczuk
basically completed 10.01.2015, refactored 08.2016
while language interpreter
syntax:
V - variables
N - numbers
Prim = N | V
Expr = Arithmetic | Boolean
Boolean = true | false | Boolean or Boolean | Boolean and Boolean| Arithmetic < Arithmetic
Arithmetic = Prim | V + V | V * V
Statement = V := Expr
| Statement ; Statement
| if Statement then {Statement} else {Statement}
| while Boolean do Statement
(didn't include parens, but who cares about parens)
*/
object Interpreter {
type Environment = scala.collection.mutable.Map[Variable, Number]
// now we need to implement bindings
// interpreter
// start small for ex. with assignment and Aexprs
def eval(exp : WhileExpr, env : Environment) : WhileExpr = exp match {
case exp : Expr =>
exp.evalArithmExp(env)
case exp : Bool => exp.evalBoolExp(env)
case Assgnmt(vrbl, assigned) => {
env(vrbl) = assigned.evalArithmExp(env)
exp
}
case Then(st1,st2) => {
eval(st1,env)
eval(st2,env)
}
case IfThenElse(cond, st1, st2) => {
if ( cond.evalBoolExp(env).bool )
eval(st1,env)
else
eval(st2,env)
}
case WhileDo(cond,st) => {
while ( cond.evalBoolExp(env).bool )
eval(st,env)
TruthValue(true)
}
}
// parser itself
import scala.util.parsing.combinator._
def main(args: Array[String]) {
println("Welcome to while language interpreter")
println("Please input your formulae, or write \"exit\" to quit")
var env : Environment = scala.collection.mutable.Map()
print(">")
var input = readLine()
var parsedValue : WhileExpr = TruthValue(true)// = WhileParser.parseAll(WhileParser.whileExp, input).get
var interpretation : WhileExpr = TruthValue(true) // = eval(parsedValue,env)
// the repl!
while ( input != "exit" )
{
parsedValue = Parser.parseAll(Parser.whileExp, input).get
// we need that damn patternmatching to make sure that undefined variables don't crash repl
parsedValue match {
case Variable(name) => try {
eval(parsedValue,env)
println( eval(parsedValue,env))
}
catch {
case e : NoValueException =>
println("undefined variable " + name)
}
finally {
}
case _ => {
interpretation = eval(parsedValue,env)
interpretation match {
case Assgnmt(vrbl, num) => println("defined variable " + vrbl.toString)
case interpretation : Stmt => print("")
case _ => println(interpretation)}
}
}
print(">")
input = readLine()
}
println("Goodbye, have a nice day")
}
}
import scala.util.parsing.combinator.JavaTokenParsers
object Parser extends JavaTokenParsers {
def whileExp : Parser[WhileExpr] = stmt | bool | arithmExp
def exp : Parser[Expr] = arithmExp
def variable : Parser[Variable] = ident ^^ { Variable(_) }
def num : Parser[Number] = floatingPointNumber ^^ { x => Number(x.toInt) }
def arithmExp : Parser[Expr] = term ~ rep(("+" | "-") ~ term) ^^ { case t1 ~ lst => lst.foldLeft(t1)( (t2,t3) => if (t3._1 == "+") Add(t2, t3._2) else Sub(t2,t3._2)) }
def term : Parser[Expr] = factor ~ rep( ("*" | "/") ~ factor) ^^ { case t1 ~ lst => lst.foldLeft(t1)( (t2,t3) => if (t3._1 == "*") Mult(t2, t3._2) else Div(t2,t3._2)) }
def factor : Parser[Expr] = variable |
num |
"("~> arithmExp <~")"
def bool : Parser[Bool] = and ~ rep("or" ~ and) ^^ { case t1 ~ lst => lst.foldLeft(t1)( (t2,t3) => Or(t2, t3._2)) }
def and : Parser[Bool] = prim ~ rep("and" ~ prim) ^^ { case t1 ~ lst => lst.foldLeft(t1)( (t2,t3) => And(t2, t3._2)) }
def prim : Parser[Bool] = tvalue ^^ { x => TruthValue(x) } |
rel |
"(" ~> bool <~ ")"
def tvalue : Parser[Boolean] = ("true" | "false") ^^ { x => x == "true" }
def rel : Parser[Comp] = arithmExp ~ relOp ~ arithmExp ^^ { case e1 ~ op ~ e2 => Comp(e1,e2,op) }
def relOp : Parser[String] = ("<" | ">") ^^ { x => x }
def stmt : Parser[Stmt] = compStmt ~ ";"~ compStmt ^^ { case s1 ~ op ~ s2 => Then(s1,s2) } | compStmt
def compStmt : Parser[Stmt] = ident ~ ":=" ~ arithmExp ^^ { case v1 ~ _ ~ exp1 => Assgnmt( Variable(v1),exp1) } |
"if" ~ bool ~ "then" ~ basicStmt ~ "else" ~ basicStmt ^^ { case _ ~ cond ~ _ ~ s1 ~ _ ~ s2 => IfThenElse(cond,s1,s2)} |
"while"~bool~"do"~basicStmt ^^ { case _ ~ cond ~ _ ~ s1 => WhileDo(cond, s1) }
def basicStmt : Parser[Stmt] = "{"~>stmt<~"}"
}
trait WhileExpr
case class NoValueException(msg: String) extends Exception(msg)
trait ArithmeticExpr extends WhileExpr {
def eval(env: Environment): Number
}
object ArithmeticExpr {
// this kinda violates DRY but I have no idea how to get it clean
def toString(ex: ArithmeticExpr): String = {
def parenthesize(ex: ArithmeticExpr) = ex match {
case ex: Binary=>
"(" + toString(ex) + ")"
case _ => ex.toString
}
ex match {
case ex: Binary=> {
ex match {
case Add(left,right) => parenthesize(left) + " + " + parenthesize(right)
case Mult(left,right) => parenthesize(left) + " * " + parenthesize(right)
case Sub(left,right) => parenthesize(left) + " - " + parenthesize(right)
case Div(left,right) => parenthesize(left) + " / " + parenthesize(right)
}
}
case _ => ex.toString
}
}
}
case class Variable(name: String) extends ArithmeticExpr {
override def toString() = name
def eval(env: Environment): Number = env.get(this) match {
case Some(n) => n
case None => throw NoValueException("variable " + name + " not found")
}
}
case class Number(num: Int) extends ArithmeticExpr {
override def toString() = num.toString
def eval(env: Environment): Number = this
}
abstract class Binary(left: ArithmeticExpr, right: ArithmeticExpr) extends ArithmeticExpr
case class Add(left: ArithmeticExpr, right: ArithmeticExpr) extends Binary(left,right) {
override def toString = ArithmeticExpr.toString(this)
def eval(env: Environment): Number = {
Number(left.eval(env).num + right.eval(env).num)
}
}
case class Mult(left: ArithmeticExpr, right: ArithmeticExpr) extends Binary(left,right) {
override def toString = ArithmeticExpr.toString(this)
def eval(env: Environment): Number = {
Number(left.eval(env).num * right.eval(env).num)
}
}
case class Sub(left: ArithmeticExpr, right: ArithmeticExpr) extends Binary(left,right) {
override def toString = ArithmeticExpr.toString(this)
def eval(env: Environment): Number = {
Number(left.eval(env).num - right.eval(env).num)
}
}
case class Div(left: ArithmeticExpr, right: ArithmeticExpr) extends Binary(left,right) {
override def toString = ArithmeticExpr.toString(this)
def eval(env: Environment): Number = {
Number(left.eval(env).num / right.eval(env).num)
}
}
// logic 'n such
trait Bool extends WhileExpr {
def eval(env: Environment): TruthValue
}
object Bool {
def toString(ex: Bool): String = {
def parenthesize(ex: Bool) = ex match {
case ex: BoolOp =>
"(" + toString(ex) + ")"
case Comp(left,right,op) =>
"(" + ArithmeticExpr.toString(left) + op + ArithmeticExpr.toString(right) + ")"
case _ => ex.toString
}
ex match {
case ex: BoolOp => {
ex match {
case And(left,right) => parenthesize(left) + " and " + parenthesize(right)
case Or(left,right) => parenthesize(left) + " or " + parenthesize(right)
}
}
case ex: Comp =>
parenthesize(ex)
case ex: TruthValue =>
ex.toString
}
}
}
case class TruthValue(bool: Boolean) extends Bool {
override def toString = bool.toString
def eval(env: Environment): TruthValue = this
}
abstract class BoolOp(left: Bool, right: Bool) extends Bool
case class And(left: Bool, right: Bool) extends BoolOp (left,right){
override def toString = Bool.toString(this)
def eval(env: Environment): TruthValue = {
TruthValue( left.eval(env).bool && right.eval(env).bool )
}
}
case class Or(left: Bool, right: Bool) extends BoolOp (left,right){
override def toString = Bool.toString(this)
def eval(env: Environment): TruthValue = {
TruthValue( left.eval(env).bool || right.eval(env).bool )
}
}
case class Comp(left: ArithmeticExpr, right: ArithmeticExpr, op: String) extends Bool {
override def toString = Bool.toString(this)
def eval(env: Environment): TruthValue = {
def toFun(s: String): (Int,Int) => Boolean = {
if (s == "<")
(x,y) => (x<y)
else
(x,y) => (x>y)
}
TruthValue( toFun(op)(left.eval(env).num, right.eval(env).num))
}
}
// control
trait Stmt extends WhileExpr
case class Assgnmt(vrbl: Variable, ex: ArithmeticExpr) extends Stmt {
override def toString = vrbl + ":= " + ex.toString
}
case class Then(st1: Stmt, st2: Stmt) extends Stmt {
override def toString = st1.toString + " ; " + st2.toString
}
case class IfThenElse(cond: Bool, st1: Stmt, st2: Stmt) extends Stmt {
override def toString = "if (" + cond.toString + " then " + st1.toString + " else " + st2.toString
}
case class WhileDo(cond: Bool, st: Stmt) extends Stmt {
override def toString = "while (" + cond.toString + ") do " + st.toString
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment