racket/collects/syntax/parse/private/litconv.rkt
2011-04-07 09:47:20 -06:00

167 lines
5.6 KiB
Racket

#lang racket/base
(require (for-syntax racket/base
"sc.rkt"
"lib.rkt"
racket/syntax
syntax/keyword
"rep-data.rkt"
"rep.rkt"
"kws.rkt")
"runtime.rkt")
(provide define-conventions
define-literal-set
kernel-literals)
(define-syntax (define-conventions stx)
(define-syntax-class header
#:description "name or name with formal parameters"
(pattern name:id
#:with formals #'()
#:attr arity (arity 0 0 null null))
(pattern (name:id . formals)
#:attr arity (parse-kw-formals #'formals #:context stx)))
(syntax-parse stx
[(define-conventions h:header rule ...)
(let ()
(define rules (check-conventions-rules #'(rule ...) stx))
(define rxs (map car rules))
(define dens0 (map cadr rules))
(define den+defs-list
(for/list ([den0 (in-list dens0)])
(let-values ([(den defs) (create-aux-def den0)])
(cons den defs))))
(define dens (map car den+defs-list))
(define defs (apply append (map cdr den+defs-list)))
(define/with-syntax (rx ...) rxs)
(define/with-syntax (def ...) defs)
(define/with-syntax (parser ...)
(map den:delayed-parser dens))
(define/with-syntax (class-name ...)
(map den:delayed-class dens))
#'(begin
(define-syntax h.name
(make-conventions
(quote-syntax get-parsers)
(lambda ()
(let ([class-names (list (quote-syntax class-name) ...)])
(map list
(list 'rx ...)
(map make-den:delayed
(generate-temporaries class-names)
class-names))))))
(define get-parsers
(lambda formals
def ...
(list parser ...)))))]))
(define-for-syntax (check-phase-level stx ctx)
(unless (or (exact-integer? (syntax-e stx))
(eq? #f (syntax-e stx)))
(raise-syntax-error #f "expected phase-level (exact integer or #f)" ctx stx))
stx)
(define-syntax (define-literal-set stx)
(syntax-case stx ()
[(define-literal-set name . rest)
(let-values ([(chunks rest)
(parse-keyword-options
#'rest
`((#:phase ,check-phase-level)
(#:for-template)
(#:for-syntax)
(#:for-label))
#:incompatible '((#:phase #:for-template #:for-syntax #:for-label))
#:context stx
#:no-duplicates? #t)])
(unless (identifier? #'name)
(raise-syntax-error #f "expected identifier" stx #'name))
(let ([relphase
(cond [(assq '#:for-template chunks) -1]
[(assq '#:for-syntax chunks) 1]
[(assq '#:for-label chunks) #f]
[else (options-select-value chunks '#:phase #:default 0)])]
[lits (syntax-case rest ()
[( (lit ...) )
(check-literals-list/litset #'(lit ...) stx)]
[_ (raise-syntax-error #f "bad syntax" stx)])])
(with-syntax ([((internal external) ...) lits]
[relphase relphase])
#`(begin
(define phase-of-literals
(if 'relphase
(+ (phase-of-enclosing-module) 'relphase)
'relphase))
(define-syntax name
(make-literalset
(list (list 'internal (quote-syntax external)) ...)
(quote-syntax phase-of-literals)))
(begin-for-syntax/once
(for ([x (in-list (syntax->list #'(external ...)))])
(unless (identifier-binding x 'relphase)
(raise-syntax-error #f
(format "literal is unbound in phase ~a" 'relphase)
(quote-syntax #,stx) x))))))))]))
(define-syntax (phase-of-enclosing-module stx)
(syntax-case stx ()
[(poem)
(let ([phase-within-module (syntax-local-phase-level)])
#`(let ([phase-of-this-expression
(variable-reference->phase (#%variable-reference))])
(- phase-of-this-expression
#,(if (zero? phase-within-module) 0 1))))]))
#|
Literal sets: The goal is for literals to refer to their bindings at
phase 0 relative to the enclosing module
Use cases, explained:
1) module X with def-lit-set is required-for-syntax
phase-of-mod-inst = 1
phase-of-def = 0
literals looked up at abs phase 1
which is phase 0 rel to module X
2) module X with local def-lit-set within define-syntax
phase-of-mod-inst = 1 (mod at 0, but +1 within define-syntax)
phase-of-def = 1
literals looked up at abs phase 0
which is phase 0 rel to module X
3) module X with def-lit-set in phase-2 position (really uncommon case!)
phase-of-mod-inst = 1 (not 2, apparently)
phase-of-def = 2
literals looked up at abs phase 0
(that's why the weird (if (z?) 0 1) term)
|#
;; Literal sets
(define-literal-set kernel-literals
(begin
begin0
define-values
define-syntaxes
define-values-for-syntax
set!
let-values
letrec-values
#%plain-lambda
case-lambda
if
quote
quote-syntax
letrec-syntaxes+values
with-continuation-mark
#%expression
#%plain-app
#%top
#%datum
#%variable-reference
module #%provide #%require
#%plain-module-begin))