Skip to content

Commit

Permalink
Merge pull request #56 from MoeOrganization/prakashk/string-ranges
Browse files Browse the repository at this point in the history
Support for string auto-increment and string ranges
  • Loading branch information
Stevan Little committed Feb 15, 2013
2 parents d88866e + 6ea3536 commit 6b35c35
Show file tree
Hide file tree
Showing 5 changed files with 290 additions and 15 deletions.
38 changes: 33 additions & 5 deletions src/main/scala/org/moe/interpreter/Interpreter.scala
Expand Up @@ -112,11 +112,34 @@ class Interpreter {
}

case RangeLiteralNode(start, end) => {
val range_start = objToInteger(eval(runtime, env, start))
val range_end = objToInteger(eval(runtime, env, end))
val range: Range = new Range(range_start, range_end + 1, 1)
val array: List[MoeObject] = range.toList.map(runtime.NativeObjects.getInt(_))
runtime.NativeObjects.getArray(array)
val s = eval(runtime, env, start)
val e = eval(runtime, env, end)
(s, e) match {
case (s: MoeIntObject, e: MoeIntObject) => {
val range_start = objToInteger(s)
val range_end = objToInteger(e)
val range: Range = new Range(range_start, range_end + 1, 1)
val array: List[MoeObject] = range.toList.map(runtime.NativeObjects.getInt(_))
runtime.NativeObjects.getArray(array)
}
case (s: MoeStringObject, e: MoeStringObject) => {
val range_start = objToString(s)
val range_end = objToString(e)

if (range_start.length > range_end.length)
runtime.NativeObjects.getArray()
else {
var elems: List[String] = List()
var str = range_start
while (str <= range_end || str.length < range_end.length) {
elems = elems :+ str
str = magicalStringIncrement(str)
}
runtime.NativeObjects.getArray(elems.map(runtime.NativeObjects.getString(_)))
}
}
case _ => throw new MoeErrors.UnexpectedType("Pair of MoeIntObject or MoeStringObject expected")
}
}

// unary operators
Expand All @@ -135,6 +158,11 @@ class Interpreter {
env.set(varName, new_n)
if (is_prefix) new_n else n
}
case s: MoeStringObject => {
val new_s = runtime.NativeObjects.getString(magicalStringIncrement(s.getNativeValue))
env.set(varName, new_s)
if (is_prefix) new_s else s
}
}
}
case DecrementNode(receiver: AST, is_prefix) => receiver match {
Expand Down
59 changes: 59 additions & 0 deletions src/main/scala/org/moe/interpreter/InterpreterUtils.scala
Expand Up @@ -178,5 +178,64 @@ object InterpreterUtils {
}
)
}

// strings matching the alphanumeric pattern /^[a-zA-Z]*[0-9]*\z/
// are incrementable

import scala.util.matching.Regex

def magicalStringIncrement(str: String): String = {
def incr_numeric_part(n: String) = {
val len = n.length
("%0" + len + "d").format(n.toInt + 1).toString
}

def incr_alpha_part(s: String) = {

def succ(c: Char, carry: Boolean): (Char, Boolean) = {
if (carry)
c match {
case 'z' => ('a', true)
case 'Z' => ('A', true)
case _ => ((c.toInt + 1).toChar, false)
}
else
(c, false)
}

var carry = true
val incremented = (for (c <- s.reverse) yield {
val succ_c = succ(c, carry)
carry = succ_c._2
succ_c._1
}).reverse
(if (carry) (incremented.head.toString ++ incremented) else incremented).mkString
}

def increment_in_parts(alpha: String, numeric: String) = {
if (alpha.isEmpty) {
// no alpha prefix; increment as numeric
incr_numeric_part(numeric)
} else if (numeric.isEmpty) {
// only alpha part
incr_alpha_part(alpha)
} else {
// both alpha and numeric parts exist. increment numeric
// part first; if it carries over then increment alpha part
val next_n = incr_numeric_part(numeric)
if (next_n.length == numeric.length)
alpha + next_n
else
incr_alpha_part(alpha) + next_n.tail
}
}

val alpha_numeric = """^([a-zA-Z]*)([0-9]*)\z""".r
str match {
case alpha_numeric(alpha, numeric) => increment_in_parts(alpha, numeric)
case _ => throw new MoeErrors.MoeException("string is not incrementable")
}
}

}

Expand Up @@ -45,7 +45,7 @@ class IncrementDecrementNodeTestSuite extends FunSuite with InterpreterTestUtils
"$foo",
IntLiteralNode(42)
),
IncrementNode(VariableAccessNode("$foo"), true),
IncrementNode(VariableAccessNode("$foo"), is_prefix = true),
VariableAccessNode("$foo")
)
)
Expand All @@ -60,7 +60,7 @@ class IncrementDecrementNodeTestSuite extends FunSuite with InterpreterTestUtils
"$foo",
IntLiteralNode(42)
),
IncrementNode(VariableAccessNode("$foo"),true)
IncrementNode(VariableAccessNode("$foo"), is_prefix = true)
)
)
val result = interpreter.eval(runtime, runtime.getRootEnv, ast)
Expand Down Expand Up @@ -105,7 +105,7 @@ class IncrementDecrementNodeTestSuite extends FunSuite with InterpreterTestUtils
"$foo",
FloatLiteralNode(98.6)
),
IncrementNode(VariableAccessNode("$foo"), true),
IncrementNode(VariableAccessNode("$foo"), is_prefix = true),
VariableAccessNode("$foo")
)
)
Expand All @@ -120,7 +120,7 @@ class IncrementDecrementNodeTestSuite extends FunSuite with InterpreterTestUtils
"$foo",
FloatLiteralNode(98.6)
),
IncrementNode(VariableAccessNode("$foo"), true)
IncrementNode(VariableAccessNode("$foo"), is_prefix = true)
)
)
val result = interpreter.eval(runtime, runtime.getRootEnv, ast)
Expand Down Expand Up @@ -165,7 +165,7 @@ class IncrementDecrementNodeTestSuite extends FunSuite with InterpreterTestUtils
"$foo",
IntLiteralNode(42)
),
DecrementNode(VariableAccessNode("$foo"), true),
DecrementNode(VariableAccessNode("$foo"), is_prefix = true),
VariableAccessNode("$foo")
)
)
Expand All @@ -180,7 +180,7 @@ class IncrementDecrementNodeTestSuite extends FunSuite with InterpreterTestUtils
"$foo",
IntLiteralNode(42)
),
DecrementNode(VariableAccessNode("$foo"),true)
DecrementNode(VariableAccessNode("$foo"), is_prefix = true)
)
)
val result = interpreter.eval(runtime, runtime.getRootEnv, ast)
Expand Down Expand Up @@ -225,7 +225,7 @@ class IncrementDecrementNodeTestSuite extends FunSuite with InterpreterTestUtils
"$foo",
FloatLiteralNode(98.6)
),
DecrementNode(VariableAccessNode("$foo"), true),
DecrementNode(VariableAccessNode("$foo"), is_prefix = true),
VariableAccessNode("$foo")
)
)
Expand All @@ -240,11 +240,109 @@ class IncrementDecrementNodeTestSuite extends FunSuite with InterpreterTestUtils
"$foo",
FloatLiteralNode(98.6)
),
DecrementNode(VariableAccessNode("$foo"), true)
DecrementNode(VariableAccessNode("$foo"), is_prefix = true)
)
)
val result = interpreter.eval(runtime, runtime.getRootEnv, ast)
assert(result.asInstanceOf[MoeFloatObject].getNativeValue === 97.6)
}

test("... basic test with variable increment (string)") {
val ast = wrapSimpleAST(
List(
VariableDeclarationNode(
"$foo",
StringLiteralNode("123")
),
IncrementNode(VariableAccessNode("$foo"), is_prefix = true)
)
)
val result = interpreter.eval(runtime, runtime.getRootEnv, ast)
assert(result.asInstanceOf[MoeStringObject].getNativeValue === "124")
}

test("... basic test with variable increment (string) #2") {
val ast = wrapSimpleAST(
List(
VariableDeclarationNode(
"$foo",
StringLiteralNode("99")
),
IncrementNode(VariableAccessNode("$foo"), is_prefix = true)
)
)
val result = interpreter.eval(runtime, runtime.getRootEnv, ast)
assert(result.asInstanceOf[MoeStringObject].getNativeValue === "100")
}

test("... basic test with variable increment (string) #3") {
val ast = wrapSimpleAST(
List(
VariableDeclarationNode(
"$foo",
StringLiteralNode("99")
),
IncrementNode(VariableAccessNode("$foo"), is_prefix = true)
)
)
val result = interpreter.eval(runtime, runtime.getRootEnv, ast)
assert(result.asInstanceOf[MoeStringObject].getNativeValue === "100")
}

test("... basic test with variable increment (string) #4") {
val ast = wrapSimpleAST(
List(
VariableDeclarationNode(
"$foo",
StringLiteralNode("a0")
),
IncrementNode(VariableAccessNode("$foo"), is_prefix = true)
)
)
val result = interpreter.eval(runtime, runtime.getRootEnv, ast)
assert(result.asInstanceOf[MoeStringObject].getNativeValue === "a1")
}

test("... basic test with variable increment (string) #5") {
val ast = wrapSimpleAST(
List(
VariableDeclarationNode(
"$foo",
StringLiteralNode("Az")
),
IncrementNode(VariableAccessNode("$foo"), is_prefix = true)
)
)
val result = interpreter.eval(runtime, runtime.getRootEnv, ast)
assert(result.asInstanceOf[MoeStringObject].getNativeValue === "Ba")
}

test("... basic test with variable increment (string) #6") {
val ast = wrapSimpleAST(
List(
VariableDeclarationNode(
"$foo",
StringLiteralNode("zz")
),
IncrementNode(VariableAccessNode("$foo"), is_prefix = true)
)
)
val result = interpreter.eval(runtime, runtime.getRootEnv, ast)
assert(result.asInstanceOf[MoeStringObject].getNativeValue === "aaa")
}

test("... basic test with variable increment (string) #7") {
val ast = wrapSimpleAST(
List(
VariableDeclarationNode(
"$foo",
StringLiteralNode("01")
),
IncrementNode(VariableAccessNode("$foo"), is_prefix = true)
)
)
val result = interpreter.eval(runtime, runtime.getRootEnv, ast)
assert(result.asInstanceOf[MoeStringObject].getNativeValue === "02")
}

}
Expand Up @@ -7,7 +7,7 @@ import org.scalatest.FunSuite

class RangeLiteralNodeTestSuite extends FunSuite with InterpreterTestUtils {

test("... basic test with Range") {
test("... basic test with Integer Range") {
val ast = wrapSimpleAST(
List(
RangeLiteralNode(
Expand All @@ -26,4 +26,23 @@ class RangeLiteralNodeTestSuite extends FunSuite with InterpreterTestUtils {
assert(array(2).asInstanceOf[MoeIntObject].getNativeValue === 3)
}

test("... basic test with String Range") {
val ast = wrapSimpleAST(
List(
RangeLiteralNode(
StringLiteralNode("a"),
StringLiteralNode("c")
)
)
)
val result = interpreter.eval(runtime, runtime.getRootEnv, ast)

val array: List[MoeObject] = result.asInstanceOf[MoeArrayObject].getNativeValue

assert(array.size === 3)
assert(array(0).asInstanceOf[MoeStringObject].getNativeValue === "a")
assert(array(1).asInstanceOf[MoeStringObject].getNativeValue === "b")
assert(array(2).asInstanceOf[MoeStringObject].getNativeValue === "c")
}

}

0 comments on commit 6b35c35

Please sign in to comment.