;;; syntax.ss ;;; Copyright 1984-2017 Cisco Systems, Inc. ;;; ;;; Licensed under the Apache License, Version 2.0 (the "License"); ;;; you may not use this file except in compliance with the License. ;;; You may obtain a copy of the License at ;;; ;;; http://www.apache.org/licenses/LICENSE-2.0 ;;; ;;; Unless required by applicable law or agreed to in writing, software ;;; distributed under the License is distributed on an "AS IS" BASIS, ;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ;;; See the License for the specific language governing permissions and ;;; limitations under the License. ;;; Authors: R. Kent Dybvig, Oscar Waddell, Bob Hieb, Carl Bruggeman, Andy Keep ;;; Portions of this code have been made available as psyntax.ss, with ;;; the following notice. ;;; Portable implementation of syntax-case ;;; Extracted from Chez Scheme Version 7.3 (Feb 26, 2007) ;;; Authors: R. Kent Dybvig, Oscar Waddell, Bob Hieb, Carl Bruggeman ;;; Copyright (c) 1992-2002 Cadence Research Systems ;;; Permission to copy this software, in whole or in part, to use this ;;; software for any lawful purpose, and to redistribute this software ;;; is granted subject to the restriction that all copies made of this ;;; software must include this copyright notice in full. This software ;;; is provided AS IS, with NO WARRANTY, EITHER EXPRESS OR IMPLIED, ;;; INCLUDING BUT NOT LIMITED TO IMPLIED WARRANTIES OF MERCHANTABILITY ;;; OR FITNESS FOR ANY PARTICULAR PURPOSE. IN NO EVENT SHALL THE ;;; AUTHORS BE LIABLE FOR CONSEQUENTIAL OR INCIDENTAL DAMAGES OF ANY ;;; NATURE WHATSOEVER. ;;; The library support code borrows ideas from the portable R6RS libraries ;;; and syntax-case system written by Abdulaziz Ghuloum and R. Kent Dybvig. ; TODO: ; * fix to provide source information: ; Error: missing definition for export(s) (y). ; * work on marked identifiers: ; This works: ; > (define-syntax a ; (syntax-rules () ; ((_ x c) (begin (define y c) (define x (lambda () y)))))) ; > (a one 1) ; > (a two 2) ; > (one) ; 1 ; > (two) ; 2 ; But this does not: ; > (define-syntax a ; (syntax-rules () ; ((_ x c) (begin (define x (lambda () y)) (define y c))))) ; > (a one 1) ; > (one) ; Error: variable y is not bound. ; The problem is that we are not establishing the substitution from ; y to its generated name until after we've expanded (lambda () y). ; * give ourselves better syntax-error formatting tools ; * unload module before loading it in sc-put-cte ; * consider allowing exports to be defined externally ; - e.g., (let ((x 3)) (module foo (x)) (let ((x 4)) (import foo) x)) ; - implement simply by discarding check-module-exports ; * consider adding three-argument free-id=?: ; - (free-id=? x y) => x & y free-id= in all environments ; - (free-id=? x y r) => x & y free-id= in environment r ; * uncomment transformer support? rename to sc-transformer ; - fact that we currently strip lexicals from the transformer-env prevents ; us from doing (let ((x 3)) (define-syntax y (transformer x)) y) ; - compiler currently expects us to generate code with only valid lexicals ; * module init expressions ; - allow inits to be interspersed with defns? ; - return value of last init or void if none? (void if last form is defn?) ; - should (let () (module () 3)) be okay? ; ? consider making symbol changes that will allow top-level references to ; track top-level imports ; ? lazy prefix import ; ? figure out some way to collect superceded modules ; ; ? try implementing "stub" interface idea ; - likely doomed because we can export two identifiers with same symname but different binding names ; symnames not necessarily different at top-level if inserted into macro output... ; - anonymous modules could probably be handled by matching their exports (if we can solve above problem) ; ? try to use vectors to contain module variable locations ; ? parse-define et al. should wrap the id for us since we seem to do that anyway ; ? lobotomize residual syntax objects ; ? chi-internal and chi-external both maintain a separate expression store ; er for each form in the body, allegedly to allow "let-syntax or ; letrec-syntax forms local to a portion or all of the body to shadow the ; definition bindings". In fact, since visibility is controlled solely ; by the wrap, the separate environments determine only how long the local ; syntax bindings are visible. In particular, they rule out things like ; the following: ; ; (let () ; (let-syntax ((a (identifier-syntax 3))) ; (define-syntax b (identifier-syntax a))) ; b) ; ; maintaining the separate stores just to rule this sort of thing out may ; not be worth the code complexity or small efficiency hit. consider ; flushing ; ; DONE 8/3/2004 (version 6.9c) ; ? avoid mark wraps in favor of marks (ie. cut fluff of building xtra pairs) ; ? have syntax-error generate warning and continue ; ? update the algebra sometime. ;;; Implementation notes: ;;; "begin" is treated as a splicing construct at top level and at ;;; the beginning of bodies. Any sequence of expressions that would ;;; be allowed where the "begin" occurs is allowed. ;;; "let-syntax" and "letrec-syntax" are also treated as splicing ;;; constructs, in violation of the R5RS. A consequence is that let-syntax ;;; and letrec-syntax do not create local contours, as do let and letrec. ;;; Although the functionality is greater as it is presently implemented, ;;; we will probably change it to conform to the R5RS. modules provide ;;; similar functionality to nonsplicing letrec-syntax when the latter is ;;; used as a definition. ;;; Objects with no standard print syntax, including objects containing ;;; cycles and syntax objects, are allowed in quoted data as long as they ;;; are contained within a syntax form or produced by datum->syntax. ;;; Such objects are never copied. ;;; When the expander encounters a reference to an identifier that has ;;; no global or lexical binding, it treats it as a global-variable ;;; reference. This allows one to write mutually recursive top-level ;;; definitions, e.g.: ;;; ;;; (define f (lambda (x) (g x))) ;;; (define g (lambda (x) (f x))) ;;; ;;; but may not always yield the intended when the variable in question ;;; is later defined as a keyword. ;;; Top-level variable definitions of syntax keywords are permitted. ;;; In order to make this work, top-level define not only produces a ;;; top-level definition in the core language, but also modifies the ;;; compile-time environment (using $sc-put-cte) to record the fact ;;; that the identifier is a variable. ;;; Top-level definitions of macro-introduced identifiers are visible ;;; only in code produced by the macro. That is, a binding for a ;;; hidden (generated) identifier is created instead, and subsequent ;;; references within the macro output are renamed accordingly. For ;;; example: ;;; ;;; (define-syntax a ;;; (syntax-rules () ;;; ((_ var exp) ;;; (begin ;;; (define secret exp) ;;; (define var ;;; (lambda () ;;; (set! secret (+ secret 17)) ;;; secret)))))) ;;; (a x 0) ;;; (x) => 17 ;;; (x) => 34 ;;; secret => Error: variable secret is not bound ;;; ;;; The definition above would fail if the definition for secret ;;; were placed after the definition for var, since the expander would ;;; encounter the references to secret before the definition that ;;; establishes the compile-time map from the identifier secret to ;;; the generated identifier. ;;; Identifiers and syntax objects are implemented as vectors for ;;; portability. As a result, it is possible to "forge" syntax ;;; objects. ;;; The input to sc-expand may contain "annotations" describing, e.g., the ;;; source file and character position from where each object was read if ;;; it was read from a file. These annotations are handled properly by ;;; sc-expand only if the annotation? hook (see hooks below) is implemented ;;; properly and the operators annotation-expression and annotation-stripped ;;; are supplied. If annotations are supplied, the proper annotated ;;; expression is passed to the various output constructors, allowing ;;; implementations to accurately correlate source and expanded code. ;;; Contact one of the authors for details if you wish to make use of ;;; this feature. ;;; Implementation of modules: ;;; ;;; The implementation of modules requires that indirect top-level exports ;;; be listed with the exported macro at some level where both are visible, ;;; e.g., ;;; ;;; (module M (alpha (beta b)) ;;; (module ((alpha a) b) ;;; (define-syntax alpha (identifier-syntax a)) ;;; (define a 'a) ;;; (define b 'b)) ;;; (define-syntax beta (identifier-syntax b))) ;;; ;;; Listing of indirect imports is not needed for macros that do not make ;;; it out to top level, including all macros that are local to a "body". ;;; (They may be listed in this case, however.) We need this information ;;; for top-level modules since a top-level module expands into a letrec ;;; for non-top-level variables and top-level definitions (assignments) for ;;; top-level variables. Because of the general nature of macro ;;; transformers, we cannot determine the set of indirect exports from the ;;; transformer code, so without the user's help, we'd have to put all ;;; variables at top level. ;;; ;;; Each such top-level identifier is given a generated name (gensym). ;;; When a top-level module is imported at top level, a compile-time ;;; alias is established from the top-level name to the generated name. ;;; The expander follows these aliases transparently. When any module is ;;; imported anywhere other than at top level, the label of the ;;; import identifier is set to the label of the export identifier. ;;; ;;; All identifiers defined within a local module are folded into the ;;; letrec created for the enclosing body. Visibility is controlled in ;;; this case and for nested top-level modules by introducing a new wrap ;;; for each module. ;;; top-level semantics (6/30/2008) ;;; - all aux keywords are defined in scheme environment ;;; - most signal syntax error when used out of context ;;; - exceptions: eval-when aux keywords (eval, load, compile, visit, revisit) ;;; - interaction-environment semantics: ;;; - initially same set of bindings and locations as Scheme environment ;;; - behaves as if session began with an implicit (import scheme) ;;; - variables implicitly imported thusly are immutable ;;; - definitions of other identifiers as variables implied ;;; - programmers are advised, however, to define all variables before ;;; entering code containing assignments or references to them, ;;; using (define id) if necessary for forward references, so that ;;; subsequent additions to the scheme environment do not invalidate ;;; existing programs ;;; - top-level definition of unmarked symbol in a given environment maps ;;; the name to a label that is uniquely determined for the environment ;;; and name. that is, multiple top-level definitions of the same name ;;; always resolve to the same label. So, for example, ;;; (define cons 3) (define (f) cons) (import scheme) ;;; (define cons 4) (f) ;=> 4 ;;; and ;;; (define cons 3) (define (g) #'cons) (import scheme) ;;; (define cons 4) (free-identifier=? (g) #'cons) ;=> #t ;;; Also, for example, if t1.ss contains (define x 3) and t2.ss contains ;;; (define x 4), x will refer to the same location in the two files. ;;; - free-identifier=? equates identifiers that are truly free-identifier=? ;;; - redefinition of aux keyword thus ruins its use as aux keyword ;;; So, for example: ;;; (define else) (case [else 3]) ;=> syntax error ;;; and ;;; (define else #f) (cond [else 3]) ;=> unspecified ;;; - literal-identifier=? is the same as free-identifier=? ;;; - eval-when checks aux keywords with free-id=?, then symbolic-id=? ;;; - okay because its aux keywords don't appear in expression context, ;;; i.e., they appear only where keywords can appear ;;; - allows user to redefine or trace, e.g., load without breaking eval-when ;;; - only form that uses names of predefined variables as aux keywords ;;; - (define / -) gives no warning, as it previously did when optimize-level ;;; is 2 or more, and does not set *flags* and *real-primref-name* on ;;; #{/ *top*:/}. ;;; - for copy-environment, operations on the environment and its copy ;;; should behave similarly. if an assignment in one affects the export ;;; of an imported module's bindings, an assignment in the other should ;;; as well. similarly, if an assignment in one affects its own default ;;; location, an assignment in the other should affect its own default ;;; location. for example, suppose in environment A, cons is the ;;; (immutable) cons from the scheme library, foo is the (mutable) foo ;;; from the bar module, and joe is a variable whose location is ;;; specific to A. then in the copy B of A, cons should be the ;;; (immutable) cons from the scheme library, foo should be the ;;; (mutable) foo from the bard module, and joe should be a variable ;;; whose location is specific to B. then the corresponding operation ;;; (definition, assignment, reference) on each has the same affect as ;;; on the other. in particular, assigning cons is an error in both, ;;; assigning foo changes module bar's foo, and assigning joe assigns ;;; the environment's local joe. defining joe in either amounts to the ;;; same as an assignment to joe. ;;; ;;; - implementation ;;; - symbols no longer have system-value slot ;;; - top-level labels are (once again) always symbols ;;; - sym.*system* = sym implied for all sym ;;; - sym.token = #{sym token:sym} implied otherwise ;;; - id.token = gensym if id is marked (as always) ;;; - sym.*top* = sym explicitly set for sym in scheme environment ;;; - sym.*cte* = (global . sym) implied for all sym ;;; - sym.*read-only* set for *system* variables ;;; - label.*read-only* set for library variables ;;; - top-level definitions: ;;; - import sets id.token to module's internal label ;;; - if id=sym is unmarked, define, define-syntax, module, alias, and ;;; dtlv set sym.token = #{sym token:sym} (mutable envs only) ;;; - if id is marked and id.token resolves to mutable variable, define ;;; residualizes an assignment to the variable's location ;;; Q: does define also residualize visit-time code to set id.token ;;; to the same label and label.*cte* to the same binding? ;;; - any other definition of a marked id sets id.token = gensym ;;; - definitions also set label.*cte* appropriately, and define and ;;; dtlv also set label.value appropriately ;;; - ref, set!, tlb?, tlv, and stlv! set sym.token = #{sym token:sym} for ;;; mutable envs if sym.token is not yet set. ref, set!, tlv, and stlv! ;;; complain if sym.token does not resolve to a variable. set! and stlv! ;;; complain if sym.token resolves to an immutable variable. ;;; - copy-environment creates new top-level labels/locations for and only ;;; for variables whose locations are the default ones for the old ;;; environment. All other mappings from symbol to label should be ;;; transferred from the old to the new environment. ;;; Bootstrapping: ;;; When changing syntax-object representations, it is necessary to support ;;; both old and new syntax-object representations in id->label. It ;;; should be sufficient to redefine syntax-object-expression to work for ;;; both old and new representations and syntax-object-wrap to return the ;;; empty-wrap for old representations. ;;; See "module oldrep" below. ;;; ../bin/scheme cmacros.so ;;; > (subset-mode 'system) ;;; > (current-expand (lambda args (apply sc-expand args))) ;;; > (optimize-level 2) ;;; > (load "syntax.ss") ;;; > (compile-file "syntax.ss" "syntax.patch") ;;; WARNING: ;;; the algebra below makes it appear that substitutions could come ;;; between a shift-mark and the anti-mark ;;; join-wraps(({m_0 ... m_n-1 m_n} . S1), (m_n:Marks . shift:Subs)) ;;; cannot get: ;;; join-wraps(({m_0 ... m_n-1 m_n} . S1), (m_n:Marks . Subs:shift:Subs)) ;;; The expander now uses the identifier's symbolic name when constructing ;;; a substitution rib for that id. We believe there's no reason for the ;;; lazy expander to do all the substitutions that the eager expander would ;;; do. When determining an identifier's binding name, we can stop at the ;;; first substitution we find. This suggests a new representation for wraps: ;;; ;;; wrap ::== ((mark ...) . (subst ...)) ;;; subst ::== #(symbolic-name label (mark ...)) | shift ;;; ;;; top-wrap = ((top)) ;;; ;;; (add-subst sname bname (Marks . Substs)) ;;; = (Marks . (#(sname bname Marks) . Substs)) ;;; ;;; (add-mark m_1 W) ;;; = (join-wraps (new-mark-wrap m_1) W) ;;; ;;; (new-mark-wrap m_1) ;;; = ((m_1) . (shift)) ;;; ;;; (join-wraps ((m_0 ... m_n-1 m_n) . (S1 ... Sm shift)) ;;; ((m_n . M2) . (shift . S2))) ;;; = (join-wraps ((m_0 ... m_n-1) . S1) (M2 . S2)) ;;; else like append ;;; {does add-mark if marks don't cancel} ;;; ;;; (id->label id (M . (#(id id' M) . S))) ;;; = id' ;;; (id->label id ((m . M) . (shift . S))) ;;; = (id->label id (M . S)) ;;; ;;; NB: This does screw up strange examples such as the following: ;;; ;;; (define-syntax a ;;; (lambda (?) ;;; (with-syntax ((xx ((lambda (x) (syntax x)) 4))) ;;; (syntax (let ((xx 3) (x 4)) (list x xx)))))) ;;; ;;; a ;=> (3 4) in v5.0b ;;; a ;=> error (duplicate bound ids) in v5.9a and beyond ;;; ;;; Which is correct? Should we substitute based on symbolic name and ;;; marks or based on the current "name" (label)? It's not clear, and ;;; substitution based on symbolic name and marks is both easier and ;;; more efficient, so we've gone with that. On the other hand, the ;;; other approach is more directly based on alpha conversion and thus ;;; perhaps more theoretically appealing. Both approaches yield ;;; (slightly different interpretations of) lexical scope. ;;; Conjecture: if we disregard out-of-context identifiers, the two ;;; mechanisms yield identical substitutions. (let () (define noexpand "noexpand") ;;; hooks to nonportable run-time helpers (include "types.ss") (import (nanopass)) (include "base-lang.ss") (include "expand-lang.ss") (begin (define top-level-eval-hook ; for top-level macro transformers and eval-when, use default ; system evaluator (lambda (x) (eval `(,noexpand ,x)))) (define local-eval-hook ; for local macro transformers, use interpreter unless profiling is enabled (lambda (x) ((if (compile-profile) eval interpret) `(,noexpand ,x)))) (define define-top-level-value-hook $set-top-level-value!) (define get-clo-info (lambda (sym) ($sgetprop sym '*clo* #f))) (define put-clo-info (lambda (sym info) ($sputprop sym '*clo* info))) (define get-global-definition-hook (lambda (sym) ($sgetprop sym '*cte* #f))) (define put-global-definition-hook (lambda (sym x) (with-tc-mutex (if (not x) ($sremprop sym '*cte*) ($sputprop sym '*cte* x))))) (define put-library-descriptor (lambda (symbol desc) ($sputprop symbol '*library* desc))) (define get-library-descriptor (lambda (symbol) ($sgetprop symbol '*library* #f))) (define rem-library-descriptor (lambda (symbol) ($sremprop symbol '*library*))) (define put-program-descriptor (lambda (symbol desc) ($sputprop symbol '*program* desc))) (define get-program-descriptor (lambda (symbol) ($sgetprop symbol '*program* #f))) (define rem-program-descriptor (lambda (symbol) ($sremprop symbol '*program*))) (define get-global-substs (lambda (symbol token) ($sgetprop symbol token #f))) (define update-global-substs! (lambda (symbol token p) (with-tc-mutex (let ([x (p ($sgetprop symbol token #f))]) (if (not x) ($sremprop symbol token) ($sputprop symbol token x)))))) (define generate-id (lambda (sym) (gensym (symbol->string sym)))) (define make-token:sym (lambda (token sym) (let ([sym-pname (symbol->string sym)]) (gensym sym-pname (let ([token-name (if (gensym? token) (gensym->unique-string token) (symbol->string token))]) (if (gensym? sym) ; assuming that token pnames/unames never contain : or % (format "~a%~a" token-name (gensym->unique-string sym)) (format "~a:~a" token-name sym-pname))))))) ) ;;; output constructors (with-output-language (Lsrc Expr) (define ae->src (lambda (ae) (and (and (annotation? ae) (fxlogtest (annotation-flags ae) (constant annotation-debug))) (annotation-source ae)))) (define build-profile (lambda (ae e) (define ae->profile-src (lambda (ae) (and (and (annotation? ae) (fxlogtest (annotation-flags ae) (constant annotation-profile))) (annotation-source ae)))) (if (and (eq? ($compile-profile) 'source) (generate-profile-forms)) (let ([src (ae->profile-src ae)]) (if src `(seq (profile ,src) ,e) e)) e))) (module (build-lambda build-library-case-lambda build-case-lambda) (define build-clause (lambda (fmls body) (let f ((ids fmls) (n 0)) (in-context CaseLambdaClause (cond ((pair? ids) (f (cdr ids) (fx+ n 1))) ((null? ids) `(clause (,fmls ...) ,n ,body)) (else `(clause (,(let f ((ids fmls)) (if (pair? ids) (cons (car ids) (f (cdr ids))) (list ids))) ...) ,(fx- -1 n) ,body))))))) (define build-clauses (lambda (clauses) (map (lambda (x) (build-clause (car x) (cadr x))) clauses))) (define build-lambda (lambda (ae vars exp) (build-profile ae `(case-lambda ,(make-preinfo-lambda (ae->src ae)) ,(build-clause vars exp))))) (define build-case-lambda (lambda (ae clauses) (build-profile ae `(case-lambda ,(make-preinfo-lambda (ae->src ae) #f) ,(build-clauses clauses) ...)))) (define build-library-case-lambda (lambda (ae libspec clauses) (build-profile ae (let ([clauses (build-clauses clauses)]) (unless (equal? (list (libspec-interface libspec)) (map (lambda (clause) (nanopass-case (Lsrc CaseLambdaClause) clause [(clause (,x* ...) ,interface ,body) interface])) clauses)) ($oops #f "libspec interface mismatch ~s" libspec)) `(case-lambda ,(make-preinfo-lambda (ae->src ae) #f libspec) ,clauses ...)))))) (define build-call (lambda (ae e e*) (build-profile ae `(call ,(make-preinfo (ae->src ae) #f) ,e ,e* ...)))) (define build-application ; used by chi-application. pulls profile form off e if e is a lambda expression ; so it won't interfere with cprep and cpvalid let recognition (lambda (ae e e*) (if (eq? ($compile-profile) 'source) (nanopass-case (Lsrc Expr) e [(seq ,e1 ,e2) (guard (nanopass-case (Lsrc Expr) e1 [(profile ,src) #t] [else #f]) (nanopass-case (Lsrc Expr) e2 [(case-lambda ,preinfo ,cl* ...) #t] [else #f])) `(seq ,e1 ,(build-call ae e2 e*))] [else (build-call ae e e*)]) (build-call ae e e*)))) (define-syntax build-primcall ; written as a macro to give lookup-primref a chance to lookup the primref at expansion time (syntax-rules () [(_ ?ae ?level ?name ?arg ...) (build-call ?ae (lookup-primref ?level ?name) (list ?arg ...))])) (define build-let (lambda (ae x* e* body) (build-call ae (build-lambda #f x* body) e*))) (define build-conditional (lambda (ae test-exp then-exp else-exp) (build-profile ae `(if ,test-exp ,then-exp ,else-exp)))) (define build-lexical-reference (lambda (ae prelex) (if (prelex-referenced prelex) (set-prelex-multiply-referenced! prelex #t) (set-prelex-referenced! prelex #t)) (build-profile ae `(ref ,(ae->src ae) ,prelex)))) (define build-lexical-assignment (lambda (ae var exp) (set-prelex-assigned! var #t) (build-profile ae `(set! ,(ae->src ae) ,var ,exp)))) (define build-cte-optimization-loc (lambda (box exp) ; box is for cp0 to store optimization info, if it pleases. the box is eq? to ; the box on the system property list for the library global label and ; stored in the library/ct-info record for the file. `(cte-optimization-loc ,box ,exp))) (define build-primitive-reference (lambda (ae name) (if ($suppress-primitive-inlining) (build-primcall ae 3 '$top-level-value `(quote ,name)) (build-profile ae (lookup-primref (fxmax (optimize-level) 2) name))))) (define build-primitive-assignment (lambda (ae name val) (build-primcall ae 3 '$set-top-level-value! `(quote ,name) val))) (module (build-global-reference build-global-assignment) (define unbound-warning (lambda (src what name) (unless (or (gensym? name) ($sgetprop name 'no-unbound-warning #f)) ($source-warning #f src #t "undeclared variable ~a ~s" what name) ($sputprop name 'no-unbound-warning #t)))) (define build-global-reference (lambda (ae name safe?) (when (eq? (subset-mode) 'system) (unbound-warning (ae->src ae) "reference to" name)) (build-primcall ae (if (or safe? (fx= (optimize-level) 3)) 3 2) '$top-level-value `(quote ,name)))) (define build-global-assignment (lambda (ae name val) (when (eq? (subset-mode) 'system) (unbound-warning (ae->src ae) "assignment to" name)) (build-primcall ae 3 '$set-top-level-value! `(quote ,name) val)))) (define build-cte-install (lambda (sym exp token) (build-primcall #f 3 '$sc-put-cte `(quote ,sym) exp `(quote ,token)))) (define build-checking-cte-install (lambda (sym exp token) (build-cte-install sym (build-primcall #f 3 '$transformer->binding exp) token))) (define build-visit-only (lambda (exp) (with-output-language (Lexpand Outer) `(visit-only ,exp)))) (define build-revisit-only (lambda (exp) (with-output-language (Lexpand Outer) `(revisit-only ,exp)))) (define safe-to-defer? (lambda (x) (nanopass-case (Lsrc Expr) x [(seq (profile ,src) (case-lambda ,preinfo ,cl* ...)) #t] [(case-lambda ,preinfo ,cl* ...) #t] [else #f]))) (define build-moi (let ([moi-record `(moi)]) (lambda () moi-record))) (module (build-foreign-procedure build-foreign-callable) (define build-fp-specifier (lambda (who what x void-okay?) (with-output-language (Ltype Type) (or (case x [(scheme-object) `(fp-scheme-object)] [(u8*) `(fp-u8*)] [(u16*) `(fp-u16*)] [(u32*) `(fp-u32*)] [(fixnum) `(fp-fixnum)] [(double-float) `(fp-double-float)] [(single-float) `(fp-single-float)] [(integer-8) `(fp-integer 8)] [(unsigned-8) `(fp-unsigned 8)] [(integer-16) `(fp-integer 16)] [(unsigned-16) `(fp-unsigned 16)] [(integer-24 integer-32) `(fp-integer 32)] [(unsigned-24 unsigned-32) `(fp-unsigned 32)] [(integer-40 integer-48 integer-56 integer-64) `(fp-integer 64)] [(unsigned-40 unsigned-48 unsigned-56 unsigned-64) `(fp-unsigned 64)] [(void) (and void-okay? `(fp-void))] [else (cond [($ftd? x) `(fp-ftd ,x)] [($ftd-as-box? x) `(fp-ftd& ,(unbox x))] [else #f])]) ($oops #f "invalid ~a ~a specifier ~s" who what x))))) (define build-foreign-procedure (lambda (ae conv* foreign-name foreign-addr params result) (build-profile ae `(foreign (,conv* ...) ,foreign-name ,foreign-addr (,(map (lambda (x) (build-fp-specifier 'foreign-procedure 'parameter x #f)) params) ...) ,(build-fp-specifier 'foreign-procedure "result" result #t))))) (define build-foreign-callable (lambda (ae conv* proc params result) (build-profile ae `(fcallable (,conv* ...) ,proc (,(map (lambda (x) (build-fp-specifier 'foreign-callable 'parameter x #f)) params) ...) ,(build-fp-specifier 'foreign-callable "result" result #t)))))) (define build-pariah (lambda (ae e) (build-profile ae `(seq (pariah) ,e)))) (define-syntax build-primref (syntax-rules () [(_ ?level ?name) (lookup-primref ?level ?name)])) (define build-primref? (lambda (ae level name) (let ([pr ($sgetprop name (if (eqv? level 2) '*prim2* '*prim3*) #f)]) (and pr (build-profile ae pr))))) (define build-data (lambda (ae exp) (build-profile ae `(quote ,exp)))) (define build-void (let ([void-record `(quote ,(void))]) (case-lambda [() void-record] [(ae) (build-profile ae void-record)]))) (define build-sequence (lambda (ae x*) (if (null? x*) (build-void ae) (build-profile ae (let loop ([x* x*]) (let ([x (car x*)] [x* (cdr x*)]) (if (null? x*) x (if (nanopass-case (Lsrc Expr) x [(quote ,d) (eq? d (void))] [else #f]) (loop x*) `(seq ,x ,(loop x*)))))))))) (define build-group (lambda (x*) (with-output-language (Lexpand Outer) (if (null? x*) (build-void no-source) (let f ([x* x*]) (let ([x (car x*)] [x* (cdr x*)]) (if (null? x*) x (if (and (Lsrc? x) (nanopass-case (Lsrc Expr) x [(quote ,d) (eq? d (void))] [else #f])) (f x*) (let ([y (f x*)]) (if (and (Lsrc? x) (Lsrc? y)) (with-output-language (Lsrc Expr) `(seq ,x ,y)) `(group ,x ,y))))))))))) (define build-letrec (lambda (ae vars val-exps body-exp) (build-profile ae (if (null? vars) body-exp `(letrec ([,vars ,val-exps] ...) ,body-exp))))) (define build-letrec* (lambda (ae vars val-exps body-exp) (build-profile ae (if (null? vars) body-exp `(letrec* ([,vars ,val-exps] ...) ,body-exp))))) (define build-body (lambda (ae vars val-exps body-exp) ((if (internal-defines-as-letrec*) build-letrec* build-letrec) ae vars val-exps body-exp))) (define build-top-module (lambda (ae types vars val-exps body-exp) (if (internal-defines-as-letrec*) (let-values ([(vars val-exps) (let f ([types types] [vars vars] [val-exps val-exps]) (if (null? types) (values '() '()) (let ([var (car vars)] [val-exp (car val-exps)]) (let-values ([(vars val-exps) (f (cdr types) (cdr vars) (cdr val-exps))]) (if (eq? (car types) 'global) (values (cons (build-lexical-var no-source 'ignore) vars) (cons (build-global-assignment no-source var val-exp) val-exps)) (values (cons var vars) (cons val-exp val-exps)))))))]) (build-letrec* ae vars val-exps body-exp)) (let-values ([(vars sets) (let f ([types types] [vars vars]) (if (null? types) (values '() '()) (let ([var (car vars)]) (let-values ([(vars sets) (f (cdr types) (cdr vars))]) (if (eq? (car types) 'global) (let ([x (build-lexical-var no-source var)]) (values (cons x vars) (cons (build-global-assignment no-source var (build-lexical-reference no-source x)) sets))) (values (cons var vars) sets))))))]) (build-letrec ae vars val-exps (if (null? sets) body-exp (build-sequence no-source (append sets (list body-exp))))))))) (define build-top-program (lambda (uid body-exp) (with-output-language (Lexpand Program) `(program ,uid ,body-exp)))) (define build-recompile-info (lambda (import-req* include-req*) (with-output-language (Lexpand Outer) `(recompile-info ,(make-recompile-info (remp (lambda (x) (libdesc-system? (get-library-descriptor (libreq-uid x)))) import-req*) include-req*))))) (define build-library/ct-info (lambda (linfo/ct) (with-output-language (Lexpand Inner) `(library/ct-info ,linfo/ct)))) (define build-library/rt-info (lambda (linfo/rt) (with-output-language (Lexpand Inner) `(library/rt-info ,linfo/rt)))) (define build-program-info (lambda (pinfo) (with-output-language (Lexpand Inner) `(program-info ,pinfo)))) (define build-top-library/rt (lambda (uid dl* db* dv* de* init*) (with-output-language (Lexpand rtLibrary) `(library/rt ,uid (,dl* ...) (,db* ...) (,dv* ...) (,de* ...) ,(build-sequence no-source init*))))) (define build-top-library/ct (lambda (uid export-id* import-code* visit-code*) (with-output-language (Lexpand ctLibrary) `(library/ct ,uid (,export-id* ...) ,(build-lambda no-source '() (build-sequence no-source import-code*)) ,(if (null? visit-code*) (build-primref 3 'void) (build-lambda no-source '() (build-sequence no-source visit-code*))))))) (define build-library-body (lambda (ae labels boxes vars val-exps body-exp) (build-letrec* ae vars val-exps (fold-right (lambda (label box var body) (if label `(seq ,(build-global-assignment no-source label (build-cte-optimization-loc box (build-lexical-reference no-source var))) ,body) body)) body-exp labels boxes vars)))) (define build-lexical-var (lambda (ae id) (make-prelex id 0 ae #f))) (define lexical-var-assigned? (lambda (x) (prelex-assigned x))) (define build-input-profile (lambda (src) `(profile ,src))) ) (define unannotate (lambda (e) (if (annotation? e) (annotation-expression e) e))) (define no-source #f) ;;; compile-time environments ;;; wrap and environment comprise two level mapping. ;;; wrap : id --> label ;;; env : label --> ;;; to handle define-property, wraps actually map id -> label/pl ;;; label/pl -> label | (label . (property ...)) ;;; property -> (label . label) ;;; environments are represented in two parts: a lexical part and a global ;;; part. The lexical part is a simple list of associations from labels ;;; to bindings. The global part is implemented by ;;; {put,get}-global-definition-hook and associates symbols with ;;; bindings. ;;; global (assumed global variable) and displaced-lexical (see below) ;;; do not usually show up in any environment; instead, they are fabricated by ;;; lookup when it finds no other bindings. ;;; ::= ((