Module: JCR

Defined in:
lib/jcr/jcr.rb,
lib/jcr/parser.rb,
lib/jcr/version.rb,
lib/jcr/find_roots.rb,
lib/jcr/check_groups.rb,
lib/jcr/evaluate_rules.rb,
lib/jcr/map_rule_names.rb,
lib/jcr/process_directives.rb,
lib/jcr/evaluate_array_rules.rb,
lib/jcr/evaluate_group_rules.rb,
lib/jcr/evaluate_value_rules.rb,
lib/jcr/evaluate_member_rules.rb,
lib/jcr/evaluate_object_rules.rb

Defined Under Namespace

Classes: ArrayBehavior, Context, EvalConditions, Evaluation, Failure, ObjectBehavior, Parser, Root, Transformer

Constant Summary collapse

VERSION =
"0.7.0"

Class Method Summary collapse

Class Method Details

.annotations_to_s(annotations) ⇒ Object



468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
# File 'lib/jcr/evaluate_rules.rb', line 468

def self.annotations_to_s( annotations )
  retval = ""
  annotations.each do |a|
    case
      when a[:unordered_annotation]
        retval = retval + "@{unordered}"
      when a[:not_annotation]
        retval = retval + "@{not}"
      when a[:root_annotation]
        retval = retval + "@{root}"
      else
        retval = retval + "@{ ** unknown annotation ** }"
    end
  end if annotations
  retval = retval + " " if retval.length != 0
  return retval
end

.array_to_s(jcr, shallow = true) ⇒ Object



290
291
292
293
# File 'lib/jcr/evaluate_array_rules.rb', line 290

def self.array_to_s( jcr, shallow=true )
  rules, annotations = get_rules_and_annotations( jcr )
  return "#{annotations_to_s( annotations)}[ #{rules_to_s( rules, shallow )} ]"
end

.bad_value(jcr, rule_atom, expected, actual) ⇒ Object



403
404
405
# File 'lib/jcr/evaluate_value_rules.rb', line 403

def self.bad_value jcr, rule_atom, expected, actual
  Evaluation.new( false, "expected #{expected} but got #{actual} for #{raised_rule(jcr,rule_atom)}" )
end

.check_array_for_group(node, mapping) ⇒ Object



109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
# File 'lib/jcr/check_groups.rb', line 109

def self.check_array_for_group node, mapping
  if node.is_a?( Array )
    node.each do |child_node|
      check_array_for_group( child_node, mapping )
    end
  elsif node.is_a? Hash
    if node[:target_rule_name]
      trule = get_name_mapping(node[:target_rule_name][:rule_name], mapping)
      disallowed_group_in_array?(trule, mapping)
    elsif node[:group_rule]
      disallowed_group_in_array?(node[:group_rule], mapping)
    else
      check_groups(node, mapping)
    end
  end
end

.check_groups(tree, mapping) ⇒ Object



21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# File 'lib/jcr/check_groups.rb', line 21

def self.check_groups( tree, mapping )
  if tree.is_a? Array
    tree.each do |node|
      check_groups( node, mapping )
    end
  else # is a hash
    if tree[:rule]
      check_groups( tree[:rule], mapping )
    elsif tree[:primitive_rule]
      check_value_for_group( tree[:primitive_rule], mapping )
    elsif tree[:member_rule]
      check_member_for_group( tree[:member_rule], mapping )
    elsif tree[:array_rule]
      check_array_for_group( tree[:array_rule], mapping )
    elsif tree[:object_rule]
      check_object_for_group( tree[:object_rule], mapping )
    end
  end
end

.check_member_for_group(node, mapping) ⇒ Object



72
73
74
75
76
77
78
79
80
81
82
83
84
# File 'lib/jcr/check_groups.rb', line 72

def self.check_member_for_group node, mapping
  if node.is_a? Array
    node = node[0]
  end
  if node[:target_rule_name]
    trule = get_name_mapping( node[:target_rule_name][:rule_name], mapping )
    disallowed_group_in_member?( trule, mapping )
  elsif node[:group_rule]
    disallowed_group_in_member?( node[:group_rule], mapping )
  else
    check_groups( node, mapping )
  end
end

.check_object_for_group(node, mapping) ⇒ Object



146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
# File 'lib/jcr/check_groups.rb', line 146

def self.check_object_for_group node, mapping
  if node.is_a?( Array )
    node.each do |child_node|
      check_object_for_group( child_node, mapping )
    end
  elsif node.is_a? Hash
    if node[:target_rule_name]
      trule = get_name_mapping(node[:target_rule_name][:rule_name], mapping)
      disallowed_group_in_object?(trule, mapping)
    elsif node[:group_rule]
      disallowed_group_in_object?(node[:group_rule], mapping)
    else
      check_groups(node, mapping)
    end
  end
end

.check_rule_target_names(node, mapping) ⇒ Object



44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
# File 'lib/jcr/map_rule_names.rb', line 44

def self.check_rule_target_names( node, mapping )
  if node.is_a? Array
    node.each do |child_node|
      check_rule_target_names( child_node, mapping )
    end
  elsif node.is_a? Hash
    if node[:target_rule_name] && !mapping[ node[:target_rule_name][:rule_name].to_str ]
      raise_rule_name_missing node[:target_rule_name][:rule_name]
    else
      if node[:rule]
        check_rule_target_names( node[:rule], mapping )
      elsif node[:group_rule]
        check_rule_target_names( node[:group_rule], mapping )
      elsif node[:primitive_rule]
        check_rule_target_names( node[:primitive_rule], mapping )
      elsif node[:array_rule]
        check_rule_target_names( node[:array_rule], mapping )
      elsif node[:object_rule]
        check_rule_target_names( node[:object_rule], mapping )
      elsif node[:member_rule]
        check_rule_target_names( node[:member_rule], mapping )
      end
    end
  end
end

.check_value_for_group(node, mapping) ⇒ Object



41
42
43
44
45
# File 'lib/jcr/check_groups.rb', line 41

def self.check_value_for_group node, mapping
  if node.is_a?( Hash ) && node[:group_rule]
    disallowed_group_in_value?( node[:group_rule], mapping )
  end
end

.cli_eval(ctx, data, root_name, quiet) ⇒ Object



336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
# File 'lib/jcr/jcr.rb', line 336

def self.cli_eval ctx, data, root_name, quiet
  ec = 2
  e = ctx.evaluate( data, root_name )
  if e.success
    unless quiet
      puts "Success!"
    end
    ec = 0
  else
    unless quiet
      puts "Failure! Use -v for more information."
      ctx.failure_report.each do |line|
        puts line
      end
    end
    ec = 3
  end
  return ec
end

.disallowed_group_in_array?(node, mapping) ⇒ Boolean

Returns:

  • (Boolean)


126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
# File 'lib/jcr/check_groups.rb', line 126

def self.disallowed_group_in_array? node, mapping
  if node.is_a? Hash
    node = [ node ]
  end
  node.each do |groupee|
    if groupee[:group_rule]
      disallowed_group_in_array?( groupee[:group_rule], mapping )
    elsif groupee[:target_rule_name]
      trule = get_name_mapping( groupee[:target_rule_name][:rule_name], mapping )
      if trule[:group_rule]
        disallowed_group_in_array?( trule[:group_rule], mapping )
      end
    elsif groupee[:member_rule]
      raise_group_error( "groups in array rules cannot have member rules", groupee[:member_rule] )
    else
      check_groups( groupee, mapping )
    end
  end
end

.disallowed_group_in_member?(node, mapping) ⇒ Boolean

Returns:

  • (Boolean)


86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
# File 'lib/jcr/check_groups.rb', line 86

def self.disallowed_group_in_member? node, mapping
  if node.is_a? Hash
    node = [ node ]
  end
  node.each do |groupee|
    if groupee[:sequence_combiner]
      raise_group_error( 'AND (comma) operation in group rule of member rule', groupee[:sequence_combiner] )
    end
    if groupee[:group_rule]
      disallowed_group_in_member?( groupee[:group_rule], mapping )
    elsif groupee[:target_rule_name]
      trule = get_name_mapping( groupee[:target_rule_name][:rule_name], mapping )
      if trule[:group_rule]
        disallowed_group_in_member?( trule[:group_rule], mapping )
      end
    elsif groupee[:member_rule]
      raise_group_error( "groups in member rules cannot have member rules", groupee[:member_rule] )
    else
      check_groups( groupee, mapping )
    end
  end
end

.disallowed_group_in_object?(node, mapping) ⇒ Boolean

Returns:

  • (Boolean)


163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
# File 'lib/jcr/check_groups.rb', line 163

def self.disallowed_group_in_object? node, mapping
  if node.is_a? Hash
    node = [ node ]
  end
  node.each do |groupee|
    if groupee[:group_rule]
      disallowed_group_in_object?( groupee[:group_rule], mapping )
    elsif groupee[:target_rule_name]
      trule = get_name_mapping( groupee[:target_rule_name][:rule_name], mapping )
      if trule[:group_rule]
        disallowed_group_in_object?( trule[:group_rule], mapping )
      end
    elsif groupee[:array_rule]
      raise_group_error( "groups in object rules cannot have array rules", groupee[:member_rule] )
    elsif groupee[:object_rule]
      raise_group_error( "groups in object rules cannot have other object rules", groupee[:member_rule] )
    elsif groupee[:primitive_rule]
      raise_group_error( "groups in object rules cannot have value rules", groupee[:member_rule] )
    else
      check_groups( groupee, mapping )
    end
  end
end

.disallowed_group_in_value?(node, mapping) ⇒ Boolean

Returns:

  • (Boolean)


47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
# File 'lib/jcr/check_groups.rb', line 47

def self.disallowed_group_in_value? node, mapping
  if node.is_a? Hash
    node = [ node ]
  end
  node.each do |groupee|
    if groupee[:sequence_combiner]
      raise_group_error( 'AND (comma) operation in group rule of value rule', groupee[:sequence_combiner] )
    end
    if groupee[:group_rule]
      disallowed_group_in_value?( groupee[:group_rule], mapping )
    elsif groupee[:target_rule_name]
      trule = get_name_mapping( groupee[:target_rule_name][:rule_name], mapping )
      disallowed_group_in_value?( trule[:rule], mapping )
    elsif groupee[:member_rule]
      raise_group_error( "groups in value rules cannot have member rules", groupee[:member_rule] )
    elsif groupee[:object_rule]
      raise_group_error( "groups in value rules cannot have object rules", groupee[:member_rule] )
    elsif groupee[:array_rule]
      raise_group_error( "groups in value rules cannot have array rules", groupee[:member_rule] )
    elsif groupee[:primitive_rule]
      disallowed_group_in_value?( groupee[:primitive_rule], mapping )
    end
  end
end

.elide(s) ⇒ Object



296
297
298
299
300
301
302
# File 'lib/jcr/evaluate_rules.rb', line 296

def self.elide s
  if s.length > 45
    s = s[0..41]
    s = s + " ..."
  end
  return s
end

.evaluate_array(jcr, rule_atom, data, econs, behavior = nil) ⇒ Object



60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
# File 'lib/jcr/evaluate_array_rules.rb', line 60

def self.evaluate_array jcr, rule_atom, data, econs, behavior = nil

  rules, annotations = get_rules_and_annotations( jcr )

  ordered = true

  if behavior && behavior.is_a?( ArrayBehavior )
    ordered = behavior.ordered
  end

  annotations.each do |a|
    if a[:unordered_annotation]
      ordered = false
      break
    end
  end

  # if the data is not an array
  return evaluate_not( annotations,
    Evaluation.new( false, "#{data} is not an array #{raised_rule(jcr,rule_atom)}"), econs ) unless data.is_a? Array

  # if the array is zero length and there are zero sub-rules (it is suppose to be empty)
  return evaluate_not( annotations,
    Evaluation.new( true, nil ), econs ) if rules.empty? && data.empty?

  # if the array is not empty and there are zero sub-rules (it is suppose to be empty)
  return evaluate_not( annotations,
    Evaluation.new( false, "Non-empty array for #{raised_rule(jcr,rule_atom)}" ), econs ) if rules.empty? && data.length != 0

  if ordered
    return evaluate_not( annotations, evaluate_array_rule_ordered( rules, rule_atom, data, econs, behavior ), econs )
  else
    return evaluate_not( annotations, evaluate_array_rule_unordered( rules, rule_atom, data, econs, behavior ), econs )
  end
end

.evaluate_array_rule(jcr, rule_atom, data, econs, behavior = nil) ⇒ Object



48
49
50
51
52
53
54
55
56
57
58
# File 'lib/jcr/evaluate_array_rules.rb', line 48

def self.evaluate_array_rule jcr, rule_atom, data, econs, behavior = nil

  push_trace_stack( econs, jcr )
  trace( econs, "Evaluating array rule starting at #{slice_to_s(jcr)} against", data )
  trace_def( econs, "array", jcr, data )
  retval = evaluate_array( jcr, rule_atom, data, econs, behavior )
  trace_eval( econs, "Array", retval, jcr, data, "array" )
  pop_trace_stack( econs )
  return retval

end

.evaluate_array_rule_ordered(jcr, rule_atom, data, econs, behavior = nil) ⇒ Object



96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
# File 'lib/jcr/evaluate_array_rules.rb', line 96

def self.evaluate_array_rule_ordered jcr, rule_atom, data, econs, behavior = nil
  retval = nil

  behavior = ArrayBehavior.new unless behavior
  array_index = behavior.last_index


  jcr.each do |rule|

    # short circuit logic
    if rule[:choice_combiner] && retval && retval.success
      next
    elsif rule[:sequence_combiner] && retval && !retval.success
      break
    end

    repeat_min, repeat_max, repeat_step = get_repetitions( rule, econs )

    # group rules must be evaluated differently
    # groups require the effects of the evaluation to be discarded if they are false
    # groups must also be given the entire array

    if (grule = get_group(rule, econs))

      if repeat_min == 0
        retval = Evaluation.new( true, nil )
      else
        for i in 1..repeat_min do
          if array_index == data.length
            return Evaluation.new( false, "array is not large enough for #{raised_rule(jcr,rule_atom)}" )
          else
            group_behavior = ArrayBehavior.new( behavior )
            group_behavior.last_index = array_index
            retval = evaluate_rule( grule, rule_atom, data, econs, group_behavior )
            if retval.success
              behavior.checked_hash.merge!( group_behavior.checked_hash )
              array_index = group_behavior.last_index
            else
              break;
            end
          end
        end
      end
      if !retval || retval.success
        for i in behavior.checked_hash.length..repeat_max-1 do
          break if array_index == data.length
          group_behavior = ArrayBehavior.new( behavior )
          group_behavior.last_index = array_index
          e = evaluate_rule( grule, rule_atom, data, econs, group_behavior )
          if e.success
            behavior.checked_hash.merge!( group_behavior.checked_hash )
            array_index = group_behavior.last_index
          else
            break;
          end
        end
      end

    else # else not grule (group)

      if repeat_min == 0
        retval = Evaluation.new( true, nil )
      else
        for i in 1..repeat_min do
          if array_index == data.length
            return Evaluation.new( false, "array is not large enough for #{raised_rule(jcr,rule_atom)}" )
          else
            retval = evaluate_rule( rule, rule_atom, data[ array_index ], econs, nil )
            break unless retval.success
            array_index = array_index + 1
            behavior.checked_hash[ i + behavior.last_index ] = retval.success
          end
        end
      end
      if !retval || retval.success
        for i in behavior.checked_hash.length..repeat_max-1 do
          break if array_index == data.length
          e = evaluate_rule( rule, rule_atom, data[ array_index ], econs, nil )
          break unless e.success
          array_index = array_index + 1
        end
      end

    end # end if grule else

    if repeat_step && ( array_index - repeat_min ) % repeat_step != 0
      retval = Evaluation.new( false, "Matches (#{array_index }) do not meat repetition step for #{repeat_max} % #{repeat_step}")
    end

  end

  behavior.last_index = array_index

  if data.length > array_index && behavior.extra_prohibited
    retval = Evaluation.new( false, "More itmes in array than specified for #{raised_rule(jcr,rule_atom)}" )
  end

  return retval

end

.evaluate_array_rule_unordered(jcr, rule_atom, data, econs, behavior = nil) ⇒ Object



197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
# File 'lib/jcr/evaluate_array_rules.rb', line 197

def self.evaluate_array_rule_unordered jcr, rule_atom, data, econs, behavior = nil

  retval = nil
  unless behavior
    behavior = ArrayBehavior.new
    behavior.ordered = false
  end
  highest_index = 0

  jcr.each do |rule|

    # short circuit logic
    if rule[:choice_combiner] && retval && retval.success
      next
    elsif rule[:sequence_combiner] && retval && !retval.success
      break
    end

    repeat_min, repeat_max, repeat_step = get_repetitions( rule, econs )

    # group rules must be evaluated differently
    # groups require the effects of the evaluation to be discarded if they are false
    # groups must also be given the entire array

    if (grule = get_group(rule, econs))

      successes = 0
      for i in 0..repeat_max-1
        group_behavior = ArrayBehavior.new( behavior )
        group_behavior.last_index = highest_index
        group_behavior.ordered = false
        e = evaluate_rule( grule, rule_atom, data, econs, group_behavior )
        if e.success
          highest_index = group_behavior.last_index
          behavior.checked_hash.merge!( group_behavior.checked_hash )
          successes = successes + 1
        else
          break;
        end
      end

      if successes == 0 && repeat_min > 0
        retval = Evaluation.new( false, "array does not contain #{rule} for #{raised_rule(jcr,rule_atom)}")
      elsif successes < repeat_min
        retval = Evaluation.new( false, "array does not have enough #{rule} for #{raised_rule(jcr,rule_atom)}")
      elsif successes > repeat_max
        retval = Evaluation.new( false, "array has too many #{rule} for #{raised_rule(jcr,rule_atom)}")
      elsif repeat_step && ( successes - repeat_min ) % repeat_step != 0
        retval = Evaluation.new( false, "array matches (#{successes}) do not meet repetition step of #{repeat_max} % #{repeat_step} with #{rule} for #{raised_rule(jcr,rule_atom)}")
      else
        retval = Evaluation.new( true, nil )
      end

    else # else not group rule

      successes = 0
      for i in behavior.last_index..data.length
        break if successes == repeat_max
        unless behavior.checked_hash[ i ]
          e = evaluate_rule( rule, rule_atom, data[ i ], econs, nil )
          if e.success
            behavior.checked_hash[ i ] = e.success
            highest_index = i if i > highest_index
            successes = successes + 1
          end
        end
      end

      if successes == 0 && repeat_min > 0
        retval = Evaluation.new( false, "array does not contain #{rule} for #{raised_rule(jcr,rule_atom)}")
      elsif successes < repeat_min
        retval = Evaluation.new( false, "array does not have enough #{rule} for #{raised_rule(jcr,rule_atom)}")
      elsif successes > repeat_max
        retval = Evaluation.new( false, "array has too many #{rule} for #{raised_rule(jcr,rule_atom)}")
      elsif repeat_step && ( successes - repeat_min ) % repeat_step != 0
        retval = Evaluation.new( false, "array matches (#{successes}) do not meet repetition step of #{repeat_max} % #{repeat_step} with #{rule} for #{raised_rule(jcr,rule_atom)}")
      else
        retval = Evaluation.new( true, nil)
      end

    end # if grule else

  end

  behavior.last_index = highest_index

  if data.length > behavior.checked_hash.length && behavior.extra_prohibited
    retval = Evaluation.new( false, "More itmes in array than specified for #{raised_rule(jcr,rule_atom)}" )
  end

  return retval
end

.evaluate_callback(jcr, data, econs, callback, e) ⇒ Object



137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
# File 'lib/jcr/evaluate_rules.rb', line 137

def self.evaluate_callback jcr, data, econs, callback, e
  retval = e
  c = econs.callbacks[ callback ]
  if e.success
    retval = c.jcr_callback :rule_eval_true, jcr, data
  else
    retval = c.jcr_callback :rule_eval_false, jcr, data, e
  end
  if retval.is_a? TrueClass
    retval = Evaluation.new( true, nil )
  elsif retval.is_a? FalseClass
    retval = Evaluation.new( false, nil )
  elsif retval.is_a? String
    retval = Evaluation.new( false, retval )
  end
  trace( econs, "Callback #{callback} given evaluation of #{e.success} and returned #{retval}")
  return retval
end

.evaluate_group(jcr, rule_atom, data, econs, behavior = nil) ⇒ Object



41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# File 'lib/jcr/evaluate_group_rules.rb', line 41

def self.evaluate_group jcr, rule_atom, data, econs, behavior = nil

  rules, annotations = get_rules_and_annotations( jcr )

  retval = nil

  rules.each do |rule|
    if rule[:choice_combiner] && retval && retval.success
      return evaluate_not( annotations, retval, econs ) # short circuit
    elsif rule[:sequence_combiner] && retval && !retval.success
      return evaluate_not( annotations, retval, econs ) # short circuit
    end
    retval = evaluate_rule( rule, rule_atom, data, econs, behavior )
  end

  return evaluate_not( annotations, retval, econs )
end

.evaluate_group_rule(jcr, rule_atom, data, econs, behavior = nil) ⇒ Object



29
30
31
32
33
34
35
36
37
38
39
# File 'lib/jcr/evaluate_group_rules.rb', line 29

def self.evaluate_group_rule jcr, rule_atom, data, econs, behavior = nil

  push_trace_stack( econs, jcr )
  trace( econs, "Evaluating group rule against ", data )
  trace_def( econs, "group", jcr, data )
  retval = evaluate_group( jcr, rule_atom, data, econs, behavior )
  trace_eval( econs, "Group", retval, jcr, data, "group" )
  pop_trace_stack( econs )
  return retval

end

.evaluate_member(jcr, rule_atom, data, econs) ⇒ Object



40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
# File 'lib/jcr/evaluate_member_rules.rb', line 40

def self.evaluate_member jcr, rule_atom, data, econs

  # unlike the other evaluate functions, here data is not just the json data.
  # it is an array, the first element being the member name or regex and the
  # second being the json data to be furthered on to other evaluation functions


  rules, annotations = get_rules_and_annotations( jcr )
  rule = merge_rules( rules )

  member_match = false

  if rule[:member_name]
    match_spec = rule[:member_name][:q_string].to_s
    if match_spec == data[ 0 ]
      member_match = true
    end
  else # must be regex
    regex = rule[:member_regex][:regex]
    if regex.is_a? Array
      match_spec = Regexp.new( "" )
      trace( econs, "Noting empty regular expression." )
    else
      match_spec = Regexp.new( rule[:member_regex][:regex].to_s )
    end
    if match_spec =~ data[ 0 ]
      member_match = true
    end
  end

  if member_match
    e = evaluate_rule( rule, rule_atom, data[ 1 ], econs )
    e.member_found = true
    return evaluate_not( annotations, e, econs )
  end

  return evaluate_not( annotations,
     Evaluation.new( false, "#{match_spec} does not match #{data[0]} for #{raised_rule( jcr, rule_atom)}" ), econs )

end

.evaluate_member_rule(jcr, rule_atom, data, econs) ⇒ Object



28
29
30
31
32
33
34
35
36
37
38
# File 'lib/jcr/evaluate_member_rules.rb', line 28

def self.evaluate_member_rule jcr, rule_atom, data, econs

  push_trace_stack( econs, jcr )
  trace( econs, "Evaluating member rule for key '#{data[0]}' starting at #{slice_to_s(jcr)} against ", data[1])
  trace_def( econs, "member", jcr, data )
  retval = evaluate_member( jcr, rule_atom, data, econs )
  trace_eval( econs, "Member", retval, jcr, data, "member" )
  pop_trace_stack( econs )
  return retval

end

.evaluate_not(annotations, evaluation, econs) ⇒ Object



248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
# File 'lib/jcr/evaluate_rules.rb', line 248

def self.evaluate_not annotations, evaluation, econs
  is_not = false
  annotations.each do |a|
    if a[:not_annotation]
      is_not = true
      break
    end
  end

  if is_not
    trace( econs, "Not annotation changing result from #{evaluation.success} to #{!evaluation.success}")
    evaluation.success = !evaluation.success
  end
  return evaluation
end

.evaluate_object(jcr, rule_atom, data, econs, behavior = nil) ⇒ Object



42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
# File 'lib/jcr/evaluate_object_rules.rb', line 42

def self.evaluate_object jcr, rule_atom, data, econs, behavior = nil

  rules, annotations = get_rules_and_annotations( jcr )

  # if the data is not an object (Hash)
  return evaluate_not( annotations,
    Evaluation.new( false, "#{data} is not an object for #{raised_rule(jcr,rule_atom)}"), econs ) unless data.is_a? Hash

  # if the object has no members and there are zero sub-rules (it is suppose to be empty)
  return evaluate_not( annotations,
    Evaluation.new( true, nil ), econs ) if rules.empty? && data.length == 0

  # if the object has members and there are zero sub-rules (it is suppose to be empty)
  return evaluate_not( annotations,
    Evaluation.new( false, "Non-empty object for #{raised_rule(jcr,rule_atom)}" ), econs ) if rules.empty? && data.length != 0

  retval = nil
  behavior = ObjectBehavior.new unless behavior

  rules.each do |rule|

    # short circuit logic
    if rule[:choice_combiner] && retval && retval.success
      next
    elsif rule[:sequence_combiner] && retval && !retval.success
      return evaluate_not( annotations, retval, econs ) # short circuit
    end

    repeat_min, repeat_max, repeat_step = get_repetitions( rule, econs )

    # Pay attention here:
    # Group rules need to be treated differently than other rules
    # Groups must be evaluated as if they are rules evaluated in
    # isolation until they evaluate as true.
    # Also, groups must be handed the entire object, not key/values
    # as member rules use.

    if (grule = get_group(rule, econs))

      successes = 0
      for i in 0..repeat_max-1
        group_behavior = ObjectBehavior.new
        group_behavior.checked_hash.merge!( behavior.checked_hash )
        e = evaluate_rule( grule, rule_atom, data, econs, group_behavior )
        if e.success
          behavior.checked_hash.merge!( group_behavior.checked_hash )
          successes = successes + 1
        else
          break;
        end
      end

      if successes == 0 && repeat_min > 0
        retval = Evaluation.new( false, "object does not contain group #{rule} for #{raised_rule(jcr,rule_atom)}")
      elsif successes < repeat_min
        retval = Evaluation.new( false, "object does not have contain necessary number of group #{rule} for #{raised_rule(jcr,rule_atom)}")
      elsif repeat_step && ( successes - repeat_min ) % repeat_step != 0
        retval = Evaluation.new( false, "object matches (#{successes}) do not have contain repetition #{repeat_max} % #{repeat_step} of group #{rule} for #{raised_rule(jcr,rule_atom)}")
      else
        retval = Evaluation.new( true, nil )
      end

    else # if not grule

      repeat_results = nil

      # do a little lookahead for member rules defined by names
      # if defined by a name, and not a regex, just pluck it from the object
      # and short-circuit the enumeration

      lookahead = get_leaf_rule( rule, econs )
      lrules, lannotations = get_rules_and_annotations( lookahead[:member_rule] )
      if lrules[0][:member_name]

        repeat_results = {}
        k = lrules[0][:member_name][:q_string].to_s
        v = data[k]
        if v
          unless behavior.checked_hash[k]
            e = evaluate_rule(rule, rule_atom, [k, v], econs, nil)
            behavior.checked_hash[k] = e.success
            repeat_results[ k ] = v if e.success
          end
        else
          trace( econs, "No member '#{k}' found in object.")
          e = evaluate_rule(rule, rule_atom, [nil, nil], econs, nil)
          repeat_results[ nil ] = nil if e.success
        end

      else

        regex = lrules[0][:member_regex][:regex]
        trace( econs, "Scanning object for #{regex}.")
        i = 0
        found = false
        repeat_results = data.select do |k,v|
          unless behavior.checked_hash[k]
            if i < repeat_max
              e = evaluate_rule(rule, rule_atom, [k, v], econs, nil)
              behavior.checked_hash[k] = e.success
              i = i + 1 if e.success
              found = true if e.member_found
              e.success
            end
          end
        end
        unless found
          trace( econs, "No member matching #{regex} found in object.")
          e = evaluate_rule(rule, rule_atom, [nil, nil], econs, nil)
          repeat_results[ nil ] = nil if e.success
        end

      end

      trace( econs, "Found #{repeat_results.length} matching members repetitions in object with min #{repeat_min} and max #{repeat_max}" )
      if repeat_results.length == 0 && repeat_min > 0
        retval = Evaluation.new( false, "object does not contain #{rule} for #{raised_rule(jcr,rule_atom)}")
      elsif repeat_results.length < repeat_min
        retval = Evaluation.new( false, "object does not have enough #{rule} for #{raised_rule(jcr,rule_atom)}")
      elsif repeat_results.length > repeat_max
        retval = Evaluation.new( false, "object has too many #{rule} for #{raised_rule(jcr,rule_atom)}")
      elsif repeat_step && ( repeat_results.length - repeat_min ) % repeat_step != 0
        retval = Evaluation.new( false, "object matches (#{repeat_results.length}) does not match repetition step of #{repeat_max} & #{repeat_step} for #{rule} for #{raised_rule(jcr,rule_atom)}")
      else
        retval = Evaluation.new( true, nil)
      end
    end

  end # end if grule else

  return evaluate_not( annotations, retval, econs )
end

.evaluate_object_rule(jcr, rule_atom, data, econs, behavior = nil) ⇒ Object



30
31
32
33
34
35
36
37
38
39
40
# File 'lib/jcr/evaluate_object_rules.rb', line 30

def self.evaluate_object_rule jcr, rule_atom, data, econs, behavior = nil

  push_trace_stack( econs, jcr )
  trace( econs, "Evaluating object rule starting at #{slice_to_s(jcr)} against", data )
  trace_def( econs, "object", jcr, data )
  retval = evaluate_object( jcr, rule_atom, data, econs, behavior )
  trace_eval( econs, "Object", retval, jcr, data, "object" )
  pop_trace_stack( econs )
  return retval

end

.evaluate_rule(jcr, rule_atom, data, econs, behavior = nil) ⇒ Object



94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
# File 'lib/jcr/evaluate_rules.rb', line 94

def self.evaluate_rule jcr, rule_atom, data, econs, behavior = nil
  if jcr.is_a?( Hash )
    if jcr[:rule_name]
      rn = slice_to_s( jcr[:rule_name] )
      trace( econs, "Named Rule: #{rn}" )
    end
  end

  retval = Evaluation.new( false, "failed to evaluate rule properly" )
  case
    when behavior.is_a?( ArrayBehavior )
      retval = evaluate_array_rule( jcr, rule_atom, data, econs, behavior)
    when behavior.is_a?( ObjectBehavior )
      retval = evaluate_object_rule( jcr, rule_atom, data, econs, behavior)
    when jcr[:rule]
      retval = evaluate_rule( jcr[:rule], rule_atom, data, econs, behavior)
    when jcr[:target_rule_name]
      target = econs.mapping[ jcr[:target_rule_name][:rule_name].to_s ]
      raise "Target rule not in mapping. This should have been checked earlier." unless target
      trace( econs, "Referencing target rule #{slice_to_s(target)} from #{slice_to_s( jcr[:target_rule_name][:rule_name] )}" )
      retval = evaluate_rule( target, target, data, econs, behavior )
    when jcr[:primitive_rule]
      retval = evaluate_value_rule( jcr[:primitive_rule], rule_atom, data, econs)
    when jcr[:group_rule]
      retval = evaluate_group_rule( jcr[:group_rule], rule_atom, data, econs, behavior)
    when jcr[:array_rule]
      retval = evaluate_array_rule( jcr[:array_rule], rule_atom, data, econs, behavior)
    when jcr[:object_rule]
      retval = evaluate_object_rule( jcr[:object_rule], rule_atom, data, econs, behavior)
    when jcr[:member_rule]
      retval = evaluate_member_rule( jcr[:member_rule], rule_atom, data, econs)
    else
      retval = Evaluation.new( true, nil )
  end
  if jcr.is_a?( Hash ) && jcr[:rule_name]
    rn = jcr[:rule_name].to_s
    if econs.callbacks[ rn ]
      retval = evaluate_callback( jcr, data, econs, rn, retval )
    end
  end
  return retval
end

.evaluate_ruleset(data, ctx, root_name = nil) ⇒ Object



112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
# File 'lib/jcr/jcr.rb', line 112

def self.evaluate_ruleset( data, ctx, root_name = nil )
  roots = []
  if root_name
    root_rule = ctx.mapping[root_name]
    raise "No rule by the name of #{root_name} for a root rule has been found" unless root_rule
    root = JCR::Root.new( root_rule, root_name )
    roots << root
  else
    roots = ctx.roots
  end

  raise "No root rule defined. Specify a root rule name" if roots.empty?

  retval = nil
  roots.each do |r|
    pp "Evaluating Root:", rule_to_s( r.rule, false ) if ctx.trace
    raise "Root rules cannot be member rules" if r.rule[:member_rule]
    econs = EvalConditions.new( ctx.mapping, ctx.callbacks, ctx.trace )
    retval = JCR.evaluate_rule( r.rule, r.rule, data, econs )
    break if retval.success
    # else
    r.failures = econs.failures
    ctx.failed_roots << r
  end

  ctx.failure_report = failure_report( ctx )
  return retval
end

.evaluate_value_rule(jcr, rule_atom, data, econs) ⇒ Object



28
29
30
31
32
33
34
35
36
37
38
39
# File 'lib/jcr/evaluate_value_rules.rb', line 28

def self.evaluate_value_rule jcr, rule_atom, data, econs

  push_trace_stack( econs, jcr )
  trace( econs, "Evaluating value rule starting at #{slice_to_s(jcr)}" )
  trace_def( econs, "value", jcr, data )
  rules, annotations = get_rules_and_annotations( jcr )

  retval = evaluate_not( annotations, evaluate_values( rules[0], rule_atom, data, econs ), econs )
  trace_eval( econs, "Value", retval, jcr, data, "value")
  pop_trace_stack( econs )
  return retval
end

.evaluate_values(jcr, rule_atom, data, econs) ⇒ Object



41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
# File 'lib/jcr/evaluate_value_rules.rb', line 41

def self.evaluate_values jcr, rule_atom, data, econs
  case

    #
    # any
    #

    when jcr[:any]
      return Evaluation.new( true, nil )

    #
    # integers
    #

    when jcr[:integer_v]
      si = jcr[:integer_v].to_s
      if si == "integer"
        return bad_value( jcr, rule_atom, "integer", data ) unless data.is_a?( Integer )
      end
    when jcr[:integer]
      i = jcr[:integer].to_s.to_i
      return bad_value( jcr, rule_atom, i, data ) unless data == i
    when jcr[:integer_min] != nil && jcr[:integer_max] == nil
      return bad_value( jcr, rule_atom, "integer", data ) unless data.is_a?( Integer )
      min = jcr[:integer_min].to_s.to_i
      return bad_value( jcr, rule_atom, min, data ) unless data >= min
    when jcr[:integer_min] == nil && jcr[:integer_max] != nil
      return bad_value( jcr, rule_atom, "integer", data ) unless data.is_a?( Integer )
      max = jcr[:integer_max].to_s.to_i
      return bad_value( jcr, rule_atom, max, data ) unless data <= max
    when jcr[:integer_min],jcr[:integer_max]
      return bad_value( jcr, rule_atom, "integer", data ) unless data.is_a?( Integer )
      min = jcr[:integer_min].to_s.to_i
      return bad_value( jcr, rule_atom, min, data ) unless data >= min
      max = jcr[:integer_max].to_s.to_i
      return bad_value( jcr, rule_atom, max, data ) unless data <= max
    when jcr[:sized_int_v]
      bits = jcr[:sized_int_v][:bits].to_i
      return bad_value( jcr, rule_atom, "int" + bits.to_s, data ) unless data.is_a?( Integer )
      min = -(2**(bits-1))
      return bad_value( jcr, rule_atom, min, data ) unless data >= min
      max = 2**(bits-1)-1
      return bad_value( jcr, rule_atom, max, data ) unless data <= max
    when jcr[:sized_uint_v]
      bits = jcr[:sized_uint_v][:bits].to_i
      return bad_value( jcr, rule_atom, "int" + bits.to_s, data ) unless data.is_a?( Integer )
      min = 0
      return bad_value( jcr, rule_atom, min, data ) unless data >= min
      max = 2**bits-1
      return bad_value( jcr, rule_atom, max, data ) unless data <= max

    #
    # floats
    #

    when jcr[:float_v]
      sf = jcr[:float_v].to_s
      if sf == "float"
        return bad_value( jcr, rule_atom, "float", data ) unless data.is_a?( Float )
      end
    when jcr[:float]
      f = jcr[:float].to_s.to_f
      return bad_value( jcr, rule_atom, f, data ) unless data == f
    when jcr[:float_min] != nil && jcr[:float_max] == nil
      return bad_value( jcr, rule_atom, "float", data ) unless data.is_a?( Float )
      min = jcr[:float_min].to_s.to_f
      return bad_value( jcr, rule_atom, min, data ) unless data >= min
    when jcr[:float_min] == nil && jcr[:float_max] != nil
      return bad_value( jcr, rule_atom, "float", data ) unless data.is_a?( Float )
      max = jcr[:float_max].to_s.to_f
      return bad_value( jcr, rule_atom, max, data ) unless data <= max
    when jcr[:float_min],jcr[:float_max]
      return bad_value( jcr, rule_atom, "float", data ) unless data.is_a?( Float )
      min = jcr[:float_min].to_s.to_f
      return bad_value( jcr, rule_atom, min, data ) unless data >= min
      max = jcr[:float_max].to_s.to_f
      return bad_value( jcr, rule_atom, max, data ) unless data <= max
    when jcr[:double_v]
      sf = jcr[:double_v].to_s
      if sf == "double"
        return bad_value( jcr, rule_atom, "double", data ) unless data.is_a?( Float )
      end

    #
    # boolean
    #

    when jcr[:true_v]
      return bad_value( jcr, rule_atom, "true", data ) unless data
    when jcr[:false_v]
      return bad_value( jcr, rule_atom, "false", data ) if data
    when jcr[:boolean_v]
      return bad_value( jcr, rule_atom, "boolean", data ) unless ( data.is_a?( TrueClass ) || data.is_a?( FalseClass ) )

    #
    # strings
    #

    when jcr[:string]
      return bad_value( jcr, rule_atom, "string", data ) unless data.is_a? String
    when jcr[:q_string]
      s = jcr[:q_string].to_s
      return bad_value( jcr, rule_atom, s, data ) unless data == s

    #
    # regex
    #

    when jcr[:regex]
      regex = Regexp.new( jcr[:regex].to_s )
      return bad_value( jcr, rule_atom, regex, data ) unless data.is_a? String
      return bad_value( jcr, rule_atom, regex, data ) unless data =~ regex

    #
    # ip addresses
    #

    when jcr[:ipv4]
      return bad_value( jcr, rule_atom, "IPv4 Address", data ) unless data.is_a? String
      begin
        ip = IPAddr.new( data )
      rescue IPAddr::InvalidAddressError
        return bad_value( jcr, rule_atom, "IPv4 Address", data )
      end
      return bad_value( jcr, rule_atom, "IPv4 Address", data ) unless ip.ipv4?
    when jcr[:ipv6]
      return bad_value( jcr, rule_atom, "IPv6 Address", data ) unless data.is_a? String
      begin
        ip = IPAddr.new( data )
      rescue IPAddr::InvalidAddressError
        return bad_value( jcr, rule_atom, "IPv6 Address", data )
      end
      return bad_value( jcr, rule_atom, "IPv6 Address", data ) unless ip.ipv6?
    when jcr[:ipaddr]
      return bad_value( jcr, rule_atom, "IP Address", data ) unless data.is_a? String
      begin
        ip = IPAddr.new( data )
      rescue IPAddr::InvalidAddressError
        return bad_value( jcr, rule_atom, "IP Address", data )
      end
      return bad_value( jcr, rule_atom, "IP Address", data ) unless ip.ipv6? || ip.ipv4?

    #
    # domain names
    #

    when jcr[:fqdn]
      return bad_value( jcr, rule_atom, "Fully Qualified Domain Name", data ) unless data.is_a? String
      return bad_value( jcr, rule_atom, "Fully Qualified Domain Name", data ) if data.empty?
      a = data.split( '.' )
      a.each do |label|
        return bad_value( jcr, rule_atom, "Fully Qualified Domain Name", data ) if label.start_with?( '-' )
        return bad_value( jcr, rule_atom, "Fully Qualified Domain Name", data ) if label.end_with?( '-' )
        label.each_char do |char|
          unless (char >= 'a' && char <= 'z') \
            || (char >= 'A' && char <= 'Z') \
            || (char >= '0' && char <='9') \
            || char == '-'
            return bad_value( jcr, rule_atom, "Fully Qualified Domain Name", data )
          end
        end
      end
    when jcr[:idn]
      return bad_value( jcr, rule_atom, "Internationalized Domain Name", data ) unless data.is_a? String
      return bad_value( jcr, rule_atom, "Internationalized Domain Name", data ) if data.empty?
      a = data.split( '.' )
      a.each do |label|
        return bad_value( jcr, rule_atom, "Internationalized Domain Name", data ) if label.start_with?( '-' )
        return bad_value( jcr, rule_atom, "Internationalized Domain Name", data ) if label.end_with?( '-' )
        label.each_char do |char|
          unless (char >= 'a' && char <= 'z') \
            || (char >= 'A' && char <= 'Z') \
            || (char >= '0' && char <='9') \
            || char == '-' \
            || char.ord > 127
            return bad_value( jcr, rule_atom, "Internationalized Domain Name", data )
          end
        end
      end

    #
    # uri and uri scheme
    #

    when jcr[:uri]
      if jcr[:uri].is_a? Hash
        t = jcr[:uri][:uri_scheme].to_s
        return bad_value( jcr, rule_atom, t, data ) unless data.is_a? String
        return bad_value( jcr, rule_atom, t, data ) unless data.start_with?( t )
      else
        return bad_value( jcr, rule_atom, "URI", data ) unless data.is_a?( String )
        uri = Addressable::URI.parse( data )
        return bad_value( jcr, rule_atom, "URI", data ) unless uri.is_a?( Addressable::URI )
      end

    #
    # phone and email value rules
    #

    when jcr[:email]
      return bad_value( jcr, rule_atom, "Email Address", data ) unless data.is_a? String
      return bad_value( jcr, rule_atom, "Email Address", data ) unless EmailAddressValidator.validate( data, true )

    when jcr[:phone]
      return bad_value( jcr, rule_atom, "Phone Number", data ) unless data.is_a? String
      p = BigPhoney::PhoneNumber.new( data )
      return bad_value( jcr, rule_atom, "Phone Number", data ) unless p.valid?

    #
    # hex values
    #

    when jcr[:hex]
      return bad_value( jcr, rule_atom, "Hex Data", data ) unless data.is_a? String
      return bad_value( jcr, rule_atom, "Hex Data", data ) unless data.length % 2 == 0
      pad_start = false
      data.each_char do |char|
        unless (char >= '0' && char <='9') \
            || (char >= 'A' && char <= 'F') \
            || (char >= 'a' && char <= 'f')
          return bad_value( jcr, rule_atom, "Hex Data", data )
        end
      end

    #
    # base32hex values
    #

    when jcr[:base32hex]
      return bad_value( jcr, rule_atom, "Base32hex Data", data ) unless data.is_a? String
      return bad_value( jcr, rule_atom, "Base32hex Data", data ) unless data.length % 8 == 0
      pad_start = false
      data.each_char do |char|
        if char == '='
          pad_start = true
        elsif pad_start && char != '='
          return bad_value( jcr, rule_atom, "Base32hex Data", data )
        else 
            unless (char >= '0' && char <='9') \
                || (char >= 'A' && char <= 'V') \
                || (char >= 'a' && char <= 'v')
              return bad_value( jcr, rule_atom, "Base32hex Data", data )
            end
        end
      end

    #
    # base32 values
    #

    when jcr[:base32]
      return bad_value( jcr, rule_atom, "Base 32 Data", data ) unless data.is_a? String
      return bad_value( jcr, rule_atom, "Base 32 Data", data ) unless data.length % 8 == 0
      pad_start = false
      data.each_char do |char|
        if char == '='
          pad_start = true
        elsif pad_start && char != '='
          return bad_value( jcr, rule_atom, "Base 32 Data", data )
        else 
            unless (char >= 'a' && char <= 'z') \
                || (char >= 'A' && char <= 'Z') \
                || (char >= '2' && char <='7')
              return bad_value( jcr, rule_atom, "Base 32 Data", data )
            end
        end
      end

    #
    # base64url values
    #

    when jcr[:base64url]
      return bad_value( jcr, rule_atom, "Base64url Data", data ) unless data.is_a? String
      return bad_value( jcr, rule_atom, "Base64url Data", data ) unless data.length % 4 == 0
      pad_start = false
      data.each_char do |char|
        if char == '='
          pad_start = true
        elsif pad_start && char != '='
          return bad_value( jcr, rule_atom, "Base64url Data", data )
        else 
            unless (char >= 'a' && char <= 'z') \
                || (char >= 'A' && char <= 'Z') \
                || (char >= '0' && char <='9') \
                || char == '-' || char == '_'
              return bad_value( jcr, rule_atom, "Base64url Data", data )
            end
        end
      end

    #
    # base64 values
    #

    when jcr[:base64]
      return bad_value( jcr, rule_atom, "Base 64 Data", data ) unless data.is_a? String
      return bad_value( jcr, rule_atom, "Base 64 Data", data ) unless data.length % 4 == 0
      pad_start = false
      data.each_char do |char|
        if char == '='
          pad_start = true
        elsif pad_start && char != '='
          return bad_value( jcr, rule_atom, "Base 64 Data", data )
        else 
            unless (char >= 'a' && char <= 'z') \
                || (char >= 'A' && char <= 'Z') \
                || (char >= '0' && char <='9') \
                || char == '+' || char == '/'
              return bad_value( jcr, rule_atom, "Base 64 Data", data )
            end
        end
      end

    #
    # time and date values
    #

    when jcr[:datetime]
      return bad_value( jcr, rule_atom, "Time and Date", data ) unless data.is_a? String
      begin
        Time.iso8601( data )
      rescue ArgumentError
        return bad_value( jcr, rule_atom, "Time and Date", data )
      end
    when jcr[:date]
      return bad_value( jcr, rule_atom, "Date", data ) unless data.is_a? String
      begin
        d = data + "T23:20:50.52Z"
        Time.iso8601( d )
      rescue ArgumentError
        return bad_value( jcr, rule_atom, "Date", data )
      end
    when jcr[:time]
      return bad_value( jcr, rule_atom, "Time", data ) unless data.is_a? String
      begin
        t = "1985-04-12T" + data + "Z"
        Time.iso8601( t )
      rescue ArgumentError
        return bad_value( jcr, rule_atom, "Time", data )
      end

    #
    # null
    #

    when jcr[:null]
      return bad_value( jcr, rule_atom, nil, data ) unless data == nil

    #
    # groups
    #

    when jcr[:group_rule]
      return evaluate_group_rule jcr[:group_rule], rule_atom, data, econs

    else
      raise "unknown value rule evaluation. this shouldn't happen"
  end
  return Evaluation.new( true, nil )
end

.failure_report(ctx) ⇒ Object



141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
# File 'lib/jcr/jcr.rb', line 141

def self.failure_report ctx
  report = []
  ctx.failed_roots.each do |failed_root|
    if failed_root.name
      report << "- ** Failures for root rule named '#{failed_root.name}'"
    else
      report << "- ** Failures for root rule at line #{failed_root.pos[0]}"
    end
    failed_root.failures.sort.map do |stack_level, failures|
      if failures.length > 1
        report << "  - failure at rule depth #{stack_level} caused by one of the following #{failures.length} reasons"
      else
        report << "  - failure at rule depth #{stack_level} caused by"
      end
      failures.each_with_index do |failure, index|
        report << "    - #{failure.json_elided} failed rule #{failure.definition} at #{failure.pos} because #{failure.reason_elided}"
      end
    end
  end
  return report
end

.find_first_slice(slice) ⇒ Object



378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
# File 'lib/jcr/evaluate_rules.rb', line 378

def self.find_first_slice slice
  if slice.is_a? Parslet::Slice
    return slice
  elsif slice.is_a?( Hash ) && !slice.empty?
    s = nil
    slice.values.each do |v|
      s = find_first_slice( v )
      break if s
    end
    return s if s
  elsif slice.is_a?( Array ) && !slice.empty?
    s = nil
    slice.each do |i|
      s = find_first_slice( i )
      break if s
    end
    return s if s
  end
  #else
  return nil
end

.find_roots(tree) ⇒ Object



37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# File 'lib/jcr/find_roots.rb', line 37

def self.find_roots( tree )
  roots = Array.new
  if tree.is_a? Hash
    tree = [ tree ]
  end
  tree.each do |node|
    if node[:rule]
      roots.concat( find_roots_in_named( node ) )
    elsif (top_rule = get_rule_by_type( node ))
      roots << Root.new( node, nil, true, true )
      roots.concat( find_roots_in_unnamed( top_rule ) )
    end
  end
  return roots
end

.find_roots_in_named(node) ⇒ Object



53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/jcr/find_roots.rb', line 53

def self.find_roots_in_named( node )
  roots = Array.new
  rn = node[:rule][:rule_name].to_str
  rule = node[:rule]
  ruledef = get_rule_by_type( rule )
  new_root = nil
  # look to see if the root_annotation is in the name before assignment ( @{root} $r = ... )
  if rule[:annotations]
    if rule[:annotations].is_a? Array
      rule[:annotations].each do |annotation|
        if annotation[:root_annotation]
          new_root = Root.new(node, rn)
          roots << new_root
          # root is found, now look into subrule for unnamed roots
          subrule = get_rule_by_type( ruledef )
          roots.concat( find_roots_in_unnamed( subrule ) ) if subrule
        end
      end
    elsif rule[:annotations][:root_annotation]
      new_root = Root.new(node, rn)
      roots << new_root
      # root is found, now look into subrule for unnamed roots
      subrule = get_rule_by_type( ruledef )
      roots.concat( find_roots_in_unnamed( subrule ) ) if subrule
    end
  end
  if ruledef && !new_root
    if ruledef.is_a? Array
      ruledef.each do |rdi|
        # if it has a @{root} annotation in the rule definition
        if rdi[:root_annotation]
          roots << Root.new(node, rn)
          # else look into the definition further and examine subrules
        elsif (subrule = get_rule_by_type(rdi))
          roots.concat(find_roots_in_unnamed(subrule))
        end
      end
    elsif ruledef.is_a? Hash
      subrule = get_rule_by_type(ruledef)
      roots.concat(find_roots_in_unnamed(subrule)) if subrule
    end
  end
  return roots
end

.find_roots_in_unnamed(node) ⇒ Object



98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
# File 'lib/jcr/find_roots.rb', line 98

def self.find_roots_in_unnamed( node )
  roots = Array.new
  if node.is_a? Array
    node.each do |n|
      if n[:root_annotation]
        roots << Root.new( node )
      elsif (subrule = get_rule_by_type( n ) )
        roots.concat( find_roots_in_unnamed( subrule ) ) if subrule
      end
    end
  else
    subrule = get_rule_by_type( node )
    roots.concat( find_roots_in_unnamed( subrule ) ) if subrule
  end
  return roots
end

.get_group(rule, econs) ⇒ Object



264
265
266
267
268
269
270
271
272
273
274
275
# File 'lib/jcr/evaluate_rules.rb', line 264

def self.get_group rule, econs
  return rule[:group_rule] if rule[:group_rule]
  #else
  if rule[:target_rule_name]
    target = econs.mapping[ rule[:target_rule_name][:rule_name].to_s ]
    raise "Target rule not in mapping. This should have been checked earlier." unless target
    trace( econs, "Referencing target rule #{slice_to_s(target)} from #{slice_to_s( rule[:target_rule_name][:rule_name] )}" )
    return get_group( target, econs )
  end
  #else
  return false
end

.get_leaf_rule(rule, econs) ⇒ Object



277
278
279
280
281
282
283
284
285
286
# File 'lib/jcr/evaluate_rules.rb', line 277

def self.get_leaf_rule rule, econs
  if rule[:target_rule_name ]
    target = econs.mapping[ rule[:target_rule_name][:rule_name].to_s ]
    raise "Target rule not in mapping. This should have been checked earlier." unless target
    trace( econs, "Referencing target rule #{slice_to_s(target)} from #{slice_to_s( rule[:target_rule_name][:rule_name] )}" )
    return target
  end
  #else
  return rule
end

.get_name_mapping(rule_name, mapping) ⇒ Object



70
71
72
73
74
# File 'lib/jcr/map_rule_names.rb', line 70

def self.get_name_mapping rule_name, mapping
  trule = mapping[ rule_name.to_str ]
  raise_rule_name_missing( rule_name ) unless trule
  return trule
end

.get_repetitions(rule, econs) ⇒ Object



156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
# File 'lib/jcr/evaluate_rules.rb', line 156

def self.get_repetitions rule, econs

  repeat_min = 1
  repeat_max = 1
  repeat_step = nil
  if rule[:optional]
    repeat_min = 0
    repeat_max = 1
  elsif rule[:one_or_more]
    repeat_min = 1
    repeat_max = Float::INFINITY
    if rule[:repetition_step]
      repeat_step = rule[:repetition_step].to_s.to_i
      repeat_min = repeat_step
    end
  elsif rule[:zero_or_more]
    repeat_min = 0
    repeat_max = Float::INFINITY
    repeat_step = rule[:repetition_step].to_s.to_i if rule[:repetition_step]
  elsif rule[:specific_repetition] && rule[:specific_repetition].is_a?( Parslet::Slice )
    repeat_min = repeat_max = rule[:specific_repetition].to_s.to_i
  else
    o = rule[:repetition_interval]
    if o
      repeat_min = 0
      repeat_max = Float::INFINITY
    end
    o = rule[:repetition_min]
    if o
      if o.is_a?( Parslet::Slice )
        repeat_min = o.to_s.to_i
      end
    end
    o = rule[:repetition_max]
    if o
      if o.is_a?( Parslet::Slice )
        repeat_max = o.to_s.to_i
      end
    end
    o = rule[:repetition_step]
    if o
      if o.is_a?( Parslet::Slice )
        repeat_step = o.to_s.to_i
      end
    end
  end

  trace( econs, "rule repetition min = #{repeat_min} max = #{repeat_max} repetition step = #{repeat_step}" )
  return repeat_min, repeat_max, repeat_step
end

.get_rule_by_type(rule) ⇒ Object



115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
# File 'lib/jcr/find_roots.rb', line 115

def self.get_rule_by_type rule
  retval = nil
  return retval unless rule.is_a? Hash
  case
    when rule[:array_rule]
      retval = rule[:array_rule]
    when rule[:object_rule]
      retval = rule[:object_rule]
    when rule[:member_rule]
      retval = rule[:member_rule]
    when rule[:primitive_rule]
      retval = rule[:primitive_rule]
    when rule[:group_rule]
      retval = rule[:group_rule]
  end
  return retval
end

.get_rules_and_annotations(jcr) ⇒ Object



207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
# File 'lib/jcr/evaluate_rules.rb', line 207

def self.get_rules_and_annotations jcr
  rules = []
  annotations = []

  if jcr.is_a?( Hash )
    jcr = [ jcr ]
  end

  if jcr.is_a? Array
    i = 0
    jcr.each do |sub|
      case
        when sub[:unordered_annotation]
          annotations << sub
          i = i + 1
        when sub[:not_annotation]
          annotations << sub
          i = i + 1
        when sub[:root_annotation]
          annotations << sub
          i = i + 1
        when sub[:primitive_rule],sub[:object_rule],sub[:group_rule],sub[:array_rule],sub[:target_rule_name]
          break
      end
    end
    rules = jcr[i,jcr.length]
  end

  return rules, annotations
end

.group_to_s(jcr, shallow = true) ⇒ Object



59
60
61
62
# File 'lib/jcr/evaluate_group_rules.rb', line 59

def self.group_to_s( jcr, shallow=true)
  rules, annotations = get_rules_and_annotations( jcr )
  return "#{annotations_to_s( annotations)}( #{rules_to_s(rules,shallow)} )"
end

.ingest_ruleset(ruleset, override = false, ruleset_alias = nil) ⇒ Object



97
98
99
100
101
102
103
104
105
106
107
108
109
110
# File 'lib/jcr/jcr.rb', line 97

def self.ingest_ruleset( ruleset, override = false, ruleset_alias=nil )
  tree = JCR.parse( ruleset )
  mapping = JCR.map_rule_names( tree, override, ruleset_alias )
  JCR.check_rule_target_names( tree, mapping )
  JCR.check_groups( tree, mapping )
  roots = JCR.find_roots( tree )
  ctx = Context.new
  ctx.tree = tree
  ctx.mapping = mapping
  ctx.callbacks = {}
  ctx.roots = roots
  JCR.process_directives( ctx )
  return ctx
end

.main(my_argv = nil) ⇒ Object



163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
# File 'lib/jcr/jcr.rb', line 163

def self.main my_argv=nil

  my_argv = ARGV unless my_argv

  options = {}

  opt_parser = OptionParser.new do |opt|
    opt.banner = "Usage: jcr [OPTIONS] [JSON_FILES]"
    opt.separator  ""
    opt.separator  "Evaluates JSON against JSON Content Rules (JCR)."
    opt.separator  ""
    opt.separator  "If -J is not specified, JSON_FILES is used."
    opt.separator  "If JSON_FILES is not specified, standard input (STDIN) is used."
    opt.separator  ""
    opt.separator  "Use -v to see results, otherwise check the exit code."
    opt.separator  ""
    opt.separator  "Options"

    opt.on("-r FILE","file containing ruleset") do |ruleset|
      if options[:ruleset]
        puts "A ruleset has already been specified. Use -h for help.", ""
        return 2
      end
      options[:ruleset] = File.open( ruleset ).read
    end

    opt.on("-R STRING","string containing ruleset. Should probably be quoted") do |ruleset|
      if options[:ruleset]
        puts "A ruleset has already been specified. Use -h for help.", ""
        return 2
      end
      options[:ruleset] = ruleset
    end

    opt.on("--test-jcr", "parse and test the JCR only") do |testjcr|
      options[:testjcr] = true
    end

    opt.on("-S STRING","name of root rule. All roots will be tried if none is specified") do |root_name|
      if options[:root_name]
        puts "A root has already been specified. Use -h for help.", ""
        return 2
      end
      options[:root_name] = root_name
    end

    opt.on("-o FILE","file containing overide ruleset (option can be repeated)") do |ruleset|
      unless options[:overrides]
        options[:overrides] = Array.new
      end
      options[:overrides] << File.open( ruleset ).read
    end

    opt.on("-O STRING","string containing overide rule (option can be repeated)") do |rule|
      unless options[:overrides]
        options[:overrides] = Array.new
      end
      options[:overrides] << rule
    end

    opt.on("-J STRING","string containing JSON to evaluate. Should probably be quoted") do |json|
      if options[:json]
        puts "JSON has already been specified. Use -h for help.", ""
        return 2
      end
      options[:json] = json
    end

    opt.on("-v","verbose") do |verbose|
      options[:verbose] = true
    end

    opt.on("-q","quiet") do |quiet|
      options[:quiet] = true
    end

    opt.on("-h","display help") do |help|
      options[:help] = true
    end

    opt.separator  ""
    opt.separator  "Return codes:"
    opt.separator  " 0 = success"
    opt.separator  " 1 = parsing or other bad condition"
    opt.separator  " 2 = fall through bad condition"
    opt.separator  " 3 = unsuccessful evaluation of JSON"

    opt.separator  ""
    opt.separator  "JCR Version " + JCR::VERSION
  end

  opt_parser.parse! my_argv

  if options[:help]
    puts "HELP","----",""
    puts opt_parser
    return 2
  elsif !options[:ruleset]
    puts "No ruleset passed! Use -R or -r options.", ""
    puts opt_parser
    return 2
  else

    begin

      ctx = Context.new( options[:ruleset], options[:verbose] )
      if options[:overrides]
        options[:overrides].each do |ov|
          ctx.override!( ov )
        end
      end

      if options[:verbose]
        pp "Ruleset Parse Tree", ctx.tree
        puts "Ruleset Map"
        ctx.mapping.each do |name,rule|
          puts "Parsed Rule: #{name}"
          puts rule_to_s( rule, false )
          puts "Parsed Rule Structure: #{name}"
          pp rule
        end
      end

      if options[:testjcr]
        #we got this far which means the JCR was already parsed without
        #issue. therefore return 0
        return 0
      elsif options[:json]
        data = JSON.parse( options[:json] )
        ec = cli_eval( ctx, data, options[:root_name], options[:quiet] )
        return ec
      elsif $stdin.tty?
        ec = 0
        if my_argv.empty?
          ec = 2
        else
          my_argv.each do |fn|
            data = JSON.parse( File.open( fn ).read )
            tec = cli_eval( ctx, data, options[:root_name], options[:quiet] )
            ec = tec if tec != 0 #record error but don't let non-error overwrite error
          end
        end
        return ec
      else
        lines = ""
        ec = 0
        ARGF.each do |line|
          lines = lines + line
          if ARGF.eof?
            data = JSON.parse( lines )
            tec = cli_eval( ctx, data, options[:root_name], options[:quiet] )
            ec = tec if tec != 0 #record error but don't let non-error overwrite error
            lines = ""
          end
        end
        return ec
      end

    rescue Parslet::ParseFailed => failure
      puts failure.parse_failure_cause.ascii_tree unless options[:quiet]
      return 1
    rescue JSON::ParserError => parser_error
      unless options[:quiet]
        puts "Unable to parse JSON"
        puts parser_error.message.inspect
      end
      return 3
    end

  end

end

.map_rule_names(tree, override = false, ruleset_alias = nil) ⇒ Object



19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# File 'lib/jcr/map_rule_names.rb', line 19

def self.map_rule_names( tree, override = false, ruleset_alias = nil )
  prefix = ""
  if ruleset_alias
    prefix = ruleset_alias
    unless prefix.end_with?( "." )
      prefix = prefix + "."
    end
  end
  rule_name_maping = Hash.new
  if tree.is_a? Hash
    tree = [ tree ]
  end
  tree.each do |node|
    if node[:rule]
      rn = prefix + node[:rule][:rule_name].to_str
      if rule_name_maping[ rn ] && !override
        raise "Rule #{rn} already exists and is defined more than once"
      else
        rule_name_maping[ rn ] = node[:rule]
      end
    end
  end
  return rule_name_maping
end

.member_to_s(jcr, shallow = true) ⇒ Object



81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
# File 'lib/jcr/evaluate_member_rules.rb', line 81

def self.member_to_s( jcr, shallow=true )
  rules, annotations = get_rules_and_annotations( jcr )
  retval = ""
  rule = merge_rules( rules )
  case
    when rule[:member_name]
      retval = %Q|"#{rule[:member_name][:q_string].to_s}"|
    when rule[:member_regex]
      retval = "/#{rule[:member_regex][:regex].to_s}/"
    else
      retval = "** unknown member rule **"
  end
  retval = retval + " : " + rule_to_s( rule, shallow )
  return annotations_to_s( annotations ) + retval
end

.merge_rules(rules) ⇒ Object



238
239
240
241
242
243
244
245
246
# File 'lib/jcr/evaluate_rules.rb', line 238

def self.merge_rules rules
  new_rule = Hash.new
  rules.each do |rule|
    new_rule.merge!(rule) do |key,oldval,newval|
      raise "error: conflict in merge of #{rule} with #{new_rule}"
    end
  end
  return new_rule
end

.object_to_s(jcr, shallow = true) ⇒ Object



175
176
177
178
# File 'lib/jcr/evaluate_object_rules.rb', line 175

def self.object_to_s( jcr, shallow=true )
  rules, annotations = get_rules_and_annotations( jcr )
  return "#{annotations_to_s( annotations)}{ #{rules_to_s(rules,shallow)} }"
end

.parse(str) ⇒ Object



462
463
464
465
466
467
# File 'lib/jcr/parser.rb', line 462

def self.parse(str)

  parser = Parser.new
  parser.parse(str)

end

.parse_and_transform(str) ⇒ Object



469
470
471
472
473
474
475
476
477
478
479
# File 'lib/jcr/parser.rb', line 469

def self.parse_and_transform(str)
  # provided for the fun of it

  parser = Parser.new
  tree = parser.parse(str)
  pp tree

  transformer = Transformer.new
  transformer.apply( tree )

end

.pop_trace_stack(econs) ⇒ Object



292
293
294
# File 'lib/jcr/evaluate_rules.rb', line 292

def self.pop_trace_stack econs
  econs.trace_stack.pop
end


481
482
483
484
485
486
487
# File 'lib/jcr/parser.rb', line 481

def self.print_tree( tree )

  tree.each do |node|
    puts "named rule: " + node[:rule][:rule_name] if node[:rule]
  end

end

.process_directives(ctx) ⇒ Object



25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# File 'lib/jcr/process_directives.rb', line 25

def self.process_directives( ctx )

  tree = ctx.tree
  if tree.is_a? Hash
    tree = [ tree ]
  end

  tree.each do |node|
    if node[:directive]
      d = node[:directive]
      case
        when d[:ruleset_id_d]
          process_ruleset_id( d[:ruleset_id_d], ctx )
        when d[:import_d]
          process_import( d[:import_d], ctx )
        when d[:jcr_version_d]
          process_jcrversion( d[:jcr_version_d], ctx )
      end
    end
  end
end

.process_import(directive, ctx) ⇒ Object



62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
# File 'lib/jcr/process_directives.rb', line 62

def self.process_import( directive, ctx )

  ruleset_id    = directive[:ruleset_id].to_str
  ruleset_alias = directive[:ruleset_id_alias].to_str
  u = ctx.map_ruleset_alias( ruleset_alias, ruleset_id )
  uri = URI.parse( u )
  ruleset = nil
  case uri.scheme
    when "http","https"
      response = Net::HTTP.get_response uri
      ruleset = response.body
    else
      ruleset = File.open( uri.path )
  end

  import_ctx = JCR.ingest_ruleset( ruleset, false, ruleset_alias )
  ctx.mapping.merge!( import_ctx.mapping )
  ctx.roots.concat( import_ctx.roots )

end

.process_jcrversion(directive, ctx) ⇒ Object



51
52
53
54
55
56
57
58
59
60
# File 'lib/jcr/process_directives.rb', line 51

def self.process_jcrversion( directive, ctx )
  major = directive[:major_version].to_str.to_i
  minor = directive[:minor_version].to_str.to_i
  if major != 0
    raise "jcr version #{major}.#{minor} is incompatible with 0.7"
  end
  if minor != 7
    raise "jcr version #{major}.#{minor} is incompatible with 0.7"
  end
end

.process_ruleset_id(directive, ctx) ⇒ Object



47
48
49
# File 'lib/jcr/process_directives.rb', line 47

def self.process_ruleset_id( directive, ctx )
  ctx.id = directive[:ruleset_id].to_str
end

.push_trace_stack(econs, jcr) ⇒ Object



288
289
290
# File 'lib/jcr/evaluate_rules.rb', line 288

def self.push_trace_stack econs, jcr
  econs.trace_stack.push( find_first_slice( jcr ) )
end

.raise_group_error(str, node) ⇒ Object



187
188
189
190
191
192
193
194
195
# File 'lib/jcr/check_groups.rb', line 187

def self.raise_group_error str, node
  if node.is_a?( Parslet::Slice )
    pos = node.line_and_column
    name = node.to_str
    raise "group rule error at line " + pos[0].to_s + " column " + pos[1].to_s + " name '" + name + "' :" + str
  else
    raise "group rule error with '" + node.to_s + "' :" + str
  end
end

.raise_rule_name_missing(rule_name) ⇒ Object



76
77
78
79
80
# File 'lib/jcr/map_rule_names.rb', line 76

def self.raise_rule_name_missing rule_name
  pos = rule_name.line_and_column
  name = rule_name.to_str
  raise "rule '" + name + "' at line " + pos[0].to_s + " column " + pos[1].to_s + " does not exist"
end

.raised_rule(jcr, rule_atom) ⇒ Object



411
412
413
# File 'lib/jcr/evaluate_rules.rb', line 411

def self.raised_rule jcr, rule_atom
  " rule at #{slice_to_s(jcr)} [ #{jcr} ] from rule at #{slice_to_s(rule_atom)}"
end

.repetitions_to_s(rule) ⇒ Object



490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
# File 'lib/jcr/evaluate_rules.rb', line 490

def self.repetitions_to_s rule
  retval = ""
  if rule[:optional]
    retval = "?"
  elsif rule[:one_or_more]
    retval = "+"
    if rule[:repetition_step]
      retval = "%" + rule[:repetition_step].to_s
    end
  elsif rule[:zero_or_more]
    retval = "*"
    retval = retval + "%" + rule[:repetition_step].to_s if rule[:repetition_step]
  elsif rule[:specific_repetition] && rule[:specific_repetition].is_a?( Parslet::Slice )
    retval = "*" + rule[:specific_repetition].to_s
  else
    if rule[:repetition_interval]
      min = "0"
      max = "INF"
      o = rule[:repetition_min]
      if o
        if o.is_a?(Parslet::Slice)
          min = o.to_s
        end
      end
      o = rule[:repetition_max]
      if o
        if o.is_a?(Parslet::Slice)
          max = o.to_s
        end
      end
      retval = "*"+min+".."+max
    end
    o = rule[:repetition_step]
    if o
      if o.is_a?( Parslet::Slice )
        retval = retval + "%" + o.to_s
      end
    end
  end
  retval = " " + retval if retval.length != 0
  return retval
end

.rule_data(data = nil) ⇒ Object



304
305
306
307
308
309
310
311
312
313
314
315
# File 'lib/jcr/evaluate_rules.rb', line 304

def self.rule_data data=nil
  if data
    if data.is_a? String
      s = '"' + data + '"'
    else
      s = data.pretty_print_inspect
    end
    return elide(s)
  end
  #else
  return nil
end

.rule_def(type, jcr) ⇒ Object



328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
# File 'lib/jcr/evaluate_rules.rb', line 328

def self.rule_def type, jcr
  s = ""
  case type
    when "value"
      s = elide(value_to_s(jcr))
    when "member"
      s = elide(member_to_s(jcr))
    when "object"
      s = elide(object_to_s(jcr))
    when "array"
      s = elide(array_to_s(jcr))
    when "group"
      s = elide(group_to_s(jcr))
    else
      s = "** unknown rule **"
  end
  return "#{type} definition: #{s}"
end

.rule_to_s(rule, shallow = true) ⇒ Object



415
416
417
418
419
420
421
422
423
424
425
426
# File 'lib/jcr/evaluate_rules.rb', line 415

def self.rule_to_s( rule, shallow=true)
  if rule[:rule_name]
    if rule[:primitive_rule]
      retval = "$#{rule[:rule_name].to_s} =: #{ruletype_to_s( rule, shallow )}"
    else
      retval = "$#{rule[:rule_name].to_s} = #{ruletype_to_s( rule, shallow )}"
    end
  else
    retval = ruletype_to_s( rule, shallow )
  end
  return retval
end

.rules_to_s(rules, shallow = true) ⇒ Object



453
454
455
456
457
458
459
460
461
462
463
464
465
466
# File 'lib/jcr/evaluate_rules.rb', line 453

def self.rules_to_s( rules, shallow=true)
  retval = ""
  rules.each do |rule|
    if rule[:rule_name]
      next
    elsif rule[:choice_combiner]
      retval = retval + " | "
    elsif rule[:sequence_combiner]
      retval = retval + " , "
    end
    retval = retval + rule_to_s( rule, shallow ) + repetitions_to_s( rule )
  end
  return retval
end

.ruletype_to_s(rule, shallow = true) ⇒ Object



428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
# File 'lib/jcr/evaluate_rules.rb', line 428

def self.ruletype_to_s( rule, shallow=true )

  if rule[:primitive_rule]
    retval = value_to_s( rule[:primitive_rule] )
  elsif rule[:member_rule]
    retval = member_to_s( rule[:member_rule], shallow )
  elsif rule[:object_rule]
    retval = object_to_s( rule[:object_rule], shallow )
  elsif rule[:array_rule]
    retval = array_to_s( rule[:array_rule], shallow )
  elsif rule[:group_rule]
    retval = group_to_s( rule[:group_rule], shallow )
  elsif rule[:target_rule_name]
    retval = target_to_s( rule[:target_rule_name] )
  elsif rule[:rule_name]
    retval = "rule: #{rule[:rule_name].to_s}"
  elsif rule[:rule]
    retval = rule_to_s( rule[:rule], shallow )
  else
    retval = "** unknown rule definition ** #{rule}"
  end
  return retval

end

.slice_to_s(slice) ⇒ Object



400
401
402
403
404
405
406
407
408
409
# File 'lib/jcr/evaluate_rules.rb', line 400

def self.slice_to_s slice
  s = find_first_slice( slice )
  if s.is_a? Parslet::Slice
    pos = s.line_and_column
    retval = "'#{s.inspect}' ( line #{pos[0]} column #{pos[1]} )"
  else
    retval = slice.to_s
  end
  retval
end

.target_to_s(jcr) ⇒ Object



486
487
488
# File 'lib/jcr/evaluate_rules.rb', line 486

def self.target_to_s( jcr )
  return annotations_to_s( jcr[:annotations] ) + "$" + jcr[:rule_name].to_s
end

.trace(econs, message, data = nil) ⇒ Object



317
318
319
320
321
322
323
324
325
326
# File 'lib/jcr/evaluate_rules.rb', line 317

def self.trace econs, message, data = nil
  if econs.trace
    if data
      message = "#{message} data: #{rule_data( data )}"
    end
    last = econs.trace_stack.last
    pos = "#{last.line_and_column}@#{last.offset}" if last
    puts "[ #{econs.trace_stack.length}:#{pos} ] #{message}"
  end
end

.trace_def(econs, type, jcr, data) ⇒ Object



347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
# File 'lib/jcr/evaluate_rules.rb', line 347

def self.trace_def econs, type, jcr, data
  if econs.trace
    s = ""
    case type
      when "value"
        s = elide( value_to_s( jcr ) )
      when "member"
        s = elide( member_to_s( jcr  ) )
      when "object"
        s = elide( object_to_s( jcr ) )
      when "array"
        s = elide( array_to_s( jcr ) )
      when "group"
        s = elide( group_to_s( jcr ) )
      else
        s = "** unknown rule **"
    end
    trace( econs, rule_def( type, jcr ) )
  end
end

.trace_eval(econs, message, evaluation, jcr, data, type) ⇒ Object



368
369
370
371
372
373
374
375
376
# File 'lib/jcr/evaluate_rules.rb', line 368

def self.trace_eval econs, message, evaluation, jcr, data, type
  if evaluation.success
    trace( econs, "#{message} evaluation is true" )
  else
    failure = Failure.new( data, jcr, type, evaluation, econs.trace_stack.length )
    econs.report_failure( failure )
    trace( econs, "#{message} evaluation failed: #{evaluation.reason}")
  end
end

.value_to_s(jcr, shallow = true) ⇒ Object



407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
# File 'lib/jcr/evaluate_value_rules.rb', line 407

def self.value_to_s( jcr, shallow=true )

  rules, annotations = get_rules_and_annotations( jcr )

  rule = rules[ 0 ]
  retval = ""
  case

    when rule[:any]
      retval =  "any"

    when rule[:integer_v]
      retval =  rule[:integer_v].to_s
    when rule[:integer]
      retval =  rule[:integer].to_s.to_i
    when rule[:integer_min],rule[:integer_max]
      min = "-INF"
      max = "INF"
      min = rule[:integer_min].to_s.to_i if rule[:integer_min]
      max = rule[:integer_max].to_s.to_i if rule[:integer_max]
      retval =  "#{min}..#{max}"
    when rule[:sized_int_v]
      retval =  "int" + rule[:sized_int_v][:bits].to_s
    when rule[:sized_uint_v]
      retval =  "uint" + rule[:sized_uint_v][:bits].to_s

    when rule[:double_v]
      retval =  rule[:double_v].to_s
    when rule[:float_v]
      retval =  rule[:float_v].to_s
    when rule[:float]
      retval =  rule[:float].to_s.to_f
    when rule[:float_min],rule[:float_max]
      min = "-INF"
      max = "INF"
      min = rule[:float_min].to_s.to_f if rule[:float_min]
      max = rule[:float_max].to_s.to_f if rule[:float_max]
      retval =  "#{min}..#{max}"

    when rule[:true_v]
      retval =  "true"
    when rule[:false_v]
      retval =  "false"
    when rule[:boolean_v]
      retval =  "boolean"

    when rule[:string]
      retval =  "string"
    when rule[:q_string]
      retval =  %Q|"#{rule[:q_string].to_s}"|

    when rule[:regex]
      retval =  "/#{rule[:regex].to_s}/"

    when rule[:ipv4]
      retval =  "ipv4"
    when rule[:ipv6]
      retval =  "ipv6"

    when rule[:fqdn]
      retval =  "fqdn"
    when rule[:idn]
      retval =  "idn"

    when rule[:uri]
      if rule[:uri].is_a? Hash
        retval =  "uri..#{rule[:uri][:uri_scheme].to_s}"
      else
        retval =  "uri"
      end

    when rule[:email]
      retval =  "email"

    when rule[:phone]
      retval =  "phone"

    when rule[:hex]
      retval =  "hex"
    when rule[:base32hex]
      retval =  "base32hex"
    when rule[:base64url]
      retval =  "base64url"
    when rule[:base64]
      retval =  "base64"

    when rule[:datetime]
      retval =  "datetime"
    when rule[:date]
      retval =  "date"
    when rule[:time]
      retval =  "time"

    when rule[:null]
      retval =  "null"

    when rule[:group_rule]
      retval =  group_to_s( rule[:group_rule], shallow )

    else
      retval =  "** unknown value rule **"
  end
  return annotations_to_s( annotations ) + retval.to_s
end