diff --git a/goal_src/goal-lib.gc b/goal_src/goal-lib.gc index 5674421efd..ba65ed04f7 100644 --- a/goal_src/goal-lib.gc +++ b/goal_src/goal-lib.gc @@ -327,71 +327,98 @@ ) ) -(defmacro while (test &rest body) +(defmacro continue (&key (from #f)) + "Skips the remainder of the current loop iteration. Optionally continue from a labeled loop." + `(return-from ,(string->symbol (if from (string-append (symbol->string from) "-continue") "continue")) #f) + ) + +(defmacro break (&key (from #f)) + "Exits the current loop immediately. Optionally break from a labeled loop." + `(return-from ,(string->symbol (if from (string-append (symbol->string from) "-break") "break")) #f) + ) + +(defmacro while (test &key (label #f) &rest body) "While loop. The test is evaluated before body." (with-gensyms (reloop test-exit) - `(begin - (goto ,test-exit) - (label ,reloop) - ,@body - (label ,test-exit) - (when-goto ,test ,reloop) - #f - ) + (let ((break-label (string->symbol (if label (string-append (symbol->string label) "-break") "break"))) + (continue-label (string->symbol (if label (string-append (symbol->string label) "-continue") "continue")))) + `(block ,break-label + (goto ,test-exit) + (label ,reloop) + (block ,continue-label + ,@(if (null? body) (list `(return-from ,continue-label #f)) body) + ) + (label ,test-exit) + (when-goto ,test ,reloop) + #f + ) ) ) +) -(defmacro until (test &rest body) +(defmacro until (test &key (label #f) &rest body) "Until loop. The body is evaluated before the test." (with-gensyms (reloop) - `(begin - (label ,reloop) - ,@body - (when-goto (not ,test) ,reloop) - ) + (let ((break-label (string->symbol (if label (string-append (symbol->string label) "-break") "break"))) + (continue-label (string->symbol (if label (string-append (symbol->string label) "-continue") "continue")))) + `(block ,break-label + (label ,reloop) + (block ,continue-label + ,@(if (null? body) (list `(return-from ,continue-label #f)) body) + ) + (when-goto (not ,test) ,reloop) + ) ) ) +) -(defmacro dotimes (var &rest body) +(defmacro dotimes (var &key (label #f) &rest body) "Loop like for (int i = 0; i < end; i++) var is a list made up of a variable to bind the amount to (second item), and the remaining forms are evaluated after the loop is finished." - `(let (( ,(first var) 0)) - (while (< ,(first var) ,(second var)) - ,@body - (1+! ,(first var)) - ) - ,@(cddr var) - ) + (let ((continue-label (string->symbol (if label (string-append (symbol->string label) "-continue") "continue")))) + `(let (( ,(first var) 0)) + (while (< ,(first var) ,(second var)) :label ,label + (block ,continue-label + ,@(if (null? body) (list `(return-from ,continue-label #f)) body) + ) + (1+! ,(first var)) + ) + ,@(cddr var) + ) ) +) -(defmacro countdown (var &rest body) +(defmacro countdown (var &key (label #f) &rest body) "Loop like for (int i = end; i-- > 0)" `(let ((,(first var) ,(second var))) - (while (!= ,(first var) 0) + (while (!= ,(first var) 0) :label ,label (set! ,(first var) (- ,(first var) 1)) ,@body ) ) ) -(defmacro loop (&rest body) +(defmacro loop (&key (label #f) &rest body) "Loop this code forever." - `(while #t + `(while #t :label ,label ,@body) ) -(defmacro doarray (bindings &rest body) +(defmacro doarray (bindings &key (label #f) &rest body) "iterate over an array. usage: (doarray ( ) )" (with-gensyms (len i) (let ((val (first bindings)) - (arr (second bindings))) + (arr (second bindings)) + (continue-label (string->symbol (if label (string-append (symbol->string label) "-continue") "continue")))) `(let* ((,len (-> ,arr length)) (,i 0) (,val (-> ,arr ,i))) - (while (< ,i ,len) - ,@body + (while (< ,i ,len) :label ,label + (block ,continue-label + ,@(if (null? body) (list `(return-from ,continue-label #f)) body) + ) (1+! ,i) (set! ,val (-> ,arr ,i)) )