自由を求めすぎて首をしめた、の巻(RBNF)

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の制約を取っ払う、という心意気は結構だけど、もちっと繊細にやってくれんかのぉ。まぁ人間が読めればいいんだから、てのはわかるけれどもさ。

どーすっかなぁ…。書くなら割り切ったバージョンを書くしかないんだけど。