cddl 0.1.0 → 0.8.8
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 +5 -5
- data/bin/cddl +80 -23
- data/cddl.gemspec +4 -2
- data/data/cddl.abnf +50 -20
- data/data/prelude.cddl +9 -3
- data/lib/cbor-pp-play.rb +13 -0
- data/lib/cbor-pp.rb +213 -0
- data/lib/cddl.rb +1001 -163
- data/test/test-cddl.rb +1620 -93
- data/test-data/7071-concise-cut.cddl +48 -0
- data/test-data/7071-concise.cddl +1 -1
- data/test-data/7071-verbose.cddl +1 -1
- data/test-data/a.cddl +9 -0
- data/test-data/ambig.cddl +2 -0
- data/test-data/b.cddl +3 -0
- data/test-data/badaddr.cddl +1 -0
- data/test-data/basic_syntax_example.cddl +7 -0
- data/test-data/cdni-ct.cddl +72 -0
- data/test-data/cose.cddl +68 -0
- data/test-data/dcaf.cddl +58 -0
- data/test-data/dcaf1.cddl +48 -0
- data/test-data/dotsize.cddl +7 -0
- data/test-data/extractor-demo.cddl +14 -0
- data/test-data/grasp-01-extract.cddl +91 -0
- data/test-data/grasp-01-test.cddl +90 -0
- data/test-data/grasp-01.cddl +90 -0
- data/test-data/grasp-09.cddl +108 -0
- data/test-data/grasp-v1.cddl +97 -0
- data/test-data/grasp-v2X.cddl +89 -0
- data/test-data/ifmap-base-2.0v17.cddl +392 -0
- data/test-data/ifmap-base-2.2v9_fh-cabo.cddl +149 -0
- data/test-data/ifmap-metadata-2.2v9_fh-cabo.cddl +239 -0
- data/test-data/ifmap2.cddl +21 -0
- data/test-data/jcr-ex.cddl +42 -0
- data/test-data/kevin5.cddl +5 -0
- data/test-data/map-group.cddl +13 -0
- data/test-data/mapkey.cddl +2 -0
- data/test-data/minimal.cddl +1 -0
- data/test-data/patch1.cddl +13 -0
- data/test-data/reused_named_group.cddl +14 -0
- data/test-data/structure.cddl +9 -0
- data/test-data/toerless0.cddl +14 -0
- data/test-data/toerless1.cddl +11 -0
- data/test-data/two_anonymous_groups.cddl +13 -0
- data/test-data/wrong1.cddl +17 -0
- data/test-data/wrong1a.cddl +23 -0
- data/test-data/wrong2.cddl +17 -0
- data/test-data/wrong2a.cddl +17 -0
- data/test-data/xmlmig.cddl +15 -0
- metadata +72 -5
data/lib/cddl.rb
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
require 'abnc'
|
|
2
2
|
require 'pp'
|
|
3
3
|
require 'pathname'
|
|
4
|
+
require 'cbor-pure' unless defined?(CBOR::Tagged)
|
|
5
|
+
require 'regexp-examples'
|
|
6
|
+
require 'colorize'
|
|
7
|
+
require 'base64'
|
|
4
8
|
|
|
5
9
|
module CDDL
|
|
6
10
|
|
|
@@ -9,7 +13,16 @@ module CDDL
|
|
|
9
13
|
PRELUDE = File.read("#{DATA_DIR}/prelude.cddl")
|
|
10
14
|
ABNF_SPEC = File.read("#{DATA_DIR}/cddl.abnf")
|
|
11
15
|
|
|
16
|
+
MANY = Float::INFINITY
|
|
17
|
+
|
|
18
|
+
MAX_RECURSE = 128 # XXX
|
|
19
|
+
|
|
20
|
+
class ParseError < ArgumentError; end
|
|
21
|
+
|
|
12
22
|
class Parser
|
|
23
|
+
|
|
24
|
+
class BackTrack < Exception; end
|
|
25
|
+
|
|
13
26
|
def initialize(source_text)
|
|
14
27
|
@abnf = Peggy::ABNF.new
|
|
15
28
|
_cresult = @abnf.compile! ABNF_SPEC, ignore: :s
|
|
@@ -17,71 +30,334 @@ module CDDL
|
|
|
17
30
|
expected_length = source_text.length + PRELUDE.length
|
|
18
31
|
if expected_length != presult
|
|
19
32
|
upto = @abnf.parse_results.keys.max
|
|
20
|
-
puts "UPTO: #{upto}"
|
|
21
|
-
pp @abnf.parse_results[upto]
|
|
22
|
-
pp @abnf.parse_results[presult]
|
|
23
|
-
|
|
24
|
-
puts @abnf.ast?
|
|
25
|
-
|
|
33
|
+
puts "UPTO: #{upto}" if $advanced
|
|
34
|
+
pp @abnf.parse_results[upto] if $advanced
|
|
35
|
+
pp @abnf.parse_results[presult] if $advanced
|
|
36
|
+
puts "SO FAR: #{presult}" if $advanced
|
|
37
|
+
puts @abnf.ast? if $advanced
|
|
38
|
+
presult ||= 0
|
|
39
|
+
part1 = source_text[[presult - 100, 0].max...presult]
|
|
40
|
+
part3 = source_text[upto...[upto + 100, source_text.length].min]
|
|
41
|
+
if upto - presult < 100
|
|
42
|
+
part2 = source_text[presult...upto]
|
|
43
|
+
else
|
|
44
|
+
part2 = source_text[presult, 50] + "......." + source_text[upto-50, 50]
|
|
45
|
+
end
|
|
46
|
+
warn "*** Look for syntax problems around the #{
|
|
47
|
+
"%%%".colorize(background: :light_yellow)} markers:\n#{
|
|
48
|
+
part1}#{"%%%".colorize(color: :green, background: :light_yellow)}#{
|
|
49
|
+
part2}#{"%%%".colorize(color: :red, background: :light_yellow)}#{
|
|
50
|
+
part3}"
|
|
51
|
+
raise ParseError, "*** Parse error at #{presult} upto #{upto} of #{
|
|
52
|
+
source_text.length} (#{expected_length})."
|
|
26
53
|
end
|
|
27
54
|
puts @abnf.ast? if $debug_ast
|
|
28
55
|
@ast = @abnf.ast?
|
|
56
|
+
# our little argument stack for rule processing
|
|
57
|
+
@insides = []
|
|
29
58
|
end
|
|
30
59
|
|
|
31
60
|
def apr # for debugging
|
|
32
61
|
@abnf.parse_results
|
|
33
62
|
end
|
|
34
63
|
|
|
64
|
+
def ast_debug
|
|
65
|
+
ast.to_s[/.*comment/m] # stop at first comment -- prelude
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def strip_nodes(n)
|
|
69
|
+
[n[0], *n[1..-1].map {|e|
|
|
70
|
+
e._strip
|
|
71
|
+
}]
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def walk(rule, anno = nil, &block)
|
|
75
|
+
r = []
|
|
76
|
+
case rule
|
|
77
|
+
when Array
|
|
78
|
+
r.concat(Array(yield rule, anno))
|
|
79
|
+
case rule[0]
|
|
80
|
+
when :type1, :array, :map
|
|
81
|
+
a = (rule.cbor_annotations rescue nil) if rule.size == 2
|
|
82
|
+
a = a.first if a
|
|
83
|
+
r.concat(rule[1..-1].map{|x| walk(x, a, &block)})
|
|
84
|
+
when :grpchoice
|
|
85
|
+
r.concat(rule[1..-1].map{|x| x.flat_map {|y| walk(y, &block)}})
|
|
86
|
+
when :member
|
|
87
|
+
r << walk(rule[3], &block)
|
|
88
|
+
r << walk(rule[4], &block)
|
|
89
|
+
when :anno
|
|
90
|
+
r << walk(rule[2], &block)
|
|
91
|
+
r << walk(rule[3], &block)
|
|
92
|
+
else
|
|
93
|
+
# p ["LEAF", rule[0]]
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
r.compact
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def cname(s)
|
|
100
|
+
s.to_s.gsub(/-/, "_")
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# Generate some simple #define lines from the value-only rules
|
|
104
|
+
def defines(prefix)
|
|
105
|
+
prefix ||= "CDDL"
|
|
106
|
+
if prefix =~ /%\d*\$?s/
|
|
107
|
+
format = prefix
|
|
108
|
+
format += "\n" unless format[-1] == "\n"
|
|
109
|
+
else
|
|
110
|
+
format = "#define #{prefix}_%s %s\n"
|
|
111
|
+
end
|
|
112
|
+
s = {} # keys form crude set of defines
|
|
113
|
+
add = proc { |*a| s[format % a] = true }
|
|
114
|
+
r = rules
|
|
115
|
+
ast.each :rule do |rule|
|
|
116
|
+
if rulename = rule.typename
|
|
117
|
+
t = rule.type.children(:type1)
|
|
118
|
+
if t.size == 1
|
|
119
|
+
if (t2 = t.first.children(:type2)) && t2.size == 1 && (v = t2.first.value)
|
|
120
|
+
add.(cname(rulename), v.to_s)
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
# CBOR::PP.pp r
|
|
126
|
+
walk(r) do |subtree, anno|
|
|
127
|
+
if subtree[0] == :type1 && subtree[1..-1].all? {|x| x[0] == :int}
|
|
128
|
+
if enumname = subtree.cbor_annotations rescue nil
|
|
129
|
+
enumname = cname(enumname.first)
|
|
130
|
+
subtree[1..-1].each do |x|
|
|
131
|
+
if memname = x.cbor_annotations
|
|
132
|
+
memname = "#{enumname}_#{cname(memname.first)}"
|
|
133
|
+
add.(memname, x[1].to_s)
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
if subtree[0] == :array
|
|
139
|
+
if (arrayname = subtree.cbor_annotations rescue nil) || anno
|
|
140
|
+
arrayname = cname(arrayname ? arrayname.first : anno)
|
|
141
|
+
subtree[1..-1].each_with_index do |x, i|
|
|
142
|
+
if x[0] == :member
|
|
143
|
+
if Array === x[3] && x[3][0] == :text
|
|
144
|
+
memname = x[3][1] # preferably use key string
|
|
145
|
+
elsif memname = x[4].cbor_annotations
|
|
146
|
+
memname = memname.first # use value annotation otherwise
|
|
147
|
+
end
|
|
148
|
+
if memname
|
|
149
|
+
memname = "#{arrayname}_#{cname(memname)}_index"
|
|
150
|
+
add.(memname, i.to_s)
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
if x[0] == :member && (x[1] != 1 || x[2] != 1)
|
|
154
|
+
break # can't give numbers if we have optionals etc.
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
s.keys.join
|
|
161
|
+
end
|
|
162
|
+
|
|
35
163
|
def rules
|
|
36
164
|
@rules = {}
|
|
165
|
+
@generics = {}
|
|
166
|
+
@bindings = [{}]
|
|
37
167
|
ast.each :rule do |rule|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
168
|
+
rule_ast =
|
|
169
|
+
if rulename = rule.groupname
|
|
170
|
+
[:grpent, rule.grpent]
|
|
171
|
+
elsif rulename = rule.typename
|
|
172
|
+
[:type1, *rule.type.children(:type1)]
|
|
173
|
+
else
|
|
174
|
+
fail "Huh?"
|
|
175
|
+
end
|
|
176
|
+
n = rulename.to_s
|
|
177
|
+
asg = rule.assign.to_s
|
|
178
|
+
if g = rule.genericparm
|
|
179
|
+
if asg != "="
|
|
180
|
+
fail "Augment #{asg.inspect} not implemented for generics"
|
|
181
|
+
end
|
|
182
|
+
ids = g.children(:id).map(&:to_s)
|
|
183
|
+
# puts ["ids", ids].inspect
|
|
184
|
+
if b = @generics[n]
|
|
185
|
+
fail "Duplicate generics definition #{n} as #{rule_ast} (was #{b})"
|
|
186
|
+
end
|
|
187
|
+
@generics[n] = [rule_ast, ids]
|
|
45
188
|
else
|
|
46
|
-
|
|
189
|
+
case asg
|
|
190
|
+
when "="
|
|
191
|
+
if @rules[n]
|
|
192
|
+
a = strip_nodes(rule_ast).inspect
|
|
193
|
+
b = strip_nodes(@rules[n]).inspect
|
|
194
|
+
if a == b
|
|
195
|
+
warn "*** Warning: Identical redefinition of #{n} as #{a}"
|
|
196
|
+
else
|
|
197
|
+
fail "Duplicate rule definition #{n} as #{b} (was #{a})"
|
|
198
|
+
end
|
|
199
|
+
end
|
|
200
|
+
@rules[n] = rule_ast
|
|
201
|
+
when "/="
|
|
202
|
+
@rules[n] ||= [:type1]
|
|
203
|
+
fail "Can't add #{rule_ast} to #{n}" unless rule_ast[0] == :type1
|
|
204
|
+
# XXX need to check existing rule as well
|
|
205
|
+
@rules[n].concat rule_ast[1..-1]
|
|
206
|
+
# puts "#{@rules[n].inspect} /="
|
|
207
|
+
when "//="
|
|
208
|
+
@rules[n] ||= [:grpchoice]
|
|
209
|
+
fail "Can't add #{rule_ast} to #{n}" unless rule_ast[0] == :grpent
|
|
210
|
+
# XXX need to check existing rule as well
|
|
211
|
+
if @rules[n][0] == :grpent # widen
|
|
212
|
+
@rules[n] = [:grpchoice, @rules[n]]
|
|
213
|
+
end
|
|
214
|
+
@rules[n] << rule_ast
|
|
215
|
+
# puts "#{@rules[n].inspect} //="
|
|
216
|
+
else
|
|
217
|
+
fail "Unknown assign #{asg.inspect}"
|
|
218
|
+
end
|
|
47
219
|
end
|
|
48
220
|
end
|
|
49
|
-
# pp @
|
|
50
|
-
@rootrule = @rules.keys.first
|
|
221
|
+
# pp @generics
|
|
222
|
+
@rootrule = @rules.keys.first # DRAFT: generics are ignored here.
|
|
51
223
|
# now process the rules...
|
|
52
224
|
@stage1 = {}
|
|
53
|
-
r_process(@rootrule, @rules[@rootrule])
|
|
54
|
-
|
|
55
|
-
|
|
225
|
+
result = r_process(@rootrule, @rules[@rootrule])
|
|
226
|
+
r_process("used_in_cddl_prelude", @rules["used_in_cddl_prelude"])
|
|
227
|
+
@rules.each do |n, r|
|
|
228
|
+
# r_process(n, r) # debug only loop
|
|
229
|
+
warn "*** Unused rule #{n}" unless @stage1[n]
|
|
230
|
+
end
|
|
231
|
+
if result[0] == :grpent
|
|
232
|
+
warn "Group at top -- first rule must be a type!"
|
|
233
|
+
end
|
|
56
234
|
# end
|
|
57
235
|
# @stage1
|
|
236
|
+
result
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
def lookup_recurse_grpent(name)
|
|
240
|
+
rule = @stage1[name]
|
|
241
|
+
# pp rule
|
|
242
|
+
fail unless rule.size == 2
|
|
243
|
+
[rule[0], *rule[1]]
|
|
58
244
|
end
|
|
59
245
|
|
|
60
|
-
|
|
246
|
+
# Memoize a bit here
|
|
247
|
+
|
|
248
|
+
REGEXP_FOR_STRING = Hash.new {|h, k|
|
|
249
|
+
h[k] = Regexp.new("\\A#{k}\\z")
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
def generate
|
|
253
|
+
@recursion = 0
|
|
254
|
+
generate1(rules)
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
def generate1(where, inmap = false)
|
|
61
258
|
case where[0]
|
|
62
259
|
when :type1
|
|
63
|
-
generate
|
|
260
|
+
fail BackTrack.new("Can't generate from empty type choice socket yet") unless where.size > 1
|
|
261
|
+
begin
|
|
262
|
+
chosen = where[rand(where.size-1)+1]
|
|
263
|
+
generate1(chosen)
|
|
264
|
+
rescue BackTrack
|
|
265
|
+
tries = where[1..-1].sample(where.size) - [chosen]
|
|
266
|
+
r = begin
|
|
267
|
+
if tries.empty?
|
|
268
|
+
BackTrack.new("No suitable alternative in type choice")
|
|
269
|
+
else
|
|
270
|
+
generate1(tries.pop)
|
|
271
|
+
end
|
|
272
|
+
rescue BackTrack
|
|
273
|
+
retry
|
|
274
|
+
end
|
|
275
|
+
fail r if BackTrack === r
|
|
276
|
+
r
|
|
277
|
+
end
|
|
278
|
+
when :grpchoice
|
|
279
|
+
fail BackTrack.new("Can't generate from empty group choice socket yet") unless where.size > 1
|
|
280
|
+
begin
|
|
281
|
+
chosen = where[rand(where.size-1)+1]
|
|
282
|
+
chosen.flat_map {|m| generate1(m, inmap)}
|
|
283
|
+
rescue BackTrack
|
|
284
|
+
tries = where[1..-1].sample(where.size) - [chosen]
|
|
285
|
+
r = begin
|
|
286
|
+
if tries.empty?
|
|
287
|
+
BackTrack.new("No suitable alternative in group choice")
|
|
288
|
+
else
|
|
289
|
+
tries.pop.flat_map {|m| generate1(m, inmap)}
|
|
290
|
+
end
|
|
291
|
+
rescue BackTrack
|
|
292
|
+
retry
|
|
293
|
+
end
|
|
294
|
+
fail r if BackTrack === r
|
|
295
|
+
r
|
|
296
|
+
end
|
|
64
297
|
when :map
|
|
65
|
-
Hash[where[1..-1].flat_map {|m|
|
|
66
|
-
when :
|
|
67
|
-
|
|
298
|
+
Hash[where[1..-1].flat_map {|m| generate1(m, true)}]
|
|
299
|
+
when :recurse_grpent
|
|
300
|
+
name = where[1]
|
|
301
|
+
rule = lookup_recurse_grpent(name)
|
|
302
|
+
if @recursion < MAX_RECURSE
|
|
303
|
+
@recursion += 1
|
|
304
|
+
#p ["recurse_grpent", *rule]
|
|
305
|
+
#r = generate1(rule)
|
|
306
|
+
r = rule[1..-1].flat_map {|m| generate1(m, inmap)}
|
|
307
|
+
@recursion -= 1
|
|
308
|
+
r
|
|
309
|
+
else
|
|
310
|
+
fail "Deep recursion into #{name}: #{@stage1[name]}, not yet implemented"
|
|
311
|
+
end
|
|
312
|
+
when :array, :grpent
|
|
313
|
+
r = where[1..-1].flat_map {|m| generate1(m).map{|e| e[1]}}
|
|
314
|
+
.flat_map {|e| Array === e && e[0] == :grpent ? e[1..-1] : [e]}
|
|
315
|
+
# nested grpents need to be "unpacked"
|
|
316
|
+
if where[0] == :grpent
|
|
317
|
+
[:grpent, *r]
|
|
318
|
+
else
|
|
319
|
+
r
|
|
320
|
+
end
|
|
68
321
|
when :member
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
322
|
+
st = where[1]
|
|
323
|
+
fudge = 4 * (1 - (@recursion / MAX_RECURSE.to_f))**3 # get less generate-happy with more recursion
|
|
324
|
+
en = [where[2], [st, fudge].max].min # truncate to fudge unless must be more
|
|
325
|
+
st += rand(en + 1 - st) if en != st
|
|
73
326
|
kr = where[3]
|
|
74
327
|
vr = where[4]
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
328
|
+
if inmap
|
|
329
|
+
unless kr
|
|
330
|
+
case vr[0]
|
|
331
|
+
when :grpent
|
|
332
|
+
fail "grpent in map #{vr.inspect}" unless vr.size == 2
|
|
333
|
+
g = Array.new(st) { generate1(vr[1], true) }.flatten(1)
|
|
334
|
+
# warn "GGG #{g.inspect}"
|
|
335
|
+
return g
|
|
336
|
+
when :grpchoice
|
|
337
|
+
g = Array.new(st) { generate1(vr, true) }.flatten(1)
|
|
338
|
+
# warn "GGG #{g.inspect}"
|
|
339
|
+
return g
|
|
340
|
+
else
|
|
341
|
+
fail "member key not given in map for #{where}" # || vr == [:grpchoice]
|
|
342
|
+
end
|
|
343
|
+
end
|
|
344
|
+
end
|
|
345
|
+
begin
|
|
346
|
+
Array.new(st) { [ (generate1(kr) if kr), # XXX: need error in map context
|
|
347
|
+
generate1(vr)
|
|
348
|
+
]}
|
|
349
|
+
rescue BackTrack
|
|
350
|
+
fail BackTrack.new("Need #{where[1]}..#{where[2]} of these: #{[kr, vr].inspect}") unless where[1] == 0
|
|
351
|
+
[]
|
|
352
|
+
end
|
|
353
|
+
when :text, :int, :float, :bytes
|
|
80
354
|
where[1]
|
|
355
|
+
when :range
|
|
356
|
+
rand(where[1])
|
|
81
357
|
when :prim
|
|
82
358
|
case where[1]
|
|
83
359
|
when nil
|
|
84
|
-
gen_word
|
|
360
|
+
gen_word # XXX: maybe always returning a string is confusing
|
|
85
361
|
when 0
|
|
86
362
|
rand(4711)
|
|
87
363
|
when 1
|
|
@@ -90,6 +366,8 @@ module CDDL
|
|
|
90
366
|
gen_word.force_encoding(Encoding::BINARY)
|
|
91
367
|
when 3
|
|
92
368
|
gen_word
|
|
369
|
+
when 6
|
|
370
|
+
CBOR::Tagged.new(where[2], generate1(where[3]))
|
|
93
371
|
when 7
|
|
94
372
|
case where[2]
|
|
95
373
|
when nil
|
|
@@ -108,22 +386,154 @@ module CDDL
|
|
|
108
386
|
else
|
|
109
387
|
fail "Can't generate prim #{where[1]}"
|
|
110
388
|
end
|
|
389
|
+
when :anno
|
|
390
|
+
target = where[2]
|
|
391
|
+
control = where[3]
|
|
392
|
+
case where[1]
|
|
393
|
+
when :size
|
|
394
|
+
should_be_int = generate1(control)
|
|
395
|
+
unless (Array === target && target[0] == :prim && [0, 2, 3].include?(target[1])) && Integer === should_be_int && should_be_int >= 0
|
|
396
|
+
fail "Don't know yet how to generate #{where}"
|
|
397
|
+
end
|
|
398
|
+
s = Random.new.bytes(should_be_int)
|
|
399
|
+
case target[1]
|
|
400
|
+
when 0
|
|
401
|
+
# silently restrict to what we can put into a uint
|
|
402
|
+
s[0...8].bytes.inject(0) {|a, b| a << 8 | b }
|
|
403
|
+
when 2
|
|
404
|
+
s
|
|
405
|
+
when 3
|
|
406
|
+
Base64.urlsafe_encode64(s)[0...should_be_int]
|
|
407
|
+
# XXX generate word a la w = gen_word
|
|
408
|
+
end
|
|
409
|
+
when :bits
|
|
410
|
+
set_of_bits = Array.new(10) { generate1(control) } # XXX: ten?
|
|
411
|
+
# p set_of_bits
|
|
412
|
+
unless (target == [:prim, 0] || target == [:prim, 2]) &&
|
|
413
|
+
set_of_bits.all? {|x| Integer === x && x >= 0 }
|
|
414
|
+
fail "Don't know yet how to generate #{where}"
|
|
415
|
+
end
|
|
416
|
+
if target == [:prim, 2]
|
|
417
|
+
set_of_bits.inject(String.new) do |s, i|
|
|
418
|
+
n = i >> 3
|
|
419
|
+
bit = 1 << (i & 7)
|
|
420
|
+
if v = s.getbyte(n)
|
|
421
|
+
s.setbyte(n, v | bit); s
|
|
422
|
+
else
|
|
423
|
+
s << "\0" * (n - s.size) << bit.chr(Encoding::BINARY)
|
|
424
|
+
end
|
|
425
|
+
end
|
|
426
|
+
else # target == [:prim, 0]
|
|
427
|
+
set_of_bits.inject(0) do |a, v|
|
|
428
|
+
a |= (1 << v)
|
|
429
|
+
end
|
|
430
|
+
end
|
|
431
|
+
when :default
|
|
432
|
+
# Hmm.
|
|
433
|
+
unless $default_warned
|
|
434
|
+
warn "*** Ignoring .default for now."
|
|
435
|
+
$default_warned = true
|
|
436
|
+
end
|
|
437
|
+
generate1(target, inmap)
|
|
438
|
+
when :eq
|
|
439
|
+
content = generate1(control)
|
|
440
|
+
if validate1(content, where)
|
|
441
|
+
return content
|
|
442
|
+
end
|
|
443
|
+
fail "Not smart enough to generate #{where}"
|
|
444
|
+
when :lt, :le, :gt, :ge, :ne
|
|
445
|
+
if Array === target && target[0] == :prim
|
|
446
|
+
content = generate1(control)
|
|
447
|
+
try = if Numeric === content
|
|
448
|
+
content = Integer(content)
|
|
449
|
+
case target[1]
|
|
450
|
+
when 0
|
|
451
|
+
case where[1]
|
|
452
|
+
when :lt
|
|
453
|
+
rand(0...content)
|
|
454
|
+
when :le
|
|
455
|
+
rand(0..content)
|
|
456
|
+
end
|
|
457
|
+
end
|
|
458
|
+
end
|
|
459
|
+
if validate1(try, where)
|
|
460
|
+
return try
|
|
461
|
+
else
|
|
462
|
+
warn "HUH gen #{where.inspect} #{try.inspect}" unless try.nil?
|
|
463
|
+
end
|
|
464
|
+
end
|
|
465
|
+
32.times do
|
|
466
|
+
content = generate1(target)
|
|
467
|
+
if validate1(content, where)
|
|
468
|
+
return content
|
|
469
|
+
end
|
|
470
|
+
end
|
|
471
|
+
fail "Not smart enough to generate #{where}"
|
|
472
|
+
when :regexp
|
|
473
|
+
regexp = generate1(control)
|
|
474
|
+
unless target == [:prim, 3] && String === regexp
|
|
475
|
+
fail "Don't know yet how to generate #{where}"
|
|
476
|
+
end
|
|
477
|
+
REGEXP_FOR_STRING[regexp].random_example(max_repeater_variance: 5)
|
|
478
|
+
when :cbor, :cborseq
|
|
479
|
+
unless target == [:prim, 2]
|
|
480
|
+
fail "Don't know yet how to generate #{where}"
|
|
481
|
+
end
|
|
482
|
+
content = CBOR::encode(generate1(control))
|
|
483
|
+
if where[1] == :cborseq
|
|
484
|
+
# remove the first head
|
|
485
|
+
n = case content.getbyte(0) - (4 << 5)
|
|
486
|
+
when 0..23; 1
|
|
487
|
+
when 24; 2
|
|
488
|
+
when 25; 3
|
|
489
|
+
when 26; 5
|
|
490
|
+
when 27; 9 # unlikely :-)
|
|
491
|
+
else fail "Generated .cborseq sequence for #{where} not an array"
|
|
492
|
+
end
|
|
493
|
+
content[0...n] = ''
|
|
494
|
+
end
|
|
495
|
+
content
|
|
496
|
+
when :within, :and
|
|
497
|
+
32.times do
|
|
498
|
+
content = generate1(target)
|
|
499
|
+
if validate1(content, control)
|
|
500
|
+
return content
|
|
501
|
+
elsif where[1] == :within
|
|
502
|
+
warn "*** #{content.inspect} meets #{target.inspect} but not #{control.inspect}"
|
|
503
|
+
end
|
|
504
|
+
end
|
|
505
|
+
fail "Not smart enough to generate #{where}"
|
|
506
|
+
else
|
|
507
|
+
fail "Don't know yet how to generate from #{where}"
|
|
508
|
+
end
|
|
509
|
+
when :recurse
|
|
510
|
+
name = where[1]
|
|
511
|
+
rule = @stage1[name]
|
|
512
|
+
if @recursion < MAX_RECURSE
|
|
513
|
+
@recursion += 1
|
|
514
|
+
#p ["recurse", *rule]
|
|
515
|
+
r = generate1(rule)
|
|
516
|
+
@recursion -= 1
|
|
517
|
+
r
|
|
518
|
+
else
|
|
519
|
+
fail "Deep recursion into #{name}: #{@stage1[name]}, not yet implemented"
|
|
520
|
+
end
|
|
111
521
|
else
|
|
112
|
-
#
|
|
522
|
+
fail "Don't know how to generate from #{where[0]} in #{where.inspect}"
|
|
113
523
|
end
|
|
114
524
|
end
|
|
115
525
|
|
|
116
|
-
VALUE_TYPE = {
|
|
526
|
+
VALUE_TYPE = {text: String, int: Integer, float: Float}
|
|
117
527
|
SIMPLE_VALUE = {
|
|
118
|
-
[:prim, 7, 20] => [true, false],
|
|
119
|
-
[:prim, 7, 21] => [true, true],
|
|
120
|
-
[:prim, 7, 22] => [true, nil],
|
|
121
|
-
[:prim, 7, 23] => [true, :undefined],
|
|
528
|
+
[:prim, 7, 20] => [true, false, :bool],
|
|
529
|
+
[:prim, 7, 21] => [true, true, :bool],
|
|
530
|
+
[:prim, 7, 22] => [true, nil, :nil],
|
|
531
|
+
[:prim, 7, 23] => [true, :undefined, :undefined],
|
|
122
532
|
}
|
|
123
533
|
|
|
124
|
-
def extract_value(t)
|
|
125
|
-
if VALUE_TYPE[t[0]]
|
|
126
|
-
[true, t[1]]
|
|
534
|
+
def extract_value(t) # []
|
|
535
|
+
if vt = VALUE_TYPE[t[0]]
|
|
536
|
+
[true, t[1], vt]
|
|
127
537
|
elsif v = SIMPLE_VALUE[t]
|
|
128
538
|
v
|
|
129
539
|
else
|
|
@@ -136,10 +546,11 @@ module CDDL
|
|
|
136
546
|
end
|
|
137
547
|
|
|
138
548
|
def validate(d, warn=true)
|
|
139
|
-
|
|
549
|
+
@recursion = 0
|
|
550
|
+
result = validate1a(d, rules)
|
|
140
551
|
unless result
|
|
141
552
|
if warn
|
|
142
|
-
warn "CDDL validation failure:"
|
|
553
|
+
warn "CDDL validation failure (#{result.inspect} for #{d.inspect}):"
|
|
143
554
|
PP::pp(validate_diag, STDERR)
|
|
144
555
|
end
|
|
145
556
|
end
|
|
@@ -153,67 +564,267 @@ module CDDL
|
|
|
153
564
|
)
|
|
154
565
|
end
|
|
155
566
|
|
|
156
|
-
def
|
|
157
|
-
#
|
|
158
|
-
|
|
567
|
+
def validate_forward(d, start, where)
|
|
568
|
+
# warn ["valforw", d, start, where].inspect
|
|
569
|
+
i = 0
|
|
570
|
+
ann = []
|
|
571
|
+
where[1..-1].each { |r|
|
|
572
|
+
t, s, e, _k, v = r # XXX
|
|
573
|
+
if t == :recurse_grpent
|
|
574
|
+
rule = lookup_recurse_grpent(s)
|
|
575
|
+
n, ann2 = validate_linear(d, start+i, rule)
|
|
576
|
+
return [false, ann] unless n
|
|
577
|
+
i += n
|
|
578
|
+
ann.concat(ann2)
|
|
579
|
+
elsif t == :grpchoice
|
|
580
|
+
return [false, ann] unless r[1..-1].any? {|cand|
|
|
581
|
+
n, ann2 = validate_forward(d, start+i, [:foo, *cand])
|
|
582
|
+
if n
|
|
583
|
+
i += n
|
|
584
|
+
ann.concat(ann2)
|
|
585
|
+
end}
|
|
586
|
+
else
|
|
587
|
+
fail r.inspect unless t == :member
|
|
588
|
+
occ = 0
|
|
589
|
+
while ((occ < e) && i != d.size && ((n, ann2 = validate_linear(d, start+i, v)); n))
|
|
590
|
+
i += n
|
|
591
|
+
occ += 1
|
|
592
|
+
ann.concat(ann2)
|
|
593
|
+
end
|
|
594
|
+
if occ < s
|
|
595
|
+
@last_message = "occur not reached in array #{d} for #{where}"
|
|
596
|
+
return [false, ann]
|
|
597
|
+
end
|
|
598
|
+
end
|
|
599
|
+
}
|
|
600
|
+
# warn ["valforw>", i].inspect
|
|
601
|
+
[i, ann]
|
|
602
|
+
end
|
|
603
|
+
|
|
604
|
+
# returns number of matches or false for breakage
|
|
605
|
+
def validate_linear(d, start, where)
|
|
606
|
+
# warn ["vallin", d, start, where].inspect
|
|
607
|
+
fail unless Array === d
|
|
608
|
+
case where[0]
|
|
609
|
+
when :grpent
|
|
610
|
+
# must be inside an array with nested occurrences
|
|
611
|
+
validate_forward(d, start, where)
|
|
612
|
+
else
|
|
613
|
+
(ann = validate1a(d[start], where)) ? [1, ann] : [false, ann]
|
|
614
|
+
end
|
|
615
|
+
end
|
|
616
|
+
|
|
617
|
+
def map_check(d, d_check, members)
|
|
618
|
+
anno = []
|
|
619
|
+
anno if members.all? { |r|
|
|
620
|
+
puts "ALL SUBRULE: #{r.inspect}" if ENV["CDDL_TRACE"]
|
|
621
|
+
t, s, e, k, v = r
|
|
622
|
+
case t
|
|
623
|
+
when :recurse_grpent
|
|
624
|
+
rule = lookup_recurse_grpent(s)
|
|
625
|
+
if ann2 = map_check(d, d_check, rule[1..-1])
|
|
626
|
+
anno.concat(ann2)
|
|
627
|
+
end
|
|
628
|
+
when :grpchoice
|
|
629
|
+
r[1..-1].any? {|cand|
|
|
630
|
+
puts "CHOICE SUBRULE: #{cand.inspect}" if ENV["CDDL_TRACE"]
|
|
631
|
+
cand_d_check = d_check.dup
|
|
632
|
+
if ann2 = map_check(d, cand_d_check, cand)
|
|
633
|
+
puts "CHOICE SUBRULE SUCCESS: #{cand.inspect}" if ENV["CDDL_TRACE"]
|
|
634
|
+
d_check.replace(cand_d_check)
|
|
635
|
+
anno.concat(ann2)
|
|
636
|
+
end
|
|
637
|
+
}
|
|
638
|
+
when :member
|
|
639
|
+
unless k
|
|
640
|
+
case v[0]
|
|
641
|
+
when :grpent
|
|
642
|
+
entries = v[1..-1]
|
|
643
|
+
when :grpchoice
|
|
644
|
+
entries = [v]
|
|
645
|
+
else
|
|
646
|
+
fail "member name not known for group entry #{r} in map"
|
|
647
|
+
end
|
|
648
|
+
d_check1 = d_check.dup
|
|
649
|
+
occ = 0
|
|
650
|
+
ann2 = []
|
|
651
|
+
while occ < e && (ann3 = map_check(d, d_check1, entries)) && ann3 != []
|
|
652
|
+
occ += 1
|
|
653
|
+
ann2.concat(ann3)
|
|
654
|
+
end
|
|
655
|
+
if occ >= s
|
|
656
|
+
d_check.replace(d_check1)
|
|
657
|
+
anno.concat(ann2)
|
|
658
|
+
puts "OCC SATISFIED: #{occ.inspect} >= #{s.inspect}" if ENV["CDDL_TRACE"]
|
|
659
|
+
anno
|
|
660
|
+
else
|
|
661
|
+
# leave some diagnostic breadcrumbs?
|
|
662
|
+
puts "OCC UNSATISFIED: #{occ.inspect} < #{s.inspect}" if ENV["CDDL_TRACE"]
|
|
663
|
+
false
|
|
664
|
+
end
|
|
665
|
+
else
|
|
666
|
+
# this is mostly quadratic; let's do the linear thing if possible
|
|
667
|
+
simple, simpleval = extract_value(k)
|
|
668
|
+
if simple
|
|
669
|
+
puts "SIMPLE: #{d_check.inspect} #{simpleval}" if ENV["CDDL_TRACE"]
|
|
670
|
+
# add occurrence check; check that val is present in the first place
|
|
671
|
+
actual = d.fetch(simpleval, :not_found)
|
|
672
|
+
if actual == :not_found
|
|
673
|
+
s == 0 # minimum occurrence must be 0 then
|
|
674
|
+
else
|
|
675
|
+
if (ann2 = validate1a(actual, v)) &&
|
|
676
|
+
d_check.delete(simpleval) {:not_found} != :not_found
|
|
677
|
+
anno.concat(ann2)
|
|
678
|
+
end
|
|
679
|
+
end
|
|
680
|
+
else
|
|
681
|
+
puts "COMPLEX: #{k.inspect} #{simple.inspect} #{simpleval.inspect}" if ENV["CDDL_TRACE"]
|
|
682
|
+
keys = d_check.keys
|
|
683
|
+
ta, keys = keys.partition{ |key| validate1(key, k)}
|
|
684
|
+
# XXX check ta.size against s/e
|
|
685
|
+
ta.all? { |val|
|
|
686
|
+
if (ann2 = validate1a(d[val], v)) &&
|
|
687
|
+
d_check.delete(val) {:not_found} != :not_found
|
|
688
|
+
anno.concat(ann2)
|
|
689
|
+
end
|
|
690
|
+
}
|
|
691
|
+
end
|
|
692
|
+
end
|
|
693
|
+
else
|
|
694
|
+
fail "Cannot validate #{t} in maps yet #{r}" # MMM
|
|
695
|
+
end
|
|
696
|
+
}
|
|
697
|
+
end
|
|
698
|
+
|
|
699
|
+
def validate1a(d, where)
|
|
700
|
+
if ann = validate1(d, where)
|
|
701
|
+
here = [d, where]
|
|
702
|
+
if Array === ann
|
|
703
|
+
[here, *ann]
|
|
704
|
+
else
|
|
705
|
+
[here]
|
|
706
|
+
end
|
|
707
|
+
end
|
|
708
|
+
end
|
|
709
|
+
|
|
710
|
+
OPERATORS = {lt: :<, le: :<=, gt: :>, ge: :>=, eq: :==, ne: :!=}
|
|
711
|
+
|
|
712
|
+
def validate1(d, where)
|
|
713
|
+
if ENV["CDDL_TRACE"]
|
|
714
|
+
puts "DATA: #{d.inspect}"
|
|
715
|
+
puts "RULE: #{where.inspect}"
|
|
716
|
+
end
|
|
717
|
+
# warn ["val1", d, where].inspect
|
|
159
718
|
@last_data = d
|
|
160
719
|
@last_rule = where
|
|
720
|
+
ann = nil
|
|
161
721
|
case where[0]
|
|
162
722
|
when :type1
|
|
163
|
-
where[1..-1].any? {|r|
|
|
723
|
+
if where[1..-1].any? {|r| ann = validate1a(d, r)}
|
|
724
|
+
ann
|
|
725
|
+
end
|
|
164
726
|
when :map
|
|
165
727
|
if Hash === d
|
|
166
728
|
d_check = d.dup
|
|
167
|
-
where[1..-1]
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
fail "member name not known for group entry #{r} in map" unless k
|
|
173
|
-
simple, simpleval = extract_value(k)
|
|
174
|
-
if simple
|
|
175
|
-
# puts "SIMPLE: #{d_check.inspect} #{simpleval}"
|
|
176
|
-
# add occurrence check; check that val is present in the first place
|
|
177
|
-
actual = d.fetch(simpleval, :not_found)
|
|
178
|
-
if actual == :not_found
|
|
179
|
-
s == 0 # minimum occurrence must be 0 then
|
|
180
|
-
else
|
|
181
|
-
validate1(actual, v) && d_check.delete(simpleval)
|
|
182
|
-
end
|
|
183
|
-
else
|
|
184
|
-
# puts "COMPLEX: #{k.inspect} #{simple.inspect} #{simpleval.inspect}"
|
|
185
|
-
keys = d_check.keys
|
|
186
|
-
ta, keys = keys.partition{ |key| validate1(key, k)}
|
|
187
|
-
# XXX check ta.size against s/e
|
|
188
|
-
ta.all? { |val|
|
|
189
|
-
validate1(d[val], v) && d_check.delete(val)
|
|
190
|
-
}
|
|
729
|
+
if (ann = map_check(d, d_check, where[1..-1])) && d_check == {}
|
|
730
|
+
ann
|
|
731
|
+
else
|
|
732
|
+
if ENV["CDDL_TRACE"]
|
|
733
|
+
puts "MAP RESIDUAL: #{d_check.inspect} for #{where[1..-1]} and #{d.inspect}"
|
|
191
734
|
end
|
|
192
|
-
|
|
735
|
+
end
|
|
193
736
|
end
|
|
194
737
|
when :array
|
|
738
|
+
# warn ["valarr", d, where].inspect
|
|
195
739
|
if Array === d
|
|
196
740
|
# validate1 against the record
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
741
|
+
idx, ann = validate_forward(d, 0, where)
|
|
742
|
+
ann if validate_result(idx == d.size) { "#{validate_diag.inspect} -- cannot complete (#{idx}, #{d.size}) array #{d} for #{where}" }
|
|
743
|
+
end
|
|
744
|
+
when :text, :int, :float, :bytes
|
|
745
|
+
_, v = extract_value(where)
|
|
746
|
+
[] if d == v
|
|
747
|
+
when :range
|
|
748
|
+
[] if where[2] === d && where[1].include?(d)
|
|
749
|
+
when :anno
|
|
750
|
+
target = where[2]
|
|
751
|
+
if ann = validate1a(d, target)
|
|
752
|
+
control = where[3]
|
|
753
|
+
case where[1]
|
|
754
|
+
when :size
|
|
755
|
+
case d
|
|
756
|
+
when Integer
|
|
757
|
+
ok, v, vt = extract_value(control)
|
|
758
|
+
if ok && vt == Integer
|
|
759
|
+
ann if (d >> (8*v)) == 0
|
|
760
|
+
end
|
|
761
|
+
when String
|
|
762
|
+
ann if validate1(d.bytesize, control)
|
|
205
763
|
end
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
764
|
+
when :bits
|
|
765
|
+
if String === d
|
|
766
|
+
d.each_byte.with_index.all? { |b, i|
|
|
767
|
+
bit = i << 3
|
|
768
|
+
ann if 8.times.all? { |nb|
|
|
769
|
+
b[nb] == 0 || validate1(bit+nb, control)
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
elsif Integer === d
|
|
773
|
+
if d >= 0
|
|
774
|
+
ok = true
|
|
775
|
+
i = 0
|
|
776
|
+
while ok && d > 0
|
|
777
|
+
if d.odd?
|
|
778
|
+
ok &&= validate1(i, control)
|
|
779
|
+
end
|
|
780
|
+
d >>= 1; i += 1
|
|
781
|
+
end
|
|
782
|
+
ann if ok
|
|
783
|
+
end
|
|
209
784
|
end
|
|
210
|
-
|
|
211
|
-
|
|
785
|
+
when :default
|
|
786
|
+
# Hmm.
|
|
787
|
+
unless $default_warned
|
|
788
|
+
warn "*** Ignoring .default for now."
|
|
789
|
+
$default_warned = true
|
|
790
|
+
end
|
|
791
|
+
ann
|
|
792
|
+
when :lt, :le, :gt, :ge, :eq, :ne
|
|
793
|
+
op = OPERATORS[where[1]]
|
|
794
|
+
ok, v, _vt = extract_value(control)
|
|
795
|
+
if ok
|
|
796
|
+
ann if d.send(op, v) rescue nil # XXX Focus ArgumentError
|
|
797
|
+
end
|
|
798
|
+
when :regexp
|
|
799
|
+
ann if (
|
|
800
|
+
if String === d
|
|
801
|
+
ok, v, vt = extract_value(control)
|
|
802
|
+
if ok && vt == String
|
|
803
|
+
re = REGEXP_FOR_STRING[v]
|
|
804
|
+
# pp re
|
|
805
|
+
d.match(re)
|
|
806
|
+
end
|
|
807
|
+
end
|
|
808
|
+
)
|
|
809
|
+
when :cbor
|
|
810
|
+
ann if validate1((CBOR::decode(d) rescue :BAD_CBOR), control)
|
|
811
|
+
when :cborseq
|
|
812
|
+
ann if validate1((CBOR::decode("\x9f".b << d << "\xff".b) rescue :BAD_CBOR), control)
|
|
813
|
+
when :within
|
|
814
|
+
if validate1(d, control)
|
|
815
|
+
ann
|
|
816
|
+
else
|
|
817
|
+
warn "*** #{d.inspect} meets #{target} but not #{control}"
|
|
818
|
+
nil
|
|
819
|
+
end
|
|
820
|
+
when :and
|
|
821
|
+
ann if validate1(d, control)
|
|
822
|
+
else
|
|
823
|
+
fail "Don't know yet how to validate against #{where}"
|
|
824
|
+
end
|
|
212
825
|
end
|
|
213
|
-
when :string, :int, :float
|
|
214
|
-
_, v = extract_value(where)
|
|
215
|
-
d == v
|
|
216
826
|
when :prim
|
|
827
|
+
# warn "validate prim WI #{where.inspect} #{d.inspect}"
|
|
217
828
|
case where[1]
|
|
218
829
|
when nil
|
|
219
830
|
true
|
|
@@ -225,10 +836,12 @@ module CDDL
|
|
|
225
836
|
String === d && d.encoding == Encoding::BINARY
|
|
226
837
|
when 3
|
|
227
838
|
String === d && d.encoding != Encoding::BINARY # cheat
|
|
839
|
+
when 6
|
|
840
|
+
CBOR::Tagged === d && d.tag == where[2] && validate1a(d.data, where[3])
|
|
228
841
|
when 7
|
|
229
842
|
t, v = extract_value(where)
|
|
230
843
|
if t
|
|
231
|
-
v
|
|
844
|
+
v.eql? d
|
|
232
845
|
else
|
|
233
846
|
case where[2]
|
|
234
847
|
when nil
|
|
@@ -240,11 +853,23 @@ module CDDL
|
|
|
240
853
|
fail
|
|
241
854
|
end
|
|
242
855
|
end
|
|
243
|
-
|
|
244
856
|
else
|
|
245
|
-
fail "Can't
|
|
857
|
+
fail "Can't validate prim #{where[1]} yet"
|
|
858
|
+
end
|
|
859
|
+
when :recurse
|
|
860
|
+
name = where[1]
|
|
861
|
+
rule = @stage1[name]
|
|
862
|
+
if @recursion < MAX_RECURSE
|
|
863
|
+
@recursion += 1
|
|
864
|
+
r = validate1a(d, rule)
|
|
865
|
+
@recursion -= 1
|
|
866
|
+
r
|
|
867
|
+
else
|
|
868
|
+
fail "Deep recursion into #{name}: #{rule}, not yet implemented"
|
|
246
869
|
end
|
|
247
870
|
else
|
|
871
|
+
@last_message = "Don't know how to validate #{where}"
|
|
872
|
+
false
|
|
248
873
|
# fail where
|
|
249
874
|
end
|
|
250
875
|
end
|
|
@@ -255,7 +880,7 @@ module CDDL
|
|
|
255
880
|
private
|
|
256
881
|
|
|
257
882
|
def gen_word
|
|
258
|
-
@words ||= File.read("/usr/share/dict/words").lines.shuffle
|
|
883
|
+
@words ||= (File.read("/usr/share/dict/words").lines.shuffle rescue %w{tic tac toe})
|
|
259
884
|
@wordptr ||= 0
|
|
260
885
|
@wordptr = 0 if @wordptr == @words.size
|
|
261
886
|
w = @words[@wordptr].chomp
|
|
@@ -263,99 +888,229 @@ module CDDL
|
|
|
263
888
|
w
|
|
264
889
|
end
|
|
265
890
|
|
|
266
|
-
|
|
891
|
+
def rule_lookup(name, canbegroup)
|
|
892
|
+
if b = @bindings.last[name]
|
|
893
|
+
b
|
|
894
|
+
else
|
|
895
|
+
r = @rules[name]
|
|
896
|
+
unless r
|
|
897
|
+
if name[0] == "$"
|
|
898
|
+
r = @rules[name] = if name[1] == "$"
|
|
899
|
+
[:grpchoice]
|
|
900
|
+
else
|
|
901
|
+
[:type1]
|
|
902
|
+
end
|
|
903
|
+
end
|
|
904
|
+
end
|
|
905
|
+
if r
|
|
906
|
+
t = r_process(name, r)
|
|
907
|
+
unless t[0] == :type1
|
|
908
|
+
fail "#{name} not a type #{t}" unless canbegroup && (t[0] == :grpent || t[0] == :grpchoice)
|
|
909
|
+
end
|
|
910
|
+
t
|
|
911
|
+
end
|
|
912
|
+
end
|
|
913
|
+
end
|
|
914
|
+
|
|
915
|
+
RECURSE_TYPE = {grpent: :recurse_grpent, grpchoice: :recurse_grpent,
|
|
916
|
+
type1: :recurse} # MMM
|
|
917
|
+
RECURSE_TYPE.default_proc = proc do |a|
|
|
918
|
+
fail a.inspect
|
|
919
|
+
end
|
|
267
920
|
|
|
268
|
-
def r_process(n, r)
|
|
921
|
+
def r_process(n, r, bindings = {})
|
|
269
922
|
t = r[0]
|
|
270
923
|
# puts "Processing rule #{n} = #{t}"
|
|
271
|
-
@stage1[n] ||=
|
|
924
|
+
@stage1[n] ||= begin
|
|
925
|
+
@stage1[n] = [t, [RECURSE_TYPE[t], n]]
|
|
926
|
+
@bindings.push(bindings)
|
|
927
|
+
r = [t, *r[1..-1].map {|e|
|
|
928
|
+
case t
|
|
929
|
+
when :grpchoice
|
|
930
|
+
fail e[0].inspect unless e[0] == :grpent
|
|
931
|
+
fail e unless e.size == 2
|
|
932
|
+
res = grpent(e[1])
|
|
933
|
+
# warn "RES #{res.inspect}"
|
|
934
|
+
res
|
|
935
|
+
when :grpent
|
|
936
|
+
grpent(e)
|
|
937
|
+
when :type1
|
|
938
|
+
type1(e, r.size == 2) # a single type1 could be a group
|
|
939
|
+
else
|
|
940
|
+
fail t
|
|
941
|
+
end}]
|
|
942
|
+
@bindings.pop
|
|
943
|
+
r.cbor_annotation_add(n) rescue nil # AAA
|
|
944
|
+
r
|
|
945
|
+
end
|
|
272
946
|
end
|
|
273
947
|
|
|
274
948
|
def value(n)
|
|
275
949
|
# cheat:
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
950
|
+
# warn n
|
|
951
|
+
s = n.to_s
|
|
952
|
+
if s[-1] == "'"
|
|
953
|
+
parts = s.split("'", 3)
|
|
954
|
+
if parts[2] != "" || parts[1] =~ /\\/
|
|
955
|
+
warn "*** Can't handle backslashes in #{s.inspect} yet"
|
|
956
|
+
end
|
|
957
|
+
[:bytes,
|
|
958
|
+
case parts[0]
|
|
959
|
+
when ""
|
|
960
|
+
parts[1].b
|
|
961
|
+
when "h"
|
|
962
|
+
parts[1].gsub(/\s/, "").chars.each_slice(2).map{ |x| Integer(x.join, 16).chr("BINARY") }.join
|
|
963
|
+
when "b64"
|
|
964
|
+
Base64.urlsafe_decode64(parts[1])
|
|
965
|
+
else
|
|
966
|
+
warn "*** Can't handle byte string type #{parts[0].inspect} yet"
|
|
967
|
+
end
|
|
968
|
+
]
|
|
969
|
+
else
|
|
970
|
+
val = eval(n.to_s)
|
|
971
|
+
# warn val
|
|
972
|
+
case val
|
|
973
|
+
when Integer; [:int, val]
|
|
974
|
+
when Numeric; [:float, val]
|
|
975
|
+
when String; [:text, val.force_encoding(Encoding::UTF_8)]
|
|
976
|
+
else fail "huh? value #{val.inspect}"
|
|
977
|
+
end
|
|
978
|
+
end
|
|
979
|
+
end
|
|
980
|
+
|
|
981
|
+
def workaround1(n)
|
|
982
|
+
n.children.each do |ch|
|
|
983
|
+
if ch.to_s == ")"
|
|
984
|
+
# warn "W1 #{ch.inspect} -> #{ch.group.inspect}"
|
|
985
|
+
return ch.group
|
|
986
|
+
end
|
|
987
|
+
end
|
|
988
|
+
nil
|
|
283
989
|
end
|
|
284
990
|
|
|
285
991
|
def grpent(n) # returns array of entries
|
|
286
992
|
occ = occur(n.occur)
|
|
287
|
-
if
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
993
|
+
if g = n.group || workaround1(n)
|
|
994
|
+
gr = group(g)
|
|
995
|
+
if occ != [1, 1]
|
|
996
|
+
gr = [[:member, *occ, nil, [:grpent, *gr]]]
|
|
997
|
+
end
|
|
998
|
+
return gr
|
|
999
|
+
end
|
|
1000
|
+
nt = n.type || (n.s && n.s.type) || (n.bareword && n.bareword.s.type) # workarounds
|
|
1001
|
+
unless nt
|
|
1002
|
+
warn "NO NT"
|
|
1003
|
+
warn @abnf.ast?
|
|
1004
|
+
fail ["ntype", n, n.children].inspect
|
|
1005
|
+
end
|
|
1006
|
+
if mk = n.memberkey # work around unclear bug in ast generation below
|
|
1007
|
+
if (t1 = mk.type1 || t1 = mk.value ||
|
|
1008
|
+
((t1 = mk.s) && (t1 = t1.type) && (t1 = t1.type1))) # workaround
|
|
1009
|
+
[[:member, *occ, type1(t1), type(nt)]]
|
|
293
1010
|
else
|
|
294
|
-
|
|
1011
|
+
bw = mk.bareword
|
|
1012
|
+
unless bw
|
|
1013
|
+
warn @abnf.ast?
|
|
1014
|
+
fail [n, n.children, mk, mk.children].inspect
|
|
1015
|
+
end
|
|
1016
|
+
name = bw.to_s
|
|
1017
|
+
[[:member, *occ, [:text, name.force_encoding(Encoding::UTF_8)], type(nt)]]
|
|
295
1018
|
end
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
1019
|
+
else
|
|
1020
|
+
t = if nbw = nt.bareword
|
|
1021
|
+
t1 = nbw.type1 # || n.bareword.s.type.type1 # workaround
|
|
1022
|
+
rest = type_collect(nt, true)
|
|
1023
|
+
s = [:type1, type1(t1, true), *rest]
|
|
1024
|
+
# warn "T2: #{s.size} #{s}" -- maybe this should have a parenthesis warning?
|
|
1025
|
+
s.size == 2 ? s[1] : s # decapsulate single-element choice
|
|
302
1026
|
else
|
|
303
|
-
|
|
304
|
-
t2[1..-1]
|
|
1027
|
+
type(nt, true) # type can actually be a group here!
|
|
305
1028
|
end
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
1029
|
+
pp ["t is", t] if ENV["CDDL_TRACE"]
|
|
1030
|
+
if t[0] == :grpent && (occ == [1, 1])
|
|
1031
|
+
# go through the members here and multiply the occs
|
|
1032
|
+
t1 = t[1..-1].flatten(1)
|
|
1033
|
+
if t1[0] == :recurse_grpent
|
|
1034
|
+
[t1]
|
|
1035
|
+
elsif Array === t1[0] && t1[0][0] == :grpchoice
|
|
1036
|
+
fail t1.inspect unless t1.size == 1
|
|
1037
|
+
t1
|
|
1038
|
+
else
|
|
1039
|
+
t1.flat_map {|t2|
|
|
1040
|
+
if t2[0] == :member
|
|
1041
|
+
[t2]
|
|
1042
|
+
else
|
|
1043
|
+
fail t1.inspect unless t2[0] == :grpent # XXX Where is the nested multiplication here?
|
|
1044
|
+
t2[1..-1]
|
|
1045
|
+
end
|
|
1046
|
+
}.map {|mem|
|
|
1047
|
+
mem[1] *= occ[0]
|
|
1048
|
+
mem[2] *= occ[1] # Does all the infinity magic
|
|
1049
|
+
# p mem
|
|
1050
|
+
mem
|
|
1051
|
+
}
|
|
1052
|
+
end
|
|
313
1053
|
else
|
|
314
|
-
[
|
|
1054
|
+
if t[0] == :grpchoice && occ == [1, 1]
|
|
1055
|
+
[t]
|
|
1056
|
+
else
|
|
1057
|
+
if t[0] == :grpent
|
|
1058
|
+
t = [t[0], *t[1..-1].flatten(1)]
|
|
1059
|
+
end
|
|
1060
|
+
if occ[0] == 0 && t == [:grpchoice]
|
|
1061
|
+
[] # we won't be able to generate any of those
|
|
1062
|
+
else
|
|
1063
|
+
if t[0] == :grpchoice # FIXME: need to package grpchoice into grpent in a member
|
|
1064
|
+
t = [:grpent, t]
|
|
1065
|
+
end
|
|
1066
|
+
[[:member, *occ, nil, t]]
|
|
1067
|
+
end
|
|
1068
|
+
end
|
|
315
1069
|
end
|
|
316
1070
|
end
|
|
317
1071
|
end
|
|
318
1072
|
|
|
319
|
-
def
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
if
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
[:string, name]
|
|
326
|
-
end
|
|
1073
|
+
def g_process(name, g, genericargs)
|
|
1074
|
+
r, ids = g
|
|
1075
|
+
args = genericargs.children(:type1).map {|x| type1(x, true)}
|
|
1076
|
+
fail "number of args #{name} #{ids.size} #{args.size}" if ids.size != args.size
|
|
1077
|
+
bindings = Hash[ids.zip(args)]
|
|
1078
|
+
r_process("#{name}<#{genericargs._strip}>", r, bindings)
|
|
327
1079
|
end
|
|
328
1080
|
|
|
329
|
-
def type_recall(name, canbegroup)
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
1081
|
+
def type_recall(name, canbegroup, genericargs)
|
|
1082
|
+
# p genericargs, "GENERIC"
|
|
1083
|
+
if genericargs && (generic = @generics[name])
|
|
1084
|
+
t = g_process(name, generic, genericargs)
|
|
1085
|
+
t
|
|
1086
|
+
elsif !genericargs && (t = rule_lookup(name, canbegroup))
|
|
335
1087
|
t
|
|
336
1088
|
else
|
|
337
|
-
fail "Unknown type #{name}"
|
|
1089
|
+
fail "Unknown type #{name} #{genericargs.inspect} #{@bindings}" #{@abnf.ast?}"
|
|
338
1090
|
end
|
|
339
1091
|
end
|
|
340
1092
|
|
|
341
|
-
def group_recall(name)
|
|
342
|
-
if
|
|
343
|
-
g =
|
|
1093
|
+
def group_recall(name, genericargs)
|
|
1094
|
+
if genericargs && (generic = @generics[name])
|
|
1095
|
+
g = g_process(name, generic, genericargs)
|
|
344
1096
|
fail "#{name} not a group" unless g[0] == :grpent
|
|
345
1097
|
g[1..-1]
|
|
1098
|
+
elsif !genericargs && (g = rule_lookup(name, true))
|
|
1099
|
+
fail "#{name} not a group" unless g[0] == :grpent
|
|
1100
|
+
g[1..-1] # AAA
|
|
346
1101
|
else
|
|
347
1102
|
fail "Unknown group #{name}"
|
|
348
1103
|
end
|
|
349
1104
|
end
|
|
350
1105
|
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
if v = n.value
|
|
1106
|
+
def type2(n, canbegroup = false)
|
|
1107
|
+
if v = n.type
|
|
1108
|
+
type(n.type)
|
|
1109
|
+
elsif v = n.value
|
|
356
1110
|
value(n)
|
|
357
1111
|
elsif v = n.typename
|
|
358
|
-
|
|
1112
|
+
ga = n.genericarg || (n.s && n.s.genericarg) # workaround
|
|
1113
|
+
t = type_recall(v.to_s, canbegroup, ga)
|
|
359
1114
|
if t[0] == :type1
|
|
360
1115
|
if t.size == 2
|
|
361
1116
|
t = t[1]
|
|
@@ -370,7 +1125,58 @@ module CDDL
|
|
|
370
1125
|
|
|
371
1126
|
end
|
|
372
1127
|
end # XXX should flatten the thing, too
|
|
1128
|
+
t = t.dup
|
|
1129
|
+
t.cbor_annotation_replace(v.to_s) rescue nil # AAA
|
|
373
1130
|
t
|
|
1131
|
+
else
|
|
1132
|
+
fail [n, n.children].inspect
|
|
1133
|
+
end
|
|
1134
|
+
end
|
|
1135
|
+
|
|
1136
|
+
def memberkey_check(s)
|
|
1137
|
+
s.each do |member|
|
|
1138
|
+
case member[0]
|
|
1139
|
+
when :grpchoice
|
|
1140
|
+
member[1..-1].each do |m|
|
|
1141
|
+
memberkey_check(m)
|
|
1142
|
+
end
|
|
1143
|
+
when :recurse_grpent # XXX we don't have the entry yet
|
|
1144
|
+
when :member
|
|
1145
|
+
fail "map entry without member key given: #{member}" unless member[3] || member[4][0] == :grpent || member[4][0] == :grpchoice
|
|
1146
|
+
else
|
|
1147
|
+
fail ["type1 member", member].inspect unless member[0] == :member
|
|
1148
|
+
end
|
|
1149
|
+
end
|
|
1150
|
+
end
|
|
1151
|
+
|
|
1152
|
+
BRACE = {"{" => :map, "[" => :array}
|
|
1153
|
+
RANGE_EXCLUDE_END = {".." => false, "..." => true}
|
|
1154
|
+
SUPPORTED_ANNOTATIONS = [:bits, :size, :regexp, :cbor, :cborseq, :within, :and,
|
|
1155
|
+
:default, :lt, :le, :gt, :ge, :eq, :ne]
|
|
1156
|
+
|
|
1157
|
+
def type1(n, canbegroup = false)
|
|
1158
|
+
# puts "NVALUE #{n.value.inspect}"
|
|
1159
|
+
if v = n.type2
|
|
1160
|
+
ch = n.children(:type2)
|
|
1161
|
+
if ro = n.rangeop
|
|
1162
|
+
cats = []
|
|
1163
|
+
[:range, Range.new(*ch.map {|l|
|
|
1164
|
+
ok, val, cat = extract_value(type2(l))
|
|
1165
|
+
fail "Can't have range with #{l}" unless ok
|
|
1166
|
+
# XXX really should be checking type coherence of range
|
|
1167
|
+
cats << cat
|
|
1168
|
+
val
|
|
1169
|
+
}, RANGE_EXCLUDE_END[ro.to_s]),
|
|
1170
|
+
cats[0] == cats[1] ? cats[0] : fail("Incompatible range #{cats}")]
|
|
1171
|
+
elsif anno = n.annotator
|
|
1172
|
+
annotyp = anno.id.to_s.intern
|
|
1173
|
+
unless SUPPORTED_ANNOTATIONS.include?(annotyp)
|
|
1174
|
+
fail "Unsupported annotation .#{annotyp}"
|
|
1175
|
+
end
|
|
1176
|
+
[:anno, annotyp, *ch.map {|t| type2(t)}]
|
|
1177
|
+
else
|
|
1178
|
+
type2(v, canbegroup)
|
|
1179
|
+
end
|
|
374
1180
|
else
|
|
375
1181
|
case str = n.to_s
|
|
376
1182
|
when "#"
|
|
@@ -382,26 +1188,45 @@ module CDDL
|
|
|
382
1188
|
s << type(tagged_type)
|
|
383
1189
|
end
|
|
384
1190
|
s
|
|
385
|
-
when /\A\(/
|
|
386
|
-
type(n.type)
|
|
387
1191
|
when /\A[\[{]/
|
|
388
|
-
s = n.children(:group).flat_map {|ch| group(ch)}
|
|
389
1192
|
type = BRACE[str[0]]
|
|
1193
|
+
@insides << type
|
|
1194
|
+
s = n.children(:group).flat_map {|g| group(g)}
|
|
1195
|
+
@insides.pop
|
|
390
1196
|
if type == :map
|
|
391
|
-
s
|
|
392
|
-
|
|
393
|
-
fail "map entry without member key given: #{member}" unless member[3]
|
|
394
|
-
end
|
|
1197
|
+
memberkey_check(s)
|
|
1198
|
+
# XXX could do the occurrence multiplier here
|
|
395
1199
|
end
|
|
1200
|
+
# warn s.inspect
|
|
396
1201
|
[type, *s]
|
|
1202
|
+
when /\A&/
|
|
1203
|
+
if gn = n.groupname
|
|
1204
|
+
s = group_recall(gn.to_s, n.genericarg).flatten(1)
|
|
1205
|
+
else
|
|
1206
|
+
@insides << :enum
|
|
1207
|
+
s = n.children(:group).flat_map {|g| group(g)}
|
|
1208
|
+
@insides.pop
|
|
1209
|
+
end
|
|
1210
|
+
[:type1, *s.map {|mem|
|
|
1211
|
+
t, _s, _e, _k, v = mem
|
|
1212
|
+
fail "enum #{t.inspect}" unless t == :member
|
|
1213
|
+
v.cbor_annotation_add(generate1(_k)) rescue nil # AAA (XXX: what if more than one?)
|
|
1214
|
+
v
|
|
1215
|
+
}
|
|
1216
|
+
]
|
|
397
1217
|
else
|
|
398
1218
|
"unimplemented #{n}"
|
|
399
1219
|
end
|
|
400
1220
|
end
|
|
401
1221
|
end
|
|
402
1222
|
|
|
403
|
-
def
|
|
404
|
-
|
|
1223
|
+
def type_collect(n, canbegroup)
|
|
1224
|
+
n.children(:type1).map {|ch| type1(ch, canbegroup)}
|
|
1225
|
+
end
|
|
1226
|
+
|
|
1227
|
+
def type(n, canbegroup = false)
|
|
1228
|
+
# pp ["nch", n.children]
|
|
1229
|
+
s = type_collect(n, canbegroup)
|
|
405
1230
|
if s.size == 1
|
|
406
1231
|
s.first
|
|
407
1232
|
else
|
|
@@ -410,7 +1235,20 @@ module CDDL
|
|
|
410
1235
|
end
|
|
411
1236
|
|
|
412
1237
|
def group(n) # returns array
|
|
413
|
-
n.children(:
|
|
1238
|
+
choices = n.children(:grpchoice)
|
|
1239
|
+
if choices == [] # "cannot happen", workaround
|
|
1240
|
+
choices = n.s.children(:grpchoice)
|
|
1241
|
+
# warn "W2: #{choices.inspect}"
|
|
1242
|
+
end
|
|
1243
|
+
choices = choices.map {|choice|
|
|
1244
|
+
choice.children(:grpent).flat_map {|ch| grpent(ch)}
|
|
1245
|
+
}
|
|
1246
|
+
case choices.size
|
|
1247
|
+
when 1
|
|
1248
|
+
choices.first
|
|
1249
|
+
else
|
|
1250
|
+
[[:grpchoice, *choices]]
|
|
1251
|
+
end
|
|
414
1252
|
end
|
|
415
1253
|
|
|
416
1254
|
def occur(n)
|
|
@@ -418,7 +1256,7 @@ module CDDL
|
|
|
418
1256
|
when ""
|
|
419
1257
|
[1, 1]
|
|
420
1258
|
when "+"
|
|
421
|
-
[1,
|
|
1259
|
+
[1, MANY]
|
|
422
1260
|
when "?"
|
|
423
1261
|
[0, 1]
|
|
424
1262
|
when /\A(\d*)\*(\d*)\z/
|
|
@@ -428,7 +1266,7 @@ module CDDL
|
|
|
428
1266
|
s = s.to_i
|
|
429
1267
|
end
|
|
430
1268
|
if (e = $2) == ""
|
|
431
|
-
e =
|
|
1269
|
+
e = MANY
|
|
432
1270
|
else
|
|
433
1271
|
e = e.to_i
|
|
434
1272
|
end
|