[racket] How to implement declations in an internal definition context?

From: Jens Axel Søgaard (jensaxel at soegaard.net)
Date: Sun Jun 15 08:26:54 EDT 2014

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


Posted on the users mailing list.