abnftt 0.1.1 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/abnftt.gemspec +1 -1
- data/bin/abnftt +6 -102
- data/lib/{abnf.rb → abnfgrammar.rb} +15 -14
- data/lib/abnftt.rb +199 -0
- metadata +8 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 397c4ddc806faa5fcb39bc900acb4fb828289c2d639a24ff10158bd0d4712bab
|
4
|
+
data.tar.gz: d8e1904fc3a2310ba3ab986840cc57bac9bcca1479cda0939ebda3ae19baa36c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 280d0eaee9b999e7c9a9b7cac343e1653a6910ecba9e88cb3d8f44652dfd31b440fd2483d8ce4164440a50f15a61e76fa8dbbd4dd98d3cd9004916eb0fb1675a
|
7
|
+
data.tar.gz: b19f0ebe121e9156c2a3f0cec93fc9e20710e69450981a8f76271bb008f9e432f9b5c9b0605194282cda2ffb39ed6216e427ab6eee5e1a4fec7e5e4799ceef5c
|
data/abnftt.gemspec
CHANGED
data/bin/abnftt
CHANGED
@@ -2,108 +2,10 @@
|
|
2
2
|
|
3
3
|
require 'pp'
|
4
4
|
require 'yaml'
|
5
|
-
require 'treetop'
|
6
5
|
|
7
6
|
Encoding.default_external = Encoding::UTF_8
|
8
7
|
|
9
|
-
require '
|
10
|
-
|
11
|
-
class Treetop::Runtime::SyntaxNode
|
12
|
-
def clean_abnf
|
13
|
-
if elements
|
14
|
-
elements.map {|el| el.clean_abnf}.join
|
15
|
-
else
|
16
|
-
text_value
|
17
|
-
end
|
18
|
-
end
|
19
|
-
def ast
|
20
|
-
fail "undefined_ast #{inspect}"
|
21
|
-
end
|
22
|
-
def ast_from_percent(base, first, second)
|
23
|
-
c1 = first.to_i(base).chr(Encoding::UTF_8)
|
24
|
-
case second[0]
|
25
|
-
when nil
|
26
|
-
["cs", c1]
|
27
|
-
when "-"
|
28
|
-
c2 = second[1..-1].to_i(base).chr(Encoding::UTF_8)
|
29
|
-
["char-range", c1, c2]
|
30
|
-
when "."
|
31
|
-
el = second.split(".")
|
32
|
-
el[0] = first
|
33
|
-
["cs", el.map {|c| c.to_i(base).chr(Encoding::UTF_8)}.join]
|
34
|
-
else
|
35
|
-
fail "ast_from_percent"
|
36
|
-
end
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
def to_treetop(ast, ofn)
|
41
|
-
modname = File.basename(ofn).sub("-", "_").sub(/[^_a-zA-Z]/, "").upcase
|
42
|
-
<<~EOS
|
43
|
-
# Encoding: UTF-8
|
44
|
-
grammar #{modname}
|
45
|
-
#{ast.map {|x| to_treetop0(x)}.join}
|
46
|
-
end
|
47
|
-
EOS
|
48
|
-
end
|
49
|
-
def to_treetop0(ast)
|
50
|
-
fail ast.inspect unless ast[0] == "="
|
51
|
-
<<~EOS
|
52
|
-
rule #{to_treetop1(ast[1])}
|
53
|
-
#{to_treetop1(ast[2])}
|
54
|
-
end
|
55
|
-
EOS
|
56
|
-
end
|
57
|
-
FIXUP_NAMES = Hash.new {|h, k| k}
|
58
|
-
FIXUP_NAMES.merge!({
|
59
|
-
"rule" => "r__rule",
|
60
|
-
})
|
61
|
-
def to_treetop1(ast)
|
62
|
-
case ast
|
63
|
-
when String
|
64
|
-
FIXUP_NAMES[ast].gsub("-", "_")
|
65
|
-
when Array
|
66
|
-
case ast[0]
|
67
|
-
when "alt" # ["alt", *a]
|
68
|
-
"(#{ast[1..-1].map {|x| to_treetop1(x)}.join(" / ")})"
|
69
|
-
when "seq" # ["seq", *a]
|
70
|
-
"(#{ast[1..-1].map {|x| to_treetop1(x)}.join(" ")})"
|
71
|
-
when "rep" # ["rep", s, e, a]
|
72
|
-
t = to_treetop1(ast[3]) || "@@@"
|
73
|
-
case [ast[1], ast[2]]
|
74
|
-
when [0, 1]
|
75
|
-
t + "?"
|
76
|
-
when [0, true]
|
77
|
-
t + "*"
|
78
|
-
when [1, true]
|
79
|
-
t + "+"
|
80
|
-
else
|
81
|
-
t + " #{ast[1]}..#{ast[2] == true ? '' : ast[2]}"
|
82
|
-
end
|
83
|
-
when "prose" # ["prose", text]
|
84
|
-
fail "prose not implemented #{ast.inspect}"
|
85
|
-
when "ci" # ["ci", text]
|
86
|
-
s = ast[1]
|
87
|
-
if s =~ /\A[^A-Za-z]*\z/
|
88
|
-
s.inspect
|
89
|
-
else
|
90
|
-
s.inspect << "i" # could do this always, but reduce noise
|
91
|
-
end
|
92
|
-
when "cs" # ["cs", text]
|
93
|
-
ast[1].inspect
|
94
|
-
when "char-range" # ["char-range", c1, c2]
|
95
|
-
c1 = Regexp.quote(ast[1])
|
96
|
-
c2 = Regexp.quote(ast[2])
|
97
|
-
"[#{c1}-#{c2}]" # XXX does that always work
|
98
|
-
when "im" # ["im", a, text]
|
99
|
-
to_treetop1(ast[1]) + " " + ast[2]
|
100
|
-
else
|
101
|
-
fail "to_treetop(#{ast.inspect})"
|
102
|
-
end
|
103
|
-
else
|
104
|
-
fail "to_treetop(#{ast.inspect})"
|
105
|
-
end
|
106
|
-
end
|
8
|
+
require 'abnftt'
|
107
9
|
|
108
10
|
unless fn = ARGV[0]
|
109
11
|
warn "Usage: abnftt grammar.abnftt"
|
@@ -111,17 +13,19 @@ unless fn = ARGV[0]
|
|
111
13
|
end
|
112
14
|
outfn = fn.sub(/\.abnftt\z/, "")
|
113
15
|
|
114
|
-
parser =
|
16
|
+
parser = ABNFGrammarParser.new
|
115
17
|
abnf_file = File.read(fn)
|
116
18
|
ast = parser.parse abnf_file
|
117
19
|
if ast
|
118
20
|
# p ast
|
21
|
+
abnf = ABNF.new(ast)
|
119
22
|
File.open("#{outfn}.yaml", "w") do |f|
|
120
|
-
f.puts
|
23
|
+
f.puts abnf.tree.to_yaml
|
121
24
|
end
|
122
25
|
# pp ast.ast
|
123
26
|
File.open("#{outfn}.treetop", "w") do |f|
|
124
|
-
|
27
|
+
modname = File.basename(outfn).gsub("-", "_").gsub(/[^_a-zA-Z]/, "").upcase
|
28
|
+
f.puts abnf.to_treetop(modname)
|
125
29
|
end
|
126
30
|
File.open("#{outfn}.abnf", "w") do |f|
|
127
31
|
f.puts ast.clean_abnf.lines.map(&:rstrip).join("\n")
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# Autogenerated from a Treetop grammar. Edits may be lost.
|
2
2
|
|
3
3
|
|
4
|
-
module
|
4
|
+
module ABNFGrammar
|
5
5
|
include Treetop::Runtime
|
6
6
|
|
7
7
|
def root
|
@@ -806,17 +806,18 @@ module ABNF
|
|
806
806
|
s = 1
|
807
807
|
e = 1
|
808
808
|
el = elements.map {|e| e.text_value}
|
809
|
-
if el[
|
810
|
-
|
811
|
-
|
812
|
-
|
813
|
-
|
814
|
-
|
815
|
-
|
816
|
-
|
817
|
-
|
818
|
-
|
819
|
-
|
809
|
+
if el != []
|
810
|
+
el[2] = el[0] = el.join if el[1] != "*"
|
811
|
+
s = if el[0] != ""
|
812
|
+
el[0].to_i
|
813
|
+
else
|
814
|
+
0
|
815
|
+
end
|
816
|
+
e = if el[2] != ""
|
817
|
+
el[2].to_i
|
818
|
+
else
|
819
|
+
true
|
820
|
+
end
|
820
821
|
end
|
821
822
|
occ = [s, e]
|
822
823
|
if occ == [1, 1]
|
@@ -2579,8 +2580,8 @@ module ABNF
|
|
2579
2580
|
|
2580
2581
|
end
|
2581
2582
|
|
2582
|
-
class
|
2583
|
-
include
|
2583
|
+
class ABNFGrammarParser < Treetop::Runtime::CompiledParser
|
2584
|
+
include ABNFGrammar
|
2584
2585
|
end
|
2585
2586
|
|
2586
2587
|
|
data/lib/abnftt.rb
ADDED
@@ -0,0 +1,199 @@
|
|
1
|
+
require 'treetop'
|
2
|
+
require 'abnfgrammar'
|
3
|
+
|
4
|
+
class Treetop::Runtime::SyntaxNode
|
5
|
+
def clean_abnf
|
6
|
+
if elements
|
7
|
+
elements.map {|el| el.clean_abnf}.join
|
8
|
+
else
|
9
|
+
text_value
|
10
|
+
end
|
11
|
+
end
|
12
|
+
def ast
|
13
|
+
fail "undefined_ast #{inspect}"
|
14
|
+
end
|
15
|
+
def ast_from_percent(base, first, second)
|
16
|
+
c1 = first.to_i(base).chr(Encoding::UTF_8)
|
17
|
+
case second[0]
|
18
|
+
when nil
|
19
|
+
["cs", c1]
|
20
|
+
when "-"
|
21
|
+
c2 = second[1..-1].to_i(base).chr(Encoding::UTF_8)
|
22
|
+
["char-range", c1, c2]
|
23
|
+
when "."
|
24
|
+
el = second.split(".")
|
25
|
+
el[0] = first
|
26
|
+
["cs", el.map {|c| c.to_i(base).chr(Encoding::UTF_8)}.join]
|
27
|
+
else
|
28
|
+
fail "ast_from_percent"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
class ABNF
|
35
|
+
@@parser = ABNFGrammarParser.new
|
36
|
+
|
37
|
+
def self.reason(parser, s)
|
38
|
+
reason = [parser.failure_reason]
|
39
|
+
parser.failure_reason =~ /^(Expected .+) after/m
|
40
|
+
reason << "#{$1.gsub("\n", '<<<NEWLINE>>>')}:" if $1
|
41
|
+
if line = s.lines.to_a[parser.failure_line - 1]
|
42
|
+
reason << line
|
43
|
+
reason << "#{'~' * (parser.failure_column - 1)}^"
|
44
|
+
end
|
45
|
+
reason.join("\n")
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.from_abnf(s)
|
49
|
+
ast = @@parser.parse s
|
50
|
+
if !ast
|
51
|
+
fail self.reason(@@parser, s)
|
52
|
+
end
|
53
|
+
ABNF.new(ast)
|
54
|
+
end
|
55
|
+
|
56
|
+
attr_accessor :ast, :rules, :tree
|
57
|
+
def initialize(ast_)
|
58
|
+
@ast = ast_
|
59
|
+
@tree = ast.ast
|
60
|
+
@rules = {}
|
61
|
+
@tree.each do |x|
|
62
|
+
op, name, val, rest = x
|
63
|
+
fail rest if rest
|
64
|
+
fail op unless op == "=" # XXX
|
65
|
+
if @rules[name]
|
66
|
+
fail "duplicate rule for name #{name}"
|
67
|
+
end
|
68
|
+
@rules[name] = val
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def generate
|
73
|
+
generate1(rules.first.first)
|
74
|
+
end
|
75
|
+
|
76
|
+
def generate1(what)
|
77
|
+
case what
|
78
|
+
when String
|
79
|
+
expansion = rules[what]
|
80
|
+
fail "can't find rules #{what}" unless expansion
|
81
|
+
generate1(expansion)
|
82
|
+
when Array
|
83
|
+
op, *args = what
|
84
|
+
case op
|
85
|
+
when "seq"
|
86
|
+
args.map {|arg| generate1(arg)}.join
|
87
|
+
when "alt"
|
88
|
+
generate1(args.sample)
|
89
|
+
when "rep"
|
90
|
+
l, h, x, rest = args
|
91
|
+
fail rest if rest
|
92
|
+
h = l+3 if h == true
|
93
|
+
n = rand(h-l+1)+l
|
94
|
+
(0...n).map { generate1(x) }.join
|
95
|
+
when "ci"
|
96
|
+
s, rest = args
|
97
|
+
fail rest if rest
|
98
|
+
s.chars.map{|x|[x.upcase, x.downcase].sample}.join
|
99
|
+
when "cs"
|
100
|
+
s, rest = args
|
101
|
+
fail rest if rest
|
102
|
+
s
|
103
|
+
when "char-range"
|
104
|
+
l, r = args
|
105
|
+
fail rest if rest
|
106
|
+
(rand(r.ord-l.ord+1)+l.ord).chr(Encoding::UTF_8)
|
107
|
+
when "prose" # ["prose", text]
|
108
|
+
fail "prose not implemented #{what.inspect}"
|
109
|
+
when "im"
|
110
|
+
warn "abnftt-style inline module ignored #{what.inspect}"
|
111
|
+
''
|
112
|
+
else
|
113
|
+
fail [op, args].inspect
|
114
|
+
end
|
115
|
+
else
|
116
|
+
fail
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
|
121
|
+
def to_treetop(modname)
|
122
|
+
<<~EOS
|
123
|
+
# Encoding: UTF-8
|
124
|
+
grammar #{modname}
|
125
|
+
#{tree.map {|x| to_treetop0(x)}.join}
|
126
|
+
end
|
127
|
+
EOS
|
128
|
+
end
|
129
|
+
def to_treetop0(ast)
|
130
|
+
fail ast.inspect unless ast[0] == "="
|
131
|
+
<<~EOS
|
132
|
+
rule #{to_treetop1(ast[1])}
|
133
|
+
#{to_treetop1(ast[2])}
|
134
|
+
end
|
135
|
+
EOS
|
136
|
+
end
|
137
|
+
FIXUP_NAMES = Hash.new {|h, k| k}
|
138
|
+
FIXUP_NAMES.merge!({
|
139
|
+
"rule" => "r__rule",
|
140
|
+
})
|
141
|
+
def to_treetop1(ast)
|
142
|
+
case ast
|
143
|
+
when String
|
144
|
+
FIXUP_NAMES[ast].gsub("-", "_")
|
145
|
+
when Array
|
146
|
+
case ast[0]
|
147
|
+
when "alt" # ["alt", *a]
|
148
|
+
"(#{ast[1..-1].map {|x| to_treetop1(x)}.join(" / ")})"
|
149
|
+
when "seq" # ["seq", *a]
|
150
|
+
"(#{ast[1..-1].map {|x| to_treetop1(x)}.join(" ")})"
|
151
|
+
when "rep" # ["rep", s, e, a]
|
152
|
+
t = to_treetop1(ast[3]) || "@@@"
|
153
|
+
case [ast[1], ast[2]]
|
154
|
+
when [0, 1]
|
155
|
+
t + "?"
|
156
|
+
when [0, true]
|
157
|
+
t + "*"
|
158
|
+
when [1, true]
|
159
|
+
t + "+"
|
160
|
+
else
|
161
|
+
t + " #{ast[1]}..#{ast[2] == true ? '' : ast[2]}"
|
162
|
+
end
|
163
|
+
when "prose" # ["prose", text]
|
164
|
+
fail "prose not implemented #{ast.inspect}"
|
165
|
+
when "ci" # ["ci", text]
|
166
|
+
s = ast[1]
|
167
|
+
if s =~ /\A[^A-Za-z]*\z/
|
168
|
+
s.inspect
|
169
|
+
else
|
170
|
+
s.inspect << "i" # could do this always, but reduce noise
|
171
|
+
end
|
172
|
+
when "cs" # ["cs", text]
|
173
|
+
ast[1].inspect
|
174
|
+
when "char-range" # ["char-range", c1, c2]
|
175
|
+
c1 = Regexp.quote(ast[1])
|
176
|
+
c2 = Regexp.quote(ast[2])
|
177
|
+
"[#{c1}-#{c2}]" # XXX does that always work
|
178
|
+
when "im" # ["im", a, text]
|
179
|
+
to_treetop1(ast[1]) + " " + ast[2]
|
180
|
+
else
|
181
|
+
fail "to_treetop(#{ast.inspect})"
|
182
|
+
end
|
183
|
+
else
|
184
|
+
fail "to_treetop(#{ast.inspect})"
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
@@gensym = 0
|
189
|
+
|
190
|
+
attr_accessor :parser
|
191
|
+
def validate(s)
|
192
|
+
@parser ||= Treetop.load_from_string(to_treetop("ABNF_Mod" << (@@gensym += 1).to_s))
|
193
|
+
parser_instance ||= @parser.new
|
194
|
+
unless result1 = parser_instance.parse(s)
|
195
|
+
fail self.class.reason(parser_instance, s)
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: abnftt
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Carsten Bormann
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-02-25 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: Less shifty support for tools based on IETF's ABNF
|
14
14
|
email: cabo@tzi.org
|
@@ -21,12 +21,13 @@ files:
|
|
21
21
|
- abnftt.gemspec
|
22
22
|
- bin/abnftt
|
23
23
|
- bin/abnftt~
|
24
|
-
- lib/
|
24
|
+
- lib/abnfgrammar.rb
|
25
|
+
- lib/abnftt.rb
|
25
26
|
homepage: http://github.com/cabo/abnftt
|
26
27
|
licenses:
|
27
28
|
- MIT
|
28
29
|
metadata: {}
|
29
|
-
post_install_message:
|
30
|
+
post_install_message:
|
30
31
|
rdoc_options: []
|
31
32
|
require_paths:
|
32
33
|
- lib
|
@@ -41,8 +42,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
41
42
|
- !ruby/object:Gem::Version
|
42
43
|
version: '0'
|
43
44
|
requirements: []
|
44
|
-
rubygems_version: 3.
|
45
|
-
signing_key:
|
45
|
+
rubygems_version: 3.2.3
|
46
|
+
signing_key:
|
46
47
|
specification_version: 4
|
47
48
|
summary: RFC 5234+7405 ABNF to Treetop
|
48
49
|
test_files: []
|