jcrvalidator 0.5.3 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml 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