#lang at-exp scheme/base (require scheme/match scheme/list scribble/manual scribble/struct scribble/decode) (require (for-syntax scheme/base)) (provide BNF BNF-seq BNF-alt BNF-alt/close BNF-etc nonterm term BNF-var ABNF attr-decl attr-sel node-var attr-label optional BNF-group kleenestar kleeneplus kleenerange) (define spacer (make-element 'hspace (list " "))) (define equals (make-element 'tt (list spacer "::=" spacer))) (define set (make-element #f (list (make-element 'tt (list spacer)) 'larr (make-element 'tt (list spacer))))) (define alt (make-element 'tt (list spacer spacer "|" spacer spacer))) (define attr (make-element 'tt (list spacer spacer spacer spacer spacer))) (define (as-flow i) (make-flow (list (make-paragraph (list i))))) (define (interleave l spacer) (make-element #f (cons (car l) (apply append (map (lambda (i) (list spacer i)) (cdr l)))))) (define (column lhs op rhs) (list (as-flow spacer) (as-flow lhs) (as-flow op) (as-flow rhs))) (define (BNF . defns) (make-table #f (apply append (for/list ([defn defns]) (match defn [(list A p1 ps ...) (cons (column A equals p1) (for/list ([p ps]) (column " " alt p)))]))))) (define (ABNF . defns) (make-table #f (apply append (for/list ([defn defns]) (match defn [(list A (list p1 as1 ...) aps ...) (cons (column A equals p1) (append (for/list ([a1 as1]) (column " " attr a1)) (append-map (match-lambda [(list p as ...) (cons (column " " alt p) (for/list ([a as]) (column " " attr a)))]) aps)))]))))) (define-for-syntax (node-var? x) (let ([x (syntax->datum x)]) (or (integer? x) (symbol? x)))) (define-syntax (node-var stx) (syntax-case stx () [(_ name) (integer? (syntax->datum #'name)) (with-syntax ([var (datum->syntax #'name (string->symbol (format "$~a" (syntax->datum #'name))))]) #'(node-var var))] [(_ name) (identifier? #'name) (with-syntax ([var-name (symbol->string (syntax->datum #'name))]) #'(schemeidfont var-name))])) (define-syntax (attr-label stx) (syntax-case stx () [(_ attribute) (identifier? #'attribute) (with-syntax ([attribute-name (symbol->string (syntax->datum #'attribute))]) #'(make-element 'sf (list attribute-name)))])) (define-syntax (attr-sel stx) (syntax-case stx () [(_ name attribute) (and (node-var? #'name) (identifier? #'attribute)) #'(make-element #f (append (list (node-var name)) (list (schemekeywordfont ".")) (list (attr-label attribute))))])) (define-syntax (attr-decl stx) (syntax-case stx () [(_ name attribute expr) (and (node-var? #'name) (identifier? #'attribute)) #'(make-element #f (append (list (make-element 'tt (list spacer spacer spacer))) (list (attr-sel name attribute)) (list set) (list expr)))])) (define (BNF-seq . l) (if (null? l) "" (interleave l spacer))) (define (BNF-alt . l) (interleave l alt)) (define (BNF-alt/close . l) (interleave l " | ")) (define BNF-etc "...") (define (BNF-var . s) (make-element 'italic (decode-content s))) (define (nonterm #:sub [arg #f] . s) (if arg (make-element #f (append (list 'lang) (list (make-element 'italic (decode-content s))) (list 'rang) (list (make-element 'subscript (list arg))))) (make-element #f (append (list 'lang) (list (make-element 'italic (decode-content s))) (list 'rang))))) (define (term s) (schemevalfont (format "~v" s))) (define (optional . s) (make-element #f (append (list "[") (decode-content s) (list "]")))) (define (BNF-group . s) (make-element #f (append (list "{") (list (apply BNF-seq (decode-content s))) (list "}")))) (define (kleenestar . s) (make-element #f (append (decode-content s) (list "*")))) (define (kleeneplus . s) (make-element #f (append (decode-content s) (list (make-element 'superscript (list "+")))))) (define (kleenerange a b . s) (make-element #f (append (decode-content s) (list (make-element 'superscript (list (format "{~a,~a}" a b)))))))