281 lines
10 KiB
Scheme
281 lines
10 KiB
Scheme
; Copyright (c) 1993-1999 by Richard Kelsey. See file COPYING.
|
|
|
|
|
|
; This is a backquote-like macro for building nodes.
|
|
;
|
|
; One goal is to produce code that is as efficient as possible.
|
|
;
|
|
; (LET-NODES (<spec1> ... <specN>) . <body>)
|
|
;
|
|
; <spec> ::= (<ident> <real-call>) | ; call node
|
|
; (<ident> (<var1> ... <varN>) <call>) | ; lambda node
|
|
; (<ident> (<var1> ... <varN> . <last-vars>) <call>) ; lambda node
|
|
;
|
|
; <var> ::= #f | Ignored variable position
|
|
; <ident> | Evaluate <ident> and copy it, rebinding <ident>
|
|
; '<ident> | Evaluate <ident> to get the variable
|
|
; (<ident> <rep>) (MAKE-VARIABLE <ident> <rep>)
|
|
;
|
|
; <last-vars> ::= <ident>
|
|
;
|
|
; <call> ::= <ident> | <real-call>
|
|
;
|
|
; <real-call> ::= (<primop-id> <exits> . <arg-list>)
|
|
;
|
|
; <arg-list> ::= (<arg1> ... <argN>) | (<arg1> ... <argN> . <last-args>)
|
|
;
|
|
; <last-args> ::= <ident>
|
|
;
|
|
; <arg> ::= 'foo literal node containing the value of foo, no rep
|
|
; '(foo rep) " " " " " " " , using rep
|
|
; (* foo) reference to foo (which evaluates to a variable)
|
|
; (! foo) foo evaluates to a node
|
|
; foo short for (! foo) when foo is an atom
|
|
; #f put nothing here
|
|
; (<primop-id> . <args>) a nested call
|
|
;--------------------------------------
|
|
;
|
|
; Example:
|
|
;
|
|
; (let-nodes ((c1 (l1 1 cont))
|
|
; (l1 ((j type/pointer)) (proc 2 l2 l3 . rest))
|
|
; (l2 () ((jump cont (* j)) '(true-value type/boolean)))
|
|
; (l3 () ((jump cont (* j)) '(false-value type/boolean))))
|
|
; (replace-body node c1))
|
|
;
|
|
; ==>
|
|
;
|
|
; (LET ((J (CREATE-VARIABLE 'J TYPE/POINTER)))
|
|
; (LET ((C1 (CREATE-CALL-NODE '2 1))
|
|
; (C.1225 (CREATE-CALL-NODE (+ '3 (LENGTH REST)) '2))
|
|
; (L1 (CREATE-LAMBDA-NODE 'C 'CONT (FLIST1 J '())))
|
|
; (C.1224 (CREATE-CALL-NODE '4 0))
|
|
; (L2 (CREATE-LAMBDA-NODE 'C 'CONT '()))
|
|
; (C.1223 (CREATE-CALL-NODE '4 0))
|
|
; (L3 (CREATE-LAMBDA-NODE 'C 'CONT '())))
|
|
; (ATTACH 0 C1 L1)
|
|
; (ATTACH 1 C1 CONT)
|
|
; (ATTACH 0 C.1225 PROC)
|
|
; (ATTACH-CALL-ARGS C.1225 (APPEND (LIST L2 L3) REST))
|
|
; (ATTACH-BODY L1 C.1225)
|
|
; (ATTACH 0 C.1224 (CREATE-PRIMOP-NODE PRIMOP/JUMP))
|
|
; (ATTACH-THREE-CALL-ARGS C.1224
|
|
; (CREATE-JUMP-MARKER CONT)
|
|
; (CREATE-REFERENCE-NODE J)
|
|
; (CREATE-LITERAL-NODE TRUE-VALUE TYPE/BOOLEAN))
|
|
; (ATTACH-BODY L2 C.1224)
|
|
; (ATTACH 0 C.1223 (CREATE-PRIMOP-NODE PRIMOP/JUMP))
|
|
; (ATTACH-THREE-CALL-ARGS C.1223
|
|
; (CREATE-JUMP-MARKER CONT)
|
|
; (CREATE-REFERENCE-NODE J)
|
|
; (CREATE-LITERAL-NODE FALSE-VALUE TYPE/BOOLEAN))
|
|
; (ATTACH-BODY L3 C.1223)
|
|
; (REPLACE-BODY NODE C1)))
|
|
;
|
|
|
|
(define (expand-let-nodes form rename compare)
|
|
(destructure (((#f specs . body) form))
|
|
(receive (vars nodes code)
|
|
(parse-node-specs specs rename compare)
|
|
`(,(rename 'let) ,vars
|
|
(,(rename 'let) ,nodes
|
|
,@code
|
|
,@body)))))
|
|
|
|
(define (test form)
|
|
(destructure (((#f specs . body) form))
|
|
(receive (vars nodes code)
|
|
(parse-node-specs specs identity eq?)
|
|
`(let ,vars
|
|
(let ,nodes
|
|
,@code
|
|
,@body)))))
|
|
|
|
; Parse the specs, returning a list of variable specs, a list of node specs,
|
|
; and a list of construction forms. An input spec is either a call or a
|
|
; lambda, each is parsed by an appropriate procedure.
|
|
|
|
(define (parse-node-specs specs r c)
|
|
(let loop ((specs (reverse specs)) (vars '()) (nodes '()) (codes '()))
|
|
(if (null? specs)
|
|
(values vars nodes codes)
|
|
(destructure ((((name . spec) . rest) specs))
|
|
(cond ((null? (cdr spec))
|
|
(receive (node code)
|
|
(construct-call name (car spec) r c)
|
|
(loop rest vars
|
|
`((,name ,node) . ,nodes) (append code codes))))
|
|
((= 2 (length spec))
|
|
(receive (vs node new-spec call)
|
|
(construct-lambda (car spec) (cadr spec) r c)
|
|
(loop (if new-spec (cons new-spec rest) rest)
|
|
(append vs vars)
|
|
`((,name ,node) . ,nodes)
|
|
(if call
|
|
`((attach-body ,name ,call) . ,codes)
|
|
codes))))
|
|
(else
|
|
(error "illegal spec in LET-NODES ~S" (cons name spec))))))))
|
|
|
|
; The names of the call-arg relation procedures, indexed by the number of
|
|
; arguments handled.
|
|
|
|
(define call-attach-names
|
|
'#(#f
|
|
#f
|
|
attach-two-call-args
|
|
attach-three-call-args
|
|
attach-four-call-args
|
|
attach-five-call-args))
|
|
|
|
; Return the node spec and construction forms for a call. This dispatches
|
|
; on whether the argument list is proper or not.
|
|
;
|
|
; <real-call> ::= (<arg0> <exits> <arg1> ... <argN>) |
|
|
; (<arg0> <exits> <arg1> ... <argN> . <last-args>))
|
|
; ((JUMP l-node value) <arg1> ... <argN>)
|
|
; ((JUMP l-node value) <arg1> ... <argN> . <last-args>)
|
|
|
|
(define (construct-call name specs r c)
|
|
(destructure (((proc . args) specs))
|
|
(really-construct-call name proc (car args) '() (cdr args) r c)))
|
|
|
|
(define (construct-nested-call specs r c)
|
|
(destructure (((primop-id . args) specs))
|
|
(let ((name (r 'call)))
|
|
(receive (node code)
|
|
(really-construct-call name primop-id 0 '() args r c)
|
|
`(,(r 'let) ((,name ,node)) ,@code ,name)))))
|
|
|
|
(define (really-construct-call name primop-id exits extra args r c)
|
|
(receive (arg-count arg-code)
|
|
(parse-call-args name extra args r c)
|
|
(let ((primop-code (get-primop-code primop-id r)))
|
|
(values `(,(r 'make-call-node) ,primop-code ,arg-count ,exits)
|
|
arg-code))))
|
|
|
|
(define (get-primop-code id r)
|
|
(cond ((name->enumerand id primop)
|
|
=> (lambda (n)
|
|
`(,(r 'get-primop) ,n)))
|
|
(else
|
|
`(,(r 'lookup-primop) ',id))))
|
|
|
|
; NAME = the call node which gets the arguments
|
|
; EXTRA = initial, already expanded arguments
|
|
; ARGS = unexpanded arguments
|
|
; LAST-ARG = an atom whose value is added to the end of the arguments
|
|
; Returns ARG-COUNT-CODE and ARG-CODE
|
|
|
|
(define (parse-call-args name extra args r c)
|
|
(receive (args last-arg)
|
|
(decouple-improper-list args)
|
|
(let* ((args (append extra (map (lambda (a) (construct-node a r c)) args)))
|
|
(count (length args)))
|
|
(if (not (null? last-arg))
|
|
(values `(,(r '+) ,count (,(r 'length) ,last-arg))
|
|
`((,(r 'attach-call-args)
|
|
,name
|
|
,(if (null? args)
|
|
last-arg
|
|
`(,(r 'append) (,(r 'list) . ,args) ,last-arg)))))
|
|
(values count
|
|
(cond ((= count 0)
|
|
'())
|
|
((and (= count 1) (car args))
|
|
`((,(r 'attach) ,name 0 ,(car args))))
|
|
((and (< count 6)
|
|
(every? identity args))
|
|
`((,(r (vector-ref call-attach-names count))
|
|
,name
|
|
,@args)))
|
|
(else
|
|
`((,(r 'attach-call-args) ,name (list . ,args))))))))))
|
|
|
|
; Return proper part of the list and its last-cdr separately.
|
|
|
|
(define (decouple-improper-list list)
|
|
(do ((list list (cdr list))
|
|
(res '() (cons (car list) res)))
|
|
((atom? list)
|
|
(values (reverse! res) list))))
|
|
|
|
; Dispatch on the type of the SPEC and return the appropriate code.
|
|
;
|
|
; <arg> ::= 'foo literal node containing the value of foo, no rep
|
|
; '(foo rep) literal node containing the value of foo
|
|
; (* foo) reference to foo (which evaluates to a variable)
|
|
; (! foo) foo evaluates to a node
|
|
; name short for (! name) when foo is an atom
|
|
|
|
(define (construct-node spec r c)
|
|
(cond ((atom? spec) spec)
|
|
(else
|
|
(destructure (((key data) spec))
|
|
(case key
|
|
((*) `(,(r 'make-reference-node) ,data))
|
|
((quote) (if (pair? data)
|
|
`(,(r 'make-literal-node) . ,data)
|
|
`(,(r 'make-literal-node) ,data type/unknown)))
|
|
((!) data)
|
|
(else
|
|
(construct-nested-call spec r c)))))))
|
|
|
|
; Parse a lambda spec. This returns a list of variable specs, code to
|
|
; construct the lambda node, a spec for the body if necessary, and
|
|
; the code needed to put it all together.
|
|
|
|
(define (construct-lambda vars call r c)
|
|
(receive (vars node)
|
|
(construct-vars vars r c)
|
|
(cond ((null? call)
|
|
(values vars node #f #f))
|
|
((atom? call)
|
|
(values vars node #f call))
|
|
(else
|
|
(let ((sym (r (generate-symbol 'c))))
|
|
(values vars node `(,sym ,call) sym))))))
|
|
|
|
; Returns the code needed to construct the variables and the code to make
|
|
; the lambda node that binds the variables.
|
|
;
|
|
; <var> ::= #f | Ignored variable position
|
|
; <ident> | Evaluate <ident> and copy it, rebinding <ident>
|
|
; '<ident> | Evaluate <ident> to get the variable
|
|
; (<ident> <rep>) (MAKE-VARIABLE <ident> <rep>)
|
|
|
|
(define (construct-vars vars r c)
|
|
(let loop ((vs vars) (vlist '()) (code '()))
|
|
(cond ((atom? vs)
|
|
(let ((vars (if (null? vs)
|
|
`(,(r 'list) . ,(reverse! vlist))
|
|
`(,(r 'append) (,(r 'list) . ,(reverse! vlist))
|
|
,vs))))
|
|
(values code `(,(r 'make-lambda-node) 'c 'cont ,vars))))
|
|
(else
|
|
(let ((spec (car vs))
|
|
(rest (cdr vs)))
|
|
(cond ((null? spec)
|
|
(loop rest (cons #f vlist) code))
|
|
((atom? spec)
|
|
(loop rest (cons spec vlist)
|
|
`((,spec (,(r 'copy-variable) ,spec)) . ,code)))
|
|
((c (car spec) 'quote)
|
|
(loop rest (cons (cadr spec) vlist) code))
|
|
(else
|
|
(loop rest (cons (car spec) vlist)
|
|
`((,(car spec)
|
|
(,(r 'make-variable) ',(car spec) ,(cadr spec)))
|
|
. ,code)))))))))
|
|
|
|
;------------------------------------------------------------------------------
|
|
; GENSYM utility
|
|
|
|
(define *generate-symbol-index* 0)
|
|
|
|
(define (generate-symbol sym)
|
|
(let ((i *generate-symbol-index*))
|
|
(set! *generate-symbol-index* (+ i 1))
|
|
(concatenate-symbol sym "." i)))
|
|
|