jcrvalidator 0.5.3 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: cc188a7903b23b3993edd4698a1236b3ae35d0d9
4
- data.tar.gz: 81a988438d5eab9632868480125122995aee3cb6
3
+ metadata.gz: 0e68e95dd86cf0ba170eae8a3ead3acb7c44ab59
4
+ data.tar.gz: fe3a8c25bf2e420c43db8c32f33e764f7356d766
5
5
  SHA512:
6
- metadata.gz: db91cb6666489b13f280b89fe852bf20fb4a0b0b284eeddbfe58b6d27f577677d6b84083eb172f17ca0a4604c4dbfc18f93e4e4c84476ed9ad49d6a0b5b94296
7
- data.tar.gz: 1f7edd537bb8a90788ea8b28bdb2119590e7a43cbc814eab3b80c75ab7fab008ab327f2eb388a83ce49d8f9b49d7b53e1c388aaea8a807ca1eb8a9a82c8f84f4
6
+ metadata.gz: 6d8a19a053ff78eeb76b2b46ee0c48be126465a80825737e87881cbdf4c9f7e4e1b9dae9c15b0768ddb27f091f4196e761f44ab2c65cc19a19a86288bbdb8d51
7
+ data.tar.gz: ba031e433185f9857056a62759caee2d7edf396cc6067d35e1bd4d3114c9dc9b27985f0bc0a299bd2128095739f0b48724afdda750e14dad575040cdb78a7a0e
@@ -27,8 +27,6 @@ module JCR
27
27
  check_groups( tree[:rule], mapping )
28
28
  elsif tree[:primitive_rule]
29
29
  check_value_for_group( tree[:primitive_rule], mapping )
30
- elsif tree[:type_choice_signifier]
31
- check_value_for_group( tree, mapping )
32
30
  elsif tree[:member_rule]
33
31
  check_member_for_group( tree[:member_rule], mapping )
34
32
  elsif tree[:array_rule]
@@ -40,7 +38,7 @@ module JCR
40
38
  end
41
39
 
42
40
  def self.check_value_for_group node, mapping
43
- if node[:group_rule]
41
+ if node.is_a?( Hash ) && node[:group_rule]
44
42
  disallowed_group_in_value?( node[:group_rule], mapping )
45
43
  end
46
44
  end
@@ -47,6 +47,18 @@ module JCR
47
47
 
48
48
  def self.evaluate_array_rule jcr, rule_atom, data, econs, behavior = nil
49
49
 
50
+ push_trace_stack( econs, jcr )
51
+ trace( econs, "Evaluating array rule starting at #{slice_to_s(jcr)} against", data )
52
+ trace_def( econs, "array", jcr, data )
53
+ retval = evaluate_array( jcr, rule_atom, data, econs, behavior )
54
+ trace_eval( econs, "Array", retval )
55
+ pop_trace_stack( econs )
56
+ return retval
57
+
58
+ end
59
+
60
+ def self.evaluate_array jcr, rule_atom, data, econs, behavior = nil
61
+
50
62
  rules, annotations = get_rules_and_annotations( jcr )
51
63
 
52
64
  ordered = true
@@ -63,21 +75,21 @@ module JCR
63
75
  end
64
76
 
65
77
  # if the data is not an array
66
- return evaluate_reject( annotations,
67
- Evaluation.new( false, "#{data} is not an array at #{jcr} from #{rule_atom}") ) unless data.is_a? Array
78
+ return evaluate_not( annotations,
79
+ Evaluation.new( false, "#{data} is not an array #{raised_rule(jcr,rule_atom)}"), econs ) unless data.is_a? Array
68
80
 
69
81
  # if the array is zero length and there are zero sub-rules (it is suppose to be empty)
70
- return evaluate_reject( annotations,
71
- Evaluation.new( true, nil ) ) if rules.empty? && data.empty?
82
+ return evaluate_not( annotations,
83
+ Evaluation.new( true, nil ), econs ) if rules.empty? && data.empty?
72
84
 
73
85
  # if the array is not empty and there are zero sub-rules (it is suppose to be empty)
74
- return evaluate_reject( annotations,
75
- Evaluation.new( false, "Non-empty array at #{jcr} from #{rule_atom}" ) ) if rules.empty? && data.length != 0
86
+ return evaluate_not( annotations,
87
+ Evaluation.new( false, "Non-empty array for #{raised_rule(jcr,rule_atom)}" ), econs ) if rules.empty? && data.length != 0
76
88
 
77
89
  if ordered
78
- return evaluate_reject( annotations, evaluate_array_rule_ordered( rules, rule_atom, data, econs, behavior ) )
90
+ return evaluate_not( annotations, evaluate_array_rule_ordered( rules, rule_atom, data, econs, behavior ), econs )
79
91
  else
80
- return evaluate_reject( annotations, evaluate_array_rule_unordered( rules, rule_atom, data, econs, behavior ) )
92
+ return evaluate_not( annotations, evaluate_array_rule_unordered( rules, rule_atom, data, econs, behavior ), econs )
81
93
  end
82
94
  end
83
95
 
@@ -97,7 +109,7 @@ module JCR
97
109
  break
98
110
  end
99
111
 
100
- repeat_min, repeat_max = get_repetitions( rule )
112
+ repeat_min, repeat_max, repeat_step = get_repetitions( rule, econs )
101
113
 
102
114
  # group rules must be evaluated differently
103
115
  # groups require the effects of the evaluation to be discarded if they are false
@@ -110,7 +122,7 @@ module JCR
110
122
  else
111
123
  for i in 1..repeat_min do
112
124
  if array_index == data.length
113
- return Evaluation.new( false, "array is not large enough for #{jcr} from #{rule_atom}" )
125
+ return Evaluation.new( false, "array is not large enough for #{raised_rule(jcr,rule_atom)}" )
114
126
  else
115
127
  group_behavior = ArrayBehavior.new( behavior )
116
128
  group_behavior.last_index = array_index
@@ -146,7 +158,7 @@ module JCR
146
158
  else
147
159
  for i in 1..repeat_min do
148
160
  if array_index == data.length
149
- return Evaluation.new( false, "array is not large enough for #{jcr} from #{rule_atom}" )
161
+ return Evaluation.new( false, "array is not large enough for #{raised_rule(jcr,rule_atom)}" )
150
162
  else
151
163
  retval = evaluate_rule( rule, rule_atom, data[ array_index ], econs, nil )
152
164
  break unless retval.success
@@ -166,12 +178,16 @@ module JCR
166
178
 
167
179
  end # end if grule else
168
180
 
181
+ if repeat_step && array_index % repeat_step != 0
182
+ retval = Evaluation.new( false, "Matches (#{array_index }) do not meat repetition step for #{repeat_max} % #{repeat_step}")
183
+ end
184
+
169
185
  end
170
186
 
171
187
  behavior.last_index = array_index
172
188
 
173
189
  if data.length > array_index && behavior.extra_prohibited
174
- retval = Evaluation.new( false, "More itmes in array than specified for #{jcr} from #{rule_atom}" )
190
+ retval = Evaluation.new( false, "More itmes in array than specified for #{raised_rule(jcr,rule_atom)}" )
175
191
  end
176
192
 
177
193
  return retval
@@ -196,7 +212,7 @@ module JCR
196
212
  break
197
213
  end
198
214
 
199
- repeat_min, repeat_max = get_repetitions( rule )
215
+ repeat_min, repeat_max, repeat_step = get_repetitions( rule, econs )
200
216
 
201
217
  # group rules must be evaluated differently
202
218
  # groups require the effects of the evaluation to be discarded if they are false
@@ -220,11 +236,13 @@ module JCR
220
236
  end
221
237
 
222
238
  if successes == 0 && repeat_min > 0
223
- retval = Evaluation.new( false, "array does not contain #{rule} for #{jcr} from #{rule_atom}")
239
+ retval = Evaluation.new( false, "array does not contain #{rule} for #{raised_rule(jcr,rule_atom)}")
224
240
  elsif successes < repeat_min
225
- retval = Evaluation.new( false, "array does not have enough #{rule} for #{jcr} from #{rule_atom}")
241
+ retval = Evaluation.new( false, "array does not have enough #{rule} for #{raised_rule(jcr,rule_atom)}")
226
242
  elsif successes > repeat_max
227
- retval = Evaluation.new( false, "array has too many #{rule} for #{jcr} from #{rule_atom}")
243
+ retval = Evaluation.new( false, "array has too many #{rule} for #{raised_rule(jcr,rule_atom)}")
244
+ elsif repeat_step && successes % repeat_step != 0
245
+ 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)}")
228
246
  else
229
247
  retval = Evaluation.new( true, nil )
230
248
  end
@@ -245,11 +263,13 @@ module JCR
245
263
  end
246
264
 
247
265
  if successes == 0 && repeat_min > 0
248
- retval = Evaluation.new( false, "array does not contain #{rule} for #{jcr} from #{rule_atom}")
266
+ retval = Evaluation.new( false, "array does not contain #{rule} for #{raised_rule(jcr,rule_atom)}")
249
267
  elsif successes < repeat_min
250
- retval = Evaluation.new( false, "array does not have enough #{rule} for #{jcr} from #{rule_atom}")
268
+ retval = Evaluation.new( false, "array does not have enough #{rule} for #{raised_rule(jcr,rule_atom)}")
251
269
  elsif successes > repeat_max
252
- retval = Evaluation.new( false, "array has too many #{rule} for #{jcr} from #{rule_atom}")
270
+ retval = Evaluation.new( false, "array has too many #{rule} for #{raised_rule(jcr,rule_atom)}")
271
+ elsif repeat_step && successes % repeat_step != 0
272
+ 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)}")
253
273
  else
254
274
  retval = Evaluation.new( true, nil)
255
275
  end
@@ -261,10 +281,14 @@ module JCR
261
281
  behavior.last_index = highest_index
262
282
 
263
283
  if data.length > behavior.checked_hash.length && behavior.extra_prohibited
264
- retval = Evaluation.new( false, "More itmes in array than specified for #{jcr} from #{rule_atom}" )
284
+ retval = Evaluation.new( false, "More itmes in array than specified for #{raised_rule(jcr,rule_atom)}" )
265
285
  end
266
286
 
267
287
  return retval
268
288
  end
269
289
 
290
+ def self.array_to_s( jcr, shallow=true )
291
+ rules, annotations = get_rules_and_annotations( jcr )
292
+ return "#{annotations_to_s( annotations)} [ #{rules_to_s( rules, shallow )} ]"
293
+ end
270
294
  end
@@ -22,25 +22,43 @@ require 'big-phoney'
22
22
  require 'jcr/parser'
23
23
  require 'jcr/map_rule_names'
24
24
  require 'jcr/check_groups'
25
+ require 'jcr/evaluate_rules'
25
26
 
26
27
  module JCR
27
28
 
28
29
  def self.evaluate_group_rule jcr, rule_atom, data, econs, behavior = nil
29
30
 
31
+ push_trace_stack( econs, jcr )
32
+ trace( econs, "Evaluating group rule against ", data )
33
+ trace_def( econs, "group", jcr, data )
34
+ retval = evaluate_group( jcr, rule_atom, data, econs, behavior )
35
+ trace_eval( econs, "Group", retval )
36
+ pop_trace_stack( econs )
37
+ return retval
38
+
39
+ end
40
+
41
+ def self.evaluate_group jcr, rule_atom, data, econs, behavior = nil
42
+
30
43
  rules, annotations = get_rules_and_annotations( jcr )
31
44
 
32
45
  retval = nil
33
46
 
34
47
  rules.each do |rule|
35
48
  if rule[:choice_combiner] && retval && retval.success
36
- return evaluate_reject( annotations, retval ) # short circuit
49
+ return evaluate_not( annotations, retval, econs ) # short circuit
37
50
  elsif rule[:sequence_combiner] && retval && !retval.success
38
- return evaluate_reject( annotations, retval ) # short circuit
51
+ return evaluate_not( annotations, retval, econs ) # short circuit
39
52
  end
40
53
  retval = evaluate_rule( rule, rule_atom, data, econs, behavior )
41
54
  end
42
55
 
43
- return evaluate_reject( annotations, retval )
56
+ return evaluate_not( annotations, retval, econs )
57
+ end
58
+
59
+ def self.group_to_s( jcr, shallow=true)
60
+ rules, annotations = get_rules_and_annotations( jcr )
61
+ return "#{annotations_to_s( annotations)} ( #{rules_to_s(rules,shallow)} )"
44
62
  end
45
63
 
46
64
  end
@@ -27,10 +27,23 @@ module JCR
27
27
 
28
28
  def self.evaluate_member_rule jcr, rule_atom, data, econs
29
29
 
30
+ push_trace_stack( econs, jcr )
31
+ trace( econs, "Evaluating member rule for key '#{data[0]}' starting at #{slice_to_s(jcr)} against ", data[1])
32
+ trace_def( econs, "member", jcr, data )
33
+ retval = evaluate_member( jcr, rule_atom, data, econs )
34
+ trace_eval( econs, "Member", retval )
35
+ pop_trace_stack( econs )
36
+ return retval
37
+
38
+ end
39
+
40
+ def self.evaluate_member jcr, rule_atom, data, econs
41
+
30
42
  # unlike the other evaluate functions, here data is not just the json data.
31
43
  # it is an array, the first element being the member name or regex and the
32
44
  # second being the json data to be furthered on to other evaluation functions
33
45
 
46
+
34
47
  rules, annotations = get_rules_and_annotations( jcr )
35
48
  rule = rules[0]
36
49
 
@@ -50,12 +63,28 @@ module JCR
50
63
 
51
64
  if member_match
52
65
  e = evaluate_rule( rule, rule_atom, data[ 1 ], econs )
53
- return evaluate_reject( annotations, e )
66
+ return evaluate_not( annotations, e, econs )
54
67
  end
55
68
 
56
- return evaluate_reject( annotations,
57
- Evaluation.new( false, "#{match_spec} does not match #{data[0]} for #{jcr} from #{rule_atom}" ) )
69
+ return evaluate_not( annotations,
70
+ Evaluation.new( false, "#{match_spec} does not match #{data[0]} for #{raised_rule( jcr, rule_atom)}" ), econs )
58
71
 
59
72
  end
60
73
 
74
+ def self.member_to_s( jcr, shallow=true )
75
+ rules, annotations = get_rules_and_annotations( jcr )
76
+ retval = ""
77
+ rule = rules[ 0 ]
78
+ case
79
+ when rule[:member_name]
80
+ retval = %Q|"#{rule[:member_name][:q_string].to_s}"|
81
+ when rule[:member_regex]
82
+ retval = "/#{rule[:member_regex][:regex].to_s}/"
83
+ else
84
+ retval = "** unknown member rule **"
85
+ end
86
+ retval = retval + " : " + rule_to_s( rule, shallow )
87
+ return annotations_to_s( annotations ) + retval
88
+ end
89
+
61
90
  end
@@ -29,19 +29,31 @@ module JCR
29
29
 
30
30
  def self.evaluate_object_rule jcr, rule_atom, data, econs, behavior = nil
31
31
 
32
+ push_trace_stack( econs, jcr )
33
+ trace( econs, "Evaluating object rule starting at #{slice_to_s(jcr)} against", data )
34
+ trace_def( econs, "object", jcr, data )
35
+ retval = evaluate_object( jcr, rule_atom, data, econs, behavior )
36
+ trace_eval( econs, "Object", retval )
37
+ pop_trace_stack( econs )
38
+ return retval
39
+
40
+ end
41
+
42
+ def self.evaluate_object jcr, rule_atom, data, econs, behavior = nil
43
+
32
44
  rules, annotations = get_rules_and_annotations( jcr )
33
45
 
34
46
  # if the data is not an object (Hash)
35
- return evaluate_reject( annotations,
36
- Evaluation.new( false, "#{data} is not an object at #{jcr} from #{rule_atom}") ) unless data.is_a? Hash
47
+ return evaluate_not( annotations,
48
+ Evaluation.new( false, "#{data} is not an object for #{raised_rule(jcr,rule_atom)}"), econs ) unless data.is_a? Hash
37
49
 
38
50
  # if the object has no members and there are zero sub-rules (it is suppose to be empty)
39
- return evaluate_reject( annotations,
40
- Evaluation.new( true, nil ) ) if rules.empty? && data.length == 0
51
+ return evaluate_not( annotations,
52
+ Evaluation.new( true, nil ), econs ) if rules.empty? && data.length == 0
41
53
 
42
54
  # if the object has members and there are zero sub-rules (it is suppose to be empty)
43
- return evaluate_reject( annotations,
44
- Evaluation.new( false, "Non-empty object at #{jcr} from #{rule_atom}" ) ) if rules.empty? && data.length != 0
55
+ return evaluate_not( annotations,
56
+ Evaluation.new( false, "Non-empty object for #{raised_rule(jcr,rule_atom)}" ), econs ) if rules.empty? && data.length != 0
45
57
 
46
58
  retval = nil
47
59
  behavior = ObjectBehavior.new unless behavior
@@ -52,10 +64,10 @@ module JCR
52
64
  if rule[:choice_combiner] && retval && retval.success
53
65
  next
54
66
  elsif rule[:sequence_combiner] && retval && !retval.success
55
- return evaluate_reject( annotations, retval ) # short circuit
67
+ return evaluate_not( annotations, retval, econs ) # short circuit
56
68
  end
57
69
 
58
- repeat_min, repeat_max = get_repetitions( rule )
70
+ repeat_min, repeat_max, repeat_step = get_repetitions( rule, econs )
59
71
 
60
72
  # Pay attention here:
61
73
  # Group rules need to be treated differently than other rules
@@ -67,8 +79,9 @@ module JCR
67
79
  if (grule = get_group(rule, econs))
68
80
 
69
81
  successes = 0
70
- for i in 0..repeat_max
82
+ for i in 0..repeat_max-1
71
83
  group_behavior = ObjectBehavior.new
84
+ group_behavior.checked_hash.merge!( behavior.checked_hash )
72
85
  e = evaluate_rule( grule, rule_atom, data, econs, group_behavior )
73
86
  if e.success
74
87
  behavior.checked_hash.merge!( group_behavior.checked_hash )
@@ -79,29 +92,66 @@ module JCR
79
92
  end
80
93
 
81
94
  if successes == 0 && repeat_min > 0
82
- retval = Evaluation.new( false, "object does not contain group #{rule} for #{jcr} from #{rule_atom}")
95
+ retval = Evaluation.new( false, "object does not contain group #{rule} for #{raised_rule(jcr,rule_atom)}")
83
96
  elsif successes < repeat_min
84
- retval = Evaluation.new( false, "object does not have contain necessary number of group #{rule} for #{jcr} from #{rule_atom}")
97
+ retval = Evaluation.new( false, "object does not have contain necessary number of group #{rule} for #{raised_rule(jcr,rule_atom)}")
98
+ elsif repeat_step && successes % repeat_step != 0
99
+ 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)}")
85
100
  else
86
101
  retval = Evaluation.new( true, nil )
87
102
  end
88
103
 
89
104
  else # if not grule
90
105
 
91
- repeat_results = data.select do |k,v|
92
- unless behavior.checked_hash[k]
93
- e = evaluate_rule(rule, rule_atom, [k, v], econs, nil)
94
- behavior.checked_hash[k] = e.success
95
- e.success
106
+ repeat_results = nil
107
+
108
+ # do a little lookahead for member rules defined by names
109
+ # if defined by a name, and not a regex, just pluck it from the object
110
+ # and short-circuit the enumeration
111
+
112
+ lookahead = get_leaf_rule( rule, econs )
113
+ lrules, lannotations = get_rules_and_annotations( lookahead[:member_rule] )
114
+ if lrules[0][:member_name]
115
+
116
+ repeat_results = {}
117
+ k = lrules[0][:member_name][:q_string].to_s
118
+ v = data[k]
119
+ if v
120
+ unless behavior.checked_hash[k]
121
+ e = evaluate_rule(rule, rule_atom, [k, v], econs, nil)
122
+ behavior.checked_hash[k] = e.success
123
+ repeat_results[ k ] = v if e.success
124
+ end
125
+ else
126
+ trace( econs, "No member '#{k}' found in object.")
127
+ end
128
+
129
+ else
130
+
131
+ trace( econs, "Scanning object.")
132
+ i = 0
133
+ repeat_results = data.select do |k,v|
134
+ unless behavior.checked_hash[k]
135
+ if i < repeat_max
136
+ e = evaluate_rule(rule, rule_atom, [k, v], econs, nil)
137
+ behavior.checked_hash[k] = e.success
138
+ i = i + 1 if e.success
139
+ e.success
140
+ end
141
+ end
96
142
  end
143
+
97
144
  end
98
145
 
146
+ trace( econs, "Found #{repeat_results.length} matching members repetitions in object with min #{repeat_min} and max #{repeat_max}" )
99
147
  if repeat_results.length == 0 && repeat_min > 0
100
- retval = Evaluation.new( false, "object does not contain #{rule} for #{jcr} from #{rule_atom}")
148
+ retval = Evaluation.new( false, "object does not contain #{rule} for #{raised_rule(jcr,rule_atom)}")
101
149
  elsif repeat_results.length < repeat_min
102
- retval = Evaluation.new( false, "object does not have enough #{rule} for #{jcr} from #{rule_atom}")
150
+ retval = Evaluation.new( false, "object does not have enough #{rule} for #{raised_rule(jcr,rule_atom)}")
103
151
  elsif repeat_results.length > repeat_max
104
- retval = Evaluation.new( false, "object has too many #{rule} for #{jcr} from #{rule_atom}")
152
+ retval = Evaluation.new( false, "object has too many #{rule} for #{raised_rule(jcr,rule_atom)}")
153
+ elsif repeat_step && repeat_results.length % repeat_step != 0
154
+ 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)}")
105
155
  else
106
156
  retval = Evaluation.new( true, nil)
107
157
  end
@@ -109,7 +159,11 @@ module JCR
109
159
 
110
160
  end # end if grule else
111
161
 
112
- return evaluate_reject( annotations, retval )
162
+ return evaluate_not( annotations, retval, econs )
113
163
  end
114
164
 
165
+ def self.object_to_s( jcr, shallow=true )
166
+ rules, annotations = get_rules_and_annotations( jcr )
167
+ return "#{annotations_to_s( annotations)} { #{rules_to_s(rules,shallow)} }"
168
+ end
115
169
  end
@@ -14,6 +14,7 @@
14
14
 
15
15
  require 'ipaddr'
16
16
  require 'time'
17
+ require 'pp'
17
18
  require 'addressable/uri'
18
19
  require 'addressable/template'
19
20
  require 'email_address_validator'
@@ -52,9 +53,11 @@ module JCR
52
53
  end
53
54
 
54
55
  class EvalConditions
55
- attr_accessor :mapping, :callbacks
56
- def initialize mapping, callbacks
56
+ attr_accessor :mapping, :callbacks, :trace, :trace_stack
57
+ def initialize mapping, callbacks, trace = false
57
58
  @mapping = mapping
59
+ @trace = trace
60
+ @trace_stack = []
58
61
  if callbacks
59
62
  @callbacks = callbacks
60
63
  else
@@ -64,6 +67,13 @@ module JCR
64
67
  end
65
68
 
66
69
  def self.evaluate_rule jcr, rule_atom, data, econs, behavior = nil
70
+ if jcr.is_a?( Hash )
71
+ if jcr[:rule_name]
72
+ rn = slice_to_s( jcr[:rule_name] )
73
+ trace( econs, "Named Rule: #{rn}" )
74
+ end
75
+ end
76
+
67
77
  retval = Evaluation.new( false, "failed to evaluate rule properly" )
68
78
  case
69
79
  when behavior.is_a?( ArrayBehavior )
@@ -75,6 +85,7 @@ module JCR
75
85
  when jcr[:target_rule_name]
76
86
  target = econs.mapping[ jcr[:target_rule_name][:rule_name].to_s ]
77
87
  raise "Target rule not in mapping. This should have been checked earlier." unless target
88
+ trace( econs, "Referencing target rule #{slice_to_s(target)} from #{slice_to_s( jcr[:target_rule_name][:rule_name] )}" )
78
89
  retval = evaluate_rule( target, target, data, econs, behavior )
79
90
  when jcr[:primitive_rule]
80
91
  retval = evaluate_value_rule( jcr[:primitive_rule], rule_atom, data, econs)
@@ -113,19 +124,26 @@ module JCR
113
124
  elsif retval.is_a? String
114
125
  retval = Evaluation.new( false, retval )
115
126
  end
127
+ trace( econs, "Callback #{callback} given evaluation of #{e.success} and returned #{retval}")
116
128
  return retval
117
129
  end
118
130
 
119
- def self.get_repetitions rule
131
+ def self.get_repetitions rule, econs
120
132
 
121
133
  repeat_min = 1
122
134
  repeat_max = 1
135
+ repeat_step = nil
123
136
  if rule[:optional]
124
137
  repeat_min = 0
125
138
  repeat_max = 1
126
139
  elsif rule[:one_or_more]
127
140
  repeat_min = 1
128
141
  repeat_max = Float::INFINITY
142
+ repeat_step = rule[:repetition_step].to_s.to_i if rule[:repetition_step]
143
+ elsif rule[:zero_or_more]
144
+ repeat_min = 0
145
+ repeat_max = Float::INFINITY
146
+ repeat_step = rule[:repetition_step].to_s.to_i if rule[:repetition_step]
129
147
  elsif rule[:specific_repetition] && rule[:specific_repetition].is_a?( Parslet::Slice )
130
148
  repeat_min = repeat_max = rule[:specific_repetition].to_s.to_i
131
149
  else
@@ -146,9 +164,16 @@ module JCR
146
164
  repeat_max = o.to_s.to_i
147
165
  end
148
166
  end
167
+ o = rule[:repetition_step]
168
+ if o
169
+ if o.is_a?( Parslet::Slice )
170
+ repeat_step = o.to_s.to_i
171
+ end
172
+ end
149
173
  end
150
174
 
151
- return repeat_min, repeat_max
175
+ trace( econs, "rule repetition min = #{repeat_min} max = #{repeat_max} repetition step = #{repeat_step}" )
176
+ return repeat_min, repeat_max, repeat_step
152
177
  end
153
178
 
154
179
  def self.get_rules_and_annotations jcr
@@ -166,7 +191,7 @@ module JCR
166
191
  when sub[:unordered_annotation]
167
192
  annotations << sub
168
193
  i = i + 1
169
- when sub[:reject_annotation]
194
+ when sub[:not_annotation]
170
195
  annotations << sub
171
196
  i = i + 1
172
197
  when sub[:root_annotation]
@@ -182,16 +207,17 @@ module JCR
182
207
  return rules, annotations
183
208
  end
184
209
 
185
- def self.evaluate_reject annotations, evaluation
186
- reject = false
210
+ def self.evaluate_not annotations, evaluation, econs
211
+ is_not = false
187
212
  annotations.each do |a|
188
- if a[:reject_annotation]
189
- reject = true
213
+ if a[:not_annotation]
214
+ is_not = true
190
215
  break
191
216
  end
192
217
  end
193
218
 
194
- if reject
219
+ if is_not
220
+ trace( econs, "Not annotation changing result from #{evaluation.success} to #{!evaluation.success}")
195
221
  evaluation.success = !evaluation.success
196
222
  end
197
223
  return evaluation
@@ -203,9 +229,180 @@ module JCR
203
229
  if rule[:target_rule_name]
204
230
  target = econs.mapping[ rule[:target_rule_name][:rule_name].to_s ]
205
231
  raise "Target rule not in mapping. This should have been checked earlier." unless target
232
+ trace( econs, "Referencing target rule #{slice_to_s(target)} from #{slice_to_s( rule[:target_rule_name][:rule_name] )}" )
206
233
  return get_group( target, econs )
207
234
  end
208
235
  #else
209
236
  return false
210
237
  end
238
+
239
+ def self.get_leaf_rule rule, econs
240
+ if rule[:target_rule_name ]
241
+ target = econs.mapping[ rule[:target_rule_name][:rule_name].to_s ]
242
+ raise "Target rule not in mapping. This should have been checked earlier." unless target
243
+ trace( econs, "Referencing target rule #{slice_to_s(target)} from #{slice_to_s( rule[:target_rule_name][:rule_name] )}" )
244
+ return target
245
+ end
246
+ #else
247
+ return rule
248
+ end
249
+
250
+ def self.push_trace_stack econs, jcr
251
+ econs.trace_stack.push( find_first_slice( jcr ) )
252
+ end
253
+
254
+ def self.pop_trace_stack econs
255
+ econs.trace_stack.pop
256
+ end
257
+
258
+ def self.elide s
259
+ if s.length > 60
260
+ s = s[0..56]
261
+ s = s + " ..."
262
+ end
263
+ return s
264
+ end
265
+
266
+ def self.trace econs, message, data = nil
267
+ if econs.trace
268
+ if data
269
+ if data.is_a? String
270
+ s = '"' + data + '"'
271
+ else
272
+ s = data.pretty_print_inspect
273
+ end
274
+ message = "#{message} data: #{elide(s)}"
275
+ end
276
+ last = econs.trace_stack.last
277
+ pos = "#{last.line_and_column}@#{last.offset}" if last
278
+ puts "[ #{econs.trace_stack.length}:#{pos} ] #{message}"
279
+ end
280
+ end
281
+
282
+ def self.trace_def econs, type, jcr, data
283
+ if econs.trace
284
+ s = ""
285
+ case type
286
+ when "value"
287
+ s = elide( value_to_s( jcr ) )
288
+ when "member"
289
+ s = elide( member_to_s( jcr ) )
290
+ when "object"
291
+ s = elide( object_to_s( jcr ) )
292
+ when "array"
293
+ s = elide( array_to_s( jcr ) )
294
+ when "group"
295
+ s = elide( group_to_s( jcr ) )
296
+ else
297
+ s = "** unknown rule **"
298
+ end
299
+ trace( econs, "#{type}: #{s}", data)
300
+ end
301
+ end
302
+
303
+ def self.trace_eval econs, message, evaluation
304
+ if evaluation.success
305
+ trace( econs, "#{message} evaluation is true" )
306
+ else
307
+ trace( econs, "#{message} evaluation failed: #{evaluation.reason}")
308
+ end
309
+ end
310
+
311
+ def self.find_first_slice slice
312
+ if slice.is_a? Parslet::Slice
313
+ return slice
314
+ elsif slice.is_a?( Hash ) && !slice.empty?
315
+ s = nil
316
+ slice.values.each do |v|
317
+ s = find_first_slice( v )
318
+ break if s
319
+ end
320
+ return s if s
321
+ elsif slice.is_a?( Array ) && !slice.empty?
322
+ s = nil
323
+ slice.each do |i|
324
+ s = find_first_slice( i )
325
+ break if s
326
+ end
327
+ return s if s
328
+ end
329
+ #else
330
+ return nil
331
+ end
332
+
333
+ def self.slice_to_s slice
334
+ s = find_first_slice( slice )
335
+ if s.is_a? Parslet::Slice
336
+ pos = s.line_and_column
337
+ retval = "'#{s.inspect}' ( line #{pos[0]} column #{pos[1]} )"
338
+ else
339
+ retval = slice.to_s
340
+ end
341
+ retval
342
+ end
343
+
344
+ def self.raised_rule jcr, rule_atom
345
+ " rule at #{slice_to_s(jcr)} [ #{jcr} ] from rule at #{slice_to_s(rule_atom)}"
346
+ end
347
+
348
+ def self.rule_to_s( rule, shallow=true)
349
+ if rule[:primitive_rule]
350
+ retval = value_to_s( rule[:primitive_rule] )
351
+ elsif rule[:member_rule]
352
+ retval = member_to_s( rule[:member_rule], shallow )
353
+ elsif rule[:object_rule]
354
+ retval = object_to_s( rule[:object_rule], shallow )
355
+ elsif rule[:array_rule]
356
+ retval = array_to_s( rule[:array_rule], shallow )
357
+ elsif rule[:group_rule]
358
+ retval = group_to_s( rule[:group_rule], shallow )
359
+ elsif rule[:target_rule_name]
360
+ retval = target_to_s( rule[:target_rule_name] )
361
+ elsif rule[:rule_name]
362
+ retval = "rule: #{rule[:rule_name].to_s}"
363
+ elsif rule[:rule]
364
+ retval = rule_to_s( rule[:rule], shallow )
365
+ else
366
+ retval = "** unknown rule definition **"
367
+ end
368
+ return retval
369
+ end
370
+
371
+ def self.rules_to_s( rules, shallow=true)
372
+ retval = ""
373
+ rules.each do |rule|
374
+ if rule[:rule_name]
375
+ next
376
+ elsif rule[:choice_combiner]
377
+ retval = retval + " | "
378
+ elsif rule[:sequence_combiner]
379
+ retval = retval + " , "
380
+ end
381
+ retval = retval + rule_to_s( rule, shallow )
382
+ end
383
+ return retval
384
+ end
385
+
386
+ def self.annotations_to_s( annotations )
387
+ retval = ""
388
+ annotations.each do |a|
389
+ case
390
+ when a[:unordered_annotation]
391
+ retval = retval + " @{unordered}"
392
+ when a[:not_annotation]
393
+ retval = retval + " @{not}"
394
+ when a[:root_annotation]
395
+ retval = retval + " @{root}"
396
+ else
397
+ retval = retval + " @{ ** unknown annotation ** }"
398
+ end
399
+ end if annotations
400
+ retval = retval + " " if retval.length != 0
401
+ return retval
402
+ end
403
+
404
+ def self.target_to_s( jcr )
405
+ return annotations_to_s( jcr[:annotations] ) + " target: " + jcr[:rule_name].to_s
406
+ end
407
+
211
408
  end