Racketifying goof-loop

I have racketified goof loops. No :for or :acc needed, and :when, :unless, and :break are no longer parethesised.
:acc blah (in ...) and :for blah (in ...) has been changed to
(blah (folding ...)) and (blah (in ...))
This commit is contained in:
Linus 2020-11-09 13:30:02 +01:00
parent 716c26c7ce
commit 2c182da570
3 changed files with 101 additions and 113 deletions

View file

@ -31,30 +31,22 @@
;; 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.
;;
;; 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 an accumulator clause.
;; * Positional update is not supported. It seems error-prone once you start
;; having a lot of loop variables, and because goof-loop does some re-ordering
;; that foof loop does not. For example:
;; (:for a (in 0 (+ a 1))) (:acc acc (in '() (cons a acc)))
;; are actually reordered in a loop, because accumulators and for loops are separated
;; due to having to propagate the accumulators through the loop.
(use-modules (helpers)
(srfi srfi-71))
(define-aux-syntaxes
;; Auxiliary syntax for the loop clauses
:when :unless :break :final :let :let* :subloop
;; Auxiliary syntax for the iterators.
:gen)
(include "iterators.scm")
(define-aux-syntaxes :for :when :unless :break :final :let :let* :acc :subloop)
(define-syntax loop
(syntax-rules ()
@ -89,62 +81,44 @@
;; 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)
(syntax-rules (=> in :when :unless :break :final :let :let* :subloop)
((_ 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))
;; Accumulator clause with a proper accumulator.
((_ orig name l a v c r f ul uw ub ((:acc 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))
;; user-whens
((_ orig name l a v c r f ul ((cur-uw ...) . uw-rest) ub ((:when test) clauses ...) . body)
((_ 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)
((_ 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)
((_ 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)
((_ 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)
((_ 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))
;; :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-rest ...) clauses ...) . body)
(push-new-subloop orig name l a v c r f ul ((uw uw* ...) . uw-rest) ub ((for-rest ...) clauses ...) . body))
;; For clause with a sequence creator.
((_ orig name l a v c r f ul uw ub ((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))
;; ERROR HANDLING?
((_ orig name l a v c r f ul uw ub (clause . rest) . body)
(syntax-error "Invalid clause in loop" clause orig))
@ -208,9 +182,8 @@
((checks ... new-checks ...) . checks-rest)
((refs ... new-refs ...) . refs-rest)
(finals ... new-finals ...)
ul uw ub clauses . body))))
ul uw ub clauses . body))
))
(define-syntax user-let
(syntax-rules (:let :let*)

View file

@ -37,6 +37,15 @@
;; hashqing
;; hashving
(define-syntax in
(syntax-rules ()
((_ ((var) (init)) n . rest)
(n () () ((var init var)) () () () . rest))
((_ ((var) (init step)) n . rest)
(n () () ((var init step)) () () () . rest))
((_ ((var) (init step stop)) n . rest)
(n () () ((var init step)) (stop) () () . rest))))
(define-syntax in-list
(syntax-rules ()
((_ ((var) source) next . rest)
@ -150,24 +159,26 @@
()
. rest))))
;;> \macro{(for ch (in-file [input-port [reader [eof?]]]))}
;; ;;> \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 (clo
;; se-input-port p)))
;; . rest))))
(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 ()
@ -187,29 +198,15 @@
((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))
(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))
()
(next ((s start) (l limit))()
((var s (+ var 1)))
((>= var l))
()
()
. rest))
((>= var l)) () () . rest))
((up-from ((var) (start (by step))) next . rest)
(next ((s start) (e step))
()
((var s (+ var e)))
()
()
()
. rest))
(next ((s start) (e step))()
((var s (+ var e))) () () () . rest))
((up-from ((var) (start)) next . rest)
(next ((s start))
()
@ -218,7 +215,11 @@
()
()
. rest))
))
;; Extra convenience, make it act like (in-range ...) from racket, but only for positive numbers.
((up-from ((var) (start limit step)) next . rest)
(next ((s start) (l limit) (e step)) () ((var s (+ var e))) ((>= var l)) () () . rest))
((up-from ((var) (start limit)) next . rest)
(up-from ((var) (start limit 1)) next . rest))))
;;> \macro{(for x (down-from [start] [(to limit)] [(by step)]))}
@ -283,6 +284,20 @@
((var (final cursor)))
. rest))))
(define-syntax folding
(syntax-rules (if)
((_ ((var) (init update (if guard))) n . rest)
(n ()
((var init (if guard update var)))
() () ()
((var var))
. rest))
((_ ((var) (init update)) n . rest)
(folding ((var) (init update (if #t))) n . rest))
((_ ((var) (init)) n . rest)
(folding ((var) (init var (if #t))) n . rest))))
;;> \macro{(for x [pair] (listing expr))}
(define-syntax listing

View file

@ -1,25 +1,25 @@
;; This is just a file with things that should be written as a test. Dump file.
(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)
(loop ((:for a (in-list '((1 2) (3 4) (5 6))))
(:when #t)
(:for b (in-list a))
(:for acc (listing b)))
=> acc)
(loop ((:for a (in-list '(1 2 3)))
(:acc oa (summing a))
(loop ((a (in-list '(((1) (2)) ((3) (4)) ((5) (6 7)))))
:when #t
(b (in-list a))
:subloop
(:for b (up-from a (to (+ a 2))))
(:acc ob (listing b)))
=> (values oa ob))
;; Should return 6 and (1 2 2 3 3 4
(c (in-list b))
(acc (listing c)))
=> acc)
(loop ((a (in-list '((1 2) (3 4) (5 6))))
:subloop
(b (in-list a))
(acc (listing b)))
=> acc)
(loop ((a (in-list '(1 2 3)))
(oa (summing a))
:subloop
(b (up-from a (to (+ a 2))))
(ob (listing b)))
=> (values oa ob))
;; Should return 6 and (1 2 2 3 3 4)