diff --git a/info.ss b/info.ss index ccdc8cf..8e3261c 100644 --- a/info.ss +++ b/info.ss @@ -7,7 +7,9 @@ (define release-notes '((p "Changes and additions:") - (ul (li "named function declarations are now allowed within !begin blocks;")))) + (ul (li "replaced the " (tt "render-pretty-javascript?") " parameter with " (tt "javascript-rendering-mode") ";") + (li "added " (tt "javascript->packed-string") ";") + (li "fixed bug that disallowed named function declarations within " (tt "!begin") " blocks.")))) (define primary-file "mirrors.ss") diff --git a/javascript/render-fast.ss b/javascript/render-fast.ss index 01eabc0..39e248a 100644 --- a/javascript/render-fast.ss +++ b/javascript/render-fast.ss @@ -36,7 +36,7 @@ (display-js expr))] [(struct FunctionDeclaration (_ id args body)) (display-str "function ") (display-js id) - (display-str " (") + (display-str "(") (display-list ", " args) (display-str ") { ") (for-each display-js body) diff --git a/javascript/render.ss b/javascript/render.ss index a234fb6..39c7141 100644 --- a/javascript/render.ss +++ b/javascript/render.ss @@ -11,35 +11,38 @@ ; Parameters ------------------------------------- -; (parameter boolean) -(define render-pretty-javascript? - (make-parameter #t)) +; (parameter (U 'pretty 'packed 'fast)) +(define javascript-rendering-mode + (make-parameter 'pretty)) ; Public procedures ------------------------------ ; javascript -> string (define (javascript->string js) - #;(parameterize ([allow-nested-function-declarations? #t] - [formatters/Expression (list* format-FunctionExpression - format-RawExpression - (formatters/Expression))] - [formatters/Statement (list* format-BeginStatement - (formatters/Statement))]) - (pretty-format (group (format-term js)) #f)) - (if (render-pretty-javascript?) - (javascript->pretty-string js) - (fast-javascript->string js))) + (case (javascript-rendering-mode) + [(pretty) (parameterize ([formatters/Expression (list* format-FunctionExpression + format-RawExpression + (formatters/Expression))] + [formatters/Statement (list* format-BeginStatement + (formatters/Statement))]) + (pretty-format (format-term js)))] + [(packed) (parameterize ([formatters/Expression (list* format-FunctionExpression + format-RawExpression + (formatters/Expression))] + [formatters/Statement (list* format-BeginStatement + (formatters/Statement))]) + (pretty-format (group (format-term js)) #f))] + [(fast) (fast-javascript->string js)])) +; javascript -> string +(define (javascript->packed-string js) + (parameterize ([javascript-rendering-mode 'packed]) + (javascript->string js))) ; javascript -> string (define (javascript->pretty-string js) - (parameterize ([allow-nested-function-declarations? #t] - [formatters/Expression (list* format-FunctionExpression - format-RawExpression - (formatters/Expression))] - [formatters/Statement (list* format-BeginStatement - (formatters/Statement))]) - (pretty-format (format-term js)))) + (parameterize ([javascript-rendering-mode 'pretty]) + (javascript->string js))) ; Custom printers -------------------------------- @@ -67,12 +70,22 @@ (let ([statements (reverse (collect-begin-substatements statements))]) (if (null? statements) (h-append) - (h-append (format-substatement (car statements)) + (h-append (format-begin-substatement (car statements)) (format-map (lambda (statement) - (h-append line (format-substatement statement))) + (h-append line (format-begin-substatement statement))) (cdr statements) formatters/StatementList))))])) +; (U Declaration Statement) -> doc +; Named function declarations aren't allowed inside regular statements in some +; JS VMs, so by default JS.plt disallows this arrangement using the contract on +; format-substatement. This is obviously no good for BeginStatements: the function +; below provides a workaround. +(define (format-begin-substatement stmt+decl) + (if (Declaration? stmt+decl) + (format-declaration stmt+decl) + (format-statement stmt+decl))) + ; RawStatement -> doc (define format-RawExpression (match-lambda @@ -92,6 +105,7 @@ ; Provide statements ----------------------------- (provide/contract - [render-pretty-javascript? (parameter/c boolean?)] + [javascript-rendering-mode (parameter/c (or/c 'pretty 'packed 'fast))] [javascript->string (-> javascript? string?)] + [javascript->packed-string (-> javascript? string?)] [javascript->pretty-string (-> javascript? string?)]) diff --git a/javascript/syntax-test.ss b/javascript/syntax-test.ss index 92c90b9..ff9a9fc 100644 --- a/javascript/syntax-test.ss +++ b/javascript/syntax-test.ss @@ -45,6 +45,8 @@ (define syntax-tests (test-suite "syntax.ss" + #:before (cut javascript-rendering-mode 'packed) + (test-js "expander: decl" (!var-debug [a 1] [b 2]) "var a = 1; console.log(\"a\" + a); var b = 2; console.log(\"b\" + b);") @@ -76,7 +78,7 @@ (test-js "decl: var unquote" (var [,(make-Identifier #f 'x) 1] [y ,(+ 2 3)]) "var x = 1, y = 5;") - + (test-js "stmt: empty begin" (!begin) "") (test-js "stmt: begin" @@ -85,6 +87,12 @@ (+ 3 4 5)) "1 + 2 + 3; var x = 2 + 3 + 4; 3 + 4 + 5;") + (test-js "stmt: begin containing nested function declarations" + (!begin (function a () (return)) + (function b () (return))) + "function a() { return; } function b() { return; }") + + (test-js "stmt: empty block" (!block) "{}") (test-js "stmt: block" diff --git a/scribblings/javascript-response.scrbl b/scribblings/javascript-response.scrbl index 29b0eb3..875a67b 100644 --- a/scribblings/javascript-response.scrbl +++ b/scribblings/javascript-response.scrbl @@ -2,6 +2,8 @@ @(require (file "base.ss")) +@(define-eval js-eval (for-syntax scheme/base) (planet untyped/mirrors/javascript/javascript)) + @title[#:tag "javascript-response"]{Rendering Javascript and sending Javascript responses} @(declare-exporting (planet untyped/mirrors/javascript/javascript)) @@ -9,21 +11,37 @@ @section{Rendering Javascript in string form} @defproc[(javascript->string [val javascript-statement?]) string?]{ -Renders a Javascript statement as a compact string with no line breaks or indentation.} +Renders a Javascript statement using the current @scheme[javascript-rendering-mode].} @defproc[(javascript->pretty-string [val javascript-statement?]) string?]{ Renders a Javascript statement as a formatted string with line breaks and indentation.} -@defthing[render-pretty-javascript? (parameter boolean?)]{ -Affects the output of @scheme[javascript->string] and @scheme[javascript->pretty-string]: +@defproc[(javascript->packed-string [val javascript-statement?]) string?]{ +Renders a Javascript statement on a single line with no indentation.} -@itemize{ - @item{when @scheme[render-pretty-javascript?] is set to @scheme[#t] (the default value), the rendering procedures behave as documented above;} - @item{when @scheme[render-pretty-javascript?] is set to @scheme[#f], the rendering procedures switch to an experimental renderer that writes all Javascript extremely quickly onto a single line.}} - -Essentially, setting @scheme[render-pretty-javascript?] to @scheme[#t] gives better rendering performance at the expense of legibility. The performance gains are only noticeable when rendering large (1000+ line) blocks of Javascript. +@defthing[javascript-rendering-mode (parameter (U 'pretty 'packed 'fast))]{ +Affects the output of @scheme[javascript->string] and @scheme[javascript->pretty-string]. The possible values are: -At the time of writing the fast renderer has not been properly tested: use it with caution!} +@itemize{ + @item{@scheme['pretty] (the default value) - @scheme[javascript->string] renders Javascript as well formatted blocks;} + @item{@scheme['packed] - @scheme[javascript->string] renders Javascript on a single line, with no indentation;} + @item{@scheme['fast] - @scheme[javascript->string] uses an experimental renderer that writes all Javascript extremely quickly onto a single line.}} + +The @scheme['fast] rendering mode is extremely naive: it inserts extra parentheses in expressions and extra semicolons between statements. At the time of writing, it has not been properly tested: use it with caution! + +@examples[ + #:eval js-eval + (define (display-statement) + (display + (javascript->string + (js (function average (a b) + (return (+ (/ a 2) (/ b 2)))))))) + (parameterize ([javascript-rendering-mode 'pretty]) + (display-statement)) + (parameterize ([javascript-rendering-mode 'packed]) + (display-statement)) + (parameterize ([javascript-rendering-mode 'fast]) + (display-statement))]} @section{Sending HTTP responses with Javascript content}