Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- object Lab4 extends jsy.util.JsyApplication {
- import jsy.lab4.ast._
- import jsy.lab4.Parser
- /*
- * CSCI 3155: Lab 4
- * <Matt Moskowitz>
- *
- * Partner: <David Bittle>
- * Collaborators: <Any Collaborators>
- */
- /*
- * Fill in the appropriate portions above by replacing things delimited
- * by '<'... '>'.
- *
- * Replace 'YourIdentiKey' in the object name above with your IdentiKey.
- *
- * Replace the 'throw new UnsupportedOperationException' expression with
- * your code in each function.
- *
- * Do not make other modifications to this template, such as
- * - adding "extends App" or "extends Application" to your Lab object,
- * - adding a "main" method, and
- * - leaving any failing asserts.
- *
- * Your lab will not be graded if it does not compile.
- *
- * This template compiles without error. Before you submit comment out any
- * code that does not compile or causes a failing assert. Simply put in a
- * 'throws new UnsupportedOperationException' as needed to get something
- * that compiles without error.
- */
- /* Collections and Higher-Order Functions */
- /* Lists */
- //cd Desktop/eclipse/workspace/lab4
- //sbt "project lab4-grader" run
- def compressRec[A](l: List[A]): List[A] = l match {
- case Nil | _ :: Nil => l
- case h1 :: (t1 @ (h2 :: _)) => if (h1 == h2) compressRec(t1) else h1:: compressRec(t1)
- }
- def compressFold[A](l: List[A]): List[A] = l.foldRight(Nil: List[A]){
- (h, acc) => acc match { //acc begins nil, h starts from value at end of list
- case Nil => h :: acc
- case h1 :: t => h1 match {
- case `h` => acc
- case _ => h :: acc
- }
- }
- }
- def mapFirst[A](f: A => Option[A])(l: List[A]): List[A] = l match {
- case Nil => l
- case h :: t => f(h) match {
- case Some(x) => x :: t
- case _ => h :: mapFirst(f)(t)
- }
- }
- /* Search Trees */
- sealed abstract class Tree {
- def insert(n: Int): Tree = this match {
- case Empty => Node(Empty, n, Empty)
- case Node(l, d, r) => if (n < d) Node(l insert n, d, r) else Node(l, d, r insert n)
- }
- def foldLeft[A](z: A)(f: (A, Int) => A): A = {
- def loop(acc: A, t: Tree): A = t match {
- case Empty => acc
- //case Node(Empty, d, Empty) => f(acc, d)
- //case Node(Empty, d, r) => loop(f(acc, d), r)
- // case Node(l, d , Empty) => loop(f(acc, d), l)
- case Node(l, d, r) => {
- val v1 = loop(acc, l)
- val v2 = f(v1, d)
- loop(v2, r)
- }
- }
- loop(z, this)
- }
- def pretty: String = {
- def p(acc: String, t: Tree, indent: Int): String = t match {
- case Empty => acc
- case Node(l, d, r) =>
- val spacer = " " * indent
- p("%s%d%n".format(spacer, d) + p(acc, l, indent + 2), r, indent + 2)
- }
- p("", this, 0)
- }
- }
- case object Empty extends Tree
- case class Node(l: Tree, d: Int, r: Tree) extends Tree
- def treeFromList(l: List[Int]): Tree =
- l.foldLeft(Empty: Tree){ (acc, i) => acc insert i }
- def sum(t: Tree): Int = t.foldLeft(0){ (acc, d) => acc + d }
- def strictlyOrdered(t: Tree): Boolean = {
- val (b, _) = t.foldLeft((true, None: Option[Int])){
- (acc, h) => acc match {
- case (b1, None) => (true, Some(h))
- case (b1, a) => (((a.get < h) && b1), Some(h))
- }
- }
- b
- }
- /* Type Inference */
- // A helper function to check whether a jsy type has a function type in it.
- // While this is completely given, this function is worth studying to see
- // how library functions are used.
- def hasFunctionTyp(t: Typ): Boolean = t match {
- case TFunction(_, _) => true
- case TObj(fields) if (fields exists { case (_, t) => hasFunctionTyp(t) }) => true
- case _ => false
- }
- def typeInfer(env: Map[String,Typ], e: Expr): Typ = {
- // Some shortcuts for convenience
- def typ(e1: Expr) = typeInfer(env, e1)
- def err[T](tgot: Typ, e1: Expr): T = throw StaticTypeError(tgot, e1, e)
- e match {
- case Print(e1) => typ(e1); TUndefined
- case N(_) => TNumber
- case B(_) => TBool
- case Undefined => TUndefined
- case S(_) => TString
- case Var(x) => env(x)
- case ConstDecl(x, e1, e2) => typeInfer(env + (x -> typ(e1)), e2)
- case Unary(Neg, e1) => typ(e1) match {
- case TNumber => TNumber
- case tgot => err(tgot, e1)
- }
- case Unary(Not, e1) => typ(e1) match {
- case TBool => TBool
- case tgot => err(tgot,e1)
- }
- // throw new UnsupportedOperationException
- case Binary(Plus, e1, e2) => (typ(e1),typ(e2)) match{
- case (TString,TString)=> TString
- case (TNumber,TNumber)=>TNumber
- case _ => err(typ(e1),e1)
- }
- //throw new UnsupportedOperationException
- case Binary(Minus|Times|Div, e1, e2) =>(typ(e1),typ(e2)) match{
- case (TNumber,TNumber)=>TNumber
- case (_) => err(typ(e1),e1)
- }
- case Binary(Eq|Ne, e1, e2) =>(typ(e1),typ(e2)) match{
- case(e1type,_) if hasFunctionTyp(e1type)=> err(e1type, e1)
- case(_,e2type) if hasFunctionTyp(e2type)=> err(e2type, e2)
- case(e1type,e2type) if(e1type==e2type)=>TBool
- case _ => err(typ(e1),e1)
- //case(TBool,TBool)=> TBool
- // case(TBool,_)=> err(typ(e2),e2)
- //
- }
- case Binary(Lt|Le|Gt|Ge, e1, e2) =>(typ(e1),typ(e2)) match{
- case(TNumber,TNumber) => TBool
- case(TBool,TBool)=> TBool
- case(TString,TString)=> TBool
- case _=>err(typ(e1),e1)
- }
- case Binary(And|Or, e1, e2) =>(typ(e1),typ(e2)) match{
- case(TBool,TBool)=> TBool
- //case(TBool,_)=> err(typ(e2),e2)
- case _=>err(typ(e1),e1)
- }
- case Binary(Seq, e1, e2) =>
- typ(e2);
- typ(e1)
- throw new UnsupportedOperationException
- case If(e1, e2, e3) =>(typ(e1), typ(e2), typ(e3)) match {
- //e1 must be a bool. e2 can be any type, e3 can be any type
- case (TBool, e2type, e3type) => {
- if (e2type == e3type) e2type
- else err(e3type, e3)
- }
- case(e1NotBool, _, _) => err(e1NotBool, e1)
- }
- case Function(p, params, tann, e1) => {
- // Bind to env1 an environment that extends env with an appropriate binding if
- // the function is potentially recursive.
- val env1 = (p, tann) match {
- case (Some(f), Some(tret)) =>
- val tprime = TFunction(params, tret)
- env + (f -> tprime)
- case (None, _) => env
- case _ => err(TUndefined, e1)
- }
- // Bind to env2 an environment that extends env1 with bindings for params.
- //base case will be the env1 that was defined above (code given to us)
- //subsequently we will be calling fold left on every entry in params
- //every time we will extend the environemnt with + along with names associated with values added
- //the result will be a new environement, e2 with all the parameters
- val env2 = params.foldLeft(env1) {
- case (acc, (pName, pValue)) => acc + (pName -> pValue)
- }
- // throw new UnsupportedOperationException
- // Match on whether the return type is specified.
- tann match {
- case None =>
- //val tBody will find the type of e1 given the environment e2 wi
- val tBody = typeInfer(env2, e1)
- //tfunction will return a type function with the params originally passed in and the type of the body.
- TFunction(params, tBody)
- case Some(tret) => {
- val tBody = typeInfer(env2, e1)
- val tBodyPrime = TFunction(params, tBody)
- //in the case of recursive functions the type returned must be of the same type as
- //the type that was passed in.
- if (tBodyPrime != TFunction(params, tret))
- err(tBody, e1)
- else TFunction(params, tret)
- }
- }
- }
- case Call(e1, args) => typ(e1) match {
- case TFunction(params, tret) if (params.length == args.length) => {
- (params, args).zipped.foreach {
- case(parArgs@(st,t),e)=> if(t!=typ(e)) err(typ(e),e);
- tret
- //throw new UnsupportedOperationException
- }
- tret
- }
- case tgot => err(tgot, e1)
- }
- case Obj(fields) =>TObj(fields.mapValues((exp: Expr) => typ(exp)))
- case GetField(e1, f) => {
- typ(e1) match {
- case TObj(field) => field.get(f) match {
- case Some(f) => f
- case None => err(typ(e1), e1)
- }
- case _ => err(typ(e1), e1)
- }
- }
- }
- }
- /* Small-Step Interpreter */
- def inequalityVal(bop: Bop, v1: Expr, v2: Expr): Boolean = {
- require(bop == Lt || bop == Le || bop == Gt || bop == Ge)
- ((v1, v2): @unchecked) match {
- case (S(s1), S(s2)) =>
- (bop: @unchecked) match {
- case Lt => s1 < s2
- case Le => s1 <= s2
- case Gt => s1 > s2
- case Ge => s1 >= s2
- }
- case (N(n1), N(n2)) =>
- (bop: @unchecked) match {
- case Lt => n1 < n2
- case Le => n1 <= n2
- case Gt => n1 > n2
- case Ge => n1 >= n2
- }
- }
- }
- def substitute(e: Expr, v: Expr, x: String): Expr = {
- require(isValue(v))
- def subst(e: Expr): Expr = substitute(e, v, x)
- e match {
- case N(_) | B(_) | Undefined | S(_) => e
- case Print(e1) => Print(subst(e1))
- case Unary(uop, e1) => Unary(uop, subst(e1))
- case Binary(bop, e1, e2) => Binary(bop, subst(e1), subst(e2))
- case If(e1, e2, e3) => If(subst(e1), subst(e2), subst(e3))
- case Var(y) => if (x == y) v else e
- case ConstDecl(y, e1, e2) => ConstDecl(y, subst(e1), if (x == y) e2 else subst(e2))
- case Function(p, params, tann, e1) =>
- p match{
- case(None)=>
- Function(p, params, tann, if (params.forall{case(n,_) => (x != n)}) subst(e1) else e1)
- case Some(a) => Function(p, params, tann, if (params.forall{case(n,_) => (x != n)}&& a!=x) subst(e1) else e1)
- }
- case Obj(field) => Obj(field.mapValues((exp: Expr) => subst(exp)))
- //case f1 @ Function(p, params, tann, e1) => {
- // if (params.exists((t1: (String, Typ)) => t1._1 == x) || Some(x) == p) {
- // f1
- // } else {
- // Function(p, params, tann, subst(e1))
- // }
- //}
- case Call(e1, args) => Call(subst(e1), args map subst)
- // case Call(e1, args) =>
- // throw new UnsupportedOperationException
- //case Obj(fields) =>
- // throw new UnsupportedOperationException
- case GetField(e1, f) => GetField(subst(e1), f)
- }
- }
- def step(e: Expr): Expr = {
- require(!isValue(e))
- def stepIfNotValue(e: Expr): Option[Expr] = if (isValue(e)) None else Some(step(e))
- e match {
- /* Base Cases: Do Rules */
- case Print(v1) if isValue(v1) => println(pretty(v1)); Undefined
- case Unary(Neg, N(n1)) => N(- n1)
- case Unary(Not, B(b1)) => B(! b1)
- case Binary(Seq, v1, e2) if isValue(v1) => e2
- case Binary(Plus, S(s1), S(s2)) => S(s1 + s2)
- case Binary(Plus, N(n1), N(n2)) => N(n1 + n2)
- case Binary(Minus, N(n1), N(n2)) => N(n1 - n2)
- case Binary(Times, N(n1), N(n2)) => N(n1 * n2)
- case Binary(Div, N(n1), N(n2)) => N(n1 / n2)
- case Binary(bop @ (Lt|Le|Gt|Ge), v1, v2) if isValue(v1) && isValue(v2) => B(inequalityVal(bop, v1, v2))
- case Binary(Eq, v1, v2) if isValue(v1) && isValue(v2) => B(v1 == v2)
- case Binary(Ne, v1, v2) if isValue(v1) && isValue(v2) => B(v1 != v2)
- case Binary(And, B(b1), e2) => if (b1) e2 else B(false)
- case Binary(Or, B(b1), e2) => if (b1) B(true) else e2
- case ConstDecl(x, v1, e2) if isValue(v1) => substitute(e2, v1, x)
- case If(B(b1), e2, e3) => b1 match{
- case (true) => e2
- case (false) => e3
- }
- case GetField(Obj(fields), f) => fields.get(f) match {
- case Some(e1) => e1
- case None => throw new StuckError(e)
- }
- case GetField(e1, f) => GetField(step(e1), f)
- case Call(v1, args) if isValue(v1) && (args forall isValue) =>
- v1 match {
- case Function(p, params, _, e1) => {
- //e1p in this case
- val e1p = (params, args).zipped.foldRight(e1){
- //
- (myParams,acc)=> myParams match{
- case((name,_),value) => substitute(acc,value,name)
- }
- }
- p match {
- case None => e1p
- case Some(x1) => substitute(e1p,v1,x1)
- }
- }
- case _ => throw new StuckError(e)
- }
- /*** Fill-in more cases here. ***/
- /* Inductive Cases: Search Rules */
- case Call(v1,args) if isValue(v1)=> Call(v1, mapFirst(stepIfNotValue)(args))
- case Call(e1,e2)=> Call(step(e1),e2)
- case Print(e1) => Print(step(e1))
- case Unary(uop, e1) => Unary(uop, step(e1))
- case Binary(bop, v1, e2) if isValue(v1) => Binary(bop, v1, step(e2))
- case Binary(bop, e1, e2) => Binary(bop, step(e1), e2)
- case If(e1, e2, e3) => If(step(e1), e2, e3)
- case ConstDecl(x, e1, e2) => ConstDecl(x, step(e1), e2)
- /*** Fill-in more cases here. ***/
- /* Everything else is a stuck error. Should not happen if e is well-typed. */
- case _ => throw StuckError(e)
- }
- }
- /* External Interfaces */
- this.debug = true // comment this out or set to false if you don't want print debugging information
- def inferType(e: Expr): Typ = {
- if (debug) {
- println("------------------------------------------------------------")
- println("Type checking: %s ...".format(e))
- }
- val t = typeInfer(Map.empty, e)
- if (debug) {
- println("Type: " + pretty(t))
- }
- t
- }
- // Interface to run your small-step interpreter and print out the steps of evaluation if debugging.
- def iterateStep(e: Expr): Expr = {
- require(closed(e))
- def loop(e: Expr, n: Int): Expr = {
- if (debug) { println("Step %s: %s".format(n, e)) }
- if (isValue(e)) e else loop(step(e), n + 1)
- }
- if (debug) {
- println("------------------------------------------------------------")
- println("Evaluating with step ...")
- }
- val v = loop(e, 0)
- if (debug) {
- println("Value: " + v)
- }
- v
- }
- // Convenience to pass in a jsy expression as a string.
- def iterateStep(s: String): Expr = iterateStep(Parser.parse(s))
- // Interface for main
- def processFile(file: java.io.File) {
- if (debug) {
- println("============================================================")
- println("File: " + file.getName)
- println("Parsing ...")
- }
- val expr =
- handle(None: Option[Expr]) {Some{
- Parser.parseFile(file)
- }} getOrElse {
- return
- }
- handle() {
- val t = inferType(expr)
- }
- handle() {
- val v1 = iterateStep(expr)
- println(pretty(v1))
- }
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement