|
| 1 | +#lang at-exp racket/base |
| 2 | + |
| 3 | +(require scribble/base scribble/core scribble/decode scribble/basic scribble/html-properties |
| 4 | + racket/list "ragged.rkt" racket/string) |
| 5 | + |
| 6 | +(provide bnf prod-link prod-ref) |
| 7 | + |
| 8 | +(define (prod-tag grammar prod-name) (list 'bnf-prod (list grammar prod-name))) |
| 9 | +(define (prod-ref name) (list "‹" name "›")) |
| 10 | +(define (prod-link grammar name) |
| 11 | + (elemref (prod-tag grammar name) (prod-ref name))) |
| 12 | + |
| 13 | + |
| 14 | +(define (render grammar parsed) |
| 15 | + (define-values (constants prods) (partition constant? parsed)) |
| 16 | + (define names (map (λ(c) (list (lhs-id-val (constant-lhs c)) (pattern-lit-val (constant-val c)))) constants)) |
| 17 | + (define (meta s) |
| 18 | + (elem s #:style (make-style #f (list (attributes '((class . "bnf-meta"))))))) |
| 19 | + (define (lit s) |
| 20 | + (elem s #:style (make-style #f (list (attributes '((class . "bnf-lit"))))))) |
| 21 | + (define (eps) |
| 22 | + (elem "ε" #:style (make-style #f (list (attributes '((class . "bnf-eps"))))))) |
| 23 | + (define (unknown-lit s) |
| 24 | + (elem s #:style (make-style #f (list (attributes '((class . "bnf-lit bnf-unknown"))))))) |
| 25 | + (define rule-name (make-parameter #f)) |
| 26 | + (define (render-help p #:start [start #f]) |
| 27 | + (cond |
| 28 | + [(pattern-seq? p) |
| 29 | + (add-between (map render-help (pattern-seq-vals p)) " ")] |
| 30 | + [(pattern-maybe? p) |
| 31 | + (list (meta "[") (render-help (pattern-maybe-val p)) (meta "]"))] |
| 32 | + [(pattern-repeat? p) |
| 33 | + (list (meta "(") (render-help (pattern-repeat-val p)) (meta ")") |
| 34 | + (if (= 0 (pattern-repeat-min p)) (meta "*") (meta "+")))] |
| 35 | + [(pattern-choice? p) |
| 36 | + (define choices (pattern-choice-vals p)) |
| 37 | + (define pat-breaks |
| 38 | + (filter (λ(i) i) |
| 39 | + (for/list |
| 40 | + ([c1 choices] [c2 (rest choices)]) |
| 41 | + (if (> (pos-line (pattern-start c2)) (pos-line (pattern-start c1))) c2 #f)))) |
| 42 | + (define breaks |
| 43 | + (if (> (pos-line (pattern-start (first choices))) (pos-line (pattern-start p))) |
| 44 | + (cons (first choices) pat-breaks) |
| 45 | + pat-breaks)) |
| 46 | + (define indent (string-append "\n" (make-string (+ 2 (* 2 (string-length (rule-name)))) #\space))) |
| 47 | + (define translated |
| 48 | + (map (λ(c) |
| 49 | + (list (if (member c breaks) indent "") |
| 50 | + (meta " | ") |
| 51 | + (render-help c))) choices)) |
| 52 | + (if (and start (> (pos-line (pattern-start (first choices))) (pos-line start))) |
| 53 | + (cons indent (flatten translated)) |
| 54 | + (drop (flatten translated) 2))] |
| 55 | + [(pattern-token? p) |
| 56 | + (define tok (assoc (pattern-token-val p) names)) |
| 57 | + (cond |
| 58 | + [tok (lit (second tok))] |
| 59 | + [else (unknown-lit (pattern-token-val p))])] |
| 60 | + [(pattern-lit? p) |
| 61 | + (lit (pattern-lit-val p))] |
| 62 | + [(pattern-id? p) |
| 63 | + (prod-link grammar (pattern-id-val p))] |
| 64 | + [(pattern-epsilon? p) |
| 65 | + (eps)] |
| 66 | + [else |
| 67 | + (printf "Unknown prod: ~a" p)])) |
| 68 | + |
| 69 | + (nested #:style (make-style 'code-inset (list (attributes '((style . "white-space: pre;"))))) |
| 70 | + (add-between |
| 71 | + (for/list [(p prods)] |
| 72 | + (parameterize ([rule-name (lhs-id-val (rule-lhs p))]) |
| 73 | + (list (elemtag (prod-tag grammar (rule-name)) |
| 74 | + (elem (prod-ref (rule-name)) |
| 75 | + #:style (make-style #f (list (attributes '((class . "bnf-rule"))))))) |
| 76 | + (meta ":") |
| 77 | + " " |
| 78 | + (render-help (rule-pattern p) #:start (rule-start p))))) |
| 79 | + "\n") |
| 80 | + ) |
| 81 | + ) |
| 82 | + |
| 83 | +(define (bnf grammar . stx) |
| 84 | + (define text (string-join stx "")) |
| 85 | + (define text-port (open-input-string text)) |
| 86 | + (port-count-lines! text-port) |
| 87 | + (define parsed (grammar-parser (tokenize text-port))) |
| 88 | + (render grammar parsed) |
| 89 | + ) |
0 commit comments