shithub: sl

ref: 3efd625f94ba38fb5c37499441fdad603db25cd3
dir: /test/ast/asttools.sl/

View raw version
; utilities for AST processing

(def (list-adjoin item lst)
  (if (member item lst)
      lst
    (cons item lst)))

(def (index-of item lst start)
  (cond ((not lst) NIL)
    ((eq? item (car lst)) start)
    (else      (index-of item (cdr lst) (+ start 1)))))

(def (each f l)
  (if (not l) l
    (begin (f (car l))
           (each f (cdr l)))))

(def (maptree-pre f tr)
  (let ((new-t (f tr)))
    (if (cons? new-t)
        (map (lambda (e) (maptree-pre f e)) new-t)
      new-t)))

(def (maptree-post f tr)
  (if (not (cons? tr))
      (f tr)
    (let ((new-t (map (lambda (e) (maptree-post f e)) tr)))
      (f new-t))))

(def (foldtree-pre f tr zero)
  (if (not (cons? tr))
      (f tr zero)
      (foldl tr (lambda (e state) (foldtree-pre f e state)) (f tr zero))))

(def (foldtree-post f tr zero)
  (if (not (cons? tr))
      (f tr zero)
      (f tr (foldl tr (lambda (e state) (foldtree-post f e state)) zero))))

; general tree transformer
; folds in preorder (foldtree-pre), maps in postorder (maptree-post)
; therefore state changes occur immediately, just by looking at the current node,
; while transformation follows evaluation order. this seems to be the most natural
; approach.
; (mapper tree state) - should return transformed tree given current state
; (folder tree state) - should return new state
(def (map&fold tr zero mapper folder)
  (let ((head (and (cons? tr) (car tr))))
    (cond
      ((eq? head 'quote) tr)
      ((or (eq? head 'the) (eq? head 'meta))
       (list head
         (cadr tr)
         (map&fold (caddr tr) zero mapper folder)))
      (else
       (let ((new-s (folder tr zero)))
         (mapper
          (if (cons? tr)
          ; head symbol is a tag; never transform it
          (cons (car tr)
            (map (lambda (e) (map&fold e new-s mapper folder))
                 (cdr tr)))
          tr)
          new-s))))))

; convert to proper list, i.e. remove "dots", and append
(def (append.2 l tail)
  (cond ((not l)    tail)
        ((atom? l)  (cons l tail))
        (else       (cons (car l) (append.2 (cdr l) tail)))))

; transform code by calling (f expr env) on each subexpr, where
; env is a list of lexical variables in effect at that point.
(def (lexical-walk f tr)
  (map&fold tr () f
        (lambda (tree state)
          (if (and (eq? (car tr) 'lambda)
               (cons? (cdr tr)))
          (append.2 (cadr tr) state)
          state))))

; collapse forms like (&& (&& (&& (&& a b) c) d) e) to (&& a b c d e)
(def (flatten-left-op op e)
  (maptree-post (lambda (node)
                  (if (and (cons? node)
                           (eq? (car node) op)
                           (cons? (cdr node))
                           (cons? (cadr node))
                           (eq? (caadr node) op))
                      (cons op
                            (append (cdadr node) (cddr node)))
                    node))
                e))

; convert all local variable references to (lexref rib slot name)
; where rib is the nesting level and slot is the stack slot#
; name is just there for reference
; this assumes lambda is the only remaining naming form
(def (lookup-var v env lev)
  (if (not env) v
    (let ((i (index-of v (car env) 0)))
      (if i (list 'lexref lev i v)
        (lookup-var v (cdr env) (+ lev 1))))))
(def (lvc- e env)
  (cond ((sym? e) (lookup-var e env 0))
        ((cons? e)
         (if (eq? (car e) 'quote)
             e
         (let* ((newvs (and (eq? (car e) 'lambda) (cadr e)))
            (newenv (if newvs (cons newvs env) env)))
           (if newvs
           (cons 'lambda
             (cons (cadr e)
                   (map (lambda (se) (lvc- se newenv))
                    (cddr e))))
           (map (lambda (se) (lvc- se env)) e)))))
        (else e)))
(def (lexical-var-conversion e)
  (lvc- e ()))

; convert let to lambda
(def (let-expand e)
  (maptree-post (lambda (n)
          (if (and (cons? n) (eq? (car n) 'let))
              `((lambda ,(map car (cadr n)) ,@(cddr n))
            ,@(map cadr (cadr n)))
                    n))
        e))

; alpha renaming
; transl is an assoc list ((old-sym-name . new-sym-name) ...)
(def (alpha-rename e transl)
  (map&fold e
        ()
        ; mapper: replace symbol if unbound
        (lambda (te env)
          (if (sym? te)
          (let ((found (assq te transl)))
            (if (and found
                 (not (memq te env)))
            (cdr found)
            te))
          te))
        ; folder: add locals to environment if entering a new scope
        (lambda (te env)
          (if (and (cons? te) (or (eq? (car te) 'let)
                                  (eq? (car te) 'lambda)))
          (append (cadr te) env)
          env))))

; flatten op with any associativity
(defmacro (flatten-all-op op e)
  `(pattern-expand
    (pattern-lambda (,op (-- l ...) (-- inner (,op ...)) (-- r ...))
                    (cons ',op (append l (cdr inner) r)))
    ,e))

(defmacro (pattern-lambda pat body)
  (let* ((args (patargs pat))
         (expander `(lambda ,args ,body)))
    `(lambda (expr)
       (let ((m (match ',pat expr)))
         (if m
             ; matches; perform expansion
             (apply ,expander (map (lambda (var) (cdr (or (assq var m) '(0 . NIL))))
                                   ',args))
           NIL)))))