Commit 83f4ea34 authored by Vladimir Sedach's avatar Vladimir Sedach
Browse files

Made returns from CL loops (implicit nil blocks) work.

parent 08bdb039
......@@ -83,6 +83,10 @@ block.")
"Used for seeing when we're in loops, so that we can introduce
proper scoping for lambdas closing over loop-bound
variables (otherwise they all share the same binding).")
(defvar *loop-return-var* nil
"Variable which is used to return values from inside loop bodies.")
(defvar loop-returns? nil
"Set to T by RETURN-FROM when it returns a value from inside a loop.")
(defvar *loop-scope-lexicals*)
(setf (documentation '*loop-scope-lexicals* 'variable)
......
......@@ -136,7 +136,7 @@ Syntax of key spec:
(lambda (var)
`(var ,var))
(remove-duplicates *vars-needing-to-be-declared*))))))
(when in-loop-scope? ;; this is probably broken when it comes to let-renaming
(when in-loop-scope? ;; this might be broken when it comes to let-renaming
(setf *loop-scope-lexicals-captured*
(append (intersection (flatten body) *loop-scope-lexicals*)
*loop-scope-lexicals-captured*)))
......
......@@ -113,21 +113,39 @@
;;; iteration
(defvar loop-wrapped? nil
"Bind to T when wrapping to avoid infinite recursion.")
(defmacro wrap-loop-for-return (name loop)
(let ((loop-body (gensym)))
`(let* ((*loop-return-var* nil)
(loop-returns? nil)
(,loop-body ,loop))
(if (and loop-returns? (not loop-wrapped?))
(let ((loop-wrapped? t))
(compile-statement `(let (,*loop-return-var*)
(,',name ,@whole)
,*loop-return-var*)))
,loop-body))))
(define-statement-operator for (init-forms cond-forms step-forms &body body)
(let ((init-forms (make-for-vars/inits init-forms)))
`(ps-js:for ,init-forms
,(mapcar #'compile-expression cond-forms)
,(mapcar #'compile-expression step-forms)
,(compile-loop-body (mapcar #'car init-forms) body))))
(wrap-loop-for-return for
`(ps-js:for ,init-forms
,(mapcar #'compile-expression cond-forms)
,(mapcar #'compile-expression step-forms)
,(compile-loop-body (mapcar #'car init-forms) body)))))
(define-statement-operator for-in ((var object) &rest body)
`(ps-js:for-in ,(compile-expression var)
,(compile-expression object)
,(compile-loop-body (list var) body)))
(wrap-loop-for-return for-in
`(ps-js:for-in ,(compile-expression var)
,(compile-expression object)
,(compile-loop-body (list var) body))))
(define-statement-operator while (test &rest body)
`(ps-js:while ,(compile-expression test)
,(compile-loop-body () body)))
(wrap-loop-for-return while
`(ps-js:while ,(compile-expression test)
,(compile-loop-body () body))))
(defmacro while (test &body body)
`(loop while ,test do (progn ,@body)))
......
......@@ -137,6 +137,7 @@
(define-statement-operator block (name &rest body)
(if in-function-scope?
(let* ((name (or name 'nilBlock))
(in-loop-scope? (if name in-loop-scope? nil))
(*dynamic-return-tags* (cons (cons name nil) *dynamic-return-tags*))
(*current-block-tag* name)
(compiled-body (compile-statement `(progn ,@body))))
......@@ -248,11 +249,11 @@ Parenscript now implements implicit return, update your code! Things like (lambd
(expressionize-result tag form)
(return-exp tag form))))
(in-loop-scope?
(when result
(warn "Trying to (RETURN ~A) from inside a loop with an implicit nil block (DO, DOLIST, DOTIMES, etc.)
Parenscript doesn't support returning values this way from inside a loop yet!"
result))
'(ps-js:break))
(setf loop-returns? t
*loop-return-var* (or *loop-return-var*
(ps-gensym "loop-result-var")))
(compile-statement `(progn (setf ,*loop-return-var* ,result)
(break))))
(t (ps-compile `(return-from nilBlock ,result)))))
(define-statement-operator throw (&rest args)
......
......@@ -266,13 +266,13 @@
(block nil (return 1) 2)
1)
;;; broken
(test-js-eval dolist-return
(dolist (x '(5 2 3))
(return (1+ x)))
6)
;;; broken
(test-js-eval equality-nary1
(let ((x 10) (y 10) (z 10))
(= x y z))
......
......@@ -2922,13 +2922,18 @@ var x = bar();")
(when (= x 1)
(return))
(chain console (log x)))
"for (var x = null, _js_arrvar2 = [2, 1, 3], _js_idx1 = 0; _js_idx1 < _js_arrvar2.length; _js_idx1 += 1) {
"(function () {
var loopResultVar3 = null;
for (var x = null, _js_arrvar2 = [2, 1, 3], _js_idx1 = 0; _js_idx1 < _js_arrvar2.length; _js_idx1 += 1) {
x = _js_arrvar2[_js_idx1];
if (x === 1) {
loopResultVar3 = null;
break;
};
console.log(x);
};")
};
return loopResultVar3;
})();")
(test-ps-js explicit-nil-block
(defun bar ()
......@@ -3068,9 +3073,31 @@ var x = bar();")
if (x) {
return 1;
} else {
var loopResultVar1 = null;
for (var i = 0; i < 4; i += 1) {
loopResultVar1 = 1;
break;
};
loopResultVar1;
};
return 2;
});")
(test-ps-js lambda-nil-return-implicit-nested2
(lambda (x)
(block foo
(if x
(return-from foo 1)
(dotimes (i 4)
(return-from foo i)))
2))
"(function (x) {
if (x) {
return 1;
} else {
for (var i = 0; i < 4; i += 1) {
return i;
};
};
return 2;
});")
......@@ -3428,3 +3455,16 @@ return function (x) {
return x;
return 2;
})();")
(test-ps-js dolist-return1
(dolist (x '(5 2 3))
(return (1+ x)))
"(function () {
var loopResultVar3 = null;
for (var x = null, _js_arrvar2 = [5, 2, 3], _js_idx1 = 0; _js_idx1 < _js_arrvar2.length; _js_idx1 += 1) {
x = _js_arrvar2[_js_idx1];
loopResultVar3 = x + 1;
break;
};
return loopResultVar3;
})();")
\ No newline at end of file
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment