Skip to content

Commit

Permalink
multi-assignment swaps now work
Browse files Browse the repository at this point in the history
  • Loading branch information
Stevan Little committed Apr 10, 2013
1 parent 30ce065 commit 1911507
Show file tree
Hide file tree
Showing 5 changed files with 85 additions and 3 deletions.
2 changes: 2 additions & 0 deletions src/main/scala/org/moe/ast/AST.scala
Expand Up @@ -81,7 +81,9 @@ case class ArrayElementAccessNode(arrayName: String, index: AST) extends AST

// TODO - these should get converted to binary ops
case class AttributeAssignmentNode(name: String, expression: AST) extends AST
case class MultiAttributeAssignmentNode(names: List[String], expressions: List[AST]) extends AST
case class VariableAssignmentNode(name: String, expression: AST) extends AST
case class MultiVariableAssignmentNode(names: List[String], expressions: List[AST]) extends AST
case class ArrayElementLvalueNode(arrayName: String, index: AST, expression: AST) extends AST
case class HashElementLvalueNode(hashName: String, key: AST, expression: AST) extends AST

Expand Down
21 changes: 21 additions & 0 deletions src/main/scala/org/moe/ast/Serializer.scala
Expand Up @@ -216,6 +216,16 @@ object Serializer {
)
)
)
case MultiAttributeAssignmentNode(names, expressions) => JSONObject(
Map(
"MultiAttributeAssignmentNode" -> JSONObject(
Map(
"names" -> names.mkString(", "),
"expressions" -> List(expressions.map(toJSON(_)))
)
)
)
)
case AttributeDeclarationNode(name, expression) => JSONObject(
Map(
"AttributeDeclarationNode" -> JSONObject(
Expand All @@ -238,6 +248,17 @@ object Serializer {
)
)
)
case MultiVariableAssignmentNode(names, expressions) => JSONObject(
Map(
"MultiVariableAssignmentNode" -> JSONObject(
Map(
"names" -> names.mkString(", "),
"expressions" -> List(expressions.map(toJSON(_)))
)
)
)
)

case VariableDeclarationNode(name, expression) => JSONObject(
Map(
"VariableDeclarationNode" -> JSONObject(
Expand Down
42 changes: 41 additions & 1 deletion src/main/scala/org/moe/interpreter/Interpreter.scala
Expand Up @@ -18,6 +18,17 @@ class Interpreter {
// no need to do all that typing ..
import runtime.NativeObjects._

def zipVars (names: List[String], expressions: List[MoeObject], f: ((String, MoeObject)) => Unit): Unit = {
if (expressions.isEmpty) {
names.foreach(f(_, getUndef))
} else if (names.isEmpty) {
()
} else {
f(names.head, expressions.headOption.getOrElse(getUndef))
zipVars(names.tail, expressions.tail, f)
}
}

val scoped = inNewEnv[MoeObject](env) _

// interpret ..
Expand Down Expand Up @@ -471,12 +482,32 @@ class Interpreter {
val expr = eval(runtime, env, expression)
env.getCurrentInvocant match {
case Some(invocant: MoeOpaque) => invocant.setValue(name, expr)
// XXX attr."setDefault"(expr) ?
case Some(invocant) => throw new MoeErrors.UnexpectedType(invocant.toString)
case None => throw new MoeErrors.MoeException("Attribute default already declared")
}
expr
}
case MultiAttributeAssignmentNode(names, expressions) => {
val klass = env.getCurrentClass.getOrElse(throw new MoeErrors.ClassNotFound("__CLASS__"))

val evaled_expressions = expressions.map(eval(runtime, env, _))
env.getCurrentInvocant match {
case Some(invocant: MoeOpaque) => {
zipVars(
names,
evaled_expressions,
{ (p) =>
klass.getAttribute(p._1).getOrElse(throw new MoeErrors.AttributeNotFound(p._1))
invocant.setValue(p._1, p._2)
}
)
}
case Some(invocant) => throw new MoeErrors.UnexpectedType(invocant.toString)
case None => throw new MoeErrors.MoeException("Attribute default already declared")
}

evaled_expressions.last
}
case AttributeDeclarationNode(name, expression) => {
val klass = env.getCurrentClass.getOrElse(
throw new MoeErrors.ClassNotFound("__CLASS__")
Expand Down Expand Up @@ -506,6 +537,15 @@ class Interpreter {
}
)

case MultiVariableAssignmentNode(names, expressions) => {
zipVars(
names,
expressions.map(eval(runtime, env, _)),
(p) => env.set(p._1, p._2).getOrElse(throw new MoeErrors.VariableNotFound(p._1))
)
env.get(names.last).getOrElse(throw new MoeErrors.VariableNotFound(names.last))
}

case VariableAssignmentNode(name, expression) => {
env.set(name, eval(runtime, env, expression)).getOrElse(
throw new MoeErrors.VariableNotFound(name)
Expand Down
4 changes: 2 additions & 2 deletions src/main/scala/org/moe/parser/MoeProductions.scala
Expand Up @@ -266,15 +266,15 @@ trait MoeProductions extends MoeLiterals with JavaTokenParsers with PackratParse
}

def multiVariableAssignment = ("(" ~> repsep(variableName, ",") <~ ")") ~ "=" ~ ("(" ~> repsep(expression, ",") <~ ")") <~ statementDelim ^^ {
case vars ~ _ ~ exprs => StatementsNode(zipEm(vars, exprs, (p) => VariableAssignmentNode(p._1, p._2)))
case vars ~ _ ~ exprs => MultiVariableAssignmentNode(vars, exprs)
}

def attributeAssignment = attributeName ~ "=" ~ expression <~ statementDelim ^^ {
case v ~ _ ~ expr => AttributeAssignmentNode(v, expr)
}

def multiAttributeAssignment = ("(" ~> repsep(attributeName, ",") <~ ")") ~ "=" ~ ("(" ~> repsep(expression, ",") <~ ")") <~ statementDelim ^^ {
case vars ~ _ ~ exprs => StatementsNode(zipEm(vars, exprs, (p) => AttributeAssignmentNode(p._1, p._2)))
case vars ~ _ ~ exprs => MultiAttributeAssignmentNode(vars, exprs)
}

def arrayElementAssignment = array_index_rule ~ "=" ~ expression <~ statementDelim ^^ {
Expand Down
19 changes: 19 additions & 0 deletions src/test/scala/org/moe/parser/MultiAssignmentTestSuite.scala
Expand Up @@ -60,6 +60,13 @@ class MultiAssignmentTestSuite extends FunSuite with BeforeAndAfter with ParserT
a(1).unboxToInt.get should equal (2)
}

test("... basic multi-assign w/ swap") {
val result = interpretCode("my ($x, $y) = (1, 2); ($x, $y) = ($y, $x); [$x, $y]")
var a = result.unboxToArrayBuffer.get
a(0).unboxToInt.get should equal (2)
a(1).unboxToInt.get should equal (1)
}

// attribute assignment

private val testClass = """
Expand All @@ -80,6 +87,11 @@ class MultiAssignmentTestSuite extends FunSuite with BeforeAndAfter with ParserT
($!x, $!y) = (1, 2, 3);
}
method test4 {
($!x, $!y) = (1, 2);
($!x, $!y) = ($!y, $!x);
}
method get { [ $!x, $!y, $!z ] }
}
"""
Expand All @@ -106,4 +118,11 @@ class MultiAssignmentTestSuite extends FunSuite with BeforeAndAfter with ParserT
a(1).unboxToInt.get should equal (2)
}

test("... basic multi-attribute-assign w/ swap") {
val result = interpretCode(testClass + "; my $x = ^Foo.new; $x.test4; $x.get")
var a = result.unboxToArrayBuffer.get
a(0).unboxToInt.get should equal (2)
a(1).unboxToInt.get should equal (1)
}

}

0 comments on commit 1911507

Please sign in to comment.