Better Grammar
define-grammar
bettergrammar*
bnf:  add
bnf:  sub
typeset-grammar
bettergrammar*-diff
typeset-grammar-diff
7.8

Better Grammar

William J. Bowman <wjb@williamjbowman.com>

It’s like racketgrammar*, but better.

Specifically, scribble/bettergrammar is library for typesetting ‘racketgrammar*‘s with difference annotations, for when you are typesetting lots of grammars with small changes between them. It supports defining and reusing grammars, annotations for differences and changes between grammars, automatically computing the difference between two grammars (using ‘sexp-diff‘), and rendering grammar differences with both highlighting and annotations to emphasize what has changed between grammars.

syntax

(define-grammar maybe-literals maybe-datum-literals id [id clause-datum ...+] ...)

 
maybe-literals = 
  | (#:literals (id ...))
     
maybe-datum-literals = 
  | (#:datum-literals (id ...))
Defines id as a grammar to be typeset by typeset-grammar and typeset-grammar-diff. The [id clause-datum] ..., maybe-literals, and maybe-datum-literals are the same as specified by second form or bettergrammar*.

Does not render anything by on its own.

Must be used in definition context.

Examples:
> (require scribble/bettergrammar)
> (define-grammar stlc-grammar (e (λ (x) e) (e e) x))
> (define-grammar let-grammar
   #:literals (integer?)
   #:datum-literals (let)
   (e (integer? (let ([x e]) e))))

Example:
@bettergrammar*[stlc-grammar]
@bettergrammar*[let-grammar]

Renders like:

  e ::= (λ (x) e)
  | (e e)
  | x
  e ::= (integer? (let ([x e]) e))

Changed in version 1.4 of package scribble-bettergrammar-lib: Added #:literals, #:datum-literals support

syntax

(bettergrammar* maybe-literals
                maybe-datum-literals
                ([addid clause-datum ...+] ...)
                ([subid clause-datum ...+] ...)
                ([id clause-datum ...+] ...))
(bettergrammar* maybe-literals
                maybe-datum-literals
                [id clause-datum ...+] ...)
(bettergrammar* maybe-literals maybe-datum-literals id)
 
maybe-datum-literals = 
  | (#:datum-literals (id ...))
Like racketgrammar*, but supports typesetting a grammar with difference annotations for non-terminals, and typesetting grammars pre-defined using define-grammar. maybe-literals have the same interpretation as in racketgrammar*.

Identifiers included in maybe-datum-literals are not typeset as variables, nor as racket literals.

The addid clauses are typeset as additions to the grammar, using +::= instead of ::= to indicate the addition to an existing nonterminal.

The subid clauses are typeset as removed from the grammar, using -::= instead of ::= to indicate the addition to an existing nonterminal.

The id clauses are typeset as in racketgrammar*, but using ::= instead of = to define the nonterminal.

The form supports a second syntax that is identical to racketgrammar*, except the nonterminal is still separated from its productions using ::=.o

The third syntax enables typesetting a grammar previously defined by define-grammar. If the grammar was defined with literals or datum-literals, these are merged into any specified directly via maybe-literals or maybe-datum-literals.

All forms also support escapes in Scribble using unsyntax, and various Scribble element transformers, such as code:hilite.

Example:
@bettergrammar*[(e (λ (x) e) x (e e) 1)]

Renders like:
  e ::= (λ (x) e)
  | x
  | (e e)
  | 1

Example:
@bettergrammar*[
((e v))
()
((v natural ()))
]

Renders like:
  e +::= v
     
  v ::= natural
  | ()

Example:
@bettergrammar*[
(e (unsyntax @bnf:add{v}) (λ (x) e) x (e e) (unsyntax @bnf:sub{1}))
((unsyntax @bnf:add{v}) natural ())
]

Renders like:
  e ::= v
  | (λ (x) e)
  | x
  | (e e)
  | 1
     
  v ::= natural
  | ()

Example:
@bettergrammar*[
(e (code:hilite v) (λ (x) e) x (e e))
(v natural ())
]

Renders like:

  e ::= v
  | (λ (x) e)
  | x
  | (e e)
     
  v ::= natural
  | ()

Examples:
> (require scribble/bettergrammar)
> (define-grammar stlc-grammar (e (λ (x) e) (e e) x))

Example:
@bettergrammar*[stlc-grammar]
Renders like:
  e ::= (λ (x) e)
  | (e e)
  | x

Changed in version 1.2 of package scribble-bettergrammar-lib: Merged functionality from typeset-grammar-diff into this form.
Changed in version 1.4: Added #:literals, #:datum-literals support

procedure

(bnf:add datum)  string?

  datum : string?
Typesets datum as an addition to a bettergrammar*. In the default style, this means hilighting with a blue background and adding a + superscript. You can redefine this by redefining the bnf-add class in your style file.

As it works over decoded strings, it can also be used outside of bettergrammar*, although this interface may change.

Example:
@bettergrammar*[
(e (unsyntax (bnf:add "v")) (λ (x) e) x (e e))
((unsyntax (bnf:add "v")) natural ())
]

Renders like:
  e ::= v
  | (λ (x) e)
  | x
  | (e e)
     
  v ::= natural
  | ()

Example:
@bnf:add{natural}

Renders like:

natural

procedure

(bnf:sub datum)  string?

  datum : string?
Typesets datum as a removal from a bettergrammar*. In the default style, this means hilighting with a red background and adding a - superscript. You can redefine this by redefining the bnf-sub class in your style file.

As it works over decoded strings, it can also be used outside of bettergrammar*, although this interface may change.

Example:
@bettergrammar*[
(e (unsyntax (bnf:sub "v")) (λ (x) e) x (e e))
((unsyntax (bnf:sub "v")) natural ())
]

Renders like:
  e ::= v
  | (λ (x) e)
  | x
  | (e e)
     
  v ::= natural
  | ()

Example:
@bnf:sub{natural}

Renders like:

natural

syntax

(typeset-grammar id)

Typeset the grammar defined as id using bettergramar*. id must have been previously defined by define-grammar.

Example:
(define-grammar stlc-grammar (e (λ (x) e) (e e) x))

Example:
@typeset-grammar[stlc-grammar]

Renders like:
  e ::= (λ (x) e)
  | (e e)
  | x

NOTE: This form is deprecated; use bettergrammar*, instead. Functionality merged into bettergrammar*; exists for backwards compatibility and will eventually be removed.

Changed in version 1.2 of package scribble-bettergrammar-lib: Deprecated

syntax

(bettergrammar*-diff maybe-literals
                     maybe-datum-literals
                     maybe-include
                     maybe-exclude clause-spec clause-spec)
 
maybe-include = 
  | (#:include (id ...))
     
maybe-exclude = 
  | (#:include (id ...))
     
maybe-literals = 
  | (#:literals (id ...))
     
maybe-datum-literals = 
  | (#:datum-literals (id ...))
     
clause-spec = identifier?
  | (clauses ...)
Compute and typeset the differnce between two grammars. Either clause-spec can be a valid racketgrammar* spec, or an identifier previously defined by define-grammar

If the optional maybe-include is given, then only the nonterminals specified in (id ...) are included in the rendered grammar.

If the optional maybe-exclude is given, then any nonterminals specified in (id ...) are excluded from the rendered grammar.

If the optional maybe-literals is given, the literals are passed to bettergrammar*. If an identifier from define-grammar is used, the literal used in that definition will be merged with the maybe-literals.

If the optional maybe-datum-literals is given, the datum literals are passed to bettergrammar*. If an identifier from define-grammar is used, the datum literal used in that definition will be merged with the maybe-datum-literals.

Renders a new grammar where non-terminals and productions the old grammar that have been removed in new grammar are typeset like with bnf:sub, and non-terminals and productions that have been added in the new grammar are typeset like with bnf:add.

The diff algorithm may not preserve source location information used to typeset, so large productions may be collapsed to a single line. The diff should preserve paren-shape. Literals should be linked properly with racket.

Example:
(define-grammar stlc-grammar-v1
  (e (λ (x) e) (e e) x)
  (v (λ (x) e))
  (x name))
(define-grammar stlc-grammar-v2
  (e (e e) x v)
  (v (λ (x) e) natural ())
  (natural 0 (add1 natural)))

Example:
@bettergramar*-diff[stlc-grammar-v1 stlc-grammar-v2]

Renders like:
  e ::= (λ (x) e)
  | (e e)
  | x
  | v
     
  v ::= (λ (x) e)
  | natural
  | ()
     
  natural ::= 0
  | (add1 natural)
     
  x ::= name

Example:
@bettergramar*-diff[#:include (e) stlc-grammar-v1 stlc-grammar-v2]

Renders like:
  e ::= (λ (x) e)
  | (e e)
  | x
  | v

Example:
@bettergramar*-diff[#:exclude (e) stlc-grammar-v1 stlc-grammar-v2]

Renders like:
  v ::= (λ (x) e)
  | natural
  | ()
     
  natural ::= 0
  | (add1 natural)
     
  x ::= name

Example:
@bettergrammar*[(e (1 ...)
 
(3 ...))]
 
@bettergrammar*[(e (2 ...))]
 
@bettergrammar*-diff[
((e (1 ...)
 
(3 ...)))
 
((e (2 ...)))
]

Renders like:

  e ::= (1 ...)
  | (3 ...)
  e ::= (2 ...)
  e ::= (2 1 ...)
  | (3 ...)

Example:
@bettergrammar*-diff[
#:literals (integer?)
#:datum-literals (let)
((e integer? (let ([x e]) e)))
((e (let ([x v]) v)
 (v integer?)))
]

Renders like:
  e ::= integer?
  | (let ([x v e]) v e)
  | (v integer?)

Changed in version 1.1 of package scribble-bettergrammar-lib: Support for second, anonymous form.
Changed in version 1.2: Renamed from typeset-grammar-diff
Changed in version 1.3: Added #:include and #:exclude support.
Changed in version 1.4: Added #:literals, #:datum-literals support; enabled mixed-diff between clauses and defined grammars.

An alias for bettergrammar*-diff for backwards compatibility.

NOTE: This form is deprecated; use bettergrammar*-diff, instead. Renamed to bettergrammar*-diff for nicer interface; exists for backwards compatibility and will eventually be removed.

Changed in version 1.2 of package scribble-bettergrammar-lib: Made an alias; deprecated