Skip to content

Commit

Permalink
Merge pull request #83 from MoeOrganization/prakashk/nested-arrays
Browse files Browse the repository at this point in the history
Multi-level indexing for arrays and hashes
  • Loading branch information
Stevan Little committed Apr 23, 2013
2 parents 0e50987 + 5fbcf95 commit 9eb55ac
Show file tree
Hide file tree
Showing 9 changed files with 158 additions and 116 deletions.
8 changes: 4 additions & 4 deletions src/main/scala/org/moe/ast/AST.scala
Expand Up @@ -87,16 +87,16 @@ case class VariableDeclarationNode(name: String, expression: AST) extends AST
case class ClassAccessNode(name: String) extends AST
case class AttributeAccessNode(name: String) extends AST
case class VariableAccessNode(name: String) extends AST
case class HashElementAccessNode(hashName: String, key: AST) extends AST
case class ArrayElementAccessNode(arrayName: String, index: AST) extends AST
case class HashElementAccessNode(hashName: String, keys: List[AST]) extends AST
case class ArrayElementAccessNode(arrayName: String, indices: List[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
case class ArrayElementLvalueNode(arrayName: String, indices: List[AST], expression: AST) extends AST
case class HashElementLvalueNode(hashName: String, keys: List[AST], expression: AST) extends AST

// operations

Expand Down
32 changes: 22 additions & 10 deletions src/main/scala/org/moe/ast/Serializer.scala
Expand Up @@ -252,44 +252,56 @@ object Serializer {
)
)

case HashElementAccessNode(hashName, key) => JSONObject(
case HashElementAccessNode(hashName, keys) => JSONObject(
Map(
"HashElementAccessNode" -> JSONObject(
Map(
"hashname" -> hashName,
"key" -> toJSON(key)
if (keys.length == 1)
"key" -> toJSON(keys.head)
else
"keys" -> JSONArray(keys.map(toJSON(_)))
)
)
)
)
case HashElementLvalueNode(arrayName, index, value) => JSONObject(
case HashElementLvalueNode(hashName, keys, value) => JSONObject(
Map(
"HashElementLvalueNode" -> JSONObject(
Map(
"hashname" -> arrayName,
"key" -> toJSON(index),
"hashname" -> hashName,
if (keys.length == 1)
"key" -> toJSON(keys.head)
else
"keys" -> JSONArray(keys.map(toJSON(_))),
"value" -> toJSON(value)
)
)
)
)

case ArrayElementAccessNode(arrayName, index) => JSONObject(
case ArrayElementAccessNode(arrayName, indices) => JSONObject(
Map(
"ArrayElementAccessNode" -> JSONObject(
Map(
"arrayname" -> arrayName,
"index" -> toJSON(index)
if (indices.length == 1)
"index" -> toJSON(indices.head)
else
"indices" -> JSONArray(indices.map(toJSON(_)))
)
)
)
)
case ArrayElementLvalueNode(arrayName, index, value) => JSONObject(
case ArrayElementLvalueNode(arrayName, indices, value) => JSONObject(
Map(
"ArrayElementLvalueNode" -> JSONObject(
Map(
"arrayname" -> arrayName,
"index" -> toJSON(index),
if (indices.length == 1)
"index" -> toJSON(indices.head)
else
"indices" -> JSONArray(indices.map(toJSON(_))),
"value" -> toJSON(value)
)
)
Expand Down Expand Up @@ -444,7 +456,7 @@ object Serializer {
)
)

case _ => "stub"
case x => "stub: " + x
}

// thanks to https://gist.github.com/umitanuki/944839
Expand Down
141 changes: 55 additions & 86 deletions src/main/scala/org/moe/interpreter/Interpreter.scala
Expand Up @@ -29,6 +29,16 @@ class Interpreter {
}
}

def callMethod(invocant: MoeObject, method: String, args: List[MoeObject], klass: String = null) =
invocant.callMethod(
invocant.getAssociatedClass.getOrElse(
throw new MoeErrors.ClassNotFound(Option(klass).getOrElse(invocant.getClassName))
).getMethod(method).getOrElse(
throw new MoeErrors.MethodNotFound("method " + method + "> missing in class " + Option(klass).getOrElse(invocant.getClassName))
),
args
)

val scoped = inNewEnv[MoeObject](env) _

// interpret ..
Expand Down Expand Up @@ -77,39 +87,36 @@ class Interpreter {

case ArrayLiteralNode(values) => getArray(values.map(eval(runtime, env, _)):_*)

case ArrayElementAccessNode(arrayName: String, index: AST) => {
val index_result = eval(runtime, env, index)
val array_value = env.get(arrayName) match {
case Some(a: MoeArrayObject) => a
case _ => throw new MoeErrors.UnexpectedType("MoeArrayObject expected")
case ArrayElementAccessNode(arrayName: String, indices: List[AST]) => {
var array_value = env.get(arrayName) match {
case Some(a: MoeArrayObject) => a
case _ => throw new MoeErrors.UnexpectedType("MoeArrayObject expected")
}

array_value.callMethod(
array_value.getAssociatedClass.getOrElse(
throw new MoeErrors.ClassNotFound("Array")
).getMethod("postcircumfix:<[]>").getOrElse(
throw new MoeErrors.MethodNotFound("postcircumfix:<[]>")
),
List(index_result)
)
indices.foldLeft[MoeObject](array_value) {
(a, i) =>
val index = eval(runtime, env, i)
callMethod(a, "postcircumfix:<[]>", List(index), "Array")
}
}

case ArrayElementLvalueNode(arrayName: String, index: AST, expr: AST) => {
val index_result = eval(runtime, env, index)
case ArrayElementLvalueNode(arrayName: String, indices: List[AST], expr: AST) => {
val array_value = env.get(arrayName) match {
case Some(a: MoeArrayObject) => a
case _ => throw new MoeErrors.UnexpectedType("MoeArrayObject expected")
}
val expr_value = eval(runtime, env, expr)

array_value.callMethod(
array_value.getAssociatedClass.getOrElse(
throw new MoeErrors.ClassNotFound("Array")
).getMethod("postcircumfix:<[]>").getOrElse(
throw new MoeErrors.MethodNotFound("postcircumfix:<[]>")
),
List(index_result, expr_value)
)
// find the deepest array and position that will be assigned
var last_index = eval(runtime, env, indices.last)
val last_array = indices.dropRight(1).foldLeft[MoeObject](array_value) {
(a, i) =>
val index = eval(runtime, env, i)
callMethod(a, "postcircumfix:<[]>", List(index), "Array")
}

// perform the assignment
val expr_value = eval(runtime, env, expr)
callMethod(last_array, "postcircumfix:<[]>", List(last_index, expr_value), "Array")
}

case PairLiteralNode(key, value) => getPair(
Expand Down Expand Up @@ -139,39 +146,36 @@ class Interpreter {
code
}

case HashElementAccessNode(hashName: String, key: AST) => {
val key_result = eval(runtime, env, key)
case HashElementAccessNode(hashName: String, keys: List[AST]) => {
val hash_map = env.get(hashName) match {
case Some(h: MoeHashObject) => h
case _ => throw new MoeErrors.UnexpectedType("MoeHashObject expected")
}

hash_map.callMethod(
hash_map.getAssociatedClass.getOrElse(
throw new MoeErrors.ClassNotFound("Hash")
).getMethod("postcircumfix:<{}>").getOrElse(
throw new MoeErrors.MethodNotFound("postcircumfix:<{}>")
),
List(key_result)
)
keys.foldLeft[MoeObject](hash_map) {
(h, k) =>
val key = eval(runtime, env, k)
callMethod(h, "postcircumfix:<{}>", List(key), "Hash")
}
}

case HashElementLvalueNode(hashName: String, key: AST, value: AST) => {
val key_result = eval(runtime, env, key)
case HashElementLvalueNode(hashName: String, keys: List[AST], value: AST) => {
val hash_map = env.get(hashName) match {
case Some(h: MoeHashObject) => h
case _ => throw new MoeErrors.UnexpectedType("MoeHashObject expected")
}
val value_result = eval(runtime, env, value)

hash_map.callMethod(
hash_map.getAssociatedClass.getOrElse(
throw new MoeErrors.ClassNotFound("Hash")
).getMethod("postcircumfix:<{}>").getOrElse(
throw new MoeErrors.MethodNotFound("postcircumfix:<{}>")
),
List(key_result, value_result)
)
// find the deepest hash and key that will be assigned
val last_key = eval(runtime, env, keys.last)
val last_hash = keys.dropRight(1).foldLeft[MoeObject](hash_map) {
(h, k) =>
val key = eval(runtime, env, k)
callMethod(h, "postcircumfix:<{}>", List(key), "Hash")
}

// perform the assignment
val value_result = eval(runtime, env, value)
callMethod(last_hash, "postcircumfix:<{}>", List(last_key, value_result), "Hash")
}

case RangeLiteralNode(start, end) => {
Expand Down Expand Up @@ -211,41 +215,20 @@ class Interpreter {

case PrefixUnaryOpNode(lhs: AST, operator: String) => {
val receiver = eval(runtime, env, lhs)
receiver.callMethod(
receiver.getAssociatedClass.getOrElse(
throw new MoeErrors.ClassNotFound(receiver.toString)
).getMethod("prefix:<" + operator + ">").getOrElse(
throw new MoeErrors.MethodNotFound("method prefix:<" + operator + "> missing in class " + receiver.getClassName)
),
List()
)
callMethod(receiver, "prefix:<" + operator + ">", List())
}

case PostfixUnaryOpNode(lhs: AST, operator: String) => {
val receiver = eval(runtime, env, lhs)
receiver.callMethod(
receiver.getAssociatedClass.getOrElse(
throw new MoeErrors.ClassNotFound(receiver.toString)
).getMethod("postfix:<" + operator + ">").getOrElse(
throw new MoeErrors.MethodNotFound("method postfix:<" + operator + "> missing in class " + receiver.getClassName)
),
List()
)
callMethod(receiver, "postfix:<" + operator + ">", List())
}

// binary operators

case BinaryOpNode(lhs: AST, operator: String, rhs: AST) => {
val receiver = eval(runtime, env, lhs)
val arg = eval(runtime, env, rhs)
receiver.callMethod(
receiver.getAssociatedClass.getOrElse(
throw new MoeErrors.ClassNotFound(receiver.toString)
).getMethod("infix:<" + operator + ">").getOrElse(
throw new MoeErrors.MethodNotFound("method infix:<" + operator + "> missing in class " + receiver.getClassName)
),
List(arg)
)
callMethod(receiver, "infix:<" + operator + ">", List(arg))
}

// short circuit binary operators
Expand All @@ -254,14 +237,7 @@ class Interpreter {
case ShortCircuitBinaryOpNode(lhs: AST, operator: String, rhs: AST) => {
val receiver = eval(runtime, env, lhs)
val arg = new MoeLazyEval(this, runtime, env, rhs)
receiver.callMethod(
receiver.getAssociatedClass.getOrElse(
throw new MoeErrors.ClassNotFound(receiver.toString)
).getMethod("infix:<" + operator + ">").getOrElse(
throw new MoeErrors.MethodNotFound("method infix:<" + operator + "> missing in class " + receiver.getClassName)
),
List(arg)
)
callMethod(receiver, "infix:<" + operator + ">", List(arg))
}

// ternary operator
Expand All @@ -270,14 +246,7 @@ class Interpreter {
val receiver = eval(runtime, env, cond)
val argTrue = new MoeLazyEval(this, runtime, env, trueExpr)
val argFalse = new MoeLazyEval(this, runtime, env, falseExpr)
receiver.callMethod(
receiver.getAssociatedClass.getOrElse(
throw new MoeErrors.ClassNotFound(receiver.toString)
).getMethod("infix:<?:>").getOrElse(
throw new MoeErrors.MethodNotFound("method infix:<?:> missing in class " + receiver.getClassName)
),
List(argTrue, argFalse)
)
callMethod(receiver, "infix:<?:>", List(argTrue, argFalse))
}

// value lookup, assignment and declaration
Expand Down Expand Up @@ -672,7 +641,7 @@ class Interpreter {
)

}
case _ => throw new MoeErrors.UnknownNode("Unknown Node")
case x => throw new MoeErrors.UnknownNode("Unknown Node: " + x)
}
}

Expand Down
4 changes: 2 additions & 2 deletions src/main/scala/org/moe/interpreter/InterpreterUtils.scala
Expand Up @@ -46,8 +46,8 @@ object InterpreterUtils {
case VariableAssignmentNode(name, expression) => walkAST(expression, callback)
case VariableDeclarationNode(name, expression) => walkAST(expression, callback)

case HashElementAccessNode(hashName, key) => walkAST(key, callback)
case ArrayElementAccessNode(arrayName, index) => walkAST(index, callback)
case HashElementAccessNode(hashName, keys) => keys.foreach(walkAST(_, callback))
case ArrayElementAccessNode(arrayName, indices) => indices.foreach(walkAST(_, callback))
// ^ Maybe we need to walk VariableAccessNode(arrayName)? Not sure.

case MethodCallNode(invocant, method_name, args) => {
Expand Down
12 changes: 6 additions & 6 deletions src/main/scala/org/moe/parser/MoeProductions.scala
Expand Up @@ -235,15 +235,15 @@ trait MoeProductions extends MoeLiterals with JavaTokenParsers with PackratParse

// declaration

private lazy val array_index_rule = "@" ~ (namespacedIdentifier <~ "[") ~ (expression <~ "]")
private lazy val hash_index_rule = "%" ~ (namespacedIdentifier <~ "{") ~ (expression <~ "}")
private lazy val array_index_rule = "@" ~ namespacedIdentifier ~ ( "[" ~> expression <~ "]" ).+
private lazy val hash_index_rule = "%" ~ namespacedIdentifier ~ ( "{" ~> expression <~ "}" ).+

def arrayIndex = array_index_rule ^^ {
case "@" ~ i ~ expr => ArrayElementAccessNode("@" + i, expr)
case "@" ~ i ~ exprs => ArrayElementAccessNode("@" + i, exprs)
}

def hashIndex = hash_index_rule ^^ {
case "%" ~ i ~ expr => HashElementAccessNode("%" + i, expr)
case "%" ~ i ~ exprs => HashElementAccessNode("%" + i, exprs)
}

// assignment
Expand Down Expand Up @@ -283,11 +283,11 @@ trait MoeProductions extends MoeLiterals with JavaTokenParsers with PackratParse
}

def arrayElementAssignment = array_index_rule ~ "=" ~ expression <~ statementDelim ^^ {
case "@" ~ array ~ index_expr ~ "=" ~ value_expr => ArrayElementLvalueNode("@" + array, index_expr, value_expr)
case "@" ~ array ~ index_exprs ~ "=" ~ value_expr => ArrayElementLvalueNode("@" + array, index_exprs, value_expr)
}

def hashElementAssignment = hash_index_rule ~ "=" ~ expression <~ statementDelim ^^ {
case "%" ~ hash ~ key_expr ~ "=" ~ value_expr => HashElementLvalueNode("%" + hash, key_expr, value_expr)
case "%" ~ hash ~ key_exprs ~ "=" ~ value_expr => HashElementLvalueNode("%" + hash, key_exprs, value_expr)
}

/**
Expand Down

0 comments on commit 9eb55ac

Please sign in to comment.