Last active
April 29, 2019 17:15
-
-
Save lambdaofgod/0a3d516afa16e5ae6be01cdbc5069018 to your computer and use it in GitHub Desktop.
While language interpreter
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
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") | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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<~"}" | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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