BNF マニアになってどーすんだ、て話。
ABNF の Pygments lexer がないので自分で書いちゃった。こんなね:
1 # -*- coding: utf-8 -*-
2 """
3 pygments.lexers.grammar_notation
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
6 Lexers for parser generators.
7
8 :copyright: Copyright 2006-2015 by the Pygments team, see AUTHORS.
9 :license: BSD, see LICENSE for details.
10 """
11
12 import re
13
14 from pygments.lexer import RegexLexer, DelegatingLexer, \
15 include, bygroups, using, words
16 from pygments.token import Punctuation, Other, Text, Comment, Operator, \
17 Keyword, Name, String, Number, Whitespace, Literal
18
19 __all__ = ['AbnfLexer']
20
21
22 # EBNF shold be moved here, i think.
23
24 class AbnfLexer(RegexLexer):
25 """
26 Lexer for `IETF 7405 ABNF
27 <http://www.ietf.org/rfc/rfc7405.txt>`_
28 (Updates `5234 <http://www.ietf.org/rfc/rfc5234.txt>`_)
29 grammars.
30
31 .. versionadded:: 2.1
32 """
33
34 name = 'ABNF'
35 aliases = ['abnf']
36 filenames = ['*.abnf']
37 mimetypes = ['text/x-abnf']
38
39 _core_rules = (
40 'ALPHA', 'BIT', 'CHAR', 'CR', 'CRLF', 'CTL', 'DIGIT',
41 'DQUOTE', 'HEXDIG', 'HTAB', 'LF', 'LWSP', 'OCTET',
42 'SP', 'VCHAR', 'WSP',)
43
44 def nonterminal_cb(self, match):
45 txt = match.group(0)
46 if txt in self._core_rules:
47 # Strictly speaking, these are not keyword but
48 # is called `Core Rule'.
49 yield match.start(), Keyword, txt
50 else:
51 yield match.start(), Name.Class, txt
52
53 tokens = {
54 'root': [
55 # comment
56 (r';.*$', Comment.Single),
57
58 # quoted
59 (r'(%[si])?"', Literal, 'quoted-termination'),
60
61 # binary (but i have never seen...)
62 (r'%b[01]+\-[01]+\b', Literal), # range
63 (r'%b[01]+(\.[01]+)*\b', Literal), # concat
64
65 # decimal
66 (r'%d[0-9]+\-[0-9]+\b', Literal), # range
67 (r'%d[0-9]+(\.[0-9]+)*\b', Literal), # concat
68
69 # hexadecimal
70 (r'%x[0-9a-fA-F]+\-[0-9a-fA-F]+\b', Literal), # range
71 (r'%x[0-9a-fA-F]+(\.[0-9a-fA-F]+)*\b', Literal), # concat
72
73 # repetition (<a>*<b>element) including nRule
74 (r'\b[0-9]+\*[0-9]+', Operator),
75 (r'\b[0-9]+\*', Operator),
76 (r'\b[0-9]+', Operator),
77 (r'\*', Operator),
78
79 # nonterminals (ALPHA *(ALPHA / DIGIT / "-"))
80 (r'[a-zA-Z][a-zA-Z0-9-]+\b', nonterminal_cb),
81
82 # operators
83 (r'(=/|=|/)', Operator),
84
85 # punctuation
86 (r'[\[\]()]', Punctuation),
87
88 # fallback
89 (r'.', Text),
90 ],
91 'quoted-termination': [
92 # double quote itself in this state, it is as '%x22'.
93 (r'"', Literal, '#pop'),
94 (r'.', Literal),
95 ]
96 }
使うとこんなです:
で、EBNF のは既にあるし、Sphinx で執筆するなら Pygments に BNF 的 lexer は不要なわけだから、ほぼ大部分で満足出来るだろう、とは思うんだけれどもね、「歴史的説明」なんかをしたい人が、オリジナル BNF の lexer を必要としたりするかもなぁ、というのがやっぱちょっと引っかかって。
オリジナルの、つまり Algol60 (Algol58) 向けに作られた本来の BNF はさすがに制約強過ぎて、現代的なプログラミング言語の文法記述に使われることはなく、ほとんどが何かしらの拡張を施した亜種を使うわけね。その中で比較的オリジナル BNF の趣を残したまま拡張し、なおかつ機械処理しやすそうなものがあったら、その lexer だけ書いちゃえば、前述「歴史の説明をしたい人々」も満足するんじゃなかろうか、と。まぁそう思ったのね。
Routing Backus-Naur Form (RBNF)。
やり過ぎだってのもう…:
1 2.1. Rule Definitions
2
3 No semantics should be assumed from special characters used in rule names.
4 For example, it would be wrong to assume that a rule carries
5 a decimal number because the rule name begins or ends with the letter
6 "d". However, individual specifications MAY choose to assign rule
7 names in any way that makes the human interpretation of the rule
8 easier.
9
10 2.1.1. Rule Name Delimitation
11
12 All rule names are enclosed by angle brackets ("<" and ">").
13 Rule names MAY include any printable characters, but MUST NOT include tabs
14 or line feeds/breaks.
15
16 Example:
17 <Path Message>
つまり「ルール名はアナーキーでオッケー」と言っている。そして大なり小なり自身が含まれてもいい、と「暗に」言ってる。くせに、エスケープの方法が定義されてない。
そしてこれのおかげで「機械処理はほぼ絶望的」に至る。割り切ればいいけどね、ルール名に大なり小なりを許さない、とかね。
オリジナルBNFの制約を取っ払う、という心意気は結構だけど、もちっと繊細にやってくれんかのぉ。まぁ人間が読めればいいんだから、てのはわかるけれどもさ。
どーすっかなぁ…。書くなら割り切ったバージョンを書くしかないんだけど。