shithub: sl

ref: 209b77a534b953d8197b6d0dc2ddb4db16f8fb80
dir: /femtolisp/ast/asttools.lsp/

View raw version
; utilities for AST processing

(define (symconcat s1 s2)
  (intern (string s1 s2)))

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

(define (index-of item lst start)
  (cond ((null lst) nil)
	((eq item (car lst)) start)
	(T (index-of item (cdr lst) (+ start 1)))))

(define (each f l)
  (if (null l) l
    (progn (f (car l))
           (each f (cdr l)))))

(define (maptree-pre f tr)
  (let ((new-t (f tr)))
    (if (consp new-t)
        (map (lambda (e) (maptree-pre f e)) new-t)
      new-t)))

(define (maptree-post f tr)
  (if (not (consp tr))
      (f tr)
    (let ((new-t (map (lambda (e) (maptree-post f e)) tr)))
      (f new-t))))

(define (foldtree-pre f t zero)
  (if (not (pair? t))
      (f t zero)
      (foldl t (lambda (e state) (foldtree-pre f e state)) (f t zero))))

(define (foldtree-post f t zero)
  (if (not (pair? t))
      (f t zero)
      (f t (foldl t (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
(define (map&fold t zero mapper folder)
  (let ((head (and (pair? t) (car t))))
    (cond ((eq? head 'quote)
	   t)
	  ((or (eq? head 'the) (eq? head 'meta))
	   (list head
		 (cadr t)
		 (map&fold (caddr t) zero mapper folder)))
	  (else
	   (let ((new-s (folder t zero)))
	     (mapper
	      (if (pair? t)
		  ; head symbol is a tag; never transform it
		  (cons (car t)
			(map (lambda (e) (map&fold e new-s mapper folder))
			     (cdr t)))
		  t)
	      new-s))))))

; collapse forms like (&& (&& (&& (&& a b) c) d) e) to (&& a b c d e)
(define (flatten-left-op op e)
  (maptree-post (lambda (node)
                  (if (and (consp node)
                           (eq (car node) op)
                           (consp (cdr node))
                           (consp (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
(define (lookup-var v env lev)
  (if (null env) v
    (let ((i (index-of v (car env) 0)))
      (if i (list 'lexref lev i v)
        (lookup-var v (cdr env) (+ lev 1))))))
(define (lvc- e env)
  (cond ((symbolp e) (lookup-var e env 0))
        ((consp 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)))))
        (T e)))
(define (lexical-var-conversion e)
  (lvc- e ()))

; convert let to lambda
(define (let-expand e)
  (maptree-post (lambda (n)
		  (if (and (consp 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) ...)
(define (alpha-rename e transl)
  (map&fold e
	    ()
	    ; mapper: replace symbol if unbound
	    (lambda (t env)
	      (if (symbol? t)
		  (let ((found (assq t transl)))
		    (if (and found
			     (not (memq t env)))
			(cdr found)
			t))
		  t))
	    ; folder: add locals to environment if entering a new scope
	    (lambda (t env)
	      (if (and (pair? t) (or (eq? (car t) 'let)
				     (eq? (car t) 'lambda)))
		  (append (cadr t) 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 (assoc var m) '(0 . nil))))
                                   ',args))
           nil)))))