summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ChangeLog26
-rw-r--r--eval.c228
-rw-r--r--lib.c62
-rw-r--r--lib.h4
-rw-r--r--txr.1140
-rw-r--r--txr.vim7
6 files changed, 451 insertions, 16 deletions
diff --git a/ChangeLog b/ChangeLog
index 11db5c38..00152406 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,29 @@
+2012-01-10 Kaz Kylheku <kaz@kylheku.com>
+
+ Spat of new features having to do with lazy processing.
+
+ * eval.c (prog1_s, gen_s, generate_s, delay_s, promise_s): New symbol
+ variables.
+ (eval_prog1, op_prog1, expand_gen, expand_delay): New static functions.
+ (expand): Handle gen and delay.
+ (lazy_mapcar_func, lazy_mapcar, lazy_mapcarv_func, lazy_mapcarv,
+ lazy_mappendv): New static functions.
+ (rangev_func, rangev, generate_func, generate, repeat_infinite_func,
+ repeat_times_func, repeatv, force): New static functions.
+ (eval_init): New operators and functions interned.
+ lazy-flatten renamed to flatten*.
+
+ * lib.c (null_f): New global variable.
+ (ltail, lazy_appendv): New functions.
+ (lazy_appendv_func): New static function.
+ (obj_init): null_f protected and initialized.
+
+ * lib.h (null_f, ltail, lazy_appendv): Declared.
+
+ * txr.1: Documented.
+
+ * txr.vim: Updated.
+
2012-01-09 Kaz Kylheku <kaz@kylheku.com>
Non-broken way to achieve intent of previous commit.
diff --git a/eval.c b/eval.c
index 11998a81..3798dca5 100644
--- a/eval.c
+++ b/eval.c
@@ -51,11 +51,12 @@ val top_vb, top_fb;
val op_table;
val eval_error_s;
-val progn_s, let_s, let_star_s, lambda_s, call_s;
+val progn_s, prog1_s, let_s, let_star_s, lambda_s, call_s;
val cond_s, if_s, defvar_s, defun_s;
val inc_s, dec_s, push_s, pop_s, flip_s, gethash_s, car_s, cdr_s, vecref_s;
val for_s, for_star_s, dohash_s, uw_protect_s, return_s, return_from_s;
-val list_s, append_s, apply_s;
+val list_s, append_s, apply_s, gen_s, generate_s;
+val delay_s, promise_s;
val make_env(val vbindings, val fbindings, val up_env)
{
@@ -373,6 +374,21 @@ val eval_progn(val forms, val env, val ctx_form)
return retval;
}
+static val eval_prog1(val forms, val env, val ctx_form)
+{
+ val retval = nil;
+
+ if (forms) {
+ retval = eval(car(forms), env, ctx_form);
+ forms = cdr(forms);
+ }
+
+ for (; forms; forms = cdr(forms))
+ eval(car(forms), env, ctx_form);
+
+ return retval;
+}
+
static val op_quote(val form, val env)
{
return second(form);
@@ -430,6 +446,11 @@ static val op_progn(val form, val env)
return eval_progn(rest(form), env, form);
}
+static val op_prog1(val form, val env)
+{
+ return eval_prog1(rest(form), env, form);
+}
+
static val op_let(val form, val env)
{
val let = first(form);
@@ -968,6 +989,20 @@ static val expand_quasi(val quasi_forms)
}
}
+static val expand_gen(val args)
+{
+ return list(generate_s,
+ list(lambda_s, nil, first(args), nao),
+ list(lambda_s, nil, second(args), nao), nao);
+}
+
+static val expand_delay(val args)
+{
+ return list(cons_s,
+ cons(quote_s, cons(promise_s, nil)),
+ cons(lambda_s, cons(nil, args)), nao);
+}
+
val expand(val form)
{
if (atom(form)) {
@@ -1089,9 +1124,14 @@ val expand(val form)
if (quasi == quasi_ex)
return form;
return rlcp(cons(sym, quasi_ex), form);
+ } else if (sym == gen_s) {
+ return expand(expand_gen(rest(form)));
+ } else if (sym == delay_s) {
+ return expand(expand_delay(rest(form)));
} else {
- /* funtion call */
- /* also handles: progn, call, if, and, or, unwind-protect, return */
+ /* funtion call
+ also handles: progn, prog1, call, if, and, or,
+ unwind-protect, return */
val args = rest(form);
val args_ex = expand_forms(args);
@@ -1153,11 +1193,176 @@ static val mappendv(val fun, val list_of_lists)
}
}
+static val lazy_mapcar_func(val env, val lcons)
+{
+ cons_bind (fun, list, env);
+
+ rplaca(lcons, funcall1(fun, car(list)));
+ rplacd(env, cdr(list));
+
+ if (cdr(list))
+ rplacd(lcons, make_lazy_cons(lcons_fun(lcons)));
+ else
+ rplacd(lcons, nil);
+ return nil;
+}
+
+static val lazy_mapcar(val fun, val list)
+{
+ if (!list)
+ return nil;
+ return make_lazy_cons(func_f1(cons(fun, list), lazy_mapcar_func));
+}
+
+static val lazy_mapcarv_func(val env, val lcons)
+{
+ cons_bind (fun, lofl, env);
+ val args = mapcar(car_f, lofl);
+ val next = mapcar(cdr_f, lofl);
+
+ rplaca(lcons, apply(fun, args, nil));
+ rplacd(env, next);
+
+ if (all_satisfy(next, identity_f, identity_f))
+ rplacd(lcons, make_lazy_cons(lcons_fun(lcons)));
+ else
+ rplacd(lcons, nil);
+ return nil;
+}
+
+static val lazy_mapcarv(val fun, val list_of_lists)
+{
+ if (!cdr(list_of_lists)) {
+ return lazy_mapcar(fun, car(list_of_lists));
+ } else if (some_satisfy(list_of_lists, null_f, identity_f)) {
+ return nil;
+ } else {
+ val lofl = copy_list(list_of_lists);
+ return make_lazy_cons(func_f1(cons(fun, lofl), lazy_mapcarv_func));
+ }
+}
+
+static val lazy_mappendv(val fun, val list_of_lists)
+{
+ return lazy_appendv(lazy_mapcarv(fun, list_of_lists));
+}
+
static val symbol_function(val sym)
{
return lookup_fun(nil, sym);
}
+static val rangev_func(val env, val lcons)
+{
+ cons_bind (from, to_step, env);
+ cons_bind (to, step, to_step);
+
+ rplaca(lcons, from);
+
+ if (equal(from, to)) {
+ rplacd(lcons, nil);
+ return nil;
+ }
+
+ if (functionp(step))
+ rplaca(env, funcall1(step, from));
+ else
+ rplaca(env, plus(from, step));
+ rplacd(lcons, make_lazy_cons(lcons_fun(lcons)));
+ return nil;
+}
+
+static val rangev(val args)
+{
+ uses_or2;
+ val from = or2(first(args), zero);
+ val to = second(args);
+ val step = or2(third(args), one);
+ val env = cons(from, cons(to, step));
+
+ return make_lazy_cons(func_f1(env, rangev_func));
+}
+
+static val generate_func(val env, val lcons)
+{
+ cons_bind (while_pred, gen_fun, env);
+
+ if (!funcall(while_pred)) {
+ rplacd(lcons, nil);
+ } else {
+ val next_item = funcall(gen_fun);
+ val lcons_next = make_lazy_cons(lcons_fun(lcons));
+ rplacd(lcons, lcons_next);
+ rplaca(lcons_next, next_item);
+ }
+ return nil;
+}
+
+static val generate(val while_pred, val gen_fun)
+{
+ val first_item = funcall(gen_fun);
+ if (!funcall(while_pred)) {
+ return nil;
+ } else {
+ val lc = make_lazy_cons(func_f1(cons(while_pred, gen_fun), generate_func));
+ rplaca(lc, first_item);
+ return lc;
+ }
+}
+
+static val repeat_infinite_func(val env, val lcons)
+{
+ if (!car(env))
+ rplaca(env, cdr(env));
+ rplaca(lcons, pop(car_l(env)));
+ rplacd(lcons, make_lazy_cons(lcons_fun(lcons)));
+ return nil;
+}
+
+static val repeat_times_func(val env, val lcons)
+{
+ cons_bind (stack, list_count, env);
+ cons_bind (list, count, list_count);
+
+ if (!stack) {
+ rplaca(env, list);
+ rplacd(list_count, count = minus(count, one));
+ }
+
+ rplaca(lcons, pop(car_l(env)));
+
+ if (!car(env) && count == one) {
+ rplacd(lcons, nil);
+ return nil;
+ }
+
+ rplacd(lcons, make_lazy_cons(lcons_fun(lcons)));
+ return nil;
+}
+
+static val repeatv(val list, val rest)
+{
+ if (!list)
+ return nil;
+ if (rest) {
+ val count = car(rest);
+ if (count == zero)
+ return nil;
+ return make_lazy_cons(func_f1(cons(list, cons(list, count)),
+ repeat_times_func));
+ }
+ return make_lazy_cons(func_f1(cons(list, list), repeat_infinite_func));
+}
+
+static val force(val promise)
+{
+ if (car(promise) != promise_s)
+ return cdr(promise);
+
+ rplaca(promise, nil);
+ return rplacd(promise, funcall(cdr(promise)));
+}
+
static void reg_fun(val sym, val fun)
{
sethash(top_fb, sym, cons(sym, fun));
@@ -1176,6 +1381,7 @@ void eval_init(void)
op_table = make_hash(nil, nil, nil);
progn_s = intern(lit("progn"), user_package);
+ prog1_s = intern(lit("prog1"), user_package);
let_s = intern(lit("let"), user_package);
let_star_s = intern(lit("let*"), user_package);
lambda_s = intern(lit("lambda"), user_package);
@@ -1202,12 +1408,17 @@ void eval_init(void)
list_s = intern(lit("list"), user_package);
append_s = intern(lit("append"), user_package);
apply_s = intern(lit("apply"), user_package);
+ gen_s = intern(lit("gen"), user_package);
+ generate_s = intern(lit("generate"), user_package);
+ delay_s = intern(lit("delay"), user_package);
+ promise_s = intern(lit("promise"), system_package);
sethash(op_table, quote_s, cptr((mem_t *) op_quote));
sethash(op_table, qquote_s, cptr((mem_t *) op_qquote_error));
sethash(op_table, unquote_s, cptr((mem_t *) op_unquote_error));
sethash(op_table, splice_s, cptr((mem_t *) op_unquote_error));
sethash(op_table, progn_s, cptr((mem_t *) op_progn));
+ sethash(op_table, prog1_s, cptr((mem_t *) op_prog1));
sethash(op_table, let_s, cptr((mem_t *) op_let));
sethash(op_table, let_star_s, cptr((mem_t *) op_let));
sethash(op_table, lambda_s, cptr((mem_t *) op_lambda));
@@ -1257,7 +1468,9 @@ void eval_init(void)
reg_fun(intern(lit("length-list"), user_package), func_n1(length_list));
reg_fun(intern(lit("mapcar"), user_package), func_n1v(mapcarv));
+ reg_fun(intern(lit("mapcar*"), user_package), func_n1v(lazy_mapcarv));
reg_fun(intern(lit("mappend"), user_package), func_n1v(mappendv));
+ reg_fun(intern(lit("mappend*"), user_package), func_n1v(lazy_mappendv));
reg_fun(apply_s, func_n2(apply_intrinsic));
reg_fun(intern(lit("reduce-left"), user_package), func_n4(reduce_left));
reg_fun(intern(lit("reduce-right"), user_package), func_n4(reduce_right));
@@ -1272,7 +1485,7 @@ void eval_init(void)
reg_fun(intern(lit("reverse"), user_package), func_n1(reverse));
reg_fun(intern(lit("ldiff"), user_package), func_n2(ldiff));
reg_fun(intern(lit("flatten"), user_package), func_n1(flatten));
- reg_fun(intern(lit("lazy-flatten"), user_package), func_n1(lazy_flatten));
+ reg_fun(intern(lit("flatten*"), user_package), func_n1(lazy_flatten));
reg_fun(intern(lit("memq"), user_package), func_n2(memq));
reg_fun(intern(lit("memql"), user_package), func_n2(memql));
reg_fun(intern(lit("memqual"), user_package), func_n2(memqual));
@@ -1445,6 +1658,11 @@ void eval_init(void)
reg_fun(intern(lit("random-fixnum"), user_package), func_n1(random_fixnum));
reg_fun(intern(lit("random"), user_package), func_n2(random));
+ reg_fun(intern(lit("range"), user_package), func_n0v(rangev));
+ reg_fun(generate_s, func_n2(generate));
+ reg_fun(intern(lit("repeat"), user_package), func_n1v(repeatv));
+ reg_fun(intern(lit("force"), user_package), func_n1(force));
+
eval_error_s = intern(lit("eval-error"), user_package);
uw_register_subtype(eval_error_s, error_s);
}
diff --git a/lib.c b/lib.c
index c5afc004..12752ef4 100644
--- a/lib.c
+++ b/lib.c
@@ -82,7 +82,7 @@ val null_string;
val nil_string;
val null_list;
-val identity_f, equal_f, eql_f, eq_f, car_f, cdr_f;
+val identity_f, equal_f, eql_f, eq_f, car_f, cdr_f, null_f;
val gensym_counter;
@@ -308,6 +308,13 @@ val *tail(val cons)
return cdr_l(cons);
}
+val *ltail(val *cons)
+{
+ while (cdr(*cons))
+ cons = cdr_l(*cons);
+ return cons;
+}
+
val pop(val *plist)
{
val ret = car(*plist);
@@ -394,6 +401,56 @@ val nappend2(val list1, val list2)
return out;
}
+static val lazy_appendv_func(val env, val lcons)
+{
+ cons_bind (last, lists, env);
+ val nonempty = nil;
+
+ while (lists) {
+ nonempty = pop(&lists);
+ if (nonempty)
+ break;
+ }
+
+ rplaca(lcons, last);
+
+ if (atom(nonempty)) {
+ rplacd(lcons, nonempty);
+ return nil;
+ }
+
+ rplacd(env, lists);
+
+ {
+ val *ptail = ltail(&nonempty);
+ rplaca(env, car(*ptail));
+ *ptail = make_lazy_cons(lcons_fun(lcons));
+ rplacd(lcons, nonempty);
+ }
+ return nil;
+}
+
+val lazy_appendv(val lists)
+{
+ val nonempty = nil;
+
+ while (lists) {
+ nonempty = pop(&lists);
+ if (nonempty)
+ break;
+ }
+
+ if (atom(nonempty))
+ return nonempty;
+
+ {
+ val *ptail = ltail(&nonempty);
+ *ptail = make_lazy_cons(func_f1(cons(car(*ptail), lists),
+ lazy_appendv_func));
+ return nonempty;
+ }
+}
+
val ldiff(val list1, val list2)
{
list_collect_decl (out, ptail);
@@ -3166,7 +3223,7 @@ static void obj_init(void)
protect(&packages, &system_package, &keyword_package,
&user_package, &null_string, &nil_string,
- &null_list, &equal_f, &eq_f, &eql_f, &car_f, &cdr_f,
+ &null_list, &equal_f, &eq_f, &eql_f, &car_f, &cdr_f, &null_f,
&identity_f, &prog_string, &env_list,
(val *) 0);
@@ -3284,6 +3341,7 @@ static void obj_init(void)
identity_f = func_n1(identity);
car_f = func_n1(car);
cdr_f = func_n1(cdr);
+ null_f = func_n1(nullp);
gensym_counter = zero;
prog_string = string(progname);
}
diff --git a/lib.h b/lib.h
index 4c8d6e69..3d55a914 100644
--- a/lib.h
+++ b/lib.h
@@ -296,7 +296,7 @@ extern val nothrow_k, args_k;
extern val null_string;
extern val null_list; /* (nil) */
-extern val identity_f, equal_f, eql_f, eq_f, car_f, cdr_f;
+extern val identity_f, equal_f, eql_f, eq_f, car_f, cdr_f, null_f;
extern val gensym_counter;
@@ -325,6 +325,7 @@ val fourth(val cons);
val fifth(val cons);
val sixth(val cons);
val *tail(val cons);
+val *ltail(val *cons);
val pop(val *plist);
val push(val v, val *plist);
val copy_list(val list);
@@ -333,6 +334,7 @@ val reverse(val in);
val append2(val list1, val list2);
val nappend2(val list1, val list2);
val appendv(val lists);
+val lazy_appendv(val lists);
val ldiff(val list1, val list2);
val flatten(val list);
val lazy_flatten(val list);
diff --git a/txr.1 b/txr.1
index 030e0167..bfe68435 100644
--- a/txr.1
+++ b/txr.1
@@ -4394,12 +4394,13 @@ alternative1 | alternative2 | ... | alternativeN
Multiple syntactic variations allowed in one place are
indicated as bar-separated items.
-.SS Operator progn
+.SS Operators progn and prog1
.TP
Syntax:
(progn <form>*)
+ (prog1 <form>*)
.TP
Description
@@ -4407,6 +4408,9 @@ Description
The progn operator evaluates forms in order, and returns the value
of the last form. The return value of (progn) is nil.
+The prog1 operator evaluates forms in order, and returns the value
+of the first form. The return value of (prog1) is nil.
+
Various other operators such as let also arrange for the evaluation
of a body of forms, the value of the last of which is returned.
These operators are said to feature an "implicit progn".
@@ -4951,6 +4955,99 @@ Here, the output produced is "abc". The value of b is not printed
because the return-from terminates block foo, and so the second pprint
form is not evaluated.
+.SS Operator delay
+
+.TP
+Syntax:
+
+ (delay <expression>)
+
+.TP
+Description:
+
+The delay operator arranges for the delayed (or "lazy") evaluation of
+<expression>. This means that the expression is not evaluated immediately.
+Rather, the delay expression produces a promise object.
+
+The promise object can later be passed to the force function (described
+later in this document). The force function will trigger the evaluation
+of the expression and retrieve the value.
+
+The expression is evaluated in the original scope, no mater where
+the force takes place.
+
+The expression is evaluated at most once, by the first call to force.
+Additional calls to force only retrieve a cached value.
+
+.TP
+Example:
+
+ @(do
+ ;; list is popped only once: the value is computed
+ ;; just once when force is called on a given promise
+ ;; for the first time.
+
+ (defun get-it (promise)
+ (format t "*list* is ~s\en" *list*)
+ (format t "item is ~s\en" (force promise))
+ (format t "item is ~s\en" (force promise))
+ (format t "*list* is ~s\en" *list*))
+
+
+ (defvar *list* '(1 2 3))
+
+ (get-it (delay (pop *list*))))
+
+ Output:
+
+ *list* is (1 2 3)
+ item is 1
+ item is 1
+ *list* is (2 3)
+
+.SS Operator gen
+
+.TP
+Syntax:
+
+ (gen <while-expression> <produce-item-expression>)
+
+.TP
+Description:
+
+The gen operator produces a lazy list, in a manner similar to the generate
+function. Whereas the generate function (documented later in this manual)
+takes functional arguments, the gen operator takes two expressions, which is
+often more convenient.
+
+The return value of gen is a lazy list. When the lazy list is accessed, for
+instance with the functions car and cdr, it produces items on demand. Prior to
+producing each item, the <while-expression> is evaluated, in its original
+lexical scope. If the expression yields true, then <produce-item-expression>
+is evaluated, and its return value is incorporated as the next item of the lazy
+list. If the expression yields false, then the lazy list immediately
+terminates.
+
+The gen operator itself immediately evaluates <while-expression> before
+producing the lazy list. If the expression yields false, then the operator
+returns the empty list nil. Otherwise, it instantiates the lazy list and
+invokes the <produce-item-expression> to force the first item.
+
+.TP
+
+Example:
+
+ @(do
+ ;; Make a lazy list of integers up to 1000
+ ;; access and print the first three.
+ (let* ((counter 0)
+ (list (gen (< counter 1000) (inc counter))))
+ (format t "~s ~s ~s\en" (pop list) (pop list) (pop list))))
+
+ Output:
+ 1 2 3
+
+
.SS Lisp Functions and Variables
When the first element of a compound form is a symbol denoting a function,
@@ -5434,13 +5531,15 @@ Description:
The length-list function returns the length of a proper or improper
list. The length of a list is the number of conses in that list.
-.SS Functions mapcar and mappend
+.SS Functions mapcar, mappend, mapcar* and mappend*
.TP
Syntax:
(mapcar <function> <list> <list>*)
(mappend <function> <list> <list>*)
+ (mapcar* <function> <list> <list>*)
+ (mappend* <function> <list> <list>*)
.TP
Description:
@@ -5464,6 +5563,27 @@ are catenated with append, and the resulting list is returned.
That is to say, (mappend f a b c) is equivalent to
(apply (fun append) (mapcar f a b c)).
+The mapcar* and mappend* functions work like mapcar and mappend, respectively.
+However, they return lazy lists rather than generating the entire
+output list prior to returning.
+
+.TP
+Caveats:
+
+Like mappend, mappend* must "consume" empty lists. For instance,
+if the function being mapped put out a sequence of nil values,
+then the result must be the empty list nil, because
+(append nil nil nil nil ...) is nil.
+
+Suppose that mappend* is used on inputs which are infinite lazy
+lists, such that the function returns nil values indefinitely.
+For instance:
+
+ ;; Danger: infinite loop!!!
+ (mappend* (fun identity) (repeat '(nil)))
+
+The mappend* function is caught in a loop trying to consume
+and squash an infinite stream of nil values.
.TP
Examples:
@@ -5646,21 +5766,21 @@ Examples:
(ldiff a b))
-> (1)
-.SS Functions flatten, lazy-flatten
+.SS Functions flatten, flatten*
.TP
Syntax:
(flatten <list>)
- (lazy-flatten <list>)
+ (flatten* <list>)
.TP
Description:
The flatten function produces a list whose elements are all of the non-nil
-atoms contained in the structure of <list>. The lazy-flatten function
+atoms contained in the structure of <list>. The flatten* function
works like flatten except that flatten creates and returns a complete
-flattened list, whereas lazy-flatten produces a lazy list which is
+flattened list, whereas flatten* produces a lazy list which is
instantiated on demand. This is particularly useful when the input
structure is itself lazy.
@@ -6054,6 +6174,14 @@ Certain object types have a custom equal function.
.SS Functions random-fixnum and random
+.SS Function force
+
+.SS Function range
+
+.SS Function generate
+
+.SS Function repeat
+
.SH APPENDIX A: NOTES ON EXOTIC REGULAR EXPRESSIONS
Users familiar with regular expressions may not be familiar with the complement
diff --git a/txr.vim b/txr.vim
index fe9f7f7c..90a12203 100644
--- a/txr.vim
+++ b/txr.vim
@@ -25,16 +25,17 @@ syn keyword txr_keyword contained repeat rep first last single empty mod modlast
syn keyword txr_keyword contained define try catch finally throw
syn keyword txr_keyword contained defex throw deffilter filter eof eol do
-syn keyword txl_keyword contained progn let syn let* lambda call fun
+syn keyword txl_keyword contained progn prog1 let syn let* lambda call fun
syn keyword txl_keyword contained cond if and or
syn keyword txl_keyword contained defvar defun inc dec set push pop flip
syn keyword txl_keyword contained for for* dohash unwind-protect block
-syn keyword txl_keyword contained return return-from
+syn keyword txl_keyword contained return return-from gen delay
syn keyword txl_keyword contained cons make-lazy-cons lcons-fun car cdr
syn keyword txl_keyword contained rplaca rplacd first rest append list
syn keyword txl_keyword contained identity typeof atom null not consp listp
syn keyword txl_keyword contained proper-listp length-list mapcar mappend apply
+syn keyword txl_keyword contained mapcar* mappend*
syn keyword txl_keyword contained reduce-left reduce-right
syn keyword txl_keyword contained second third fourth fifth sixth copy-list nreverse
syn keyword txl_keyword contained reverse ldiff flatten lazy-flatten
@@ -76,6 +77,8 @@ syn keyword txl_keyword contained functionp interp-fun-p *random-state*
syn keyword txl_keyword contained make-random-state random-state-p
syn keyword txl_keyword contained random-fixnum random
+syn keyword txl_keyword contained range generate repeat force
+
syn match txr_hash "#" contained
syn match txr_quote "[,']" contained