Skip to content

Commit

Permalink
Added a very simple Javascript module system.
Browse files Browse the repository at this point in the history
Any JS file written in the s-exp language (not the plain
language, yet) exports a single binding called 'script',
which contains the code as a Javascript structure. This
structure is also placed in Javascript registry
(javascript-registry.ss) where it can be later recalled
using registry->string. As modules are always loded in
topological order they will always be registered in the
correct order so dependencies come before scripts that
depend on them.

I have one final change to make: require statements for
Javascript inside a Javascript file should automatically
prefix their import so it doesn't clash with the script
binding created in the requiring module.

Note that javascript-registry.ss is written in Typed Scheme.
  • Loading branch information
Noel Welsh committed Jun 24, 2009
1 parent 3a064b6 commit 3ec6ef9
Show file tree
Hide file tree
Showing 7 changed files with 127 additions and 7 deletions.
8 changes: 6 additions & 2 deletions javascript/all-javascript-tests.ss
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@
"lang-test.ss"
#;"render-test.ss"
"response-test.ss"
"syntax-test.ss")
"syntax-test.ss"
"javascript-registry-test.ss"
"sexp/module-test.ss")

(define all-javascript-tests
(test-suite "javascript"
Expand All @@ -16,7 +18,9 @@
lang-tests
syntax-tests
#;render-tests
response-tests))
response-tests
javascript-registry-tests
module-tests))

; Provide statements -----------------------------

Expand Down
34 changes: 34 additions & 0 deletions javascript/javascript-registry-test.ss
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#lang scheme/base

(require "../test-base.ss"
"javascript-registry.ss"
"javascript.ss")

(define/provide-test-suite javascript-registry-tests

#:before (lambda () (registry-clear!))

(test-case
"registered script returned from registry"
(after
(define script (js (+ 1 2)))
(registry-add! script)
(check-equal? (registry->string) (javascript->string script))

(registry-clear!)))

(test-case
"registered scripts returned in order of registering"
(after
(define s1 (js (+ 1 2)))
(define s2 (js (/ 2 1)))
(registry-add! s1)
(registry-add! s2)
(check-equal? (registry->string)
(string-append
(javascript->string s1)
"\n"
(javascript->string s2)))

(registry-clear!)))
)
25 changes: 25 additions & 0 deletions javascript/javascript-registry.ss
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#lang typed-scheme

(require/opaque-type Javascript javascript? "javascript.ss")
(require/typed scheme/string [string-join ((Listof String) String -> String)])
(require/typed "javascript.ss" [javascript->string (Javascript -> String)])

(: registry (Listof Javascript))
(define registry null)

(: registry-add! (Javascript -> Void))
(define (registry-add! s)
(set! registry (cons s registry)))

(: registry->string (-> String))
(define (registry->string)
(string-join (map javascript->string (reverse registry)) "\n"))

(: registry-clear! (-> Void))
(define (registry-clear!)
(set! registry null))

(provide
registry-add!
registry->string
registry-clear!)
44 changes: 44 additions & 0 deletions javascript/sexp/module-test.ss
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#lang scheme/base

(require scheme/match
scheme/runtime-path
"../../test-base.ss"
"../javascript.ss"
"../javascript-registry.ss")

(define-runtime-path here ".")
(define t1:script #f)

(define/provide-test-suite module-tests

;; Dynamically requiring the test1.ss module here makes
;; this test immune to the effects of other tests that may
;; play around with the registry. If we statically
;; required test1.ss, another test suite might clear the
;; registry before we get run.
#:before (lambda ()
(registry-clear!)
(set! t1:script (dynamic-require (build-path here "test1.ss") 'script)))

(test-case
"module exports correct script binding"
(check-equal? t1:script
(js (function dave (a b) (+ a b))
(function noel (a b) (/ a b)))))

(test-case
"registry contains module's script"
(check
regexp-match?
(regexp-quote (javascript->string t1:script))
(registry->string)))

(test-case
"registry contains dependency's script"
(match (regexp-match-positions
(regexp-quote (javascript->string t1:script))
(registry->string))
[(list (cons start end))
(check > start 0)]
[_ (fail "Registry does not contain test1.ss script")]))
)
13 changes: 8 additions & 5 deletions javascript/sexp/module.ss
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
(require (for-syntax scheme/base
(planet untyped/unlib:3/debug)
(planet untyped/unlib:3/syntax))
"../javascript.ss")
"../javascript.ss"
"../javascript-registry.ss")

(define-for-syntax (extract-requires stx)
(let loop ([stx stx] [req-accum null] [stmt-accum null])
Expand All @@ -23,13 +24,15 @@
(syntax-case stx ()
[(module-begin) #'(#%plain-module-begin (begin #f))]
[(module-begin req+stmt ...)
(with-syntax ([((require ...) (stmt ...))
(with-syntax ([script (datum->syntax stx 'script)]
[((require ...) (stmt ...))
(extract-requires #'(req+stmt ...))])
#'(#%plain-module-begin
require ...
(define ans (js stmt ...))
(display (javascript->pretty-string ans))
(provide ans)))]))
(define script (js stmt ...))
(display (javascript->pretty-string script))
(registry-add! script)
(provide script)))]))

(provide (rename-out [module-begin #%module-begin])
(except-out (all-from-out scheme/base) #%module-begin))
6 changes: 6 additions & 0 deletions javascript/sexp/test1.ss
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#lang s-exp "module.ss"

(require (prefix-in foo: "test2.ss"))

(function dave (a b) (+ a b))
(function noel (a b) (/ a b))
4 changes: 4 additions & 0 deletions javascript/sexp/test2.ss
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#lang s-exp "module.ss"

(function matt (a b) (* a b))
(function xian (a b) (- a b))

0 comments on commit 3ec6ef9

Please sign in to comment.