syntax/parse: literals, literal-sets, and phases (todo: docs)

typed-scheme: added missing import for literal
This commit is contained in:
Ryan Culpepper 2010-05-04 11:50:42 -06:00
parent f42adad3f8
commit eff9147ddc
7 changed files with 182 additions and 81 deletions

View File

@ -258,16 +258,10 @@
(fail x (fail x
#:expect (expectation pattern0) #:expect (expectation pattern0)
#:fce fc)))] #:fce fc)))]
[#s(pat:literal attrs literal #f) [#s(pat:literal attrs literal input-phase lit-phase)
#`(if (and (identifier? x) (free-identifier=? x (quote-syntax literal)))
k
(fail x
#:expect (expectation pattern0)
#:fce fc))]
[#s(pat:literal attrs literal phase)
#`(if (and (identifier? x) #`(if (and (identifier? x)
(free-identifier=? x (quote-syntax literal) (free-identifier=?/phases x input-phase
(phase+ (syntax-local-phase-level) phase))) (quote-syntax literal) lit-phase))
k k
(fail x (fail x
#:expect (expectation pattern0) #:expect (expectation pattern0)
@ -629,7 +623,7 @@
[(_ #s(pat:datum attrs d)) [(_ #s(pat:datum attrs d))
#'(begin (collect-error '(datum d)) #'(begin (collect-error '(datum d))
(make-expect:atom 'd))] (make-expect:atom 'd))]
[(_ #s(pat:literal attrs lit phase)) [(_ #s(pat:literal attrs lit input-phase lit-phase))
#'(begin (collect-error '(literal lit)) #'(begin (collect-error '(literal lit))
(make-expect:literal (quote-syntax lit)))] (make-expect:literal (quote-syntax lit)))]
;; 2 pat:compound patterns ;; 2 pat:compound patterns

View File

@ -1,6 +1,7 @@
#lang racket/base #lang racket/base
(require racket/contract/base (require racket/contract/base
racket/dict racket/dict
racket/list
syntax/stx syntax/stx
syntax/id-table syntax/id-table
"../util.ss" "../util.ss"
@ -92,9 +93,9 @@ A ConventionRule is (list regexp DeclEntry)
#| #|
A LiteralSet is A LiteralSet is
(make-literalset (listof (list symbol id ct-phase))) (make-literalset (listof (list symbol id)) stx)
|# |#
(define-struct literalset (literals) #:transparent) (define-struct literalset (literals phase) #:transparent)
;; make-dummy-stxclass : identifier -> SC ;; make-dummy-stxclass : identifier -> SC
;; Dummy stxclass for calculating attributes of recursive stxclasses. ;; Dummy stxclass for calculating attributes of recursive stxclasses.
@ -110,14 +111,14 @@ DeclEnv =
(listof ConventionRule)) (listof ConventionRule))
DeclEntry = DeclEntry =
(make-den:lit id id ct-phase) (make-den:lit id id ct-phase ct-phase)
(make-den:class id id (listof syntax) bool) (make-den:class id id (listof syntax) bool)
(make-den:parser id id (listof SAttr) bool bool) (make-den:parser id id (listof SAttr) bool bool)
(make-den:delayed id id id) (make-den:delayed id id id)
|# |#
(define-struct declenv (table conventions)) (define-struct declenv (table conventions))
(define-struct den:lit (internal external phase)) (define-struct den:lit (internal external input-phase lit-phase))
(define-struct den:class (name class args)) (define-struct den:class (name class args))
(define-struct den:parser (parser description attrs splicing? commit?)) (define-struct den:parser (parser description attrs splicing? commit?))
(define-struct den:delayed (parser description class)) (define-struct den:delayed (parser description class))
@ -127,7 +128,8 @@ DeclEntry =
(for/fold ([table (make-immutable-bound-id-table)]) (for/fold ([table (make-immutable-bound-id-table)])
([literal literals]) ([literal literals])
(bound-id-table-set table (car literal) (bound-id-table-set table (car literal)
(make den:lit (car literal) (cadr literal) (caddr literal)))) (make den:lit (first literal) (second literal)
(third literal) (fourth literal))))
conventions)) conventions))
(define (declenv-lookup env id #:use-conventions? [use-conventions? #t]) (define (declenv-lookup env id #:use-conventions? [use-conventions? #t])
@ -141,7 +143,7 @@ DeclEntry =
;; So blame-declare? only applies to stxclass declares ;; So blame-declare? only applies to stxclass declares
(let ([val (declenv-lookup env id #:use-conventions? #f)]) (let ([val (declenv-lookup env id #:use-conventions? #f)])
(match val (match val
[(struct den:lit (_i _e _p)) [(struct den:lit (_i _e _ip _lp))
(wrong-syntax id "identifier previously declared as literal")] (wrong-syntax id "identifier previously declared as literal")]
[(struct den:class (name _c _a)) [(struct den:class (name _c _a))
(if (and blame-declare? stxclass-name) (if (and blame-declare? stxclass-name)
@ -205,10 +207,9 @@ DeclEntry =
(define SideClause/c (define SideClause/c
(or/c clause:fail? clause:with? clause:attr?)) (or/c clause:fail? clause:with? clause:attr?))
;; ct-phase ;; ct-phase = syntax, expr that computes absolute phase
;; #f means not specified, ie default, ie 0 ;; usually = #'(syntax-local-phase-level)
;; syntax means computed by given expr (define ct-phase/c syntax?)
(define ct-phase/c (or/c syntax? #f))
(provide (struct-out den:lit) (provide (struct-out den:lit)
(struct-out den:class) (struct-out den:class)
@ -225,7 +226,7 @@ DeclEntry =
[stxclass-lookup-config (parameter/c (symbols 'no 'try 'yes))] [stxclass-lookup-config (parameter/c (symbols 'no 'try 'yes))]
[new-declenv [new-declenv
(->* [(listof (list/c identifier? identifier? ct-phase/c))] (->* [(listof (list/c identifier? identifier? ct-phase/c ct-phase/c))]
[#:conventions list?] [#:conventions list?]
DeclEnv/c)] DeclEnv/c)]
[declenv-lookup [declenv-lookup

View File

@ -17,7 +17,7 @@ A Base is (listof IAttr)
A SinglePattern is one of A SinglePattern is one of
(make-pat:any Base) (make-pat:any Base)
(make-pat:var Base id id (listof stx) (listof IAttr) bool) (make-pat:var Base id id (listof stx) (listof IAttr) bool)
(make-pat:literal Base identifier ct-phase) (make-pat:literal Base identifier ct-phase ct-phase)
(make-pat:datum Base datum) (make-pat:datum Base datum)
(make-pat:ghost Base GhostPattern SinglePattern) (make-pat:ghost Base GhostPattern SinglePattern)
(make-pat:head Base HeadPattern SinglePattern) (make-pat:head Base HeadPattern SinglePattern)
@ -38,7 +38,7 @@ A ListPattern is a subtype of SinglePattern; one of
(define-struct pat:any (attrs) #:prefab) (define-struct pat:any (attrs) #:prefab)
(define-struct pat:var (attrs name parser args nested-attrs commit?) #:prefab) (define-struct pat:var (attrs name parser args nested-attrs commit?) #:prefab)
(define-struct pat:literal (attrs id phase) #:prefab) (define-struct pat:literal (attrs id input-phase lit-phase) #:prefab)
(define-struct pat:datum (attrs datum) #:prefab) (define-struct pat:datum (attrs datum) #:prefab)
(define-struct pat:ghost (attrs ghost inner) #:prefab) (define-struct pat:ghost (attrs ghost inner) #:prefab)
(define-struct pat:head (attrs head tail) #:prefab) (define-struct pat:head (attrs head tail) #:prefab)
@ -186,8 +186,8 @@ A Kind is one of
(define (create-pat:datum datum) (define (create-pat:datum datum)
(make pat:datum null datum)) (make pat:datum null datum))
(define (create-pat:literal literal phase) (define (create-pat:literal literal input-phase lit-phase)
(make pat:literal null literal phase)) (make pat:literal null literal input-phase lit-phase))
(define (create-pat:ghost g sp) (define (create-pat:ghost g sp)
(cond [(ghost:and? g) (cond [(ghost:and? g)

View File

@ -42,10 +42,15 @@
[create-aux-def [create-aux-def
(-> DeclEntry/c (-> DeclEntry/c
(values DeclEntry/c (listof syntax?)))] (values DeclEntry/c (listof syntax?)))]
#|
[check-literals-list [check-literals-list
;; NEEDS txlift context ;; NEEDS txlift context
(-> syntax? syntax? (-> syntax? syntax?
(listof (list/c identifier? identifier? ct-phase/c)))] (listof (list/c identifier? identifier? ct-phase/c ct-phase/c)))]
|#
[check-literals-list/litset
(-> syntax? syntax?
(listof (list/c identifier? identifier?)))]
#| #|
[check-literal-sets-list [check-literal-sets-list
;; NEEDS txlift context ;; NEEDS txlift context
@ -192,8 +197,7 @@
(define convs (options-select-value chunks '#:conventions #:default null)) (define convs (options-select-value chunks '#:conventions #:default null))
(define localconvs (options-select-value chunks '#:local-conventions #:default null)) (define localconvs (options-select-value chunks '#:local-conventions #:default null))
(define literals (define literals
(append-lits+litsets (check-literals-bound lits strict?) (append-lits+litsets lits litsets))
litsets))
(define-values (convs-rules convs-defs) (define-values (convs-rules convs-defs)
(for/fold ([convs-rules null] [convs-defs null]) (for/fold ([convs-rules null] [convs-defs null])
([conv-entry convs]) ([conv-entry convs])
@ -217,18 +221,6 @@
(let-values ([(parsers descriptions) (get-procedures arg ...)]) (let-values ([(parsers descriptions) (get-procedures arg ...)])
(apply values parsers))))) (apply values parsers)))))
(define (check-literals-bound lits strict?)
(define phase (syntax-local-phase-level))
(when strict?
(for ([p lits])
;; FIXME: hack...
(unless (or (identifier-binding (cadr p) phase)
(identifier-binding (cadr p) (add1 phase))
(identifier-binding (cadr p) (sub1 phase))
(identifier-binding (cadr p) #f))
(wrong-syntax (cadr p) "unbound identifier not allowed as literal"))))
lits)
;; decls-create-defs : DeclEnv -> (values DeclEnv (listof stx)) ;; decls-create-defs : DeclEnv -> (values DeclEnv (listof stx))
(define (decls-create-defs decls0) (define (decls-create-defs decls0)
(define (updater key value defs) (define (updater key value defs)
@ -237,9 +229,10 @@
(declenv-update/fold decls0 updater null)) (declenv-update/fold decls0 updater null))
;; create-aux-def : DeclEntry -> (values DeclEntry (listof stx)) ;; create-aux-def : DeclEntry -> (values DeclEntry (listof stx))
;; FIXME: replace with txlift mechanism
(define (create-aux-def entry) (define (create-aux-def entry)
(match entry (match entry
[(struct den:lit (_i _e _p)) [(struct den:lit (_i _e _ip _lp))
(values entry null)] (values entry null)]
[(struct den:class (name class args)) [(struct den:class (name class args))
(cond [(identifier? name) (cond [(identifier? name)
@ -483,8 +476,8 @@
(define (parse-pat:id id decls allow-head?) (define (parse-pat:id id decls allow-head?)
(define entry (declenv-lookup decls id)) (define entry (declenv-lookup decls id))
(match entry (match entry
[(struct den:lit (internal literal phase)) [(struct den:lit (internal literal input-phase lit-phase))
(create-pat:literal literal phase)] (create-pat:literal literal input-phase lit-phase)]
[(struct den:class (_n _c _a)) [(struct den:class (_n _c _a))
(error 'parse-pat:id (error 'parse-pat:id
"(internal error) decls had leftover stxclass entry: ~s" "(internal error) decls had leftover stxclass entry: ~s"
@ -610,11 +603,15 @@
(define (parse-pat:literal stx decls) (define (parse-pat:literal stx decls)
(syntax-case stx (~literal) (syntax-case stx (~literal)
[(~literal lit) [(~literal lit . more)
;; FIXME: support #:phase option here
(unless (identifier? #'lit) (unless (identifier? #'lit)
(wrong-syntax #'lit "expected identifier")) (wrong-syntax #'lit "expected identifier"))
(create-pat:literal #'lit #f)] (let* ([chunks (parse-keyword-options/eol #'more phase-directive-table
#:no-duplicates? #t
#:context stx)]
[phase (options-select-value chunks '#:phase #:default #f)])
;; FIXME: Duplicates phase expr!
(create-pat:literal #'lit phase phase))]
[_ [_
(wrong-syntax stx "bad ~~literal pattern")])) (wrong-syntax stx "bad ~~literal pattern")]))
@ -977,7 +974,9 @@
[_ [_
(raise-syntax-error #f "expected attribute name with optional depth declaration" ctx stx)])) (raise-syntax-error #f "expected attribute name with optional depth declaration" ctx stx)]))
;; check-literals-list : stx stx -> (listof (list id id ct-phase)) ;; check-literals-list : stx stx -> (listof (list id id ct-phase ct-phase))
;; - txlifts defs of phase expressions
;; - txlifts checks that literals are bound
(define (check-literals-list stx ctx) (define (check-literals-list stx ctx)
(unless (stx-list? stx) (unless (stx-list? stx)
(raise-syntax-error #f "expected literals list" ctx stx)) (raise-syntax-error #f "expected literals list" ctx stx))
@ -986,37 +985,72 @@
(when dup (raise-syntax-error #f "duplicate literal identifier" ctx dup))) (when dup (raise-syntax-error #f "duplicate literal identifier" ctx dup)))
lits)) lits))
;; check-literal-entry : stx stx -> (list id id ct-phase) ;; check-literal-entry : stx stx -> (list id id ct-phase ct-phase)
(define (check-literal-entry stx ctx) (define (check-literal-entry stx ctx)
(define (go internal external phase)
(txlift #`(check-literal (quote-syntax #,external)
#,phase (quote-syntax #,ctx)))
(list internal external phase phase))
(syntax-case stx () (syntax-case stx ()
[(internal external #:phase phase) [(internal external #:phase phase)
(and (identifier? #'internal) (identifier? #'external)) (and (identifier? #'internal) (identifier? #'external))
(list #'internal #'external (txlift #'phase))] (go #'internal #'external (txlift #'phase))]
[(internal external) [(internal external)
(and (identifier? #'internal) (identifier? #'external)) (and (identifier? #'internal) (identifier? #'external))
(list #'internal #'external #f)] (go #'internal #'external #'(syntax-local-phase-level))]
[id [id
(identifier? #'id) (identifier? #'id)
(list #'id #'id #f)] (go #'id #'id #'(syntax-local-phase-level))]
[_ [_
(raise-syntax-error #f "expected literal (identifier or pair of identifiers)" (raise-syntax-error #f "expected literal entry"
ctx stx)])) ctx stx)]))
;; Literal sets - Definition
;; check-literals-list/litset : stx stx -> (listof (list id id))
(define (check-literals-list/litset stx ctx)
(let ([lits (for/list ([x (stx->list stx)])
(check-literal-entry/litset x ctx))])
(let ([dup (check-duplicate-identifier (map car lits))])
(when dup (raise-syntax-error #f "duplicate literal identifier" ctx dup)))
lits))
;; check-literal-entry/litset : stx stx -> (list id id)
(define (check-literal-entry/litset stx ctx)
(syntax-case stx ()
[(internal external)
(and (identifier? #'internal) (identifier? #'external))
(list #'internal #'external)]
[id
(identifier? #'id)
(list #'id #'id)]
[_
(raise-syntax-error #f "expected literal entry"
ctx stx)]))
;; Literal sets - Import
;; check-literal-sets-list : stx stx -> (listof (listof (list id id ct-phase^2)))
(define (check-literal-sets-list stx ctx) (define (check-literal-sets-list stx ctx)
(unless (stx-list? stx) (unless (stx-list? stx)
(raise-syntax-error #f "expected literal-set list" ctx stx)) (raise-syntax-error #f "expected literal-set list" ctx stx))
(for/list ([x (stx->list stx)]) (for/list ([x (stx->list stx)])
(check-literal-set-entry x ctx))) (check-literal-set-entry x ctx)))
;; check-literal-set-entry : stx stx -> (listof (list id id ct-phase)) ;; check-literal-set-entry : stx stx -> (listof (list id id ct-phase^2))
(define (check-literal-set-entry stx ctx) (define (check-literal-set-entry stx ctx)
(define (elaborate litset-id lctx phase) (define (elaborate litset-id lctx phase)
;; phase is #f (not specified, means 0) or syntax (expr)
(let ([litset (syntax-local-value/catch litset-id literalset?)]) (let ([litset (syntax-local-value/catch litset-id literalset?)])
(unless litset (unless litset
(raise-syntax-error #f "expected identifier defined as a literal-set" (raise-syntax-error #f "expected identifier defined as a literal-set"
ctx litset-id)) ctx litset-id))
(elaborate-litset litset lctx phase stx))) (elaborate2 litset lctx phase)))
(define (elaborate2 litset lctx phase)
(for/list ([entry (literalset-literals litset)])
(list (datum->syntax lctx (car entry) stx)
(cadr entry)
phase
(literalset-phase litset))))
(syntax-case stx () (syntax-case stx ()
[(litset . more) [(litset . more)
(and (identifier? #'litset)) (and (identifier? #'litset))
@ -1024,22 +1058,16 @@
#:no-duplicates? #t #:no-duplicates? #t
#:context ctx)] #:context ctx)]
[lctx (options-select-value chunks '#:at #:default #'litset)] [lctx (options-select-value chunks '#:at #:default #'litset)]
[phase (options-select-value chunks '#:phase #:default #f)]) [phase (options-select-value chunks '#:phase
(elaborate #'litset lctx (and phase (txlift phase))))] #:default #'(syntax-local-phase-level))])
(elaborate #'litset lctx (txlift phase)))]
[litset [litset
(identifier? #'litset) (identifier? #'litset)
(elaborate #'litset #'litset #f)] (elaborate #'litset #'litset #'(syntax-local-phase-level))]
[_ [_
(raise-syntax-error #f "expected literal-set entry" ctx stx)])) (raise-syntax-error #f "expected literal-set entry" ctx stx)]))
(define (elaborate-litset litset lctx phase srcctx) ;; Conventions
;; phase is #f (not specified, means 0) or syntax (expr)
(for/list ([entry (literalset-literals litset)])
(list (datum->syntax lctx (car entry) srcctx)
(cadr entry)
(cond [(not (caddr entry)) phase]
[(not phase) (caddr entry)]
[else #`(phase+ #,(caddr entry) #,phase)]))))
;; returns (listof (cons Conventions (listof syntax))) ;; returns (listof (cons Conventions (listof syntax)))
(define (check-conventions-list stx ctx) (define (check-conventions-list stx ctx)

View File

@ -604,7 +604,55 @@ An Expectation is one of
;; ;;
(provide phase+) (provide phase+
check-literal
free-identifier=?/phases)
(define (phase+ a b) (define (phase+ a b)
(and (number? a) (number? b) (+ a b))) (and (number? a) (number? b) (+ a b)))
;; check-literal : id phase-level stx -> void
;; FIXME: change to normal 'error', if src gets stripped away
(define (check-literal id phase ctx)
(unless (identifier-binding id phase)
(raise-syntax-error #f "literal identifier has no binding" ctx id)))
;; free-identifier=?/phases : id phase-level id phase-level -> boolean
;; Determines whether x has the same binding at phase-level phase-x
;; that y has at phase-level y.
;; At least one of the identifiers MUST have a binding (module or lexical)
(define (free-identifier=?/phases x phase-x y phase-y)
(let ([base-phase (syntax-local-phase-level)])
(let ([bx (identifier-binding x (phase+ base-phase phase-x))]
[by (identifier-binding y (phase+ base-phase phase-y))])
(cond [(and (list? bx) (list? by))
(let ([modx (module-path-index-resolve (first bx))]
[namex (second bx)]
[phasex (fifth bx)]
[mody (module-path-index-resolve (first by))]
[namey (second by)]
[phasey (fifth by)])
(and (eq? modx mody) ;; resolved-module-paths are interned
(eq? namex namey)
(equal? phasex phasey)))]
[else
;; One must be lexical (can't be #f, since one must be bound)
;; lexically-bound names bound in only one phase; just compare
(free-identifier=? x y)]))))
;; ----
(provide begin-for-syntax/once)
;; (begin-for-syntax/once expr/phase1 ...)
;; evaluates in pass 2 of module/intdefs expansion
(define-syntax (begin-for-syntax/once stx)
(syntax-case stx ()
[(bfs/o e ...)
(cond [(list? (syntax-local-context))
#`(define-values ()
(begin (begin-for-syntax/once e ...)
(values)))]
[else
#'(let-syntax ([m (lambda _ (begin e ...) #'(void))])
(m))])]))

View File

@ -144,21 +144,50 @@
(define-syntax (define-literal-set stx) (define-syntax (define-literal-set stx)
(syntax-case stx () (syntax-case stx ()
[(define-literal-set name (lit ...)) [(define-literal-set name (lit ...))
(begin (let ([phase-of-definition (syntax-local-phase-level)])
(unless (identifier? #'name) (unless (identifier? #'name)
(raise-syntax-error #f "expected identifier" stx #'name)) (raise-syntax-error #f "expected identifier" stx #'name))
(with-txlifts/defs (let ([lits (check-literals-list/litset #'(lit ...) stx)])
(lambda () (with-syntax ([((internal external) ...) lits])
(let ([lits (check-literals-list #'(lit ...) stx)]) #`(begin
(with-syntax ([((internal external _) ...) lits] (define phase-of-literals
[(phase ...) (let ([phase-of-module-instantiation
(for/list ([lit lits]) ;; Hack to get enclosing module's base phase
(if (caddr lit) (variable-reference->phase (#%variable-reference))])
#`(quote-syntax #,(caddr lit)) (- phase-of-module-instantiation
#'(quote #f)))]) '#,(if (zero? phase-of-definition) 0 1))))
#'(define-syntax name (define-syntax name
(make-literalset (make-literalset
(list (list 'internal (quote-syntax external) phase) ...))))))))])) (list (list 'internal (quote-syntax external)) ...)
(quote-syntax phase-of-literals)))
(begin-for-syntax/once
(for ([x (syntax->list #'(external ...))])
(unless (identifier-binding x 0)
(raise-syntax-error #f "literal identifier has no binding"
(quote-syntax #,stx) x))))))))]))
#|
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)
|#
;; ---- ;; ----

View File

@ -10,7 +10,8 @@
(prefix-in t: (combine-in "base-types-extra.ss" "base-types.ss")) (only-in "colon.ss" :) (prefix-in t: (combine-in "base-types-extra.ss" "base-types.ss")) (only-in "colon.ss" :)
scheme/match scheme/match
(for-template scheme/base "base-types-extra.ss" "colon.ss") (for-template scheme/base "base-types-extra.ss" "colon.ss")
(for-template (prefix-in t: "base-types-extra.ss"))) (for-template (prefix-in t: "base-types-extra.ss")
(prefix-in t: (only-in "base-types.ss" Vectorof))))
(define-struct poly (name vars) #:prefab) (define-struct poly (name vars) #:prefab)