Skip to content

Commit

Permalink
Add support for list element deletion
Browse files Browse the repository at this point in the history
  • Loading branch information
melvic-ybanez committed Nov 3, 2023
1 parent e988390 commit 1a8dd6f
Show file tree
Hide file tree
Showing 6 changed files with 51 additions and 22 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ like this:
[Success] Ducks should quack!
[Success] Denji should say 'Woof!'
[Success] Class properties should be updated
Ran 158 tests. Successful: 158. Failed: 0.
Ran 163 tests. Successful: 163. Failed: 0.
```

The tests themselves are written in Dry (while the `testDry` command is written in Scala). You can see the directory
Expand Down
16 changes: 2 additions & 14 deletions src/main/scala/com/melvic/dry/interpreter/eval/EvalExpr.scala
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ private[eval] trait EvalExpr {
case (dict: DDictionary, evaluatedKey) =>
Result.fromOption(dict.getByKey(evaluatedKey), RuntimeError.undefinedKey(key, token))
case (sequence: DSequence, evaluatedKey) =>
accessNumericIndex(evaluatedKey, key, sequence.size, token)(sequence.getByIndex(_).ok)
Evaluate.accessNumericIndex(evaluatedKey, key, sequence.size, token)(sequence.getByIndex(_).ok)
}
}

Expand All @@ -223,7 +223,7 @@ private[eval] trait EvalExpr {
case (dict: DDictionary, evaluatedKey) =>
Evaluate.expr(value).map(dict.setByKey(evaluatedKey, _))
case (list: DList, evaluatedKey) =>
accessNumericIndex(evaluatedKey, key, list.size, token) { index =>
Evaluate.accessNumericIndex(evaluatedKey, key, list.size, token) { index =>
Evaluate.expr(value).map(list.setByIndex(index, _))
}
}
Expand Down Expand Up @@ -253,18 +253,6 @@ private[eval] trait EvalExpr {
dictFields.map(fields => DDictionary(fields.to(mutable.Map), env))
}

private[eval] def accessNumericIndex(evaluatedKey: Value, key: IndexKey, limit: Int, token: Token)(
access: Int => Out
): Out =
evaluatedKey match {
case Value.Num(index) if index % 1 == 0 =>
val intIndex = index.toInt
if (index < 0 || index >= limit)
RuntimeError.indexOutOfBounds(intIndex, token.line).fail[Value]
else access(intIndex)
case _ => RuntimeError.invalidIndex(key, token).fail[Value]
}

private[eval] def exprList(elems: List[Expr])(implicit context: Context[Expr]): Result[List[Value]] =
elems.foldFailFast(Result.succeed(List.empty[Value])) { (result, elem) =>
Evaluate.expr(elem).map(_ :: result)
Expand Down
9 changes: 6 additions & 3 deletions src/main/scala/com/melvic/dry/interpreter/eval/EvalStmt.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import com.melvic.dry.interpreter.Value.{Returned, Unit => VUnit}
import com.melvic.dry.interpreter.eval.Context.implicits._
import com.melvic.dry.interpreter.eval.Evaluate.Out
import com.melvic.dry.interpreter.values.Value.ToValue
import com.melvic.dry.interpreter.values.{DDictionary, DModule, Value}
import com.melvic.dry.interpreter.values.{DDictionary, DList, DModule, Value}
import com.melvic.dry.interpreter.{Env, ModuleManager, Run}
import com.melvic.dry.result.Failure.RuntimeError
import com.melvic.dry.result.Result
Expand Down Expand Up @@ -79,8 +79,11 @@ private[eval] trait EvalStmt {

def deleteStmt(implicit context: Context[DeleteStmt]): Out = node match {
case DeleteStmt(obj, key, token) =>
Evaluate.index(obj, key, token) { case (dict: DDictionary, evaluatedKey) =>
Result.fromOption(dict.deleteByKey(evaluatedKey), RuntimeError.undefinedKey(key, token))
Evaluate.index(obj, key, token) {
case (dict: DDictionary, evaluatedKey) =>
Result.fromOption(dict.deleteByKey(evaluatedKey), RuntimeError.undefinedKey(key, token))
case (list: DList, evaluatedKey) =>
Evaluate.accessNumericIndex(evaluatedKey, key, list.size, token)(list.deleteByIndex(_).ok)
}
}

Expand Down
15 changes: 15 additions & 0 deletions src/main/scala/com/melvic/dry/interpreter/eval/Evaluate.scala
Original file line number Diff line number Diff line change
@@ -1,8 +1,23 @@
package com.melvic.dry.interpreter.eval

import com.melvic.dry.Token
import com.melvic.dry.ast.Expr.IndexKey
import com.melvic.dry.interpreter.values.Value
import com.melvic.dry.result.Failure.RuntimeError
import com.melvic.dry.result.Result.Result

object Evaluate extends EvalExpr with EvalDecl {
type Out = Result[Value]

private[eval] def accessNumericIndex(evaluatedKey: Value, key: IndexKey, limit: Int, token: Token)(
access: Int => Out
): Out =
evaluatedKey match {
case Value.Num(index) if index % 1 == 0 =>
val intIndex = index.toInt
if (index < 0 || index >= limit)
RuntimeError.indexOutOfBounds(intIndex, token.line).fail[Value]
else access(intIndex)
case _ => RuntimeError.invalidIndex(key, token).fail[Value]
}
}
3 changes: 3 additions & 0 deletions src/main/scala/com/melvic/dry/interpreter/values/DList.scala
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,7 @@ final case class DList(elems: ListBuffer[Value], env: Env) extends DSequence {

def setByIndex(index: Int, value: Value): Value =
(elems(index) = value).unit

def deleteByIndex(index: Int): Value =
elems.remove(index).unit
}
28 changes: 24 additions & 4 deletions tests/data_structs/test_lists.dry
Original file line number Diff line number Diff line change
Expand Up @@ -35,21 +35,40 @@ class TestList {
xs[""] = 20;
});
assert_error("Updating list with index < 0", Errors.INDEX_OUT_OF_BOUNDS, lambda() {
xs[-1];
xs[-1] = 0;
});

assert_error("Updating list with index == size", Errors.INDEX_OUT_OF_BOUNDS, lambda() {
xs[3];
xs[3] = 19;
});
assert_error("Updating list with index > size", Errors.INDEX_OUT_OF_BOUNDS, lambda() {
xs[4];
xs[4] = 1;
});

xs[0] = 10;
xs[1] = 2;
assert_equals("Updating list successfully", xs, [10, 2, 3]);
}

def test_delete() {
let xs = ["apple", "banana", "orange"];
assert_error("Deleting a list element with string index", Errors.INVALID_INDEX, lambda() {
del xs[""];
});
assert_error("Deleting a list element with index < 0", Errors.INDEX_OUT_OF_BOUNDS, lambda() {
del xs[-1];
});

assert_error("Deleting a list element with index == size", Errors.INDEX_OUT_OF_BOUNDS, lambda() {
del xs[3];
});
assert_error("Deleting a list element with index > size", Errors.INDEX_OUT_OF_BOUNDS, lambda() {
del xs[4];
});
del xs[1];
assert_equals("Deleting a list element successfully", xs, ["apple", "orange"]);
}

def test_size() {
let xs = [1, 2, 3, 4, 5];
assert_equals("Non-empty list size", xs.size(), 5);
Expand All @@ -73,4 +92,5 @@ test_list.test_get();
test_list.test_size();
test_list.test_type();
test_list.test_add_method();
test_list.test_set();
test_list.test_set();
test_list.test_delete();

0 comments on commit 1a8dd6f

Please sign in to comment.