Skip to content

Commit

Permalink
Start working on array destructuring.
Browse files Browse the repository at this point in the history
  • Loading branch information
okamsn committed Nov 28, 2023
1 parent d923a01 commit 6560187
Show file tree
Hide file tree
Showing 2 changed files with 358 additions and 137 deletions.
135 changes: 135 additions & 0 deletions loopy-commands.el
Original file line number Diff line number Diff line change
Expand Up @@ -3145,6 +3145,141 @@ A wrapper around `loopy--destructure-for-iteration-command'."
Unlike `loopy--basic-builtin-destructuring', this function
does destructuring and returns instructions.
NAME is the name of the command. VAR is a variable name. VAL is a value."
(let* ((remaining-var var)
(value-holder (gensym (format "%s-destructured-seq-" name)))
(instructions `((loopy--iteration-vars (,value-holder nil))
(loopy--main-body (setq ,value-holder ,val)))))

(map-let (('whole whole-var)
('pos pos-vars)
('opt opt-vars)
('rest rest-var)
('key key-vars)
('map map-vars)
('allow-other-keys allow-other-keys))

(loopy--get-var-groups var)

;; Handle the whole var.
(when (eq (seq-first var) '&whole)
(dolist (instr (loopy--parse-loop-command
`(,name ,(seq-elt var 1) ,value-holder ,@args)))
(push instr instructions))
(setq remaining-var (seq-drop remaining-var 2)))

;; How variables are set depends on type. For lists, we wish to use `pop'
;; to avoid traversing the list more than once. For arrays, we must use
;; `aref'.
(cl-etypecase remaining-var
(symbol
(push `(loopy--accumulation-vars (,remaining-var ,value-holder))
instructions))
(list
(let ((key-vars)
(this-var)
(looking-at-key-vars)
(var-is-dotted (not (proper-list-p remaining-var))))

(while (car-safe remaining-var)

(setq this-var (car remaining-var))
(cond
((eq this-var '_) ; Do nothing in this case.
(push `(loopy--main-body (setq ,value-holder (cdr ,value-holder)))
instructions)
(setq remaining-var (cdr remaining-var)))

((eq this-var '&rest)
(setq looking-at-key-vars nil)
(when var-is-dotted
(signal 'loopy-&rest-dotted (list var)))
(dolist (instr (loopy--parse-loop-command
`( ,name ,(cl-second remaining-var)
,value-holder ,@args)))
(push instr instructions))
(setq remaining-var (cddr remaining-var)))

((memq this-var '(&key &keys))
(setq looking-at-key-vars t
remaining-var (cdr remaining-var)))

(looking-at-key-vars
(push this-var key-vars)
(setq remaining-var (cdr remaining-var)))

(t
(dolist (instr (loopy--parse-loop-command
`(,name ,this-var (pop ,value-holder) ,@args)))
(push instr instructions))
(setq remaining-var (cdr remaining-var)))))

;; If `remaining-var' is not nil, then it is now the final atom of an
;; improper list.
(when remaining-var
(dolist (instr (loopy--parse-loop-command
`(,name ,remaining-var ,value-holder ,@args)))
(push instr instructions)))

;; TODO: In Emacs 28, `pcase' was changed so that all named variables
;; are at least bound to nil. Before that version, we should make sure
;; that `default' is bound.
(let ((default nil))
(ignore default)
(pcase-dolist ((or `(,kvar ,default)
kvar)
key-vars)
(dolist (instr
(loopy--parse-loop-command
`( ,name ,kvar
,(let ((key (intern (format ":%s" kvar))))
(if default
`(if-let ((key-found (plist-member ,value-holder
,key)))
(cl-second key-found)
,default)
`(plist-get ,value-holder ,key)))
,@args)))
(push instr instructions))))))

(array
(cl-loop named loop
with array-length = (length remaining-var)
for symbol-or-seq across remaining-var
for index from 0
do (cond
((eq symbol-or-seq '_))
((eq symbol-or-seq '&rest)
(let* ((next-idx (1+ index))
(next-var (aref remaining-var next-idx)))
;; Check that the var after `&rest' is the last:
(when (> (1- array-length) next-idx)
(signal 'loopy-&rest-multiple (list var)))

(dolist (instr
(loopy--parse-loop-command
`( ,name ,next-var
(cl-subseq ,value-holder ,index) ,@args)))
(push instr instructions)))
;; Exit the loop.
(cl-return-from loop))
(t
(dolist (instr
(loopy--parse-loop-command
`( ,name ,symbol-or-seq
(aref ,value-holder ,index) ,@args)))
(push instr instructions))))))))

;; Return the instructions in the correct order.
(nreverse instructions)))

(cl-defun loopy--parse-destructuring-accumulation-command2
((name var val &rest args))
"Return instructions for destructuring accumulation commands.
Unlike `loopy--basic-builtin-destructuring', this function
does destructuring and returns instructions.
NAME is the name of the command. VAR is a variable name. VAL is a value."
(let* ((remaining-var var)
(value-holder (gensym (format "%s-destructured-seq-" name)))
Expand Down
Loading

0 comments on commit 6560187

Please sign in to comment.