mirror of
https://git.savannah.gnu.org/git/guile.git
synced 2025-04-30 11:50:28 +02:00
242 lines
6.3 KiB
Scheme
242 lines
6.3 KiB
Scheme
;; Only has enough of the ecmascript language for compilation from cps
|
|
(define-module (language javascript)
|
|
#:use-module (ice-9 match)
|
|
#:use-module (srfi srfi-9)
|
|
#:use-module (srfi srfi-9 gnu)
|
|
#:export (
|
|
make-const const
|
|
make-function function
|
|
make-return return
|
|
make-call call
|
|
make-block block
|
|
make-new new
|
|
make-id id
|
|
make-refine refine
|
|
make-branch branch
|
|
make-var var
|
|
make-binop binop
|
|
make-ternary ternary
|
|
make-prefix prefix
|
|
|
|
print-statement))
|
|
|
|
;; Copied from (language cps)
|
|
;; Should put in a srfi 99 module
|
|
(define-syntax define-record-type*
|
|
(lambda (x)
|
|
(define (id-append ctx . syms)
|
|
(datum->syntax ctx (apply symbol-append (map syntax->datum syms))))
|
|
(syntax-case x ()
|
|
((_ name field ...)
|
|
(and (identifier? #'name) (and-map identifier? #'(field ...)))
|
|
(with-syntax ((cons (id-append #'name #'make- #'name))
|
|
(pred (id-append #'name #'name #'?))
|
|
((getter ...) (map (lambda (f)
|
|
(id-append f #'name #'- f))
|
|
#'(field ...))))
|
|
#'(define-record-type name
|
|
(cons field ...)
|
|
pred
|
|
(field getter)
|
|
...))))))
|
|
|
|
;; TODO: add type predicates to fields so I can only construct valid
|
|
;; objects
|
|
(define-syntax-rule (define-js-type name field ...)
|
|
(begin
|
|
(define-record-type* name field ...)
|
|
(set-record-type-printer! name print-js)))
|
|
|
|
(define (print-js exp port)
|
|
(format port "#<js ~S>" (unparse-js exp)))
|
|
|
|
(define-js-type const c)
|
|
(define-js-type function args body)
|
|
(define-js-type return exp)
|
|
(define-js-type call function args)
|
|
(define-js-type block statements)
|
|
(define-js-type new expr)
|
|
(define-js-type id name)
|
|
(define-js-type refine id field)
|
|
(define-js-type branch test then else)
|
|
(define-js-type var id exp)
|
|
(define-js-type binop op arg1 arg2)
|
|
(define-js-type ternary test then else)
|
|
(define-js-type prefix op expr)
|
|
|
|
(define (unparse-js exp)
|
|
(match exp
|
|
(($ const c)
|
|
`(const ,c))
|
|
(($ function args body)
|
|
`(function ,args ,@(map unparse-js body)))
|
|
(($ return exp)
|
|
`(return ,(unparse-js exp)))
|
|
(($ call function args)
|
|
`(call ,(unparse-js function) ,@(map unparse-js args)))
|
|
(($ block statements)
|
|
`(block ,@(map unparse-js statements)))
|
|
(($ new expr)
|
|
`(new ,(unparse-js expr)))
|
|
(($ id name)
|
|
`(id ,name))
|
|
(($ refine id field)
|
|
`(refine ,(unparse-js id) ,(unparse-js field)))
|
|
(($ branch test then else)
|
|
`(if ,(unparse-js test)
|
|
(block ,@(map unparse-js then))
|
|
(block ,@(map unparse-js else))))
|
|
(($ var id exp)
|
|
`(var ,id ,(unparse-js exp)))
|
|
(($ binop op arg1 arg2)
|
|
`(binop ,op ,(unparse-js arg1) ,(unparse-js arg2)))
|
|
(($ ternary test then else)
|
|
`(ternary ,(unparse-js test) ,(unparse-js then) ,(unparse-js else)))
|
|
(($ prefix op expr)
|
|
`(prefix ,op ,(unparse-js expr)))
|
|
))
|
|
|
|
(define (print-exp exp port)
|
|
(match exp
|
|
|
|
(($ const c)
|
|
(print-const c port))
|
|
|
|
(($ id name)
|
|
(print-id name port))
|
|
|
|
(($ call (and ($ function _ _) fun) args)
|
|
(format port "(")
|
|
(print-exp fun port)
|
|
(format port ")(")
|
|
(print-separated args print-exp "," port)
|
|
(format port ")"))
|
|
|
|
(($ call fun args)
|
|
(print-exp fun port)
|
|
(format port "(")
|
|
(print-separated args print-exp "," port)
|
|
(format port ")"))
|
|
|
|
|
|
(($ refine expr field)
|
|
(print-exp expr port)
|
|
(format port "[")
|
|
(print-exp field port)
|
|
(format port "]"))
|
|
|
|
(($ function params body)
|
|
(format port "function (")
|
|
(print-separated params print-id "," port)
|
|
(format port ")")
|
|
(print-block body port))
|
|
|
|
(($ block stmts)
|
|
(print-block stmts port))
|
|
|
|
(($ new expr)
|
|
(format port "new ")
|
|
(print-exp expr port))
|
|
|
|
(($ binop op arg1 arg2)
|
|
(display "(" port)
|
|
(print-exp arg1 port)
|
|
(display ")" port)
|
|
(print-binop op port)
|
|
(display "(" port)
|
|
(print-exp arg2 port)
|
|
(display ")" port))
|
|
|
|
(($ ternary test then else)
|
|
(display "(" port)
|
|
(print-exp test port)
|
|
(display ") ? (" port)
|
|
(print-exp then port)
|
|
(display ") : (" port)
|
|
(print-exp else port)
|
|
(display ")" port))
|
|
|
|
(($ prefix op exp)
|
|
(print-prefix op port)
|
|
(display "(" port)
|
|
(print-exp exp port)
|
|
(display ")" port))
|
|
))
|
|
|
|
(define (print-binop op port)
|
|
(case op
|
|
((or) (display "||" port))
|
|
((and) (display "&&" port))
|
|
((=) (display "==" port))
|
|
((+ - < <= > >= ===) (format port "~a" op))
|
|
(else
|
|
(throw 'unprintable-binop op))))
|
|
|
|
(define (print-prefix op port)
|
|
(case op
|
|
((not) (display "!" port))
|
|
((typeof + -)
|
|
(format port "~a" op))
|
|
(else
|
|
(throw 'unprintable-prefix op))))
|
|
|
|
(define (print-statement stmt port)
|
|
(match stmt
|
|
(($ var id exp)
|
|
(format port "var ")
|
|
(print-id id port)
|
|
(format port " = ")
|
|
(print-exp exp port)
|
|
(format port ";"))
|
|
|
|
(($ branch test then else)
|
|
(format port "if (")
|
|
(print-exp test port)
|
|
(format port ") {")
|
|
(print-block then port)
|
|
(format port "} else {")
|
|
(print-block else port)
|
|
(format port "}"))
|
|
|
|
(($ return expr)
|
|
(format port "return ")
|
|
(print-exp expr port)
|
|
(format port ";"))
|
|
|
|
(expr
|
|
(print-exp expr port)
|
|
(format port ";"))))
|
|
|
|
(define (print-id id port)
|
|
(display id port))
|
|
|
|
(define (print-block stmts port)
|
|
(format port "{")
|
|
(print-statements stmts port)
|
|
(format port "}"))
|
|
|
|
(define (print-statements stmts port)
|
|
(for-each (lambda (stmt)
|
|
(print-statement stmt port))
|
|
stmts))
|
|
|
|
(define (print-const c port)
|
|
(cond ((string? c)
|
|
;; FIXME:
|
|
;; Scheme strings and JS Strings are different, and not just in
|
|
;; terms of mutability
|
|
(write c port))
|
|
((number? c)
|
|
(write c port))
|
|
(else
|
|
(throw 'unprintable-const c))))
|
|
|
|
(define (print-separated args printer separator port)
|
|
(unless (null? args)
|
|
(let ((first (car args))
|
|
(rest (cdr args)))
|
|
(printer first port)
|
|
(for-each (lambda (x)
|
|
(display separator port)
|
|
(printer x port))
|
|
rest))))
|