]> code.delx.au - gnu-emacs-elpa/blobdiff - packages/stream/stream.el
update stream.el to the latest version
[gnu-emacs-elpa] / packages / stream / stream.el
index 6e06afda4d62817d36d6053220848a56a7e3be15..567a9e354e8fc147a89941b1f2b34fff93481d62 100644 (file)
@@ -4,8 +4,8 @@
 
 ;; Author: Nicolas Petton <nicolas@petton.fr>
 ;; Keywords: stream, laziness, sequences
-;; Version: 1.0
-;; Package-Requires: ((emacs "25.1"))
+;; Version: 2.1.0
+;; Package-Requires: ((emacs "25"))
 ;; Package: stream
 
 ;; Maintainer: nicolas@petton.fr
 
 (eval-when-compile (require 'cl-lib))
 (require 'seq)
+(require 'thunk)
 
 (eval-and-compile
   (defconst stream--identifier '--stream--
     "Symbol internally used to identify streams."))
 
-(defmacro stream--delay (&rest body)
-  "Delay the evaluation of BODY."
-  (declare (debug t))
-  (let ((forced (make-symbol "forced"))
-        (val (make-symbol "val")))
-    `(let (,forced ,val)
-       (lambda ()
-         (unless ,forced
-           (setf ,val (progn ,@body))
-           (setf ,forced t))
-         ,val))))
-
-(defun stream--force (delayed)
-  "Force the evaluation of DELAYED."
-  (funcall delayed))
-
 (defmacro stream-make (&rest body)
   "Return a stream built from BODY.
-BODY must return nil or a cons cell, which cdr is itself a
+BODY must return nil or a cons cell whose cdr is itself a
 stream."
   (declare (debug t))
-  `(list ',stream--identifier (stream--delay ,@body)))
+  `(list ',stream--identifier (thunk-delay ,@body)))
 
 (defmacro stream-cons (first rest)
   "Return a stream built from the cons of FIRST and REST.
@@ -113,7 +98,7 @@ SEQ can be a list, vector or string."
 
 (cl-defmethod stream ((buffer buffer) &optional pos)
   "Return a stream of the characters of the buffer BUFFER.
-BUFFER-OR-NAME may be a buffer or a string (buffer name).
+BUFFER may be a buffer or a string (buffer name).
 The sequence starts at POS if non-nil, 1 otherwise."
   (with-current-buffer buffer
     (unless pos (setq pos (point-min)))
@@ -128,6 +113,25 @@ The sequence starts at POS if non-nil, 1 otherwise."
            (char-after (point)))))
      (stream buffer (1+ pos)))))
 
+(declare-function iter-next "generator")
+
+(defun stream-from-iterator (iterator)
+  "Return a stream generating new elements through ITERATOR.
+ITERATOR is an iterator object in terms of the \"generator\"
+package."
+  (stream-make
+   (condition-case nil
+       (cons (iter-next iterator) (stream-from-iterator iterator))
+     (iter-end-of-sequence nil))))
+
+(defun stream-regexp (buffer regexp)
+  (stream-make
+   (let (match)
+     (with-current-buffer buffer
+       (setq match (re-search-forward regexp nil t)))
+     (when match
+       (cons (match-data) (stream-regexp buffer regexp))))))
+
 (defun stream-range (&optional start end step)
   "Return a stream of the integers from START to END, stepping by STEP.
 If START is nil, it defaults to 0. If STEP is nil, it defaults to
@@ -142,39 +146,63 @@ range is infinite."
      (stream-range (+ start step) end step))))
 \f
 
-(defun stream-p (stream)
+(defun streamp (stream)
   "Return non-nil if STREAM is a stream, nil otherwise."
   (and (consp stream)
        (eq (car stream) stream--identifier)))
 
 (defun stream-empty ()
   "Return an empty stream."
-  (list stream--identifier (stream--delay nil)))
+  (list stream--identifier (thunk-delay nil)))
 
 (defun stream-empty-p (stream)
   "Return non-nil is STREAM is empty, nil otherwise."
-  (null (stream--force (cadr stream))))
+  (null (thunk-force (cadr stream))))
 
 (defun stream-first (stream)
   "Return the first element of STREAM."
-  (car (stream--force (cadr stream))))
+  (car (thunk-force (cadr stream))))
 
 (defun stream-rest (stream)
   "Return a stream of all but the first element of STREAM."
-  (cdr (stream--force (cadr stream))))
+  (or (cdr (thunk-force (cadr stream)))
+      (stream-empty)))
+
+(defun stream-append (&rest streams)
+  "Concatenate the STREAMS.
+Requesting elements from the resulting stream will request the
+elements in the STREAMS in order."
+  (if (null streams)
+      (stream-empty)
+    (stream-make
+     (let ((first (pop streams)))
+       (while (and (stream-empty-p first) streams)
+         (setq first (pop streams)))
+       (if (stream-empty-p first)
+           nil
+         (cons (stream-first first)
+               (if streams (apply #'stream-append (stream-rest first) streams)
+                 (stream-rest first))))))))
+
+(defmacro stream-pop (stream)
+  "Return the first element of STREAM and set the value of STREAM to its rest."
+  (unless (symbolp stream)
+    (error "STREAM must be a symbol"))
+  `(prog1
+       (stream-first ,stream)
+     (setq ,stream (stream-rest ,stream))))
 \f
 
 ;;; cl-generic support for streams
 
-(defvar stream--generalizer
-  (cl-generic-make-generalizer
-   11
-   (lambda (name)
-     `(when (stream-p ,name)
-        'stream))
-   (lambda (tag)
-     (when (eq tag 'stream)
-       '(stream)))))
+(cl-generic-define-generalizer stream--generalizer
+  11
+  (lambda (name)
+    `(when (streamp ,name)
+       'stream))
+  (lambda (tag)
+    (when (eq tag 'stream)
+      '(stream))))
 
 (cl-defmethod cl-generic-generalizers ((_specializer (eql stream)))
   "Support for `stream' specializers."
@@ -183,17 +211,17 @@ range is infinite."
 
 ;;; Implementation of seq.el generic functions
 
-(cl-defgeneric seq-p ((_stream stream))
+(cl-defmethod seqp ((_stream stream))
   t)
 
-(cl-defgeneric seq-elt ((stream stream) n)
+(cl-defmethod seq-elt ((stream stream) n)
   "Return the element of STREAM at index N."
   (while (> n 0)
     (setq stream (stream-rest stream))
     (setq n (1- n)))
   (stream-first stream))
 
-(cl-defgeneric seq-length ((stream stream))
+(cl-defmethod seq-length ((stream stream))
   "Return the length of STREAM.
 This function will eagerly consume the entire stream."
   (let ((len 0))
@@ -202,35 +230,36 @@ This function will eagerly consume the entire stream."
       (setq stream (stream-rest stream)))
     len))
 
-(cl-defgeneric seq-subseq ((stream stream) start end)
+(cl-defmethod seq-subseq ((stream stream) start end)
   (seq-take (seq-drop stream start) (- end start)))
 
-(cl-defgeneric seq-into-sequence ((stream stream))
-  "Convert STREAM into a sequence"
+(cl-defmethod seq-into-sequence ((stream stream))
+  "Convert STREAM into a sequence."
   (let ((list))
     (seq-doseq (elt stream)
       (push elt list))
     (nreverse list)))
 
-(cl-defgeneric seq-into ((stream stream) type)
+(cl-defmethod seq-into ((stream stream) type)
   "Convert STREAM into a sequence of type TYPE."
   (seq-into (seq-into-sequence stream) type))
 
-(cl-defgeneric seq-into ((stream stream) (_type (eql stream)))
+(cl-defmethod seq-into ((stream stream) (_type (eql stream)))
   stream)
 
-(cl-defgeneric seq-into ((seq sequence) (_type (eql stream)))
+(cl-defmethod seq-into ((seq sequence) (_type (eql stream)))
   (stream seq))
 
-(cl-defgeneric seq-take ((stream stream) n)
+(cl-defmethod seq-take ((stream stream) n)
   "Return a stream of the first N elements of STREAM."
-  (if (zerop n)
+  (if (or (zerop n)
+          (stream-empty-p stream))
       (stream-empty)
     (stream-cons
      (stream-first stream)
      (seq-take (stream-rest stream) (1- n)))))
 
-(cl-defgeneric seq-drop ((stream stream) n)
+(cl-defmethod seq-drop ((stream stream) n)
   "Return a stream of STREAM without its first N elements."
   (stream-make
    (while (not (or (stream-empty-p stream) (zerop n)))
@@ -240,14 +269,14 @@ This function will eagerly consume the entire stream."
      (cons (stream-first stream)
            (stream-rest stream)))))
 
-(cl-defgeneric seq-take-while (pred (stream stream))
+(cl-defmethod seq-take-while (pred (stream stream))
   "Return a stream of the successive elements for which (PRED elt) is non-nil in STREAM."
   (stream-make
    (when (funcall pred (stream-first stream))
      (cons (stream-first stream)
            (seq-take-while pred (stream-rest stream))))))
 
-(cl-defgeneric seq-drop-while (pred (stream stream))
+(cl-defmethod seq-drop-while (pred (stream stream))
   "Return a stream from the first element for which (PRED elt) is nil in STREAM."
   (stream-make
    (while (not (or (stream-empty-p stream)
@@ -257,25 +286,25 @@ This function will eagerly consume the entire stream."
      (cons (stream-first stream)
            (stream-rest stream)))))
 
-(cl-defgeneric seq-map (function (stream stream))
-  "Return a stream.
-The elements of the produced sequence consist of the application
-of FUNCTION to each element of STREAM."
-  (if (stream-empty-p stream)
-      stream
-    (stream-cons
-      (funcall function (stream-first stream))
-     (seq-map function (stream-rest stream)))))
+(cl-defmethod seq-map (function (stream stream))
+    "Return a stream representing the mapping of FUNCTION over STREAM.
+The elements of the produced stream are the results of the
+applications of FUNCTION on each element of STREAM in succession."
+  (stream-make
+   (when (not (stream-empty-p stream))
+     (cons (funcall function (stream-first stream))
+           (seq-map function (stream-rest stream))))))
 
-(cl-defgeneric seq-do (function (stream stream))
+(cl-defmethod seq-do (function (stream stream))
   "Evaluate FUNCTION for each element of STREAM eagerly, and return nil.
 
-`seq-do' should never be used on infinite streams."
+`seq-do' should never be used on infinite streams without some
+kind of nonlocal exit."
   (while (not (stream-empty-p stream))
     (funcall function (stream-first stream))
     (setq stream (stream-rest stream))))
 
-(cl-defgeneric seq-filter (pred (stream stream))
+(cl-defmethod seq-filter (pred (stream stream))
   "Return a stream of the elements for which (PRED element) is non-nil in STREAM."
   (if (stream-empty-p stream)
       stream
@@ -288,7 +317,7 @@ of FUNCTION to each element of STREAM."
        (cons (stream-first stream)
              (seq-filter pred (stream-rest stream)))))))
 
-(cl-defgeneric seq-copy ((stream stream))
+(cl-defmethod seq-copy ((stream stream))
   "Return a shallow copy of STREAM."
   (stream-cons (stream-first stream)
                (stream-rest stream)))