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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3bc3e072613b51decf47f31c6ccbff2af79e403131d889ce6bbc075d097d5af2
4
- data.tar.gz: 0d17df7192ac612129632e001e348f531f2c483041b961d447837a4dd3755b6f
3
+ metadata.gz: 397c4ddc806faa5fcb39bc900acb4fb828289c2d639a24ff10158bd0d4712bab
4
+ data.tar.gz: d8e1904fc3a2310ba3ab986840cc57bac9bcca1479cda0939ebda3ae19baa36c
5
5
  SHA512:
6
- metadata.gz: 729b268fe2df5265d44560cd6dd07dc3a01de87a9d3fca4570953bf920566f35aadd64ba752f83560bf4b47d1404a6146500a0621d95e35416627ef8df5b115a
7
- data.tar.gz: e319d947ef1f7ce20ece128b8b993d3b7931c4a3985b54ef8d7849ee79420a4d23b071cf6baa784062b92fec0ca74f4aea42d8e81829ba83f3911b71ede587fa
6
+ metadata.gz: 280d0eaee9b999e7c9a9b7cac343e1653a6910ecba9e88cb3d8f44652dfd31b440fd2483d8ce4164440a50f15a61e76fa8dbbd4dd98d3cd9004916eb0fb1675a
7
+ data.tar.gz: b19f0ebe121e9156c2a3f0cec93fc9e20710e69450981a8f76271bb008f9e432f9b5c9b0605194282cda2ffb39ed6216e427ab6eee5e1a4fec7e5e4799ceef5c
data/abnftt.gemspec CHANGED
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = "abnftt"
3
- s.version = "0.1.1"
3
+ s.version = "0.2.1"
4
4
  s.summary = "RFC 5234+7405 ABNF to Treetop"
5
5
  s.description = %q{Less shifty support for tools based on IETF's ABNF}
6
6
  s.author = "Carsten Bormann"
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 'abnf'
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 = ABNFParser.new
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 ast.ast.to_yaml
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
- f.puts to_treetop(ast.ast, outfn)
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 ABNF
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[1] == "*"
810
- if el[0] != ""
811
- s = el[0].to_i
812
- else
813
- s = 0
814
- end
815
- if el[2] != ""
816
- e = el[2].to_i
817
- else
818
- e = true
819
- end
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 ABNFParser < Treetop::Runtime::CompiledParser
2583
- include ABNF
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.1.1
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: 2019-03-24 00:00:00.000000000 Z
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/abnf.rb
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.0.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: []