#lang scheme/base
(require (planet "evector.scm" ("soegaard" "evector.plt" 1))
(except-in srfi/1/list any)
(only-in scheme/list flatten)
scheme/string
scheme/match
scheme/require-transform
scheme/promise
"../syntax/ast-core.ss"
"../syntax/ast-utils.ss"
"../syntax/token.ss"
"../syntax/exceptions.ss"
"../runtime/runtime.ss"
"../../debug.ss"
"../syntax/parse.ss"
"../planet.ss"
"helpers.ss"
"hoist.ss"
"context.ss")
(require (for-syntax scheme/require-transform)
(for-syntax scheme/base))
(require (for-template scheme/base)
(for-template (planet "evector.scm" ("soegaard" "evector.plt" 1)))
(for-template "../runtime/runtime.ss")
(for-template "../syntax/parse.ss"))
(provide compile-module compile-script compile-global compile-interaction compile-function-expression with-syntax-errors)
(define-syntax-rule (syntax/loc* loc expr)
(syntax/loc (region->syntax loc)
expr))
(define (loop? stmt)
(or (DoWhileStatement? stmt)
(WhileStatement? stmt)
(ForStatement? stmt)
(ForInStatement? stmt)))
(define (with-syntax-errors thunk)
(with-handlers ([exn:fail:syntax?
(lambda (exn)
(let* ([loc (exn:fail:syntax-location exn)]
[text (format "~a" (exn:fail:syntax-text exn))]
[stxloc (build-syntax (string->symbol text) loc)])
(raise-syntax-error 'parse (exn-message exn) stxloc stxloc)))])
(thunk)))
(define (module-declaration-context?)
(and (eq? (current-compilation-context) 'module)
(eq? (current-lexical-context) 'top)
(not (current-nested?))))
(define (Identifier->Variable id)
(make-Variable id (Identifier->syntax id #:context (current-source-syntax))))
(define (Identifier->Import internal external module-spec module-path)
(let ([id (Identifier->syntax internal #:context (current-source-syntax))])
(make-Import internal id module-spec module-path (delay (eval-import? module-path (Identifier-name external))))))
(define (resolve-import path sym)
(let-values ([(imports sources) (expand-import (with-syntax ([path path]) #'path))])
(cond
[(findf (lambda (import) (eq? (import-src-sym import) sym)) imports)
=> (lambda (import)
(import-local-id import))]
[else #f])))
(define eval-import
(delay (resolve-import standard-library-path 'eval)))
(define (eval-import? module-path sym)
(cond
[(resolve-import module-path sym)
=> (lambda (id)
(free-identifier=? id (force eval-import)))]
[else #f]))
(define ((compile-export all-defined) export)
(unless (module-declaration-context?)
(raise-syntax-error 'compile "illegal context for export statement" (region->syntax (Term-location export))))
(match export
[(struct ExportDeclaration (loc export-specs))
(with-syntax ([(spec ...) (map (compile-export-spec all-defined) export-specs)])
(syntax/loc* loc
(provide spec ...)))]))
(define ((compile-export-spec all-defined) spec)
(match spec
[(? Identifier?)
(let ([symbol (Identifier-name spec)])
(cond
[(findf (lambda (variable)
(eq? (Identifier-name (Variable-source variable)) symbol))
all-defined)
=> Variable-compiled]
[else
(raise-syntax-error 'compile (format "unbound export variable: ~a" symbol) (region->syntax (Term-location spec)))]))]
[(struct ExportBindings (loc bindings))
(raise-syntax-error 'compile "multiple-identifier export not yet implemented" (region->syntax loc))]
[(struct ReexportSpecifier (loc module exclusions))
(raise-syntax-error 'compile "module re-export not yet implemented" (region->syntax loc))]
[(struct ExclusionList (loc (list)))
(with-syntax ([(x ...) (map Variable-compiled all-defined)])
(syntax/loc* loc
(combine-out x ...)))]
[(struct ExclusionList (loc (list exclude ...)))
(raise-syntax-error 'compile "exclusion list not yet implemented" (region->syntax loc))]))
(define (compile-import-binding module-spec module-path binding)
(match binding
[(struct ImportBinding (loc label #f))
(compile-import-binding module-spec module-path (make-ImportBinding loc label label))]
[(struct ImportBinding (loc label internal))
(let ([internal-id (Identifier->syntax internal #:context (current-source-syntax))]
[external-id (Identifier->syntax label #:context #'here)])
(values (with-syntax ([module-path module-path]
[internal internal-id]
[external external-id])
(syntax/loc* loc (rename-in module-path [external internal])))
(make-Import internal internal-id module-spec module-path (delay (eval-import? module-path (Identifier-name label))))))]))
(define (parse-planet-path path loc)
(define ((maybe p?) x)
(or (p? x) (not x)))
(match path
[(list (? string? symbolic-path))
`(planet ,(string->symbol symbolic-path))]
[(list (? string? user) (? string? package))
(parse-planet-path `(,user ,package #f #f "main.ss"))]
[(list (? string? user) (? string? package) (? (maybe number?) major))
(parse-planet-path `(,user ,package ,major #f "main.ss"))]
[(list (? string? user) (? string? package) (? (maybe number?) major) (? (maybe number?) minor) (? (maybe string?) path))
(let* ([path (reverse (regexp-split #rx"/" (or path "main.ss")))]
[file (car path)]
[subpath (reverse (cdr path))]
[major (or (and major (list major)) '())]
[minor (or (and minor (list minor)) '())])
`(planet ,file (,user ,package ,@major ,@minor) ,@subpath))]
[(list path ...)
(raise-syntax-error 'compile
(format "planet protocol: expects (string) or (string, string[, [number][, [number][, [string]]]]); received ~a" path)
(region->syntax loc))]))
(define (compile-module-spec spec)
(match spec
[(struct ModuleSpecifier (loc 'file (list (? string? path))))
path]
[(struct ModuleSpecifier (loc 'planet (list path ...)))
(parse-planet-path path loc)]
[(struct ModuleSpecifier (loc 'collect (list 'js)))
standard-library-path]
[(struct ModuleSpecifier (loc 'collect (list path ...)))
(string->symbol (string-join (map symbol->string path) "/"))]))
(define (compile-import-spec spec)
(match spec
[(struct ImportSpecifier (loc module bindings))
(match bindings
[(? Identifier?)
(let ([loc (Term-location bindings)])
(compile-import-spec (make-ImportSpecifier loc module (list (make-ImportBinding loc bindings #f)))))]
[(struct ExclusionList (loc* (list)))
(with-syntax ([module-path (datum->syntax (current-source-syntax) (compile-module-spec module))])
(values #'module-path
(let-values ([(imports sources) (expand-import #'module-path)])
(for/list ([import imports])
(let ([id (make-Identifier loc* (syntax->datum (import-local-id import)))])
(Identifier->Import id id module #'module-path))))))]
[(struct ExclusionList (_ (list excludes ...)))
(raise-syntax-error 'compile "exclusion list not yet implemented" (region->syntax loc))]
[(list (? ImportBinding? bindings) ...)
(let ([module-path (compile-module-spec module)])
(let-values ([(specs imports) (for/lists (specs imports) ([binding bindings])
(compile-import-binding module module-path binding))])
(values (with-syntax ([(spec ...) specs])
(syntax/loc* loc
(combine-in spec ...)))
imports)))])]))
(define (compile-import import)
(unless (module-declaration-context?)
(raise-syntax-error 'compile "illegal context for import statement" (region->syntax (Term-location import))))
(match import
[(struct ImportDeclaration (loc (list (? ImportSpecifier? import-specs) ...)))
(let-values ([(requires imports) (for/lists (requires imports) ([import-spec import-specs])
(compile-import-spec import-spec))])
(with-syntax ([(require-spec ...) requires])
(values #'(require require-spec ...)
(flatten imports))))]))
(define (compile-imports decls)
(let-values ([(requires imports) (for/lists (requires imports) ([decl decls])
(compile-import decl))])
(values requires (flatten imports))))
(define (compile-module-declarations funs vars import-decls export-decls)
(let-values ([(requires imports) (compile-imports import-decls)])
(let* ([fun-ids (map FunctionDeclaration-name funs)]
[all-defined (map Identifier->Variable (append fun-ids vars))]
[provides (map (compile-export all-defined) export-decls)]
[new-env (bind (append all-defined imports) (current-scope))]
[definitions (with-syntax ([(var ...) (map Variable-compiled all-defined)]
[(init-e ...) (append (with-scope new-env
(map compile-function-declaration funs))
(map (lambda (var) #'(void)) vars))])
(syntax->list #'((define var init-e) ...)))])
(values requires provides definitions new-env))))
(define (compile-module elts)
(let-values ([(funs vars imports exports body) (hoist-program-unit elts)])
(parameterize ([current-compilation-context 'module]
[current-lexical-context 'top]
[current-nested? #f]
[current-pragmas (hash-set (current-pragmas) '(lexical scope) #t)]
[current-scope empty-scope])
(let-values ([(requires provides definitions initial-env) (compile-module-declarations funs vars imports exports)])
(with-syntax ([(req ...) requires]
[(prov ...) provides]
[(defn ...) definitions]
[(s ...) (with-scope initial-env
(map compile-statement body))])
#'(begin
req ...
defn ...
(begin
(current-this global-object)
(current-completion #f)
s ...
(void))
prov ...))))))
(define (compile-global elts)
(let-values ([(funs vars imports exports body) (hoist-program-unit elts)])
(when (pair? imports)
(raise-syntax-error 'compile "illegal context for import statement" (region->syntax (Term-location (car imports)))))
(when (pair? exports)
(raise-syntax-error 'compile "illegal context for export statement" (region->syntax (Term-location (car exports)))))
(parameterize ([current-lexical-context 'top]
[current-scope (and (not (contains-direct-eval? body))
(current-scope))])
(let-values ([(definitions new-env) (compile-script-declarations funs vars)])
(with-syntax ([(defn ...) definitions]
[scope-chain scope-chain-id]
[variable-object variable-object-id]
[(s ...) (with-scope new-env
(map compile-statement body))])
#'(lambda (scope-chain variable-object)
(parameterize ([current-completion #f])
defn ... s ...
(current-completion))))))))
(define (compile-script elts)
(with-syntax ([function (parameterize ([current-compilation-context 'script])
(compile-global elts))])
#'(function (list global-object) global-object)))
(define (compile-script-declarations funs vars)
(let* ([fun-ids (map FunctionDeclaration-name funs)]
[all-ids (append fun-ids vars)]
[all-variables (map Identifier->Variable all-ids)]
[new-env (bind all-variables (current-scope))]
[definitions (with-syntax ([variable-object variable-object-id]
[(var ...) (map Variable-compiled all-variables)]
[(var-key ...) (map Identifier->string-expression all-ids)]
[(init-e ...) (append (with-scope new-env
(map compile-function-declaration funs))
(map (lambda (var) #'(void)) vars))])
(syntax->list #'((define-syntax var
(syntax-id-rules (set!)
[(set! var expr) (object-put! variable-object var-key expr)]
[var (object-get variable-object var-key)]))
...
(set! var init-e) ...)))])
(values definitions new-env)))
(define (compile-interaction elt)
(let*-values ([(funs vars imports exports stmts) (hoist-program-unit elt)]
[(definitions new-env) (with-scope #f
(compile-script-declarations funs vars))])
(when (pair? imports)
(raise-syntax-error 'compile "illegal context for import statement" (region->syntax (Term-location (car imports)))))
(when (pair? exports)
(raise-syntax-error 'compile "illegal context for export statement" (region->syntax (Term-location (car exports)))))
(parameterize ([current-compilation-context 'interaction]
[current-lexical-context 'top])
(with-syntax ([(defn ...) definitions]
[scope-chain scope-chain-id]
[variable-object variable-object-id]
[(s ...) (for/list ([stmt stmts])
(dynamic-code (with-scope #f (compile-statement stmt))
(Term-location stmt)))]
[(previous-completion) (generate-temporaries '(previous-completion))])
#'(begin
(define previous-completion (current-completion))
(current-completion #f)
(define scope-chain (list global-object))
(define variable-object global-object)
defn ... s ...
(begin0
(cond
[(current-completion)
=> (lambda (v)
(object-set! global-object "it" v)
v)]
[else #f])
(current-completion previous-completion)))))))
(define (reference-expression? expr)
(or (VarReference? expr)
(BracketReference? expr)
(DotReference? expr)))
(define (compile-deletion expr)
(match expr
[(struct VarReference (loc id))
(cond
[(hash-ref (current-pragmas) '(lexical scope) (lambda () #f))
(raise-syntax-error 'compile "cannot delete lexically scoped variables" (region->syntax loc))]
[(and (current-scope) (not (bound? id)))
#'(quote true)]
[(not (current-scope))
(with-syntax ([scope-chain scope-chain-id]
[key (Identifier->string-expression id)])
(syntax/loc (region->syntax loc)
(scope-chain-delete! scope-chain key)))]
[else
#'(quote false)])]
[(struct BracketReference (loc container key))
(with-syntax ([container-e (compile-expression container)]
[key-e (compile-expression key)]
[(obj-val) (generate-temporaries '(obj-val))])
(syntax/loc (region->syntax loc)
(let ([obj-val (value->object container-e)])
(object-delete! obj-val key-e))))]
[(struct DotReference (loc container id))
(with-syntax ([container-e (compile-expression container)]
[key-e (Identifier->string-expression id)]
[(obj-val) (generate-temporaries '(obj-val))])
(syntax/loc (region->syntax loc)
(let ([obj-val (value->object container-e)])
(object-delete! obj-val key-e))))]))
(define (compile-assignment lhs rhs-stx)
(match lhs
[(struct VarReference (loc id))
(debug 'scope-resolution "looking for ~a in ~v" (Identifier-name id) (current-scope))
(cond
[(and (current-scope) (resolve id))
=> (lambda (variable)
(when (Import? variable)
(raise-syntax-error 'compile (format "cannot assign to module import ~a" (Identifier-name id)) (region->syntax loc)))
(with-syntax ([x (with-loc loc (Variable-compiled variable))]
[(val) (generate-temporaries '(val))]
[rhs-e rhs-stx])
(syntax/loc (region->syntax loc)
(let ([val rhs-e])
(set! x val)
val))))]
[(current-scope)
(debug 'unbound-reference "~a unbound at ~a" (Identifier-name id) (region->string loc))
(if (hash-ref (current-pragmas) '(lexical scope) (lambda () #f))
(raise-syntax-error 'compile (format "unbound variable ~a" (Identifier-name id)) (region->syntax loc))
(with-syntax ([key (Identifier->string-expression id)]
[rhs-e rhs-stx]
[(val) (generate-temporaries '(val))])
(syntax/loc (region->syntax loc)
(let ([val rhs-e])
(object-put! global-object key val)
val))))]
[else
(with-syntax ([scope-chain scope-chain-id]
[key (Identifier->string-expression id)]
[rhs-e rhs-stx])
(syntax/loc (region->syntax loc)
(scope-chain-set! scope-chain key rhs-e)))])]
[(struct BracketReference (loc container key))
(with-syntax ([container-e (compile-expression container)]
[key-e (compile-expression key)]
[rhs-e rhs-stx]
[(container-val key-val) (generate-temporaries '(container-val key-val))])
(syntax/loc (region->syntax loc)
(let* ([container-val (value->object container-e)]
[key-val key-e])
(object-set! container-val key-val rhs-e))))]
[(struct DotReference (loc container id))
(with-syntax ([container-e (compile-expression container)]
[rhs-e rhs-stx]
[key-val (Identifier->string-expression id)])
(syntax/loc (region->syntax loc)
(let ([container-val (value->object container-e)])
(object-set! container-val key-val rhs-e))))]
[_ (raise-syntax-error 'compile "invalid assignment left-hand side" (region->syntax (Term-location lhs)))]))
(define (compile-lookup expr)
(match expr
[(struct VarReference (loc id))
(debug 'scope-resolution "looking for ~a in ~v" (Identifier-name id) (current-scope))
(cond
[(and (current-scope) (resolve id))
=> (lambda (variable)
(with-loc loc (Variable-compiled variable)))]
[(current-scope)
(debug 'unbound-reference "~a unbound at ~a" (Identifier-name id) (region->string loc))
(if (hash-ref (current-pragmas) '(lexical scope) (lambda () #f))
(raise-syntax-error 'compile (format "unbound variable ~a" (Identifier-name id)) (region->syntax loc))
(with-syntax ([stxloc (region->syntax loc)]
[key (Identifier->string-expression id)])
(syntax/loc (region->syntax loc)
(or (object-get global-object key)
(raise (make-exn:fail:contract:variable (format "~a is not defined" key)
(current-continuation-marks)
(string->symbol key)))))))]
[else
(with-syntax ([scope-chain scope-chain-id]
[key (Identifier->string-expression id)])
(syntax/loc (region->syntax loc)
(or (scope-chain-get scope-chain key)
(raise (make-exn:fail:contract:variable (format "~a is not defined" key)
(current-continuation-marks)
(string->symbol key))))))])]
[(struct BracketReference (loc container key))
(with-syntax ([container-e (compile-expression container)]
[key-e (compile-expression key)]
[(container-val) (generate-temporaries '(container-val))])
(syntax/loc (region->syntax loc)
(let ([container-val (value->object container-e)])
(or (object-get container-val key-e) (void)))))]
[(struct DotReference (loc container id))
(with-syntax ([container-e (compile-expression container)]
[key-val (Identifier->string-expression id)]
[(container-val) (generate-temporaries '(container-val))])
(syntax/loc (region->syntax loc)
(let ([container-val (value->object container-e)])
(or (object-get container-val key-val) (void)))))]))
(define (compile-function-expression expr)
(match (hoist-expression expr)
[(struct FunctionExpression/hoisted (loc name args body funs vars imports exports))
(compile-function loc name args body funs vars)]))
(define (compile-function-declaration decl)
(match decl
[(struct FunctionDeclaration/hoisted (loc name args body funs vars imports exports))
(compile-function loc name args body funs vars)]))
(define (compile-function loc name args body funs vars)
(let ([arity (length args)])
(with-syntax ([(func-object arg-list args-object) (generate-temporaries '(func-object arg-list args-object))])
(let ([arg-bindings (map Identifier->Variable args)]
[fun-bindings (map Identifier->Variable funs)]
[var-bindings (map Identifier->Variable (cons (make-Identifier loc 'arguments) vars))]
[name-binding (map Identifier->Variable (if name (list name) null))]
[dynamic? (or (not (current-scope))
(and (not (hash-ref (current-pragmas) '(lexical scope) (lambda () #f)))
(contains-direct-eval? body)))])
(let ([still-lexically-scoped? (and (current-scope) (not dynamic?))]
[new-static-env (bind arg-bindings
(bind fun-bindings
(bind var-bindings
(bind name-binding (current-scope)))))]
[arg-refs (map (lambda (arg)
(make-VarReference (Term-location arg) arg))
args)]
[arguments-ref (make-VarReference loc (make-Identifier loc 'arguments))]
[all-local-bindings (delete-duplicates (append arg-bindings fun-bindings var-bindings) Variable=?)])
(with-syntax ([return (datum->syntax #f 'return)]
[(s ...) (parameterize ([current-lexical-context 'function]
[current-nested? #f]
[current-scope (and still-lexically-scoped? new-static-env)])
(map compile-statement body))]
[set-arguments-object!
(parameterize ([current-scope (and still-lexically-scoped? new-static-env)])
(with-syntax ([(getter ...) (map (lambda (arg-ref)
#`(lambda ()
#,(compile-lookup arg-ref)))
arg-refs)]
[(setter ...) (map (lambda (arg-ref)
(with-syntax ([v (car (generate-temporaries '(v)))])
#`(lambda (v)
#,(compile-assignment arg-ref #'v))))
arg-refs)])
(compile-assignment arguments-ref
#'(build-arguments-object func-object
(list (cons getter setter) ...)
arg-list))))]
[set-arguments!
(parameterize ([current-scope (and still-lexically-scoped? new-static-env)])
(with-syntax ([(rhs ...) (map (lambda (arg-ref)
(with-syntax ([set-undefined! (compile-assignment arg-ref #'(void))]
[set-next! (compile-assignment arg-ref #'(car arg-list))])
#'(if (null? arg-list)
(begin set-undefined! '())
(begin set-next! (cdr arg-list)))))
arg-refs)])
#'(let* ([arg-list rhs] ...)
(void))))]
[(set-nested-func! ...)
(parameterize ([current-scope (and still-lexically-scoped? new-static-env)])
(map (lambda (fun)
(compile-assignment (make-VarReference (Term-location fun) (FunctionDeclaration-name fun))
(compile-function-declaration fun)))
funs))])
(let ([block-stx (syntax/loc (region->syntax loc)
(begin
set-arguments-object!
set-arguments!
set-nested-func! ...
(parameterize ([current-completion #f])
(let/ec return
s ...
(void)))))])
(with-syntax ([body (if (or dynamic? (not (current-scope)))
(dynamic-code (with-dynamic-bindings all-local-bindings block-stx #:variable-object? #t) loc)
(with-lexical-bindings all-local-bindings block-stx))]
[arity arity])
(if name
(with-syntax ([set-f! (parameterize ([current-scope new-static-env])
(compile-assignment (make-VarReference loc name)
#'func-object))])
(with-bindings name-binding
(syntax/loc (region->syntax loc)
(letrec ([func-object (build-function arity (lambda arg-list body))])
set-f!
func-object))))
(syntax/loc (region->syntax loc)
(letrec ([func-object (build-function arity (lambda arg-list body))])
func-object)))))))))))
(define (dynamic-code body-stx [loc #f] [extend-scope-chain (lambda (scope-chain) scope-chain)] [shadow-loc #f])
(with-syntax ([scope-chain scope-chain-id]
[body-e body-stx])
(if (not (current-scope))
(with-syntax ([scope-e (extend-scope-chain scope-chain-id)])
(syntax/loc (region->syntax loc)
(let ([scope-chain scope-e])
body-e)))
(let ([variables (for/list ([var (in-hash-values (current-scope))]) var)])
(with-syntax ([(x ...) (map Variable-compiled variables)]
[(setter ...) (for/list ([variable variables])
(with-syntax ([x (Variable-compiled variable)]
[v (car (generate-temporaries '(v)))])
(if (Import? variable)
#'(lambda (v) v)
#'(lambda (v) (set! x v) v))))]
[(x-name ...) (map (compose Identifier->string-syntax Variable-source) variables)])
(with-syntax ([scope-e (extend-scope-chain
#'(list (make-frame
(object-table
[x-name (lambda () x) setter ()]
...))
global-object))])
(syntax/loc (region->syntax loc)
(let ([scope-chain scope-e])
body-e))))))))
(define (compile-statement stmt)
(if (BlockStatement/hoisted? stmt)
(compile-top-level-statement stmt)
(compile-nested-statement stmt)))
(define (compile-top-level-statement stmt)
(match stmt
[(struct BlockStatement/hoisted (loc stmts funs vars))
(let ([var-bindings (map Identifier->Variable vars)]
[fun-bindings (map (compose Identifier->Variable FunctionDeclaration-name) funs)])
(let ([new-static-env (bind var-bindings
(bind fun-bindings (current-scope)))])
(with-syntax ([(f ...) (map Variable-compiled fun-bindings)]
[(fe ...) (parameterize ([current-nested? #t])
(with-scope new-static-env
(map compile-function-declaration funs)))]
[(s ...) (with-scope new-static-env
(map compile-statement stmts))])
(with-bindings var-bindings
(with-bindings fun-bindings
(syntax/loc (region->syntax loc)
(begin
(set! f fe) ...
s ...
(current-completion))))))))]))
(define (compile-nested-statement stmt)
(parameterize ([current-nested? #t])
(match stmt
[(struct EmptyStatement (loc))
(syntax/loc* loc '#f)]
[(struct ExpressionStatement (loc expr))
(with-syntax ([e (compile-expression expr)])
(syntax/loc* loc
(complete! e)))]
[(struct IfStatement (loc test consequent alternate))
(with-syntax ([test-e (compile-expression test)]
[consequent-s (compile-statement consequent)]
[alternate-s (if alternate (compile-statement alternate) #''#f)])
(syntax/loc* loc
(if (true-value? test-e)
consequent-s
alternate-s)))]
[(? loop?)
(with-syntax ([(break continue) (generate-temporaries '(break continue))])
(parameterize ([current-labels (cons (list #f #'break #'continue)
(current-labels))])
(compile-loop stmt #'break #'continue)))]
[(struct ContinueStatement (loc #f))
(cond
[(ormap (lambda (tuple)
(and (pair? (cddr tuple))
(caddr tuple)))
(current-labels))
=> (lambda (continue-id)
(with-syntax ([continue continue-id])
(syntax/loc* loc
(continue '#f))))]
[else (let ([stxloc (build-syntax 'continue loc)])
(raise-syntax-error 'continue "invalid continue" stxloc stxloc))])]
[(struct ContinueStatement (loc label))
(cond
[(null? (current-labels))
(raise-syntax-error 'continue "invalid continue" (build-syntax 'continue loc))]
[(assq (Identifier-name label) (current-labels))
=> (lambda (tuple)
(if (pair? (cddr tuple))
(with-syntax ([continue (caddr tuple)])
(syntax/loc* loc
(continue '#f)))
(raise-syntax-error 'continue "invalid label" (region->syntax (Term-location label)))))]
[else (raise-syntax-error 'continue "invalid label" (region->syntax (Term-location label)))])]
[(struct BreakStatement (loc #f))
(when (null? (current-labels))
(let ([stxloc (build-syntax 'break loc)])
(raise-syntax-error 'break "invalid break" stxloc stxloc)))
(with-syntax ([break (cadar (current-labels))])
(syntax/loc* loc
(break (current-completion))))]
[(struct BreakStatement (loc label))
(cond
[(null? (current-labels))
(raise-syntax-error 'break "invalid break" (build-syntax 'break loc))]
[(assq (Identifier-name label) (current-labels))
=> (lambda (tuple)
(with-syntax ([break (cadr tuple)])
(syntax/loc* loc
(break (current-completion)))))]
[else (raise-syntax-error 'break "invalid label" (region->syntax (Term-location label)))])]
[(struct ReturnStatement (loc value))
(unless (eq? (current-lexical-context) 'function)
(let ([stxloc (build-syntax 'return loc)])
(raise-syntax-error 'return "invalid return" stxloc stxloc)))
(with-syntax ([return (datum->syntax #f 'return)]
[e (if value
(compile-expression value)
#'(void))])
(syntax/loc* loc
(return e)))]
[(struct LetStatement (loc bindings body))
(let ([var-bindings (map (compose Identifier->Variable VariableInitializer-id) bindings)]
[inits (map (compose compile-optional-expression VariableInitializer-init) bindings)])
(with-syntax ([body (with-scope (bind var-bindings (current-scope))
(compile-statement body))])
(with-bindings var-bindings #:inits inits
(syntax/loc (region->syntax loc) body))))]
[(struct WithStatement (loc object body))
(when (hash-ref (current-pragmas) '(lexical scope) (lambda () #f))
(raise-syntax-error 'compile "illegal context (lexically scoped) for `with' statement" (region->syntax loc)))
(let* ([body-stx (with-scope #f
(compile-statement body))]
[object-stx (compile-expression object)]
[extend-scope-chain (lambda (scope-chain-id)
(with-syntax ([scope-chain scope-chain-id]
[object-e object-stx])
#'(cons object-e scope-chain)))])
(dynamic-code body-stx loc extend-scope-chain (Term-location object)))]
[(struct SwitchStatement (loc expr (list (struct CaseClause (_ qs as)) ...)))
(with-syntax ([e (compile-expression expr)]
[(x v break falling-through?) (generate-temporaries '(x v break falling-through?))])
(with-syntax ([(q ...) (map (lambda (q)
(if q
(with-syntax ([test-e (compile-expression q)])
#'(lambda (x)
(eq? (js:=== x test-e) 'true)))
#'(lambda (x) '#t)))
qs)])
(parameterize ([current-labels (cons (list #f #'break) (current-labels))])
(with-syntax ([((a ...) ...) (map (lambda (stmts)
(map compile-statement stmts))
as)])
(syntax/loc* loc
(let ([v e])
(let/ec break
(let ([falling-through? '#f])
(when (or falling-through? (q v))
(set! falling-through? '#t)
a ...)
...
(current-completion)))))))))]
[(struct LabelledStatement (loc label (? loop? loop)))
(let ([label-name (Identifier-name label)])
(with-syntax ([(break continue) (generate-temporaries '(break continue))])
(parameterize ([current-labels (cons (list label-name #'break #'continue)
(current-labels))])
(compile-loop loop #'break #'continue))))]
[(struct LabelledStatement (loc label statement))
(let ([label-name (Identifier-name label)])
(with-syntax ([(break) (generate-temporaries '(break))])
(parameterize ([current-labels (cons (list label-name #'break)
(current-labels))])
(with-syntax ([s (compile-statement statement)])
(syntax/loc* loc
(let/ec break s))))))]
[(struct ThrowStatement (loc value))
(with-syntax ([stxloc (region->syntax loc)]
[e (compile-expression value)])
(syntax/loc* loc
(raise-runtime-exception stxloc e)))]
[(struct TryStatement (loc body catches finally))
(with-syntax ([body-s (compile-statement body)]
[(catch-e ...) (map compile-catch-clause catches)])
(with-syntax ([try-catch #'(with-handlers ([exn:runtime? catch-e]
...)
body-s)])
(if finally
(with-syntax ([finally-s (compile-statement finally)])
(syntax/loc* loc
(begin (dynamic-wind
void
(lambda () try-catch)
(lambda () finally-s))
(current-completion))))
(syntax/loc* loc try-catch))))]
)))
(define (compile-catch-clause clause)
(match clause
[(struct CatchClause (loc exn catch))
(with-syntax ([exn-value (car (generate-temporaries '(exn-value)))])
(let ([var-bindings (list (Identifier->Variable exn))]
[inits (list (syntax/loc* loc
(exn:runtime-value exn-value)))])
(with-syntax ([body (with-bindings var-bindings #:inits inits
(with-scope (bind var-bindings (current-scope))
(compile-statement catch)))])
(syntax/loc (region->syntax loc)
(lambda (exn-value) body)))))]))
(define (compile-loop stmt break-id continue-id)
(match stmt
[(struct DoWhileStatement (loc body test))
(with-syntax ([body-s (compile-statement body)]
[test-e (compile-expression test)]
[break break-id]
[continue continue-id])
(syntax/loc* loc
(let/ec break
(let loop ()
(let/ec continue body-s)
(if (true-value? test-e)
(loop)
(current-completion))))))]
[(struct WhileStatement (loc test body))
(with-syntax ([test-e (compile-expression test)]
[body-s (compile-statement body)]
[break break-id]
[continue continue-id])
(syntax/loc* loc
(let/ec break
(let loop ()
(if (true-value? test-e)
(begin (let/ec continue body-s)
(loop))
(current-completion))))))]
[(struct ForStatement (loc init test incr body))
(with-syntax ([init-e (if init
(compile-expression init)
#'(void))]
[test-e (if test
(compile-expression test)
#'(quote true))]
[incr-e (if incr
(compile-expression incr)
#'(void))]
[body-s (compile-statement body)]
[break break-id]
[continue continue-id]
[(loop) (generate-temporaries '(loop))])
(syntax/loc* loc
(begin
init-e
(let/ec break
(let loop ()
(if (true-value? test-e)
(begin (let/ec continue body-s)
incr-e
(loop))
(current-completion)))))))]
[(struct ForInStatement (loc lhs container body))
(with-syntax ([(object next-key key) (generate-temporaries '(object next-key key))])
(with-syntax ([container-e (compile-expression container)]
[update (compile-assignment lhs #'key)]
[body-s (compile-statement body)]
[break break-id]
[continue continue-id])
(syntax/loc (region->syntax loc)
(let/ec break
(let* ([object container-e]
[next-key (object-keys-stream object)])
(let loop ()
(let ([key (next-key)])
(if key
(begin
update
(let/ec continue body-s)
(loop))
(current-completion)))))))))]
))
(define (field-reference? x)
(or (BracketReference? x)
(DotReference? x)))
(define (compile-optional-expression expr [default #'(void)])
(if expr (compile-expression expr) default))
(define (compile-expression expr)
(match expr
[(struct StringLiteral (loc value))
(with-syntax ([literal (build-syntax value loc)])
(syntax/loc* loc
(quote literal)))]
[(struct NumericLiteral (loc value))
(with-syntax ([literal (build-syntax value loc)])
(syntax/loc* loc
(quote literal)))]
[(struct BooleanLiteral (loc value))
(if value
(syntax/loc* loc 'true)
(syntax/loc* loc 'false))]
[(struct NullLiteral (loc))
(syntax/loc* loc '())]
[(struct RegexpLiteral (loc pattern global? case-insensitive?))
(begin (printf "expression not compiled: ~v~n" expr)
#'"<<regular expression>>")]
[(struct ArrayLiteral (loc elts))
(with-syntax ([(e ...) (for/list ([elt elts])
(compile-optional-expression elt #'#f))])
(syntax/loc* loc
(build-array (evector e ...))))]
[(struct ObjectLiteral (loc properties))
(let ([names (map (lambda (prop)
(let ([name (car prop)])
(cond
[(NumericLiteral? name) (NumericLiteral-value name)]
[(StringLiteral? name) (StringLiteral-value name)]
[(Identifier? name) (Identifier->string-syntax name)])))
properties)]
[values (map cdr properties)])
(with-syntax ([(key ...) names]
[(e ...) (map compile-expression values)])
(syntax/loc* loc
(build-object
(object-table [key e] ...)))))]
[(struct ThisReference (loc))
(syntax/loc (region->syntax loc)
(current-this))]
[(? reference-expression?)
(compile-lookup expr)]
[(struct NewExpression (loc constructor args))
(with-syntax ([stxloc (region->syntax loc)]
[constructor-e (compile-expression constructor)]
[(e ...) (map compile-expression args)]
[(ctor) (generate-temporaries '(ctor))])
(syntax/loc* loc
(let ([ctor constructor-e])
(unless (object? ctor)
(raise-runtime-type-error stxloc "constructor" ctor))
((object-construct ctor) e ...))))]
[(struct PostfixExpression (loc operand op))
(with-syntax ([op-e (if (eq? op '++) #'js:+ #'js:-)]
[operand-e (compile-expression operand)]
[update (compile-expression (make-AssignmentExpression loc
operand
(if (eq? op '++) '+= '-=)
(make-NumericLiteral loc 1)))]
[(v) (generate-temporaries '(v))])
(syntax/loc (region->syntax loc)
(let ([v (value->number operand-e)])
update
v)))]
[(struct PrefixExpression (loc op operand))
(cond
[(memq op '(++ --))
(let ([op (if (eq? op '++) '+= '-=)])
(compile-expression
(make-AssignmentExpression loc operand op (make-NumericLiteral loc 1))))]
[(eq? op 'delete)
(compile-deletion operand)]
[else
(with-syntax ([op-e (operator->syntax op)]
[operand-e (compile-expression operand)])
(syntax/loc* loc
(op-e operand-e)))])]
[(struct InfixExpression (loc left '&& right))
(with-syntax ([left-e (compile-expression left)]
[right-e (compile-expression right)])
(syntax/loc* loc
(if (true-value? left-e) right-e 'false)))]
[(struct InfixExpression (loc left '\|\| right))
(with-syntax ([left-e (compile-expression left)]
[right-e (compile-expression right)]
[(tmp) (generate-temporaries '(tmp))])
(syntax/loc* loc
(let ([tmp left-e])
(if (true-value? tmp) tmp right-e))))]
[(struct InfixExpression (loc left op right))
(with-syntax ([left-e (compile-expression left)]
[op-e (operator->syntax op)]
[right-e (compile-expression right)])
(syntax/loc* loc
(op-e left-e right-e)))]
[(struct ConditionalExpression (loc test consequent alternate))
(with-syntax ([test-e (compile-expression test)]
[consequent-e (compile-expression consequent)]
[alternate-e (compile-expression alternate)])
(syntax/loc* loc
(if test-e consequent-e alternate-e)))]
[(struct AssignmentExpression (loc left '= right))
(compile-assignment left (compile-expression right))]
[(struct AssignmentExpression (loc left op right))
(compile-expression
(make-AssignmentExpression loc
left
'=
(make-InfixExpression (Term-location right)
left
(assignment-operator->infix-operator op)
right)))]
[(struct FunctionExpression/hoisted (loc name args body funs vars imports exports))
(compile-function loc name args body funs vars)]
[(struct LetExpression (loc bindings body))
(let ([var-bindings (map (compose Identifier->Variable VariableInitializer-id) bindings)]
[inits (map (compose compile-expression VariableInitializer-init) bindings)])
(with-syntax ([body (with-scope (bind var-bindings (current-scope))
(compile-expression body))])
(with-bindings var-bindings #:inits inits
(syntax/loc* loc body))))]
[(struct CallExpression (loc (and method (struct BracketReference (_ container key))) args))
(with-syntax ([stxloc (region->syntax loc)]
[container-e (compile-expression container)]
[key-e (compile-expression key)]
[(container-val key-val function-val) (generate-temporaries '(container-val key-val function-val))]
[(arg-e ...) (map compile-expression args)]
[(arg-val ...) (generate-temporaries args)])
(syntax/loc (region->syntax loc)
(let* ([container-val container-e]
[key-val key-e]
[function-val (object-get container-val key-val)]
[arg-val arg-e] ...)
(parameterize ([current-this container-val])
(call function-val
(list arg-val ...)
(lambda (str1 str2)
(raise-runtime-type-error stxloc str1 str2)))))))]
[(struct CallExpression (loc (struct DotReference (_ container id)) args))
(with-syntax ([stxloc (region->syntax loc)]
[container-e (compile-expression container)]
[key-val (Identifier->string-expression id)]
[(container-val function-val) (generate-temporaries '(container-val function-val))]
[(arg-e ...) (map compile-expression args)]
[(arg-val ...) (generate-temporaries args)])
(syntax/loc (region->syntax loc)
(let* ([container-val container-e]
[function-val (object-get container-val key-val)]
[arg-val arg-e] ...)
(parameterize ([current-this container-val])
(call function-val
(list arg-val ...)
(lambda (str1 str2)
(raise-runtime-type-error stxloc str1 str2)))))))]
[(struct CallExpression (loc (? direct-eval? function) args))
(let ([lexically-scoped? (hash-ref (current-pragmas) '(lexical scope) (lambda () #f))]
[stxloc (region->syntax loc)])
(with-syntax ([(arg-e ...) (map compile-expression args)]
[scope-chain scope-chain-id]
[variable-object variable-object-id]
[eval-begin (datum->syntax (current-eval-context) 'eval-begin stxloc)]
[stxloc stxloc]
[(function-val arg-vals arg-val ...) (generate-temporaries (append '(function-val arg-vals) args))])
(with-syntax ([invoke-script (if lexically-scoped?
#'(let ([variable-object (build-object (object-table))])
(function-val (cons variable-object scope-chain) variable-object))
#'(function-val scope-chain variable-object))])
(with-syntax ([direct-eval #'(let ([arg-vals (list arg-e ...)])
(if (null? arg-vals)
(void)
(let ([function-val (with-handlers ([exn? (lambda (exn)
(raise-runtime-exception stxloc (exn-message exn)))])
(eval-syntax #`(eval-begin #,@(parse-program-unit (value->string (car arg-vals))))))])
invoke-script)))])
(if lexically-scoped?
(dynamic-code (syntax/loc* loc direct-eval) loc)
(syntax/loc* loc
(if (original-eval?)
direct-eval
(let ([function-val (object-get global-object '"eval")]
[arg-val arg-e] ...)
(parameterize ([current-this global-object])
(call function-val
(list arg-val ...)
(lambda (str1 str2)
(raise-runtime-type-error stxloc str1 str2))))))))))))]
[(struct CallExpression (loc function args))
(with-syntax ([stxloc (region->syntax loc)]
[function-e (compile-expression function)]
[(arg-e ...) (map compile-expression args)]
[(function-val arg-val ...) (generate-temporaries (cons 'function-val args))])
(syntax/loc (region->syntax loc)
(let ([function-val function-e]
[arg-val arg-e] ...)
(parameterize ([current-this global-object])
(call function-val
(list arg-val ...)
(lambda (str1 str2)
(raise-runtime-type-error stxloc str1 str2)))))))]
[(struct ParenExpression (loc expr))
(compile-expression expr)]
[(struct ListExpression (loc (list)))
#'(void)]
[(struct ListExpression (loc exprs))
(with-syntax ([(e ...) (map compile-expression exprs)])
(syntax/loc* loc
(begin e ...)))]
))