mirror of
https://git.savannah.gnu.org/git/guile.git
synced 2025-06-07 04:30:18 +02:00
163 lines
6.1 KiB
Scheme
163 lines
6.1 KiB
Scheme
(define-module (language cps compile-js)
|
|
#:use-module (language cps)
|
|
#:use-module ((language js-il)
|
|
#:renamer (lambda (x) (if (eqv? x 'make-prompt) 'make-prompt* x)))
|
|
#:use-module (ice-9 match)
|
|
#:export (compile-js))
|
|
|
|
(define optimize (@@ (language cps compile-bytecode) optimize))
|
|
(define convert-closures (@@ (language cps compile-bytecode) convert-closures))
|
|
(define reify-primitives (@@ (language cps compile-bytecode) reify-primitives))
|
|
(define renumber (@@ (language cps compile-bytecode) renumber))
|
|
|
|
(define (compile-js exp env opts)
|
|
;; See comment in `optimize' about the use of set!.
|
|
(set! exp (optimize exp opts))
|
|
(set! exp (convert-closures exp))
|
|
;; first-order optimization should go here
|
|
(set! exp (reify-primitives exp))
|
|
(set! exp (renumber exp))
|
|
(match exp
|
|
(($ $program funs)
|
|
;; TODO: I should special case the compilation for the initial fun,
|
|
;; as this is the entry point for the program, and shouldn't get a
|
|
;; "self" argument, for now, I add "undefined" as the first
|
|
;; argument in the call to it.
|
|
;; see compile-exp in (language js-il compile-javascript)
|
|
(values (make-program (compile-fun (car funs))
|
|
(map compile-fun (cdr funs)))
|
|
env
|
|
env))))
|
|
|
|
(define (compile-fun fun)
|
|
(match fun
|
|
(($ $cont k ($ $kfun _ _ self ($ $cont tail ($ $ktail)) clause))
|
|
(call-with-values
|
|
(lambda ()
|
|
(extract-clauses self clause))
|
|
(lambda (jump-table clauses)
|
|
(make-var
|
|
(make-kid k)
|
|
(make-function
|
|
(make-id self) (make-kid tail)
|
|
(make-local (map (lambda (clause)
|
|
(compile-clause clause self tail))
|
|
clauses)
|
|
(make-jump-table jump-table)))))))))
|
|
|
|
(define (extract-clauses self clause)
|
|
(define (make-params* self req opts rest kw allow-other-keys?)
|
|
(make-params (make-id self)
|
|
(map make-id req)
|
|
(map make-id opts)
|
|
(and rest (make-id rest))
|
|
(map make-id kw)
|
|
allow-other-keys?))
|
|
(let loop ((clause clause) (specs '()) (clauses '()))
|
|
(match clause
|
|
(($ $cont k ($ $kclause ($ $arity req opts rest kw allow-other-keys?) _ #f))
|
|
(values (reverse (acons (make-params* self req opts rest kw allow-other-keys?)
|
|
(make-kid k)
|
|
specs))
|
|
(reverse (cons clause clauses))))
|
|
(($ $cont k ($ $kclause ($ $arity req opts rest kw allow-other-keys?) _ alternate))
|
|
(loop alternate
|
|
(acons (make-params* self req opts rest kw allow-other-keys?)
|
|
(make-kid k)
|
|
specs)
|
|
(cons clause clauses))))))
|
|
|
|
(define (compile-clause clause self tail)
|
|
(match clause
|
|
(($ $cont k ($ $kclause ($ $arity req opt rest ((_ _ kw-syms) ...) _) body _))
|
|
(make-var
|
|
(make-kid k)
|
|
(make-continuation
|
|
(append (list (make-id self))
|
|
(map make-id req)
|
|
(map make-id opt)
|
|
(map make-id kw-syms)
|
|
(if rest (list (make-id rest)) '()))
|
|
(match body
|
|
(($ $cont k ($ $kargs () () exp))
|
|
(compile-term exp))
|
|
(($ $cont k _)
|
|
(make-local (list (compile-cont body))
|
|
(make-continue
|
|
(make-kid k)
|
|
(map make-id (append req opt kw-syms (if rest (list rest) '()))))))))))))
|
|
|
|
(define (not-supported msg clause)
|
|
(error 'not-supported msg clause))
|
|
|
|
(define (compile-term term)
|
|
(match term
|
|
(($ $letk conts body)
|
|
(make-local (map compile-cont conts) (compile-term body)))
|
|
(($ $continue k src exp)
|
|
(compile-exp exp k))))
|
|
|
|
(define (compile-cont cont)
|
|
(match cont
|
|
(($ $cont k ($ $kargs names syms body))
|
|
;; use the name part?
|
|
(make-var (make-kid k)
|
|
(make-continuation (map make-id syms)
|
|
(compile-term body))))
|
|
(($ $cont k ($ $kreceive ($ $arity req _ (? symbol? rest) _ _) k2))
|
|
(make-var
|
|
(make-kid k)
|
|
(make-continuation (append (map make-id req) (list (make-id rest)))
|
|
(make-continue (make-kid k2)
|
|
(append (map make-id req)
|
|
(list (make-id rest)))))))
|
|
(($ $cont k ($ $kreceive ($ $arity req _ #f _ _) k2))
|
|
(make-var (make-kid k)
|
|
(make-continuation (map make-id req)
|
|
(make-continue (make-kid k2)
|
|
(map make-id req)))))))
|
|
|
|
(define (compile-exp exp k)
|
|
(match exp
|
|
(($ $branch kt exp)
|
|
(compile-test exp (make-kid kt) (make-kid k)))
|
|
(($ $primcall 'return (arg))
|
|
(make-continue (make-kid k) (list (make-id arg))))
|
|
(($ $call name args)
|
|
(make-call (make-id name) (make-kid k) (map make-id args)))
|
|
(($ $callk label proc args)
|
|
(make-continue (make-kid label)
|
|
(cons* (make-id proc)
|
|
(make-kid k)
|
|
(map make-id args))))
|
|
(($ $values values)
|
|
(make-continue (make-kid k) (map make-id values)))
|
|
(($ $prompt escape? tag handler)
|
|
(make-seq
|
|
(list
|
|
(make-prompt* escape? (make-id tag) (make-kid handler))
|
|
(make-continue (make-kid k) '()))))
|
|
(_
|
|
(make-continue (make-kid k) (list (compile-exp* exp))))))
|
|
|
|
(define (compile-exp* exp)
|
|
(match exp
|
|
(($ $const val)
|
|
(make-const val))
|
|
(($ $primcall name args)
|
|
(make-primcall name (map make-id args)))
|
|
(($ $closure label nfree)
|
|
(make-closure (make-kid label) nfree))
|
|
(($ $values (val))
|
|
;; FIXME:
|
|
;; may happen if a test branch of a conditional compiles to values
|
|
;; placeholder till I learn if multiple values could be returned.
|
|
(make-id val))))
|
|
|
|
(define (compile-test exp kt kf)
|
|
;; TODO: find out if the expression is always simple enough that I
|
|
;; don't need to create a new continuation (which will require extra
|
|
;; arguments being passed through)
|
|
(make-branch (compile-exp* exp)
|
|
(make-continue kt '())
|
|
(make-continue kf '())))
|