eval/inject improved; testing won t fail on non existing javascript engine
This commit is contained in:
@@ -8,9 +8,10 @@
|
||||
|
||||
(provide js js/expression)
|
||||
|
||||
;; Convert a Racket value produced by (eval <racket-expr>) inside a js form to
|
||||
;; a JavaScript literal string. This is a Racket -> JavaScript interpolation
|
||||
;; boundary, not JavaScript eval.
|
||||
;; Convert a Racket value produced by (inject <racket-expr>) or the
|
||||
;; historical alias (eval <racket-expr>) inside a js form to a JavaScript
|
||||
;; literal string. This is a Racket -> JavaScript interpolation boundary,
|
||||
;; not JavaScript eval.
|
||||
(define (jsmaker-runtime-escape-js-string s)
|
||||
(define out (open-output-string))
|
||||
(for ([ch (in-string s)])
|
||||
@@ -261,8 +262,9 @@
|
||||
(format "__~a_~a" prefix tmp-counter))
|
||||
|
||||
;; Runtime interpolation escape hatch. Before compiling the syntax to a
|
||||
;; datum, the public macros replace each (eval <racket-expr>) subform by a
|
||||
;; unique placeholder symbol. The generated macro result is then a Racket
|
||||
;; datum, the public macros replace each (inject <racket-expr>) subform by a
|
||||
;; unique placeholder symbol. The historical alias (eval <racket-expr>) is
|
||||
;; kept for compatibility. The generated macro result is then a Racket
|
||||
;; expression that replaces those placeholders by JavaScript literals computed
|
||||
;; from <racket-expr> in the use-site lexical context. This is intentionally
|
||||
;; different from JavaScript eval, which remains available as
|
||||
@@ -276,27 +278,49 @@
|
||||
(define (fresh-eval-placeholder)
|
||||
(set! counter (add1 counter))
|
||||
(string->symbol (format "__rkt_eval_slot_~a__" counter)))
|
||||
(define (walk d)
|
||||
(match d
|
||||
[(list 'quote _) d]
|
||||
[(list 'quasiquote _) d]
|
||||
[(list 'eval expr)
|
||||
(define ph (fresh-eval-placeholder))
|
||||
(set! evals (append evals (list (list (symbol->string ph) expr))))
|
||||
ph]
|
||||
[(? pair?) (cons (walk (car d)) (walk (cdr d)))]
|
||||
[(? vector?) (list->vector (map walk (vector->list d)))]
|
||||
[_ d]))
|
||||
(values (walk (syntax->datum stx)) evals))
|
||||
(define (syntax-head-symbol x)
|
||||
(define xs (syntax->list x))
|
||||
(and xs
|
||||
(pair? xs)
|
||||
(identifier? (car xs))
|
||||
(syntax-e (car xs))))
|
||||
(define (walk-pair p)
|
||||
(cond
|
||||
[(null? p) '()]
|
||||
[(pair? p) (cons (walk (car p)) (walk-pair (cdr p)))]
|
||||
[else (walk p)]))
|
||||
(define (walk x)
|
||||
(define e (if (syntax? x) (syntax-e x) x))
|
||||
(cond
|
||||
[(syntax? x)
|
||||
(case (syntax-head-symbol x)
|
||||
[(quote quasiquote) (syntax->datum x)]
|
||||
[(inject eval)
|
||||
(define xs (syntax->list x))
|
||||
(cond
|
||||
[(and xs (= (length xs) 2))
|
||||
(define ph (fresh-eval-placeholder))
|
||||
(set! evals (append evals (list (list (symbol->string ph) (cadr xs)))))
|
||||
ph]
|
||||
[else (walk-pair e)])]
|
||||
[else
|
||||
(cond
|
||||
[(pair? e) (walk-pair e)]
|
||||
[(vector? e) (list->vector (map walk (vector->list e)))]
|
||||
[else e])])]
|
||||
[(pair? e) (walk-pair e)]
|
||||
[(vector? e) (list->vector (map walk (vector->list e)))]
|
||||
[else e]))
|
||||
(values (walk stx) evals))
|
||||
|
||||
(define (wrap-compiled-js stx compiled evals)
|
||||
(for/fold ([acc #`#,compiled]) ([ev (in-list evals)])
|
||||
(define placeholder (first ev))
|
||||
(define racket-expr-datum (second ev))
|
||||
(define racket-expr-stx (second ev))
|
||||
#`(string-replace #,acc
|
||||
#,placeholder
|
||||
(jsmaker-runtime-value->js
|
||||
#,(datum->syntax stx racket-expr-datum)))))
|
||||
#,racket-expr-stx))))
|
||||
|
||||
;; Racket conditionals treat only #f as false. JavaScript would also
|
||||
;; reject 0, "", null and undefined in condition position, so every
|
||||
@@ -1408,6 +1432,8 @@ if (~a !== false) return ~a;" tmp (compile-expr arg) tmp tmp)))
|
||||
(format "(~a = ~a)" (compile-assignment-target target) (compile-expr rhs))]
|
||||
[(list 'return) "undefined"]
|
||||
[(list 'return e) (compile-expr e)]
|
||||
[(list 'inject racket-expr)
|
||||
(compile-racket-eval racket-expr)]
|
||||
[(list 'eval racket-expr)
|
||||
(compile-racket-eval racket-expr)]
|
||||
[(list f args ...) (compile-call f args)]
|
||||
@@ -1424,6 +1450,12 @@ if (~a !== false) return ~a;" tmp (compile-expr arg) tmp tmp)))
|
||||
[(list 'delete-prop! _ ...) #f]
|
||||
[(list 'while _ ...) #f]
|
||||
[(list 'for _ ...) #f]
|
||||
;; Top-level js is statement/program output. with-handlers may contain
|
||||
;; a handler used only for side effects; compiling it as an implicit
|
||||
;; tail return would emit return statements inside the generated
|
||||
;; try/catch and prematurely leave the caller's wrapper. Use
|
||||
;; js/expression when the value of with-handlers is needed.
|
||||
[(list 'with-handlers _ ...) #f]
|
||||
[_ #t]))
|
||||
|
||||
(define (compile-top forms)
|
||||
|
||||
Reference in New Issue
Block a user