First commit
This commit is contained in:
		
						commit
						ef96da9658
					
				
					 4 changed files with 855 additions and 0 deletions
				
			
		
							
								
								
									
										94
									
								
								README.md
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										94
									
								
								README.md
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,94 @@ | |||
| # goof-loop - a scheme looping facility | ||||
| 
 | ||||
| WARNING: CURRENTLY PRE-ALPHA | ||||
| 
 | ||||
| goof-loops aims to be an amalgamation of the racket for loops and Alex Shinn's foof-loop. We are many that found racket's for loops a breeze of fresh air, but in the end their most general forms (for/fold and for/foldr) are kinda odd to work with. If you choose not to use those general for loops, you cannot express arbitrary transformations, like say a fibonacci sequence, since for clauses cannot reference eachother. This is understandable given how they are tied to the underlying racket sequences, but still somewhat disappointing. goof-loop tries to fix this: | ||||
| 
 | ||||
| ``` | ||||
| (loop ((:for a (in 0 b)) | ||||
|        (:for b (in 1 (+ a b))) | ||||
|        (:for count (up-from 0 (to 1000))) | ||||
|        (:for acc (listing b))) | ||||
|   => acc | ||||
|   (display b) (newline)) | ||||
| ``` | ||||
| 
 | ||||
| The above example will display and accumulate the 1000 first fibonacci numbers. Doing the same thing in racket requires you to manually handle all the state in fold-variables using for/fold. It is a simple example, but proves the usefulness of goof-loop. | ||||
| 
 | ||||
| Compared to foof-loop, some things are added. Apart from minor syntactic changes, subloops are supported. The best way is to show: | ||||
| 
 | ||||
| ``` | ||||
| (define lst '((1 2) 'dud (3 4) (5 6))) | ||||
| (loop ((:for a (in-list lst)) | ||||
|        (:when (pair? a)) | ||||
|        (:for b (in-list a)) | ||||
|        (:for acc (summing b))) | ||||
|   => acc) | ||||
| ``` | ||||
| 
 | ||||
| This will sum all the sublists of lst and produce the result 21. Any :when, :unless, or :break clause will break out a subloop if any subsequent for clauses are found. | ||||
| 
 | ||||
| ## Differences from foof-loop | ||||
| 
 | ||||
| ### syntactical | ||||
| 
 | ||||
| all keywords are prepended with a : to distinguish them from regular variables. for -> :for | ||||
| 
 | ||||
| while and until are removed in favour of :break. | ||||
| 
 | ||||
| :when and :unless are added to better control when the loop body is executed (and accumulators accumulated) | ||||
| 
 | ||||
| with-clauses are removed in favour of (:for var (in init [step [stop]])) | ||||
| 
 | ||||
| accumulators are no longer for-clauses, but should be prepended with :acc. | ||||
| 
 | ||||
| ### Regressions | ||||
| 
 | ||||
| only :acc clauses are visible in the final-expression. This is due to for-clauses not being promoted through to outer loops (since they should not keep their state). | ||||
| 
 | ||||
| :for clauses cannot finalize, due to the above thing. The reason for distinguishing between :for and :acc is to be able to promote accumulators outwards and finalizers inwards. This is not implemented. | ||||
| 
 | ||||
| ### changes | ||||
| 
 | ||||
| (with var [init [step [guard]]]) => (:for var (in init [step [stop-expr]])). guard was a procedure, but now it is an expression. | ||||
| 
 | ||||
| (with var 10 (- var 1) negative?) => (:for var (in 10 (- var 10) (negative? var))) | ||||
| 
 | ||||
| I plan to remove non-named variable updates. That is a minor inconveniance, but unnamed updates has been my largest source of bugs, so I have grown to hate them. | ||||
| 
 | ||||
| ### similarities | ||||
| 
 | ||||
| You can of course still have a larger control of your loops: | ||||
| 
 | ||||
| ``` | ||||
| (loop loopy-loop ((:for a (up-from 1 (to 11)))) | ||||
|   => '() | ||||
|   (if (odd? a) | ||||
|       (cons (* a (- a)) (loopy-loop)) | ||||
|       (cons (* a a) (loopy-loop)))) | ||||
| 
 | ||||
| ;; => (-1 4 -9 16 -25 36 -49 64 -81 100) | ||||
| ``` | ||||
| 
 | ||||
| Named updates have a bug, sadly. | ||||
| 
 | ||||
| 
 | ||||
| ## Todo | ||||
| 
 | ||||
| Currently, there is a bug if you have subloops more than 2 loops deep where all accumulators are reset. This should be an easy fix. | ||||
| 
 | ||||
| Regarding the above: fixing that bug does nothing! I can only output loops of at most 2. | ||||
| 
 | ||||
| Should we add finalizers for :for-clauses? I can't see the need outside of a potential (in-file ...), which can't be properly supported anyway since I won't do any dynamic-wind stuff. | ||||
| 
 | ||||
| Is (:for var (in init step stop)) and (:acc var (in init update)) good syntax? the :with clause of foof-loop is nice, but what should it be called for accumulators? Should we go back to calling both :acc and :for just ":for" and re-add :with and an accumulating counterpart? What should that accumulating counterpart be called? :acc? | ||||
| 
 | ||||
| Add racket #:final clauses.  | ||||
| 
 | ||||
| ## foof, what a guy | ||||
| 
 | ||||
| I have previously expressed some admiration for Alex and I will do it again. The source of chibi loop is extremely elegant, and all but the hairiest part is written in syntax-rules. Not only has he written my two favourite SRFIs, his input in all the other discussions I have seen is always on-point, pragmatic and generally fantastic. He neither knows of this project, nor embraces it in any way. Y'all should go look at the source of (chibi loop) though. | ||||
| 
 | ||||
| ## Licence | ||||
| 
 | ||||
| The same BSD-styled license Alex uses for chibi-loop. | ||||
							
								
								
									
										418
									
								
								goof.scm
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										418
									
								
								goof.scm
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,418 @@ | |||
| ;; goof loop - a bastardisation of chibi loop. | ||||
| ;; | ||||
| ;; Copyright 2020 Linus Björnstam | ||||
| ;; Copyright 2000-2015 Alex Shinn (original author of chibi-loop) | ||||
| ;; 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. | ||||
| ;; 3. The name of the author(s) may not be used to endorse or promote products | ||||
| ;;    derived from this software without specific prior written permission. | ||||
| ;; | ||||
| ;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. | ||||
| 
 | ||||
| 
 | ||||
| ;; This is a looping construct obviously based on (chibi loop) (aka: | ||||
| ;; foof-loop) by Alex Shinn. The name goof-loop is a play on that | ||||
| ;; name, and the fact that I goofed in the chibi issue tracker when | ||||
| ;; trying to understand the iterator protocol. | ||||
| ;; | ||||
| ;; It extends chibi loop in the following way: | ||||
| ;;   * adds support for binding variables in the loop clauses. | ||||
| ;;   * Adds :when, :unless, and :break clauses that controls when the loop | ||||
| ;;     body executes and when values are collected by accumulating for clauses. | ||||
| ;;     similar to how #:when, #:unless and #:break works in racket. | ||||
| ;;   * Planned: add support for subloops, akin to what the starified loops of | ||||
| ;;     racket do. | ||||
| ;; | ||||
| ;;   It restricts chibi loops in the following ways: | ||||
| ;;    * with- and for-clauses  are no longer visible in the final expression, for that you | ||||
| ;;      must use a clause for which I don't have a name yet. | ||||
| 
 | ||||
| 
 | ||||
| (use-modules (helpers) | ||||
|              (srfi srfi-71)) | ||||
| 
 | ||||
| (include "iterators.scm") | ||||
| 
 | ||||
| ;; TODO: Add intermediate subloops. Make sure that accumulators are properly propagated. | ||||
| ;; TODO. fix let-kw-form. Don't use mutation. This should work:(define (partition predicate list) | ||||
| ;; (loop continue ((:for element (in-list list)) | ||||
| ;;                 (:acc satisfied (in  '())) | ||||
| ;;                 (:acc  unsatisfied  (in '()))) | ||||
| ;;   => (values (reverse satisfied) | ||||
| ;;              (reverse unsatisfied)) | ||||
| ;;   (if (predicate element) | ||||
| ;;       (continue (=> satisfied (cons element satisfied))) | ||||
| ;;       (continue (=> unsatisfied (cons element unsatisfied)))))) | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| (define-aux-syntaxes :for :when :unless :break :final :let :let* :acc :subloop) | ||||
| 
 | ||||
| (define-syntax loop | ||||
|   (syntax-rules () | ||||
|     ((loop (clauses ...)  body ...) | ||||
|      (cl (loop (clauses ...) body ...) | ||||
|          loop-name | ||||
|          (()) (()) (()) (()) (()) () (()) (()) (()) | ||||
|          (clauses ...) | ||||
|          body ... (loop-name))) | ||||
|     ((loop name (clauses ...) . body) | ||||
|      (cl (loop name (clauses ...) . body) | ||||
|          name | ||||
|          (()) (()) (()) (()) (()) () (()) (()) (()) | ||||
|          (clauses ...) | ||||
|          . body)))) | ||||
| 
 | ||||
| (define-syntax push-new-subloop | ||||
|   (syntax-rules () | ||||
|     ((_ orig name (lets ...) (accs ...) (vars ...) (checks ...) (refs ...) f (ul ...) (uw ...) (ub ...) clauses . body) | ||||
|      (cl orig name | ||||
|          (() lets ...) | ||||
|          (() accs ...) | ||||
|          (() vars ...) | ||||
|          (() checks ...) | ||||
|          (() refs ...) | ||||
|          f | ||||
|          (() ul ...) | ||||
|          (() uw ...) | ||||
|          (() ub ...) | ||||
|          clauses . body)))) | ||||
| 
 | ||||
| 
 | ||||
| ;; Clauses sorts all the clauses into subloops and positions everything where it should be. | ||||
| (define-syntax cl | ||||
|   (syntax-rules (=> in :for :with :when :unless :break :final :let :acc) | ||||
|     ((_ orig name l a v c r f ul uw ub () => expr . body) | ||||
|      (emit orig name l a v c r f ul uw ub expr . body)) | ||||
|     ((_ orig name l a v c r f ul uw ub () . body) | ||||
|      (emit orig name l a v c r f ul uw ub (if #f #f) . body)) | ||||
|     ;; :for-clauses | ||||
|     ;; found a for clause when we have a :when or :unless clause. Push new subloop | ||||
|     ((_ orig name l a v c r f ul ((uw uw* ...) . uw-rest) ub ((:for for-rest ...) clauses ...) . body) | ||||
|      (push-new-subloop orig name l a v c r f ul ((uw uw* ...) . uw-rest) ub ((:for for-rest ...) clauses ...) . body)) | ||||
| 
 | ||||
|     ;; The four special :for var (in ...)-clauses where user specifies their own iteration | ||||
|     ((_ orig name l a v c r f ul ((uw uw* ...) uw-rest ...) ub ((:with args ...) clauses ...) . body) | ||||
|      (push-new-subloop name l a v c r f ul ((uw uw* ...) uw-rest ...) ub ((:with args ...) clauses ...) . body)) | ||||
|      | ||||
|     ((_ orig name l a ((v-cur ...) . v-rest)  ((c-cur ...) . c-rest) | ||||
|         r f ul uw ub ((:for var (in init step guard-expr)) clauses ...) . body) | ||||
|      (cl orig name l a ((v-cur ... (var init step)) . v-rest) ((c-cur ... guard-expr) . c-rest) | ||||
|          r f ul uw ub (clauses ...) . body)) | ||||
|     ((_ orig name l a ((v-cur ...) . v-rest) c r f ul uw ub ((:for var (in init step)) clauses ...) . body) | ||||
|      (cl orig name l a ((v-cur ... (var init step)) . v-rest) c r f ul uw ub (clauses ...) . body)) | ||||
|     ((_ orig name l a ((v-cur ...) . v-rest) c r f ul uw ub ((:for var (in init)) clauses ...) . body) | ||||
|      (cl orig name l a ((v-cur ... (var init var)) . v-rest) c r f ul uw ub (clauses ...) . body)) | ||||
|     ;; For clause with a sequence creator. | ||||
|     ((_ orig name l a v c r f ul uw ub ((:for id ids ... (iterator source ...)) clauses ...) . body) | ||||
|      (iterator ((id ids ...) (source ...)) cl-next orig name l a v c r f ul uw ub (clauses ...) . body)) | ||||
| 
 | ||||
|     ;; Accumulating clauses | ||||
|     ;; should I push a subloop on a when clause? | ||||
|     ;; Currently these have no stop or if-clauses. Maybe add? | ||||
|     ((_ orig name l a v c r f ul uw ub ((:acc var (in init update)) clauses ...) . body) | ||||
|      (cl-next () ((var init update)) () () () ((var var)) orig name l a v c r f ul uw ub (clauses ...) . body)) | ||||
|     ((_ orig name l a v c r f ul uw ub ((:acc var (in init)) clauses ...) . body) | ||||
|      (cl-next () ((var init var)) () () () ((var var)) orig name l a v c r f ul uw ub (clauses ...) . body))   | ||||
| 
 | ||||
|     ;; user-whens | ||||
|     ((_ orig name l a v c r f ul ((cur-uw ...) . uw-rest) ub ((:when test) clauses ...) . body) | ||||
|      (cl orig name l a v c r f ul ((cur-uw ... test) . uw-rest) ub (clauses ...) . body)) | ||||
|     ((_ orig name l a v c r f ul ((cur-uw ...) . uw-rest) ub ((:unless test) clauses ...) . body) | ||||
|      (cl orig name l a v c r f ul ((cur-uw ... (not test)) . uw-rest) ub (clauses ...) . body)) | ||||
|     ;; USER BREAKS | ||||
|     ;; This pushes a #t to the user when expression, thus forcing a subloop if a for-clause is found afterwards. | ||||
|     ((_ orig name l a v c r f ul ((cur-uw ...) . uw-rest) ((cur-ub ...) . ub-rest) ((:break expr) clauses ...) . body) | ||||
|      (cl orig name l a v c r f ul ((cur-uw ... #t) . uw-rest) ((cur-ub ... expr) . ub-rest) (clauses ...) . body)) | ||||
|     ;; USER LETS | ||||
|     ((_ orig name l a v c r f ((cur-ul ...) . ul-rest) uw ub ((:let id id* ... expr) clauses ...) . body) | ||||
|      (cl orig name l a v c r f ((cur-ul ... (:let id id* ... expr)) . ul-rest) uw ub (clauses ...) . body)) | ||||
|     ((_ orig name l a v c r f ((cur-ul ...) . ul-rest) uw ub ((:let* id id* ... expr) clauses ...) . body) | ||||
|      (cl orig name l a v c r f ((cur-ul ... (:let* id id* ... expr)) . ul-rest) (clauses ...) . body)) | ||||
| 
 | ||||
|     ;; Explicit subloop. Shorthand for (:when #t) | ||||
|     ((_ orig name l a v c r f ul ((cur-uw ...) . uw-rest) ub (:subloop clauses ...) . body) | ||||
|      (cl orig name l a v c r f ul ((cur-uw ... #t) . uw-rest) ub (clauses ...) . body)) | ||||
|      | ||||
|     ;; ERROR HANDLING? | ||||
|     ((_ orig name l a v c r f ul uw ub (clause . rest) . body) | ||||
|      (syntax-error "Invalid clause in loop" clause orig)) | ||||
| 
 | ||||
|     )) | ||||
| 
 | ||||
| 
 | ||||
| ;; cl-next integrates the results | ||||
| ;; THIS WILL NEED TO BE UPDATED TO PROPERLY PUSH (accvar accinit accvar) ... down to the first accumulator. currently it | ||||
| ;; will be re-initialized for every previous loop except the innermost one. | ||||
| ;; THIS needs to work: | ||||
| ;; (loop ((:for a (in-list '((1 2) (3 4) (5 6)))) | ||||
| ;;        (:when #t) | ||||
| ;;        (:for b (in-list a)) | ||||
| ;;        (:for acc (listing b))) | ||||
| ;;   => acc) | ||||
| ;; as well as this: | ||||
| ;; (loop ((:for a (in-list '(((1) (2)) ((3) (4)) ((5) (6 7))))) | ||||
| ;;        (:when #t) | ||||
| ;;        (:for b (in-list a)) | ||||
| ;;        (:when :t)   | ||||
| ;;        (:for c (in-list b)) | ||||
| ;;        (:for acc (listing c))) | ||||
| ;;   => acc) | ||||
| 
 | ||||
| (define-syntax cl-next | ||||
|   (syntax-rules () | ||||
|     ((_ (new-lets ...) ((accvar accinit accupdate) ...) (new-vars ...) (new-checks ...) (new-refs ...) (new-finals ...) | ||||
|         orig name | ||||
|         ((lets ...)) | ||||
|         ((accs ...)) | ||||
|         ((vars ...)) | ||||
|         ((checks ...)) | ||||
|         ((refs ...)) | ||||
|         (finals ...) ul uw ub clauses . body) | ||||
|       (cl orig name | ||||
|           ((lets ... new-lets ...)) | ||||
|           ((accs ... (accvar accinit accupdate) ...)) | ||||
|           ((vars ... new-vars ...)) | ||||
|           ((checks ... new-checks ...)) | ||||
|           ((refs ... new-refs ...)) | ||||
|           (finals ... new-finals ...) | ||||
|           ul uw ub clauses . body)) | ||||
|     ;; We have a subloop! | ||||
|     ((_ (new-lets ...) ((accvar accinit accupdate) ...) (new-vars ...) (new-checks ...) (new-refs ...) (new-finals ...) | ||||
|         orig name | ||||
|         ((lets ...) . lets-rest) | ||||
|         ((accs ...) ((oldacc oldinit oldupdate) ...) ...) | ||||
|         ((vars ...) . vars-rest) | ||||
|         ((checks ...) . checks-rest) | ||||
|         ((refs ...) . refs-rest) | ||||
|         (finals ...) ul uw ub clauses . body) | ||||
|       (cl orig name | ||||
|           ((lets ... new-lets ...) . lets-rest) | ||||
|           ((accs ... (accvar accvar accupdate) ...) ((oldacc oldinit oldupdate) ... (accvar accinit accvar) ...) ...) | ||||
|           ((vars ... new-vars ...) . vars-rest) | ||||
|           ((checks ... new-checks ...) . checks-rest) | ||||
|           ((refs ... new-refs ...) . refs-rest) | ||||
|           (finals ... new-finals ...) | ||||
|           ul uw ub clauses . body)))) | ||||
|          | ||||
| 
 | ||||
| 
 | ||||
| (define-syntax user-let | ||||
|   (syntax-rules (:let :let*) | ||||
|     ((_ () () () body ...) | ||||
|      (begin body ...)) | ||||
|     ((_ (lets ...) () () . body) | ||||
|      (let (lets ...) | ||||
|        . body)) | ||||
|     ((_ () (stars ...) () . body) | ||||
|      (let* (stars ...) . body)) | ||||
| 
 | ||||
|     ;; These twe clauses handle let type changes. | ||||
|     ((_ () (stars ... last) ((:let id id* ... expr) clauses ...) . body) | ||||
|      (let* (stars ...) | ||||
|        (user-let (last (id id* ... expr)) () (clauses ...) . body))) | ||||
|     ((_ (lets ...) () ((:let* id id* ... expr) clauses ...) . body) | ||||
|      (let (lets ...) | ||||
|        (user-let () ((id id* ... expr)) (clauses ...) . body))) | ||||
| 
 | ||||
|     ;; 2 clauses new of the same that already existed | ||||
|     ((_ (lets ...) () ((:let id id* ... expr) clauses ...) . body) | ||||
|      (user-let (lets ... (id id* ... expr)) () (clauses ...) . body)) | ||||
|     ((_ () (stars ...) ((:let* id id* ... expr) clauses ...) . body) | ||||
|      (user-let () (stars ... (id id* ... expr)) (clauses ...) . body)))) | ||||
| 
 | ||||
| ;; If there is no subloops, we emit to the simple case | ||||
| (define-syntax emit | ||||
|   (syntax-rules () | ||||
|     ((_ orig name (one) . rest) | ||||
|      (emit-one orig name (one) . rest)) | ||||
|     ((_ orig name . rest) | ||||
|      (emit-many/first #f name . rest)))) | ||||
|      | ||||
| (define-syntax emit-one | ||||
|   (syntax-rules () | ||||
|     ((_ orig name | ||||
|         ((lets ...)) | ||||
|         (((accvar accinit accstep) ...)) | ||||
|         (((var init step) ...)) | ||||
|         ((checks ...)) | ||||
|         ((refs ...)) | ||||
|         ((final-binding final-value) ...) | ||||
|         ((user-lets ...)) ((user-whens ...)) ((user-breaks ...)) final-expr . body) | ||||
|      (let* (lets ...) | ||||
|        (define (final-fun final-binding ...) | ||||
|          final-expr) | ||||
|        (define (loopy-loop accvar ... var ...) | ||||
|          (if (or checks ...) | ||||
|              (final-fun final-value ...) | ||||
|              (let (refs ...) | ||||
|                (user-let () () (user-lets ...) | ||||
|                  (if (and user-whens ...) | ||||
|                      (let-kw-form name (loopy-loop (accvar accstep) ... (var step) ...) | ||||
|                        (if (or user-breaks ...) | ||||
|                            (final-fun final-value ...) | ||||
|                            (let () (if #f #f) . body))) | ||||
|                      (loopy-loop accvar ... step ...)))))) | ||||
|        (loopy-loop accinit ... init ...))))) | ||||
| 
 | ||||
| ;; Emit-many/first emits the outermost let loop and binds the final lambda. | ||||
| (define-syntax emit-many/first | ||||
|   (syntax-rules () | ||||
|     ((_ orig name | ||||
|         (lets-next ... (lets ...)) | ||||
|         (accs-next ... ((accvar accinit accstep) ...)) | ||||
|         (vars-next ... ((var init step) ...)) | ||||
|         (checks-next ... (checks ...)) | ||||
|         (refs-next ... (refs ...)) | ||||
|         ((final-binding final-value) ...) | ||||
|         (ul-next ... (user-lets ...)) | ||||
|         (uw-next ... (user-whens ...)) | ||||
|         (ub-next ... (user-breaks ...)) | ||||
|         final-expr | ||||
|         . body) | ||||
|      (let* ((final-fun (lambda (final-binding ...) final-expr)) | ||||
|             lets ...) | ||||
|        (let outer-loop ((accvar accinit) ... | ||||
|                         (var init) ...) | ||||
|          (if (or checks ...) | ||||
|              (final-fun final-value ...) | ||||
|              (let (refs ...) | ||||
|                (user-let () () (user-lets ...) | ||||
|                  (if (and user-whens ...) | ||||
|                      (cond | ||||
|                       ((or user-breaks ...)  (final-fun final-value ...)) | ||||
|                       (else (emit-many/rest orig | ||||
|                                              name | ||||
|                                              (outer-loop accstep ... step ...) | ||||
|                                              (lets-next ...) | ||||
|                                              (accs-next ...) | ||||
|                                              (vars-next ...) | ||||
|                                              (checks-next ...) | ||||
|                                              (refs-next ...) | ||||
|                                              ;; THIS IS NOW A COMPLETE call to final | ||||
|                                              (final-fun final-value ...) | ||||
|                                              (ul-next ...) | ||||
|                                              (uw-next ...) | ||||
|                                              (ub-next ...) | ||||
|                                              . body))) | ||||
|                      (outer-loop accvar ... step ...)))))))))) | ||||
| 
 | ||||
| (define-syntax emit-many/rest | ||||
|   (syntax-rules () | ||||
|     ;; match innermost loop | ||||
|     ((_ orig | ||||
|         name | ||||
|         next | ||||
|         ((lets ...)) | ||||
|         (((accvar accinit accstep) ...)) | ||||
|         (((var init step) ...)) | ||||
|         ((checks ...)) | ||||
|         ((refs ...)) | ||||
|         final | ||||
|         ((user-lets ...)) | ||||
|         ((user-whens ...)) | ||||
|         ((user-breaks ...)) | ||||
|         . body) | ||||
|      (let innermost-loop ((accvar accinit) ... | ||||
|                           (var init) ...) | ||||
|        (let* (lets ...) | ||||
|          (if (or checks ...) | ||||
|              next | ||||
|              (let (refs ...) | ||||
|                (user-let () () (user-lets ...) | ||||
|                  (if (and user-whens ...)          | ||||
|                      (cond | ||||
|                       ((or user-breaks ...) final) | ||||
|                       (else | ||||
|                        (let-kw-form name (innermost-loop (accvar accstep) ... (var step) ...) | ||||
|                          . body))) | ||||
|                      (innermost-loop accvar ... step ...)))))))) | ||||
| 
 | ||||
|     ;; Any intermediate loops | ||||
|     ((_ orig | ||||
|         name | ||||
|         next | ||||
|         (next-lets ... (lets ...)) | ||||
|         (next-accs ... ((accvar accinit accupdate) ...)) | ||||
|         (next-vars ... ((var init update) ...)) | ||||
|         (next-checks ... (checks ...)) | ||||
|         (next-refs ... (refs ...)) | ||||
|         final | ||||
|         (ul-next ... (user-lets ...)) | ||||
|         (uw-next ... (user-whens ...)) | ||||
|         (ub-next ... (user-breaks ...)) | ||||
|         . body) | ||||
|      (display "not implemented")))) | ||||
| 
 | ||||
| 
 | ||||
| ;; Helper procedures for let-kw-form | ||||
| (define (syntax= s1 s2) | ||||
|   (equal? (syntax->datum s1) (syntax->datum s2))) | ||||
| 
 | ||||
| (define (named-update? syn) | ||||
|   (syntax-case syn (=>) | ||||
|     ((=> var update) #t) | ||||
|     (_ #f))) | ||||
| 
 | ||||
| 
 | ||||
| (define (update-psn! params psn val) | ||||
|   (list-set! params psn | ||||
|              (list (car (list-ref params psn)) val))) | ||||
| 
 | ||||
| (define (update-name! params name val) | ||||
|   (let loop ((params params)) | ||||
|     (cond | ||||
|      ((null? params) (error "unknown loop parameter name " name (list '=> name val))) | ||||
|      ((syntax= name (caar params)) | ||||
|       (set-cdr! (car params) (list val)) | ||||
|       (display (syntax->datum val)) | ||||
|       ) | ||||
|      (else | ||||
|       (loop (cdr params)))))) | ||||
| 
 | ||||
| (define (syntax->list stx) | ||||
|   (syntax-case stx () | ||||
|     ((a ...) #'(a ...)))) | ||||
| 
 | ||||
| (define-syntax let-kw-form | ||||
|   (syntax-rules () | ||||
|     ((_ macro-name (loop-name (var step) ...) . body) | ||||
|      (let-syntax ((macro-name | ||||
|                    (lambda (stx) | ||||
|                      ;; this way of formulating params means it is an alist with syntax objects | ||||
|                      ;; as keys instead of a list of syntax objects | ||||
|                      (define params (list #'(var step) ...)) | ||||
|                      (with-ellipsis ::: | ||||
|                        (let loop ((lst (cdr (syntax->list stx))) (pos 0)) | ||||
|                          (if (null? lst) | ||||
|                              (with-syntax ((((v s) :::) params)) | ||||
|                                #'(loop-name s :::)) | ||||
|                              (syntax-case (car lst) (=>) | ||||
|                                ((=> name val) | ||||
|                                 (update-name! params #'name #'val) | ||||
|                                 (loop (cdr lst) #f)) | ||||
|                                (val pos | ||||
|                                  (begin | ||||
|                                    (update-psn! params psn #'val) | ||||
|                                    (loop (cdr lst) (+ pos 1)))) | ||||
|                                (_ (error "Positional arguments cannot be updated after a named argument"))))))))) | ||||
|        . body)))) | ||||
|                         | ||||
|                         | ||||
							
								
								
									
										14
									
								
								helpers.scm
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								helpers.scm
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,14 @@ | |||
| (define-module (helpers) | ||||
|   #:export (define-aux-syntax define-aux-syntaxes define-parameters | ||||
|              get-proc syntax->list)) | ||||
| 
 | ||||
| (define-syntax define-aux-syntax | ||||
|   (lambda (stx) | ||||
|     (syntax-case stx () | ||||
|       ((_ name) | ||||
|        #'(define-syntax name | ||||
|            (lambda (stx) | ||||
|              (syntax-violation 'name "Loop clause used outside of loop macro" stx))))))) | ||||
| (define-syntax-rule (define-aux-syntaxes name ...) | ||||
|   (begin | ||||
|     (define-aux-syntax name) ...)) | ||||
							
								
								
									
										329
									
								
								iterators.scm
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										329
									
								
								iterators.scm
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,329 @@ | |||
| ;; iterators.scm - iterators for goof-loop.  | ||||
| ;; | ||||
| ;; Copyright 2020 Linus Björnstam | ||||
| ;; Copyright 2000-2015 Alex Shinn (original author of chibi-loop) | ||||
| ;; 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. | ||||
| ;; 3. The name of the author(s) may not be used to endorse or promote products | ||||
| ;;    derived from this software without specific prior written permission. | ||||
| ;; | ||||
| ;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. | ||||
| 
 | ||||
| ;; TODO iterators | ||||
| ;; in-stream | ||||
| ;; in-naturals | ||||
| ;; in-hash with variations | ||||
| 
 | ||||
| ;; TODO: accumulators | ||||
| ;; vectoring | ||||
| ;; hashing | ||||
| ;; hashqing | ||||
| ;; hashving | ||||
| 
 | ||||
| (define-syntax in-list | ||||
|   (syntax-rules () | ||||
|     ((_ ((var) source) next . rest) | ||||
|      (in-list ((var cursor) source) next . rest)) | ||||
|     ((_       ((var cursor) source) next . rest) | ||||
|      (in-list ((var cursor succ) source) next . rest)) | ||||
|     ((_ ((var cursor succ) (source)) next . rest) | ||||
|      (in-list ((var cursor succ) (source cdr)) next . rest)) | ||||
|     ((_ ((var cursor succ) (source step)) next . rest) | ||||
|      (next | ||||
|       ;; outer let bindings, bound outside the loop, unchanged during the loop | ||||
|       () | ||||
|       ;; accumulators. These are the same as the bindings below, but values are | ||||
|       ;; kept through subloops. | ||||
|       () | ||||
|       ;; iterator, init, step | ||||
|       ((cursor source succ)) | ||||
|       ;; tests to check whether the iterator is exhausted. | ||||
|       ((not (pair? cursor))) | ||||
|       ;; loop variables (called refs) and updates. | ||||
|       ((var (car cursor)) | ||||
|        (succ (step cursor))) | ||||
|       ;; final bindings: things bound in the final function. | ||||
|       () | ||||
|       ;; the continuation. | ||||
|       . rest)))) | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| (define-syntax in-lists | ||||
|   (syntax-rules () | ||||
|     ((in-lists ((elts) lol) next . rest) | ||||
|      (in-lists ((elts pairs) lol) next . rest)) | ||||
|     ((in-lists ((elts pairs) lol) next . rest) | ||||
|      (in-lists ((elts pairs succ) lol) next . rest)) | ||||
|     ((in-lists ((elts pairs succ) (lol)) next . rest) | ||||
|      (in-lists ((elts pairs succ) (lol cdr)) next . rest)) | ||||
|     ((in-lists ((elts pairs succ) (lol)) next . rest) | ||||
|      (in-lists ((elts pairs succ) (lol cdr)) next . rest)) | ||||
|     ((in-lists ((elts pairs succ) (lol step)) next . rest) | ||||
|      (in-lists ((elts pairs succ) (lol step null?)) next . rest)) | ||||
|     ((in-lists ((elts pairs succ) (lol step done?)) next . rest) | ||||
|      (next () | ||||
|            () | ||||
|            ((pairs lol succ)) | ||||
|            ((let lp ((ls pairs)) ; an in-lined ANY | ||||
|               (and (pair? ls) (if (done? (car ls)) #t (lp (cdr ls)))))) | ||||
|            ((elts (map car pairs)) | ||||
|             (succ (map step pairs))) | ||||
|            () | ||||
|            . rest)) | ||||
|     )) | ||||
| 
 | ||||
| (define-syntax define-in-indexed | ||||
|   (syntax-rules () | ||||
|     ((define-in-indexed in-type in-type-reverse length ref) | ||||
|      (begin | ||||
|        (define-syntax in-type | ||||
|          (syntax-rules () | ||||
|            ((in-type seq next . rest) | ||||
|             (%in-idx >= (lambda (x i) (+ i 1)) (lambda (x) 0) length ref tmp seq next . rest)))) | ||||
|        (define-syntax in-type-reverse | ||||
|          (syntax-rules () | ||||
|            ((in-type-reverse seq next . rest) | ||||
|             (%in-idx < (lambda (x i) (- i 1)) (lambda (x) (- (length x) 1)) (lambda (x) 0) ref tmp seq next . rest)))) | ||||
|        )))) | ||||
| 
 | ||||
| (define-in-indexed in-vector in-vector-reverse vector-length vector-ref) | ||||
| 
 | ||||
| (define-in-indexed in-string in-string-reverse string-length string-ref) | ||||
| 
 | ||||
| ;; helper for the above string and vector iterators | ||||
| (define-syntax %in-idx | ||||
|   (syntax-rules () | ||||
|     ;;   cmp inc start end ref | ||||
|     ((%in-idx ge + s e r tmp ((var) (seq ...)) next . rest) | ||||
|      (%in-idx ge + s e r tmp ((var seq-index) (seq ...)) next . rest)) | ||||
|     ((%in-idx ge + s e r tmp ((var index) (seq)) next . rest) | ||||
|      (%in-idx ge + s e r tmp ((var index) (seq (s tmp) (e tmp))) next . rest)) | ||||
|     ((%in-idx ge + s e r tmp ((var index) (seq from)) next . rest) | ||||
|      (%in-idx ge + s e r tmp ((var index) (seq from (e tmp))) next . rest)) | ||||
|     ((%in-idx ge + s e r tmp ((var index) (seq from to)) next . rest) | ||||
|      (next ((tmp seq) (end to)) | ||||
|            () | ||||
|            ((index from (+ tmp index))) | ||||
|            ((ge index end)) | ||||
|            ((var (r tmp index))) | ||||
|            () | ||||
|        . rest)) | ||||
|     )) | ||||
| 
 | ||||
| ;;> \macro{(for ch (in-port [input-port [reader [eof?]]]))} | ||||
| 
 | ||||
| (define-syntax in-port | ||||
|   (syntax-rules () | ||||
|     ((in-port ((var) source) next . rest) | ||||
|      (in-port ((var p) source) next . rest)) | ||||
|     ((in-port ((var p) ()) next . rest) | ||||
|      (in-port ((var p) ((current-input-port))) next . rest)) | ||||
|     ((in-port ((var p) (port)) next . rest) | ||||
|      (in-port ((var p) (port read-char)) next . rest)) | ||||
|     ((in-port ((var p) (port read-char)) next . rest) | ||||
|      (in-port ((var p) (port read-char eof-object?)) next . rest)) | ||||
|     ((in-port ((var p) (port reader eof?)) next . rest) | ||||
|      (next ((p port) (r reader) (e? eof?)) | ||||
|            () | ||||
|            ((var (r p) (r p))) | ||||
|            ((e? var)) | ||||
|            () | ||||
|            () | ||||
|            . rest)))) | ||||
| 
 | ||||
| ;;> \macro{(for ch (in-file [input-port [reader [eof?]]]))} | ||||
| 
 | ||||
| (define-syntax in-file | ||||
|   (syntax-rules () | ||||
|     ((in-file ((var) source) next . rest) | ||||
|      (in-file ((var p) source) next . rest)) | ||||
|     ((in-file ((var p) (file)) next . rest) | ||||
|      (in-file ((var p) (file read-char)) next . rest)) | ||||
|     ((in-file ((var p) (file reader)) next . rest) | ||||
|      (in-file ((var p) (file reader eof-object?)) next . rest)) | ||||
|     ((in-file ((var p) (file reader eof?)) next . rest) | ||||
|      (next ((p (open-input-file file)) (r reader) (e? eof?)) | ||||
|            () | ||||
|            ((var (r p) (r p))) | ||||
|            ((e? var)) | ||||
|            () | ||||
|            ((dummy (close-input-port p))) | ||||
|            . rest)))) | ||||
| 
 | ||||
| (define-syntax in-generator | ||||
|   (syntax-rules () | ||||
|     ((_ ((var) (source)) next . rest) | ||||
|      (next ((gen source)) | ||||
|            () | ||||
|            ((var (gen) (gen))) | ||||
|            ((eof-object? var)) | ||||
|            () | ||||
|            () | ||||
|            . rest)))) | ||||
| 
 | ||||
| ;;> \macro{(for x (up-from [start] [(to limit)] [(by step)]))} | ||||
| 
 | ||||
| (define-syntax up-from | ||||
|   (syntax-rules (to by) | ||||
|     ((up-from (() . args) next . rest) | ||||
|      (up-from ((var) . args) next . rest)) | ||||
|     ((up-from ((var) (start (to limit) (by step))) next . rest) | ||||
|      (next ((s start) (l limit) (e step)) | ||||
|            () | ||||
|            ((var s (+ var e))) | ||||
|            ((>= var l)) | ||||
|            () | ||||
|            () | ||||
|            . rest)) | ||||
|     ((up-from ((var) (start (to limit))) next . rest) | ||||
|      (next ((s start) (l limit)) | ||||
|            () | ||||
|            ((var s (+ var 1))) | ||||
|            ((>= var l)) | ||||
|            () | ||||
|            () | ||||
|            . rest)) | ||||
|     ((up-from ((var) (start (by step))) next . rest) | ||||
|      (next ((s start) (e step)) | ||||
|            () | ||||
|            ((var s (+ var e))) | ||||
|            () | ||||
|            () | ||||
|            () | ||||
|            . rest)) | ||||
|     ((up-from ((var) (start)) next . rest) | ||||
|      (next ((s start)) | ||||
|            () | ||||
|            ((var s (+ var 1))) | ||||
|            () | ||||
|            () | ||||
|            () | ||||
|            . rest)) | ||||
|     )) | ||||
| 
 | ||||
| ;;> \macro{(for x (down-from [start] [(to limit)] [(by step)]))} | ||||
| 
 | ||||
| (define-syntax down-from | ||||
|   (syntax-rules (to by) | ||||
|     ((down-from (() . args) next . rest) | ||||
|      (down-from ((var) . args) next . rest)) | ||||
|     ((down-from ((var) (start (to limit) (by step))) next . rest) | ||||
|      (next ((s start) (l limit) (e step)) | ||||
|            () | ||||
|            ((var (- s e) (- var e))) | ||||
|            ((< var l)) | ||||
|            () | ||||
|            () | ||||
|            . rest)) | ||||
|     ((down-from ((var) (start (to limit))) next . rest) | ||||
|      (next ((s start) (l limit)) | ||||
|            () | ||||
|            ((var (- s 1) (- var 1))) | ||||
|            ((< var l)) | ||||
|            () | ||||
|            () | ||||
|            . rest)) | ||||
|     ((down-from ((var) (start (by step))) next . rest) | ||||
|      (next ((s start) (e step)) | ||||
|            () | ||||
|            ((var (- s e) (- var e))) | ||||
|            () | ||||
|            () | ||||
|            () | ||||
|            . rest)) | ||||
|     ((down-from ((var) (start)) next . rest) | ||||
|      (next ((s start)) | ||||
|            () | ||||
|            ((var (- s 1) (- var 1))) | ||||
|            () | ||||
|            () | ||||
|            () | ||||
|            . rest)) | ||||
|     )) | ||||
| 
 | ||||
| (define-syntax accumulating | ||||
|   (syntax-rules (initial if) | ||||
|     ((accumulating (kons final init) ((var) . x) next . rest) | ||||
|      (accumulating (kons final init) ((var cursor) . x) next . rest)) | ||||
|     ((accumulating (kons final init) ((var cursor) ((initial i) . x)) n . rest) | ||||
|      (accumulating (kons final i) ((var cursor) x) n . rest)) | ||||
|     ((accumulating (kons final init) ((var cursor) (expr (if check))) n . rest) | ||||
|      (n ((tmp-kons kons)) | ||||
|         ((cursor '() (if check (tmp-kons expr cursor) cursor))) | ||||
|         () | ||||
|         () | ||||
|         () | ||||
|         ((var (final cursor))) | ||||
|         . rest)) | ||||
|     ((accumulating (kons final init) ((var cursor) (expr)) n . rest) | ||||
|      (n ((tmp-kons kons)) | ||||
|         ((cursor '() (tmp-kons expr cursor))) | ||||
|         () | ||||
|         () | ||||
|         () | ||||
|         ((var (final cursor))) | ||||
|         . rest)))) | ||||
| 
 | ||||
| ;;> \macro{(for x [pair] (listing expr))} | ||||
| 
 | ||||
| (define-syntax listing | ||||
|   (syntax-rules () | ||||
|     ((listing args next . rest) | ||||
|      (accumulating (cons reverse '()) args next . rest)))) | ||||
| 
 | ||||
| ;;> \macro{(for x [pair] (listing-reverse expr))} | ||||
| 
 | ||||
| (define-syntax listing-reverse | ||||
|   (syntax-rules () | ||||
|     ((listing-reverse args next . rest) | ||||
|      (accumulating (cons (lambda (x) x) '()) args next . rest)))) | ||||
| 
 | ||||
| (define (append-reverse rev tail) | ||||
|   (if (null? rev) tail (append-reverse (cdr rev) (cons (car rev) tail)))) | ||||
| 
 | ||||
| ;;> \macro{(for x [pair] (appending expr))} | ||||
| 
 | ||||
| (define-syntax appending | ||||
|   (syntax-rules () | ||||
|     ((appending args next . rest) | ||||
|      (accumulating (append-reverse reverse '()) args next . rest)))) | ||||
| 
 | ||||
| ;;> \macro{(for x [pair] (appending-reverse expr))} | ||||
| 
 | ||||
| (define-syntax appending-reverse | ||||
|   (syntax-rules () | ||||
|     ((appending-reverse args next . rest) | ||||
|      (accumulating (append-reverse (lambda (x) x) '()) args next . rest)))) | ||||
| 
 | ||||
| ;;> \macro{(for x (summing expr))} | ||||
| 
 | ||||
| (define-syntax summing | ||||
|   (syntax-rules () | ||||
|     ((summing args next . rest) | ||||
|      (accumulating (+ (lambda (x) x) 0) args next . rest)))) | ||||
| 
 | ||||
| ;;> \macro{(for x (multiplying expr))} | ||||
| 
 | ||||
| (define-syntax multiplying | ||||
|   (syntax-rules () | ||||
|     ((multiplying args next . rest) | ||||
|      (accumulating (* (lambda (x) x) 1) args next . rest)))) | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Linus
						Linus