#lang scheme/base
(require (prefix-in set: (planet dherman/set:3:0/seteq))
(planet cobbe/contract-utils:1/contract-utils)
scheme/list
scheme/contract
scheme/match
"ast-core.ss"
"ast-utils.ss"
"../../private/config.ss"
"../compiler/helpers.ss")
(require (for-syntax scheme/base))
(define-struct operator (declaration-parser statement-parser expression-parser))
(define current-expansion-context
(make-parameter 'top))
(define (symbolic-identifier=? id1 id2)
(eq? (syntax->datum id1) (syntax->datum id2)))
(define (split-javadot id)
(map string->symbol (regexp-split #rx"\\." (symbol->string id))))
(define ((keyword=? env) id1 id2)
(let ([s1 (syntax->datum id1)]
[s2 (syntax->datum id2)])
(and (eq? s1 s2)
(eq? (hash-ref env s1 (lambda () #f)) (hash-ref initial-env s2 (lambda () #f))))))
(define (identifiers? stx)
(andmap identifier? (syntax->list stx)))
(define (syntax-list stxs)
(if (syntax? stxs)
(syntax->list stxs)
stxs))
(define (syntax-map f stxes env)
(map (lambda (stx)
(f stx env))
(syntax-list stxes)))
(define-syntax (transformer x)
(syntax-case x ()
[(transformer name type (stx env) clauses ...)
#'(transformer name type #:keywords () (stx env) clauses ...)]
[(transformer name type #:keywords (lits ...) (stx env) clauses ...)
(with-syntax ([operator (string->symbol (format "syntax->~a" (syntax->datum #'type)))])
#'(lambda (stx env)
(syntax-case* stx (lits ...) symbolic-identifier=?
clauses ...
[_ (raise-syntax-error 'operation (format "invalid ~a" name) stx)])))]))
(define (lookup-operator stx env)
(syntax-case* stx (#%keyword) symbolic-identifier=?
[(#%keyword . op)
(let ([name (syntax->datum #'op)])
(cond
[(hash-ref initial-env name (lambda () #f))]
[else (raise-syntax-error '#%keyword "unrecognized keyword" stx)]))]
[_ (and (identifier? stx)
(let ([operator (hash-ref env (syntax->datum stx) (lambda () #f))])
(and (operator? operator) operator)))]))
(define (union-map f stxs)
(set:unions (map f (syntax-list stxs))))
(define (term-vars* terms env)
(union-map (lambda (term)
(term-vars term env))
terms))
(define (syntax-vars* stxs env)
(union-map (lambda (stx)
(syntax-vars stx env))
stxs))
(define (syntax-clause-vars* stxs env)
(union-map (lambda (stx)
(syntax-clause-vars stx env))
stxs))
(define (term-init-vars init env)
(match init
[(struct VariableInitializer (_ (struct Identifier (_ id)) init))
(set:list->set (list id))]))
(define (syntax-init-vars stx env)
(syntax-case stx ()
[id
(identifier? #'id)
(set:list->set (list (syntax->datum #'id)))]
[(id . _)
(identifier? #'id)
(set:list->set (list (syntax->datum #'id)))]
[_ set:empty]))
(define (term-vars term env)
(match term
[(struct FunctionDeclaration (_ (struct Identifier (_ name)) args body))
(set:list->set (list name))]
[(struct VariableDeclaration (_ inits))
(union-map (lambda (init)
(term-init-vars init env))
inits)]
[(struct BlockStatement (_ elts))
(term-vars* elts env)]
[(struct IfStatement (_ _ consequent alternate))
(term-vars* (list consequent alternate) env)]
[(struct DoWhileStatement (loc body test))
(term-vars body env)]
[(struct WhileStatement (loc test body))
(term-vars body env)]
[(struct ForStatement (_ (struct VariableDeclaration (_ inits)) test incr body))
(set:union (union-map (lambda (init)
(term-init-vars init env))
inits)
(term-vars body env))]
[(struct ForStatement (_ _ test incr body))
(term-vars body env)]
[(struct ForInStatement (_ (struct VariableDeclaration (_ (list init))) container body))
(set:union (term-init-vars init env)
(term-vars body env))]
[(struct ForInStatement (_ _ container body))
(term-vars body env)]
[(struct WithStatement (_ context body))
(term-vars body env)]
[(struct SwitchStatement (_ test cases))
(union-map (lambda (clause)
(term-clause-vars clause env))
cases)]
[(struct LabelledStatement (_ (struct Identifier (_ l)) stmt))
(term-vars stmt env)]
[(struct TryStatement (_ body catch finally))
(set:union (if finally (term-vars finally env) set:empty)
(union-map (lambda (clause)
(term-clause-vars clause env))
catch)
(term-vars body env))]
[_ set:empty]))
(define (syntax-vars stx env)
(syntax-case* stx (function var block if do while for in with switch label try finally) (keyword=? env)
[(function id (arg ...) body ...)
(identifier? #'id)
(set:list->set (list (syntax->datum #'id)))]
[(function . _)
set:empty]
[(var decls ...)
(union-map (lambda (decl)
(syntax-init-vars decl env))
#'(decls ...))]
[(block stmts ...)
(syntax-vars* #'(stmts ...) env)]
[(if test consequent alternate)
(syntax-vars* #'(consequent alternate) env)]
[(if test consequent)
(syntax-vars #'consequent env)]
[(do body test)
(syntax-vars #'body env)]
[(while test body)
(syntax-vars #'body env)]
[(for (var x) in container body)
(set:union (syntax-init-vars #'x env)
(syntax-vars #'body env))]
[(for lhs in container body)
(syntax-vars #'body env)]
[(for (var inits ...) test incr body)
(set:union (union-map (lambda (init)
(syntax-init-vars init env))
#'(inits ...))
(syntax-vars #'body env))]
[(for init test incr body)
(syntax-vars #'body env)]
[(with context body)
(syntax-vars #'body env)]
[(switch test cases ...)
(syntax-clause-vars* #'(cases ...) env)]
[(label id stmt)
(syntax-vars #'stmt env)]
[(try body catches ... (finally clause))
(set:union (syntax-vars #'body env)
(syntax-clause-vars* #'(catches ...) env)
(syntax-vars #'clause env))]
[(try body catches ...)
(set:union (syntax-vars #'body env)
(syntax-vars* #'(catches ...) env))]
[_ set:empty]))
(define (syntax-clause-vars stx env)
(syntax-case* stx (case catch) (keyword=? env)
[(case expr stmts ...)
(syntax-vars* #'(stmts ...) env)]
[(catch id body)
(syntax-vars #'body env)]))
(define (term-clause-vars clause env)
(match clause
[(struct CatchClause (_ (struct Identifier (_ id)) body))
(term-vars body env)]
[(struct CaseClause (loc case stmts))
(term-vars* stmts env)]))
(define #%regexp
(transformer 'regexp expression (stx env)
[(_ pattern global? case-insensitive?)
(make-RegexpLiteral stx (syntax->datum #'pattern) (syntax->datum #'global?) (syntax->datum #'case-insensitive?))]))
(define #%array
(transformer 'array expression (stx env)
[(_ elts ...)
(make-ArrayLiteral stx (->ArrayElements #'(elts ...) env))]))
(define #%call
(transformer 'call expression (stx env)
[(method args ...)
(make-CallExpression stx
(->Expression #'method env)
(->Expressions #'(args ...) env))]))
(define #%object
(transformer 'object expression (stx env)
[(object [ids vals] ...)
(make-ObjectLiteral stx (map (lambda (id val)
(cons (->Identifier id)
(->Expression val env)))
(syntax->list #'(ids ...))
(syntax->list #'(vals ...))))]))
(define #%field-ref
(transformer 'field-ref expression (stx env)
[(field-ref container key)
(make-BracketReference stx
(->Expression #'container env)
(->Expression #'key env))]))
(define #%field
(transformer 'field expression (stx env)
[(field container id)
(make-DotReference stx
(->Expression #'container env)
(->Identifier #'id))]))
(define #%new
(transformer 'new expression (stx env)
[(new constructor args ...)
(make-NewExpression stx
(->Expression #'constructor env)
(->Expressions #'(args ...) env))]))
(define #%prefix
(transformer 'prefix expression (stx env)
[(prefix op expr)
(make-PrefixExpression stx (syntax->datum #'op) (->Expression #'expr env))]))
(define (#%infix op)
(transformer op expression (stx env)
[(_ expr1 expr2 exprs ...)
(let loop ([accum (make-InfixExpression stx
(->Expression #'expr1 env)
op
(->Expression #'expr2 env))]
[exprs (syntax->list #'(exprs ...))])
(if (null? exprs)
accum
(loop (make-InfixExpression stx
accum
op
(->Expression (car exprs) env))
(cdr exprs))))]))
(define #%postfix
(transformer 'postfix expression (stx env)
[(postfix expr op)
(make-PostfixExpression stx (->Expression #'expr env) (syntax->datum #'op))]))
(define #%if-expression
(transformer 'if expression (stx env)
[(if test consequent alternate)
(make-ConditionalExpression stx
(->Expression #'test env)
(->Expression #'consequent env)
(->Expression #'alternate env))]))
(define (#%assign op)
(transformer op expression (stx env)
[(_ left right)
(make-AssignmentExpression stx
(->Expression #'left env)
op
(->Expression #'right env))]))
(define #%function-expression
(transformer 'function expression (stx env)
[(function name (args ...) body ...)
(identifier? #'name)
(let ([env* (extend-env (append (map syntax->datum (syntax->list #'(name args ...)))
(set:set->list (syntax-vars* #'(body ...) env)))
env)])
(make-FunctionExpression stx
(->Identifier #'name)
(->Identifiers #'(args ...))
(parameterize ([current-expansion-context 'function])
(->SourceElements #'(body ...) env*))))]
[(function (args ...) body ...)
(let ([env* (extend-env (append (map syntax->datum (syntax->list #'(args ...)))
(set:set->list (syntax-vars* #'(body ...) env)))
env)])
(make-FunctionExpression stx
#f
(->Identifiers #'(args ...))
(parameterize ([current-expansion-context 'function])
(->SourceElements #'(body ...) env*))))]))
(define #%begin
(transformer 'begin expression (stx env)
[(begin exprs ...)
(make-ListExpression stx (->Expressions #'(exprs ...) env))]))
(define #%expression
(transformer '#%expression expression (stx env)
[(#%expression expr)
(->Expression #'expr env)]))
(define #%block
(transformer 'block statement (stx env)
[(block stmts ...)
(make-BlockStatement stx (->SubStatements #'(stmts ...) env))]))
(define #%if-statement
(transformer 'if statement (stx env)
[(if test consequent alternate)
(make-IfStatement stx
(->Expression #'test env)
(->SubStatement #'consequent env)
(->SubStatement #'alternate env))]
[(if test consequent)
(make-IfStatement stx
(->Expression #'test env)
(->SubStatement #'consequent env)
#f)]))
(define #%do
(transformer 'do statement (stx env)
[(do body test)
(make-DoWhileStatement stx
(->SubStatement #'body env)
(->Expression #'test env))]))
(define #%while
(transformer 'while statement (stx env)
[(while test body)
(make-WhileStatement stx
(->Expression #'test env)
(->SubStatement #'body env))]))
(define #%for
(transformer 'for statement #:keywords (var in) (stx env)
[(for (var x) in container body)
(make-ForInStatement stx
(make-VariableDeclaration #'x (list (->VariableInitializer #'x env)))
(->Expression #'container env)
(->SubStatement #'body env))]
[(for lhs in container body)
(make-ForInStatement stx
(->Expression #'lhs env)
(->Expression #'container env)
(->SubStatement #'body env))]
[(for (var inits ...) test incr body)
(make-ForStatement stx
(make-VariableDeclaration stx (->VariableInitializers #'(inits ...) env))
(->Expression #'test env)
(->Expression #'incr env)
(->SubStatement #'body env))]
[(for init test incr body)
(make-ForStatement stx
(->Expression #'init env)
(->Expression #'test env)
(->Expression #'incr env)
(->SubStatement #'body env))]))
(define #%continue
(transformer 'continue statement (stx env)
[(continue l)
(make-ContinueStatement stx (->Identifier #'l))]
[(continue)
(make-ContinueStatement stx #f)]))
(define #%break
(transformer 'break statement (stx env)
[(break l)
(make-BreakStatement stx (->Identifier #'l))]
[(break)
(make-BreakStatement stx #f)]))
(define #%return
(transformer 'return statement (stx env)
[(return expr)
(make-ReturnStatement stx (->Expression #'expr env))]
[(return)
(make-ReturnStatement stx #f)]))
(define #%with
(transformer 'with statement (stx env)
[(with context body)
(make-WithStatement stx (->Expression #'context env) (->SubStatement #'body env))]))
(define #%switch
(transformer 'switch stateent (stx env)
[(switch test cases ...)
(make-SwitchStatement stx
(->Expression #'test env)
(->CaseClauses #'(cases ...) env))]))
(define #%label
(transformer 'label statement (stx env)
[(label l stmt)
(make-LabelledStatement stx
(->Identifier #'l)
(->SubStatement #'stmt env))]))
(define #%throw
(transformer 'throw statement (stx env)
[(throw expr)
(make-ThrowStatement stx (->Expression #'expr env))]))
(define #%try
(transformer 'try statement #:keywords (finally) (stx env)
[(try body catches ... (finally clause))
(make-TryStatement stx
(->BlockStatement #'body)
(->CatchClauses #'(catches ...) env)
(->BlockStatement #'clause env))]
[(try body catches ...)
(make-TryStatement stx
(->BlockStatement #'body)
(->CatchClauses #'(catches ...) env)
#f)]))
(define #%statement
(transformer '#%statement statement (stx env)
[(#%statement stmt)
(->Statement #'stmt env)]))
(define #%var
(transformer 'var declaration (stx env)
[(var decls ...)
(make-VariableDeclaration stx (->VariableInitializers #'(decls ...) env))]))
(define #%function-declaration
(transformer 'function declaration (stx env)
[(function name (args ...) body ...)
(begin
(when (and (eq? (current-expansion-context) 'block)
(not (allow-nested-function-declarations?)))
(raise-syntax-error 'syntax->statement "illegally nested function definition" stx))
(let ([env* (extend-env (append (map syntax->datum (syntax->list #'(name args ...)))
(set:set->list (syntax-vars* #'(body ...) env)))
env)])
(make-FunctionDeclaration stx
(->Identifier #'name)
(->Identifiers #'(args ...))
(parameterize ([current-expansion-context 'function])
(->SourceElements #'(body ...) env*)))))]))
(define (alias bindings new-name old-name)
(cond
[(assq old-name bindings)
=> (lambda (operator)
(cons new-name operator))]
[else #f]))
(define assignment-operator-bindings
(for/list ([op assignment-operators])
(cons op (make-operator #f #f (#%assign op)))))
(define infix-operator-bindings
(for/list ([op infix-operators])
(cons op (make-operator #f #f (#%infix op)))))
(define convenient-alias-bindings
(filter-map
values
(list (alias infix-operator-bindings 'and '&&)
(alias infix-operator-bindings 'or '\|\|)
(alias infix-operator-bindings 'bitwise-ior '\|)
(alias infix-operator-bindings 'bitwise-and '\&)
(alias infix-operator-bindings 'bitwise-xor '^)
(alias infix-operator-bindings 'bitwise-not '~))))
(define expression-bindings
`((array . ,(make-operator #f #f #%array))
(regexp . ,(make-operator #f #f #%regexp))
(object . ,(make-operator #f #f #%object))
(field-ref . ,(make-operator #f #f #%field-ref))
(field . ,(make-operator #f #f #%field))
(new . ,(make-operator #f #f #%new))
(prefix . ,(make-operator #f #f #%prefix))
(postfix . ,(make-operator #f #f #%postfix))
(begin . ,(make-operator #f #f #%begin))
(#%expression . ,(make-operator #f #f #%expression))
))
(define statement-bindings
`((block . ,(make-operator #f #%block #f))
(do . ,(make-operator #f #%do #f))
(while . ,(make-operator #f #%while #f))
(for . ,(make-operator #f #%for #f))
(continue . ,(make-operator #f #%continue #f))
(break . ,(make-operator #f #%break #f))
(return . ,(make-operator #f #%return #f))
(with . ,(make-operator #f #%with #f))
(switch . ,(make-operator #f #%switch #f))
(label . ,(make-operator #f #%label #f))
(throw . ,(make-operator #f #%throw #f))
(try . ,(make-operator #f #%try #f))
(#%statement . ,(make-operator #f #%statement #f))
))
(define declaration-bindings
`((var . ,(make-operator #%var #f #f))))
(define shared-bindings
`((if . ,(make-operator #f #%if-statement #%if-expression))
(function . ,(make-operator #%function-declaration #f #%function-expression))))
(define initial-env
(make-immutable-hash
(append assignment-operator-bindings
infix-operator-bindings
convenient-alias-bindings
expression-bindings
statement-bindings
declaration-bindings
shared-bindings)))
(define (extend-env names env)
(let loop ([names names]
[env env])
(if (null? names) env (loop (cdr names) (hash-set env (car names) #t)))))
(define (->Expressions stxs env)
(syntax-map ->Expression stxs env))
(define (->ArrayElements stxs env)
(syntax-map ->ArrayElement stxs env))
(define (->Identifiers stxs)
(map ->Identifier (syntax-list stxs)))
(define (->VariableInitializers stxs env)
(syntax-map ->VariableInitializer stxs env))
(define (->SourceElements stxs env)
(syntax-map ->SourceElement stxs env))
(define (->SubStatements stxs env)
(syntax-map ->SubStatement stxs env))
(define (->CaseClauses stxs env)
(syntax-map ->CaseClause stxs env))
(define (->CatchClauses stxs env)
(syntax-map ->CatchClause stxs env))
(define (symbol->Identifier sym stx)
(check-valid-identifier! 'syntax->expression sym stx)
(make-Identifier stx sym))
(define (symbol->Expression sym stx env)
(case (hash-ref env sym (lambda () #t))
[(this) (make-ThisReference stx)]
[(null) (make-NullLiteral stx)]
[else (make-VarReference stx (symbol->Identifier sym stx))]))
(define (->Identifier stx)
(symbol->Identifier (syntax->datum stx) stx))
(define (->Expression stx env)
(syntax-case* stx (null this) symbolic-identifier=?
[(op . _)
(lookup-operator #'op env)
(let ([operator (lookup-operator #'op env)])
(cond
[(operator-expression-parser operator)
=> (lambda (parse)
(parameterize ([current-expansion-context 'expression])
(parse stx env)))]
[else (raise-syntax-error 'syntax->expression "not an expression form" #'op stx)]))]
[(op . _)
(#%call stx env)]
[null (make-NullLiteral stx)]
[this (make-ThisReference stx)]
[x
(identifier? #'x)
(let ([chain (reverse (split-javadot (syntax->datum #'x)))])
(let f ([id (car chain)] [prefix (cdr chain)])
(if (null? prefix)
(symbol->Expression id stx env)
(let ([container (f (car prefix) (cdr prefix))])
(make-DotReference stx container (make-Identifier stx id))))))]
[datum
(let ([datum (syntax->datum #'datum)])
(cond
[(string? datum) (make-StringLiteral stx datum)]
[(number? datum) (make-NumericLiteral stx datum)]
[(boolean? datum) (make-BooleanLiteral stx datum)]
[else (raise-syntax-error 'syntax->expression "unrecognized literal" stx)]))]))
(define (->ArrayElement stx env)
(syntax-case stx ()
[() #f]
[_ (->Expression stx env)]))
(define (->Statement stx env)
(syntax-case stx ()
[()
(make-EmptyStatement stx)]
[(op . _)
(lookup-operator #'op env)
(let ([operator (lookup-operator #'op env)])
(cond
[(operator-statement-parser operator)
=> (lambda (parser)
(parameterize ([current-expansion-context 'statement])
(parser stx env)))]
[else (make-ExpressionStatement stx (->Expression stx env))]))]
[_ (make-ExpressionStatement stx (->Expression stx env))]))
(define (->SourceElement stx env)
(syntax-case stx ()
[(op . _)
(lookup-operator #'op env)
(let ([operator (lookup-operator #'op env)])
(cond
[(operator-declaration-parser operator)
=> (lambda (parser)
(parser stx env))]
[else (->Statement stx env)]))]
[_ (->Statement stx env)]))
(define (->SubStatement stx env)
(parameterize ([current-expansion-context 'block])
(->SourceElement stx env)))
(define (->VariableInitializer stx env)
(syntax-case stx ()
[id
(identifier? #'id)
(let ([id (syntax->datum #'id)])
(check-valid-identifier! 'syntax->variable-initializer id stx)
(make-VariableInitializer stx (->Identifier stx) #f))]
[(id expr)
(identifier? #'id)
(make-VariableInitializer stx (->Identifier #'id) (->Expression #'expr env))]
[_ (raise-syntax-error 'syntax->variable-initializer "invalid variable initializer" stx)]))
(define (->CaseClause stx env)
(syntax-case* stx (default case) symbolic-identifier=?
[(default stmts ...)
(make-CaseClause stx #f (->SubStatements #'(stmts ...) env))]
[(case expr stmts ...)
(make-CaseClause stx (->Expression #'expr env) (->SubStatements #'(stmts ...) env))]
[_ (raise-syntax-error 'syntax->case-clause "invalid case clause" stx)]))
(define (->CatchClause stx env)
(syntax-case* stx (catch) symbolic-identifier=?
[(catch id body)
(let ([env* (extend-env (list (syntax->datum #'id)) env)])
(make-CatchClause stx (->Identifier #'id) (->BlockStatement #'body env*)))]
[_ (raise-syntax-error 'syntax->catch-clause "invalid catch clause" stx)]))
(define (->BlockStatement stx env)
(let ([stmt (->Statement stx env)])
(if (BlockStatement? stmt)
stmt
(make-BlockStatement (Term-location stmt) (list stmt)))))
(define (syntax->expression stx)
(->Expression stx initial-env))
(define (sexp->expression sexp)
(syntax->expression (datum->syntax #f sexp)))
(define (syntax->statement stx)
(let ([env (extend-env (set:set->list (syntax-vars stx initial-env)) initial-env)])
(->Statement stx env)))
(define (sexp->statement sexp)
(syntax->statement (datum->syntax #f sexp)))
(define (syntax->source-element stx)
(let ([env (extend-env (set:set->list (syntax-vars stx initial-env)) initial-env)])
(->SourceElement stx env)))
(define (sexp->source-element sexp)
(syntax->source-element (datum->syntax #f sexp)))
(define (syntax->program-unit stx)
(let ([env (extend-env (set:set->list (syntax-vars* stx initial-env)) initial-env)])
(syntax-case stx ()
[(stmts ...)
(->SourceElements #'(stmts ...) env)])))
(define (sexp->program-unit sexp)
(syntax->program-unit (datum->syntax #f sexp)))
(define (keyword env name [default (string->symbol (format "#%~a" name))])
(if (eq? (hash-ref env name (lambda () #f)) #t) default name))
(define (ArrayElement-> elt env)
(if elt (Expression-> elt env) #'()))
(define (ArrayElements-> elts env)
(for/list ([elt elts])
(ArrayElement-> elt env)))
(define (Property-> elt)
(match elt
[(struct Identifier (_ name)) name]
[(struct StringLiteral (_ value)) value]
[(struct NumericLiteral (_ value)) value]))
(define (Properties-> props)
(for/list ([prop props])
(Property-> prop)))
(define (Expressions-> exprs env)
(for/list ([expr exprs])
(Expression-> expr env)))
(define (Identifiers-> ids env)
(map Identifier-name ids))
(define (Expression-> expr env)
(match expr
[(struct StringLiteral (loc str))
(datum->syntax #f str (location->syntax loc))]
[(struct RegexpLiteral (loc pattern global? case-insensitive?))
(with-syntax ([regexp (keyword env 'regexp)])
(quasisyntax/loc (location->syntax loc)
(regexp #,pattern #,global? #,case-insensitive?)))]
[(struct NumericLiteral (loc n))
(datum->syntax #f n (location->syntax loc))]
[(struct BooleanLiteral (loc b))
(datum->syntax #f b (location->syntax loc))]
[(struct NullLiteral (loc))
(datum->syntax #f 'null (location->syntax loc))]
[(struct ArrayLiteral (loc elts))
(with-syntax ([array (keyword env 'array)])
(quasisyntax/loc (location->syntax loc)
(array #,@(ArrayElements-> elts env))))]
[(struct ObjectLiteral (loc (list (cons props values) ...)))
(with-syntax ([object (keyword env 'object)]
[(prop ...) (Properties-> props)]
[(value ...) (Expressions-> values env)])
(quasisyntax/loc (location->syntax loc)
(object [prop value] ...)))]
[(struct ThisReference (loc))
(datum->syntax #f 'this (location->syntax loc))]
[(struct VarReference (loc (struct Identifier (_ id))))
(datum->syntax #f id (location->syntax loc))]
[(struct BracketReference (loc container key))
(with-syntax ([field-ref 'field-ref])
(quasisyntax/loc (location->syntax loc)
(field-ref #,(Expression-> container env)
#,(Expression-> key env))))]
[(struct DotReference (loc container (struct Identifier (_ id))))
(with-syntax ([field (keyword env 'field)])
(quasisyntax/loc (location->syntax loc)
(field #,(Expression-> container env) #,id)))]
[(struct NewExpression (loc constructor args))
(with-syntax ([new 'new]
[constructor (Expression-> constructor env)]
[(arg ...) (Expressions-> args env)])
(quasisyntax/loc (location->syntax loc)
(new constructor arg ...)))]
[(struct PrefixExpression (loc op expr))
(with-syntax ([prefix (keyword env 'prefix)])
(quasisyntax/loc (location->syntax loc)
(prefix #,op #,(Expression-> expr env))))]
[(struct PostfixExpression (loc expr op))
(with-syntax ([prefix (keyword env 'postfix)])
(quasisyntax/loc (location->syntax loc)
(postfix #,(Expression-> expr env) #,op)))]
[(struct InfixExpression (loc left op right))
(with-syntax ([op op])
(quasisyntax/loc (location->syntax loc)
(op #,(Expression-> left env) #,(Expression-> right env))))]
[(struct ConditionalExpression (loc test consequent alternate))
(with-syntax ([if 'if])
(quasisyntax/loc (location->syntax loc)
(if #,(Expression-> test env)
#,(Expression-> consequent env)
#,(Expression-> alternate env))))]
[(struct AssignmentExpression (loc lhs op rhs))
(with-syntax ([op op])
(quasisyntax/loc (location->syntax loc)
(op #,(Expression-> lhs env) #,(Expression-> rhs env))))]
[(struct FunctionExpression (loc #f (list args ...) body))
(let ([env* (extend-env args env)])
(with-syntax ([function 'function]
[(arg ...) (Identifiers-> args env)]
[(body ...) (SourceElements-> body env*)])
(quasisyntax/loc (location->syntax loc)
(function (arg ...) body ...))))]
[(struct FunctionExpression (loc (struct Identifier (_ name)) (list args ...) body))
(let ([env* (extend-env (cons name args) env)])
(with-syntax ([function 'function]
[name name]
[(arg ...) (Identifiers-> args env)]
[(body ...) (SourceElements-> body env*)])
(quasisyntax/loc (location->syntax loc)
(function name (arg ...) body ...))))]
[(struct ListExpression (loc exprs))
(with-syntax ([begin (keyword env 'begin)]
[(expr ...) (Expressions-> exprs env)])
(quasisyntax/loc (location->syntax loc)
(begin expr ...)))]
[(struct CallExpression (loc method args))
(with-syntax ([method (Expression-> method env)]
[(arg ...) (Expressions-> args env)])
(quasisyntax/loc (location->syntax loc)
(method arg ...)))]
[(struct ParenExpression (loc expr))
(Expression-> expr env)]
))
(define (SourceElements-> elts env)
(for/list ([elt elts])
(SourceElement-> elt env)))
(define (SourceElement-> elt env)
(match elt
[(? FunctionDeclaration?)
(FunctionDeclaration-> elt env)]
[(struct VariableDeclaration (loc inits))
(with-syntax ([var 'var]
[(init ...) (VariableInitializers-> inits env)])
(quasisyntax/loc (location->syntax loc)
(var init ...)))]
[_ (Statement-> elt env)]))
(define (VariableInitializers-> inits env)
(for/list ([init inits])
(VariableInitializer-> init env)))
(define (VariableInitializer-> init env)
(match init
[(struct VariableInitializer (loc (struct Identifier (_ id)) #f))
(datum->syntax #f id (location->syntax loc))]
[(struct VariableInitializer (loc1 (struct Identifier (loc2 id)) init))
(with-syntax ([id (datum->syntax #f id (location->syntax loc2))])
(quasisyntax/loc (location->syntax loc1)
[id #,(Expression-> init env)]))]))
(define (FunctionDeclaration-> decl env)
(match decl
[(struct FunctionDeclaration (loc (struct Identifier (_ name)) (list args ...) body))
(let ([env* (extend-env (cons name args) env)])
(with-syntax ([function 'function]
[name name]
[(arg ...) (Identifiers-> args env)]
[(body ...) (SourceElements-> body env*)])
(quasisyntax/loc (location->syntax loc)
(function name (arg ...) body ...))))]))
(define (Statement-> stmt env)
(match stmt
[(struct BlockStatement (loc elts))
(with-syntax ([block (keyword env 'block)]
[(elt ...) (SubStatements-> elts env)])
(syntax/loc (location->syntax loc)
(block elt ...)))]
[(struct EmptyStatement (loc))
(syntax/loc (location->syntax loc)
())]
[(struct ExpressionStatement (loc expr))
(with-syntax ([e (Expression-> expr env)])
(if (or (FunctionExpression? expr) (ConditionalExpression? expr))
(with-syntax ([#%expression '#%expression])
(syntax/loc loc
(#%expression e)))
#'e))]
[(struct IfStatement (loc test consequent alternate))
(if alternate
(with-syntax ([if 'if])
(quasisyntax/loc (location->syntax loc)
(if #,(Expression-> test env)
#,(SubStatement-> consequent env)
#,(SubStatement-> alternate env))))
(with-syntax ([if 'if])
(quasisyntax/loc (location->syntax loc)
(if #,(Expression-> test env)
#,(SubStatement-> consequent env)))))]
[(struct DoWhileStatement (loc body test))
(with-syntax ([do 'do])
(quasisyntax/loc (location->syntax loc)
(do #,(SubStatement-> body env) #,(Expression-> test env))))]
[(struct WhileStatement (loc test body))
(with-syntax ([while 'while])
(quasisyntax/loc (location->syntax loc)
(while #,(Expression-> test env) #,(SubStatement-> body env))))]
[(struct ForStatement (loc init test incr body))
(with-syntax ([for 'for])
(quasisyntax/loc (location->syntax loc)
(for #,(cond
[(not init) #f]
[(VariableDeclaration? init) (SourceElement-> init env)]
[else (Expression-> init env)])
#,(if test (Expression-> test env) #t)
#,(if incr (Expression-> incr env) #f)
#,(SubStatement-> body env))))]
[(struct ForInStatement (loc
(struct VariableDeclaration (_ (list (struct VariableInitializer (_ (struct Identifier (_ x)) #f)))))
container
body))
(with-syntax ([for 'for]
[in 'in]
[var (keyword env 'var '#%var)])
(quasisyntax/loc (location->syntax loc)
(for (var #,x) in #,(Expression-> container env)
#,(SubStatement-> body env))))]
[(struct ForInStatement (loc (? Expression? var) container body))
(with-syntax ([for 'for]
[in 'in])
(quasisyntax/loc (location->syntax loc)
(for #,(Expression-> var env) in #,(Expression-> container env)
#,(SubStatement-> body env))))]
[(struct ContinueStatement (loc #f))
(with-syntax ([continue 'continue])
(quasisyntax/loc (location->syntax loc)
(continue)))]
[(struct ContinueStatement (loc (struct Identifier (_ id))))
(with-syntax ([continue 'continue])
(quasisyntax/loc (location->syntax loc)
(continue #,id)))]
[(struct BreakStatement (loc #f))
(with-syntax ([break 'break])
(quasisyntax/loc (location->syntax loc)
(break)))]
[(struct BreakStatement (loc (struct Identifier (_ id))))
(with-syntax ([break 'break])
(quasisyntax/loc (location->syntax loc)
(break #,id)))]
[(struct ReturnStatement (loc value))
(with-syntax ([return 'return])
(if value
(quasisyntax/loc (location->syntax loc)
(return #,(Expression-> value env)))
(syntax/loc (location->syntax loc)
(return))))]
[(struct WithStatement (loc context body))
(with-syntax ([with 'with])
(quasisyntax/loc (location->syntax loc)
(with #,(Expression-> context env)
#,(SubStatement-> body env))))]
[(struct SwitchStatement (loc test cases))
(with-syntax ([switch 'switch]
[test (Expression-> test env)]
[(clause ...) (CaseClauses-> cases env)])
(quasisyntax/loc (location->syntax loc)
(switch test clause ...)))]
[(struct LabelledStatement (loc (struct Identifier (_ l)) stmt))
(with-syntax ([label (keyword env 'label)])
(quasisyntax/loc (location->syntax loc)
(label #,l #,(SubStatement-> stmt env))))]
[(struct ThrowStatement (loc value))
(with-syntax ([throw 'throw])
(quasisyntax/loc (location->syntax loc)
(throw #,(Expression-> value env))))]
[(struct TryStatement (loc body catch finally))
(with-syntax ([try 'try]
[body (Statement-> body env)]
[(catch ...) (CatchClauses-> catch env)]
[(finally ...) (if finally (list (cons 'finally (Statement-> finally env))) null)])
(quasisyntax/loc (location->syntax loc)
(try body catch ... finally ...)))]))
(define (SubStatement-> elt env)
(SourceElement-> elt env))
(define (SubStatements-> elts env)
(for/list ([elt elts])
(SubStatement-> elt env)))
(define (CaseClauses-> clauses env)
(for/list ([clause clauses])
(CaseClause-> clause env)))
(define (CaseClause-> clause env)
(match clause
[(struct CaseClause (loc #f stmts))
(with-syntax ([default 'default]
[(stmt ...) (SubStatements-> stmts env)])
(quasisyntax/loc (location->syntax loc)
(default stmt ...)))]
[(struct CaseClause (loc expr stmts))
(with-syntax ([case 'case]
[expr (Expression-> expr env)]
[(stmt ...) (SubStatements-> stmts env)])
(quasisyntax/loc (location->syntax loc)
(case expr stmt ...)))]))
(define (CatchClauses-> clauses env)
(for/list ([clause clauses])
(CatchClause-> clause env)))
(define (CatchClause-> clause env)
(match clause
[(struct CatchClause (loc (struct Identifier (_ id)) body))
(with-syntax ([catch 'catch])
(quasisyntax/loc (location->syntax loc)
(catch #,id #,(Statement-> body (extend-env (list id) env)))))]))
(define (expression->syntax expr)
(Expression-> expr initial-env))
(define (expression->sexp expr)
(syntax->datum (expression->syntax expr)))
(define (statement->syntax stmt)
(let ([env (extend-env (set:set->list (term-vars stmt initial-env)) initial-env)])
(Statement-> stmt env)))
(define (statement->sexp stmt)
(syntax->datum (statement->syntax stmt)))
(define (source-element->syntax elt)
(let ([env (extend-env (set:set->list (term-vars elt initial-env)) initial-env)])
(SourceElement-> elt env)))
(define (source-element->sexp elt)
(syntax->datum (source-element->syntax elt)))
(define (program-unit->syntax pgm)
(let ([env (extend-env (set:set->list (term-vars* pgm initial-env)) initial-env)])
(SourceElements-> pgm env)))
(define (program-unit->sexp pgm)
(syntax->datum (program-unit->syntax pgm)))
(provide syntax->expression syntax->statement syntax->source-element syntax->program-unit)
(provide expression->syntax statement->syntax source-element->syntax program-unit->syntax)
(provide sexp->expression sexp->statement sexp->source-element sexp->program-unit)
(provide expression->sexp statement->sexp source-element->sexp program-unit->sexp)
(require scheme/port)
(define (mk sexp)
(with-input-from-string (with-output-to-string (lambda () (write sexp)))
read-syntax))