|
|
|
#lang racket/base
|
|
|
|
|
|
|
|
|
|
|
|
(require racket/contract/base)
|
|
|
|
|
|
|
|
|
|
|
|
(provide
|
|
|
|
(struct-out terminal-derivation)
|
|
|
|
(struct-out nonterminal-derivation)
|
|
|
|
(contract-out
|
|
|
|
[parser-derivation? predicate/c]
|
|
|
|
[parser-derivation-first-terminal (-> parser-derivation? any/c)]
|
|
|
|
[parser-derivation-last-terminal (-> parser-derivation? any/c)]
|
|
|
|
[parser-derivation
|
|
|
|
(case->
|
|
|
|
(-> any/c terminal-derivation?)
|
|
|
|
(-> semantic-action? parser-derivation? #:rest (listof parser-derivation?)
|
|
|
|
nonterminal-derivation?))]
|
|
|
|
[parser-derivation->syntax (-> parser-derivation? syntax?)]
|
|
|
|
[parser-derivation->datum (-> parser-derivation? any/c)]))
|
|
|
|
|
|
|
|
|
|
|
|
(require racket/match
|
|
|
|
racket/sequence
|
|
|
|
racket/struct
|
|
|
|
rebellion/collection/vector
|
|
|
|
rebellion/private/static-name
|
|
|
|
yaragg/base/semantic-action
|
|
|
|
yaragg/base/token)
|
|
|
|
|
|
|
|
|
|
|
|
(module+ test
|
|
|
|
(require (submod "..")
|
|
|
|
racket/syntax-srcloc
|
|
|
|
rackunit))
|
|
|
|
|
|
|
|
|
|
|
|
;@----------------------------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
|
|
(define (parser-derivation? v)
|
|
|
|
(or (terminal-derivation? v) (nonterminal-derivation? v)))
|
|
|
|
|
|
|
|
|
|
|
|
;; A (Terminal-Derivation V) represents a terminal that was matched by the grammar. It contains the
|
|
|
|
;; value V of the (Token T V) that was matched.
|
|
|
|
(struct terminal-derivation (value) #:transparent)
|
|
|
|
|
|
|
|
|
|
|
|
;; A (Nonterminal-Derivation V A) represents a nonterminal that was matched by the grammar. It
|
|
|
|
;; contains the action of type (Semantic-Action A) of the production rule that matched, and an
|
|
|
|
;; immutable vector of subderivations
|
|
|
|
(struct nonterminal-derivation (action children)
|
|
|
|
|
|
|
|
#:guard
|
|
|
|
(let ([contract-guard
|
|
|
|
(struct-guard/c semantic-action? (sequence/c parser-derivation? #:min-count 1))])
|
|
|
|
(λ (action children name)
|
|
|
|
(let-values ([(action children) (contract-guard action children name)])
|
|
|
|
(values action (sequence->vector children)))))
|
|
|
|
|
|
|
|
#:transparent
|
|
|
|
#:property prop:custom-print-quotable 'never
|
|
|
|
#:methods gen:custom-write
|
|
|
|
[(define write-proc
|
|
|
|
(make-constructor-style-printer
|
|
|
|
(λ (_) 'nonterminal-derivation)
|
|
|
|
(λ (this)
|
|
|
|
(cons (nonterminal-derivation-action this)
|
|
|
|
(vector->list (nonterminal-derivation-children this))))))])
|
|
|
|
|
|
|
|
|
|
|
|
(define parser-derivation
|
|
|
|
(case-lambda
|
|
|
|
[(value) (terminal-derivation value)]
|
|
|
|
[(action first-child . children) (nonterminal-derivation action (cons first-child children))]))
|
|
|
|
|
|
|
|
|
|
|
|
(define (parser-derivation-first-terminal derivation)
|
|
|
|
(match derivation
|
|
|
|
[(terminal-derivation value) value]
|
|
|
|
[(nonterminal-derivation _ (vector first-child _ ...))
|
|
|
|
(parser-derivation-first-terminal first-child)]))
|
|
|
|
|
|
|
|
|
|
|
|
(define (parser-derivation-last-terminal derivation)
|
|
|
|
(match derivation
|
|
|
|
[(terminal-derivation value) value]
|
|
|
|
[(nonterminal-derivation _ (vector _ ... last-child))
|
|
|
|
(parser-derivation-last-terminal last-child)]))
|
|
|
|
|
|
|
|
|
|
|
|
(module+ test
|
|
|
|
(test-case (name-string parser-derivation-first-terminal)
|
|
|
|
|
|
|
|
(test-case "terminal"
|
|
|
|
(check-equal? (parser-derivation-first-terminal (terminal-derivation 1)) 1))
|
|
|
|
|
|
|
|
(test-case "nonterminal of terminals"
|
|
|
|
(define derivation
|
|
|
|
(parser-derivation
|
|
|
|
(label-action 'a)
|
|
|
|
(parser-derivation 1)
|
|
|
|
(parser-derivation 2)
|
|
|
|
(parser-derivation 3)))
|
|
|
|
(check-equal? (parser-derivation-first-terminal derivation) 1))
|
|
|
|
|
|
|
|
(test-case "nonterminal of nonterminals and terminals"
|
|
|
|
(define derivation
|
|
|
|
(parser-derivation
|
|
|
|
(label-action 'a)
|
|
|
|
(parser-derivation (label-action 'b) (parser-derivation 1))
|
|
|
|
(parser-derivation 2)
|
|
|
|
(parser-derivation 3)))
|
|
|
|
(check-equal? (parser-derivation-first-terminal derivation) 1)))
|
|
|
|
|
|
|
|
(test-case (name-string parser-derivation-last-terminal)
|
|
|
|
|
|
|
|
(test-case "terminal"
|
|
|
|
(check-equal? (parser-derivation-last-terminal (terminal-derivation 1)) 1))
|
|
|
|
|
|
|
|
(test-case "nonterminal of terminals"
|
|
|
|
(define derivation
|
|
|
|
(parser-derivation
|
|
|
|
(label-action 'a)
|
|
|
|
(parser-derivation 1)
|
|
|
|
(parser-derivation 2)
|
|
|
|
(parser-derivation 3)))
|
|
|
|
(check-equal? (parser-derivation-last-terminal derivation) 3))
|
|
|
|
|
|
|
|
(test-case "nonterminal of nonterminals and terminals"
|
|
|
|
(define derivation
|
|
|
|
(parser-derivation
|
|
|
|
(label-action 'a)
|
|
|
|
(parser-derivation 1)
|
|
|
|
(parser-derivation 2)
|
|
|
|
(parser-derivation (label-action 'b) (parser-derivation 3))))
|
|
|
|
(check-equal? (parser-derivation-last-terminal derivation) 3))))
|
|
|
|
|
|
|
|
|
|
|
|
(define (parser-derivation->syntax derivation)
|
|
|
|
(define first-token (parser-derivation-first-terminal derivation))
|
|
|
|
(define last-token (parser-derivation-last-terminal derivation))
|
|
|
|
|
|
|
|
(define (->splice derivation)
|
|
|
|
(match derivation
|
|
|
|
[(terminal-derivation t) (list (syntax-token->syntax t))]
|
|
|
|
[(nonterminal-derivation action children)
|
|
|
|
(define children-syntaxes
|
|
|
|
(for*/list ([child (in-vector children)]
|
|
|
|
[spliced-child (in-list (->splice child))])
|
|
|
|
spliced-child))
|
|
|
|
(semantic-action-build-syntax-splice
|
|
|
|
action children-syntaxes #:first-token first-token #:last-token last-token)]))
|
|
|
|
|
|
|
|
(match (->splice derivation)
|
|
|
|
[(list stx) stx]))
|
|
|
|
|
|
|
|
|
|
|
|
(define (parser-derivation->datum derivation)
|
|
|
|
|
|
|
|
(define (->splice derivation)
|
|
|
|
(match derivation
|
|
|
|
[(terminal-derivation t) (list t)]
|
|
|
|
[(nonterminal-derivation action children)
|
|
|
|
(define child-data
|
|
|
|
(for*/list ([child (in-vector children)]
|
|
|
|
[datum (in-list (->splice child))])
|
|
|
|
datum))
|
|
|
|
(semantic-action-build-datum-splice action child-data)]))
|
|
|
|
|
|
|
|
(match (->splice derivation)
|
|
|
|
[(list datum) datum]))
|
|
|
|
|
|
|
|
|
|
|
|
(module+ test
|
|
|
|
(test-case (name-string parser-derivation->datum)
|
|
|
|
|
|
|
|
(test-case "datum terminals"
|
|
|
|
(define derivation (parser-derivation 'a))
|
|
|
|
(check-equal? (parser-derivation->datum derivation) 'a))
|
|
|
|
|
|
|
|
(test-case "datum labels"
|
|
|
|
(define derivation
|
|
|
|
(parser-derivation (label-action 'a)
|
|
|
|
(parser-derivation 'b)
|
|
|
|
(parser-derivation 'c)
|
|
|
|
(parser-derivation 'd)))
|
|
|
|
(check-equal? (parser-derivation->datum derivation) '(a b c d)))
|
|
|
|
|
|
|
|
(test-case "datum cuts"
|
|
|
|
(define derivation
|
|
|
|
(parser-derivation (label-action 'a)
|
|
|
|
(parser-derivation cut-action (parser-derivation 'b))
|
|
|
|
(parser-derivation 'c)
|
|
|
|
(parser-derivation cut-action (parser-derivation 'd))))
|
|
|
|
(check-equal? (parser-derivation->datum derivation) '(a c)))
|
|
|
|
|
|
|
|
(test-case "datum splices"
|
|
|
|
(define derivation
|
|
|
|
(parser-derivation (label-action 'a)
|
|
|
|
(parser-derivation 'b)
|
|
|
|
(parser-derivation splice-action
|
|
|
|
(parser-derivation 'c1)
|
|
|
|
(parser-derivation 'c2)
|
|
|
|
(parser-derivation 'c3))
|
|
|
|
(parser-derivation 'd)))
|
|
|
|
(check-equal? (parser-derivation->datum derivation) '(a b c1 c2 c3 d)))
|
|
|
|
|
|
|
|
(test-case "datum pairs"
|
|
|
|
(define derivation
|
|
|
|
(parser-derivation (build-pair-action) (parser-derivation 'b) (parser-derivation 'c)))
|
|
|
|
(check-equal? (parser-derivation->datum derivation) '(b . c)))
|
|
|
|
|
|
|
|
(test-case "datum lists"
|
|
|
|
(define derivation
|
|
|
|
(parser-derivation (build-list-action)
|
|
|
|
(parser-derivation 'b)
|
|
|
|
(parser-derivation 'c)
|
|
|
|
(parser-derivation 'd)))
|
|
|
|
(check-equal? (parser-derivation->datum derivation) '(b c d)))
|
|
|
|
|
|
|
|
(test-case "improper datum lists"
|
|
|
|
(define derivation
|
|
|
|
(parser-derivation (build-improper-list-action)
|
|
|
|
(parser-derivation 'b)
|
|
|
|
(parser-derivation 'c)
|
|
|
|
(parser-derivation 'd)))
|
|
|
|
(check-equal? (parser-derivation->datum derivation) '(b c . d)))
|
|
|
|
|
|
|
|
(test-case "datum vectors"
|
|
|
|
(define derivation
|
|
|
|
(parser-derivation (build-vector-action)
|
|
|
|
(parser-derivation 'b)
|
|
|
|
(parser-derivation 'c)
|
|
|
|
(parser-derivation 'd)))
|
|
|
|
(check-equal? (parser-derivation->datum derivation) #(b c d)))
|
|
|
|
|
|
|
|
(test-case "datum hashes"
|
|
|
|
(define derivation
|
|
|
|
(parser-derivation (build-hash-action)
|
|
|
|
(parser-derivation 'a)
|
|
|
|
(parser-derivation 1)
|
|
|
|
(parser-derivation 'b)
|
|
|
|
(parser-derivation 2)))
|
|
|
|
(check-equal? (parser-derivation->datum derivation) (hash 'a 1 'b 2)))
|
|
|
|
|
|
|
|
(test-case "datum hashes with duplicate keys"
|
|
|
|
(define derivation
|
|
|
|
(parser-derivation (build-hash-action)
|
|
|
|
(parser-derivation 'a)
|
|
|
|
(parser-derivation 1)
|
|
|
|
(parser-derivation 'a)
|
|
|
|
(parser-derivation 2)))
|
|
|
|
(check-exn exn:fail:contract? (λ () (parser-derivation->datum derivation))))
|
|
|
|
|
|
|
|
(test-case "datum boxes"
|
|
|
|
(define derivation
|
|
|
|
(parser-derivation (build-box-action) (parser-derivation 'a)))
|
|
|
|
(check-equal? (parser-derivation->datum derivation) (box-immutable 'a)))
|
|
|
|
|
|
|
|
(test-case "prefab datum structs"
|
|
|
|
(define derivation
|
|
|
|
(parser-derivation (build-prefab-struct-action 'point)
|
|
|
|
(parser-derivation 1)
|
|
|
|
(parser-derivation 2)))
|
|
|
|
(check-equal? (parser-derivation->datum derivation) #s(point 1 2))))
|
|
|
|
|
|
|
|
(test-case (name-string parser-derivation->syntax)
|
|
|
|
|
|
|
|
(test-case "syntax terminals"
|
|
|
|
(define derivation (parser-derivation (syntax-token 'a #:position 1 #:span 1)))
|
|
|
|
(define actual (parser-derivation->syntax derivation))
|
|
|
|
(check-equal? (syntax->datum actual) 'a)
|
|
|
|
(check-equal? (syntax-srcloc actual) (srcloc #false #false #false 1 1)))
|
|
|
|
|
|
|
|
(test-case "syntax labels"
|
|
|
|
(define derivation
|
|
|
|
(parser-derivation
|
|
|
|
(label-action 'a)
|
|
|
|
(parser-derivation (syntax-token 'b #:position 1 #:span 1))
|
|
|
|
(parser-derivation (syntax-token 'c #:position 2 #:span 1))
|
|
|
|
(parser-derivation (syntax-token 'd #:position 3 #:span 1))))
|
|
|
|
(define actual (parser-derivation->syntax derivation))
|
|
|
|
(check-equal? (syntax->datum actual) '(a b c d))
|
|
|
|
(check-equal? (syntax-srcloc actual) (srcloc #false #false #false 1 3)))
|
|
|
|
|
|
|
|
(test-case "syntax cuts"
|
|
|
|
(define derivation
|
|
|
|
(parser-derivation
|
|
|
|
(label-action 'a)
|
|
|
|
(parser-derivation cut-action (parser-derivation (syntax-token 'b #:position 1 #:span 1)))
|
|
|
|
(parser-derivation (syntax-token 'c #:position 2 #:span 1))
|
|
|
|
(parser-derivation cut-action (parser-derivation (syntax-token 'd #:position 3 #:span 1)))))
|
|
|
|
(define actual (parser-derivation->syntax derivation))
|
|
|
|
(check-equal? (syntax->datum actual) '(a c))
|
|
|
|
(check-equal? (syntax-srcloc actual) (srcloc #false #false #false 1 3)))
|
|
|
|
|
|
|
|
(test-case "syntax splices"
|
|
|
|
(define derivation
|
|
|
|
(parser-derivation
|
|
|
|
(label-action 'a)
|
|
|
|
(parser-derivation (syntax-token 'b #:position 1 #:span 1))
|
|
|
|
(parser-derivation splice-action
|
|
|
|
(parser-derivation (syntax-token 'c1 #:position 2 #:span 1))
|
|
|
|
(parser-derivation (syntax-token 'c2 #:position 3 #:span 1))
|
|
|
|
(parser-derivation (syntax-token 'c3 #:position 4 #:span 1)))
|
|
|
|
(parser-derivation (syntax-token 'd #:position 5 #:span 1))))
|
|
|
|
(define actual (parser-derivation->syntax derivation))
|
|
|
|
(check-equal? (syntax->datum actual) '(a b c1 c2 c3 d))
|
|
|
|
(check-equal? (syntax-srcloc actual) (srcloc #false #false #false 1 5)))
|
|
|
|
|
|
|
|
(test-case "syntax pairs"
|
|
|
|
(define derivation
|
|
|
|
(parser-derivation
|
|
|
|
(build-pair-action)
|
|
|
|
(parser-derivation (syntax-token 'b #:position 1 #:span 1))
|
|
|
|
(parser-derivation (syntax-token 'c #:position 2 #:span 1))))
|
|
|
|
(define actual (parser-derivation->syntax derivation))
|
|
|
|
(check-equal? (syntax->datum actual) '(b . c))
|
|
|
|
(check-equal? (syntax-srcloc actual) (srcloc #false #false #false 1 2)))
|
|
|
|
|
|
|
|
(test-case "syntax lists"
|
|
|
|
(define derivation
|
|
|
|
(parser-derivation
|
|
|
|
(build-list-action)
|
|
|
|
(parser-derivation (syntax-token 'b #:position 1 #:span 1))
|
|
|
|
(parser-derivation (syntax-token 'c #:position 2 #:span 1))
|
|
|
|
(parser-derivation (syntax-token 'd #:position 3 #:span 1))))
|
|
|
|
(define actual (parser-derivation->syntax derivation))
|
|
|
|
(check-equal? (syntax->datum actual) '(b c d))
|
|
|
|
(check-equal? (syntax-srcloc actual) (srcloc #false #false #false 1 3)))
|
|
|
|
|
|
|
|
(test-case "improper syntax lists"
|
|
|
|
(define derivation
|
|
|
|
(parser-derivation
|
|
|
|
(build-improper-list-action)
|
|
|
|
(parser-derivation (syntax-token 'b #:position 1 #:span 1))
|
|
|
|
(parser-derivation (syntax-token 'c #:position 2 #:span 1))
|
|
|
|
(parser-derivation (syntax-token 'd #:position 3 #:span 1))))
|
|
|
|
(define actual (parser-derivation->syntax derivation))
|
|
|
|
(check-equal? (syntax->datum actual) '(b c . d))
|
|
|
|
(check-equal? (syntax-srcloc actual) (srcloc #false #false #false 1 3)))
|
|
|
|
|
|
|
|
(test-case "syntax vectors"
|
|
|
|
(define derivation
|
|
|
|
(parser-derivation
|
|
|
|
(build-vector-action)
|
|
|
|
(parser-derivation (syntax-token 'b #:position 1 #:span 1))
|
|
|
|
(parser-derivation (syntax-token 'c #:position 2 #:span 1))
|
|
|
|
(parser-derivation (syntax-token 'd #:position 3 #:span 1))))
|
|
|
|
(define actual (parser-derivation->syntax derivation))
|
|
|
|
(check-equal? (syntax->datum actual) #(b c d))
|
|
|
|
(check-equal? (syntax-srcloc actual) (srcloc #false #false #false 1 3)))
|
|
|
|
|
|
|
|
(test-case "syntax hash tables"
|
|
|
|
(define derivation
|
|
|
|
(parser-derivation
|
|
|
|
(build-hash-action)
|
|
|
|
(parser-derivation (syntax-token 'a #:position 1 #:span 1))
|
|
|
|
(parser-derivation (syntax-token 1 #:position 2 #:span 1))
|
|
|
|
(parser-derivation (syntax-token 'b #:position 3 #:span 1))
|
|
|
|
(parser-derivation (syntax-token 2 #:position 4 #:span 1))))
|
|
|
|
(define actual (parser-derivation->syntax derivation))
|
|
|
|
(check-equal? (syntax->datum actual) (hash 'a 1 'b 2))
|
|
|
|
(check-equal? (syntax-srcloc actual) (srcloc #false #false #false 1 4)))
|
|
|
|
|
|
|
|
(test-case "syntax hash tables with duplicate keys"
|
|
|
|
(define derivation
|
|
|
|
(parser-derivation
|
|
|
|
(build-hash-action)
|
|
|
|
(parser-derivation (syntax-token 'a #:position 1 #:span 1))
|
|
|
|
(parser-derivation (syntax-token 1 #:position 2 #:span 1))
|
|
|
|
(parser-derivation (syntax-token 'a #:position 3 #:span 1))
|
|
|
|
(parser-derivation (syntax-token 2 #:position 4 #:span 1))))
|
|
|
|
(check-exn exn:fail:contract? (λ () (parser-derivation->syntax derivation))))
|
|
|
|
|
|
|
|
(test-case "syntax boxes"
|
|
|
|
(define derivation
|
|
|
|
(parser-derivation
|
|
|
|
(build-box-action) (parser-derivation (syntax-token 'a #:position 1 #:span 1))))
|
|
|
|
(define actual (parser-derivation->syntax derivation))
|
|
|
|
(check-equal? (syntax->datum actual) (box-immutable 'a))
|
|
|
|
(check-equal? (syntax-srcloc actual) (srcloc #false #false #false 1 1)))
|
|
|
|
|
|
|
|
(test-case "prefab syntax structs"
|
|
|
|
(define derivation
|
|
|
|
(parser-derivation
|
|
|
|
(build-prefab-struct-action 'point)
|
|
|
|
(parser-derivation (syntax-token 5 #:position 1 #:span 1))
|
|
|
|
(parser-derivation (syntax-token 10 #:position 2 #:span 1))))
|
|
|
|
(define actual (parser-derivation->syntax derivation))
|
|
|
|
(check-equal? (syntax->datum actual) #s(point 5 10))
|
|
|
|
(check-equal? (syntax-srcloc actual) (srcloc #false #false #false 1 2)))))
|