Skip to content

Commit

Permalink
Merge pull request #73 from MoeOrganization/prakashk/ternary-bitshift…
Browse files Browse the repository at this point in the history
…-repl

Ternary op, bit-shift op and repl enhancement
  • Loading branch information
Stevan Little committed Mar 21, 2013
2 parents f145d21 + 72f3e41 commit 97a6b07
Show file tree
Hide file tree
Showing 11 changed files with 189 additions and 13 deletions.
4 changes: 4 additions & 0 deletions src/main/scala/org/moe/ast/AST.scala
Expand Up @@ -54,6 +54,10 @@ case class PostfixUnaryOpNode(rhs: AST, operator: String) extends AST
case class BinaryOpNode(lhs: AST, operator: String, rhs: AST) extends AST
case class ShortCircuitBinaryOpNode(lhs: AST, operator: String, rhs: AST) extends AST

// ternary operator

case class TernaryOpNode(cond: AST, trueExpr: AST, falseExpr: AST) extends AST

// value lookup, assignment and declaration

case class ParameterNode(name: String) extends AST
Expand Down
42 changes: 39 additions & 3 deletions src/main/scala/org/moe/ast/Serializer.scala
Expand Up @@ -75,6 +75,18 @@ object Serializer {
)
)

case TernaryOpNode(condExpr, trueExpr, falseExpr) => JSONObject(
Map(
"BinaryOpNode" -> JSONObject(
Map(
"condition" -> toJSON(condExpr),
"trueExpr" -> toJSON(trueExpr),
"falseExpr" -> toJSON(falseExpr)
)
)
)
)

case ShortCircuitBinaryOpNode(lhs, operator, rhs) => JSONObject(
Map(
"ShortCircuitBinaryOpNode" -> JSONObject(
Expand Down Expand Up @@ -353,13 +365,37 @@ object Serializer {
}

// thanks to https://gist.github.com/umitanuki/944839
// modified to make the output a bit more compact where appropriate
def pprint(j: Option[Any], l: Int = 0):String = {

def is_simple_object(x: Option[Any]): Boolean = {
x match {
case Some(o: JSONObject) => if (o.obj.keys.size > 1)
false
else
o.obj.head match {
case (_, v: JSONObject) => false
case (_, v: JSONArray) => false
case _ => true
}
case Some(a: JSONArray) => if (a.list.size > 1) false else is_simple_object(Some(a.list.head))
case _ => true
}
}

val indent = (for(i <- List.range(0, l)) yield " ").mkString
j match{
case Some(o: JSONObject) => {
List("{",
o.obj.keys.map(key => indent + " " + "\"" + key + "\" : " + pprint(o.obj.get(key), l + 1)).mkString(",\n"),
indent + "}").mkString("\n")
val max_key_len = o.obj.keys.map(key => key.length).max
def padded(key: String) =
"\"" + key + "\"" + (if (key.length < max_key_len) " ".padTo(max_key_len - key.length, ' ') else "")

if (is_simple_object(Some(o)))
"{ " + o.obj.keys.map(key => padded(key) + " : " + pprint(o.obj.get(key), l + 1)).mkString(",\n") + " }"
else
List("{",
o.obj.keys.map(key => indent + " " + padded(key) + " : " + pprint(o.obj.get(key), l + 1)).mkString(",\n"),
indent + "}").mkString("\n")
}
case Some(a: JSONArray) => {
List("[",
Expand Down
16 changes: 16 additions & 0 deletions src/main/scala/org/moe/interpreter/Interpreter.scala
Expand Up @@ -190,6 +190,22 @@ class Interpreter {
)
}

// ternary operator

case TernaryOpNode(cond: AST, trueExpr: AST, falseExpr: AST) => {
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)
)
}

// value lookup, assignment and declaration

case ClassAccessNode(name) => {
Expand Down
17 changes: 12 additions & 5 deletions src/main/scala/org/moe/parser/Expressions.scala
Expand Up @@ -14,7 +14,7 @@ trait Expressions extends Literals with JavaTokenParsers with PackratParsers {
(namespacedIdentifier <~ "{") ~
(expression <~ "}")

lazy val expression: PackratParser[AST] = logicalOrOp
lazy val expression: PackratParser[AST] = ternaryOp

// This is what I want
// def binOpResult = { case left ~ op ~ right => MethodCallNode(left, op, List(right)) }
Expand All @@ -27,8 +27,11 @@ trait Expressions extends Literals with JavaTokenParsers with PackratParsers {
// TODO: nonassoc list operators (rightward)
// TODO: left , =>
// TODO: right = += -= *= etc.
// TODO: right ?:

// right ?:
lazy val ternaryOp: PackratParser[AST] = logicalOrOp ~ "?" ~ ternaryOp ~ ":" ~ ternaryOp ^^ {
case cond ~ "?" ~ trueExpr ~ ":" ~ falseExpr => TernaryOpNode(cond, trueExpr, falseExpr)
} | logicalOrOp

// left || TODO: //
lazy val logicalOrOp: PackratParser[AST] = logicalOrOp ~ """\|\||//""".r ~ logicalAndOp ^^ {
Expand Down Expand Up @@ -56,12 +59,16 @@ trait Expressions extends Literals with JavaTokenParsers with PackratParsers {
} | relOp

// nonassoc < > <= >= lt gt le ge
lazy val relOp: PackratParser[AST] = relOp ~ "[<>]=?|lt|gt|le|ge".r ~ addOp ^^ {
lazy val relOp: PackratParser[AST] = relOp ~ "[<>]=?|lt|gt|le|ge".r ~ bitShiftOp ^^ {
case left ~ op ~ right => BinaryOpNode(left, op, right)
} | addOp
} | bitShiftOp

// TODO: nonassoc named unary operators
// TODO: left << >>

// left << >>
lazy val bitShiftOp: PackratParser[AST] = bitShiftOp ~ "<<|>>".r ~ addOp ^^ {
case left ~ op ~ right => BinaryOpNode(left, op, right)
} | addOp

// left + - .
lazy val addOp: PackratParser[AST] = addOp ~ "[-+.]".r ~ mulOp ^^ {
Expand Down
11 changes: 7 additions & 4 deletions src/main/scala/org/moe/parser/Parser.scala
Expand Up @@ -11,14 +11,17 @@ object MoeParsers extends Statements {
def getEntryPoint: Parser[AST] = statements

// Parser wrapper -- indicates the start node
def parseFromEntry(input: String): StatementsNode =
def parseFromEntry(input: String): StatementsNode = {
def error_msg(msg: String, next: Input) = "[" + next.pos + "] error: " + msg + "\n\n" + next.pos.longString

parseAll(getEntryPoint, input) match {
case Success(result, _) => result.asInstanceOf[StatementsNode]
case failure : NoSuccess => if (failure.next.atEnd)
throw new MoeErrors.ParserInputIncomplete(failure.msg)
case NoSuccess(msg, next) => if (next.atEnd)
throw new MoeErrors.ParserInputIncomplete(error_msg(msg, next))
else
throw new MoeErrors.ParserInputError(failure.msg)
throw new MoeErrors.ParserInputError(error_msg(msg, next))
}
}
}

object Parser {
Expand Down
2 changes: 1 addition & 1 deletion src/main/scala/org/moe/runtime/MoeLazyEval.scala
Expand Up @@ -16,7 +16,7 @@ class MoeLazyEval (
node: AST
) extends MoeObject {
def eval = {
println("lazy eval - " + Serializer.toJSONPretty(node))
// println("lazy eval - " + Serializer.toJSONPretty(node))
interpreter.eval(runtime, env, node)
}
}
23 changes: 23 additions & 0 deletions src/main/scala/org/moe/runtime/builtins/BoolClass.scala
Expand Up @@ -29,6 +29,29 @@ object BoolClass {
)
)

// ternary operator

boolClass.addMethod(
new MoeMethod(
"infix:<?:>",
new MoeSignature(List(new MoeParameter("$trueExpr"), new MoeParameter("$falseExpr"))),
env,
{ (e) =>
val inv = self(e)
if (inv.isTrue)
e.get("$trueExpr").get match {
case deferredExpr: MoeLazyEval => deferredExpr.eval
case expr: MoeObject => expr
}
else
e.get("$falseExpr").get match {
case deferredExpr: MoeLazyEval => deferredExpr.eval
case expr: MoeObject => expr
}
}
)
)

/**
* List of Methods to support:
*
Expand Down
18 changes: 18 additions & 0 deletions src/main/scala/org/moe/runtime/builtins/IntClass.scala
Expand Up @@ -238,6 +238,24 @@ object IntClass {
)
)

intClass.addMethod(
new MoeMethod(
"infix:<<<>",
new MoeSignature(List(new MoeParameter("$other"))),
env,
(e) => e.getCurrentInvocant.get.asInstanceOf[MoeIntObject].bit_shift_left(r, e.get("$other").get)
)
)

intClass.addMethod(
new MoeMethod(
"infix:<>>>",
new MoeSignature(List(new MoeParameter("$other"))),
env,
(e) => e.getCurrentInvocant.get.asInstanceOf[MoeIntObject].bit_shift_right(r, e.get("$other").get)
)
)

// methods

intClass.addMethod(
Expand Down
Expand Up @@ -109,6 +109,14 @@ class MoeIntObject(
getNativeValue ^ other.unboxToInt.get
)

def bit_shift_left (r: MoeRuntime, other: MoeObject): MoeIntObject = r.NativeObjects.getInt(
getNativeValue << other.unboxToInt.get
)

def bit_shift_right (r: MoeRuntime, other: MoeObject): MoeIntObject = r.NativeObjects.getInt(
getNativeValue >> other.unboxToInt.get
)

// methods

def abs (r: MoeRuntime): MoeIntObject = r.NativeObjects.getInt(Math.abs(getNativeValue))
Expand Down
36 changes: 36 additions & 0 deletions src/test/scala/org/moe/parser/SimpleExpressionBoolTestSuite.scala
Expand Up @@ -109,4 +109,40 @@ class SimpleExpressionBoolTestSuite extends FunSuite with BeforeAndAfter with Pa
result.unboxToInt.get should equal (0)
}

// ternary operator

test("... ternary operator ... [1 == 1 ? 2 : 3]") {
val result = interpretCode("1 == 1 ? 2 : 3")
result.unboxToInt.get should equal (2)
}

test("... ternary operator ... [1 == 2 ? 2 : 3]") {
val result = interpretCode("1 == 2 ? 2 : 3")
result.unboxToInt.get should equal (3)
}

test("... ternary operator (right assoc) ... [1 == 1 ? 2 == 2 ? 3 : 4 : 5]") {
val result = interpretCode("1 == 1 ? 2 == 2 ? 3 : 4 : 5")
result.unboxToInt.get should equal (3)
}

test("... ternary operator (right assoc) ... [1 == 1 ? 2 != 2 ? 3 : 4 : 5]") {
val result = interpretCode("1 == 1 ? 2 != 2 ? 3 : 4 : 5")
result.unboxToInt.get should equal (4)
}

test("... ternary operator (right assoc) ... [1 != 1 ? 2 == 2 ? 3 : 4 : 5]") {
val result = interpretCode("1 != 1 ? 2 == 2 ? 3 : 4 : 5")
result.unboxToInt.get should equal (5)
}

test("... ternary operator (right assoc) ... [1 != 1 ? 2 : 3 == 3 ? 4 : 5]") {
val result = interpretCode("1 != 1 ? 2 : 3 == 3 ? 4 : 5")
result.unboxToInt.get should equal (4)
}

test("... ternary operator (right assoc) ... [1 != 1 ? 2 : 3 != 3 ? 4 : 5]") {
val result = interpretCode("1 != 1 ? 2 : 3 != 3 ? 4 : 5")
result.unboxToInt.get should equal (5)
}
}
25 changes: 25 additions & 0 deletions src/test/scala/org/moe/parser/SimpleExpressionTestSuite.scala
Expand Up @@ -411,6 +411,31 @@ class SimpleExpressionTestSuite extends FunSuite with BeforeAndAfter with Parser
result.unboxToInt.get should equal (0xbfed)
}

test("... literal int bitwise left shift ... [32 << 1]") {
val result = interpretCode("32 << 1")
result.unboxToInt.get should equal (64)
}

test("... literal int bitwise left shift ... [257 << 7]") {
val result = interpretCode("257 << 7")
result.unboxToInt.get should equal (32896)
}

test("... literal int bitwise left shift ... [20 << 20]") {
val result = interpretCode("20 << 20")
result.unboxToInt.get should equal (20971520)
}

test("... literal int bitwise right shift ... [32 >> 1]") {
val result = interpretCode("32 >> 1")
result.unboxToInt.get should equal (16)
}

test("... literal int bitwise right shift ... [33023 >> 7]") {
val result = interpretCode("33023 >> 7")
result.unboxToInt.get should equal (257)
}

// methods

test("... literal int abs method ... [2->abs]") {
Expand Down

0 comments on commit 97a6b07

Please sign in to comment.