ddao-kwalify 0.7.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (208) hide show
  1. data/CHANGES.txt +243 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.txt +61 -0
  4. data/bin/kwalify +13 -0
  5. data/contrib/inline-require +153 -0
  6. data/contrib/kwalify +4159 -0
  7. data/doc-api/classes/CommandOptionError.html +184 -0
  8. data/doc-api/classes/CommandOptionParser.html +325 -0
  9. data/doc-api/classes/Kwalify.html +292 -0
  10. data/doc-api/classes/Kwalify/AssertionError.html +148 -0
  11. data/doc-api/classes/Kwalify/BaseError.html +297 -0
  12. data/doc-api/classes/Kwalify/BaseParser.html +461 -0
  13. data/doc-api/classes/Kwalify/CommandOptionError.html +168 -0
  14. data/doc-api/classes/Kwalify/ErrorHelper.html +223 -0
  15. data/doc-api/classes/Kwalify/HashInterface.html +118 -0
  16. data/doc-api/classes/Kwalify/Json.html +105 -0
  17. data/doc-api/classes/Kwalify/KwalifyError.html +111 -0
  18. data/doc-api/classes/Kwalify/Main.html +339 -0
  19. data/doc-api/classes/Kwalify/MetaValidator.html +448 -0
  20. data/doc-api/classes/Kwalify/Parser.html +155 -0
  21. data/doc-api/classes/Kwalify/PlainYamlParser.html +523 -0
  22. data/doc-api/classes/Kwalify/PlainYamlParser/Alias.html +165 -0
  23. data/doc-api/classes/Kwalify/Rule.html +433 -0
  24. data/doc-api/classes/Kwalify/SchemaError.html +148 -0
  25. data/doc-api/classes/Kwalify/SyntaxError.html +185 -0
  26. data/doc-api/classes/Kwalify/Types.html +302 -0
  27. data/doc-api/classes/Kwalify/Util.html +389 -0
  28. data/doc-api/classes/Kwalify/Util/HashLike.html +246 -0
  29. data/doc-api/classes/Kwalify/Util/OrderedHash.html +330 -0
  30. data/doc-api/classes/Kwalify/ValidationError.html +148 -0
  31. data/doc-api/classes/Kwalify/Validator.html +381 -0
  32. data/doc-api/classes/Kwalify/Yaml.html +194 -0
  33. data/doc-api/classes/Kwalify/Yaml/Parser.html +1538 -0
  34. data/doc-api/classes/Kwalify/YamlParser.html +542 -0
  35. data/doc-api/classes/Kwalify/YamlSyntaxError.html +119 -0
  36. data/doc-api/classes/Test.html +107 -0
  37. data/doc-api/classes/Test/Unit.html +101 -0
  38. data/doc-api/created.rid +1 -0
  39. data/doc-api/files/__/README_txt.html +172 -0
  40. data/doc-api/files/kwalify/errors_rb.html +114 -0
  41. data/doc-api/files/kwalify/main_rb.html +118 -0
  42. data/doc-api/files/kwalify/messages_rb.html +107 -0
  43. data/doc-api/files/kwalify/meta-validator_rb.html +117 -0
  44. data/doc-api/files/kwalify/parser/base_rb.html +116 -0
  45. data/doc-api/files/kwalify/parser/yaml_rb.html +117 -0
  46. data/doc-api/files/kwalify/rule_rb.html +116 -0
  47. data/doc-api/files/kwalify/types_rb.html +114 -0
  48. data/doc-api/files/kwalify/util/assert-text-equal_rb.html +115 -0
  49. data/doc-api/files/kwalify/util/hash-interface_rb.html +114 -0
  50. data/doc-api/files/kwalify/util/hashlike_rb.html +107 -0
  51. data/doc-api/files/kwalify/util/option-parser_rb.html +107 -0
  52. data/doc-api/files/kwalify/util/ordered-hash_rb.html +107 -0
  53. data/doc-api/files/kwalify/util/testcase-helper_rb.html +115 -0
  54. data/doc-api/files/kwalify/util_rb.html +107 -0
  55. data/doc-api/files/kwalify/validator_rb.html +117 -0
  56. data/doc-api/files/kwalify/yaml-parser_rb.html +117 -0
  57. data/doc-api/files/kwalify_rb.html +121 -0
  58. data/doc-api/fr_class_index.html +57 -0
  59. data/doc-api/fr_file_index.html +45 -0
  60. data/doc-api/fr_method_index.html +168 -0
  61. data/doc-api/index.html +24 -0
  62. data/doc-api/rdoc-style.css +208 -0
  63. data/doc/docstyle.css +188 -0
  64. data/doc/img/fig01.png +0 -0
  65. data/doc/users-guide.html +2050 -0
  66. data/examples/address-book/Makefile +10 -0
  67. data/examples/address-book/address-book.schema.yaml +45 -0
  68. data/examples/address-book/address-book.yaml +36 -0
  69. data/examples/data-binding/BABEL.data.yaml +63 -0
  70. data/examples/data-binding/BABEL.schema.yaml +31 -0
  71. data/examples/data-binding/Makefile +8 -0
  72. data/examples/data-binding/Rakefile +13 -0
  73. data/examples/data-binding/main.rb +27 -0
  74. data/examples/invoice/Makefile +9 -0
  75. data/examples/invoice/invoice.schema.yaml +43 -0
  76. data/examples/invoice/invoice.yaml +32 -0
  77. data/examples/tapkit/Makefile +10 -0
  78. data/examples/tapkit/main.rb +7 -0
  79. data/examples/tapkit/tapkit.schema.yaml +146 -0
  80. data/examples/tapkit/tapkit.yaml +85 -0
  81. data/lib/kwalify.rb +67 -0
  82. data/lib/kwalify/errors.rb +128 -0
  83. data/lib/kwalify/kwalify.schema.yaml +58 -0
  84. data/lib/kwalify/main.rb +442 -0
  85. data/lib/kwalify/messages.rb +173 -0
  86. data/lib/kwalify/meta-validator.rb +276 -0
  87. data/lib/kwalify/parser/base.rb +127 -0
  88. data/lib/kwalify/parser/yaml.rb +841 -0
  89. data/lib/kwalify/rule.rb +560 -0
  90. data/lib/kwalify/templates/genclass-java.eruby +222 -0
  91. data/lib/kwalify/templates/genclass-php.eruby +104 -0
  92. data/lib/kwalify/templates/genclass-ruby.eruby +113 -0
  93. data/lib/kwalify/types.rb +156 -0
  94. data/lib/kwalify/util.rb +157 -0
  95. data/lib/kwalify/util/assert-text-equal.rb +46 -0
  96. data/lib/kwalify/util/hash-interface.rb +18 -0
  97. data/lib/kwalify/util/hashlike.rb +51 -0
  98. data/lib/kwalify/util/option-parser.rb +220 -0
  99. data/lib/kwalify/util/ordered-hash.rb +57 -0
  100. data/lib/kwalify/util/testcase-helper.rb +112 -0
  101. data/lib/kwalify/validator.rb +282 -0
  102. data/lib/kwalify/yaml-parser.rb +870 -0
  103. data/setup.rb +1585 -0
  104. data/test/Rookbook.yaml +10 -0
  105. data/test/data/users-guide/AddressBook.java.expected +40 -0
  106. data/test/data/users-guide/BABEL.data.yaml +24 -0
  107. data/test/data/users-guide/BABEL.schema.yaml +30 -0
  108. data/test/data/users-guide/ExampleAddressBook.java +47 -0
  109. data/test/data/users-guide/Group.java.expected +24 -0
  110. data/test/data/users-guide/Person.java.expected +44 -0
  111. data/test/data/users-guide/address_book.rb +52 -0
  112. data/test/data/users-guide/address_book.schema.yaml +28 -0
  113. data/test/data/users-guide/address_book.yaml +27 -0
  114. data/test/data/users-guide/answers-schema.yaml +12 -0
  115. data/test/data/users-guide/answers-validator.rb +52 -0
  116. data/test/data/users-guide/babel_genclass.result +26 -0
  117. data/test/data/users-guide/config.schema.yaml +7 -0
  118. data/test/data/users-guide/config.yaml +4 -0
  119. data/test/data/users-guide/document01a.yaml +3 -0
  120. data/test/data/users-guide/document01b.yaml +3 -0
  121. data/test/data/users-guide/document02a.yaml +4 -0
  122. data/test/data/users-guide/document02b.yaml +4 -0
  123. data/test/data/users-guide/document03a.yaml +6 -0
  124. data/test/data/users-guide/document03b.yaml +6 -0
  125. data/test/data/users-guide/document04a.yaml +9 -0
  126. data/test/data/users-guide/document04b.yaml +9 -0
  127. data/test/data/users-guide/document05a.yaml +11 -0
  128. data/test/data/users-guide/document05b.yaml +12 -0
  129. data/test/data/users-guide/document06a.yaml +15 -0
  130. data/test/data/users-guide/document06b.yaml +16 -0
  131. data/test/data/users-guide/document07a.yaml +9 -0
  132. data/test/data/users-guide/document07b.yaml +7 -0
  133. data/test/data/users-guide/document12a.json +10 -0
  134. data/test/data/users-guide/document12b.json +6 -0
  135. data/test/data/users-guide/document13a.yaml +17 -0
  136. data/test/data/users-guide/document14a.yaml +3 -0
  137. data/test/data/users-guide/document14b.yaml +3 -0
  138. data/test/data/users-guide/document15a.yaml +6 -0
  139. data/test/data/users-guide/document15b.yaml +5 -0
  140. data/test/data/users-guide/example_address_book.rb +10 -0
  141. data/test/data/users-guide/example_address_book_java.result +32 -0
  142. data/test/data/users-guide/example_address_book_ruby.result +31 -0
  143. data/test/data/users-guide/genclass_java.result +4 -0
  144. data/test/data/users-guide/howto-validation-with-parsing.rb +28 -0
  145. data/test/data/users-guide/howto-validation.rb +25 -0
  146. data/test/data/users-guide/howto3.rb +6 -0
  147. data/test/data/users-guide/howto3.result +5 -0
  148. data/test/data/users-guide/howto3.yaml +8 -0
  149. data/test/data/users-guide/howto5_databinding.result +111 -0
  150. data/test/data/users-guide/invalid01.result +3 -0
  151. data/test/data/users-guide/invalid02.result +5 -0
  152. data/test/data/users-guide/invalid03.result +5 -0
  153. data/test/data/users-guide/invalid04.result +4 -0
  154. data/test/data/users-guide/invalid05.result +11 -0
  155. data/test/data/users-guide/invalid06.result +4 -0
  156. data/test/data/users-guide/invalid07.result +3 -0
  157. data/test/data/users-guide/invalid08.result +3 -0
  158. data/test/data/users-guide/invalid12.json +8 -0
  159. data/test/data/users-guide/invalid14.result +4 -0
  160. data/test/data/users-guide/invalid15.result +4 -0
  161. data/test/data/users-guide/loadbabel.rb +27 -0
  162. data/test/data/users-guide/loadconfig.rb +16 -0
  163. data/test/data/users-guide/loadconfig.result +6 -0
  164. data/test/data/users-guide/models.rb +22 -0
  165. data/test/data/users-guide/option_ha.result +6 -0
  166. data/test/data/users-guide/option_ha_genclass_java.result +7 -0
  167. data/test/data/users-guide/schema01.yaml +3 -0
  168. data/test/data/users-guide/schema02.yaml +12 -0
  169. data/test/data/users-guide/schema03.yaml +9 -0
  170. data/test/data/users-guide/schema04.yaml +20 -0
  171. data/test/data/users-guide/schema05.yaml +29 -0
  172. data/test/data/users-guide/schema06.yaml +11 -0
  173. data/test/data/users-guide/schema12.json +12 -0
  174. data/test/data/users-guide/schema13.yaml +13 -0
  175. data/test/data/users-guide/schema14.yaml +5 -0
  176. data/test/data/users-guide/schema15.yaml +21 -0
  177. data/test/data/users-guide/valid01.result +2 -0
  178. data/test/data/users-guide/valid02.result +2 -0
  179. data/test/data/users-guide/valid03.result +2 -0
  180. data/test/data/users-guide/valid04.result +2 -0
  181. data/test/data/users-guide/valid05.result +2 -0
  182. data/test/data/users-guide/valid06.result +2 -0
  183. data/test/data/users-guide/valid07.result +2 -0
  184. data/test/data/users-guide/valid08.result +2 -0
  185. data/test/data/users-guide/valid12.result +2 -0
  186. data/test/data/users-guide/valid13.result +2 -0
  187. data/test/data/users-guide/valid14.result +2 -0
  188. data/test/data/users-guide/valid15.result +2 -0
  189. data/test/data/users-guide/validate08.rb +37 -0
  190. data/test/test-action.rb +78 -0
  191. data/test/test-action.yaml +738 -0
  192. data/test/test-databinding.rb +83 -0
  193. data/test/test-databinding.yaml +339 -0
  194. data/test/test-main.rb +157 -0
  195. data/test/test-main.yaml +384 -0
  196. data/test/test-metavalidator.rb +80 -0
  197. data/test/test-metavalidator.yaml +1179 -0
  198. data/test/test-parser-yaml.rb +57 -0
  199. data/test/test-parser-yaml.yaml +1749 -0
  200. data/test/test-rule.rb +26 -0
  201. data/test/test-rule.yaml +317 -0
  202. data/test/test-users-guide.rb +75 -0
  203. data/test/test-validator.rb +95 -0
  204. data/test/test-validator.yaml +986 -0
  205. data/test/test-yaml-parser.rb +47 -0
  206. data/test/test-yaml-parser.yaml +1226 -0
  207. data/test/test.rb +60 -0
  208. metadata +261 -0
@@ -0,0 +1,841 @@
1
+ ###
2
+ ### $Rev: 99 $
3
+ ### $Release: 0.7.1 $
4
+ ### copyright(c) 2005-2008 kuwata-lab all rights reserved.
5
+ ###
6
+
7
+ require 'kwalify/validator'
8
+ require 'kwalify/errors'
9
+ require 'kwalify/util'
10
+ require 'kwalify/parser/base'
11
+
12
+
13
+
14
+ module Kwalify
15
+
16
+ module Yaml
17
+ end
18
+
19
+ end
20
+
21
+
22
+ ##
23
+ ## YAML parser with validator
24
+ ##
25
+ ## ex.
26
+ ## schema = YAML.load_file('schema.yaml')
27
+ ## require 'kwalify'
28
+ ## validator = Kwalify::Validator.new(schema)
29
+ ## parser = Kwalify::Yaml::Parser.new(validator) # validator is optional
30
+ ## #parser.preceding_alias = true # optional
31
+ ## #parser.data_binding = true # optional
32
+ ## ydoc = parser.parse_file('data.yaml')
33
+ ## errors = parser.errors
34
+ ## if errors && !errors.empty?
35
+ ## errors.each do |e|
36
+ ## puts "line=#{e.linenum}, path=#{e.path}, mesg=#{e.message}"
37
+ ## end
38
+ ## end
39
+ ##
40
+ class Kwalify::Yaml::Parser < Kwalify::BaseParser
41
+
42
+
43
+ alias reset_scanner reset
44
+
45
+
46
+ def initialize(validator=nil, properties={})
47
+ @validator = validator.is_a?(Hash) ? Kwalify::Validator.new(validator) : validator
48
+ @data_binding = properties[:data_binding] # enable data binding or not
49
+ @preceding_alias = properties[:preceding_alias] # allow preceding alias or not
50
+ @sequence_class = properties[:sequence_class] || Array
51
+ @mapping_class = properties[:mapping_class] || Hash
52
+ end
53
+ attr_accessor :validator # Validator
54
+ attr_accessor :data_binding # boolean
55
+ attr_accessor :preceding_alias # boolean
56
+ attr_accessor :sequence_class # Class
57
+ attr_accessor :mapping_class # Class
58
+
59
+
60
+ def reset_parser()
61
+ @anchors = {}
62
+ @errors = []
63
+ @done = {}
64
+ @preceding_aliases = []
65
+ @location_table = {} # object_id -> sequence or mapping
66
+ @doc = nil
67
+ end
68
+ attr_reader :errors
69
+
70
+
71
+ def _error(klass, message, path, linenum, column)
72
+ ex = klass.new(message)
73
+ ex.path = path.is_a?(Array) ? '/' + path.join('/') : path
74
+ ex.linenum = linenum
75
+ ex.column = column
76
+ ex.filename = @filename
77
+ return ex
78
+ end
79
+ private :_error
80
+
81
+
82
+ # def _validate_error(message, path, linenum=@linenum, column=@column)
83
+ # #message = _build_message(message_key)
84
+ # error = _error(ValidationError, message.to_s, path, linenum, column)
85
+ # @errors << error
86
+ # end
87
+ # private :_validate_error
88
+
89
+
90
+ def _set_error_info(linenum=@linenum, column=@column, &block)
91
+ len = @errors.length
92
+ yield
93
+ n = @errors.length - len
94
+ (1..n).each do |i|
95
+ error = @errors[-i]
96
+ error.linenum ||= linenum
97
+ error.column ||= column
98
+ error.filename ||= @filename
99
+ end if n > 0
100
+ end
101
+
102
+
103
+ def skip_spaces_and_comments()
104
+ scan(/\s+/)
105
+ while match?(/\#/)
106
+ scan(/.*?\n/)
107
+ scan(/\s+/)
108
+ end
109
+ end
110
+
111
+
112
+ def document_start?()
113
+ return match?(/---\s/) && @column == 1
114
+ end
115
+
116
+
117
+ def stream_end?()
118
+ return match?(/\.\.\.\s/) && @column == 1
119
+ end
120
+
121
+
122
+ def has_next?()
123
+ return !(eos? || stream_end?)
124
+ end
125
+
126
+
127
+ def parse(input=nil, opts={})
128
+ reset_scanner(input, opts[:filename], opts[:untabify]) if input
129
+ return parse_next()
130
+ end
131
+
132
+
133
+ def parse_file(filename, opts={})
134
+ opts[:filename] = filename
135
+ return parse(File.read(filename), opts)
136
+ end
137
+
138
+
139
+ def parse_next()
140
+ reset_parser()
141
+ path = []
142
+ skip_spaces_and_comments()
143
+ if document_start?()
144
+ scan(/.*\n/)
145
+ skip_spaces_and_comments()
146
+ end
147
+ _linenum = @linenum #*V
148
+ _column = @column #*V
149
+ rule = @validator ? @validator.rule : nil #*V
150
+ uniq_table = nil #*V
151
+ parent = nil #*V
152
+ val = parse_block_value(0, rule, path, uniq_table, parent)
153
+ _set_error_info(_linenum, _column) do #*V
154
+ @validator._validate(val, rule, [], @errors, @done, uniq_table, false) #*V
155
+ end if rule #*V
156
+ resolve_preceding_aliases(val) if @preceding_alias
157
+ unless eos? || document_start?() || stream_end?()
158
+ raise _syntax_error("document end expected (maybe invalid tab char found).", path)
159
+ end
160
+ @doc = val
161
+ @location_table[-1] = [_linenum, _column]
162
+ return val
163
+ end
164
+
165
+
166
+ def parse_stream(input, opts={}, &block)
167
+ reset_scanner(input, opts[:filename], opts[:untabify])
168
+ ydocs = block_given? ? nil : []
169
+ while true
170
+ ydoc = parse_next()
171
+ ydocs ? (ydocs << ydoc) : (yield ydoc)
172
+ break if eos? || stream_end?()
173
+ document_start?() or raise "** internal error"
174
+ scan(/.*\n/)
175
+ end
176
+ return ydocs
177
+ end
178
+
179
+ alias parse_documents parse_stream
180
+
181
+
182
+ MAPKEY_PATTERN = /([\w.][-\w.:]*\*?|".*?"|'.*?'|:\w+|=|<<)[ \t]*:\s+/ # :nodoc:
183
+
184
+ PRECEDING_ALIAS_PLACEHOLDER = Object.new # :nodoc:
185
+
186
+
187
+ def parse_anchor(rule, path, uniq_table, container)
188
+ name = group(1)
189
+ if @anchors.key?(name)
190
+ raise _syntax_error("&#{name}: anchor duplicated.", path,
191
+ @linenum, @column - name.length)
192
+ end
193
+ skip_spaces_and_comments()
194
+ return name
195
+ end
196
+
197
+
198
+ def parse_alias(rule, path, uniq_table, container)
199
+ name = group(1)
200
+ if @anchors.key?(name)
201
+ val = @anchors[name]
202
+ elsif @preceding_alias
203
+ @preceding_aliases << [name, rule, path.dup, container,
204
+ @linenum, @column - name.length - 1]
205
+ val = PRECEDING_ALIAS_PLACEHOLDER
206
+ else
207
+ raise _syntax_error("*#{name}: anchor not found.", path,
208
+ @linenum, @column - name.length - 1)
209
+ end
210
+ skip_spaces_and_comments()
211
+ return val
212
+ end
213
+
214
+
215
+ def resolve_preceding_aliases(val)
216
+ @preceding_aliases.each do |name, rule, path, container, _linenum, _column|
217
+ unless @anchors.key?(name)
218
+ raise _syntax_error("*#{name}: anchor not found.", path, _linenum, _column)
219
+ end
220
+ key = path[-1]
221
+ val = @anchors[name]
222
+ raise unless !container.respond_to?('[]') || container[key].equal?(PRECEDING_ALIAS_PLACEHOLDER)
223
+ if container.is_a?(Array)
224
+ container[key] = val
225
+ else
226
+ put_to_map(rule, container, key, val, _linenum, _column)
227
+ end
228
+ _set_error_info(_linenum, _column) do #*V
229
+ @validator._validate(val, rule, path, @errors, @done, false) #*V
230
+ end if rule #*V
231
+ end
232
+ end
233
+
234
+
235
+ def parse_block_value(level, rule, path, uniq_table, container)
236
+ skip_spaces_and_comments()
237
+ ## nil
238
+ return nil if @column <= level || eos?
239
+ ## anchor and alias
240
+ name = nil
241
+ if scan(/\&([-\w]+)/)
242
+ name = parse_anchor(rule, path, uniq_table, container)
243
+ elsif scan(/\*([-\w]+)/)
244
+ return parse_alias(rule, path, uniq_table, container)
245
+ end
246
+ ## type
247
+ if scan(/!!?\w+/)
248
+ skip_spaces_and_comments()
249
+ end
250
+ ## sequence
251
+ if match?(/-\s+/)
252
+ if rule && !rule.sequence
253
+ #_validate_error("sequence is not expected.", path)
254
+ rule = nil
255
+ end
256
+ seq = create_sequence(rule, @linenum, @column)
257
+ @anchors[name] = seq if name
258
+ parse_block_seq(seq, rule, path, uniq_table)
259
+ return seq
260
+ end
261
+ ## mapping
262
+ if match?(MAPKEY_PATTERN)
263
+ if rule && !rule.mapping
264
+ #_validate_error("mapping is not expected.", path)
265
+ rule = nil
266
+ end
267
+ map = create_mapping(rule, @linenum, @column)
268
+ @anchors[name] = map if name
269
+ parse_block_map(map, rule, path, uniq_table)
270
+ return map
271
+ end
272
+ ## sequence (flow-style)
273
+ if match?(/\[/)
274
+ if rule && !rule.sequence
275
+ #_validate_error("sequence is not expected.", path)
276
+ rule = nil
277
+ end
278
+ seq = create_sequence(rule, @linenum, @column)
279
+ @anchors[name] = seq if name
280
+ parse_flow_seq(seq, rule, path, uniq_table)
281
+ return seq
282
+ end
283
+ ## mapping (flow-style)
284
+ if match?(/\{/)
285
+ if rule && !rule.mapping
286
+ #_validate_error("mapping is not expected.", path)
287
+ rule = nil
288
+ end
289
+ map = create_mapping(rule, @linenum, @column)
290
+ @anchors[name] = map if name
291
+ parse_flow_map(map, rule, path, uniq_table)
292
+ return map
293
+ end
294
+ ## block text
295
+ if match?(/[|>]/)
296
+ text = parse_block_text(level, rule, path, uniq_table)
297
+ @anchors[name] = text if name
298
+ return text
299
+ end
300
+ ## scalar
301
+ scalar = parse_block_scalar(rule, path, uniq_table)
302
+ @anchors[name] = scalar if name
303
+ return scalar
304
+ end
305
+
306
+
307
+ def parse_block_seq(seq, seq_rule, path, uniq_table)
308
+ level = @column
309
+ rule = seq_rule ? seq_rule.sequence[0] : nil
310
+ path.push(nil)
311
+ i = 0
312
+ _linenum = @linenum #*V
313
+ _column = @column #*V
314
+ uniq_table = rule ? rule._uniqueness_check_table() : nil #*V
315
+ while level == @column && scan(/-\s+/)
316
+ path[-1] = i
317
+ skip_spaces_and_comments() #*V
318
+ _linenum2 = @linenum
319
+ _column2 = @column
320
+ val = parse_block_value(level, rule, path, uniq_table, seq)
321
+ add_to_seq(rule, seq, val, _linenum2, _column2) # seq << val
322
+ _set_error_info(_linenum, _column) do #*V
323
+ @validator._validate(val, rule, path, @errors, @done, uniq_table, false) #*V
324
+ end if rule && !val.equal?(PRECEDING_ALIAS_PLACEHOLDER) #*V
325
+ skip_spaces_and_comments()
326
+ i += 1
327
+ _linenum = @linenum #*V
328
+ _column = @column #*V
329
+ end
330
+ path.pop()
331
+ return seq
332
+ end
333
+
334
+
335
+ def _parse_map_value(map, map_rule, path, level, key, is_merged, uniq_table,
336
+ _linenum, _column, _linenum2, _column2) #:nodoc:
337
+ key = to_mapkey(key)
338
+ path[-1] = key
339
+ #if map.is_a?(Hash) && map.key?(key) && !is_merged
340
+ if map.respond_to?('key?') && map.key?(key) && !is_merged
341
+ rule = map_rule.mapping[key]
342
+ unless rule && rule.default
343
+ raise _syntax_error("mapping key is duplicated.", path)
344
+ end
345
+ end
346
+ #
347
+ if key == '=' # default
348
+ val = level ? parse_block_value(level, nil, path, uniq_table, map) \
349
+ : parse_flow_value(nil, path, uniq_table, map)
350
+ map.default = val
351
+ elsif key == '<<' # merge
352
+ classobj = nil
353
+ if map_rule && map_rule.classname
354
+ map_rule = map_rule.dup()
355
+ classobj = map_rule.classobj
356
+ map_rule.classname = nil
357
+ map_rule.classobj = nil
358
+ end
359
+ val = level ? parse_block_value(level, map_rule, path, uniq_table, map) \
360
+ : parse_flow_value(map_rule, path, uniq_table, map)
361
+ if val.is_a?(Array)
362
+ val.each_with_index do |v, i|
363
+ unless v.is_a?(Hash) || (classobj && val.is_a?(classobj))
364
+ raise _syntax_error("'<<': mapping required.", path + [i])
365
+ end
366
+ end
367
+ values = val
368
+ elsif val.is_a?(Hash) || (classobj && val.is_a?(classobj))
369
+ values = [val]
370
+ else
371
+ raise _syntax_error("'<<': mapping (or sequence of mapping) required.", path)
372
+ end
373
+ #
374
+ values.each do |hash|
375
+ if !hash.is_a?(Hash)
376
+ assert_error "hash=#{hash.inspect}" unless classobj && hash.is_a?(classobj)
377
+ obj = hash
378
+ hash = {}
379
+ obj.instance_variables.each do |name|
380
+ key = name[1..-1] # '@foo' => 'foo'
381
+ val = obj.instane_variable_get(name)
382
+ hash[key] = val
383
+ end
384
+ end
385
+ for key, val in hash
386
+ path[-1] = key #*V
387
+ rule = map_rule ? map_rule.mapping[key] : nil #*V
388
+ utable = uniq_table ? uniq_table[key] : nil #*V
389
+ _validate_map_value(map, map_rule, rule, path, utable, #*V
390
+ key, val, _linenum, _column) #*V
391
+ put_to_map(rule, map, key, val, _linenum2, _column2)
392
+ end
393
+ end
394
+ is_merged = true
395
+ else # other
396
+ rule = map_rule ? map_rule.mapping[key] : nil #*V
397
+ utable = uniq_table ? uniq_table[key] : nil #*V
398
+ val = level ? parse_block_value(level, rule, path, utable, map) \
399
+ : parse_flow_value(rule, path, utable, map)
400
+ _validate_map_value(map, map_rule, rule, path, utable, key, val, #*V
401
+ _linenum, _column) #*V
402
+ put_to_map(rule, map, key, val, _linenum2, _column2)
403
+ end
404
+ return is_merged
405
+ end
406
+
407
+
408
+ def _validate_map_value(map, map_rule, rule, path, uniq_table, key, val, #*V
409
+ _linenum, _column) #*V
410
+ if map_rule && !rule #*V
411
+ #_validate_error("unknown mapping key.", path) #*V
412
+ _set_error_info(_linenum, _column) do #*V
413
+ error = Kwalify::ErrorHelper.validate_error(:key_undefined, #*V
414
+ rule, path, map, ["#{key}:"]) #*V
415
+ @errors << error #*V
416
+ #error.linenum = _linenum #*V
417
+ #error.column = _column #*V
418
+ end #*V
419
+ end #*V
420
+ _set_error_info(_linenum, _column) do #*V
421
+ @validator._validate(val, rule, path, @errors, @done, uniq_table, false) #*V
422
+ end if rule && !val.equal?(PRECEDING_ALIAS_PLACEHOLDER) #*V
423
+ end
424
+
425
+
426
+ def parse_block_map(map, map_rule, path, uniq_table)
427
+ _start_linenum = @linenum #*V
428
+ _start_column = @column #*V
429
+ level = @column
430
+ path.push(nil)
431
+ is_merged = false
432
+ while true
433
+ _linenum = @linenum #*V
434
+ _column = @column #*V
435
+ break unless level == @column && scan(MAPKEY_PATTERN)
436
+ key = group(1)
437
+ skip_spaces_and_comments() #*V
438
+ _linenum2 = @linenum #*V
439
+ _column2 = @column #*V
440
+ is_merged = _parse_map_value(map, map_rule, path, level, key, is_merged,
441
+ uniq_table, _linenum, _column, _linenum2, _column2)
442
+ #skip_spaces_and_comments()
443
+ end
444
+ path.pop()
445
+ _set_error_info(_start_linenum, _start_column) do #*V
446
+ @validator._validate_mapping_required_keys(map, map_rule, #*V
447
+ path, @errors) #*V
448
+ end if map_rule #*V
449
+ return map
450
+ end
451
+
452
+
453
+ def to_mapkey(str)
454
+ if str[0] == ?" || str[0] == ?'
455
+ return str[1..-2]
456
+ else
457
+ return to_scalar(str)
458
+ end
459
+ end
460
+ private :to_mapkey
461
+
462
+
463
+ def parse_block_scalar(rule, path, uniq_table)
464
+ _linenum = @linenum #*V
465
+ _column = @column #*V
466
+ ch = peep(1)
467
+ if ch == '"' || ch == "'"
468
+ val = scan_string()
469
+ scan(/[ \t]*(?:\#.*)?$/)
470
+ else
471
+ scan(/(.*?)[ \t]*(?:\#.*)?$/)
472
+ #str.rstrip!
473
+ val = to_scalar(group(1))
474
+ end
475
+ val = create_scalar(rule, val, _linenum, _column) #*V
476
+ #_set_error_info(_linenum, _column) do #*V
477
+ # @validator._validate_unique(val, rule, path, @errors, uniq_table) #*V
478
+ #end if uniq_table #*V
479
+ skip_spaces_and_comments()
480
+ return val
481
+ end
482
+
483
+
484
+ def parse_block_text(column, rule, path, uniq_table)
485
+ _linenum = @linenum #*V
486
+ _column = @column #*V
487
+ indicator = scan(/[|>]/)
488
+ chomping = scan(/[-+]/)
489
+ num = scan(/\d+/)
490
+ indent = num ? column + num.to_i - 1 : nil
491
+ unless scan(/[ \t]*(.*?)(\#.*)?\r?\n/) # /[ \t]*(\#.*)?\r?\n/
492
+ raise _syntax_error("Syntax Error (line break or comment are expected)", path)
493
+ end
494
+ s = group(1)
495
+ is_folded = false
496
+ while match?(/( *)(.*?)(\r?\n)/)
497
+ spaces = group(1)
498
+ text = group(2)
499
+ nl = group(3)
500
+ if indent.nil?
501
+ if spaces.length >= column
502
+ indent = spaces.length
503
+ elsif text.empty?
504
+ s << nl
505
+ scan(/.*?\n/)
506
+ next
507
+ else
508
+ @diagnostic = 'text indent in block text may be shorter than that of first line or specified column.'
509
+ break
510
+ end
511
+ else
512
+ if spaces.length < indent && !text.empty?
513
+ @diagnostic = 'text indent in block text may be shorter than that of first line or specified column.'
514
+ break
515
+ end
516
+ end
517
+ scan(/.*?\n/)
518
+ if indicator == '|'
519
+ s << spaces[indent..-1] if spaces.length >= indent
520
+ s << text << nl
521
+ else # indicator == '>'
522
+ if !text.empty? && spaces.length == indent
523
+ if s.sub!(/\r?\n((\r?\n)+)\z/, '\1')
524
+ nil
525
+ elsif is_folded
526
+ s.sub!(/\r?\n\z/, ' ')
527
+ end
528
+ #s.sub!(/\r?\n\z/, '') if !s.sub!(/\r?\n(\r?\n)+\z/, '\1') && is_folded
529
+ is_folded = true
530
+ else
531
+ is_folded = false
532
+ s << spaces[indent..-1] if spaces.length > indent
533
+ end
534
+ s << text << nl
535
+ end
536
+ end
537
+ ## chomping
538
+ if chomping == '+'
539
+ nil
540
+ elsif chomping == '-'
541
+ s.sub!(/(\r?\n)+\z/, '')
542
+ else
543
+ s.sub!(/(\r?\n)(\r?\n)+\z/, '\1')
544
+ end
545
+ #
546
+ skip_spaces_and_comments()
547
+ val = s
548
+ #_set_error_info(_linenum, _column) do #*V
549
+ # @validator._validate_unique(val, rule, path, @errors, uniq_table) #*V
550
+ #end if uniq_table #*V
551
+ return val
552
+ end
553
+
554
+
555
+ def parse_flow_value(rule, path, uniq_table, container)
556
+ skip_spaces_and_comments()
557
+ ## anchor and alias
558
+ name = nil
559
+ if scan(/\&([-\w]+)/)
560
+ name = parse_anchor(rule, path, uniq_table, container)
561
+ elsif scan(/\*([-\w]+)/)
562
+ return parse_alias(rule, path, uniq_table, container)
563
+ end
564
+ ## type
565
+ if scan(/!!?\w+/)
566
+ skip_spaces_and_comments()
567
+ end
568
+ ## sequence
569
+ if match?(/\[/)
570
+ if rule && !rule.sequence #*V
571
+ #_validate_error("sequence is not expected.", path) #*V
572
+ rule = nil #*V
573
+ end #*V
574
+ seq = create_sequence(rule, @linenum, @column)
575
+ @anchors[name] = seq if name
576
+ parse_flow_seq(seq, rule, path, uniq_table)
577
+ return seq
578
+ end
579
+ ## mapping
580
+ if match?(/\{/)
581
+ if rule && !rule.mapping #*V
582
+ #_validate_error("mapping is not expected.", path) #*V
583
+ rule = nil #*V
584
+ end #*V
585
+ map = create_mapping(rule, @linenum, @column)
586
+ @anchors[name] = map if name
587
+ parse_flow_map(map, rule, path, uniq_table)
588
+ return map
589
+ end
590
+ ## scalar
591
+ scalar = parse_flow_scalar(rule, path, uniq_table)
592
+ @anchors[name] = scalar if name
593
+ return scalar
594
+ end
595
+
596
+
597
+ def parse_flow_seq(seq, seq_rule, path, uniq_table)
598
+ #scan(/\[\s*/)
599
+ scan(/\[/)
600
+ skip_spaces_and_comments()
601
+ if scan(/\]/)
602
+ nil
603
+ else
604
+ rule = seq_rule ? seq_rule.sequence[0] : nil #*V
605
+ uniq_table = rule ? rule._uniqueness_check_table() : nil #*V
606
+ path.push(nil)
607
+ i = 0
608
+ while true
609
+ path[-1] = i
610
+ _linenum = @linenum #*V
611
+ _column = @column #*V
612
+ val = parse_flow_value(rule, path, uniq_table, seq)
613
+ add_to_seq(rule, seq, val, _linenum, _column) # seq << val
614
+ _set_error_info(_linenum, _column) do #*V
615
+ @validator._validate(val, rule, path, @errors, @done, uniq_table, false) #*V
616
+ end if rule && !val.equal?(PRECEDING_ALIAS_PLACEHOLDER) #*V
617
+ skip_spaces_and_comments()
618
+ break unless scan(/,\s+/)
619
+ i += 1
620
+ if match?(/\]/)
621
+ raise _syntax_error("sequence item required (or last comma is extra).", path)
622
+ end
623
+ end
624
+ path.pop()
625
+ unless scan(/\]/)
626
+ raise _syntax_error("flow sequence is not closed by ']'.", path)
627
+ end
628
+ end
629
+ skip_spaces_and_comments()
630
+ return seq
631
+ end
632
+
633
+
634
+ def parse_flow_map(map, map_rule, path, uniq_table)
635
+ #scan(/\{\s*/) # not work?
636
+ _start_linenum = @linenum #*V
637
+ _start_column = @column #*V
638
+ scan(/\{/)
639
+ skip_spaces_and_comments()
640
+ if scan(/\}/)
641
+ nil
642
+ else
643
+ path.push(nil)
644
+ is_merged = false
645
+ while true
646
+ _linenum = @linenum #*V
647
+ _column = @column #*V
648
+ unless scan(MAPKEY_PATTERN)
649
+ raise _syntax_error("mapping key is expected.", path)
650
+ end
651
+ key = group(1)
652
+ skip_spaces_and_comments()
653
+ _linenum2 = @linenum #*V
654
+ _column2 = @column #*V
655
+ is_merged = _parse_map_value(map, map_rule, path, nil, key, is_merged,
656
+ uniq_table, _linenum, _column, _linenum2, _column2)
657
+ #skip_spaces_and_comments()
658
+ break unless scan(/,\s+/)
659
+ end
660
+ path.pop()
661
+ unless scan(/\}/)
662
+ raise _syntax_error("flow mapping is not closed by '}'.", path)
663
+ end
664
+ end
665
+ skip_spaces_and_comments()
666
+ _set_error_info(_start_linenum, _start_column) do #*V
667
+ @validator._validate_mapping_required_keys(map, map_rule, path, @errors) #*V
668
+ end if map_rule #*V
669
+ return map
670
+ end
671
+
672
+
673
+ def parse_flow_scalar(rule, path, uniq_table)
674
+ ch = peep(1)
675
+ _linenum = @linenum #*V
676
+ _column = @column #*V
677
+ if ch == '"' || ch == "'"
678
+ val = scan_string()
679
+ else
680
+ str = scan(/[^,\]\}\#]*/)
681
+ if match?(/,\S/)
682
+ while match?(/,\S/)
683
+ str << scan(/./)
684
+ str << scan(/[^,\]\}\#]*/)
685
+ end
686
+ end
687
+ str.rstrip!
688
+ val = to_scalar(str)
689
+ end
690
+ val = create_scalar(rule, val, _linenum, _column) #*V
691
+ #_set_error_info(_linenum, _column) do #*V
692
+ # @validator._validate_unique(val, rule, path, @errors, uniq_table) #*V
693
+ #end if uniq_table #*V
694
+ skip_spaces_and_comments()
695
+ return val
696
+ end
697
+
698
+
699
+ ####
700
+
701
+
702
+ def to_scalar(str)
703
+ case str
704
+ when nil ; val = nil
705
+ when /\A-?\d+\.\d+\z/ ; val = str.to_f
706
+ when /\A-?\d+\z/ ; val = str.to_i
707
+ when /\A(true|yes)\z/ ; val = true
708
+ when /\A(false|no)\z/ ; val = false
709
+ when /\A(null|~)\z/ ; val = nil
710
+ when /\A"(.*)"\z/ ; val = $1
711
+ when /\A'(.*)'\z/ ; val = $1
712
+ when /\A:(\w+)\z/ ; val = $1.intern
713
+ when /\A(\d\d\d\d)-(\d\d)-(\d\d)(?: (\d\d):(\d\d):(\d\d))?\z/
714
+ year, month, day, hour, min, sec = $1, $2, $3, $4, $5, $6
715
+ if hour
716
+ val = Time.mktime(year, month, day, hour, min, sec)
717
+ else
718
+ val = Date.new(year.to_i, month.to_i, day.to_i)
719
+ end
720
+ ## or
721
+ #params = [$1, $2, $3, $4, $5, $6]
722
+ #val = Time.mktime(*params)
723
+ else
724
+ val = str.empty? ? nil : str
725
+ end
726
+ skip_spaces_and_comments()
727
+ return val
728
+ end
729
+
730
+
731
+ ##
732
+
733
+ protected
734
+
735
+
736
+ def create_sequence(rule, linenum, column)
737
+ seq = @sequence_class.new
738
+ @location_table[seq.__id__] = []
739
+ return seq
740
+ end
741
+
742
+
743
+ def create_mapping(rule, linenum, column)
744
+ if rule && rule.classobj && @data_binding
745
+ classobj = rule.classobj
746
+ map = classobj.new
747
+ else
748
+ classobj = nil
749
+ map = @mapping_class.new # {}
750
+ end
751
+ @location_table[map.__id__] = hash = {}
752
+ hash[:classobj] = classobj if classobj
753
+ return map
754
+ end
755
+
756
+
757
+ def create_scalar(rule, value, linenum, column)
758
+ return value
759
+ end
760
+
761
+
762
+ def add_to_seq(rule, seq, val, linenum, column)
763
+ seq << val
764
+ @location_table[seq.__id__] << [linenum, column]
765
+ end
766
+
767
+
768
+ def put_to_map(rule, map, key, val, linenum, column)
769
+ #if map.is_a?(Hash)
770
+ # map[key] = val
771
+ #elsif map.respond_to?(name="#{key}=")
772
+ # map.__send__(name, val)
773
+ #elsif map.respond_to?('[]=')
774
+ # map[key] = val
775
+ #else
776
+ # map.instance_variable_set("@#{key}", val)
777
+ #end
778
+ map[key] = val
779
+ @location_table[map.__id__][key] = [linenum, column]
780
+ end
781
+
782
+
783
+ def _getclass(classname)
784
+ mod = Object
785
+ classname.split(/::/).each do |modname|
786
+ mod = mod.const_get(modname) # raises NameError when module not found
787
+ end
788
+ return mod
789
+ end
790
+
791
+
792
+ public
793
+
794
+
795
+ def location(path)
796
+ if path.empty? || path == '/'
797
+ return @location_table[-1] # return value is [linenum, column]
798
+ end
799
+ if path.is_a?(Array)
800
+ items = path.collect { |item| to_scalar(item) }
801
+ elsif path.is_a?(String)
802
+ items = path.split('/').collect { |item| to_scalar(item) }
803
+ items.shift if path[0] == ?/ # delete empty string on head
804
+ else
805
+ raise ArgumentError.new("path should be Array or String.")
806
+ end
807
+ last_item = items.pop()
808
+ c = @doc # collection
809
+ items.each do |item|
810
+ if c.is_a?(Array)
811
+ c = c[item.to_i]
812
+ elsif c.is_a?(Hash)
813
+ c = c[item]
814
+ elsif (table = @location_table[c.__id__]) && table[:classobj]
815
+ if c.respond_to?(item)
816
+ c = c.__send__(item)
817
+ elsif c.respond_to?("[]=")
818
+ c = c[item]
819
+ else
820
+ assert false
821
+ end
822
+ else
823
+ #assert false
824
+ raise ArgumentError.new("#{path.inspect}: invalid path.")
825
+ end
826
+ end
827
+ collection = @location_table[c.__id__]
828
+ return nil if collection.nil?
829
+ index = c.is_a?(Array) ? last_item.to_i : last_item
830
+ return collection[index] # return value is [linenum, column]
831
+ end
832
+
833
+
834
+ def set_errors_linenum(errors)
835
+ errors.each do |error|
836
+ error.linenum, error.column = location(error.path)
837
+ end
838
+ end
839
+
840
+
841
+ end