[racket] How to implement declations in an internal definition context?
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 <jensaxel at 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