home *** CD-ROM | disk | FTP | other *** search
- ;; LOOP-Facility nach CLTL2
- ;; (LOOP {loop-clause}*), CLTL2 S. 163,709-747
- ;; Bruno Haible 19.10.1991-20.10.1991, 22.10.1991, 6.6.1993, 28.6.1994
-
- (in-package "LISP")
- (export '(loop loop-finish))
- (pushnew 'loop *features*)
-
- (in-package "SYSTEM")
-
- ;; Parser-Hilfsfunktionen:
-
- (eval-when (compile load eval)
-
- ; (loop-keywordp obj) stellt fest, ob obj ein Loop-Keyword ist,
- ; und liefert dann das entsprechende Symbol (eindeutig), sonst NIL.
- (defun loop-keywordp (obj)
- (and (symbolp obj)
- (gethash (symbol-name obj)
- (load-time-value
- (make-hash-table :test #'equal
- :initial-contents
- (mapcar #'(lambda (s) (cons (symbol-name s) s))
- '(named
- for as and from downfrom upfrom to downto upto below
- above by in on = then across being each the hash-key
- hash-keys hash-value hash-values of using symbol
- present-symbol internal-symbol external-symbol symbols
- present-symbols internal-symbols external-symbols
- repeat
- while until always never thereis
- collect collecting append appending nconc nconcing
- count counting sum summing maximize maximizing
- minimize minimizing into
- with
- if when unless else end it
- do doing return
- of-type
- initially finally
- ) ) ) )
- ) ) )
-
- (defvar *whole*) ; die gesamte Form (LOOP ...)
-
- ; (loop-syntax-error loop-keyword) meldet einen Syntaxfehler
- (defun loop-syntax-error (loop-keyword)
- (error
- #L{
- DEUTSCH "~S: Syntaxfehler nach ~A in ~S"
- ENGLISH "~S: syntax error after ~A in ~S"
- FRANCAIS "~S : mauvaise syntaxe après ~A dans ~S"
- }
- 'loop (symbol-name loop-keyword) *whole*
- ) )
-
- ;; Destructuring:
-
- ; (destructure-vars pattern) liefert die Liste der Variablen,
- ; die in pattern vorkommen.
- (defun destructure-vars (pattern)
- (cond ((null pattern) nil)
- ((atom pattern) (list pattern))
- (t (nconc (destructure-vars (car pattern))
- (destructure-vars (cdr pattern))
- ) ) ) )
-
- ; (empty-tree-p pattern) stellt fest, ob in pattern
- ; überhaupt keine Variablen vorkommen.
- (defun empty-tree-p (pattern)
- (cond ((null pattern) t)
- ((atom pattern) nil)
- (t (and (empty-tree-p (car pattern)) (empty-tree-p (cdr pattern))))
- ) )
-
- ; (destructure-type pattern type) liefert eine Liste von Declaration-Specifiern,
- ; die die Variablen aus pattern zu den Typen aus type deklarieren.
- (defun destructure-type (pattern type)
- (cond ((null pattern) nil)
- ((atom pattern) (list `(TYPE ,type ,pattern)))
- ((consp type)
- (nconc (destructure-type (car pattern) (car type))
- (destructure-type (cdr pattern) (cdr type))
- ))
- (t (let ((vars (destructure-vars pattern)))
- (if vars (list `(TYPE ,type ,@vars)) nil)
- ) ) ) )
-
- ; (simple-type-p type) stellt fest, ob der Typ type nach Destructuring nur
- ; aus NIL, T, FIXNUM, FLOAT besteht (und damit ein OF-TYPE überflüssig macht).
- (defun simple-type-p (type)
- (if (atom type)
- (case type
- ((NIL T FIXNUM FLOAT) t)
- (t nil)
- )
- (and (simple-type-p (car type))
- (simple-type-p (cdr type))
- ) ) )
-
- (defvar *helpvars*) ; Vektor mit Hilfsvariablen fürs Destructuring
-
- ; (helpvar n) liefert die (n+1)-te Hilfsvariable (n>=0). Es müssen schon
- ; mindestens n Hilfvariablen gebraucht worden sein.
- ; Evtl. wird eine neue Hilfvariable erzeugt.
- (defun helpvar (n)
- (when (= n (fill-pointer *helpvars*))
- (vector-push-extend (gensym) *helpvars*)
- )
- (aref *helpvars* n)
- )
-
- ; (destructure pattern form) liefert eine Liste von Listen
- ; (Variable Form). Das erste ist eine Variable aus pattern, das
- ; zweite eine Form, an die die Variable zu binden ist bzw. die
- ; der Variablen zuzuweisen ist. Auf die Reihenfolge der Bindungen
- ; bzw. Zuweisungen kommt es nicht an (d.h. es sind sowohl LET
- ; als auch LET* bzw. sowohl PSETQ als auch SETQ möglich).
- (defun destructure (pattern form)
- (labels ((destructure-tree (pattern form helpvar-count)
- ; helpvar-count = Anzahl der belegten Hilfsvariablen
- (cond ((empty-tree-p pattern) nil)
- ((atom pattern) (list (list pattern form)))
- ((empty-tree-p (car pattern))
- (destructure-tree (cdr pattern) `(CDR ,form) helpvar-count)
- )
- ((empty-tree-p (cdr pattern))
- (destructure-tree (car pattern) `(CAR ,form) helpvar-count)
- )
- (t ; muß form zwischendurch einer Hilfsvariablen zuweisen
- (let ((helpvar (helpvar helpvar-count)))
- (nconc (destructure-tree (car pattern) `(CAR (SETQ ,helpvar ,form)) (1+ helpvar-count))
- (destructure-tree (cdr pattern) `(CDR ,helpvar) helpvar-count)
- )) ) ) ) )
- (or (destructure-tree pattern form 0)
- ; keine Variablen -> muß trotzdem form auswerten!
- (list (list (helpvar 0) form))
- ) ) )
-
- ; Liefert zu einer Liste (var ...) von Variablen ohne Initialisierungsformen
- ; die Bindungsliste ((var var-init) ...), wobei var-init mit den declspecs
- ; verträglich ist.
- (defun default-bindings (vars declspecs)
- ; Verwende NIL oder 0 oder 0.0 - falls das paßt -
- ; oder verwende NIL und erweitere die Typdeklaration.
- (let ((bindings (mapcar #'(lambda (var) (list var 'NIL)) vars)))
- (dolist (declspec declspecs)
- ; declspec hat die Form (TYPE type . vars)
- (let ((type (second declspec)) h)
- (cond ((typep 'NIL type) ) ; OK
- ((or (typep (setq h '0) type) (typep (setq h '0.0) type))
- (dolist (var (cddr declspec))
- (setf (second (find var bindings :key #'first)) h)
- ))
- (t (setf (second declspec) `(OR NULL ,type)))
- ) ) )
- bindings
- ) )
-
- ;; Weitere Hilfsfunktionen:
-
- ; (wrap-initialisations initialisations form) wickelt eine (umgedrehte!)
- ; Liste von Initialisierungen um form herum und liefert die neue Form.
- (defun wrap-initialisations (initialisations form)
- (dolist (initialisation initialisations)
- (let ((name (first initialisation))
- (bindings (second initialisation))
- (declarations (third initialisation)))
- (setq form
- `(,name
- ,@(case name
- (MULTIPLE-VALUE-BIND bindings)
- (LET `(,bindings))
- (PROGN bindings)
- )
- ,@(if declarations `((DECLARE ,@declarations)))
- ,@(cdddr initialisation)
- ,form
- )
- ) ) )
- form
- )
-
- (defvar *last-it*) ; Variable, die das letzte Test-Ergebnis ("it") enthält
- (defvar *used-it*) ; Flag, ob diese Variable benutzt wird
-
- ; Das Gros des Expanders:
- (defun expand-loop (*whole* body)
- (let ((body-rest body) ; alle Parse-Funktionen verkürzen body-rest
- (block-name 'NIL) ; Name des umgebenden BLOCKs
- (already-within-main nil) ; im zweiten Teil von {variables}* {main}* ?
- (*helpvars* (make-array 1 :fill-pointer 0 :adjustable t)) ; Vektor
- ; mit Hilfsvariablen fürs Destructuring
- (*last-it* nil) ; Variable, die das letzte Test-Ergebnis ("it") enthält
- (acculist-var nil) ; Akkumulationsvariable für collect, append etc.
- (accunum-var nil) ; Akkumulationsvariable für count, sum etc.
- (accu-vars-nil nil) ; Akkumulationsvariablen mit Initialwert NIL
- (accu-vars-0 nil) ; Akkumulationsvariablen mit Initialwert 0
- (accu-declarations nil) ; Typdeklarationen (umgedrehte Liste von declspecs)
- (initialisations nil) ; Bindungen: ((everytime seen-= LET/LET*/MVBIND bindings declspecs ...) ...)
- ; (umgedrehte Liste)
- (seen-for-as-= nil) ; schon eine FOR-AS-= Klausel gesehen?
- (initially-code nil) ; initially-Code (umgedrehte Liste)
- (stepbefore-code nil) ; Code zum Abbruch vor dem Schleifendurchlauf (umgedrehte Liste)
- (main-code nil) ; Code im Hauptteil der Schleife (umgedrehte Liste)
- (stepafter-code nil) ; Code zur Vorbereitung des nächsten Schleifendurchlaufs (umgedrehte Liste)
- (accu-vars-nreverse nil) ; Akkumulationsvariablen, die am Schluß umzudrehen sind
- (finally-code nil) ; finally-Code (umgedrehte Liste)
- (results nil) ; Liste von Ergebnisformen (höchstens eine!)
- )
- (labels
- ((next-kw () ; Schaut, ob als nächstes ein Keyword kommt.
- ; Wenn ja, wird es geliefert. Wenn nein, Ergebnis NIL.
- (and (consp body-rest) (loop-keywordp (first body-rest)))
- )
- (parse-kw-p (kw) ; Schaut, ob als nächstes das Keyword kw kommt.
- ; Wenn ja, wird es übergangen. Wenn nein, Ergebnis NIL.
- (and (consp body-rest) (eq (loop-keywordp (first body-rest)) kw)
- (progn (pop body-rest) t)
- ) )
- (parse-form (kw) ; Nach kw: parst expr
- (unless (consp body-rest) (loop-syntax-error kw))
- (pop body-rest)
- )
- (parse-form-or-it (kw) ; Nach kw: parst expr, das auch 'it' sein kann
- (unless (consp body-rest) (loop-syntax-error kw))
- (let ((form (pop body-rest)))
- (if (eq (loop-keywordp form) 'it)
- (if *last-it*
- (progn (setq *used-it* t) *last-it*)
- (loop-syntax-error 'it)
- )
- form
- ) ) )
- (parse-var-typespec () ; parst var [typespec]
- ; Liefert das Variablen-Pattern und eine Liste von declspecs.
- (unless (consp body-rest)
- (error
- #L{
- DEUTSCH "~S: Variable fehlt."
- ENGLISH "~S: missing variable"
- FRANCAIS "~S : Il manque une variable."
- }
- 'loop
- ) )
- (let ((pattern (pop body-rest))
- (typedecl nil))
- (block nil
- (unless (consp body-rest) (return))
- (case (loop-keywordp (first body-rest))
- ((NIL) ; kein Loop-Keyword -> als Typespec interpretieren
- (setq typedecl (pop body-rest))
- (unless (simple-type-p typedecl)
- (warn
- #L{
- DEUTSCH "~S: Nach ~S wird ~S als Typspezifikation interpretiert."
- ENGLISH "~S: After ~S, ~S is interpreted as a type specification"
- FRANCAIS "~S : Après ~S, on traite ~S comme une spécification d'un type."
- }
- 'loop pattern typedecl
- )) )
- ((OF-TYPE) ; OF-TYPE -> danach kommt ein Typespec
- (pop body-rest)
- (setq typedecl (parse-form 'of-type))
- )
- (T (return)) ; sonstiges
- )
- (setq typedecl (destructure-type pattern typedecl))
- )
- (values pattern typedecl)
- ) )
- (parse-progn () ; parst: {expr}*
- ; und liefert die Liste der Formen
- (let ((list nil))
- (loop
- (unless (and (consp body-rest)
- (not (loop-keywordp (first body-rest)))
- )
- (return)
- )
- (push (pop body-rest) list)
- )
- (nreverse list)
- ) )
- (parse-unconditional () ; parst ein Unconditional
- ; unconditional ::= {do | doing} {expr}*
- ; unconditional ::= return expr
- ; Liefert eine Lisp-Form oder NIL wenn's kein Unconditional war.
- (let ((kw (next-kw)))
- (case kw
- ((DO DOING)
- (pop body-rest)
- `(PROGN ,@(parse-progn))
- )
- ((RETURN)
- (pop body-rest)
- `(RETURN-FROM ,block-name ,(parse-form-or-it kw))
- )
- (t 'NIL)
- ) ) )
- (parse-clause () ; parst eine Clause
- ; clause ::= accumulation | conditional | unconditional
- ; accumulation ::= {collect | collecting | append | appending |
- ; nconc | nconcing} expr [into var]
- ; accumulation ::= {count | counting | sum | summing |
- ; maximize | maximizing | minimize |
- ; minimizing} expr [into var] [typespec]
- ; conditional ::= {if | when | unless} expr clause {and clause}*
- ; [else clause {and clause}*] [end]
- ; Liefert eine Lisp-Form oder NIL wenn's keine Clause war.
- (or (parse-unconditional)
- (let ((kw (next-kw)))
- (case kw
- ((COLLECT COLLECTING APPEND APPENDING NCONC NCONCING)
- (pop body-rest)
- (let ((form (parse-form-or-it kw))
- (accuvar nil))
- (when (parse-kw-p 'into)
- (unless (and (consp body-rest)
- (symbolp (setq accuvar (pop body-rest)))
- )
- (loop-syntax-error 'into)
- ) )
- (if accuvar
- (pushnew accuvar accu-vars-nreverse)
- (progn
- (setq accuvar
- (or acculist-var (setq acculist-var (gensym)))
- )
- (push `(SYS::LIST-NREVERSE ,accuvar) results)
- ) )
- (push accuvar accu-vars-nil)
- `(SETQ ,accuvar
- (,(case kw
- ((COLLECT COLLECTING) 'CONS)
- ((APPEND APPENDING) 'REVAPPEND)
- ((NCONC NCONCING) 'NRECONC)
- )
- ,form
- ,accuvar
- ) )
- ))
- ((COUNT COUNTING SUM SUMMING MAXIMIZE MAXIMIZING MINIMIZE MINIMIZING)
- (pop body-rest)
- (let ((form (parse-form-or-it kw))
- (accuvar nil))
- (when (parse-kw-p 'into)
- (unless (and (consp body-rest)
- (symbolp (setq accuvar (pop body-rest)))
- )
- (loop-syntax-error 'into)
- ) )
- (unless accuvar
- (setq accuvar
- (or accunum-var (setq accunum-var (gensym)))
- )
- (push accuvar results)
- )
- (when (and (consp body-rest)
- (not (loop-keywordp (first body-rest)))
- )
- (let ((type (pop body-rest)))
- (case kw
- ((MAXIMIZE MAXIMIZING MINIMIZE MINIMIZING)
- (setq type `(OR NULL ,type)) ; wegen Startwert NIL
- ) )
- (push `(TYPE ,type ,accuvar) accu-declarations)
- ) )
- (case kw
- ((MAXIMIZE MAXIMIZING MINIMIZE MINIMIZING)
- (push accuvar accu-vars-nil)
- )
- ((COUNT COUNTING SUM SUMMING)
- (push accuvar accu-vars-0)
- ) )
- (case kw
- ((COUNT COUNTING) `(WHEN ,form (INCF ,accuvar)))
- ((SUM SUMMING) `(SETQ ,accuvar (+ ,accuvar ,form)))
- ((MAXIMIZE MAXIMIZING) `(SETQ ,accuvar (MAX-IF ,form ,accuvar)))
- ((MINIMIZE MINIMIZING) `(SETQ ,accuvar (MIN-IF ,form ,accuvar)))
- )) )
- ((IF WHEN UNLESS)
- (pop body-rest)
- (let* ((condition (parse-form kw))
- (it-var (gensym))
- used-it
- (true-form
- (let ((*last-it* it-var) (*used-it* nil))
- (prog1
- (parse-clauses kw)
- (setq used-it *used-it*)
- ) ) )
- (false-form 'NIL))
- (when (parse-kw-p 'else)
- (setq false-form
- (let ((*last-it* it-var) (*used-it* nil))
- (prog1
- (parse-clauses 'else)
- (setq used-it (or used-it *used-it*))
- ) ) ) )
- (parse-kw-p 'end)
- (when used-it
- (psetq it-var `((,it-var ,condition))
- condition it-var
- ) )
- (let ((form
- `(IF ,(if (eq kw 'UNLESS)
- `(NOT ,condition) ; UNLESS
- `,condition ; IF, WHEN
- )
- ,true-form
- ,false-form
- )
- ))
- (if used-it `(LET ,it-var ,form) `,form)
- )) )
- (t 'NIL)
- ) ) ) )
- (parse-clauses (kw) ; Nach kw: parst clause {and clause}*
- ; oder kurz {clause}+{and}
- ; Liefert eine Lisp-Form.
- (let ((clauses nil))
- (loop
- (let ((clause (parse-clause)))
- (unless clause (loop-syntax-error kw))
- (push clause clauses)
- )
- (unless (parse-kw-p 'and) (return))
- (setq kw 'and)
- (setq *last-it* nil) ; 'it' ist nur in der ersten Klausel gültig
- )
- `(PROGN ,@(nreverse clauses))
- ) )
- ; Binden und Initialisieren von Variablen:
- ; Nach dpANS 6.1.1.4 gelten zwei Grundregeln:
- ; - Beim Initialisieren von FOR-AS Variablen (außer FOR-AS-=) sind
- ; mindestens alle vorherigen FOR-AS Variablen sichtbar.
- ; - Beim Initialisieren von FOR-AS-= Variablen sind alle FOR-AS Variablen
- ; sichtbar.
- ; Man könnte erst alle Variablen binden und dann im initially-code
- ; die Initialisierungen durchführen. Wir führen demgegenüber zwei
- ; Optimierungen durch:
- ; - Falls vor der FOR-AS Variablen keine FOR-AS-= Klausel kommt,
- ; braucht die Variable zum Zeitpunkt ihrer Initialisierung nicht
- ; sichtbar zu sein, und wir verlagern ihre Initialisierung nach
- ; vorne, zur Bindung. (Ausnahme: Wenn die Initialisierung - z.B. bei
- ; AREF - erst nach Überprüfung der Abbruchbedingungen erfolgen darf.)
- ; - Falls eine Variable gar nicht sichtbar zu sein braucht, weil keine
- ; FOR-AS-= Klausel vorkommt und hinter ihr auch keine andere FOR-AS
- ; Klausel stört, können die Bindung und die Initialiserung der
- ; Variablen ins Schleifeninnere verschoben werden.
- (note-initialisation (everytime requires-stepbefore specform bindings declspecs &rest more-forms)
- (when (or bindings declspecs more-forms)
- (push (list* everytime (or seen-for-as-= requires-stepbefore) specform bindings declspecs more-forms)
- initialisations
- ) ) )
- )
- ;; Los geht's!
- ; parst: [named name]
- (when (parse-kw-p 'named)
- (unless (and (consp body-rest) (symbolp (first body-rest)))
- (loop-syntax-error 'named)
- )
- (setq block-name (pop body-rest))
- )
- (loop
- ; main ::= clause | termination | initially | finally |
- ; with | for-as | repeat
- ; termination ::= {while | until | always | never | thereis} expr
- ; initially ::= initially {expr}*
- ; finally ::= finally { unconditional | {expr}* }
- ; with ::= with {var-typespec [= expr]}+{and}
- ; for-as ::= {for | as} {var-typespec ...}+{and}
- ; repeat ::= repeat expr
- (unless (consp body-rest) (return))
- (let ((clause (parse-clause)))
- (if clause
- (progn (setq already-within-main t) (push clause main-code))
- (let ((kw (loop-keywordp (first body-rest))))
- (case kw
- ((WHILE UNTIL ALWAYS NEVER THEREIS)
- (pop body-rest)
- (setq already-within-main t)
- (let ((form (parse-form kw)))
- (push (case kw
- (WHILE `(UNLESS ,form (LOOP-FINISH)) )
- (UNTIL `(WHEN ,form (LOOP-FINISH)) )
- (ALWAYS
- (push 'T results)
- `(UNLESS ,form (RETURN-FROM ,block-name 'NIL))
- )
- (NEVER
- (push 'T results)
- `(WHEN ,form (RETURN-FROM ,block-name 'NIL))
- )
- (THEREIS
- (let ((dummy (gensym)))
- `(BLOCK ,dummy
- (RETURN-FROM ,block-name
- (OR ,form (RETURN-FROM ,dummy NIL))
- ) )
- ) )
- )
- main-code
- )) )
- ((INITIALLY)
- (pop body-rest)
- (push `(PROGN ,@(parse-progn)) initially-code)
- )
- ((FINALLY)
- (pop body-rest)
- (push (or (parse-unconditional) `(PROGN ,@(parse-progn)))
- finally-code
- ))
- ((WITH FOR AS REPEAT)
- (pop body-rest)
- (when already-within-main
- (warn
- #L{
- DEUTSCH "~S: ~A-Klauseln sollten vor dem Schleifeninhalt kommen."
- ENGLISH "~S: ~A clauses should occur before the loop's main body"
- FRANCAIS "~S : Les phrases ~A doivent apparaître avant le contenu principale de la boucle."
- }
- 'loop (symbol-name kw)
- ) )
- (case kw
- ((WITH)
- (let ((bindings nil)
- (declspecs nil))
- (loop
- (let (new-bindings)
- (multiple-value-bind (pattern new-declspecs) (parse-var-typespec)
- (if (parse-kw-p '=)
- ; Initialisierungsform angegeben.
- (let ((form (parse-form '=)))
- (setq new-bindings (destructure pattern form))
- )
- ; keine Initialisierungsform angegeben.
- (setq new-bindings (default-bindings (destructure-vars pattern) new-declspecs))
- )
- (setq bindings (revappend new-bindings bindings))
- (setq declspecs (revappend new-declspecs declspecs))
- ) )
- (unless (parse-kw-p 'and) (return))
- (setq kw 'and)
- )
- (note-initialisation nil nil 'LET (nreverse bindings) (nreverse declspecs))
- ))
- ((FOR AS)
- ; for-as ::= {for | as} for-as-clause {and [{for | as}] for-as-clause}*
- ; for-as-clause ::= var-typespec
- ; [{from | downfrom | upfrom} expr]
- ; [{to | downto | upto | below | above} expr]
- ; [by expr]
- ; for-as-clause ::= var-typespec {in | on} expr [by expr]
- ; for-as-clause ::= var-typespec = expr [then expr]
- ; for-as-clause ::= var-typespec across expr
- ; for-as-clause ::= var-typespec being {each | the}
- ; {hash-key[s] | hash-value[s]}
- ; {in | of} expr
- ; [using ( {hash-value | hash-key} var ) ]
- ; for-as-clause ::= var-typespec being {each | the}
- ; {symbol[s] | present-symbol[s] | internal-symbol[s] | external-symbol[s]}
- ; {in | of} expr
- (let ((bindings nil)
- (declspecs nil)
- (initialisations nil)
- (stepafter nil))
- (labels ((note-initialisation (&rest args)
- ;; Aufrufe von note-initialisation müssen temporär aufgehoben werden.
- (push args initialisations)
- )
- (note-endtest (endtest)
- (note-initialisation nil t
- 'PROGN
- (list endtest)
- nil
- ) )
- (note-endtest-both (endtest)
- (note-endtest endtest)
- (push nil stepafter-code)
- (push endtest stepafter-code)
- )
- (note-endtest-assign (endtest destructured-pattern new-declspecs)
- (note-endtest-both
- `(IF ,endtest
- (LOOP-FINISH)
- (SETQ ,@(apply #'append destructured-pattern))
- ) )
- (note-initialisation nil t
- 'LET
- destructured-pattern
- new-declspecs
- ) )
- )
- (loop
- (multiple-value-bind (pattern new-declspecs) (parse-var-typespec)
- (let ((preposition (next-kw)))
- (case preposition
- ((IN ON)
- (pop body-rest)
- (let ((start-form (parse-form preposition))
- (step-function-form '(FUNCTION CDR))
- (step-function-var nil))
- (when (parse-kw-p 'by)
- (setq step-function-form (parse-form 'by))
- )
- (unless (and (consp step-function-form)
- (eq (first step-function-form) 'FUNCTION)
- (consp (cdr step-function-form))
- (null (cddr step-function-form))
- (symbolp (second step-function-form))
- )
- (setq step-function-var (gensym))
- )
- (let ((var (gensym))) ; Hilfsvariable
- (push `(,var ,start-form) bindings)
- (when step-function-var
- (push `(,step-function-var ,step-function-form) bindings)
- )
- (note-endtest-assign
- `(ENDP ,var)
- (destructure pattern (if (eq preposition 'IN) `(CAR ,var) `,var))
- new-declspecs
- )
- (push
- (list var
- (if step-function-var
- `(FUNCALL ,step-function-var ,var)
- `(,(second step-function-form) ,var)
- ) )
- stepafter
- )
- )) )
- (=
- (pop body-rest)
- (let* ((first-form (parse-form 'preposition))
- (then-form first-form))
- (when (parse-kw-p 'then)
- (setq then-form (parse-form 'then))
- )
- (setq bindings
- (revappend (destructure pattern first-form)
- bindings
- ) )
- (setq declspecs (revappend new-declspecs declspecs))
- (unless (constantp first-form)
- (setq seen-for-as-= t)
- )
- (unless (and (eql first-form then-form) (constantp then-form))
- (setq stepafter (revappend (destructure pattern then-form) stepafter))
- )) )
- (ACROSS
- (pop body-rest)
- (let ((vector-form (parse-form preposition))
- (vector-var (gensym))
- (index-var (gensym)))
- (push `(,vector-var ,vector-form) bindings)
- (push `(,index-var 0) bindings)
- (note-endtest-assign
- `(>= ,index-var (LENGTH ,vector-var))
- (destructure pattern `(AREF ,vector-var ,index-var))
- new-declspecs
- )
- (push (list index-var `(1+ ,index-var)) stepafter)
- ))
- (BEING
- (pop body-rest)
- (let ((plural (next-kw)))
- (case plural
- ((EACH THE) )
- (t (loop-syntax-error 'being))
- )
- (pop body-rest)
- (let ((preposition (next-kw)))
- (case preposition
- ((HASH-KEY HASH-VALUE
- SYMBOL PRESENT-SYMBOL INTERNAL-SYMBOL EXTERNAL-SYMBOL
- )
- (when (eq plural 'THE)
- (warn
- #L{
- DEUTSCH "~S: Nach ~S sollte ein Plural kommen, nicht ~A"
- ENGLISH "~S: After ~S a plural loop keyword is required, not ~A"
- FRANCAIS "~S : Après ~S, on s'attend au pluriel et non ~A"
- }
- 'loop plural (symbol-name preposition)
- )) )
- ((HASH-KEYS HASH-VALUES
- SYMBOLS PRESENT-SYMBOLS INTERNAL-SYMBOLS EXTERNAL-SYMBOLS
- )
- (when (eq plural 'EACH)
- (warn
- #L{
- DEUTSCH "~S: Nach ~S sollte ein Singular kommen, nicht ~A"
- ENGLISH "~S: After ~S a singular loop keyword is required, not ~A"
- FRANCAIS "~S : Après ~S, on s'attend au singulier et non ~A"
- }
- 'loop plural (symbol-name preposition)
- )) )
- (t (loop-syntax-error plural))
- )
- (pop body-rest)
- (case (next-kw)
- ((IN OF) )
- (t (loop-syntax-error preposition))
- )
- (pop body-rest)
- (let ((form (parse-form preposition)))
- (case preposition
- ((HASH-KEY HASH-KEYS HASH-VALUE HASH-VALUES)
- (let ((other-pattern nil))
- (when (parse-kw-p 'using)
- (unless (and (consp body-rest)
- (consp (car body-rest))
- (consp (cdar body-rest))
- (null (cddar body-rest))
- (case (loop-keywordp (caar body-rest))
- ((HASH-KEY HASH-KEYS)
- (case preposition
- ((HASH-VALUE HASH-VALUES) t) (t nil)
- ))
- ((HASH-VALUE HASH-VALUES)
- (case preposition
- ((HASH-KEY HASH-KEYS) t) (t nil)
- ))
- ) )
- (loop-syntax-error 'using)
- )
- (setq other-pattern (second (pop body-rest)))
- )
- (let ((state-var (gensym))
- (nextp-var (gensym))
- (nextkey-var (gensym))
- (nextvalue-var (gensym)))
- (multiple-value-bind (nextmain-var nextother-var)
- (case preposition
- ((HASH-KEY HASH-KEYS) (values nextkey-var nextvalue-var))
- ((HASH-VALUE HASH-VALUES) (values nextvalue-var nextkey-var))
- )
- (push `(,state-var (SYS::HASH-TABLE-ITERATOR ,form)) bindings)
- (note-initialisation t nil
- 'MULTIPLE-VALUE-BIND
- `((,nextp-var ,nextkey-var ,nextvalue-var)
- (SYS::HASH-TABLE-ITERATE ,state-var)
- )
- (unless other-pattern `((IGNORE ,nextother-var)))
- )
- (note-endtest-both `(UNLESS ,nextp-var (LOOP-FINISH)))
- (note-initialisation t nil
- 'LET
- (destructure pattern nextmain-var)
- new-declspecs
- )
- (when other-pattern
- (note-initialisation t nil
- 'LET
- (destructure other-pattern nextother-var)
- nil
- ) )
- )) ) )
- ((SYMBOL SYMBOLS PRESENT-SYMBOL PRESENT-SYMBOLS
- INTERNAL-SYMBOL INTERNAL-SYMBOLS EXTERNAL-SYMBOL EXTERNAL-SYMBOLS
- )
- (let ((flags (case preposition
- ((SYMBOL SYMBOLS) '(:internal :external :inherited))
- ((PRESENT-SYMBOL PRESENT-SYMBOLS) '(:internal :external))
- ((INTERNAL-SYMBOL INTERNAL-SYMBOLS) '(:internal))
- ((EXTERNAL-SYMBOL EXTERNAL-SYMBOLS) '(:external))
- ) )
- (state-var (gensym))
- (nextp-var (gensym))
- (nextsym-var (gensym)))
- (push `(,state-var (SYS::PACKAGE-ITERATOR ,form ',flags))
- bindings
- )
- (note-initialisation t nil
- 'MULTIPLE-VALUE-BIND
- `((,nextp-var ,nextsym-var)
- (SYS::PACKAGE-ITERATE ,state-var)
- )
- nil
- )
- (note-endtest-both `(UNLESS ,nextp-var (LOOP-FINISH)))
- (note-initialisation t nil
- 'LET
- (destructure pattern nextsym-var)
- new-declspecs
- )) )
- )) ) ) )
- (t
- (unless (symbolp pattern) (loop-syntax-error kw))
- (let ((step-start-p nil)
- (step-end-p nil)
- (step-by-p nil)
- step-start-form
- step-end-form
- step-by-form)
- ; erste optionale Klausel:
- (block nil
- (case preposition
- (FROM (setq step-start-p 't))
- (UPFROM (setq step-start-p 'up))
- (DOWNFROM (setq step-start-p 'down))
- (t (return))
- )
- (pop body-rest)
- (setq step-start-form (parse-form preposition))
- )
- ; zweite optionale Klausel:
- (block nil
- (setq preposition (next-kw))
- (case preposition
- (TO (setq step-end-p 't))
- ((UPTO BELOW) (setq step-end-p 'up))
- ((DOWNTO ABOVE) (setq step-end-p 'down))
- (t (return))
- )
- (pop body-rest)
- (setq step-end-form (parse-form preposition))
- )
- ; dritte optionale Klausel:
- (when (parse-kw-p 'by)
- (setq step-by-p t)
- (setq step-by-form (parse-form 'by))
- )
- ; Iterationsrichtung bestimmen:
- (let ((step-direction
- (if (or (eq step-start-p 'down) (eq step-end-p 'down))
- (if (or (eq step-start-p 'up) (eq step-end-p 'up))
- (error
- #L{
- DEUTSCH "~S: Iterationsrichtung nach ~A unklar."
- ENGLISH "~S: questionable iteration direction after ~A"
- FRANCAIS "~S : On compte vers le haut ou vers le bas après ~A ?"
- }
- 'loop (symbol-name kw)
- )
- 'down
- )
- 'up
- )) )
- ; Startwert bestimmen:
- (unless step-start-p
- (if (eq step-direction 'down)
- ; Abwärtsiteration ohne Startwert ist nicht erlaubt.
- ; Die zweite optionale Klausel (d.h. preposition) muß abwärts zeigen.
- (error
- #L{
- DEUTSCH "~S: Zusammen mit ~A muß FROM oder DOWNFROM angegeben werden."
- ENGLISH "~S: specifying ~A requires FROM or DOWNFROM"
- FRANCAIS "~S : ~A ne va qu'avec FROM ou DOWNFROM"
- }
- 'loop (symbol-name preposition)
- )
- ; Aufwärtsiteration -> Startwert 0
- (setq step-start-form '0)
- ) )
- (push `(,pattern ,step-start-form) bindings)
- (setq declspecs (revappend new-declspecs declspecs))
- ; Endwert bestimmen:
- (when step-end-p
- (unless (constantp step-end-form)
- (let ((step-end-var (gensym)))
- (push `(,step-end-var ,step-end-form) bindings)
- (setq step-end-form step-end-var)
- ) ) )
- ; Schrittweite bestimmen:
- (unless step-by-p (setq step-by-form '1))
- (unless (constantp step-by-form)
- (let ((step-by-var (gensym)))
- (push `(,step-by-var ,step-by-form) bindings)
- (setq step-by-form step-by-var)
- ) )
- ; Endtest bestimmen:
- (when step-end-p
- (let* ((compfun
- (if (eq step-direction 'up)
- (if (eq preposition 'below) '>= '>) ; up
- (if (eq preposition 'above) '<= '<) ; down
- ) )
- (endtest
- (if (and (constantp step-end-form) (zerop (eval step-end-form)))
- (case compfun
- (>= `(NOT (MINUSP ,pattern)) )
- (> `(PLUSP ,pattern) )
- (<= `(NOT (PLUSP ,pattern)) )
- (< `(MINUSP ,pattern) )
- )
- `(,compfun ,pattern ,step-end-form)
- )) )
- (note-endtest-both `(WHEN ,endtest (LOOP-FINISH)))
- ) )
- (push
- (list pattern `(,(if (eq step-direction 'up) '+ '-) ,pattern ,step-by-form))
- stepafter
- )) ) )
- ) ) )
- (unless (parse-kw-p 'and) (return))
- (setq kw 'and)
- (case (next-kw) ((FOR AS) (pop body-rest)))
- ) )
- (when (setq stepafter (apply #'append (nreverse stepafter)))
- (push `(PSETQ ,@stepafter) stepafter-code)
- )
- (push 'NIL stepafter-code) ; Markierung für spätere Initialisierungen
- (note-initialisation nil nil 'LET (nreverse bindings) (nreverse declspecs))
- (dolist (initialisation (nreverse initialisations))
- (apply #'note-initialisation
- (and (first initialisation) stepafter-code) (rest initialisation)
- ) )
- ))
- ((REPEAT)
- (let ((form (parse-form kw))
- (var (gensym)))
- (note-initialisation nil nil 'LET `((,var ,form)) nil)
- (push `(UNLESS (PLUSP ,var) (LOOP-FINISH)) stepbefore-code)
- (push `(SETQ ,var (1- ,var)) stepafter-code)
- ))
- ))
- (t (error
- #L{
- DEUTSCH "~S: Illegale Syntax bei ~S in ~S"
- ENGLISH "~S: illegal syntax near ~S in ~S"
- FRANCAIS "~S : syntaxe illégale près de ~S dans ~S"
- }
- 'loop (first body-rest) *whole*
- ) )
- ) ) ) ) )
- ; Noch einige semantische Tests:
- (setq results (delete-duplicates results :test #'equal))
- (when (> (length results) 1)
- (error
- #L{
- DEUTSCH "~S: Ergebnis der Schleife ~S nicht eindeutig spezifiziert."
- ENGLISH "~S: ambiguous result of loop ~S"
- FRANCAIS "~S : Le résultat de la boucle ~S est ambigu."
- }
- 'loop *whole*
- ) )
- (unless (null results)
- (push `(RETURN-FROM ,block-name ,@results) finally-code)
- )
- ; Initialisierungen abarbeiten und optimieren:
- (let ((initialisations1 nil)
- (initialisations2 nil))
- (let ((last-initialisations initialisations))
- (unless seen-for-as-=
- (loop
- (when (null initialisations) (return))
- (let ((initialisation (first initialisations)))
- (unless (first initialisation) (return))
- ; letzte Initialiserungsklausel nach initialisations2 verschieben:
- (pop initialisations)
- (when (eq (caddr initialisation) 'MULTIPLE-VALUE-BIND)
- (setq initialisations last-initialisations) (return))
- (push (cddr initialisation) initialisations2)
- ) ) ) )
- (setq initialisations (nreverse initialisations))
- (loop
- (when (null initialisations) (return))
- (let* ((initialisation (pop initialisations))
- (everytime (first initialisation))
- (requires-stepbefore (second initialisation)))
- (setq initialisation (cddr initialisation))
- (let* ((name (first initialisation))
- (bindings (second initialisation))
- (declarations (third initialisation))
- (vars (case name
- (MULTIPLE-VALUE-BIND (first bindings))
- (PROGN '())
- (otherwise (mapcar #'first bindings))))
- (initforms
- (cons (case name
- (MULTIPLE-VALUE-BIND `(MULTIPLE-VALUE-SETQ ,@bindings))
- (LET `(SETQ ,@(apply #'append bindings)))
- (PROGN (car bindings))
- )
- (cdddr initialisation)
- )) )
- (if requires-stepbefore
- ; wegen seen-for-as-= oder AREF nicht optimierbar
- (progn
- (push
- (list 'LET
- (default-bindings vars declarations)
- declarations
- )
- initialisations1
- )
- (if everytime
- (setq stepbefore-code (nreverse (revappend initforms stepbefore-code)))
- (setq initially-code (revappend initforms initially-code))
- ) )
- ; Initialisierungsklausel nach initialisations1 schaffen:
- (progn
- (push initialisation initialisations1)
- ; und evtl. stepafter-code erweitern:
- (when everytime
- (setf (cdr everytime) (nconc (cdr everytime) initforms))
- ) )
- ) ) ) )
- (setq initialisations1 (nreverse initialisations1))
- (push
- (list 'LET
- `(,@(map 'list #'(lambda (var) `(,var NIL)) *helpvars*)
- ,@(mapcar #'(lambda (var) `(,var NIL)) (delete-duplicates accu-vars-nil))
- ,@(mapcar #'(lambda (var) `(,var 0)) (delete-duplicates accu-vars-0))
- )
- (nreverse accu-declarations)
- )
- initialisations1
- )
- (flet ((split (l)
- (let (sublist newlist)
- (dolist (item l)
- (if item
- (push item sublist)
- (setq newlist (revappend sublist newlist)
- sublist nil)))
- (setq newlist (revappend sublist newlist))
- newlist)))
- `(MACROLET ((LOOP-FINISH () (LOOP-FINISH-ERROR)))
- (BLOCK ,block-name
- ,(wrap-initialisations (nreverse initialisations1)
- `(MACROLET ((LOOP-FINISH () '(GO END-LOOP)))
- (TAGBODY
- ,@(if initially-code `((PROGN ,@(nreverse initially-code))))
- BEGIN-LOOP
- ,@(if stepbefore-code `((PROGN ,@(nreverse stepbefore-code))))
- ,(wrap-initialisations (nreverse initialisations2)
- `(PROGN ,@(nreverse main-code))
- )
- ,@(if stepafter-code `((PROGN ,@(split stepafter-code))))
- (GO BEGIN-LOOP)
- END-LOOP
- ,@(mapcar #'(lambda (var) `(SETQ ,var (SYS::LIST-NREVERSE ,var)))
- accu-vars-nreverse
- )
- (MACROLET ((LOOP-FINISH () (LOOP-FINISH-WARN) '(GO END-LOOP)))
- ,@(nreverse finally-code)
- ) ) )
- )
- ) ) )
- ) ) ) )
-
- ;; Der eigentliche Macro:
-
- (defmacro loop (&whole whole &body body)
- (if (some #'loop-keywordp body)
- ; neue Form von LOOP
- (expand-loop whole body)
- ; alte Form von LOOP
- (let ((tag (gensym)))
- `(BLOCK NIL (TAGBODY ,tag ,@body (GO ,tag)))
- ) ) )
- (defmacro loop-finish (&whole whole)
- (error
- #L{
- DEUTSCH "~S ist nur aus ~S heraus möglich."
- ENGLISH "~S is possible only from within ~S"
- FRANCAIS "~S n'est possible qu'à l'intérieur de ~S."
- }
- whole 'loop
- ) )
- (defun loop-finish-warn ()
- (warn
- #L{
- DEUTSCH "Von der Verwendung von ~S in FINALLY-Klauseln wird abgeraten. Das kann nämlich zu Endlosschleifen führen."
- ENGLISH "Use of ~S in FINALLY clauses is deprecated because it can lead to infinite loops."
- FRANCAIS "On recommande de ne pas utiliser ~S dans des phrases FINALLY car cela peut amener à des boucles infinies."
- }
- '(loop-finish)
- ) )
- (defun loop-finish-error ()
- (error
- #L{
- DEUTSCH "~S ist hier nicht möglich."
- ENGLISH "~S is not possible here"
- FRANCAIS "~S n'est pas possible ici."
- }
- '(loop-finish)
- ) )
-
- )
-
- ;; Run-Time-Support:
-
- (defun max-if (x y)
- (if y (max x y) x)
- )
- (defun min-if (x y)
- (if y (min x y) x)
- )
-
-