abnftt 0.1.1 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
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: []