From a68a67376127cb9accf26c4ed43438f188eb24c8 Mon Sep 17 00:00:00 2001 From: Kaz Kylheku Date: Thu, 6 Mar 2025 21:42:50 -0800 Subject: 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, ... --- lib.c | 35 +++++++++++++++++++++-------------- tests/012/iter.tl | 24 ++++++++++++++++++++++++ 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) -- cgit v1.2.3