ebnf 2.1.0 → 2.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +7 -3
- data/VERSION +1 -1
- data/bin/ebnf +5 -5
- data/etc/doap.ttl +10 -6
- data/etc/ebnf.html +22 -117
- data/etc/ebnf.ll1.rb +1 -1
- data/etc/ebnf.peg.rb +1 -1
- data/lib/ebnf/base.rb +3 -2
- data/lib/ebnf/ll1/lexer.rb +2 -50
- data/lib/ebnf/ll1/scanner.rb +0 -1
- data/lib/ebnf/native.rb +2 -2
- data/lib/ebnf/peg/parser.rb +40 -18
- data/lib/ebnf/peg/rule.rb +47 -11
- data/lib/ebnf/unescape.rb +62 -0
- data/lib/ebnf/writer.rb +68 -37
- data/lib/ebnf.rb +1 -0
- metadata +60 -17
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: '08b2411d5c4d34425d00259126e0d6f55c086b2c60c74e8d3ddc6a099a60ec5e'
|
4
|
+
data.tar.gz: d8185780e437d3db9c2644d62f51d497b25be130d20b79d63e3101e222180408
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b972788258b8261d6e59a093268f31be74d3db13535b0db63199aae9ed36b93602d6b8034439152ce62361797eb3990b747d33a55551baa01bd2d1a9aed6bf6f
|
7
|
+
data.tar.gz: cc3b0bb1ecd8c0f0e7135989e96bb826d35f1406ecc3482e88a00f77aaf1df0c8ab5c6f98cb11b37c2c83f77b2d9d762f227d20a0bec44a27cbe48616c31f4a4
|
data/README.md
CHANGED
@@ -3,8 +3,9 @@
|
|
3
3
|
[EBNF][] parser and generic parser generator.
|
4
4
|
|
5
5
|
[![Gem Version](https://badge.fury.io/rb/ebnf.png)](https://badge.fury.io/rb/ebnf)
|
6
|
-
[![Build Status](https://
|
7
|
-
[![Coverage Status](https://coveralls.io/repos/dryruby/ebnf/badge.svg)](https://coveralls.io/r/dryruby/ebnf)
|
6
|
+
[![Build Status](https://github.com/dryruby/ebnf/workflows/CI/badge.svg?branch=develop)](https://github.com/dryruby/ebnf/actions?query=workflow%3ACI)
|
7
|
+
[![Coverage Status](https://coveralls.io/repos/dryruby/ebnf/badge.svg?branch=develop)](https://coveralls.io/r/dryruby/ebnf?branch=develop)
|
8
|
+
[![Gitter chat](https://badges.gitter.im/ruby-rdf/rdf.png)](https://gitter.im/ruby-rdf/rdf)
|
8
9
|
|
9
10
|
## Description
|
10
11
|
This is a [Ruby][] implementation of an [EBNF][] and [BNF][] parser and parser generator.
|
@@ -101,6 +102,8 @@ On a parsing failure, and exception is raised with information that may be usefu
|
|
101
102
|
The [EBNF][] variant used here is based on [W3C](https://w3.org/) [EBNF][] (see {file:etc/ebnf.ebnf EBNF grammar}) as defined in the
|
102
103
|
[XML 1.0 recommendation](https://www.w3.org/TR/REC-xml/), with minor extensions:
|
103
104
|
|
105
|
+
Note that the grammar includes an optional `[identifer]` in front of rule names, which can be in conflict with the `RANGE` terminal. It is typically not a problem, but if it comes up, try parsing with the `native` parser, add comments or sequences to disambiguate. EBNF does not have beginning of line checks as all whitespace is treated the same, so the common practice of identifying each rule inherently leads to such ambiguity.
|
106
|
+
|
104
107
|
The character set for EBNF is UTF-8.
|
105
108
|
|
106
109
|
The general form of a rule is:
|
@@ -259,7 +262,8 @@ This repository uses [Git Flow](https://github.com/nvie/gitflow) to mange develo
|
|
259
262
|
list in the the `README`. Alphabetical order applies.
|
260
263
|
* Do note that in order for us to merge any non-trivial changes (as a rule
|
261
264
|
of thumb, additions larger than about 15 lines of code), we need an
|
262
|
-
explicit [public domain dedication][PDD] on record from you
|
265
|
+
explicit [public domain dedication][PDD] on record from you,
|
266
|
+
which you will be asked to agree to on the first commit to a repo within the organization.
|
263
267
|
|
264
268
|
## License
|
265
269
|
This is free and unencumbered public domain software. For more information,
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
2.
|
1
|
+
2.2.0
|
data/bin/ebnf
CHANGED
@@ -34,7 +34,7 @@ OPT_ARGS = [
|
|
34
34
|
["--prefix", "-p", GetoptLong::REQUIRED_ARGUMENT,"Prefix to use when generating Turtle"],
|
35
35
|
["--progress", "-v", GetoptLong::NO_ARGUMENT, "Detail on execution"],
|
36
36
|
["--renumber", GetoptLong::NO_ARGUMENT, "Renumber parsed reules"],
|
37
|
-
["--validate", GetoptLong::NO_ARGUMENT, "Validate grammar"],
|
37
|
+
["--validate", GetoptLong::NO_ARGUMENT, "Validate grammar and any generated HTML"],
|
38
38
|
["--help", "-?", GetoptLong::NO_ARGUMENT, "This message"]
|
39
39
|
]
|
40
40
|
def usage
|
@@ -67,7 +67,7 @@ opts.each do |opt, arg|
|
|
67
67
|
end
|
68
68
|
options[:format] = arg.to_sym
|
69
69
|
when '--format'
|
70
|
-
unless %w(abnf abnfh ebnf html isoebnf isoebnfh rb sxp).include?(arg)
|
70
|
+
unless %w(abnf abnfh ebnf html isoebnf isoebnfh rb sxp ttl).include?(arg)
|
71
71
|
STDERR.puts("unrecognized output format #{arg}")
|
72
72
|
usage
|
73
73
|
end
|
@@ -99,11 +99,11 @@ ebnf.renumber! if options[:renumber]
|
|
99
99
|
|
100
100
|
res = case options[:output_format]
|
101
101
|
when :abnf then ebnf.to_s(format: :abnf)
|
102
|
-
when :abnfh then ebnf.to_html(format: :abnf)
|
102
|
+
when :abnfh then ebnf.to_html(format: :abnf, validate: options[:validate])
|
103
103
|
when :ebnf then ebnf.to_s
|
104
|
-
when :html then ebnf.to_html
|
104
|
+
when :html then ebnf.to_html(validate: options[:validate])
|
105
105
|
when :isoebnf then ebnf.to_s(format: :isoebnf)
|
106
|
-
when :isoebnfh then ebnf.to_html(format: :isoebnf)
|
106
|
+
when :isoebnfh then ebnf.to_html(format: :isoebnf, validate: options[:validate])
|
107
107
|
when :sxp then ebnf.to_sxp
|
108
108
|
when :ttl then ebnf.to_ttl(options[:prefix], options[:namespace])
|
109
109
|
when :rb then ebnf.to_ruby(out, grammarFile: ARGV[0], **options)
|
data/etc/doap.ttl
CHANGED
@@ -12,11 +12,18 @@
|
|
12
12
|
doap:name "ebnf" ;
|
13
13
|
doap:homepage <https://github.com/dryruby/ebnf> ;
|
14
14
|
doap:license <https://unlicense.org/1.0/> ;
|
15
|
-
doap:shortdesc "EBNF parser and parser generator"@en ;
|
16
|
-
doap:description "EBNF is a Ruby parser for W3C EBNF and a parser generator for
|
15
|
+
doap:shortdesc "EBNF parser and parser generator in Ruby."@en ;
|
16
|
+
doap:description "EBNF is a Ruby parser for W3C EBNF and a parser generator for PEG and LL(1). Also includes parsing modes for ISO EBNF and ABNF."@en ;
|
17
17
|
doap:created "2011-08-29"^^xsd:date ;
|
18
18
|
doap:programming-language "Ruby" ;
|
19
|
-
doap:implements <http://dbpedia.org/resource/Compiler-compiler
|
19
|
+
doap:implements <http://dbpedia.org/resource/Compiler-compiler>,
|
20
|
+
<https://en.wikipedia.org/wiki/LL_parser>,
|
21
|
+
<https://en.wikipedia.org/wiki/Parsing_expression_grammar>,
|
22
|
+
<https://pdos.csail.mit.edu/~baford/packrat/thesis/>,
|
23
|
+
<https://www.w3.org/TR/REC-xml/#sec-notation>,
|
24
|
+
<https://en.wikipedia.org/wiki/Backus–Naur_form>,
|
25
|
+
<https://www.iso.org/standard/26153.html>,
|
26
|
+
<https://www.rfc-editor.org/rfc/rfc5234>;
|
20
27
|
doap:category <http://dbpedia.org/resource/Resource_Description_Framework>,
|
21
28
|
<http://dbpedia.org/resource/Ruby_(programming_language)> ;
|
22
29
|
doap:download-page <> ;
|
@@ -27,7 +34,4 @@
|
|
27
34
|
doap:maintainer <https://greggkellogg.net/foaf#me> ;
|
28
35
|
doap:documenter <https://greggkellogg.net/foaf#me> ;
|
29
36
|
foaf:maker <https://greggkellogg.net/foaf#me> ;
|
30
|
-
dc:title "ebnf" ;
|
31
|
-
dc:description "EBNF is a Ruby parser for W3C EBNF and a parser generator for compliant LL(1) grammars."@en ;
|
32
|
-
dc:date "2011-08-29"^^xsd:date ;
|
33
37
|
dc:creator <https://greggkellogg.net/foaf#me> .
|
data/etc/ebnf.html
CHANGED
@@ -11,7 +11,7 @@
|
|
11
11
|
<td>[2]</td>
|
12
12
|
<td><code>declaration</code></td>
|
13
13
|
<td>::=</td>
|
14
|
-
<td
|
14
|
+
<td>"@terminals" <code>|</code> <a href="#grammar-production-pass">pass</a></td>
|
15
15
|
</tr>
|
16
16
|
<tr id="grammar-production-rule">
|
17
17
|
<td>[3]</td>
|
@@ -53,61 +53,24 @@
|
|
53
53
|
<td>[9]</td>
|
54
54
|
<td><code>primary</code></td>
|
55
55
|
<td>::=</td>
|
56
|
-
<td><a href="#grammar-production-HEX">HEX</a
|
57
|
-
</tr>
|
58
|
-
<tr>
|
59
|
-
<td>[9]</td>
|
60
|
-
<td><code></code></td>
|
61
|
-
<td>|</td>
|
62
|
-
<td><a href="#grammar-production-SYMBOL">SYMBOL</a></td>
|
63
|
-
</tr>
|
64
|
-
<tr>
|
65
|
-
<td>[9]</td>
|
66
|
-
<td><code></code></td>
|
67
|
-
<td>|</td>
|
68
|
-
<td><a href="#grammar-production-O_RANGE">O_RANGE</a></td>
|
69
|
-
</tr>
|
70
|
-
<tr>
|
71
|
-
<td>[9]</td>
|
72
|
-
<td><code></code></td>
|
73
|
-
<td>|</td>
|
74
|
-
<td><a href="#grammar-production-RANGE">RANGE</a></td>
|
75
|
-
</tr>
|
76
|
-
<tr>
|
77
|
-
<td>[9]</td>
|
78
|
-
<td><code></code></td>
|
79
|
-
<td>|</td>
|
80
|
-
<td><a href="#grammar-production-STRING1">STRING1</a></td>
|
81
|
-
</tr>
|
82
|
-
<tr>
|
83
|
-
<td>[9]</td>
|
84
|
-
<td><code></code></td>
|
85
|
-
<td>|</td>
|
86
|
-
<td><a href="#grammar-production-STRING2">STRING2</a></td>
|
87
|
-
</tr>
|
88
|
-
<tr>
|
89
|
-
<td>[9]</td>
|
90
|
-
<td><code></code></td>
|
91
|
-
<td>|</td>
|
92
|
-
<td><code>(</code> "<code class="grammar-literal">(</code>" <a href="#grammar-production-expression">expression</a> "<code class="grammar-literal">)</code>"<code>)</code> </td>
|
56
|
+
<td><a href="#grammar-production-HEX">HEX</a> <code>|</code> <a href="#grammar-production-SYMBOL">SYMBOL</a> <code>|</code> <a href="#grammar-production-O_RANGE">O_RANGE</a> <code>|</code> <a href="#grammar-production-RANGE">RANGE</a> <code>|</code> <a href="#grammar-production-STRING1">STRING1</a> <code>|</code> <a href="#grammar-production-STRING2">STRING2</a> <code>|</code> <code>(</code> "<code class="grammar-literal">(</code>" <a href="#grammar-production-expression">expression</a> "<code class="grammar-literal">)</code>"<code>)</code> </td>
|
93
57
|
</tr>
|
94
58
|
<tr id="grammar-production-pass">
|
95
59
|
<td>[10]</td>
|
96
60
|
<td><code>pass</code></td>
|
97
61
|
<td>::=</td>
|
98
|
-
<td
|
62
|
+
<td>"@pass" <a href="#grammar-production-expression">expression</a></td>
|
99
63
|
</tr>
|
100
|
-
<tr
|
101
|
-
<td>@terminals</td>
|
102
|
-
<td><code></code></td>
|
64
|
+
<tr>
|
65
|
+
<td colspan=2>@terminals</td>
|
103
66
|
<td></td>
|
104
|
-
<td><strong
|
67
|
+
<td><strong># Productions for terminals</strong></td>
|
105
68
|
</tr>
|
106
69
|
<tr id="grammar-production-LHS">
|
107
70
|
<td>[11]</td>
|
108
71
|
<td><code>LHS</code></td>
|
109
72
|
<td>::=</td>
|
110
|
-
<td><code>(</code> "<code class="grammar-literal">[</code>" <a href="#grammar-production-SYMBOL">SYMBOL</a> "<code class="grammar-literal">]</code>" <code class="grammar-char-escape"><abbr title="space">#x20</abbr></code><code>+</code> <code>)</code> <code>?</code> <a href="#grammar-production-SYMBOL">SYMBOL</a> <code class="grammar-char-escape"><abbr title="space">#x20</abbr></code><code>*</code>
|
73
|
+
<td><code>(</code> "<code class="grammar-literal">[</code>" <a href="#grammar-production-SYMBOL">SYMBOL</a> "<code class="grammar-literal">]</code>" <code class="grammar-char-escape"><abbr title="space">#x20</abbr></code><code>+</code> <code>)</code> <code>?</code> <a href="#grammar-production-SYMBOL">SYMBOL</a> <code class="grammar-char-escape"><abbr title="space">#x20</abbr></code><code>*</code> "::="</td>
|
111
74
|
</tr>
|
112
75
|
<tr id="grammar-production-SYMBOL">
|
113
76
|
<td>[12]</td>
|
@@ -119,91 +82,37 @@
|
|
119
82
|
<td>[13]</td>
|
120
83
|
<td><code>HEX</code></td>
|
121
84
|
<td>::=</td>
|
122
|
-
<td
|
85
|
+
<td>"#x" <code>(</code> <code>[</code> <code class="grammar-literal">a-f</code><code>]</code> <code>|</code> <code>[</code> <code class="grammar-literal">A-F</code><code>]</code> <code>|</code> <code>[</code> <code class="grammar-literal">0-9</code><code>]</code> <code>)</code> <code>+</code> </td>
|
123
86
|
</tr>
|
124
87
|
<tr id="grammar-production-RANGE">
|
125
88
|
<td>[14]</td>
|
126
89
|
<td><code>RANGE</code></td>
|
127
90
|
<td>::=</td>
|
128
|
-
<td>"<code class="grammar-literal">[</code>"</td>
|
129
|
-
</tr>
|
130
|
-
<tr id="grammar-production-">
|
131
|
-
<td>[14]</td>
|
132
|
-
<td><code></code></td>
|
133
|
-
<td></td>
|
134
|
-
<td><code>(</code> <code>(</code> <a href="#grammar-production-R_CHAR">R_CHAR</a> "<code class="grammar-literal">-</code>" <a href="#grammar-production-R_CHAR">R_CHAR</a><code>)</code><code>(</code> <a href="#grammar-production-HEX">HEX</a> "<code class="grammar-literal">-</code>" <a href="#grammar-production-HEX">HEX</a><code>)</code> <code>|</code> <a href="#grammar-production-R_CHAR">R_CHAR</a> <code>|</code> <a href="#grammar-production-HEX">HEX</a><code>)</code> <code>+</code></td>
|
135
|
-
</tr>
|
136
|
-
<tr id="grammar-production-">
|
137
|
-
<td>[14]</td>
|
138
|
-
<td><code></code></td>
|
139
|
-
<td></td>
|
140
|
-
<td>"<code class="grammar-literal">-</code>"<code>?</code></td>
|
141
|
-
</tr>
|
142
|
-
<tr id="grammar-production-">
|
143
|
-
<td>[14]</td>
|
144
|
-
<td><code></code></td>
|
145
|
-
<td></td>
|
146
|
-
<td><code>(</code> "<code class="grammar-literal">]</code>" <code>-</code> <a href="#grammar-production-LHS">LHS</a><code>)</code> </td>
|
91
|
+
<td>"<code class="grammar-literal">[</code>" <code>(</code> <code>(</code> <a href="#grammar-production-R_CHAR">R_CHAR</a> "<code class="grammar-literal">-</code>" <a href="#grammar-production-R_CHAR">R_CHAR</a><code>)</code> <code>|</code> <code>(</code> <a href="#grammar-production-HEX">HEX</a> "<code class="grammar-literal">-</code>" <a href="#grammar-production-HEX">HEX</a><code>)</code> <code>|</code> <a href="#grammar-production-R_CHAR">R_CHAR</a> <code>|</code> <a href="#grammar-production-HEX">HEX</a><code>)</code> <code>+</code> "<code class="grammar-literal">-</code>"<code>?</code> <code>(</code> "<code class="grammar-literal">]</code>" <code>-</code> <a href="#grammar-production-LHS">LHS</a><code>)</code> </td>
|
147
92
|
</tr>
|
148
93
|
<tr id="grammar-production-O_RANGE">
|
149
94
|
<td>[15]</td>
|
150
95
|
<td><code>O_RANGE</code></td>
|
151
96
|
<td>::=</td>
|
152
|
-
<td>"
|
153
|
-
</tr>
|
154
|
-
<tr id="grammar-production-">
|
155
|
-
<td>[15]</td>
|
156
|
-
<td><code></code></td>
|
157
|
-
<td></td>
|
158
|
-
<td><code>(</code> <code>(</code> <a href="#grammar-production-R_CHAR">R_CHAR</a> "<code class="grammar-literal">-</code>" <a href="#grammar-production-R_CHAR">R_CHAR</a><code>)</code><code>(</code> <a href="#grammar-production-HEX">HEX</a> "<code class="grammar-literal">-</code>" <a href="#grammar-production-HEX">HEX</a><code>)</code> <code>|</code> <a href="#grammar-production-R_CHAR">R_CHAR</a> <code>|</code> <a href="#grammar-production-HEX">HEX</a><code>)</code> <code>+</code></td>
|
159
|
-
</tr>
|
160
|
-
<tr id="grammar-production-">
|
161
|
-
<td>[15]</td>
|
162
|
-
<td><code></code></td>
|
163
|
-
<td></td>
|
164
|
-
<td>"<code class="grammar-literal">-</code>"<code>?</code></td>
|
165
|
-
</tr>
|
166
|
-
<tr id="grammar-production-">
|
167
|
-
<td>[15]</td>
|
168
|
-
<td><code></code></td>
|
169
|
-
<td></td>
|
170
|
-
<td>"<code class="grammar-literal">]</code>"</td>
|
97
|
+
<td>"[^" <code>(</code> <code>(</code> <a href="#grammar-production-R_CHAR">R_CHAR</a> "<code class="grammar-literal">-</code>" <a href="#grammar-production-R_CHAR">R_CHAR</a><code>)</code> <code>|</code> <code>(</code> <a href="#grammar-production-HEX">HEX</a> "<code class="grammar-literal">-</code>" <a href="#grammar-production-HEX">HEX</a><code>)</code> <code>|</code> <a href="#grammar-production-R_CHAR">R_CHAR</a> <code>|</code> <a href="#grammar-production-HEX">HEX</a><code>)</code> <code>+</code> "<code class="grammar-literal">-</code>"<code>?</code> "<code class="grammar-literal">]</code>"</td>
|
171
98
|
</tr>
|
172
99
|
<tr id="grammar-production-STRING1">
|
173
100
|
<td>[16]</td>
|
174
101
|
<td><code>STRING1</code></td>
|
175
102
|
<td>::=</td>
|
176
|
-
<td>'<code class="grammar-literal"
|
103
|
+
<td>'<code class="grammar-literal">"</code>' <code>(</code> <a href="#grammar-production-CHAR">CHAR</a> <code>-</code> '<code class="grammar-literal">"</code>'<code>)</code> <code>*</code> '<code class="grammar-literal">"</code>'</td>
|
177
104
|
</tr>
|
178
105
|
<tr id="grammar-production-STRING2">
|
179
106
|
<td>[17]</td>
|
180
107
|
<td><code>STRING2</code></td>
|
181
108
|
<td>::=</td>
|
182
|
-
<td>"<code class="grammar-literal"
|
109
|
+
<td>"<code class="grammar-literal">'</code>" <code>(</code> <a href="#grammar-production-CHAR">CHAR</a> <code>-</code> "<code class="grammar-literal">'</code>"<code>)</code> <code>*</code> "<code class="grammar-literal">'</code>"</td>
|
183
110
|
</tr>
|
184
111
|
<tr id="grammar-production-CHAR">
|
185
112
|
<td>[18]</td>
|
186
113
|
<td><code>CHAR</code></td>
|
187
114
|
<td>::=</td>
|
188
|
-
<td><code>[</code> <code class="grammar-char-escape"><abbr title="horizontal tab">#x09</abbr></code><code class="grammar-char-escape"><abbr title="new line">#x0A</abbr></code><code class="grammar-char-escape"><abbr title="carriage return">#x0D</abbr></code><code>]</code></td>
|
189
|
-
</tr>
|
190
|
-
<tr>
|
191
|
-
<td>[18]</td>
|
192
|
-
<td><code></code></td>
|
193
|
-
<td>|</td>
|
194
|
-
<td><code>[</code> <code class="grammar-char-escape"><abbr title="space">#x20</abbr></code><code class="grammar-literal">-</code><code class="grammar-char-escape"><abbr title="unicode ''">#xD7FF</abbr></code><code>]</code></td>
|
195
|
-
</tr>
|
196
|
-
<tr>
|
197
|
-
<td>[18]</td>
|
198
|
-
<td><code></code></td>
|
199
|
-
<td>|</td>
|
200
|
-
<td><code>[</code> <code class="grammar-char-escape"><abbr title="unicode ''">#xE000</abbr></code><code class="grammar-literal">-</code><code class="grammar-char-escape"><abbr title="unicode '�'">#xFFFD</abbr></code><code>]</code></td>
|
201
|
-
</tr>
|
202
|
-
<tr>
|
203
|
-
<td>[18]</td>
|
204
|
-
<td><code></code></td>
|
205
|
-
<td>|</td>
|
206
|
-
<td><code>[</code> <code class="grammar-char-escape"><abbr title="unicode '𐀀'">#x00010000</abbr></code><code class="grammar-literal">-</code><code class="grammar-char-escape"><abbr title="unicode ''">#x0010FFFF</abbr></code><code>]</code> </td>
|
115
|
+
<td><code>[</code> <code class="grammar-char-escape"><abbr title="horizontal tab">#x09</abbr></code><code class="grammar-char-escape"><abbr title="new line">#x0A</abbr></code><code class="grammar-char-escape"><abbr title="carriage return">#x0D</abbr></code><code>]</code> <code>|</code> <code>[</code> <code class="grammar-char-escape"><abbr title="space">#x20</abbr></code><code class="grammar-literal">-</code><code class="grammar-char-escape"><abbr title="unicode 'Reserved'">#xD7FF</abbr></code><code>]</code> <code>|</code> <code>[</code> <code class="grammar-char-escape"><abbr title="unicode 'Private-use'">#xE000</abbr></code><code class="grammar-literal">-</code><code class="grammar-char-escape"><abbr title="unicode 'Graphic'">#xFFFD</abbr></code><code>]</code> <code>|</code> <code>[</code> <code class="grammar-char-escape"><abbr title="unicode 'Graphic'">#x00010000</abbr></code><code class="grammar-literal">-</code><code class="grammar-char-escape"><abbr title="unicode 'Noncharacter'">#x0010FFFF</abbr></code><code>]</code> </td>
|
207
116
|
</tr>
|
208
117
|
<tr id="grammar-production-R_CHAR">
|
209
118
|
<td>[19]</td>
|
@@ -224,28 +133,24 @@
|
|
224
133
|
<td><code>[</code> <code class="grammar-char-escape"><abbr title="horizontal tab">#x09</abbr></code><code class="grammar-char-escape"><abbr title="new line">#x0A</abbr></code><code class="grammar-char-escape"><abbr title="carriage return">#x0D</abbr></code><code class="grammar-char-escape"><abbr title="space">#x20</abbr></code><code>]</code></td>
|
225
134
|
</tr>
|
226
135
|
<tr>
|
227
|
-
<td
|
228
|
-
<td><code></code></td>
|
136
|
+
<td colspan=2></td>
|
229
137
|
<td>|</td>
|
230
|
-
<td><code>(</code> <code>(</code> <code>(</code> "<code class="grammar-literal">#</code>" <code>-</code>
|
138
|
+
<td><code>(</code> <code>(</code> <code>(</code> "<code class="grammar-literal">#</code>" <code>-</code> "#x"<code>)</code> <code>|</code> "//"<code>)</code> <code>[</code> <code class="grammar-literal">^</code><code class="grammar-char-escape"><abbr title="new line">#x0A</abbr></code><code class="grammar-char-escape"><abbr title="carriage return">#x0D</abbr></code><code>]</code> <code>*</code> <code>)</code></td>
|
231
139
|
</tr>
|
232
140
|
<tr>
|
233
|
-
<td
|
234
|
-
<td><code></code></td>
|
141
|
+
<td colspan=2></td>
|
235
142
|
<td>|</td>
|
236
|
-
<td><code>(</code>
|
143
|
+
<td><code>(</code> "/*" <code>(</code> <code>(</code> "<code class="grammar-literal">*</code>" <code>[</code> <code class="grammar-literal">^/</code><code>]</code> <code>)</code> <code>?</code> <code>|</code> <code>[</code> <code class="grammar-literal">^*</code><code>]</code> <code>)</code> <code>*</code> "*/"<code>)</code></td>
|
237
144
|
</tr>
|
238
145
|
<tr>
|
239
|
-
<td
|
240
|
-
<td><code></code></td>
|
146
|
+
<td colspan=2></td>
|
241
147
|
<td>|</td>
|
242
|
-
<td><code>(</code>
|
148
|
+
<td><code>(</code> "(*" <code>(</code> <code>(</code> "<code class="grammar-literal">*</code>" <code>[</code> <code class="grammar-literal">^)</code><code>]</code> <code>)</code> <code>?</code> <code>|</code> <code>[</code> <code class="grammar-literal">^*</code><code>]</code> <code>)</code> <code>*</code> "*)"<code>)</code> </td>
|
243
149
|
</tr>
|
244
|
-
<tr
|
245
|
-
<td>@pass</td>
|
246
|
-
<td><code></code></td>
|
247
|
-
<td></td>
|
150
|
+
<tr>
|
151
|
+
<td colspan=2>@pass</td>
|
248
152
|
<td></td>
|
153
|
+
<td><a href="#grammar-production-PASS">PASS</a></td>
|
249
154
|
</tr>
|
250
155
|
</tbody>
|
251
156
|
</table>
|
data/etc/ebnf.ll1.rb
CHANGED
data/etc/ebnf.peg.rb
CHANGED
data/lib/ebnf/base.rb
CHANGED
@@ -220,9 +220,10 @@ module EBNF
|
|
220
220
|
# Output formatted EBNF as HTML
|
221
221
|
#
|
222
222
|
# @param [:abnf, :ebnf, :isoebnf] format (:ebnf)
|
223
|
+
# @param [Boolean] validate (false) validate generated HTML.
|
223
224
|
# @return [String]
|
224
|
-
def to_html(format: :ebnf)
|
225
|
-
Writer.html(*ast, format: format)
|
225
|
+
def to_html(format: :ebnf, validate: false)
|
226
|
+
Writer.html(*ast, format: format, validate: validate)
|
226
227
|
end
|
227
228
|
|
228
229
|
##
|
data/lib/ebnf/ll1/lexer.rb
CHANGED
@@ -32,60 +32,12 @@ module EBNF::LL1
|
|
32
32
|
# @see https://en.wikipedia.org/wiki/Lexical_analysis
|
33
33
|
class Lexer
|
34
34
|
include Enumerable
|
35
|
-
|
36
|
-
ESCAPE_CHARS = {
|
37
|
-
'\\t' => "\t", # \u0009 (tab)
|
38
|
-
'\\n' => "\n", # \u000A (line feed)
|
39
|
-
'\\r' => "\r", # \u000D (carriage return)
|
40
|
-
'\\b' => "\b", # \u0008 (backspace)
|
41
|
-
'\\f' => "\f", # \u000C (form feed)
|
42
|
-
'\\"' => '"', # \u0022 (quotation mark, double quote mark)
|
43
|
-
"\\'" => '\'', # \u0027 (apostrophe-quote, single quote mark)
|
44
|
-
'\\\\' => '\\' # \u005C (backslash)
|
45
|
-
}.freeze
|
46
|
-
ESCAPE_CHAR4 = /\\u(?:[0-9A-Fa-f]{4,4})/u.freeze # \uXXXX
|
47
|
-
ESCAPE_CHAR8 = /\\U(?:[0-9A-Fa-f]{8,8})/u.freeze # \UXXXXXXXX
|
48
|
-
ECHAR = /\\./u.freeze # More liberal unescaping
|
49
|
-
UCHAR = /#{ESCAPE_CHAR4}|#{ESCAPE_CHAR8}/n.freeze
|
35
|
+
include ::EBNF::Unescape
|
50
36
|
|
51
37
|
##
|
52
38
|
# @return [Regexp] defines whitespace, including comments, otherwise whitespace must be explicit in terminals
|
53
39
|
attr_reader :whitespace
|
54
40
|
|
55
|
-
##
|
56
|
-
# Returns a copy of the given `input` string with all `\uXXXX` and
|
57
|
-
# `\UXXXXXXXX` Unicode codepoint escape sequences replaced with their
|
58
|
-
# unescaped UTF-8 character counterparts.
|
59
|
-
#
|
60
|
-
# @param [String] string
|
61
|
-
# @return [String]
|
62
|
-
# @see https://www.w3.org/TR/rdf-sparql-query/#codepointEscape
|
63
|
-
def self.unescape_codepoints(string)
|
64
|
-
string = string.dup
|
65
|
-
string.force_encoding(Encoding::ASCII_8BIT) if string.respond_to?(:force_encoding)
|
66
|
-
|
67
|
-
# Decode \uXXXX and \UXXXXXXXX code points:
|
68
|
-
string = string.gsub(UCHAR) do |c|
|
69
|
-
s = [(c[2..-1]).hex].pack('U*')
|
70
|
-
s.respond_to?(:force_encoding) ? s.force_encoding(Encoding::ASCII_8BIT) : s
|
71
|
-
end
|
72
|
-
|
73
|
-
string.force_encoding(Encoding::UTF_8) if string.respond_to?(:force_encoding)
|
74
|
-
string
|
75
|
-
end
|
76
|
-
|
77
|
-
##
|
78
|
-
# Returns a copy of the given `input` string with all string escape
|
79
|
-
# sequences (e.g. `\n` and `\t`) replaced with their unescaped UTF-8
|
80
|
-
# character counterparts.
|
81
|
-
#
|
82
|
-
# @param [String] input
|
83
|
-
# @return [String]
|
84
|
-
# @see https://www.w3.org/TR/rdf-sparql-query/#grammarEscapes
|
85
|
-
def self.unescape_string(input)
|
86
|
-
input.gsub(ECHAR) { |escaped| ESCAPE_CHARS[escaped] || escaped[1..-1]}
|
87
|
-
end
|
88
|
-
|
89
41
|
##
|
90
42
|
# Tokenizes the given `input` string or stream.
|
91
43
|
#
|
@@ -338,7 +290,7 @@ module EBNF::LL1
|
|
338
290
|
# @return [String]
|
339
291
|
def unescape(string)
|
340
292
|
if @options[:unescape]
|
341
|
-
|
293
|
+
EBNF::Unescape.unescape(string)
|
342
294
|
else
|
343
295
|
string
|
344
296
|
end
|
data/lib/ebnf/ll1/scanner.rb
CHANGED
data/lib/ebnf/native.rb
CHANGED
@@ -287,10 +287,10 @@ module EBNF
|
|
287
287
|
case m = s[0,1]
|
288
288
|
when '"', "'" # STRING1 or STRING2
|
289
289
|
l, s = s[1..-1].split(m.rstrip, 2)
|
290
|
-
[
|
290
|
+
[Unescape.unescape_string(l), s]
|
291
291
|
when '[' # RANGE, O_RANGE
|
292
292
|
l, s = s[1..-1].split(/(?<=[^\\])\]/, 2)
|
293
|
-
[[:range,
|
293
|
+
[[:range, Unescape.unescape_string(l)], s]
|
294
294
|
when '#' # HEX
|
295
295
|
s.match(/(#x\h+)(.*)$/)
|
296
296
|
l, s = $1, $2
|
data/lib/ebnf/peg/parser.rb
CHANGED
@@ -55,6 +55,7 @@ module EBNF::PEG
|
|
55
55
|
def production_handlers; (@production_handlers ||= {}); end
|
56
56
|
def terminal_handlers; (@terminal_handlers ||= {}); end
|
57
57
|
def terminal_regexps; (@terminal_regexps ||= {}); end
|
58
|
+
def terminal_options; (@terminal_options ||= {}); end
|
58
59
|
|
59
60
|
##
|
60
61
|
# Defines the pattern for a terminal node and a block to be invoked
|
@@ -72,9 +73,6 @@ module EBNF::PEG
|
|
72
73
|
# defaults to the expression defined in the associated rule.
|
73
74
|
# If unset, the terminal rule is used for matching.
|
74
75
|
# @param [Hash] options
|
75
|
-
# @option options [Hash{String => String}] :map ({})
|
76
|
-
# A mapping from terminals, in lower-case form, to
|
77
|
-
# their canonical value
|
78
76
|
# @option options [Boolean] :unescape
|
79
77
|
# Cause strings and codepoints to be unescaped.
|
80
78
|
# @yield [value, prod]
|
@@ -88,6 +86,7 @@ module EBNF::PEG
|
|
88
86
|
def terminal(term, regexp = nil, **options, &block)
|
89
87
|
terminal_regexps[term] = regexp if regexp
|
90
88
|
terminal_handlers[term] = block if block_given?
|
89
|
+
terminal_options[term] = options.freeze
|
91
90
|
end
|
92
91
|
|
93
92
|
##
|
@@ -102,6 +101,8 @@ module EBNF::PEG
|
|
102
101
|
# Options which are returned from {Parser#onStart}.
|
103
102
|
# @option options [Boolean] :as_hash (false)
|
104
103
|
# If the production is a `seq`, causes the value to be represented as a single hash, rather than an array of individual hashes for each sub-production. Note that this is not always advisable due to the possibility of repeated productions within the sequence.
|
104
|
+
# @option options[:upper, :lower] :insensitive_strings
|
105
|
+
# Perform case-insensitive match of strings not defined as terminals, and map to either upper or lower case.
|
105
106
|
# @yield [data, block]
|
106
107
|
# @yieldparam [Hash] data
|
107
108
|
# A Hash defined for the current production, during :start
|
@@ -184,6 +185,8 @@ module EBNF::PEG
|
|
184
185
|
# @option options[Integer] :high_water passed to lexer
|
185
186
|
# @option options [Logger] :logger for errors/progress/debug.
|
186
187
|
# @option options[Integer] :low_water passed to lexer
|
188
|
+
# @option options[Boolean] :seq_hash (false)
|
189
|
+
# If `true`, sets the default for the value sent to a production handler that is for a `seq` to a hash composed of the flattened consitutent hashes that are otherwise provided.
|
187
190
|
# @option options [Symbol, Regexp] :whitespace
|
188
191
|
# Symbol of whitespace rule (defaults to `@pass`), or a regular expression
|
189
192
|
# for eating whitespace between non-terminal rules (strongly encouraged).
|
@@ -197,6 +200,7 @@ module EBNF::PEG
|
|
197
200
|
# @raise [Exception] Raises exceptions for parsing errors
|
198
201
|
# or errors raised during processing callbacks. Internal
|
199
202
|
# errors are raised using {Error}.
|
203
|
+
# @todo FIXME implement seq_hash
|
200
204
|
def parse(input = nil, start = nil, rules = nil, **options, &block)
|
201
205
|
start ||= options[:start]
|
202
206
|
rules ||= options[:rules] || []
|
@@ -269,7 +273,8 @@ module EBNF::PEG
|
|
269
273
|
# @param [String] message Error string
|
270
274
|
# @param [Hash{Symbol => Object}] options
|
271
275
|
# @option options [URI, #to_s] :production
|
272
|
-
# @option options [
|
276
|
+
# @option options [Boolean] :raise abort furhter processing
|
277
|
+
# @option options [Array] :backtrace state where error occured
|
273
278
|
# @see #debug
|
274
279
|
def error(node, message, **options)
|
275
280
|
lineno = options[:lineno] || (scanner.lineno if scanner)
|
@@ -282,7 +287,11 @@ module EBNF::PEG
|
|
282
287
|
@recovering = true
|
283
288
|
debug(node, m, level: 3, **options)
|
284
289
|
if options[:raise] || @options[:validate]
|
285
|
-
raise Error.new(m,
|
290
|
+
raise Error.new(m,
|
291
|
+
lineno: lineno,
|
292
|
+
rest: options[:rest],
|
293
|
+
production: options[:production],
|
294
|
+
backtrace: options[:backtrace])
|
286
295
|
end
|
287
296
|
end
|
288
297
|
|
@@ -365,25 +374,27 @@ module EBNF::PEG
|
|
365
374
|
@productions << prod
|
366
375
|
debug("#{prod}(:start)", "",
|
367
376
|
lineno: (scanner.lineno if scanner),
|
368
|
-
pos: (scanner.pos if scanner)
|
369
|
-
|
377
|
+
pos: (scanner.pos if scanner)
|
378
|
+
) do
|
379
|
+
"#{prod}, pos: #{scanner ? scanner.pos : '?'}, rest: #{scanner ? scanner.rest[0..20].inspect : '?'}"
|
380
|
+
end
|
370
381
|
if handler
|
371
382
|
# Create a new production data element, potentially allowing handler
|
372
383
|
# to customize before pushing on the @prod_data stack
|
373
|
-
data = {}
|
384
|
+
data = {_production: prod}
|
374
385
|
begin
|
375
386
|
self.class.eval_with_binding(self) {
|
376
387
|
handler.call(data, @parse_callback)
|
377
388
|
}
|
378
389
|
rescue ArgumentError, Error => e
|
379
|
-
error("start", "#{e.class}: #{e.message}", production: prod)
|
390
|
+
error("start", "#{e.class}: #{e.message}", production: prod, backtrace: e.backtrace)
|
380
391
|
@recovering = false
|
381
392
|
end
|
382
393
|
@prod_data << data
|
383
394
|
elsif self.class.production_handlers[prod]
|
384
395
|
# Make sure we push as many was we pop, even if there is no
|
385
396
|
# explicit start handler
|
386
|
-
@prod_data << {}
|
397
|
+
@prod_data << {_production: prod}
|
387
398
|
end
|
388
399
|
return self.class.start_options.fetch(prod, {}) # any options on this production
|
389
400
|
end
|
@@ -397,6 +408,9 @@ module EBNF::PEG
|
|
397
408
|
prod = @productions.last
|
398
409
|
handler, clear_packrat = self.class.production_handlers[prod]
|
399
410
|
data = @prod_data.pop if handler || self.class.start_handlers[prod]
|
411
|
+
error("finish",
|
412
|
+
"prod_data production mismatch: expected #{prod.inspect}, got #{data[:_production].inspect}",
|
413
|
+
production: prod, prod_data: @prod_data) if data && prod != data[:_production]
|
400
414
|
if handler && !@recovering && result != :unmatched
|
401
415
|
# Pop production data element from stack, potentially allowing handler to use it
|
402
416
|
result = begin
|
@@ -404,14 +418,13 @@ module EBNF::PEG
|
|
404
418
|
handler.call(result, data, @parse_callback)
|
405
419
|
}
|
406
420
|
rescue ArgumentError, Error => e
|
407
|
-
error("finish", "#{e.class}: #{e.message}", production: prod)
|
421
|
+
error("finish", "#{e.class}: #{e.message}", production: prod, backtrace: e.backtrace)
|
408
422
|
@recovering = false
|
409
423
|
end
|
410
424
|
end
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
level: result == :unmatched ? 0 : 1) do
|
425
|
+
debug("#{prod}(:finish)", "",
|
426
|
+
lineno: (scanner.lineno if scanner),
|
427
|
+
level: result == :unmatched ? 0 : 1) do
|
415
428
|
"#{result.inspect}@(#{scanner ? scanner.pos : '?'}), rest: #{scanner ? scanner.rest[0..20].inspect : '?'}"
|
416
429
|
end
|
417
430
|
self.clear_packrat if clear_packrat
|
@@ -433,12 +446,12 @@ module EBNF::PEG
|
|
433
446
|
handler.call(value, parentProd, @parse_callback)
|
434
447
|
}
|
435
448
|
rescue ArgumentError, Error => e
|
436
|
-
error("terminal", "#{e.class}: #{e.message}", value: value, production: prod)
|
449
|
+
error("terminal", "#{e.class}: #{e.message}", value: value, production: prod, backtrace: e.backtrace)
|
437
450
|
@recovering = false
|
438
451
|
end
|
439
452
|
end
|
440
453
|
progress("#{prod}(:terminal)", "",
|
441
|
-
depth: (depth +
|
454
|
+
depth: (depth + 1),
|
442
455
|
lineno: (scanner.lineno if scanner),
|
443
456
|
level: value == :unmatched ? 0 : 1) do
|
444
457
|
"#{value.inspect}@(#{scanner ? scanner.pos : '?'})"
|
@@ -460,10 +473,19 @@ module EBNF::PEG
|
|
460
473
|
#
|
461
474
|
# @param [Symbol] sym
|
462
475
|
# @return [Regexp]
|
463
|
-
def
|
476
|
+
def terminal_regexp(sym)
|
464
477
|
self.class.terminal_regexps[sym]
|
465
478
|
end
|
466
479
|
|
480
|
+
##
|
481
|
+
# Find a regular expression defined for a terminal
|
482
|
+
#
|
483
|
+
# @param [Symbol] sym
|
484
|
+
# @return [Regexp]
|
485
|
+
def terminal_options(sym)
|
486
|
+
self.class.terminal_options[sym]
|
487
|
+
end
|
488
|
+
|
467
489
|
##
|
468
490
|
# Record furthest failure.
|
469
491
|
#
|
data/lib/ebnf/peg/rule.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
module EBNF::PEG
|
2
2
|
# Behaviior for parsing a PEG rule
|
3
3
|
module Rule
|
4
|
+
include ::EBNF::Unescape
|
5
|
+
|
4
6
|
##
|
5
7
|
# Initialized by parser when loading rules.
|
6
8
|
# Used for finding rules and invoking elements of the parse process.
|
@@ -24,6 +26,7 @@ module EBNF::PEG
|
|
24
26
|
# * `opt`: returns the value matched, or `nil` if unmatched.
|
25
27
|
# * `plus`: returns an array of the values matched for the specified production, or `:unmatched`, if none are matched. For Terminals, these are concatenated into a single string.
|
26
28
|
# * `range`: returns a string composed of the values matched, or `:unmatched`, if less than `min` are matched.
|
29
|
+
# * `rept`: returns an array of the values matched for the speficied production, or `:unmatched`, if none are matched. For Terminals, these are concatenated into a single string.
|
27
30
|
# * `seq`: returns an array composed of single-entry hashes for each matched production indexed by the production name, or `:unmatched` if any production fails to match. For Terminals, returns a string created by concatenating these values. Via option in a `production` or definition, the result can be a single hash with values for each matched production; note that this is not always possible due to the possibility of repeated productions within the sequence.
|
28
31
|
# * `star`: returns an array of the values matched for the specified production. For Terminals, these are concatenated into a single string.
|
29
32
|
#
|
@@ -44,9 +47,18 @@ module EBNF::PEG
|
|
44
47
|
# If the terminal is defined with a regular expression,
|
45
48
|
# use that to match the input,
|
46
49
|
# otherwise,
|
47
|
-
if regexp = parser.
|
48
|
-
|
50
|
+
if regexp = parser.terminal_regexp(sym)
|
51
|
+
term_opts = parser.terminal_options(sym)
|
52
|
+
if matched = input.scan(regexp)
|
53
|
+
# Optionally map matched
|
54
|
+
matched = term_opts.fetch(:map, {}).fetch(matched.downcase, matched)
|
55
|
+
|
56
|
+
# Optionally unescape matched
|
57
|
+
matched = unescape(matched) if term_opts[:unescape]
|
58
|
+
end
|
59
|
+
|
49
60
|
result = parser.onTerminal(sym, (matched ? matched : :unmatched))
|
61
|
+
|
50
62
|
# Update furthest failure for strings and terminals
|
51
63
|
parser.update_furthest_failure(input.pos, input.lineno, sym) if result == :unmatched
|
52
64
|
parser.packrat[sym][pos] = {
|
@@ -60,6 +72,7 @@ module EBNF::PEG
|
|
60
72
|
eat_whitespace(input)
|
61
73
|
end
|
62
74
|
start_options = parser.onStart(sym)
|
75
|
+
string_regexp_opts = start_options[:insensitive_strings] ? Regexp::IGNORECASE : 0
|
63
76
|
|
64
77
|
result = case expr.first
|
65
78
|
when :alt
|
@@ -73,7 +86,12 @@ module EBNF::PEG
|
|
73
86
|
raise "No rule found for #{prod}" unless rule
|
74
87
|
rule.parse(input)
|
75
88
|
when String
|
76
|
-
input.scan(Regexp.new(Regexp.quote(prod)))
|
89
|
+
s = input.scan(Regexp.new(Regexp.quote(prod), string_regexp_opts))
|
90
|
+
case start_options[:insensitive_strings]
|
91
|
+
when :lower then s && s.downcase
|
92
|
+
when :upper then s && s.upcase
|
93
|
+
else s
|
94
|
+
end || :unmatched
|
77
95
|
end
|
78
96
|
if alt == :unmatched
|
79
97
|
# Update furthest failure for strings and terminals
|
@@ -111,7 +129,7 @@ module EBNF::PEG
|
|
111
129
|
raise "No rule found for #{prod}" unless rule
|
112
130
|
rule.parse(input)
|
113
131
|
when String
|
114
|
-
input.scan(Regexp.new(Regexp.quote(prod))) || :unmatched
|
132
|
+
input.scan(Regexp.new(Regexp.quote(prod), string_regexp_opts)) || :unmatched
|
115
133
|
end
|
116
134
|
if res != :unmatched
|
117
135
|
# Update furthest failure for terminals
|
@@ -122,7 +140,7 @@ module EBNF::PEG
|
|
122
140
|
end
|
123
141
|
when :opt
|
124
142
|
# Result is the matched value or nil
|
125
|
-
opt = rept(input, 0, 1, expr[1])
|
143
|
+
opt = rept(input, 0, 1, expr[1], string_regexp_opts, **start_options)
|
126
144
|
|
127
145
|
# Update furthest failure for strings and terminals
|
128
146
|
parser.update_furthest_failure(input.pos, input.lineno, expr[1]) if terminal?
|
@@ -130,7 +148,7 @@ module EBNF::PEG
|
|
130
148
|
when :plus
|
131
149
|
# Result is an array of all expressions while they match,
|
132
150
|
# at least one must match
|
133
|
-
plus = rept(input, 1, '*', expr[1])
|
151
|
+
plus = rept(input, 1, '*', expr[1], string_regexp_opts)
|
134
152
|
|
135
153
|
# Update furthest failure for strings and terminals
|
136
154
|
parser.update_furthest_failure(input.pos, input.lineno, expr[1]) if terminal?
|
@@ -142,6 +160,14 @@ module EBNF::PEG
|
|
142
160
|
parser.update_furthest_failure(input.pos, input.lineno, expr[1])
|
143
161
|
:unmatched
|
144
162
|
end
|
163
|
+
when :rept
|
164
|
+
# Result is an array of all expressions while they match,
|
165
|
+
# an empty array of none match
|
166
|
+
rept = rept(input, expr[1], expr[2], expr[3], string_regexp_opts)
|
167
|
+
|
168
|
+
# # Update furthest failure for strings and terminals
|
169
|
+
parser.update_furthest_failure(input.pos, input.lineno, expr[3]) if terminal?
|
170
|
+
rept.is_a?(Array) && terminal? ? rept.join("") : rept
|
145
171
|
when :seq
|
146
172
|
# Evaluate each expression into an array of hashes where each hash contains a key from the associated production and the value is the parsed value of that production. Returns :unmatched if the input does not match the production. Value ordering is ensured by native Hash ordering.
|
147
173
|
seq = expr[1..-1].each_with_object([]) do |prod, accumulator|
|
@@ -152,7 +178,12 @@ module EBNF::PEG
|
|
152
178
|
raise "No rule found for #{prod}" unless rule
|
153
179
|
rule.parse(input)
|
154
180
|
when String
|
155
|
-
input.scan(Regexp.new(Regexp.quote(prod)))
|
181
|
+
s = input.scan(Regexp.new(Regexp.quote(prod), string_regexp_opts))
|
182
|
+
case start_options[:insensitive_strings]
|
183
|
+
when :lower then s && s.downcase
|
184
|
+
when :upper then s && s.upcase
|
185
|
+
else s
|
186
|
+
end || :unmatched
|
156
187
|
end
|
157
188
|
if res == :unmatched
|
158
189
|
# Update furthest failure for strings and terminals
|
@@ -173,7 +204,7 @@ module EBNF::PEG
|
|
173
204
|
when :star
|
174
205
|
# Result is an array of all expressions while they match,
|
175
206
|
# an empty array of none match
|
176
|
-
star = rept(input, 0, '*', expr[1])
|
207
|
+
star = rept(input, 0, '*', expr[1], string_regexp_opts)
|
177
208
|
|
178
209
|
# Update furthest failure for strings and terminals
|
179
210
|
parser.update_furthest_failure(input.pos, input.lineno, expr[1]) if terminal?
|
@@ -205,8 +236,9 @@ module EBNF::PEG
|
|
205
236
|
# @param [Integer] max
|
206
237
|
# If it is an integer, it stops matching after max entries.
|
207
238
|
# @param [Symbol, String] prod
|
239
|
+
# @param [Integer] string_regexp_opts
|
208
240
|
# @return [:unmatched, Array]
|
209
|
-
def rept(input, min, max, prod)
|
241
|
+
def rept(input, min, max, prod, string_regexp_opts, **options)
|
210
242
|
result = []
|
211
243
|
|
212
244
|
case prod
|
@@ -218,9 +250,13 @@ module EBNF::PEG
|
|
218
250
|
result << res
|
219
251
|
end
|
220
252
|
when String
|
221
|
-
while (res = input.scan(Regexp.new(Regexp.quote(prod)))) && (max == '*' || result.length < max)
|
253
|
+
while (res = input.scan(Regexp.new(Regexp.quote(prod), string_regexp_opts))) && (max == '*' || result.length < max)
|
222
254
|
eat_whitespace(input) unless terminal?
|
223
|
-
result <<
|
255
|
+
result << case options[:insensitive_strings]
|
256
|
+
when :lower then res.downcase
|
257
|
+
when :upper then res.upcase
|
258
|
+
else res
|
259
|
+
end
|
224
260
|
end
|
225
261
|
end
|
226
262
|
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# Unsecape strings
|
3
|
+
module EBNF::Unescape
|
4
|
+
ESCAPE_CHARS = {
|
5
|
+
'\\t' => "\t", # \u0009 (tab)
|
6
|
+
'\\n' => "\n", # \u000A (line feed)
|
7
|
+
'\\r' => "\r", # \u000D (carriage return)
|
8
|
+
'\\b' => "\b", # \u0008 (backspace)
|
9
|
+
'\\f' => "\f", # \u000C (form feed)
|
10
|
+
'\\"' => '"', # \u0022 (quotation mark, double quote mark)
|
11
|
+
"\\'" => '\'', # \u0027 (apostrophe-quote, single quote mark)
|
12
|
+
'\\\\' => '\\' # \u005C (backslash)
|
13
|
+
}.freeze
|
14
|
+
ESCAPE_CHAR4 = /\\u(?:[0-9A-Fa-f]{4,4})/u.freeze # \uXXXX
|
15
|
+
ESCAPE_CHAR8 = /\\U(?:[0-9A-Fa-f]{8,8})/u.freeze # \UXXXXXXXX
|
16
|
+
ECHAR = /\\./u.freeze # More liberal unescaping
|
17
|
+
UCHAR = /#{ESCAPE_CHAR4}|#{ESCAPE_CHAR8}/n.freeze
|
18
|
+
|
19
|
+
##
|
20
|
+
# Returns a copy of the given `input` string with all `\uXXXX` and
|
21
|
+
# `\UXXXXXXXX` Unicode codepoint escape sequences replaced with their
|
22
|
+
# unescaped UTF-8 character counterparts.
|
23
|
+
#
|
24
|
+
# @param [String] string
|
25
|
+
# @return [String]
|
26
|
+
# @see https://www.w3.org/TR/rdf-sparql-query/#codepointEscape
|
27
|
+
def unescape_codepoints(string)
|
28
|
+
string = string.dup
|
29
|
+
string.force_encoding(Encoding::ASCII_8BIT) if string.respond_to?(:force_encoding)
|
30
|
+
|
31
|
+
# Decode \uXXXX and \UXXXXXXXX code points:
|
32
|
+
string = string.gsub(UCHAR) do |c|
|
33
|
+
s = [(c[2..-1]).hex].pack('U*')
|
34
|
+
s.respond_to?(:force_encoding) ? s.force_encoding(Encoding::ASCII_8BIT) : s
|
35
|
+
end
|
36
|
+
|
37
|
+
string.force_encoding(Encoding::UTF_8) if string.respond_to?(:force_encoding)
|
38
|
+
string
|
39
|
+
end
|
40
|
+
module_function :unescape_codepoints
|
41
|
+
|
42
|
+
##
|
43
|
+
# Returns a copy of the given `input` string with all string escape
|
44
|
+
# sequences (e.g. `\n` and `\t`) replaced with their unescaped UTF-8
|
45
|
+
# character counterparts.
|
46
|
+
#
|
47
|
+
# @param [String] input
|
48
|
+
# @return [String]
|
49
|
+
# @see https://www.w3.org/TR/rdf-sparql-query/#grammarEscapes
|
50
|
+
def unescape_string(input)
|
51
|
+
input.gsub(ECHAR) { |escaped| ESCAPE_CHARS[escaped] || escaped[1..-1]}
|
52
|
+
end
|
53
|
+
module_function :unescape_string
|
54
|
+
|
55
|
+
# Perform string and codepoint unescaping if defined for this terminal
|
56
|
+
# @param [String] string
|
57
|
+
# @return [String]
|
58
|
+
def unescape(string)
|
59
|
+
unescape_string(unescape_codepoints(string))
|
60
|
+
end
|
61
|
+
module_function :unescape
|
62
|
+
end
|
data/lib/ebnf/writer.rb
CHANGED
@@ -2,12 +2,14 @@
|
|
2
2
|
require 'rdf'
|
3
3
|
require 'strscan' unless defined?(StringScanner)
|
4
4
|
require "ostruct"
|
5
|
+
require 'unicode/types'
|
5
6
|
|
6
7
|
##
|
7
8
|
# Serialize ruleset back to EBNF
|
8
9
|
module EBNF
|
9
10
|
class Writer
|
10
11
|
LINE_LENGTH = 80
|
12
|
+
LINE_LENGTH_HTML = 200
|
11
13
|
|
12
14
|
# ASCII escape names
|
13
15
|
ASCII_ESCAPE_NAMES = [
|
@@ -85,22 +87,23 @@ module EBNF
|
|
85
87
|
#
|
86
88
|
# @param [Array<Rule>] rules
|
87
89
|
# @param [:abnf, :ebnf, :isoebnf] format (:ebnf)
|
90
|
+
# @param [Boolean] validate (false) validate generated HTML.
|
88
91
|
# @return [Object]
|
89
|
-
def self.html(*rules, format: :ebnf)
|
92
|
+
def self.html(*rules, format: :ebnf, validate: false)
|
90
93
|
require 'stringio' unless defined?(StringIO)
|
91
94
|
buf = StringIO.new
|
92
|
-
Writer.new(rules, out: buf, html: true, format: format)
|
95
|
+
Writer.new(rules, out: buf, html: true, format: format, validate: validate)
|
93
96
|
buf.string
|
94
97
|
end
|
95
98
|
|
96
99
|
##
|
97
100
|
# @param [Array<Rule>] rules
|
101
|
+
# @param [:abnf, :ebnf, :isoebnf] format (:ebnf)
|
102
|
+
# @param [Boolean] html (false) generate HTML output
|
103
|
+
# @param [Boolean] validate (false) validate generated HTML.
|
98
104
|
# @param [Hash{Symbol => Object}] options
|
99
105
|
# @param [#write] out ($stdout)
|
100
|
-
|
101
|
-
# @option options [Symbol] format
|
102
|
-
# @option options [Boolean] html (false)
|
103
|
-
def initialize(rules, out: $stdout, html: false, format: :ebnf, **options)
|
106
|
+
def initialize(rules, out: $stdout, html: false, format: :ebnf, validate: false, **options)
|
104
107
|
@options = options.merge(html: html)
|
105
108
|
return if rules.empty?
|
106
109
|
|
@@ -118,19 +121,24 @@ module EBNF
|
|
118
121
|
lhs_fmt = "%<id>-#{max_id+2}s " + lhs_fmt
|
119
122
|
lhs_length += max_id + 3
|
120
123
|
end
|
121
|
-
rhs_length = LINE_LENGTH - lhs_length
|
124
|
+
rhs_length = (html ? LINE_LENGTH_HTML : LINE_LENGTH) - lhs_length
|
122
125
|
|
123
126
|
if html
|
124
127
|
# Output as formatted HTML
|
125
128
|
begin
|
126
129
|
require 'erubis'
|
130
|
+
require 'htmlentities'
|
131
|
+
@coder = HTMLEntities.new
|
127
132
|
eruby = Erubis::Eruby.new(ERB_DESC)
|
128
133
|
formatted_rules = rules.map do |rule|
|
129
134
|
if rule.kind == :terminals || rule.kind == :pass
|
130
135
|
OpenStruct.new(id: ("@#{rule.kind}"),
|
131
136
|
sym: nil,
|
132
137
|
assign: nil,
|
133
|
-
formatted: (
|
138
|
+
formatted: (
|
139
|
+
rule.kind == :terminals ?
|
140
|
+
"<strong># Productions for terminals</strong>" :
|
141
|
+
self.send(format_meth, rule.expr)))
|
134
142
|
else
|
135
143
|
formatted_expr = self.send(format_meth, rule.expr)
|
136
144
|
# Measure text without markup
|
@@ -151,7 +159,7 @@ module EBNF
|
|
151
159
|
formatted.sub!(%r{\s*<code>\|</code>\s*}, '')
|
152
160
|
(ndx > 0 ? (rule.alt? ? '|' : '') : '=')
|
153
161
|
end
|
154
|
-
lines << OpenStruct.new(id: ("[#{rule.id}]" if rule.id),
|
162
|
+
lines << OpenStruct.new(id: ((ndx == 0 ? "[#{rule.id}]" : "") if rule.id),
|
155
163
|
sym: (rule.sym if ndx == 0 || format == :abnf),
|
156
164
|
assign: assign,
|
157
165
|
formatted: formatted)
|
@@ -168,10 +176,24 @@ module EBNF
|
|
168
176
|
end
|
169
177
|
end
|
170
178
|
end.flatten
|
171
|
-
|
179
|
+
|
180
|
+
html_result = eruby.evaluate(format: format, rules: formatted_rules)
|
181
|
+
|
182
|
+
if validate
|
183
|
+
begin
|
184
|
+
# Validate the output HTML
|
185
|
+
doc = Nokogiri::HTML5("<!DOCTYPE html>" + html_result, max_errors: 10)
|
186
|
+
raise EncodingError, "Errors found in generated HTML:\n " +
|
187
|
+
doc.errors.map(&:to_s).join("\n ") unless doc.errors.empty?
|
188
|
+
rescue LoadError, NoMethodError
|
189
|
+
# Skip
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
out.write html_result
|
172
194
|
return
|
173
195
|
rescue LoadError
|
174
|
-
$stderr.puts "Generating HTML requires erubis
|
196
|
+
$stderr.puts "Generating HTML requires erubis and htmlentities gems to be loaded"
|
175
197
|
end
|
176
198
|
end
|
177
199
|
|
@@ -216,7 +238,7 @@ module EBNF
|
|
216
238
|
|
217
239
|
# Format the expression part of a rule
|
218
240
|
def format_ebnf(expr, sep: nil, embedded: false)
|
219
|
-
return (@options[:html] ? %(<a href="#grammar-production-#{expr}">#{expr}</a>) : expr.to_s) if expr.is_a?(Symbol)
|
241
|
+
return (@options[:html] ? %(<a href="#grammar-production-#{@coder.encode expr}">#{@coder.encode expr}</a>) : expr.to_s) if expr.is_a?(Symbol)
|
220
242
|
if expr.is_a?(String)
|
221
243
|
return expr.length == 1 ?
|
222
244
|
format_ebnf_char(expr) :
|
@@ -290,10 +312,10 @@ module EBNF
|
|
290
312
|
# Format a single-character string, prefering hex for non-main ASCII
|
291
313
|
def format_ebnf_char(c)
|
292
314
|
case c.ord
|
293
|
-
when (0x21) then (@options[:html] ? %("<code class="grammar-literal">#{c}</code>") : %{"#{c}"})
|
294
|
-
when 0x22 then (@options[:html] ? %('<code class="grammar-literal"
|
295
|
-
when (0x23..0x7e) then (@options[:html] ? %("<code class="grammar-literal">#{c}</code>") : %{"#{c}"})
|
296
|
-
when (0x80..0xFFFD) then (@options[:html] ? %("<code class="grammar-literal">#{c}</code>") : %{"#{c}"})
|
315
|
+
when (0x21) then (@options[:html] ? %("<code class="grammar-literal">#{@coder.encode c}</code>") : %{"#{c}"})
|
316
|
+
when 0x22 then (@options[:html] ? %('<code class="grammar-literal">"</code>') : %{'"'})
|
317
|
+
when (0x23..0x7e) then (@options[:html] ? %("<code class="grammar-literal">#{@coder.encode c}</code>") : %{"#{c}"})
|
318
|
+
when (0x80..0xFFFD) then (@options[:html] ? %("<code class="grammar-literal">#{@coder.encode c}</code>") : %{"#{c}"})
|
297
319
|
else escape_ebnf_hex(c)
|
298
320
|
end
|
299
321
|
end
|
@@ -308,7 +330,7 @@ module EBNF
|
|
308
330
|
while !s.eos?
|
309
331
|
case
|
310
332
|
when s.scan(/\A[!"\u0024-\u007e]+/)
|
311
|
-
buffer << (@options[:html] ? %(<code class="grammar-literal">#{s.matched}</code>) : s.matched)
|
333
|
+
buffer << (@options[:html] ? %(<code class="grammar-literal">#{@coder.encode s.matched}</code>) : s.matched)
|
312
334
|
when s.scan(/\A#x\h+/)
|
313
335
|
buffer << escape_ebnf_hex(s.matched[2..-1].hex.chr(Encoding::UTF_8))
|
314
336
|
else
|
@@ -328,7 +350,8 @@ module EBNF
|
|
328
350
|
end
|
329
351
|
end
|
330
352
|
|
331
|
-
"#{quote}#{string}#{quote}"
|
353
|
+
res = "#{quote}#{string}#{quote}"
|
354
|
+
@options[:html] ? @coder.encode(res) : res
|
332
355
|
end
|
333
356
|
|
334
357
|
def escape_ebnf_hex(u)
|
@@ -340,16 +363,20 @@ module EBNF
|
|
340
363
|
end
|
341
364
|
char = fmt % u.ord
|
342
365
|
if @options[:html]
|
343
|
-
if u.ord <= 0x20
|
344
|
-
|
366
|
+
char = if u.ord <= 0x20
|
367
|
+
%(<abbr title="#{ASCII_ESCAPE_NAMES[u.ord]}">#{@coder.encode char}</abbr>)
|
368
|
+
elsif u.ord == 0x22
|
369
|
+
%(<abbr title="quot">>"</abbr>)
|
345
370
|
elsif u.ord < 0x7F
|
346
|
-
|
371
|
+
%(<abbr title="ascii '#{@coder.encode u}'">#{@coder.encode char}</abbr>)
|
347
372
|
elsif u.ord == 0x7F
|
348
|
-
|
373
|
+
%(<abbr title="delete">#{@coder.encode char}</abbr>)
|
349
374
|
elsif u.ord <= 0xFF
|
350
|
-
|
375
|
+
%(<abbr title="extended ascii '#{@coder.encode char}'">#{char}</abbr>)
|
376
|
+
elsif (%w(Control Private-use Surrogate Noncharacter Reserved) - ::Unicode::Types.of(u)).empty?
|
377
|
+
%(<abbr title="unicode '#{u}'">#{char}</abbr>)
|
351
378
|
else
|
352
|
-
|
379
|
+
%(<abbr title="unicode '#{::Unicode::Types.of(u).first}'">#{char}</abbr>)
|
353
380
|
end
|
354
381
|
%(<code class="grammar-char-escape">#{char}</code>)
|
355
382
|
else
|
@@ -363,7 +390,7 @@ module EBNF
|
|
363
390
|
|
364
391
|
# Format the expression part of a rule
|
365
392
|
def format_abnf(expr, sep: nil, embedded: false, sensitive: true)
|
366
|
-
return (@options[:html] ? %(<a href="#grammar-production-#{expr}">#{expr}</a>) : expr.to_s) if expr.is_a?(Symbol)
|
393
|
+
return (@options[:html] ? %(<a href="#grammar-production-#{@coder.encode expr}">#{@coder.encode expr}</a>) : expr.to_s) if expr.is_a?(Symbol)
|
367
394
|
if expr.is_a?(String)
|
368
395
|
if expr.length == 1
|
369
396
|
return format_abnf_char(expr)
|
@@ -380,7 +407,7 @@ module EBNF
|
|
380
407
|
seq.unshift(:seq)
|
381
408
|
return format_abnf(seq, sep: nil, embedded: false)
|
382
409
|
else
|
383
|
-
return (@options[:html] ? %("<code class="grammar-literal">#{'%s' if sensitive}#{expr}</code>") : %(#{'%s' if sensitive}"#{expr}"))
|
410
|
+
return (@options[:html] ? %("<code class="grammar-literal">#{'%s' if sensitive}#{@coder.encode expr}</code>") : %(#{'%s' if sensitive}"#{expr}"))
|
384
411
|
end
|
385
412
|
end
|
386
413
|
parts = {
|
@@ -448,7 +475,7 @@ module EBNF
|
|
448
475
|
# Format a single-character string, prefering hex for non-main ASCII
|
449
476
|
def format_abnf_char(c)
|
450
477
|
if /[\x20-\x21\x23-\x7E]/.match?(c)
|
451
|
-
c.inspect
|
478
|
+
@options[:html] ? %("<code class="grammar-literal">#{@coder.encode c}</code>") : c.inspect
|
452
479
|
else
|
453
480
|
escape_abnf_hex(c)
|
454
481
|
end
|
@@ -528,15 +555,17 @@ module EBNF
|
|
528
555
|
char = "%x" + (fmt % u.ord)
|
529
556
|
if @options[:html]
|
530
557
|
if u.ord <= 0x20
|
531
|
-
char = %(<abbr title="#{ASCII_ESCAPE_NAMES[u.ord]}">#{char}</abbr>)
|
532
|
-
elsif u.ord
|
533
|
-
|
558
|
+
char = %(<abbr title="#{ASCII_ESCAPE_NAMES[u.ord]}">#{@coder.encode char}</abbr>)
|
559
|
+
elsif u.ord == 0x22
|
560
|
+
%(<abbr title="quot">>"</abbr>)
|
561
|
+
elsif u.ord < 0x7F
|
562
|
+
char = %(<abbr title="ascii '#{u}'">#{@coder.encode char}</abbr>)
|
534
563
|
elsif u.ord == 0x7F
|
535
|
-
char = %(<abbr title="delete">#{char}</abbr>)
|
564
|
+
char = %(<abbr title="delete">#{@coder.encode char}</abbr>)
|
536
565
|
elsif u.ord <= 0xFF
|
537
566
|
char = %(<abbr title="extended ascii '#{u}'">#{char}</abbr>)
|
538
567
|
else
|
539
|
-
char = %(<abbr title="unicode '#{u}'">#{char}</abbr>)
|
568
|
+
char = %(<abbr title="unicode '#{u.unicode_normaliz}'">#{char}</abbr>)
|
540
569
|
end
|
541
570
|
%(<code class="grammar-char-escape">#{char}</code>)
|
542
571
|
else
|
@@ -550,7 +579,7 @@ module EBNF
|
|
550
579
|
|
551
580
|
# Format the expression part of a rule
|
552
581
|
def format_isoebnf(expr, sep: nil, embedded: false)
|
553
|
-
return (@options[:html] ? %(<a href="#grammar-production-#{expr}">#{expr}</a>) : expr.to_s) if expr.is_a?(Symbol)
|
582
|
+
return (@options[:html] ? %(<a href="#grammar-production-#{@coder.encode expr}">#{@coder.encode expr}</a>) : expr.to_s) if expr.is_a?(Symbol)
|
554
583
|
if expr.is_a?(String)
|
555
584
|
expr = expr[2..-1].hex.chr if expr =~ /\A#x\h+/
|
556
585
|
expr.chars.each do |c|
|
@@ -558,9 +587,9 @@ module EBNF
|
|
558
587
|
ISOEBNF::TERMINAL_CHARACTER.match?(c)
|
559
588
|
end
|
560
589
|
if expr =~ /"/
|
561
|
-
return (@options[:html] ? %('<code class="grammar-literal">#{expr}</code>') : %('#{expr}'))
|
590
|
+
return (@options[:html] ? %('<code class="grammar-literal">#{@coder.encode expr}</code>') : %('#{expr}'))
|
562
591
|
else
|
563
|
-
return (@options[:html] ? %("<code class="grammar-literal">#{expr}</code>") : %("#{expr}"))
|
592
|
+
return (@options[:html] ? %("<code class="grammar-literal">#{@coder.encode expr}</code>") : %("#{expr}"))
|
564
593
|
end
|
565
594
|
end
|
566
595
|
parts = {
|
@@ -679,11 +708,13 @@ module EBNF
|
|
679
708
|
<table class="grammar">
|
680
709
|
<tbody id="grammar-productions" class="<%= @format %>">
|
681
710
|
<% for rule in @rules %>
|
682
|
-
<tr<%= %{ id="grammar-production-#{rule.sym}"} unless %w(=/ |).include?(rule.assign)
|
711
|
+
<tr<%= %{ id="grammar-production-#{rule.sym}"} unless %w(=/ |).include?(rule.assign) || rule.sym.nil?%>>
|
683
712
|
<% if rule.id %>
|
684
|
-
<td
|
713
|
+
<td<%= " colspan=2" unless rule.sym %>><%= rule.id %></td>
|
685
714
|
<% end %>
|
715
|
+
<% if rule.sym %>
|
686
716
|
<td><code><%== rule.sym %></code></td>
|
717
|
+
<% end %>
|
687
718
|
<td><%= rule.assign %></td>
|
688
719
|
<td><%= rule.formatted %></td>
|
689
720
|
</tr>
|
data/lib/ebnf.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ebnf
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Gregg Kellogg
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-08-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: sxp
|
@@ -52,6 +52,48 @@ dependencies:
|
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '3.1'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: htmlentities
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '4.3'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '4.3'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: unicode-types
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '1.6'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '1.6'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: amazing_print
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '1.2'
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '1.2'
|
55
97
|
- !ruby/object:Gem::Dependency
|
56
98
|
name: rdf-spec
|
57
99
|
requirement: !ruby/object:Gem::Requirement
|
@@ -81,47 +123,47 @@ dependencies:
|
|
81
123
|
- !ruby/object:Gem::Version
|
82
124
|
version: '3.1'
|
83
125
|
- !ruby/object:Gem::Dependency
|
84
|
-
name:
|
126
|
+
name: nokogiri
|
85
127
|
requirement: !ruby/object:Gem::Requirement
|
86
128
|
requirements:
|
87
129
|
- - "~>"
|
88
130
|
- !ruby/object:Gem::Version
|
89
|
-
version: '
|
131
|
+
version: '1.10'
|
90
132
|
type: :development
|
91
133
|
prerelease: false
|
92
134
|
version_requirements: !ruby/object:Gem::Requirement
|
93
135
|
requirements:
|
94
136
|
- - "~>"
|
95
137
|
- !ruby/object:Gem::Version
|
96
|
-
version: '
|
138
|
+
version: '1.10'
|
97
139
|
- !ruby/object:Gem::Dependency
|
98
|
-
name:
|
140
|
+
name: erubis
|
99
141
|
requirement: !ruby/object:Gem::Requirement
|
100
142
|
requirements:
|
101
143
|
- - "~>"
|
102
144
|
- !ruby/object:Gem::Version
|
103
|
-
version: '
|
145
|
+
version: '2.7'
|
104
146
|
type: :development
|
105
147
|
prerelease: false
|
106
148
|
version_requirements: !ruby/object:Gem::Requirement
|
107
149
|
requirements:
|
108
150
|
- - "~>"
|
109
151
|
- !ruby/object:Gem::Version
|
110
|
-
version: '
|
152
|
+
version: '2.7'
|
111
153
|
- !ruby/object:Gem::Dependency
|
112
154
|
name: rspec
|
113
155
|
requirement: !ruby/object:Gem::Requirement
|
114
156
|
requirements:
|
115
157
|
- - "~>"
|
116
158
|
- !ruby/object:Gem::Version
|
117
|
-
version: '3.
|
159
|
+
version: '3.10'
|
118
160
|
type: :development
|
119
161
|
prerelease: false
|
120
162
|
version_requirements: !ruby/object:Gem::Requirement
|
121
163
|
requirements:
|
122
164
|
- - "~>"
|
123
165
|
- !ruby/object:Gem::Version
|
124
|
-
version: '3.
|
166
|
+
version: '3.10'
|
125
167
|
- !ruby/object:Gem::Dependency
|
126
168
|
name: rspec-its
|
127
169
|
requirement: !ruby/object:Gem::Requirement
|
@@ -164,8 +206,8 @@ dependencies:
|
|
164
206
|
- - "~>"
|
165
207
|
- !ruby/object:Gem::Version
|
166
208
|
version: '13.0'
|
167
|
-
description: EBNF is a Ruby parser for W3C EBNF and a parser generator for
|
168
|
-
LL(1)
|
209
|
+
description: EBNF is a Ruby parser for W3C EBNF and a parser generator for PEG and
|
210
|
+
LL(1). Also includes parsing modes for ISO EBNF and ABNF.
|
169
211
|
email: public-rdf-ruby@w3.org
|
170
212
|
executables:
|
171
213
|
- ebnf
|
@@ -226,13 +268,14 @@ files:
|
|
226
268
|
- lib/ebnf/peg/rule.rb
|
227
269
|
- lib/ebnf/rule.rb
|
228
270
|
- lib/ebnf/terminals.rb
|
271
|
+
- lib/ebnf/unescape.rb
|
229
272
|
- lib/ebnf/version.rb
|
230
273
|
- lib/ebnf/writer.rb
|
231
274
|
homepage: https://github.com/dryruby/ebnf
|
232
275
|
licenses:
|
233
276
|
- Unlicense
|
234
277
|
metadata: {}
|
235
|
-
post_install_message:
|
278
|
+
post_install_message:
|
236
279
|
rdoc_options: []
|
237
280
|
require_paths:
|
238
281
|
- lib
|
@@ -247,8 +290,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
247
290
|
- !ruby/object:Gem::Version
|
248
291
|
version: '0'
|
249
292
|
requirements: []
|
250
|
-
rubygems_version: 3.
|
251
|
-
signing_key:
|
293
|
+
rubygems_version: 3.2.15
|
294
|
+
signing_key:
|
252
295
|
specification_version: 4
|
253
|
-
summary: EBNF parser and parser generator.
|
296
|
+
summary: EBNF parser and parser generator in Ruby.
|
254
297
|
test_files: []
|