summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ChangeLog28
-rw-r--r--eval.c1
-rw-r--r--lib.c38
-rw-r--r--lib.h22
-rw-r--r--match.c1
-rw-r--r--parser.y2
-rw-r--r--txr.1316
7 files changed, 368 insertions, 40 deletions
diff --git a/ChangeLog b/ChangeLog
index 226f38f9..18f9e17c 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,31 @@
+2011-12-15 Kaz Kylheku <kaz@kylheku.com>
+
+ * eval.c (eval_init): not added as synonym for null.
+
+ * lib.c (copy_list): Use list_collect_append rather than
+ list_collect_terminate.
+ (append2, appendv): Simplified using new list_collect_append.
+ (nappend2): Simplified using new list_collect_nconc.
+
+ * lib.h (list_collect): Added check for accidental usage
+ of list_collect after list_append, since PTAIL has different
+ semantics.
+ (list_collect_nconc, list_collect_append): Semantics fixed so that
+ append collecting works more like the Common Lisp append function,
+ allowing trailing atoms or a lone atom. The meaning of PTAIL is
+ changed, however. Now PTAIL actually tracks the head of the most
+ recently appended segment. Each append operation has to first
+ traverse the previously added piece to get to the end.
+
+ (list_collect_terminate): Macro removed.
+
+ * match.c (v_gather): Removed useless use of list_collect_terminate.
+
+ * parser.y: Some headers added that are needed by list_collect.
+
+ * txr.1: Documented append, list, atom, null, not, consp, make-lazy-cons,
+ lcons-fun, listp, proper-listp, length-list, mapcar, mappend, and apply.
+
2011-12-14 Kaz Kylheku <kaz@kylheku.com>
@# comments are becoming obsolescent. @# comments
diff --git a/eval.c b/eval.c
index d8755fa4..e78f4051 100644
--- a/eval.c
+++ b/eval.c
@@ -1128,6 +1128,7 @@ void eval_init(void)
reg_fun(intern(lit("atom"), user_package), func_n1(atom));
reg_fun(intern(lit("null"), user_package), func_n1(nullp));
+ reg_fun(intern(lit("not"), user_package), func_n1(nullp));
reg_fun(intern(lit("consp"), user_package), func_n1(consp));
reg_fun(intern(lit("listp"), user_package), func_n1(listp));
reg_fun(intern(lit("proper-listp"), user_package), func_n1(proper_listp));
diff --git a/lib.c b/lib.c
index 8e1652e7..9c3f1e34 100644
--- a/lib.c
+++ b/lib.c
@@ -319,14 +319,14 @@ val push(val value, val *plist)
val copy_list(val list)
{
- list_collect_decl (out, tail);
+ list_collect_decl (out, ptail);
while (consp(list)) {
- list_collect(tail, car(list));
+ list_collect(ptail, car(list));
list = cdr(list);
}
- list_collect_terminate(tail, list);
+ list_collect_append(ptail, list);
return out;
}
@@ -359,14 +359,11 @@ val reverse(val in)
val append2(val list1, val list2)
{
- list_collect_decl (out, tail);
+ list_collect_decl (out, ptail);
- while (list1) {
- list_collect(tail, car(list1));
- list1 = cdr(list1);
- }
+ list_collect_append (ptail, list1);
+ list_collect_append (ptail, list2);
- list_collect_terminate(tail, list2);
return out;
}
@@ -376,14 +373,9 @@ val appendv(val lists)
for (; lists; lists = cdr(lists)) {
val item = car(lists);
- if (consp(item)) {
- list_collect_append(ptail, car(lists));
- } else {
- if (cdr(lists))
- uw_throwf(error_s, lit("append: ~s is not a list"), item, nao);
- list_collect_terminate(ptail, item);
- return out;
- }
+ if (listp(*ptail))
+ uw_throwf(error_s, lit("append: ~s is not a list"), *ptail, nao);
+ list_collect_append(ptail, item);
}
return out;
@@ -391,16 +383,12 @@ val appendv(val lists)
val nappend2(val list1, val list2)
{
- val temp, iter;
-
- if (list1 == nil)
- return list2;
+ list_collect_decl (out, ptail);
- for (iter = list1; (temp = cdr(iter)) != nil; iter = temp)
- ; /* empty */
+ list_collect_nconc (ptail, list1);
+ list_collect_nconc (ptail, list2);
- *cdr_l(iter) = list2;
- return list1;
+ return out;
}
val ldiff(val list1, val list2)
diff --git a/lib.h b/lib.h
index 30fd35c1..345c3e35 100644
--- a/lib.h
+++ b/lib.h
@@ -570,29 +570,29 @@ INLINE val eq(val a, val b) { return ((a) == (b) ? t : nil); }
#define list_collect(PTAIL, OBJ) \
do { \
+ if (*PTAIL) \
+ internal_error("mixed collect style"); \
*PTAIL = cons(OBJ, nil); \
PTAIL = cdr_l(*PTAIL); \
} while(0)
#define list_collect_nconc(PTAIL, OBJ) \
do { \
- obj_t *o_b_j = (OBJ); \
- *PTAIL = o_b_j; \
- if (o_b_j) \
- PTAIL = tail(o_b_j); \
+ if (*PTAIL) { \
+ PTAIL = tail(*PTAIL); \
+ } \
+ *PTAIL = OBJ; \
} while (0)
#define list_collect_append(PTAIL, OBJ) \
do { \
- obj_t *o_b_j = copy_list(OBJ); \
- *PTAIL = o_b_j; \
- if (o_b_j) \
- PTAIL = tail(o_b_j); \
+ if (*PTAIL) { \
+ *PTAIL = copy_list(*PTAIL); \
+ PTAIL = tail(*PTAIL); \
+ } \
+ *PTAIL = OBJ; \
} while (0)
-#define list_collect_terminate(PTAIL, OBJ) \
- do *PTAIL = (OBJ); while(0)
-
#define cons_bind(CAR, CDR, CONS) \
obj_t *c_o_n_s ## CAR ## CDR = CONS; \
obj_t *CAR = car(c_o_n_s ## CAR ## CDR); \
diff --git a/match.c b/match.c
index fdd7666a..e8cdf22a 100644
--- a/match.c
+++ b/match.c
@@ -2221,7 +2221,6 @@ static val v_gather(match_files_ctx *c)
}
}
- list_collect_terminate (ptail, nil);
specs = new_specs;
if (consp(max_data)) {
diff --git a/parser.y b/parser.y
index cc6a6dbd..08fe1c96 100644
--- a/parser.y
+++ b/parser.y
@@ -31,9 +31,11 @@
#include <limits.h>
#include <dirent.h>
#include <stdlib.h>
+#include <setjmp.h>
#include <wchar.h>
#include "config.h"
#include "lib.h"
+#include "unwind.h"
#include "regex.h"
#include "utf8.h"
#include "match.h"
diff --git a/txr.1 b/txr.1
index 80dc14dd..58805f3e 100644
--- a/txr.1
+++ b/txr.1
@@ -5138,26 +5138,336 @@ Examples:
.SS Function append
+.TP
+Syntax:
+
+(append [<list>* <cdr>])
+
+.TP
+Description:
+
+The append function creates a new list which is a catenation of the
+list arguments. All arguments are optional, such that (append) produces
+the empty list.
+
+If a single argument is specified, then append simply returns the value of that
+argument. It may be any kind of object.
+
+If N arguments are specified, where N > 1, then the first N-1 arguments must be
+proper lists. Copies of these lists are catenated together. The last argument,
+argument N, may be any kind of object. It is installed into the cdr field of
+the last cons cell of the resulting list. Thus, if argument N is also a list, it
+is catenated onto the resulting list, but without being copied. Argument N may
+be an atom other than nil; in that case append produces an improper list.
+
+.TP
+Examples:
+
+;; An atom is returned.
+(append 3) -> 3
+
+;; A list is also just returned: no copying takes place.
+;; The eq function can verify that the same object emerges
+;; from append that went in.
+(let ((list '(1 2 3)))
+ (eq (append list) list)) -> t
+
+(append '(1 2 3) '(4 5 6) 7) -> '(1 2 3 4 5 6 . 7))
+
+;; the (4 5 6) tail of the resulting list is the original
+;; (4 5 6) object, shared with that list.
+
+(append '(1 2 3) '(4 5 6)) -> '(1 2 3 4 5 6)
+
+(append nil) -> nil
+
+;; (1 2 3) is copied: it is not the last argument
+(append '(1 2 3) nil) -> (1 2 3)
+
+;; empty lists disappear
+(append nil '(1 2 3) nil '(4 5 6)) -> (1 2 3 4 5 6)
+(append nil nil nil) -> nil
+
+;; atoms and improper lists other than in the last position
+;; are erroneous
+(append '(a . b) 3 '(1 2 3)) -> **error**
+
.SS Function list
+.TP
+Syntax:
+
+(list <value>*)
+
+.TP
+Description:
+
+The list function creates a new list, whose elements are the
+argument values.
+
+.TP
+Examples:
+
+(list) -> nil
+(list 1) -> (1)
+(list 'a 'b) -> (a b)
+
.SS Function atom
+.TP
+Syntax:
+(atom <value>)
+
+.TP
+Description:
+
+The atom function tests whether a value is an atom. It returns t if this is the
+case, nil otherwise. All values which are not cons cells are atoms.
+
+(atom x) is equivalent to (not (consp x)).
+
+.TP
+Examples:
+
+(atom 3) -> t
+(atom (cons 1 2)) -> nil
+(atom "abc") -> t
+(atom '(3)) -> nil
+
+.SS Functions null and not
+
+.TP
+Syntax:
+(null <value>)
+(not <value>)
+
+.TP
+Description:
+
+The null and not functions are synonyms. They tests whether a value is the
+object nil. They returns t if this is the case, nil otherwise.
+
+.TP
+Examples:
+
+(null '()) -> t
+(null nil) -> t
+(null ()) -> t
+
+(if (null x) (format t "x is nil!"))
+
+(let ((list '(b c d)))
+ (if (not (memq 'a list))
+ (format t "list ~s does not contain the symbol a\en")))
+
.SS Function consp
+.TP
+Syntax:
+(consp <value>)
+
+.TP
+Description:
+
+The atom function tests whether a value is a cons. It returns t if this is the
+case, nil otherwise.
+
+(consp x) is equivalent to (not (atom x)).
+
+Non-empty lists test positive under consp because a list is represented
+as a reference to the first cons in a chain of one or more conses.
+
+.TP
+Examples:
+
+(consp 3) -> nil
+(consp (cons 1 2)) -> t
+(consp "abc") -> nil
+(consp '(3)) -> t
+
+
.SS Function make_lazy_cons
-.SS Function lcons_fun
+.TP
+Syntax:
+
+(make-lazy-cons <function>)
+
+.TP
+Description:
+
+The function make-lazy-cons makes a special kind of cons cell called a lazy
+cons, or lcons. Lazy conses are useful for implementing lazy lists.
+
+Lazy lists are lists which are not allocated all at once. Rather,
+their elements materialize when they are accessed, like
+magic stepping stones appearing under one's feet out of thin air.
+
+A lazy cons has "car" and "cdr" fields like a regular cons, and those
+fields are initialized to nil when the lazy cons is created. A lazy cons also
+has an update function, the one which is provided as an argument to
+make-lazy-cons.
+
+When either the car and cdr fields of a cons are accessed for the first time,
+the function is automatically invoked first. That function has the opportunity
+to initialize the car and cdr fields. Once the function is called, it is removed
+from the lazy cons: the lazy cons no longer has an update function.
+
+To continue a lazy list, the function can make another call to make-lazy-cons
+and install the resulting cons as the cdr of the lazy cons.
+
+.TP
+Example:
+
+;;; lazy list of integers between min and max
+(defun integer-range (min max)
+ (let ((counter min))
+ ;; min is greater than max; just return empty list,
+ ;; otherwise return a lazy list
+ (if (> min max)
+ nil
+ (make-lazy-cons (lambda (lcons)
+ ;; install next number into car
+ (rplaca lcons counter)
+ ;; now deal wit cdr field
+ (cond
+ ;; max reached, terminate list with nil!
+ ((eql counter max)
+ (rplacd lcons nil))
+ ;; max not reached: increment counter
+ ;; and extend with another lazy cons
+ (t
+ (inc counter)
+ (rplacd lcons (make-lazy-cons
+ (lcons-fun lcons))))))))))
+
+.SS Function lcons-fun
+
+.TP
+Syntax:
+
+(lcons-fun <lazy-cons>)
+
+.TP
+Description:
+
+The lcons-fun function retrieves the update function of a lazy cons.
+Once a lazy cons has been accessed, it no longer has an update function
+and lcons-fun returns nil. While the update function of a lazy cons is
+executing, it is still accessible. This allows the update function
+to retrieve a reference to itself and propagate itself into
+another lazy cons (as in the example under make-lazy-cons).
.SS Functions listp and proper-listp
+.TP
+Syntax:
+
+(listp <value>)
+(proper-listp <value>)
+
+.TP
+Description:
+
+The listp and proper-listp functions test, respectively, whether
+the specified value is a list, or a proper list, and return
+t or nil accordingly.
+
+The listp test is weaker, and executes without having to traverse
+the object. (listp x) is equivalent to (or (null x) (consp x)).
+The empty list is a list, and a cons cell is a list.
+
+The proper-listp function returns t only for proper lists. A proper list is
+either nil, or a cons whose cdr is a proper list. proper-listp traverses the
+list, and its execution will not terminate if the list is circular.
+
.SS Function length-list
-.SS Function mapcar
+.TP
+Syntax:
+
+(length-list <list>)
+
+.TP
+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
+
+.TP
+Syntax:
+
+(mapcar <function> <list> <list>*)
+(mappend <function> <list> <list>*)
+
+.TP
+When given three arguments, the mapcar function processes applies a function to
+the elements of a list and returns a list of the resulting values.
+Essentially, the list is filtered through the function.
+
+When additional lists are given as arguments, this filtering behavior is
+generalized in the following way: mapcar traverses the lists in parallel,
+taking a value from each list as an argument to the function. If there
+are two lists, the function is called with two arguments and so forth.
+The process is limited by the length of the shortest list.
+The return values of the function are collected into a new list which is
+returned.
+
+The mappend function works like mapcar, with the following difference.
+Rather than accumulating the values returned by the function into a list,
+mappend expects the items returned by the function to be lists which
+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)).
+
+
+.TP
+Examples:
+
+;; multiply every element by two
+(mapcar (lambda (item) (* 2 item)) '(1 2 3)) -> (4 6 8)
+
+;; "zipper" two lists together
+(mapcar (lambda (le ri) (list le ri)) '(1 2 3) '(a b c)) '((1 a) (2 b) (3 c)))
+
+;; like append, mappend allows a lone atom or a trailing atom:
+(mappend (fun identity) 3) -> (3)
+(mappend (fun identity) '((1) 2)) -> (1 . 2)
+
+;; take just the even numbers
+(mappend (lambda (item) (if (evenp x) (list x))) '(1 2 3 4 5))
+-> (2 4)
-.SS Function mappend
.SS Function apply
+.TP
+Syntax:
+
+(apply <function> <arglist>)
+
+.TP
+Description:
+
+The apply function treats a list of values as individual arguments that
+are passed to the specified function, which is called, and its return
+value becomes the return value of apply.
+
+.TP
+Examples:
+
+;; '(1 2 3) becomes arguments to list, thus (list 1 2 3).
+(apply (fun list) '(1 2 3)) -> (1 2 3)
+
+.TP
+Dialect note:
+
+TXR Lisp apply does not take additional arguments before the list.
+In Common Lisp we can write (apply #'list 1 2 (list 3 4 5)) which
+yields (1 2 3 4 5). In TXR Lisp, this usage can be simulated using
+(apply (fun list) (list 1 2 (list 3 4 5))) or
+(apply (fun list) '(,1 ,2 ,*(list 3 4 5))) .
+
.SS Functions reduce-left and reduce-right
.SS Function copy-list