Skip to content

Commit

Permalink
Merge pull request #78 from MoeOrganization/prakashk/list-op
Browse files Browse the repository at this point in the history
ArrayClass methods, array/hash elements as lvalues etc
  • Loading branch information
Stevan Little committed Apr 9, 2013
2 parents ff028a9 + 8445b11 commit 041c1a3
Show file tree
Hide file tree
Showing 12 changed files with 402 additions and 48 deletions.
2 changes: 2 additions & 0 deletions src/main/scala/org/moe/ast/AST.scala
Expand Up @@ -81,6 +81,8 @@ 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 VariableAssignmentNode(name: String, expression: 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

// operations

Expand Down
23 changes: 23 additions & 0 deletions src/main/scala/org/moe/ast/Serializer.scala
Expand Up @@ -247,6 +247,18 @@ object Serializer {
)
)
)
case HashElementLvalueNode(arrayName, index, value) => JSONObject(
Map(
"HashElementLvalueNode" -> JSONObject(
Map(
"hashname" -> arrayName,
"key" -> toJSON(index),
"value" -> toJSON(value)
)
)
)
)

case ArrayElementAccessNode(arrayName, index) => JSONObject(
Map(
"ArrayElementAccessNode" -> JSONObject(
Expand All @@ -257,6 +269,17 @@ object Serializer {
)
)
)
case ArrayElementLvalueNode(arrayName, index, value) => JSONObject(
Map(
"ArrayElementLvalueNode" -> JSONObject(
Map(
"arrayname" -> arrayName,
"index" -> toJSON(index),
"value" -> toJSON(value)
)
)
)
)

case MethodCallNode(invocant, method_name, args) => JSONObject(
Map(
Expand Down
36 changes: 36 additions & 0 deletions src/main/scala/org/moe/interpreter/Interpreter.scala
Expand Up @@ -83,6 +83,24 @@ class Interpreter {
)
}

case ArrayElementLvalueNode(arrayName: String, index: AST, expr: 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")
}
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)
)
}

case PairLiteralNode(key, value) => getPair(
eval(runtime, env, key).unboxToString.get -> eval(runtime, env, value)
)
Expand Down Expand Up @@ -127,6 +145,24 @@ class Interpreter {
)
}

case HashElementLvalueNode(hashName: String, key: AST, value: AST) => {
val key_result = eval(runtime, env, key)
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)
)
}

case RangeLiteralNode(start, end) => {
val s = eval(runtime, env, start)
val e = eval(runtime, env, end)
Expand Down
25 changes: 18 additions & 7 deletions src/main/scala/org/moe/parser/MoeProductions.scala
Expand Up @@ -64,7 +64,7 @@ trait MoeProductions extends MoeLiterals with JavaTokenParsers with PackratParse
case left ~ op ~ right => BinaryOpNode(left, op, right)
} | addOp

// left + - .
// left + - ~
lazy val addOp: PackratParser[AST] = addOp ~ "[-+~]".r ~ mulOp ^^ {
case left ~ op ~ right => BinaryOpNode(left, op, right)
} | mulOp
Expand All @@ -87,9 +87,6 @@ trait MoeProductions extends MoeLiterals with JavaTokenParsers with PackratParse
// used for explicit coercion
// (see: http://perlcabal.org/syn/S03.html#Symbolic_unary_precedence)

// Perl6 uses ~ for stringification (same as its concatentation op);
// since our concat op is ".", we use it as the prefix op

lazy val coerceOp: PackratParser[AST] = "[+?~]".r ~ fileTestOps ^^ {
case op ~ expr => PrefixUnaryOpNode(expr, op)
} | fileTestOps
Expand All @@ -107,7 +104,7 @@ trait MoeProductions extends MoeLiterals with JavaTokenParsers with PackratParse
*********************************************************************
*/

// left ->
// left .
lazy val applyOp: PackratParser[AST] = (applyOp <~ ".") ~ namespacedIdentifier ~ ("(" ~> repsep(expression, ",") <~ ")").? ^^ {
case invocant ~ method ~ Some(args) => MethodCallNode(invocant, method, args)
case invocant ~ method ~ None => MethodCallNode(invocant, method, List())
Expand All @@ -124,9 +121,13 @@ trait MoeProductions extends MoeLiterals with JavaTokenParsers with PackratParse

lazy val anonCodeCall: PackratParser[AST] = anonCodeInvocant ~ "." ~ ("(" ~> repsep(expression, ",") <~ ")") ^^ {
case anonCode ~ _ ~ args => MethodCallNode(anonCode, "call", args)
} | listOpLeftward

// left terms and list operators (leftward)
lazy val listOpLeftward: PackratParser[AST] = namespacedIdentifier ~ rep1sep(expression, ",") ^^ {
case sub ~ args => SubroutineCallNode(sub, args)
} | simpleExpression

// TODO: left terms and list operators (leftward)

/**
*********************************************************************
Expand Down Expand Up @@ -181,7 +182,7 @@ trait MoeProductions extends MoeLiterals with JavaTokenParsers with PackratParse

// Hash Literals

def barehashKey: Parser[StringLiteralNode] = """[0-9\w_]*""".r ^^ StringLiteralNode
def barehashKey: Parser[StringLiteralNode] = """[0-9\w_]+""".r ^^ StringLiteralNode
def hashKey: Parser[AST] = variable | arrayIndex | hashIndex | literalValue | barehashKey

def hashContent: Parser[List[PairLiteralNode]] = repsep(pair, ",")
Expand Down Expand Up @@ -254,6 +255,14 @@ trait MoeProductions extends MoeLiterals with JavaTokenParsers with PackratParse
case v ~ _ ~ expr => AttributeAssignmentNode(v, expr)
}

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

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

/**
*********************************************************************
* Now we are getting into statements,
Expand Down Expand Up @@ -409,6 +418,8 @@ trait MoeProductions extends MoeLiterals with JavaTokenParsers with PackratParse
| variableAssignment
| attributeAssignment
| useStatement
| arrayElementAssignment
| hashElementAssignment
| expression <~ statementDelim.?
| scopeBlock
)
Expand Down
12 changes: 12 additions & 0 deletions src/main/scala/org/moe/runtime/MoeObject.scala
Expand Up @@ -86,6 +86,18 @@ class MoeObject(
"{ #instance(" + id + ")" + associatedClass.map({ k => k.toString }).getOrElse("") + " }"
}

/**
* returns true of this is equal to 'that'
* Should be overridden by subclasses
*/
def equal_to(that: MoeObject): Boolean = toString == that.toString

/**
* returns true of this is not equal to 'that'
* Should be overridden by subclasses
*/
def not_equal_to(that: MoeObject): Boolean = toString != that.toString

// unboxing

def unboxToBoolean : Try[Boolean] = Success(isTrue)
Expand Down
95 changes: 93 additions & 2 deletions src/main/scala/org/moe/runtime/builtins/ArrayClass.scala
Expand Up @@ -35,15 +35,20 @@ object ArrayClass {
arrayClass.addMethod(
new MoeMethod(
"postcircumfix:<[]>",
new MoeSignature(List(new MoePositionalParameter("$i"))),
new MoeSignature(List(new MoePositionalParameter("$i"), new MoeOptionalParameter("$value"))),
env,
{ (e) =>
var index = e.get("$i").get.unboxToInt.get
val array = self(e).unboxToArrayBuffer.get

while (index < 0) index += array.size

try {
array(index)
e.get("$value") match {
case Some(none: MoeUndefObject) => array(index)
case Some(value: MoeObject) => self(e).bind_pos(r, getInt(index), value)
case _ => array(index)
}
} catch {
case _: java.lang.IndexOutOfBoundsException => getUndef // TODO: warn
}
Expand Down Expand Up @@ -202,6 +207,92 @@ object ArrayClass {
)
)

arrayClass.addMethod(
new MoeMethod(
"reduce",
new MoeSignature(List(new MoePositionalParameter("&f"), new MoeOptionalParameter("$init"))),
env,
{ (e) => e.get("$init") match {
case Some(none: MoeUndefObject) => self(e).reduce(r, e.getAs[MoeCode]("&f").get, None)
case Some(init: MoeObject) => self(e).reduce(r, e.getAs[MoeCode]("&f").get, Some(init))
case _ => self(e).reduce(r, e.getAs[MoeCode]("&f").get, None)
}
}
)
)

arrayClass.addMethod(
new MoeMethod(
"first",
new MoeSignature(List(new MoePositionalParameter("&f"))),
env,
(e) => self(e).first(r, e.getAs[MoeCode]("&f").get)
)
)

arrayClass.addMethod(
new MoeMethod(
"max",
new MoeSignature(),
env,
(e) => self(e).max(r)
)
)

arrayClass.addMethod(
new MoeMethod(
"maxstr",
new MoeSignature(),
env,
(e) => self(e).maxstr(r)
)
)

arrayClass.addMethod(
new MoeMethod(
"min",
new MoeSignature(),
env,
(e) => self(e).min(r)
)
)

arrayClass.addMethod(
new MoeMethod(
"minstr",
new MoeSignature(),
env,
(e) => self(e).minstr(r)
)
)

arrayClass.addMethod(
new MoeMethod(
"shuffle",
new MoeSignature(),
env,
(e) => self(e).shuffle(r)
)
)

arrayClass.addMethod(
new MoeMethod(
"sum",
new MoeSignature(),
env,
(e) => self(e).sum(r)
)
)

arrayClass.addMethod(
new MoeMethod(
"eqv",
new MoeSignature(List(new MoePositionalParameter("@that"))),
env,
(e) => self(e).equal_to(r, e.getAs[MoeArrayObject]("@that").get)
)
)

/**
* List of Methods to support:
* - exists ($value)
Expand Down
8 changes: 6 additions & 2 deletions src/main/scala/org/moe/runtime/builtins/HashClass.scala
Expand Up @@ -31,11 +31,15 @@ object HashClass {
hashClass.addMethod(
new MoeMethod(
"postcircumfix:<{}>",
new MoeSignature(List(new MoePositionalParameter("$key"))),
new MoeSignature(List(new MoePositionalParameter("$key"), new MoeOptionalParameter("$value"))),
env,
{ (e) =>
val hash = self(e)
hash.at_key(r, e.getAs[MoeStrObject]("$key").get)
e.get("$value") match {
case Some(none: MoeUndefObject) => hash.at_key(r, e.getAs[MoeStrObject]("$key").get)
case Some(value: MoeObject) => hash.bind_key(r, e.getAs[MoeStrObject]("$key").get, e.get("$value").get)
case _ => hash.at_key(r, e.getAs[MoeStrObject]("$key").get)
}
}
)
)
Expand Down
55 changes: 53 additions & 2 deletions src/main/scala/org/moe/runtime/nativeobjects/MoeArrayObject.scala
Expand Up @@ -23,8 +23,12 @@ class MoeArrayObject(

def bind_pos (r: MoeRuntime, i: MoeIntObject, v: MoeObject): MoeObject = {
val idx = i.unboxToInt.get
if (idx >= array.length) setNativeValue(array.padTo(idx, r.NativeObjects.getUndef))
array.insert(idx, v)
if (idx < array.length)
array(idx) = v
else {
setNativeValue(array.padTo(idx, r.NativeObjects.getUndef))
array.insert(idx, v)
}
v
}

Expand Down Expand Up @@ -80,6 +84,53 @@ class MoeArrayObject(
r.NativeObjects.getUndef
}

def reduce (r: MoeRuntime, f: MoeCode, init: Option[MoeObject]): MoeObject = init match {
case Some(init_val) => array.foldLeft(init_val)({ (a, b) => f.execute(new MoeArguments(List(a, b))) })
case None => array.reduceLeft ({ (a, b) => f.execute(new MoeArguments(List(a, b))) })
}

def first (r: MoeRuntime, f: MoeCode): MoeObject =
array.dropWhile(i => f.execute(new MoeArguments(List(i))).isFalse).head

def max (r: MoeRuntime): MoeIntObject = r.NativeObjects.getInt(
array.map(i => i.unboxToInt.get).max
)

def maxstr (r: MoeRuntime): MoeStrObject = r.NativeObjects.getStr(
array.map(s => s.unboxToString.get).max
)

def min (r: MoeRuntime): MoeIntObject = r.NativeObjects.getInt(
array.map(i => i.unboxToInt.get).min
)

def minstr (r: MoeRuntime): MoeStrObject = r.NativeObjects.getStr(
array.map(s => s.unboxToString.get).min
)

def shuffle (r: MoeRuntime): MoeArrayObject = r.NativeObjects.getArray(
scala.util.Random.shuffle(array) : _*
)

def sum (r: MoeRuntime): MoeIntObject = r.NativeObjects.getInt(
array.map(i => i.unboxToInt.get).sum
)

// equality
def equal_to (r: MoeRuntime, that: MoeArrayObject): MoeBoolObject =
r.NativeObjects.getBool(
length(r).equal_to(that.length(r))
&&
((unboxToArrayBuffer.get, that.unboxToArrayBuffer.get).zipped.forall( (a, b) => a.equal_to(b) ))
)

def not_equal_to (r: MoeRuntime, that: MoeArrayObject): MoeBoolObject =
r.NativeObjects.getBool(
length(r).not_equal_to(that.length(r))
||
((unboxToArrayBuffer.get, that.unboxToArrayBuffer.get).zipped.exists( (a, b) => a.not_equal_to(b) ))
)

// MoeNativeObject overrides

override def copy = new MoeArrayObject(ArrayBuffer(getNativeValue:_*), getAssociatedClass)
Expand Down

0 comments on commit 041c1a3

Please sign in to comment.