-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Bracket notation for list element access
- Loading branch information
1 parent
da2cf21
commit 75ec614
Showing
9 changed files
with
80 additions
and
121 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
15 changes: 0 additions & 15 deletions
15
src/main/scala/com/melvic/dry/interpreter/values/Collections.scala
This file was deleted.
Oops, something went wrong.
13 changes: 13 additions & 0 deletions
13
src/main/scala/com/melvic/dry/interpreter/values/Countable.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package com.melvic.dry.interpreter.values | ||
|
||
import com.melvic.dry.interpreter.Env | ||
import com.melvic.dry.result.Result.implicits.ToResult | ||
|
||
trait Countable { | ||
def size: Int | ||
|
||
def addSizeMethod(env: Env): AddProperty = | ||
_ + ("size" -> Callable.noArg(env) { | ||
Value.Num(size).ok | ||
}) | ||
} |
3 changes: 0 additions & 3 deletions
3
src/main/scala/com/melvic/dry/interpreter/values/DDictionary.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
44 changes: 5 additions & 39 deletions
44
src/main/scala/com/melvic/dry/interpreter/values/DList.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,56 +1,22 @@ | ||
package com.melvic.dry.interpreter.values | ||
|
||
import com.melvic.dry.Token | ||
import com.melvic.dry.Token.TokenType | ||
import com.melvic.dry.ast.Expr | ||
import com.melvic.dry.interpreter.Env | ||
import com.melvic.dry.result.Failure.RuntimeError | ||
import com.melvic.dry.result.Result | ||
|
||
import scala.collection.mutable | ||
import scala.collection.mutable.ListBuffer | ||
import scala.util.chaining.scalaUtilChainingOps | ||
|
||
final case class DList(elems: ListBuffer[Value], env: Env) extends DObject with Collections.Countable { | ||
final case class DList(elems: ListBuffer[Value], env: Env) extends DSequence { | ||
override def klass: Metaclass = DClass("List", Map.empty, env) | ||
|
||
override val fields: mutable.Map[String, Value] = | ||
addIndexFields() | ||
.pipe(addAtMethod) | ||
.pipe(addSizeMethod(env)) | ||
.pipe(addAddMethod) | ||
addSizeMethod(env) | ||
.andThen(addAddMethod)(Map.empty) | ||
.to(mutable.Map) | ||
|
||
// TODO: Replace with bracket-notation | ||
private def addIndexFields() = | ||
elems.zipWithIndex | ||
.map { case (elem, i) => ("_" + i) -> elem } | ||
.to(Map) | ||
|
||
// TODO: Replace with bracket-notation | ||
private def addAtMethod: AddProperty = | ||
_ + ("at" -> Callable.withLineNo(1, env)(line => { case indexValue :: _ => | ||
// This is temporary. We are going to replace this whole function | ||
// with the [] operator anyway | ||
val valueString = Value.show(indexValue) | ||
|
||
indexValue.toNum | ||
.fold( | ||
RuntimeError | ||
.invalidIndex( | ||
// again, these values are temporary and should be removed | ||
Left(Expr.Literal.Str(valueString)), | ||
Token(TokenType.Str(valueString), valueString, line) | ||
) | ||
.fail[Value] | ||
) { num => | ||
val index = num.value.toInt | ||
Result.fromOption(elems.lift(index), RuntimeError.indexOutOfBounds(index, line)) | ||
} | ||
})) | ||
|
||
private def addAddMethod: AddProperty = | ||
_ + ("add" -> Callable.unarySuccess(env)(elem => copy(elems = elems += elem))) | ||
|
||
override def size: Int = elems.size | ||
|
||
override def getByIndex(index: Int): Value = elems(index) | ||
} |
5 changes: 5 additions & 0 deletions
5
src/main/scala/com/melvic/dry/interpreter/values/DSequence.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
package com.melvic.dry.interpreter.values | ||
|
||
trait DSequence extends DObject with Countable { | ||
def getByIndex(index: Int): Value | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,59 +1,53 @@ | ||
def test_list_str() { | ||
let xs = [1, "two", 3]; | ||
assert_equals("Stringify lists", str(xs), "[1, two, 3]"); | ||
assert_equals("Empty list string representation", str([]), "[]"); | ||
class TestList { | ||
def test_str() { | ||
let xs = [1, "two", 3]; | ||
assert_equals("Stringify lists", str(xs), "[1, two, 3]"); | ||
assert_equals("Empty list string representation", str([]), "[]"); | ||
} | ||
|
||
def test_get() { | ||
let xs = [1, "two", 3]; | ||
|
||
assert_equals("First element", xs[0], 1); | ||
assert_equals("Second element", xs[1], "two"); | ||
assert_equals("Third element", xs[2], 3); | ||
|
||
let empty = []; | ||
assert_error("Reading list elements with string index", Errors.INVALID_INDEX, lambda() { | ||
empty[""]; | ||
}); | ||
assert_error("Reading list elements with index < 0", Errors.INDEX_OUT_OF_BOUNDS, lambda() { | ||
empty[-1]; | ||
}); | ||
|
||
assert_error("Reading list elements with index == size", Errors.INDEX_OUT_OF_BOUNDS, lambda() { | ||
xs[3]; | ||
}); | ||
assert_error("Reading list elements with index > size", Errors.INDEX_OUT_OF_BOUNDS, lambda() { | ||
xs[4]; | ||
}); | ||
} | ||
|
||
def test_size() { | ||
let xs = [1, 2, 3, 4, 5]; | ||
assert_equals("Non-empty list size", xs.size(), 5); | ||
assert_equals("Empty list size", [].size(), 0); | ||
} | ||
|
||
def test_add_method() { | ||
let xs = ["one"]; | ||
assert_equals("New element is added to the list", str(xs.add("two")), "[one, two]"); | ||
assert_equals("Size should increase after adding new item", xs.size(), 2); | ||
} | ||
|
||
def test_type() { | ||
assert_equals("List type", typeof([1, 2]), "list"); | ||
} | ||
} | ||
|
||
def test_list_index() { | ||
let xs = [1, "two", 3]; | ||
|
||
assert_equals("First element", xs._0, 1); | ||
assert_equals("Second element", xs._1, "two"); | ||
assert_equals("Third element", xs._2, 3); | ||
|
||
xs._0 = 10; | ||
assert_equals("Updated first element", xs._0, 10); | ||
} | ||
|
||
def test_list_size() { | ||
let xs = [1, 2, 3, 4, 5]; | ||
assert_equals("Non-empty list size", xs.size(), 5); | ||
assert_equals("Empty list size", [].size(), 0); | ||
} | ||
|
||
def test_list_at_method() { | ||
let xs = ["first", "second", "third"]; | ||
let success_count = 0; | ||
|
||
if (xs._0 == xs.at(0)) success_count = success_count + 1; | ||
if (xs._1 == xs.at(1)) success_count = success_count + 1; | ||
if (xs._2 == xs.at(2)) success_count = success_count + 1; | ||
|
||
// This description is a lie though | ||
assert_equals("For all list `xs` and integer `i`, `xs.at(i)` is equal to `xs._<i>`", success_count, 3); | ||
} | ||
|
||
def test_list_add_method() { | ||
let xs = ["one"]; | ||
assert_equals("New element is added to the list", str(xs.add("two")), "[one, two]"); | ||
assert_equals("Size should increase after adding new item", xs.size(), 2); | ||
} | ||
|
||
def test_out_of_bounds() { | ||
assert_error("Index out of bounds", Errors.INDEX_OUT_OF_BOUNDS, lambda() { | ||
let xs = [1]; | ||
xs.at(2); | ||
}); | ||
} | ||
|
||
def test_list_type() { | ||
assert_equals("List type", typeof([1, 2]), "list"); | ||
} | ||
|
||
test_list_str(); | ||
test_list_index(); | ||
test_list_size(); | ||
test_list_at_method(); | ||
test_out_of_bounds(); | ||
test_list_type(); | ||
test_list_add_method(); | ||
let test_list = TestList(); | ||
test_list.test_str(); | ||
test_list.test_get(); | ||
test_list.test_size(); | ||
test_list.test_type(); | ||
test_list.test_add_method(); |