[racket] How to implement declations in an internal definition context?
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))])]))
-------------- next part --------------
A non-text attachment was scrubbed...
Name: example-of-declare-and-letvar.rkt
Type: application/octet-stream
Size: 6257 bytes
Desc: not available
URL: <http://lists.racket-lang.org/users/archive/attachments/20140613/2a583124/attachment-0001.obj>