class CDDL::Parser
Constants
- ABNF_ENCODING_FOR_CONOP
- ABNF_PARSER_FOR_STRING
- BRACE
- CDDL_FEATURE_REJECT
- FEATURE_REJECT_RE
- FLOAT_AI_FROM_SIZE
- OPERATORS
- OPERATORS_MATCH
- RANGE_EXCLUDE_END
- RECURSE_TYPE
- REGEXP_FOR_STRING
Memoize a bit here
- SIMPLE_VALUE
- SIMPLE_VALUE_SIMPLE
- STRING_ESCAPES
- SUPPORTED_ANNOTATIONS
- VALUE_TYPE
Attributes
ast[R]
Public Class Methods
new(source_text)
click to toggle source
# File lib/cddl.rb, line 46 def initialize(source_text) @abnf = Peggy::ABNF.new _cresult = @abnf.compile! ABNF_SPEC, ignore: :s presult = @abnf.parse? :cddl, (source_text + PRELUDE) expected_length = source_text.length + PRELUDE.length if expected_length != presult upto = @abnf.parse_results.keys.max puts "UPTO: #{upto}" if $advanced pp @abnf.parse_results[upto] if $advanced pp @abnf.parse_results[presult] if $advanced puts "SO FAR: #{presult}" if $advanced puts @abnf.ast? if $advanced presult ||= 0 part1 = source_text[[presult - 100, 0].max...presult] part3 = source_text[upto...[upto + 100, source_text.length].min] if upto - presult < 100 part2 = source_text[presult...upto] else part2 = source_text[presult, 50] + "......." + (source_text[upto-50, 50] || "") end warn "*** Look for syntax problems around the #{ "%%%".colorize(background: :light_yellow)} markers:\n#{ part1}#{"%%%".colorize(color: :green, background: :light_yellow)}#{ part2}#{"%%%".colorize(color: :red, background: :light_yellow)}#{ part3}" raise ParseError, "*** Parse error at #{presult} upto #{upto} of #{ source_text.length} (#{expected_length})." end puts @abnf.ast? if $debug_ast @ast = @abnf.ast? # our little argument stack for rule processing @insides = [] # collect error information @last_message = "" end
Public Instance Methods
apr()
click to toggle source
# File lib/cddl.rb, line 82 def apr # for debugging @abnf.parse_results end
ast_debug()
click to toggle source
# File lib/cddl.rb, line 86 def ast_debug ast.to_s[/.*comment/m] # stop at first comment -- prelude end
cname(s)
click to toggle source
# File lib/cddl.rb, line 121 def cname(s) s.to_s.gsub(/-/, "_") end
defines(prefix)
click to toggle source
Generate some simple define lines from the value-only rules
# File lib/cddl.rb, line 126 def defines(prefix) prefix ||= "CDDL" if prefix =~ /%\d*\$?s/ format = prefix format += "\n" unless format[-1] == "\n" else format = "#define #{prefix}_%s %s\n" end s = {} # keys form crude set of defines add = proc { |*a| s[format % a] = true } r = rules ast.each :rule do |rule| if rulename = rule.typename t = rule.type.children(:type1) if t.size == 1 if (t2 = t.first.children(:type2)) && t2.size == 1 && (v = t2.first.value) add.(cname(rulename), v.to_s) end end end end # CBOR::PP.pp r walk(r) do |subtree, anno| if subtree[0] == :type1 && subtree[1..-1].all? {|x| x[0] == :int} if enumname = subtree.cbor_annotations rescue nil enumname = cname(enumname.first) subtree[1..-1].each do |x| if memname = x.cbor_annotations memname = "#{enumname}_#{cname(memname.first)}" add.(memname, x[1].to_s) end end end end if subtree[0] == :array if (arrayname = subtree.cbor_annotations rescue nil) || anno arrayname = cname(arrayname ? arrayname.first : anno) subtree[1..-1].each_with_index do |x, i| if x[0] == :member if Array === x[3] && x[3][0] == :text memname = x[3][1] # preferably use key string elsif memname = x[4].cbor_annotations memname = memname.first # use value annotation otherwise end if memname memname = "#{arrayname}_#{cname(memname)}_index" add.(memname, i.to_s) end end if x[0] == :member && (x[1] != 1 || x[2] != 1) break # can't give numbers if we have optionals etc. end end end end end s.keys.join end
extract_arg0(t)
click to toggle source
# File lib/cddl.rb, line 789 def extract_arg0(t) return [false] unless t[0] == :array [true, (el = t[1] return [false] unless el[0..3] == [:member, 1, 1, nil] ok, v, vt = extract_value(el[4]) return [false] unless ok [v, vt] ), *t[2..-1]] end
extract_array(t)
click to toggle source
# File lib/cddl.rb, line 778 def extract_array(t) return [false] unless t[0] == :array [true, *t[1..-1].map { |el| return [false] unless el[0..3] == [:member, 1, 1, nil] ok, v, vt = extract_value(el[4]) return [false] unless ok [v, vt] }] end
extract_feature(control, d)
click to toggle source
# File lib/cddl.rb, line 801 def extract_feature(control, d) ok, v, vt = extract_value(control) if ok nm = v det = d warn "*** feature controller should be a string: #{control.inspect}" unless String == vt else ok, *v = extract_array(control) if ok && v.size == 2 nm = v[0][0] det = v[1][0] warn "*** first element of feature controller should be a string: #{control.inspect}" unless String === nm else warn "*** feature controller not implemented: #{control.inspect}" end end [nm, det] end
extract_value(t)
click to toggle source
# File lib/cddl.rb, line 737 def extract_value(t) # [] if vt = VALUE_TYPE[t[0]] [true, t[1], vt] elsif t[0] == :prim && t[1] == 7 case t2 = t[2] when Integer when Array a, b, c = extract_value(t2) if a && c == Integer t2 = b end end if v = SIMPLE_VALUE[[:prim, 7, t2]] v elsif SIMPLE_VALUE_SIMPLE === t2 [true, CBOR::Simple.new(t2), :simple] end elsif t[0] == :anno _, conop, target, control = t warn ["EXV0", conop, target, control].inspect if ENV["CDDL_TRACE"] if conop == :cat || conop == :plus || conop == :det ok1, v1, vt1 = extract_value(target) ok2, v2, vt2 = extract_value(control) # warn ["EXV", ok1, v1, vt1, ok2, v2, vt2].inspect if ok1 && ok2 if vt1 == Integer [true, v1 + Integer(v2), Integer] if vt2 == Integer || vt2 == Float elsif vt1 == Float [true, v1 + v2, vt1] if vt2 == Integer || vt2 == Float else if conop == :det v1 = remove_indentation(v1) v2 = remove_indentation(v2) end [true, v1 + v2, vt1] if vt1 == vt2 end end rescue nil end end || [false] end
generate()
click to toggle source
# File lib/cddl.rb, line 311 def generate @recursion = 0 generate1(rules) end
generate1(where, inmap = false)
click to toggle source
# File lib/cddl.rb, line 316 def generate1(where, inmap = false) case where[0] when :type1 fail BackTrack.new("Can't generate from empty type choice socket yet") unless where.size > 1 begin chosen = where[rand(where.size-1)+1] generate1(chosen) rescue BackTrack tries = where[1..-1].sample(where.size) - [chosen] r = begin if tries.empty? BackTrack.new("No suitable alternative in type choice") else generate1(tries.pop) end rescue BackTrack retry end fail r if BackTrack === r r end when :grpchoice fail BackTrack.new("Can't generate from empty group choice socket yet") unless where.size > 1 begin chosen = where[rand(where.size-1)+1] chosen.flat_map {|m| generate1(m, inmap)} rescue BackTrack tries = where[1..-1].sample(where.size) - [chosen] r = begin if tries.empty? BackTrack.new("No suitable alternative in group choice") else tries.pop.flat_map {|m| generate1(m, inmap)} end rescue BackTrack retry end fail r if BackTrack === r r end when :map Hash[where[1..-1].flat_map {|m| generate1(m, true)}] when :recurse_grpent name = where[1] rule = lookup_recurse_grpent(name) if @recursion < MAX_RECURSE @recursion += 1 #p ["recurse_grpent", *rule] #r = generate1(rule) r = rule[1..-1].flat_map {|m| generate1(m, inmap)} @recursion -= 1 r else fail "Deep recursion into #{name}: #{@stage1[name]}, not yet implemented" end when :array, :grpent r = where[1..-1].flat_map {|m| generate1(m).map{|e| e[1]}} .flat_map {|e| Array === e && e[0] == :grpent ? e[1..-1] : [e]} # nested grpents need to be "unpacked" if where[0] == :grpent [:grpent, *r] else r end when :member st = where[1] fudge = 4 * (1 - (@recursion / MAX_RECURSE.to_f))**3 # get less generate-happy with more recursion en = [where[2], [st, fudge].max].min # truncate to fudge unless must be more st += rand(en + 1 - st) if en != st kr = where[3] vr = where[4] if inmap unless kr case vr[0] when :grpent # fail "grpent in map #{vr.inspect}" unless vr.size == 2 g = Array.new(st) { vr[1..-1].map{ |vrx| generate1(vrx, true) }.flatten(1) }.flatten(1) # warn "GGG #{g.inspect}" return g when :grpchoice g = Array.new(st) { generate1(vr, true) }.flatten(1) # warn "GGG #{g.inspect}" return g else fail "member key not given in map for #{where}" # || vr == [:grpchoice] end end end begin Array.new(st) { [ (generate1(kr) if kr), # XXX: need error in map context generate1(vr) ]} rescue BackTrack fail BackTrack.new("Need #{where[1]}..#{where[2]} of these: #{[kr, vr].inspect}") unless where[1] == 0 [] end when :text, :int, :float, :bytes where[1] when :range rand(where[1]) when :prim case where[1] when nil gen_word # XXX: maybe always returning a string is confusing when 0 rand(4711) when 1 ~rand(815) when 2 gen_word.force_encoding(Encoding::BINARY) when 3 gen_word when 6 tn = Integer === where[2] ? where[2] : generate1(where[2]) unless Integer === tn && tn >= 0 fail "Can't generate a tag number out of #{where[2]}" end CBOR::Tagged.new(tn, generate1(where[3])) when 7 w2 = where[2] if Array === w2 w2 = generate1(w2) end case w2 when nil Math::PI when 20 false when 21 true when 22 nil when 0..19, 23, 32..255 CBOR::Simple.new(w2) when 25 while !(hs = Half.encode_from_single_bytes([myfrand(5)].pack("g"))) end Half.decode(hs) when 26 while (a = [myfrand(8)].pack("g").unpack("g").first).to_cbor.size != 5 end a when 27 while (a = myfrand(11)).to_cbor.size != 9 end a else fail "Can't generate prim #7.#{where[2].inspect}" # XXX retry array generate end else fail "Can't generate prim #{where[1]}" end when :anno target = where[2] control = where[3] case conop = where[1] when :size should_be_int = generate1(control) unless (Array === target && target[0] == :prim && [0, 2, 3].include?(target[1])) && Integer === should_be_int && should_be_int >= 0 fail "Don't know yet how to generate #{where}" end s = Random.new.bytes(should_be_int) case target[1] when 0 # silently restrict to what we can put into a uint s[0...8].bytes.inject(0) {|a, b| a << 8 | b } when 2 s when 3 Base64.urlsafe_encode64(s)[0...should_be_int] # XXX generate word a la w = gen_word end when :bits set_of_bits = Array.new(10) { generate1(control) } # XXX: ten? # p set_of_bits unless (target == [:prim, 0] || target == [:prim, 2]) && set_of_bits.all? {|x| Integer === x && x >= 0 } fail "Don't know yet how to generate #{where}" end if target == [:prim, 2] set_of_bits.inject(String.new) do |s, i| n = i >> 3 bit = 1 << (i & 7) if v = s.getbyte(n) s.setbyte(n, v | bit); s else s << "\0" * (n - s.size) << bit.chr(Encoding::BINARY) end end else # target == [:prim, 0] set_of_bits.inject(0) do |a, v| a |= (1 << v) end end when :default # Hmm. unless $default_warned warn "*** Ignoring .default for now." $default_warned = true end generate1(target, inmap) when :feature feature, = extract_feature(control, nil) # warn "@@@ GENFEAT #{feature.inspect} #{CDDL_FEATURE_REJECT[feature].inspect}" if CDDL_FEATURE_REJECT[feature] fail BackTrack.new("Feature '#{feature}' rejected") end generate1(target, inmap) when :cat, :det lhs = generate1(target, inmap) rhs = generate1(control) if conop == :det lhs = remove_indentation(lhs) rhs = remove_indentation(rhs) end begin lhs + rhs rescue Exception => e fail "Can't #{lhs.inspect} .cat #{rhs.inspect}: #{e.message}" end when :plus lhs = generate1(target, inmap) rhs = generate1(control) begin case lhs when Integer lhs + Integer(rhs) when Numeric lhs + rhs else fail "#{lhs.inspect}: Not a number" end rescue Exception => e fail "Can't #{lhs.inspect} .plus #{rhs.inspect}: #{e.message}" end when :eq content = generate1(control) if validate1(content, where) return content end fail "Not smart enough to generate #{where}" when :lt, :le, :gt, :ge, :ne if Array === target && target[0] == :prim content = generate1(control) try = if Numeric === content content = Integer(content) case target[1] when 0 case conop when :lt rand(0...content) when :le rand(0..content) end end end if validate1(try, where) return try else warn "HUH gen #{where.inspect} #{try.inspect}" unless try.nil? end end try_generate(target) do |content| if validate1(content, where) return content end end fail "Not smart enough to generate #{where}" when :regexp regexp = generate1(control) unless target == [:prim, 3] && String === regexp fail "Don't know yet how to generate #{where}" end REGEXP_FOR_STRING[regexp].random_example(max_repeater_variance: 5) when :abnf, :abnfb grammar = generate1(control) bytes = true if target == [:prim, 2] bytes = false if target == [:prim, 3] unless !bytes.nil? && String === grammar fail "Don't know yet how to generate #{where}" end grammar << "\n" unless grammar =~ /\n\z/ out = ABNF_PARSER_FOR_STRING[grammar].generate if conop == :abnfb out = out.codepoints.pack("C*") end enc = bytes ? Encoding::BINARY : Encoding::UTF_8 out.force_encoding(enc) when :cbor, :cborseq, :cbordet, :cborseqdet unless target == [:prim, 2] fail "Don't know yet how to generate #{where}" end input = generate1(control) if conop == :cbordet || conop == :cborseqdet input = input.cbor_prepare_deterministic end content = CBOR::encode(input) if conop == :cborseq || conop == :cborseqdet # remove the first head content = unpack_array_to_sequence(content, where) end content when :json unless target == [:prim, 3] fail "Don't know yet how to generate #{where}" end content = JSON::dump(generate1(control)) content when :decimal unless target == [:prim, 3] fail "Don't know yet how to generate #{where}" end content = Integer(generate1(control)).to_s content when :join try_generate(control) do |content| if Array === content && content.all? {|x| String === x} && Set[content.map {|x| x.encoding}].size == 1 content = content.join if validate1(content, target) return content end end end fail "Don't know yet how to generate #{where}" when :b64u, :"b64u-sloppy", :b64c, :"b64c-sloppy", :b45, :b32, :h32, :hex, :hexlc, :hexuc try_generate(control) do |content| if String === content content = case conop when :b64u, :"b64u-sloppy" Base64.urlsafe_encode64(content, padding: false) when :b64c, :"b64c-sloppy" Base64.strict_encode64(content) when :b45 Base45Lite.encode(content) when :b32 Base32.table = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567" Base32.encode(content).gsub("=", "") when :h32 Base32.table = "0123456789ABCDEFGHIJKLMNOPQRSTUV" Base32.encode(content).gsub("=", "") when :hex, :hexuc content.unpack("H*")[0].upcase when :hexlc content.unpack("H*")[0] else fail "Cannot happen" end if validate1(content, target) return content end end end fail "Not smart enough to generate #{where}" when :printf try_generate(control) do |content| if Array === content && content.size >= 1 fmt, *data = content if String === fmt begin content = fmt % data if validate1(content, target) return content end rescue ArgumentError => e # be verbose about mismatches here @last_message << "\n** #{fmt.inspect} ArgumentError #{e}" end end end end fail "Not smart enough to generate #{where}#{@last_message}" when :within, :and try_generate(target) do |content| if validate1(content, control) return content elsif conop == :within warn "*** #{content.inspect} meets #{target.inspect} but not #{control.inspect}" end end fail "Not smart enough to generate #{where}" else fail "Don't know yet how to generate from #{where}" end when :recurse name = where[1] rule = @stage1[name] if @recursion < MAX_RECURSE @recursion += 1 #p ["recurse", *rule] r = generate1(rule) @recursion -= 1 r else fail "Deep recursion into #{name}: #{@stage1[name]}, not yet implemented" end else fail "Don't know how to generate from #{where[0]} in #{where.inspect}" end end
lookup_recurse_grpent(name)
click to toggle source
# File lib/cddl.rb, line 261 def lookup_recurse_grpent(name) rule = @stage1[name] # pp rule fail unless rule.size == 2 [rule[0], *rule[1]] end
map_check(d, d_check, members)
click to toggle source
# File lib/cddl.rb, line 903 def map_check(d, d_check, members) anno = [] anno if members.all? { |r| puts "ALL SUBRULE: #{r.inspect}" if ENV["CDDL_TRACE"] t, s, e, k, v = r case t when :recurse_grpent rule = lookup_recurse_grpent(s) if ann2 = map_check(d, d_check, rule[1..-1]) anno.concat(ann2) end when :grpchoice r[1..-1].any? {|cand| puts "CHOICE SUBRULE: #{cand.inspect}" if ENV["CDDL_TRACE"] cand_d_check = d_check.dup if ann2 = map_check(d, cand_d_check, cand) puts "CHOICE SUBRULE SUCCESS: #{cand.inspect}" if ENV["CDDL_TRACE"] d_check.replace(cand_d_check) anno.concat(ann2) end } when :member unless k case v[0] when :grpent entries = v[1..-1] when :grpchoice entries = [v] else fail "member name not known for group entry #{r} in map" end d_check1 = d_check.dup occ = 0 ann2 = [] while occ < e && (ann3 = map_check(d, d_check1, entries)) && ann3 != [] occ += 1 ann2.concat(ann3) end if occ >= s d_check.replace(d_check1) anno.concat(ann2) puts "OCC SATISFIED: #{occ.inspect} >= #{s.inspect}" if ENV["CDDL_TRACE"] anno else # leave some diagnostic breadcrumbs? puts "OCC UNSATISFIED: #{occ.inspect} < #{s.inspect}" if ENV["CDDL_TRACE"] false end else # this is mostly quadratic; let's do the linear thing if possible simple, simpleval = extract_value(k) if simple puts "SIMPLE: #{d_check.inspect} #{simpleval}" if ENV["CDDL_TRACE"] # add occurrence check; check that val is present in the first place actual = d.fetch(simpleval, :not_found) if actual == :not_found s == 0 # minimum occurrence must be 0 then else if (ann2 = validate1a(actual, v)) && d_check.delete(simpleval) {:not_found} != :not_found anno.concat(ann2) end end else puts "COMPLEX: #{k.inspect} #{simple.inspect} #{simpleval.inspect}" if ENV["CDDL_TRACE"] keys = d_check.keys ta, keys = keys.partition{ |key| validate1(key, k)} count = 0 catch :enough do ta.all? { |val| if (ann2 = validate1a(d[val], v)) && # XXX check cut or not! d_check.delete(val) {:not_found} != :not_found anno.concat(ann2) throw :enough, true if (count += 1) == e true end } end and validate_result(count >= s) { "not enough #{ta.inspect} for #{r.inspect}" } end end else fail "Cannot validate #{t} in maps yet #{r}" # MMM end } end
myfrand(exp)
click to toggle source
# File lib/cddl.rb, line 305 def myfrand(exp) exprange = 1 << exp expoffset= exprange >> 1 (rand(2)*2-1) * rand() * 2.0**(rand(exprange)-expoffset) end
remove_indentation(s)
click to toggle source
# File lib/cddl.rb, line 268 def remove_indentation(s) l = s.lines indent = l.grep(/\S/).map {|l| l[/^\s*/].size}.min l.map {|l| l.sub(/^ {0,#{indent}}/, "")}.join end
rules()
click to toggle source
# File lib/cddl.rb, line 185 def rules @rules = {} @generics = {} @bindings = [{}] ast.each :rule do |rule| rule_ast = if rulename = rule.groupname [:grpent, rule.grpent] elsif rulename = rule.typename [:type1, *rule.type.children(:type1)] else fail "Huh?" end n = rulename.to_s asg = rule.assign.to_s if g = rule.genericparm if asg != "=" fail "Augment #{asg.inspect} not implemented for generics" end ids = g.children(:id).map(&:to_s) # puts ["ids", ids].inspect if b = @generics[n] fail "Duplicate generics definition #{n} as #{rule_ast} (was #{b})" end @generics[n] = [rule_ast, ids] else case asg when "=" if @rules[n] a = strip_nodes(rule_ast).inspect b = strip_nodes(@rules[n]).inspect if a == b warn "*** Warning: Identical redefinition of #{n} as #{a}" else fail "Duplicate rule definition #{n} as #{b} (was #{a})" end end @rules[n] = rule_ast when "/=" @rules[n] ||= [:type1] fail "Can't add #{rule_ast} to #{n}" unless rule_ast[0] == :type1 # XXX need to check existing rule as well @rules[n].concat rule_ast[1..-1] # puts "#{@rules[n].inspect} /=" when "//=" @rules[n] ||= [:grpchoice] fail "Can't add #{rule_ast} to #{n}" unless rule_ast[0] == :grpent # XXX need to check existing rule as well if @rules[n][0] == :grpent # widen @rules[n] = [:grpchoice, @rules[n]] end @rules[n] << rule_ast # puts "#{@rules[n].inspect} //=" else fail "Unknown assign #{asg.inspect}" end end end # pp @generics @rootrule = @rules.keys.first # DRAFT: generics are ignored here. # now process the rules... @stage1 = {} result = r_process(@rootrule, @rules[@rootrule]) r_process("used_in_cddl_prelude", @rules["used_in_cddl_prelude"]) @rules.each do |n, r| # r_process(n, r) # debug only loop warn "*** Unused rule #{n}" unless @stage1[n] end if result[0] == :grpent warn "Group at top -- first rule must be a type!" end # end # @stage1 result end
strip_nodes(n)
click to toggle source
# File lib/cddl.rb, line 90 def strip_nodes(n) [n[0], *n[1..-1].map {|e| e._strip }] end
try_generate(target) { |content| ... }
click to toggle source
# File lib/cddl.rb, line 722 def try_generate(target) 32.times do content = generate1(target) yield content # should return if success end end
unpack_array_to_sequence(content, where)
click to toggle source
# File lib/cddl.rb, line 274 def unpack_array_to_sequence(content, where) # remove the first head n = case content.getbyte(0) - (4 << 5) when 0..23; 1 when 24; 2 when 25; 3 when 26; 5 when 27; 9 # unlikely :-) else fail ".cborseq sequence for #{where} not an array" end content[0...n] = '' content end
validate(d, warn=true)
click to toggle source
# File lib/cddl.rb, line 824 def validate(d, warn=true) @recursion = 0 $features = Hash.new {|h, k| h[k] = {}} result = validate1a(d, rules) if warn if result if $features != {} features = $features.reject {|k, v| CDDL_FEATURE_OK[k.to_s] } # warn "@@@ FEAT #{CDDL_FEATURE_OK.inspect} #{CDDL_FEATURE_REJECT.inspect}" warn "** Features potentially used (#$fn): #{features.map {|k, v| "#{k}: #{v.keys}"}.join(", ")}" if features != {} end else warn "CDDL validation failure (#{result.inspect} for #{d.inspect}):" PP::pp(validate_diag, STDERR) puts(validate_diag.cbor_diagnostic) end end result end
validate1(d, where)
click to toggle source
# File lib/cddl.rb, line 1017 def validate1(d, where) if ENV["CDDL_TRACE"] puts "DATA: #{d.inspect}" puts "RULE: #{where.inspect}" end # warn ["val1", d, where].inspect @last_data = d @last_rule = where ann = nil case where[0] when :type1 if where[1..-1].any? {|r| ann = validate1a(d, r)} ann end when :map if Hash === d d_check = d.dup if (ann = map_check(d, d_check, where[1..-1])) && d_check == {} ann else if ENV["CDDL_TRACE"] puts "MAP RESIDUAL: #{d_check.inspect} for #{where[1..-1]} and #{d.inspect}" end end end when :array # warn ["valarr", d, where].inspect if Array === d # validate1 against the record idx, ann = validate_forward(d, 0, where) ann if validate_result(idx == d.size) { "#{validate_diag.inspect} -- cannot complete (#{idx}, #{d.size}) array #{d} for #{where}" } end when :int, :float _, v = extract_value(where) [] if d == v when :text, :bytes _, v = extract_value(where) [] if d == v && ((d.encoding == Encoding::BINARY) == (v.encoding == Encoding::BINARY)) when :range [] if where[2] === d && where[1].include?(d) when :anno _, conop, target, control = where if conop == :cat || conop == :plus || conop == :det ok1, v1, vt1 = extract_value(target) ok2, v2, vt2 = extract_value(control) warn ["ANNO0", ok1, v1, vt1, ok2, v2, vt2, d].inspect if ENV["CDDL_TRACE"] if ok1 && ok2 v2 = Integer(v2) if vt1 == Integer if conop == :det v1 = remove_indentation(v1) v2 = remove_indentation(v2) end # warn ["ANNO", ok1, v1, vt1, ok2, v2, vt2, d].inspect [] if d == v1 + v2 # XXX Focus ArgumentError elsif conop == :cat && String === d warn ["CAT-L", ok1, v1, vt1, ok2, v2, vt2, d].inspect if ENV["CDDL_TRACE"] if ok1 && vt1 == String # d and lhs (v1) need to agree in encoding if d.encoding == v1.encoding if d[0...v1.length] == v1 d2 = d[v1.length..-1] warn ["CAT-L1", d2, d2.encoding, control].inspect if ENV["CDDL_TRACE"] validate1a_ignore_encoding(d2, control) end end elsif ok2 && vt2 == String warn ["CAT-R", ok1, v1, vt1, ok2, v2, vt2, d].inspect if ENV["CDDL_TRACE"] if d[-v2.length..-1] == v2 d1 = d[0...-v2.length] validate1a(d1, control) end end elsif conop == :plus && Integer === d if ok1 && vt1 == Integer validate1a(d - Integer(v1), control) elsif ok2 && vt2 == Integer validate1a(d - Integer(v2), target) end end elsif ann = validate1a(d, target) case conop when :size case d when Integer ok, v, vt = extract_value(control) if ok && vt == Integer ann if (d >> (8*v)) == 0 end when String ann if validate1(d.bytesize, control) end when :bits if String === d d.each_byte.with_index.all? { |b, i| bit = i << 3 ann if 8.times.all? { |nb| b[nb] == 0 || validate1(bit+nb, control) } } elsif Integer === d if d >= 0 ok = true i = 0 while ok && d > 0 if d.odd? ok &&= validate1(i, control) end d >>= 1; i += 1 end ann if ok end end when :default # Hmm. unless $default_warned warn "*** Ignoring .default for now." $default_warned = true end ann when :feature nm, det = extract_feature(control, d) $features[nm][det] = true ann if !CDDL_FEATURE_REJECT[nm] when :lt, :le, :gt, :ge, :eq, :ne op = OPERATORS[conop] ok, v, _vt = extract_value(control) if ok ann if d.send(op, v) rescue nil # XXX Focus ArgumentError else needs_to_match = OPERATORS_MATCH[conop] unless needs_to_match.nil? ann if !!validate1(d, control) == needs_to_match end end when :regexp ann if ( if String === d ok, v, vt = extract_value(control) if ok && vt == String re = REGEXP_FOR_STRING[v] # pp re d.match(re) end end ) when :abnf, :abnfb ann if ( if String === d ok, v, vt = extract_value(control) if ok && vt == String begin ABNF_PARSER_FOR_STRING[v].validate( d.dup.force_encoding(ABNF_ENCODING_FOR_CONOP[conop]).codepoints.pack("U*") ) true rescue => e # warn "*** #{e}" # XXX @last_message = e.to_s.force_encoding(Encoding::UTF_8) nil end end end ) when :cbor ann if validate1((CBOR::decode(d) rescue :BAD_CBOR), control) when :cborseq ann if validate1((CBOR::decode("\x9f".b << d << "\xff".b) rescue :BAD_CBOR), control) when :cbordet decoded = CBOR::decode(d) rescue :BAD_CBOR if d != decoded.to_deterministic_cbor @last_message = "CBOR #{d.inspect} not deterministically encoded" nil else ann if validate1(decoded, control) end when :cborseqdet decoded = CBOR::decode("\x9f".b << d << "\xff".b) rescue :BAD_CBOR if d != unpack_array_to_sequence(decoded.to_deterministic_cbor, d.inspect) @last_message = "CBOR Sequence #{d.inspect} not deterministically encoded" nil else ann if validate1(decoded, control) end when :json ann if validate1((JSON::load(d) rescue :BAD_JSON), control) when :decimal ann if ( if /\A0|-?[1-9][0-9]*\z/ === d validate1((d.to_i rescue :BAD_JSON), control) end ) when :join t = control v = if t[0] == :array t[1..-1].map { |el| if el[0..2] == [:member, 1, 1] ok, v, vt = extract_value(el[4]) if ok [true, v, vt] else [false, el[4]] end end } end warn "@@@JOIN@@@ #{v.inspect}" if ENV["CDDL_TRACE"] ok = true if v ix = 0 rest = d.dup while ix < v.length if left = v[ix] if left[0] # match constant value first fail "Not a string for #{left.inspect} in #{where}" unless String == left[2] want = left[1] have = rest[0...left[1].length] if want == have rest[0...left[1].length] = '' ix += 1 next else fail ".join: want #{want.inspect}, have #{have.inspect}" end else ix += 1 if ix == v.length # match remaining warn "@@@JOIN ok in #{ok.inspect} rest #{rest.inspect}" if ENV["CDDL_TRACE"] ok &&= validate1(rest, left[1]) warn "@@@JOIN ok out #{ok.inspect}" if ENV["CDDL_TRACE"] # more diag rest = '' next else if mid = v[ix] if mid[0] # have constant value to split over fail "Not a string for #{mid} in #{where}" unless String == mid[2] rest1, rest2 = rest.split(mid[1], 2) if rest2 warn "@@@JOIN ok in #{ok.inspect} rest1 #{rest1.inspect}" if ENV["CDDL_TRACE"] ok &&= validate1(rest1, left[1]) warn "@@@JOIN ok out #{ok.inspect}" if ENV["CDDL_TRACE"] rest = rest2 ix += 1 next else fail "Can't find #{mid[1].inspect} in #{rest.inspect}" end else fail "Cannot validate consecutive non-constant members of .join in #{where}" end else fail "Cannot handle .join over #{t[ix+1]} in #{where}" end end end else fail "Cannot handle .join over #{t[ix+1]} in #{where}" end end fail "Can't match #{rest.inspect} for .join in #{where}" if rest != '' ann if ok else fail "Don't know yet how to validate against #{where}" end when :b64u, :"b64u-sloppy", :b64c, :"b64c-sloppy", :b45, :b32, :h32, :hex, :hexlc, :hexuc ann if ( String === d && ( decoded = case conop when :b64u /[+\/=]/ !~ d && Base64.urlsafe_decode64(d) when :"b64u-sloppy" /[-_=]/ !~ d && Base64.decode64(d.tr("+/", "-_")) when :b64c Base64.strict_decode64(d) when :"b64c-sloppy" Base64.decode64(d) when :b32 /=/ !~ d && (Base32.table = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567") && Base32.decode(d) when :b45 Base45Lite.decode(d) when :h32 /=/ !~ d && (Base32.table = "0123456789ABCDEFGHIJKLMNOPQRSTUV") && Base32.decode(d) when :hex /\A[0-9a-fA-F]*\z/ =~ d && [d].pack("H*") when :hexuc /\A[0-9A-F]*\z/ =~ d && [d].pack("H*") when :hexlc /\A[0-9a-f]*\z/ =~ d && [d].pack("H*") else fail "Cannot happen" end ) && validate1(decoded.b, control) ) when :printf ann if String === d && ( ok, fmt, *v = extract_arg0(control) if ok && String == fmt[1] fmt = fmt[0] # warn "** ok #{ok.inspect} fmt #{fmt.inspect} v #{v.inspect}" decoded = d.scanf(fmt) # this is a bit too lenient, so let's do: encode_again = fmt % decoded if encode_again != d warn "** fmt #{fmt.inspect} d #{d.inspect} decoded #{decoded.inspect} encode_again #{encode_again.inspect}" else validate1(decoded, [:array, *v]) end end ) when :within if validate1(d, control) ann else warn "*** #{d.inspect} meets #{target} but not #{control}" nil end when :and ann if validate1(d, control) else fail "Don't know yet how to validate against #{where}" end end when :prim # warn "validate prim WI #{where.inspect} #{d.inspect}" case where[1] when nil true when 0 Integer === d && d >= 0 && d <= 0xffffffffffffffff when 1 Integer === d && d < 0 && d >= -0x10000000000000000 when 2 String === d && d.encoding == Encoding::BINARY when 3 String === d && d.encoding != Encoding::BINARY # cheat when 6 if Integer === d t = 2 # assuming only 2/3 match an Integer if d < 0 d = ~d t = 3 end d = CBOR::Tagged.new(t, d == 0 ? "".b : d.digits(256).reverse!.pack("C*")) end CBOR::Tagged === d && ( Integer === where[2] ? d.tag == where[2] : validate1a(d.tag, where[2]) ) && validate1a(d.data, where[3]) when 7 t, v = extract_value(where) if t v.eql? d else case w2 = where[2] when nil Float === d || CBOR::Simple === d || [false, true, nil].include?(d) when Array headnum = case d when Float FLOAT_AI_FROM_SIZE[d.to_cbor.size] when false 20 when true 21 when nil 22 when CBOR::Simple d.value end validate1a(headnum, w2) when 25, 26, 27 Float === d && FLOAT_AI_FROM_SIZE[d.to_cbor.size] == w2 else fail [:val7, d, where].inspect end end else fail "Can't validate prim #{where[1]} yet" end when :recurse name = where[1] rule = @stage1[name] if @recursion < MAX_RECURSE @recursion += 1 r = validate1a(d, rule) @recursion -= 1 r else fail "Deep recursion into #{name}: #{rule}, not yet implemented" end else @last_message = "Don't know how to validate #{where}" false # fail where end end
validate1a(d, where)
click to toggle source
# File lib/cddl.rb, line 1002 def validate1a(d, where) if ann = validate1(d, where) here = [d, where] if Array === ann [here, *ann] else [here] end end end
validate1a_ignore_encoding(d, where)
click to toggle source
# File lib/cddl.rb, line 989 def validate1a_ignore_encoding(d, where) if String === d validate1a(d.force_encoding(Encoding::BINARY), where) or ( text = d.force_encoding(Encoding::UTF_8).scrub { fail "can't use bytes as text" } rescue :NOT_A_TEXT_STRING validate1a(text, where) ) else validate1a(d, where) end end
validate_diag()
click to toggle source
# File lib/cddl.rb, line 820 def validate_diag [@last_data, @last_rule, @last_message] end
validate_forward(d, start, where)
click to toggle source
# File lib/cddl.rb, line 851 def validate_forward(d, start, where) # warn ["valforw", d, start, where].inspect i = 0 ann = [] where[1..-1].each { |r| t, s, e, _k, v = r # XXX if t == :recurse_grpent rule = lookup_recurse_grpent(s) n, ann2 = validate_linear(d, start+i, rule) return [false, ann] unless n i += n ann.concat(ann2) elsif t == :grpchoice return [false, ann] unless r[1..-1].any? {|cand| n, ann2 = validate_forward(d, start+i, [:foo, *cand]) if n i += n ann.concat(ann2) end} else fail r.inspect unless t == :member occ = 0 while ((occ < e) && i != d.size && ((n, ann2 = validate_linear(d, start+i, v)); n)) i += n occ += 1 ann.concat(ann2) end if occ < s # warn "*** lme #{@last_message.encoding} #{@last_message}" # warn "*** #{"\noccur #{occ} < #{s}, not reached at #{i} in array #{d} for #{where}".encoding}" @last_message << "\noccur #{occ} < #{s}, not reached at #{i} in array #{d} for #{where}" return [false, ann] end end } # warn ["valforw>", i].inspect [i, ann] end
validate_linear(d, start, where)
click to toggle source
returns number of matches or false for breakage
# File lib/cddl.rb, line 891 def validate_linear(d, start, where) # warn ["vallin", d, start, where].inspect fail unless Array === d case where[0] when :grpent # must be inside an array with nested occurrences validate_forward(d, start, where) else (ann = validate1a(d[start], where)) ? [1, ann] : [false, ann] end end
validate_result(check) { || ... }
click to toggle source
# File lib/cddl.rb, line 844 def validate_result(check) check || ( @last_message << yield false ) end
walk(rule, anno = nil) { |rule, anno))| ... }
click to toggle source
# File lib/cddl.rb, line 96 def walk(rule, anno = nil, &block) r = [] case rule when Array r.concat(Array(yield rule, anno)) case rule[0] when :type1, :array, :map a = (rule.cbor_annotations rescue nil) if rule.size == 2 a = a.first if a r.concat(rule[1..-1].map{|x| walk(x, a, &block)}) when :grpchoice r.concat(rule[1..-1].map{|x| x.flat_map {|y| walk(y, &block)}}) when :member r << walk(rule[3], &block) r << walk(rule[4], &block) when :anno r << walk(rule[2], &block) r << walk(rule[3], &block) else # p ["LEAF", rule[0]] end end r.compact end
Private Instance Methods
g_process(name, g, genericargs)
click to toggle source
# File lib/cddl.rb, line 1653 def g_process(name, g, genericargs) r, ids = g args = genericargs.children(:type1).map {|x| type1(x, true)} # warn "** args #{args.inspect}" fail "number of args #{name} #{ids.size} #{args.size}" if ids.size != args.size bindings = Hash[ids.zip(args)] # warn "** bindings: #{bindings.inspect}" genname = "#{name}<#{args.inspect}>" # XXX this works up to a RECURSE... # warn "** genname #{genname}" r_process(genname, r, bindings) end
gen_word()
click to toggle source
# File lib/cddl.rb, line 1422 def gen_word @words ||= (File.read("/usr/share/dict/words").lines.shuffle rescue %w{tic tac toe}) @wordptr ||= 0 @wordptr = 0 if @wordptr == @words.size w = @words[@wordptr].chomp @wordptr += 1 w end
group(n)
click to toggle source
# File lib/cddl.rb, line 1856 def group(n) # returns array choices = n.children(:grpchoice) if choices == [] # "cannot happen", workaround choices = n.s.children(:grpchoice) # warn "W2: #{choices.inspect}" end choices = choices.map {|choice| choice.children(:grpent).flat_map {|ch| grpent(ch)} } case choices.size when 1 choices.first else [[:grpchoice, *choices]] end end
group_recall(name, genericargs)
click to toggle source
# File lib/cddl.rb, line 1677 def group_recall(name, genericargs) if genericargs && (generic = @generics[name]) g = g_process(name, generic, genericargs) fail "#{name} not a group" unless g[0] == :grpent g[1..-1] elsif !genericargs && (g = rule_lookup(name, true)) fail "#{name} not a group" unless g[0] == :grpent g[1..-1] # AAA else fail "Unknown group #{name}" end end
grpent(n)
click to toggle source
# File lib/cddl.rb, line 1571 def grpent(n) # returns array of entries occ = occur(n.occur) if g = n.group || workaround1(n) gr = group(g) if occ != [1, 1] gr = [[:member, *occ, nil, [:grpent, *gr]]] end return gr end nt = n.type || (n.s && n.s.type) || (n.bareword && n.bareword.s.type) # workarounds unless nt warn "NO NT" warn @abnf.ast? fail ["ntype", n, n.children].inspect end if mk = n.memberkey # work around unclear bug in ast generation below if (t1 = mk.type1 || t1 = mk.value || ((t1 = mk.s) && (t1 = t1.type) && (t1 = t1.type1))) # workaround [[:member, *occ, type1(t1), type(nt)]] else bw = mk.bareword unless bw warn @abnf.ast? fail [n, n.children, mk, mk.children].inspect end name = bw.to_s [[:member, *occ, [:text, name.force_encoding(Encoding::UTF_8)], type(nt)]] end else t = if nbw = nt.bareword t1 = nbw.type1 # || n.bareword.s.type.type1 # workaround rest = type_collect(nt, true) s = [:type1, type1(t1, true), *rest] # warn "T2: #{s.size} #{s}" -- maybe this should have a parenthesis warning? s.size == 2 ? s[1] : s # decapsulate single-element choice else type(nt, true) # type can actually be a group here! end pp ["t is", t] if ENV["CDDL_TRACE"] if t[0] == :grpent && (occ == [1, 1]) # go through the members here and multiply the occs t1 = t[1..-1].flatten(1) if t1[0] == :recurse_grpent [t1] elsif Array === t1[0] && t1[0][0] == :grpchoice fail t1.inspect unless t1.size == 1 t1 else t1.flat_map {|t2| if t2[0] == :member [t2] else fail t1.inspect unless t2[0] == :grpent # XXX Where is the nested multiplication here? t2[1..-1] end }.map {|mem| mem[1] *= occ[0] mem[2] *= occ[1] # Does all the infinity magic # p mem mem } end else if t[0] == :grpchoice && occ == [1, 1] [t] else if t[0] == :grpent t = [t[0], *t[1..-1].flatten(1)] end if occ[0] == 0 && t == [:grpchoice] [] # we won't be able to generate any of those else if t[0] == :grpchoice # FIXME: need to package grpchoice into grpent in a member t = [:grpent, t] end [[:member, *occ, nil, t]] end end end end end
memberkey_check(s)
click to toggle source
# File lib/cddl.rb, line 1720 def memberkey_check(s) s.each do |member| case member[0] when :grpchoice member[1..-1].each do |m| memberkey_check(m) end when :recurse_grpent # XXX we don't have the entry yet when :member fail "map entry #{member.inspect} without member key given: #{member}" unless member[3] || member[4][0] == :grpent || member[4][0] == :grpchoice else fail ["type1 member", member].inspect unless member[0] == :member end end end
occur(n)
click to toggle source
# File lib/cddl.rb, line 1873 def occur(n) case n.to_s when "" [1, 1] when "+" [1, MANY] when "?" [0, 1] when /\A(\d*)\*(\d*)\z/ if (s = $1) == "" s = 0 else s = s.to_i end if (e = $2) == "" e = MANY else e = e.to_i end [s, e] else fail "huh #{n.to_s}" end end
r_process(n, r, bindings = {})
click to toggle source
# File lib/cddl.rb, line 1465 def r_process(n, r, bindings = {}) t = r[0] # puts "Processing rule #{n} = #{t}" @stage1[n] ||= begin @stage1[n] = [t, [RECURSE_TYPE[t], n]] @bindings.push(bindings) r = [t, *r[1..-1].map {|e| case t when :grpchoice fail e[0].inspect unless e[0] == :grpent fail e unless e.size == 2 res = grpent(e[1]) # warn "RES #{res.inspect}" res when :grpent grpent(e) when :type1 type1(e, r.size == 2) # a single type1 could be a group else fail t end}] @bindings.pop r.cbor_annotation_add(n) rescue nil # AAA r end end
rule_lookup(name, canbegroup)
click to toggle source
# File lib/cddl.rb, line 1431 def rule_lookup(name, canbegroup) if b = @bindings.last[name] b else r = @rules[name] unless r if name[0] == "$" r = @rules[name] = if name[1] == "$" [:grpchoice] else [:type1] end elsif s = ENV["CDDL_INVENT"] s = "_" if s == "" r = [:type1, [:text, "#{s}-#{name}"]] return r end end if r t = r_process(name, r) unless t[0] == :type1 fail "#{name} not a type #{t}" unless canbegroup && (t[0] == :grpent || t[0] == :grpchoice) end t end end end
type(n, canbegroup = false)
click to toggle source
# File lib/cddl.rb, line 1846 def type(n, canbegroup = false) # pp ["nch", n.children] s = type_collect(n, canbegroup) if s.size == 1 s.first else [:type1, *s] end end
type1(n, canbegroup = false)
click to toggle source
# File lib/cddl.rb, line 1749 def type1(n, canbegroup = false) # puts "NVALUE #{n.value.inspect}" if v = n.type2 ch = n.children(:type2) if ro = n.rangeop cats = [] [:range, Range.new(*ch.map {|l| ok, val, cat = extract_value(type2(l)) fail "Can't have range with #{l}" unless ok # XXX really should be checking type coherence of range cats << cat val }, RANGE_EXCLUDE_END[ro.to_s]), cats[0] == cats[1] ? cats[0] : fail("Incompatible range #{cats}")] elsif anno = n.annotator annotyp = anno.id.to_s.intern unless SUPPORTED_ANNOTATIONS.include?(annotyp) fail "Unsupported annotation .#{annotyp}" end [:anno, annotyp, *ch.map {|t| type2(t)}] else type2(v, canbegroup) end else case str = n.to_s when "#" [:prim] when /\A#(\d+)/ maj = $1.to_i s = [:prim, maj, *n.children(:uint).map(&:to_s).map(&:to_i)] if tn = n.headnumber if ui = tn.uint s << ui.to_s.to_i elsif tt = tn.type s << type(tt) end end if tagged_type = n.type s << type(tagged_type) end s when /\A[\[{]/ type = BRACE[str[0]] @insides << type s = n.children(:group).flat_map {|g| group(g)} @insides.pop if type == :map memberkey_check(s) # XXX could do the occurrence multiplier here end # warn s.inspect [type, *s] when /\A&/ if gn = n.groupname s = group_recall(gn.to_s, n.genericarg).flatten(1) else @insides << :enum s = n.children(:group).flat_map {|g| group(g)} @insides.pop end [:type1, *s.map {|mem| t, _s, _e, _k, v = mem fail "enum #{t.inspect}" unless t == :member v.cbor_annotation_add(generate1(_k)) rescue nil # AAA (XXX: what if more than one?) v } ] when /\A~/ if tn = n.typename s = type_recall(tn.to_s, false, n.genericarg).flatten(1) else fail "No typename in unwrap #{n}" end if s[0] != :type1 fail [:UNWRAPs0, s].inspect end case s[1] when :map, :array [:grpent, s[2..-1]] when :prim if s[2] != 6 fail [:UNWRAPs2, s].inspect end s[4] # check :type1? else fail [:UNWRAPs1, s].inspect end else "unimplemented #{n}" end end end
type2(n, canbegroup = false)
click to toggle source
# File lib/cddl.rb, line 1690 def type2(n, canbegroup = false) if v = n.type type(n.type) elsif v = n.value value(n) elsif v = n.typename ga = n.genericarg || (n.s && n.s.genericarg) # workaround t = type_recall(v.to_s, canbegroup, ga) if t[0] == :type1 if t.size == 2 t = t[1] else t = [:type1, *t[1..-1].flat_map {|t1| if t1[0] == :type1 t1[1..-1] else [t1] end }] end end # XXX should flatten the thing, too t = t.dup t.cbor_annotation_replace(v.to_s) rescue nil # AAA t else fail [n, n.children].inspect end end
type_collect(n, canbegroup)
click to toggle source
# File lib/cddl.rb, line 1842 def type_collect(n, canbegroup) n.children(:type1).map {|ch| type1(ch, canbegroup)} end
type_recall(name, canbegroup, genericargs)
click to toggle source
# File lib/cddl.rb, line 1665 def type_recall(name, canbegroup, genericargs) # p genericargs, "GENERIC" if genericargs && (generic = @generics[name]) t = g_process(name, generic, genericargs) t elsif !genericargs && (t = rule_lookup(name, canbegroup)) t else fail "Unknown type #{name} #{genericargs.inspect} #{@bindings}" #{@abnf.ast?}" end end
value(n)
click to toggle source
# File lib/cddl.rb, line 1504 def value(n) # cheat: # warn n s = n.to_s if s[-1] == "'" bsqual, *parts = s.split("'", -1) if parts[-1] != "" warn "*** Problem decoding byte string #{s.inspect}" end bsval = parts[0...-1].join("'").gsub(/\\u([Dd][89AaBb][0-9a-zA-Z]{2})\\u([Dd][CcDdEeFf][0-9a-zA-Z]{2})|\\u([0-9a-zA-Z]{4})|\\u\{([0-9a-zA-Z]+)\}|\r?(\n)|\\([^u])/) { if hex = $3 || $4 hex.to_i(16).chr(Encoding::UTF_8) elsif lf = $5 lf elsif escaped = $6 STRING_ESCAPES[$6] or ( fail "Invalid String Escape #{escaped.inspect} in #{s.inspect}" ) else ((($1.to_i(16) & 0x3ff) << 10) + ($2.to_i(16) & 0x3ff) + 0x10000).chr(Encoding::UTF_8) end } [:bytes, case bsqual when "" bsval.b when "h" bsval.gsub(/\s/, "").chars.each_slice(2).map{ |x| Integer(x.join, 16).chr("BINARY") }.join.b when "b64" Base64.urlsafe_decode64(bsval) else warn "*** Can't handle byte string type #{bsqual.inspect} yet" end ] else if s[-1] == '"' s.gsub!(/\\u([Dd][89AaBb][0-9a-zA-Z]{2})\\u([Dd][CcDdEeFf][0-9a-zA-Z]{2})|\\([^u]|u[0-9a-zA-Z]{4})/) { if $3 "\\#$3" # skip this, use eval parser else ((($1.to_i(16) & 0x3ff) << 10) + ($2.to_i(16) & 0x3ff) + 0x10000).chr(Encoding::UTF_8) end } end val = eval(s) # warn val case val when Integer; [:int, val] when Numeric; [:float, val] when String; [:text, val.force_encoding(Encoding::UTF_8)] else fail "huh? value #{val.inspect}" end end end
workaround1(n)
click to toggle source
# File lib/cddl.rb, line 1561 def workaround1(n) n.children.each do |ch| if ch.to_s == ")" # warn "W1 #{ch.inspect} -> #{ch.group.inspect}" return ch.group end end nil end