Permalink
Browse files

properly support all the intricacies of for/in

you can now do for (var n = 1 in obj) and for (some[assignableExpr] in obj)
  • Loading branch information...
1 parent 182fe50 commit 3011e580f9c2d0d692caffe22ebe1efbfad496be @marijnh committed Jan 17, 2011
Showing with 46 additions and 33 deletions.
  1. +1 −1 as.txt
  2. +45 −32 src/parse.lisp
View
2 as.txt
@@ -37,5 +37,5 @@
(:while cond body)
(:do cond body)
(:for init cond step body)
-(:for-in var name obj body)
+(:for-in init lhs obj body)
(:switch val (case . body)*)
View
@@ -160,21 +160,33 @@
:collect (statement)))
(next)))
+ (def for-in (label init lhs)
+ (let ((obj (progn (next) (expression))))
+ (expect #\))
+ (as :for-in init lhs obj (with-label-scope :loop label (statement)))))
+
+ (def regular-for (label init)
+ (expect #\;)
+ (let ((test (prog1 (unless (semicolonp) (expression)) (expect #\;)))
+ (step (if (tokenp token :punc #\)) nil (expression))))
+ (expect #\))
+ (as :for init test step (with-label-scope :loop label (statement)))))
+
(def for* (label)
(expect #\()
- (let ((var (tokenp token :keyword :var)))
- (when var (next))
- (if (and (token-type-p token :name) (tokenp (peek) :operator :in))
- (let ((name (token-value token)))
- (skip 2)
- (let ((obj (expression)))
- (expect #\))
- (as :for-in var name obj (with-label-scope :loop label (statement)))))
- (let ((init (prog1 (cond ((semicolonp) nil) (var (var*)) (t (expression))) (expect #\;)))
- (test (prog1 (unless (semicolonp) (expression)) (expect #\;)))
- (step (if (tokenp token :punc #\)) nil (expression))))
- (expect #\))
- (as :for init test step (with-label-scope :loop label (statement)))))))
+ (cond ((semicolonp) (regular-for label nil))
+ ((tokenp token :keyword :var)
+ (let* ((var (progn (next) (var* t)))
+ (defs (second var)))
+ (if (and (not (cdr defs)) (tokenp token :operator :in))
+ (for-in label var (as :name (caar defs)))
+ (regular-for label var))))
+ (t (let ((init (expression t t)))
+ (if (tokenp token :operator :in)
+ (if (is-assignable init)
+ (for-in label nil init)
+ (error* "Bad for/in syntax."))
+ (regular-for label init))))))
(def function* (statement)
(with-defs
@@ -220,18 +232,18 @@
(setf finally (ensure-block)))
(as :try body catch finally)))
- (def vardefs ()
+ (def vardefs (no-in)
(unless (token-type-p token :name) (unexpected token))
(let ((name (token-value token)) val)
(next)
(when (tokenp token :operator :=)
- (next) (setf val (expression nil)))
+ (next) (setf val (expression nil no-in)))
(if (tokenp token :punc #\,)
- (progn (next) (cons (cons name val) (vardefs)))
+ (progn (next) (cons (cons name val) (vardefs no-in)))
(list (cons name val)))))
- (def var* ()
- (as :var (vardefs)))
+ (def var* (&optional no-in)
+ (as :var (vardefs no-in)))
(def new* ()
(let ((newexp (expr-atom nil)))
@@ -321,39 +333,40 @@
(error* "Invalid use of '~a' operator." op))
(as tag op expr))
- (def expr-op (left min-prec)
- (let* ((op (and (token-type-p token :operator) (token-value token)))
+ (def expr-op (left min-prec no-in)
+ (let* ((op (and (token-type-p token :operator) (or (not no-in) (not (eq (token-value token) :in)))
+ (token-value token)))
(prec (and op (gethash op *precedence*))))
(if (and prec (> prec min-prec))
- (let ((right (progn (next) (expr-op (expr-atom t) prec))))
- (expr-op (as :binary op left right) min-prec))
+ (let ((right (progn (next) (expr-op (expr-atom t) prec no-in))))
+ (expr-op (as :binary op left right) min-prec no-in))
left)))
- (def expr-ops ()
- (expr-op (expr-atom t) 0))
+ (def expr-ops (no-in)
+ (expr-op (expr-atom t) 0 no-in))
- (def maybe-conditional ()
- (let ((expr (expr-ops)))
+ (def maybe-conditional (no-in)
+ (let ((expr (expr-ops no-in)))
(if (tokenp token :operator :?)
(let ((yes (progn (next) (expression nil))))
(expect #\:)
- (as :conditional expr yes (expression nil)))
+ (as :conditional expr yes (expression nil no-in)))
expr)))
(def is-assignable (expr)
(case (car expr)
((:dot :sub) t) (:name (not (equal (second expr) "this")))))
- (def maybe-assign ()
- (let ((left (maybe-conditional)))
+ (def maybe-assign (no-in)
+ (let ((left (maybe-conditional no-in)))
(if (and (token-type-p token :operator) (gethash (token-value token) *assignment*))
(if (is-assignable left)
- (as :assign (gethash (token-value token) *assignment*) left (progn (next) (maybe-assign)))
+ (as :assign (gethash (token-value token) *assignment*) left (progn (next) (maybe-assign no-in)))
(error* "Invalid assignment."))
left)))
- (def expression (&optional (commas t))
- (let ((expr (maybe-assign)))
+ (def expression (&optional (commas t) (no-in nil))
+ (let ((expr (maybe-assign no-in)))
(if (and commas (tokenp token :punc #\,))
(as :seq expr (progn (next) (expression)))
@mishoo

mishoo Feb 20, 2011

Contributor

I'm wondering if we shouldn't pass along no-in when recursing?

@marijnh

marijnh Feb 20, 2011

Owner

Depends on which recurse. For something inside parentheses, no. For others, yes. If you see something that looks wrong, point it out.

expr)))

0 comments on commit 3011e58

Please sign in to comment.