summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lisplib.c18
-rw-r--r--share/txr/stdlib/doloop.tl54
-rw-r--r--txr.1164
3 files changed, 236 insertions, 0 deletions
diff --git a/lisplib.c b/lisplib.c
index 9043577a..67946714 100644
--- a/lisplib.c
+++ b/lisplib.c
@@ -541,6 +541,23 @@ static val ffi_instantiate(val set_fun)
return nil;
}
+static val doloop_set_entries(val dlt, val fun)
+{
+ val name[] = {
+ lit("doloop"), lit("doloop*"),
+ nil
+ };
+ set_dlt_entries(dlt, name, fun);
+ return nil;
+}
+
+static val doloop_instantiate(val set_fun)
+{
+ funcall1(set_fun, nil);
+ load(format(nil, lit("~adoloop.tl"), stdlib_path, nao));
+ return nil;
+}
+
val dlt_register(val dlt,
val (*instantiate)(val),
val (*set_entries)(val, val))
@@ -581,6 +598,7 @@ void lisplib_init(void)
dlt_register(dl_table, error_instantiate, error_set_entries);
dlt_register(dl_table, keyparams_instantiate, keyparams_set_entries);
dlt_register(dl_table, ffi_instantiate, ffi_set_entries);
+ dlt_register(dl_table, doloop_instantiate, doloop_set_entries);
reg_fun(intern(lit("try-load"), system_package), func_n1(lisplib_try_load));
}
diff --git a/share/txr/stdlib/doloop.tl b/share/txr/stdlib/doloop.tl
new file mode 100644
index 00000000..770b7b05
--- /dev/null
+++ b/share/txr/stdlib/doloop.tl
@@ -0,0 +1,54 @@
+;; Copyright 2017
+;; Kaz Kylheku <kaz@kylheku.com>
+;; Vancouver, Canada
+;; All rights reserved.
+;;
+;; Redistribution and use in source and binary forms, with or without
+;; modification, are permitted provided that the following conditions are met:
+;;
+;; 1. Redistributions of source code must retain the above copyright notice, this
+;; list of conditions and the following disclaimer.
+;;
+;; 2. Redistributions in binary form must reproduce the above copyright notice,
+;; this list of conditions and the following disclaimer in the documentation
+;; and/or other materials provided with the distribution.
+;;
+;; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+;; ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+;; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+;; DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+;; FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+;; SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+;; CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+;; OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+;; OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+(defun sys:expand-doloop (f vars cexp body)
+ (let ((xvars (mapcar (tc
+ (((:whole w v i s . r))
+ (if r (compile-error f "excess elements in ~s" w) w))
+ (((:whole w v i . r))
+ (if r
+ (compile-error f "bad variable clause syntax ~s" w)
+ ^(,v ,i ,i)))
+ (((:whole w v . r))
+ (if r
+ (compile-error f "bad variable clause syntax ~s" w)
+ ^(,v nil ,v)))
+ ((v) ^(,v nil ,v)))
+ vars))
+ (pllel (eq (car f) 'doloop)))
+ ^(,(if pllel 'for 'for*)
+ ,(mapcar (aret ^(,@1 ,@2)) xvars)
+ ,cexp
+ ((,(if pllel 'pset 'set) ,*(mappend (ado unless (eq @1 @3)
+ ^(,@1 ,@3))
+ xvars)))
+ ,*body)))
+
+(defmacro doloop (:form f vars cexp . body)
+ (sys:expand-doloop f vars cexp body))
+
+(defmacro doloop* (:form f vars cexp . body)
+ (sys:expand-doloop f vars cexp body))
diff --git a/txr.1 b/txr.1
index 50519af1..ca91a329 100644
--- a/txr.1
+++ b/txr.1
@@ -15365,6 +15365,170 @@ allowing the
.code return
operator to be used to terminate at any point.
+.coNP Macros @ doloop and @ doloop*
+.synb
+.mets ({doloop | doloop*}
+.mets \ \ ({ sym | >> ( sym >> [ init-form <> [ step-form ])}*)
+.mets \ \ >> ([ test-form << result-form *])
+.mets \ \ << body-form *)
+.syne
+.desc
+The
+.code doloop
+and
+.code doloop*
+macros provide an iteration construct inspired by the ANSI Common Lisp
+.code do
+and
+.code do*
+macros.
+
+Each
+.meta sym
+element in the form must be a symbol suitable for use as a variable name.
+
+The
+.code doloop
+macro binds each
+.meta sym
+to the value produced by evaluating the adjacent
+.metn init-form .
+Then, in the environment in which these variables now exist,
+.meta test-form
+is evaluated. If that form yields
+.codn nil ,
+then the loop terminates. The
+.metn result-form -s
+are evaluated, and the value of the last one is returned.
+
+If
+.metn result-form -s
+are absent, then
+.code nil
+is returned.
+
+If
+.meta test-form
+is also absent, then the loop terminates and returns
+.codn nil .
+
+If
+.meta test-form
+produces a true value, then
+.metn result-form -s
+are not evaluated. Instead, the
+.metn body-form -s
+are evaluated. Next, the loop variables are updated by assigning
+to each
+.meta sym
+the value of
+.metn step-form .
+
+The following defaulting behaviors apply in regard to the variable
+syntax. For each
+.meta sym
+which has an associated
+.meta init-form
+but no
+.metn step-form ,
+the
+.meta init-form
+is duplicated and taken as the
+.metn step-form .
+Thus a variable specification like
+.code "(x y)"
+is equivalent to
+.codn "(x y y)" .
+If both forms are omitted, then the
+.meta init-form
+is taken to be
+.codn nil ,
+and the
+.meta step-form
+is taken to be
+.metn sym .
+This means that the variable form
+.code "(x)"
+is equivalent to
+.code "(x nil x)"
+which has the effect that
+.code x
+retains its current value when the next loop iteration begins.
+Lastly, the
+.meta sym
+variant is equivalent to
+.cblk
+.meti <> ( sym )
+.cble
+so that
+.code x
+is also equivalent to
+.codn "(x nil x)" .
+
+The differences between
+.code doloop
+and
+.code doloop*
+are:
+.code doloop
+binds the variables in parallel, similarly to
+.codn let ,
+whereas
+.code doloop*
+binds sequentially, like
+.codn let* ;
+moreover,
+.code doloop
+performs the
+.meta step-form
+assignments in parallel as if using a single
+.cblk
+.meti (pset < sym0 < step-form-0 < sym1 < step-form-1 ...)
+.cble
+form, whereas
+.code doloop*
+performs the assignment sequentially as if using
+.code set
+rather than
+.codn pset .
+
+The
+.code doloop
+and
+.code doloop*
+macros establish an anonymous
+.codn block ,
+allowing early return from the loop, with a value, via the
+.code return
+operator.
+
+.TP* "Dialect Note:"
+These macros are substantially different from the ANSI Common Lisp
+.code do
+and
+.code do*
+macros. Firstly, the termination logic is inverted; effectively they
+implement "while" loops, whereas their ANSI CL counterparts implement
+"until" loops. Secondly, in the ANSI CL macros, the defaulting of
+the missing
+.meta step-form
+is different. Variables with no
+.meta step-form
+are not updated. In particular, this means that the form
+.code "(x y)"
+is not equivalent to
+.codn "(x y y)" ;
+the ANSI CL macros do not feature the automatic replication of
+.meta init-form
+into the
+.meta step-form
+position.
+Lastly,
+.code doloop
+and
+.code doloop*
+do not implement an implicit
+.codn tagbody .
.coNP Operators @ block and @ block*
.synb