summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKaz Kylheku <kaz@kylheku.com>2025-03-06 21:42:50 -0800
committerKaz Kylheku <kaz@kylheku.com>2025-03-06 21:42:50 -0800
commita68a67376127cb9accf26c4ed43438f188eb24c8 (patch)
tree5afc06409f0afdca8dbf162519e5ee2bd4bbf195
parent862677be200fce120af136a392a780eb4816fa64 (diff)
downloadtxr-a68a67376127cb9accf26c4ed43438f188eb24c8.tar.gz
txr-a68a67376127cb9accf26c4ed43438f188eb24c8.tar.bz2
txr-a68a67376127cb9accf26c4ed43438f188eb24c8.zip
iterator API: reject objects that don't make sense.
* lib.c (iter_more): Do not return t for unrecognized objects, but thrown an exception. Do return t for conses, which is necessary since they are iterators for lists. Also, as a special case, we return t for struct objects that don't have an iter-more method. This is needed for the documented fast protocol. Iterator objects implementing the fast protocol still get iter-more invoked on them. The client usually doesn't know that the iterator implements the fast protocol, and so calls iter-more, which unconditionally has to returns true. (iter_item): Do not fall back on car(iter) for all unhandled objects. Only conses are handled via car. All unrecognized objects trigger an exception. (iter_step): Do not try to handle list-like objects via cdr, only lists. Improve the diagnostic for hitting the end of an improper list: diagnostic shows the cons cell rather than just the terminating atom. * tests/012/iter.tl: Some test cases validating that the functions error out for strings and vectors. Much more coverage is possible here but doesn't seem worth it; e.g. that the functions reject a buffer, regex, function, ...
-rw-r--r--lib.c35
-rw-r--r--tests/012/iter.tl24
2 files changed, 45 insertions, 14 deletions
diff --git a/lib.c b/lib.c
index ed0a47fe..1e127e88 100644
--- a/lib.c
+++ b/lib.c
@@ -1287,6 +1287,8 @@ static val iter_dynamic(struct seq_iter *si_orig)
val iter_more(val iter)
{
+ val self = lit("iter-more");
+
switch (type(iter)) {
case NIL:
return nil;
@@ -1295,6 +1297,8 @@ val iter_more(val iter)
case NUM:
case BGNUM:
case FLNUM:
+ case CONS:
+ case LCONS:
return t;
case COBJ:
if (iter->co.cls == seq_iter_cls)
@@ -1307,15 +1311,19 @@ val iter_more(val iter)
val iter_more_meth = get_special_slot(iter, iter_more_m);
if (iter_more_meth)
return funcall1(iter_more_meth, iter);
+ return t; /* needed for supporting fast procotol */
}
- /* fallthrough */
default:
- return t;
+ break;
}
+
+ unsup_obj(self, iter);
}
val iter_item(val iter)
{
+ val self = lit("iter-item");
+
switch (type(iter)) {
case NIL:
return nil;
@@ -1324,6 +1332,9 @@ val iter_item(val iter)
case BGNUM:
case FLNUM:
return iter;
+ case CONS:
+ case LCONS:
+ return car(iter);
case COBJ:
if (iter->co.cls == seq_iter_cls)
{
@@ -1338,8 +1349,10 @@ val iter_item(val iter)
}
/* fallthrough */
default:
- return car(iter);
+ break;
}
+
+ unsup_obj(self, iter);
}
val iter_step(val iter)
@@ -1359,7 +1372,8 @@ val iter_step(val iter)
{
val next = cdr(iter);
if (next && !consp(next))
- uw_throwf(type_error_s, lit("~a: ~s is not a cons"), self, next, nao);
+ uw_throwf(type_error_s, lit("~a: ~s is improperly terminated"),
+ self, iter, nao);
return next;
}
case COBJ:
@@ -1379,17 +1393,10 @@ val iter_step(val iter)
}
/* fallthrough */
default:
- {
- val next = cdr(iter);
- if (next) {
- seq_info_t sinf = seq_info(next);
- if (sinf.kind != SEQ_LISTLIKE)
- uw_throwf(type_error_s, lit("~a: ~s is improperly terminated"),
- self, iter, nao);
- }
- return next;
- }
+ break;
}
+
+ unsup_obj(self, iter);
}
val iter_reset(val iter, val obj)
diff --git a/tests/012/iter.tl b/tests/012/iter.tl
index 9b12d49c..94a50f34 100644
--- a/tests/012/iter.tl
+++ b/tests/012/iter.tl
@@ -130,3 +130,27 @@
(equal l0 l1) t
(equal l1 l2) nil
(equal (cdr l1) l2) t)))
+
+(mtest
+ (iter-more "") :error
+ (iter-more "a") :error
+ (iter-more "abc") :error
+ (iter-more #()) :error
+ (iter-more #(1)) :error
+ (iter-more #(1 2 3)) :error)
+
+(mtest
+ (iter-step "") :error
+ (iter-step "a") :error
+ (iter-step "abc") :error
+ (iter-step #()) :error
+ (iter-step #(1)) :error
+ (iter-step #(1 2 3)) :error)
+
+(mtest
+ (iter-item "") :error
+ (iter-item "a") :error
+ (iter-item "abc") :error
+ (iter-item #()) :error
+ (iter-item #(1)) :error
+ (iter-item #(1 2 3)) :error)