Merge commit '728a186263' as 'third_party/lisp/fiveam'
				
					
				
			This commit is contained in:
		
						commit
						7db9b2aa71
					
				
					 20 changed files with 2596 additions and 0 deletions
				
			
		
							
								
								
									
										226
									
								
								third_party/lisp/fiveam/src/utils.lisp
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										226
									
								
								third_party/lisp/fiveam/src/utils.lisp
									
										
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,226 @@ | |||
| ;;;; -*- Mode: Lisp; indent-tabs-mode: nil -*- | ||||
| 
 | ||||
| (in-package :it.bese.fiveam) | ||||
| 
 | ||||
| (defmacro dolist* ((iterator list &optional return-value) &body body) | ||||
|   "Like DOLIST but destructuring-binds the elements of LIST. | ||||
| 
 | ||||
| If ITERATOR is a symbol then dolist* is just like dolist EXCEPT | ||||
| that it creates a fresh binding." | ||||
|   (if (listp iterator) | ||||
|       (let ((i (gensym "DOLIST*-I-"))) | ||||
|         `(dolist (,i ,list ,return-value) | ||||
|            (destructuring-bind ,iterator ,i | ||||
|              ,@body))) | ||||
|       `(dolist (,iterator ,list ,return-value) | ||||
|          (let ((,iterator ,iterator)) | ||||
|            ,@body)))) | ||||
| 
 | ||||
| (defun make-collector (&optional initial-value) | ||||
|   "Create a collector function. | ||||
| 
 | ||||
| A Collector function will collect, into a list, all the values | ||||
| passed to it in the order in which they were passed. If the | ||||
| callector function is called without arguments it returns the | ||||
| current list of values." | ||||
|   (let ((value initial-value) | ||||
|         (cdr (last initial-value))) | ||||
|     (lambda (&rest items) | ||||
|       (if items | ||||
|           (progn | ||||
|             (if value | ||||
|                 (if cdr | ||||
|                     (setf (cdr cdr) items | ||||
|                           cdr (last items)) | ||||
|                     (setf cdr (last items))) | ||||
|                 (setf value items | ||||
|                       cdr (last items))) | ||||
|             items) | ||||
|           value)))) | ||||
| 
 | ||||
| (defun partitionx (list &rest lambdas) | ||||
|   (let ((collectors (mapcar (lambda (l) | ||||
|                               (cons (if (and (symbolp l) | ||||
|                                              (member l (list :otherwise t) | ||||
|                                                      :test #'string=)) | ||||
|                                         (constantly t) | ||||
|                                         l) | ||||
|                                     (make-collector))) | ||||
|                             lambdas))) | ||||
|     (dolist (item list) | ||||
|       (block item | ||||
|         (dolist* ((test-func . collector-func) collectors) | ||||
|           (when (funcall test-func item) | ||||
|             (funcall collector-func item) | ||||
|             (return-from item))))) | ||||
|     (mapcar #'funcall (mapcar #'cdr collectors)))) | ||||
| 
 | ||||
| ;;;; ** Anaphoric conditionals | ||||
| 
 | ||||
| (defmacro if-bind (var test &body then/else) | ||||
|   "Anaphoric IF control structure. | ||||
| 
 | ||||
| VAR (a symbol) will be bound to the primary value of TEST. If | ||||
| TEST returns a true value then THEN will be executed, otherwise | ||||
| ELSE will be executed." | ||||
|   (assert (first then/else) | ||||
|           (then/else) | ||||
|           "IF-BIND missing THEN clause.") | ||||
|   (destructuring-bind (then &optional else) | ||||
|       then/else | ||||
|     `(let ((,var ,test)) | ||||
|        (if ,var ,then ,else)))) | ||||
| 
 | ||||
| (defmacro aif (test then &optional else) | ||||
|   "Just like IF-BIND but the var is always IT." | ||||
|   `(if-bind it ,test ,then ,else)) | ||||
| 
 | ||||
| ;;;; ** Simple list matching based on code from Paul Graham's On Lisp. | ||||
| 
 | ||||
| (defmacro acond2 (&rest clauses) | ||||
|   (if (null clauses) | ||||
|       nil | ||||
|       (with-gensyms (val foundp) | ||||
|         (destructuring-bind ((test &rest progn) &rest others) | ||||
|             clauses | ||||
|           `(multiple-value-bind (,val ,foundp) | ||||
|                ,test | ||||
|              (if (or ,val ,foundp) | ||||
|                  (let ((it ,val)) | ||||
|                    (declare (ignorable it)) | ||||
|                    ,@progn) | ||||
|                  (acond2 ,@others))))))) | ||||
| 
 | ||||
| (defun varsymp (x) | ||||
|   (and (symbolp x) | ||||
|        (let ((name (symbol-name x))) | ||||
|          (and (>= (length name) 2) | ||||
|               (char= (char name 0) #\?))))) | ||||
| 
 | ||||
| (defun binding (x binds) | ||||
|   (labels ((recbind (x binds) | ||||
|              (aif (assoc x binds) | ||||
|                   (or (recbind (cdr it) binds) | ||||
|                       it)))) | ||||
|     (let ((b (recbind x binds))) | ||||
|       (values (cdr b) b)))) | ||||
| 
 | ||||
| (defun list-match (x y &optional binds) | ||||
|   (acond2 | ||||
|     ((or (eql x y) (eql x '_) (eql y '_)) | ||||
|      (values binds t)) | ||||
|     ((binding x binds) (list-match it y binds)) | ||||
|     ((binding y binds) (list-match x it binds)) | ||||
|     ((varsymp x) (values (cons (cons x y) binds) t)) | ||||
|     ((varsymp y) (values (cons (cons y x) binds) t)) | ||||
|     ((and (consp x) (consp y) (list-match (car x) (car y) binds)) | ||||
|      (list-match (cdr x) (cdr y) it)) | ||||
|     (t (values nil nil)))) | ||||
| 
 | ||||
| (defun vars (match-spec) | ||||
|   (let ((vars nil)) | ||||
|     (labels ((find-vars (spec) | ||||
|                (cond | ||||
|                  ((null spec) nil) | ||||
|                  ((varsymp spec) (push spec vars)) | ||||
|                  ((consp spec) | ||||
|                   (find-vars (car spec)) | ||||
|                   (find-vars (cdr spec)))))) | ||||
|       (find-vars match-spec)) | ||||
|     (delete-duplicates vars))) | ||||
| 
 | ||||
| (defmacro list-match-case (target &body clauses) | ||||
|   (if clauses | ||||
|       (destructuring-bind ((test &rest progn) &rest others) | ||||
|           clauses | ||||
|         (with-gensyms (tgt binds success) | ||||
|           `(let ((,tgt ,target)) | ||||
|              (multiple-value-bind (,binds ,success) | ||||
|                  (list-match ,tgt ',test) | ||||
|                (declare (ignorable ,binds)) | ||||
|                (if ,success | ||||
|                    (let ,(mapcar (lambda (var) | ||||
|                                    `(,var (cdr (assoc ',var ,binds)))) | ||||
|                                  (vars test)) | ||||
|                      (declare (ignorable ,@(vars test))) | ||||
|                      ,@progn) | ||||
|                    (list-match-case ,tgt ,@others)))))) | ||||
|       nil)) | ||||
| 
 | ||||
| ;;;; * def-special-environment | ||||
| 
 | ||||
| (defun check-required (name vars required) | ||||
|   (dolist (var required) | ||||
|     (assert (member var vars) | ||||
|             (var) | ||||
|             "Unrecognized symbol ~S in ~S." var name))) | ||||
| 
 | ||||
| (defmacro def-special-environment (name (&key accessor binder binder*) | ||||
|                                   &rest vars) | ||||
|   "Define two macros for dealing with groups or related special variables. | ||||
| 
 | ||||
| ACCESSOR is defined as a macro: (defmacro ACCESSOR (VARS &rest | ||||
| BODY)).  Each element of VARS will be bound to the | ||||
| current (dynamic) value of the special variable. | ||||
| 
 | ||||
| BINDER is defined as a macro for introducing (and binding new) | ||||
| special variables. It is basically a readable LET form with the | ||||
| prorpe declarations appended to the body. The first argument to | ||||
| BINDER must be a form suitable as the first argument to LET. | ||||
| 
 | ||||
| ACCESSOR defaults to a new symbol in the same package as NAME | ||||
| which is the concatenation of \"WITH-\" NAME. BINDER is built as | ||||
| \"BIND-\" and BINDER* is BINDER \"*\"." | ||||
|   (unless accessor | ||||
|     (setf accessor (format-symbol (symbol-package name) "~A-~A" '#:with name))) | ||||
|   (unless binder | ||||
|     (setf binder   (format-symbol (symbol-package name) "~A-~A" '#:bind name))) | ||||
|   (unless binder* | ||||
|     (setf binder*  (format-symbol (symbol-package binder) "~A~A" binder '#:*))) | ||||
|   `(eval-when (:compile-toplevel :load-toplevel :execute) | ||||
|      (flet () | ||||
|        (defmacro ,binder (requested-vars &body body) | ||||
|          (check-required ',name ',vars (mapcar #'car requested-vars)) | ||||
|          `(let ,requested-vars | ||||
|             (declare (special ,@(mapcar #'car requested-vars))) | ||||
|             ,@body)) | ||||
|        (defmacro ,binder* (requested-vars &body body) | ||||
|          (check-required ',name ',vars (mapcar #'car requested-vars)) | ||||
|          `(let* ,requested-vars | ||||
|             (declare (special ,@(mapcar #'car requested-vars))) | ||||
|             ,@body)) | ||||
|        (defmacro ,accessor (requested-vars &body body) | ||||
|          (check-required ',name ',vars requested-vars) | ||||
|          `(locally (declare (special ,@requested-vars)) | ||||
|             ,@body)) | ||||
|        ',name))) | ||||
| 
 | ||||
| ;; Copyright (c) 2002-2006, Edward Marco Baringer | ||||
| ;; All rights reserved. | ||||
| ;; | ||||
| ;; Redistribution and use in source and binary forms, with or without | ||||
| ;; modification, are permitted provided that the following conditions are | ||||
| ;; met: | ||||
| ;; | ||||
| ;;  - Redistributions of source code must retain the above copyright | ||||
| ;;    notice, this list of conditions and the following disclaimer. | ||||
| ;; | ||||
| ;;  - 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. | ||||
| ;; | ||||
| ;;  - Neither the name of Edward Marco Baringer, nor BESE, nor the names | ||||
| ;;    of its contributors may be used to endorse or promote products | ||||
| ;;    derived from this software without specific prior written permission. | ||||
| ;; | ||||
| ;; 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 | ||||
| ;; OWNER 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 | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue