For the archives: A solution using an alternative strategy. #lang racket (require (for-syntax syntax/parse racket/format))
(begin-for-syntax (define module-level-variables '()) ; each internal definition contexts is given an index (define intdefs (make-hasheq)) ; each index is associated to a list of declared names (define local-variables (make-hash)) (define (new-intdef? intdef) (hash-has-key? intdefs intdef)) (define (index-of-intdef intdef) (hash-ref! intdefs intdef (hash-count intdefs))) (define (add-local-var! index var) (hash-update! local-variables index (λ (old-vars) (cons var old-vars)) '()))) (define-syntax (vars stx) (with-syntax ([vs module-level-variables]) #''vs)) (begin-for-syntax (define refresh-identifier (compose syntax-local-introduce syntax-local-get-shadower))) (define-syntax (declare stx) (syntax-parse stx [(_ id) (define var (syntax->datum #'id)) (define ctx (syntax-local-context)) (cond [(eq? ctx 'module) (set! module-level-variables (cons var module-level-variables)) #'(begin)] [(list? ctx) ; internal definition context (define old-scope? (new-intdef? ctx)) (define index (index-of-intdef ctx)) (add-local-var! index var) (cond [old-scope? #'(begin)] [else (with-syntax ([vars (refresh-identifier #'vars)] [old-vars (syntax-local-get-shadower #'vars)] [index index]) #'(define-syntax (vars st) (with-syntax ([locals (hash-ref local-variables index)]) #'(append 'locals (old-vars)))))])] [else (error 'declare (~a "declarations are only allowed at the module level " "and in internal definition contexts"))])])) (declare a) (display (vars)) (let () (display (vars)) (declare x) (display (vars)) (let () (display (vars)) (declare s) (declare t) (display (vars))) (declare y) (display (vars))) (declare b) ; displays: (b a)(y x b a)(y x b a)(t s y x b a)(t s y x b a)(y x b a) 2014-06-13 16:24 GMT+02:00 Jens Axel Søgaard <jensa...@soegaard.net>: > Hi All, > > In order to add linear equation syntax to MetaPict I need to > understand how implement declarations in an internal definition > context. For practice purposes I have written the following small > program. So far global declarations and local declaration via a > let-var binding construct works. I am now attempting to transfer the > methods used in let-var into an implementation of declarations that > work in an internal definition context. Unfortunately I am stuck. > > In the definition of let-var I have used syntax-parameterize and I > can't figure what to do in an internal definition context. > > Any help is appreciated. I have inserted and attached the program > below, but there is a pretty, color version at PasteRack: > > http://pasterack.org/pastes/9771 > > /Jens Axel > > #lang racket > (require racket/splicing racket/stxparam) > (require (for-syntax syntax/parse racket/format)) > > ;;; > ;;; IMPLEMENTED > ;;; > > ;;; This program implement three forms: > ;;; (declare x) that declares that the symbol x is the > name of a variable > ;;; vars which evaluates to a list of all > declared variables > ;;; at the point, where vars occurs > ;;; (let-var (x ...) body) a local declaration, where the symbols x ... > ;;; are declared in body, but not outside. > > ;;; Example > > ; Form: Result: > ; vars () > ; (declare x) > ; vars (x) > ; (let-var (y) vars) (y x) > ; vars (x) > ; (let-var (y) (let-var z 1) vars) (y x) > > ;;; > ;;; PROBLEM > ;;; > > ; i) How can I extend the definition of declare in order to > ; support declarations in a internal definition context? > > ; That is, is there a way to get this interaction: > > ; Form: Result: > ; vars () > ; (declare x) > ; vars (x) > ; (let () (declare y) vars) (y x) > ; vars (y) > ; (let () > ; (declare y) > ; (let () (declare z) 1) > ; vars) (y x) > > > (begin-for-syntax > ; Global variables are simply stored in a compile time variable > (define globals '())) > > ; At expansion start there is only global variables, so > ; vars simply returns a quoted copy of the symbols collected in globals > (define-syntax-parameter vars (λ(stx) #`'#,globals)) > > ; Local variables on the other hand must be kept in a syntax parameter > ; to keep the list of locally declared variable around at syntax > transformation time. > (define-syntax-parameter locals '()) > > > (define-syntax (orig-declare stx) > ; A simple version of declare (here named orig-declare) simply adds > ; declared variables to the global list. > (syntax-parse stx > [(_ v) > (define var (syntax->datum #'v)) > (unless (member var globals) > (set! globals (cons var globals))) > #'(void)])) > > > ; The following tests reveals that everything works as expected: > > ; Expression Expected > vars ; () > (orig-declare x) ; > vars ; (x) > (orig-declare x) ; > vars ; (x) > (orig-declare y) ; > vars ; (y x) > > > ; Locally declared variables are implemented using syntax-parameterize. > ; Both the user facing syntax vars and the list of locally > ; declared are adjusted with the help of syntax-parameterize. > > ; (let-var (var ...) body ...) > ; evaluate body in an environment where var ... are declared variables > (define-syntax (let-var stx) > (syntax-parse stx > [(_ () body ...) > #'(let () body ...)] > [(_ (v:id vs:id ...) body ...) > (let () > (define var (syntax->datum #'v)) > (define old-locals (syntax-parameter-value #'locals)) > (define new-locals (cons var old-locals)) > #`(syntax-parameterize ([vars (λ (stx) #''#,(append new-locals > globals))]) > (syntax-parameterize ([locals '#,new-locals]) > (let-var (vs ...) body ...))))])) > > > ; Expression Expected > (let-var (a) vars) ; ( a y x) > (let-var (a b) vars) ; (b a y x) > (let-var (a) (let-var (b) vars)) ; (b a y x) > (let-var (a) (let-var (b) 7) vars) ; ( a y x) > > > ;;; > ;;; The hope was to copy the ideas used in let-var to implement > ;;; declarations in an internal definition context. > > ;;; The point where I am stuck: > ;;; syntax-parameterize can adjust the meaning of locals and vars in > a given body > ;;; But in an internal definition context, the meaning of locals and vars > ;;; must be adjusted in the rest of the internal definition context, > ;;; so at the point where (declare x) is expanded, there is no > access to the "body". > > (define-syntax (declare stx) > (syntax-parse stx > [(_ v) > (define var (syntax->datum #'v)) > (define ctx (syntax-local-context)) > (cond > [(eq? ctx 'top-level) > ; outside any module, definition or expression > ; (except as a subsexpression in a top-level begin) > ; i.e. think repl... > (error 'declare "the top-level context not supported (yet?)")] > [(eq? ctx 'module-begin) > ; inside the body of a module as the only form within the module > (error 'declare "the module-begin context not supported (yet?)")] > [(eq? ctx 'expression) > ; in a context where only expressions are allowed > (error 'declare "declarations can not appear as expressions")] > [(eq? ctx 'module) > ; in a module (inside the module-begin layer) > (unless (member var globals) > (set! globals (cons var globals))) > #'(void)] > [(list? ctx) ; internal definition context > ; in a nested context that allows both definitions and expressions > ; e.g. the body of a let expression is an internal definition context > ; For let and friends, a defintion in an internal definition context > ; is a equivalent to local binding via letrec-syntaxes+values. > ; Each body is partially expanded into one of: > ; i) define-values > ; ii) define-syntaxes > ; iii) primitive expression other than begin > ; iv) a begin form (which is spliced in, and the newly-spliced > subforms > ; are also partially expanded) > ; The definitions and expressions are then rewritten into a > letrec-syntaxes+values form > > ; Note: A (define-syntax-parameter ...) expands into define-syntaxes. > ; But what about syntax-parameterize ? > (define var (syntax->datum #'v)) > (define old-locals (unbox (syntax-parameter-value #'locals))) > (define new-locals (cons var old-locals)) > (displayln new-locals) > #'(void) ; <- What to write here? > ] > [else (error 'declare (~a "expansion reached unhandled > expansion context " ctx))])])) -- -- Jens Axel Søgaard ____________________ Racket Users list: http://lists.racket-lang.org/users