diff --git a/main.rkt b/main.rkt index 7005df3..6bf1a6c 100644 --- a/main.rkt +++ b/main.rkt @@ -123,6 +123,15 @@ (define s2 (regexp-replace* #rx"[^A-Za-z0-9_$]" s1 "_")) (if (regexp-match? #rx"^[A-Za-z_$]" s2) s2 (string-append "_" s2))) + (define (dot-prop-symbol? x) + (and (symbol? x) + (let ([s (symbol->string x)]) + (and (> (string-length s) 1) + (char=? (string-ref s 0) #\.))))) + + (define (dot-prop->js x) + (prop->js (string->symbol (substring (symbol->string x) 1)))) + (define (compile-assignment-target target) (match target [(? symbol?) (id->js target)] @@ -398,6 +407,14 @@ (format "if (~a) ~a\nreturn undefined;" (compile-expr c) (block (compile-body body)))] [(list 'unless c body ...) (format "if (!(~a)) ~a\nreturn undefined;" (compile-expr c) (block (compile-body body)))] + [(list 'set! _ ...) + (join-lines (list (compile-stmt d) "return undefined;"))] + [(list 'vector-set! _ ...) + (join-lines (list (compile-stmt d) "return undefined;"))] + [(list 'set-prop! _ ...) + (join-lines (list (compile-stmt d) "return undefined;"))] + [(list 'delete-prop! _ ...) + (join-lines (list (compile-stmt d) "return undefined;"))] [_ (format "return ~a;" (compile-expr d))])) (define (compile-stmt d) @@ -414,6 +431,8 @@ (format "let ~a = ~a;" (id->js id) (compile-expr rhs))] [(list 'define-values ids rhs) (format "let [~a] = ~a;" (compile-arg-list ids) (compile-expr rhs))] + [(list 'set! obj (? dot-prop-symbol? prop) rhs) + (format "~a.~a = ~a;" (compile-expr obj) (dot-prop->js prop) (compile-expr rhs))] [(list 'set! target rhs) (format "~a = ~a;" (compile-assignment-target target) (compile-expr rhs))] [(list 'return) @@ -1383,6 +1402,8 @@ if (~a !== false) return ~a;" tmp (compile-expr arg) tmp tmp))) (parens (compile-delete-target obj key))] [(list 'set-prop! obj key val) (format "(~a[~a] = ~a)" (compile-expr obj) (compile-expr key) (compile-expr val))] + [(list 'set! obj (? dot-prop-symbol? prop) rhs) + (format "(~a.~a = ~a)" (compile-expr obj) (dot-prop->js prop) (compile-expr rhs))] [(list 'set! target rhs) (format "(~a = ~a)" (compile-assignment-target target) (compile-expr rhs))] [(list 'return) "undefined"] @@ -1392,8 +1413,26 @@ if (~a !== false) return ~a;" tmp (compile-expr arg) tmp tmp))) [(list f args ...) (compile-call f args)] [_ (literal->js d)])) + (define (top-tail-returnable? d) + (match d + [(list 'define _ ...) #f] + [(list 'define-values _ ...) #f] + [(list 'define-class _ ...) #f] + [(list 'set! _ ...) #f] + [(list 'vector-set! _ ...) #f] + [(list 'set-prop! _ ...) #f] + [(list 'delete-prop! _ ...) #f] + [(list 'while _ ...) #f] + [(list 'for _ ...) #f] + [_ #t])) + (define (compile-top forms) - (compile-body forms #:return-last? #f))) + (cond + [(null? forms) ""] + [(top-tail-returnable? (last forms)) + (compile-body forms #:return-last? #t)] + [else + (compile-body forms #:return-last? #f)]))) (define-syntax (js stx) (syntax-case stx () diff --git a/scrbl/js-maker.scrbl b/scrbl/js-maker.scrbl index 9f607ed..2b4ff3e 100644 --- a/scrbl/js-maker.scrbl +++ b/scrbl/js-maker.scrbl @@ -16,7 +16,7 @@ (list (verbatim rkt) (verbatim js))))) @title{js-maker: a Syntax-Driven Racket-to-JavaScript Generator} -@author+email["Hans Dijkema" "hans@dijkewijk.nl"] +@author+email["Hans Dijkema" ""] @defmodule[jsmaker] @@ -58,7 +58,32 @@ function square(x) { Inside function bodies, the last expression is returned automatically unless it is already a statement form such as @racket[return], @racket[define], -@racket[set!], @racket[while], or @racket[for]. +@racket[set!], @racket[while], or @racket[for]. At the top level of a +@racket[js] form, js-maker also returns the value of a final value-producing +form, such as @racket[let], @racket[begin], @racket[if], or +@racket[with-handlers]. This makes @racket[js] output suitable as the body of a +WebView @tt{runJavaScript} wrapper function. + +For example: + +@racketblock[ +(let ([html "
4 words
"))) - #:preamble (dom-preamble "These are four words")) + (side-effect-expr (dom-preamble "These are four words") exercise04 "state.afterHeading") + (jsexpr->string '(("afterend" "4 words
")))) - (js-program-test + (js-expression-test 'dom-ex05-replace-punctuation-faces - exercise05 - "paragraph.innerHTML" - (jsexpr->string "Really🤔 Yes😲 No🤔") - #:preamble (dom-preamble "Really? Yes! No?")))) + (side-effect-expr (dom-preamble "Really? Yes! No?") exercise05 "paragraph.innerHTML") + (jsexpr->string "Really🤔 Yes😲 No🤔")))) (define engine (find-js-engine)) (run-jsmaker-regression 'jsmaker-dom-exercises tests "/tmp/jsmaker-dom-exercises.js" #:engine engine) diff --git a/testing/jsmaker-regression.rkt b/testing/jsmaker-regression.rkt index f9c5a51..79c6331 100644 --- a/testing/jsmaker-regression.rkt +++ b/testing/jsmaker-regression.rkt @@ -63,12 +63,6 @@ (js-expression-test 'for-fold (js/expression (for/fold ([s 0]) ([x (in-list (list 1 2 3))]) (+ x s))) "6") (js-expression-test 'map-filter (js/expression (filter (lambda (x) (> x 2)) (map (lambda (x) (+ x 1)) (list 1 2 3)))) "[3,4]") (js-expression-test 'hash-ref (js/expression (hash-ref (hash 'a 1 'b 2) 'b)) "2") - (js-expression-test 'compile-time-eval-var - (let ((x 10) - (y 20)) - (js/expression (let ((a (eval (* x y)))) - (+ a a)))) - "400") (js-expression-test 'compile-time-eval-number (js/expression (eval (+ 1 2))) "3")