Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • shchen/cs320
  • raveendr/cs320
  • mwojnaro/cs320
3 results
Show changes
Showing
with 423 additions and 0 deletions
......@@ -2,16 +2,38 @@ package amyc.ast
import amyc.utils.Positioned
// Definitions of symbolic Amy syntax trees
trait TreeModule {
/* A polymorphic module containing definitions of Amy trees.
*
* This trait represents either nominal trees (where names have not been resolved)
* or symbolic trees (where names/qualified names) have been resolved to unique identifiers.
* This is done by having two type fields within the module,
* which will be instantiated differently by the two different modules.
*
*/
trait TreeModule { self =>
/* Represents the type for the name for this tree module.
* (It will be either a plain string, or a unique symbol)
*/
type Name
// Represents a name within an module
type QualifiedName
// A printer that knows how to print trees in this module.
// The modules will instantiate it as appropriate
val printer: Printer { val treeModule: self.type }
// Common ancestor for all trees
trait Tree extends Positioned
trait Tree extends Positioned {
override def toString: String = printer(this)
}
// Expressions
trait Expr extends Tree
// Variables
case class Variable(name: Identifier) extends Expr
case class Variable(name: Name) extends Expr
// Literals
trait Literal[+T] extends Expr { val value: T }
......@@ -37,8 +59,8 @@ trait TreeModule {
case class Not(e: Expr) extends Expr
case class Neg(e: Expr) extends Expr
// Function/ type constructor call
case class Call(qname: Identifier, args: List[Expr]) extends Expr
// Function/constructor call
case class Call(qname: QualifiedName, args: List[Expr]) extends Expr
// The ; operator
case class Sequence(e1: Expr, e2: Expr) extends Expr
// Local variable definition
......@@ -57,28 +79,38 @@ trait TreeModule {
abstract class Pattern extends Tree
case class WildcardPattern() extends Pattern // _
case class IdPattern(name: Identifier) extends Pattern // x
case class IdPattern(name: Name) extends Pattern // x
case class LiteralPattern[+T](lit: Literal[T]) extends Pattern // 42, true
case class CaseClassPattern(constr: Identifier, args: List[Pattern]) extends Pattern // C(arg1, arg2)
case class CaseClassPattern(constr: QualifiedName, args: List[Pattern]) extends Pattern // C(arg1, arg2)
// Definitions
trait Definition extends Tree { val name: Identifier }
case class ModuleDef(name: Identifier, defs: List[ClassOrFunDef], optExpr: Option[Expr]) extends Definition
trait Definition extends Tree { val name: Name }
case class ModuleDef(name: Name, defs: List[ClassOrFunDef], optExpr: Option[Expr]) extends Definition
trait ClassOrFunDef extends Definition
case class FunDef(name: Identifier, params: List[ParamDef], retType: TypeTree, body: Expr) extends ClassOrFunDef {
case class FunDef(name: Name, params: List[ParamDef], retType: TypeTree, body: Expr) extends ClassOrFunDef {
def paramNames = params.map(_.name)
}
case class AbstractClassDef(name: Identifier) extends ClassOrFunDef
case class CaseClassDef(name: Identifier, fields: List[TypeTree], parent: Identifier) extends ClassOrFunDef
case class ParamDef(name: Identifier, tpe: TypeTree) extends Definition
case class AbstractClassDef(name: Name) extends ClassOrFunDef
case class CaseClassDef(name: Name, fields: List[TypeTree], parent: Name) extends ClassOrFunDef
case class ParamDef(name: Name, tt: TypeTree) extends Definition
// Types
trait Type
case object IntType extends Type
case object BooleanType extends Type
case object StringType extends Type
case object UnitType extends Type
case class ClassType(qname: Identifier) extends Type
case object IntType extends Type {
override def toString: String = "Int"
}
case object BooleanType extends Type {
override def toString: String = "Boolean"
}
case object StringType extends Type {
override def toString: String = "String"
}
case object UnitType extends Type {
override def toString: String = "Unit"
}
case class ClassType(qname: QualifiedName) extends Type {
override def toString: String = printer.printQName(qname)(false).print
}
// A wrapper for types that is also a Tree (i.e. has a position)
case class TypeTree(tpe: Type) extends Tree
......@@ -87,5 +119,24 @@ trait TreeModule {
case class Program(modules: List[ModuleDef]) extends Tree
}
// Identifiers represent unique names in Amy
class Identifier private(val name: String)
/* A module containing trees where the names have not been resolved.
* Instantiates Name to String and QualifiedName to a pair of Strings
* representing (module, name) (where module is optional)
*/
object NominalTreeModule extends TreeModule {
type Name = String
case class QualifiedName(module: Option[String], name: String) {
override def toString: String = printer.printQName(this)(false).print
}
val printer = NominalPrinter
}
/* A module containing trees where the names have been resolved to unique identifiers.
* Both Name and ModuleName are instantiated to Identifier.
*/
object SymbolicTreeModule extends TreeModule {
type Name = Identifier
type QualifiedName = Identifier
val printer = SymbolicPrinter
}
package amyc
package interpreter
import utils._
import ast.SymbolicTreeModule._
import ast.Identifier
import analyzer.SymbolTable
// An interpreter for Amy programs, implemented in Scala
object Interpreter extends Pipeline[(Program, SymbolTable), Unit] {
// A class that represents a value computed by interpreting an expression
abstract class Value {
def asInt: Int = this.asInstanceOf[IntValue].i
def asBoolean: Boolean = this.asInstanceOf[BooleanValue].b
def asString: String = this.asInstanceOf[StringValue].s
override def toString: String = this match {
case IntValue(i) => i.toString
case BooleanValue(b) => b.toString
case StringValue(s) => s
case UnitValue => "()"
case CaseClassValue(constructor, args) =>
constructor.name + "(" + args.map(_.toString).mkString(", ") + ")"
}
}
case class IntValue(i: Int) extends Value
case class BooleanValue(b: Boolean) extends Value
case class StringValue(s: String) extends Value
case object UnitValue extends Value
case class CaseClassValue(constructor: Identifier, args: List[Value]) extends Value
def run(ctx: Context)(v: (Program, SymbolTable)): Unit = {
val (program, table) = v
// These built-in functions do not have an Amy implementation in the program,
// instead their implementation is encoded in this map
val builtIns: Map[(String, String), (List[Value]) => Value] = Map(
("Std", "printInt") -> { args => println(args.head.asInt); UnitValue },
("Std", "printString") -> { args => println(args.head.asString); UnitValue },
("Std", "readString") -> { args => StringValue(scala.io.StdIn.readLine()) },
("Std", "readInt") -> { args =>
val input = scala.io.StdIn.readLine()
try {
IntValue(input.toInt)
} catch {
case ne: NumberFormatException =>
ctx.reporter.fatal(s"""Could not parse "$input" to Int""")
}
},
("Std", "intToString") -> { args => StringValue(args.head.asInt.toString) },
("Std", "digitToString") -> { args => StringValue(args.head.asInt.toString) }
)
// Utility functions to interface with the symbol table.
def isConstructor(name: Identifier) = table.getConstructor(name).isDefined
def findFunctionOwner(functionName: Identifier) = table.getFunction(functionName).get.owner.name
def findFunction(owner: String, name: String) = {
program.modules.find(_.name.name == owner).get.defs.collectFirst {
case fd@FunDef(fn, _, _, _) if fn.name == name => fd
}.get
}
// Interprets a function, using evaluations for local variables contained in 'locals'
// TODO: Complete all missing cases. Look at the given ones for guidance.
def interpret(expr: Expr)(implicit locals: Map[Identifier, Value]): Value = {
expr match {
case Variable(name) =>
???
case IntLiteral(i) =>
???
case BooleanLiteral(b) =>
???
case StringLiteral(s) =>
???
case UnitLiteral() =>
???
case Plus(lhs, rhs) =>
IntValue(interpret(lhs).asInt + interpret(rhs).asInt)
case Minus(lhs, rhs) =>
???
case Times(lhs, rhs) =>
???
case Div(lhs, rhs) =>
???
case Mod(lhs, rhs) =>
???
case LessThan(lhs, rhs) =>
???
case LessEquals(lhs, rhs) =>
???
case And(lhs, rhs) =>
???
case Or(lhs, rhs) =>
???
case Equals(lhs, rhs) =>
??? // Hint: Take care to implement Amy equality semantics
case Concat(lhs, rhs) =>
???
case Not(e) =>
???
case Neg(e) =>
???
case Call(qname, args) =>
???
// Hint: Check if it is a call to a constructor first,
// then if it is a built-in function (otherwise it is a normal function).
// Use the helper methods provided above to retrieve information from the symbol table.
// Think how locals should be modified.
case Sequence(e1, e2) =>
???
case Let(df, value, body) =>
???
case Ite(cond, thenn, elze) =>
???
case Match(scrut, cases) =>
???
// Hint: We give you a skeleton to implement pattern matching
// and the main body of the implementation
val evS = interpret(scrut)
// None = pattern does not match
// Returns a list of pairs id -> value,
// where id has been bound to value within the pattern.
// Returns None when the pattern fails to match.
// Note: Only works on well typed patterns (which have been ensured by the type checker).
def matchesPattern(v: Value, pat: Pattern): Option[List[(Identifier, Value)]] = {
((v, pat): @unchecked) match {
case (_, WildcardPattern()) =>
???
case (_, IdPattern(name)) =>
???
case (IntValue(i1), LiteralPattern(IntLiteral(i2))) =>
???
case (BooleanValue(b1), LiteralPattern(BooleanLiteral(b2))) =>
???
case (StringValue(_), LiteralPattern(StringLiteral(_))) =>
???
case (UnitValue, LiteralPattern(UnitLiteral())) =>
???
case (CaseClassValue(con1, realArgs), CaseClassPattern(con2, formalArgs)) =>
???
}
}
// Main "loop" of the implementation: Go through every case,
// check if the pattern matches, and if so return the evaluation of the case expression
cases.to(LazyList).map(matchCase =>
val MatchCase(pat, rhs) = matchCase
(rhs, matchesPattern(evS, pat))
).find(_._2.isDefined) match {
case Some((rhs, Some(moreLocals))) =>
interpret(rhs)(locals ++ moreLocals)
case _ =>
// No case matched
ctx.reporter.fatal(s"Match error: ${evS.toString}@${scrut.position}")
}
case Error(msg) =>
???
}
}
for {
m <- program.modules
e <- m.optExpr
} {
interpret(e)(Map())
}
}
}
object BasicError
def throwError(msg: String): Unit = {
error(msg)
}
throwError("basic error test")
end BasicError
object Division
1/0
end Division
object Locals
def foo(i: Int(32)): Int(32) = {
val i: Int(32) = 0;
if (i == 0) { error("") }
else { 0 }
}
foo(1)
end Locals
object MatchError
abstract class Foo
case class Bar() extends Foo
case class Baz() extends Foo
Bar() match { case Baz() => () }
end MatchError
object MatchError
abstract class Foo
case class Bar() extends Foo
case class Baz(i: Int(32)) extends Foo
Baz(1) match { case Baz(2) => () }
end MatchError
object MatchError
abstract class Foo
case class Bar() extends Foo
case class Baz(f: Foo) extends Foo
Baz(Bar()) match { case Baz(Baz(_)) => () }
end MatchError
object MatchError
abstract class Foo
case class Bar(s: String) extends Foo
Bar("foo") match { case Bar("foo") => () }
end MatchError
object MinimalError
error("")
end MinimalError
object ShortCircuit
false || error("")
end ShortCircuit
0
1
1024
27
10
1
2
222
3
6
4
8
2
1
0
0
-1
-1
1
-1
test finished
\ No newline at end of file
correct
not correct
not correct
correct
test finished
\ No newline at end of file
test finished
\ No newline at end of file
42
42
2
3
prim
prim
not prim
not prim
test finished
\ No newline at end of file
true
false
true
false
true
false
false
true
false
false
true
true
5! = 120
10! = 3628800
3
8
2
4
1
2