dc-kwalify 0.7.2

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.
Files changed (209) hide show
  1. data/CHANGES.txt +252 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.txt +61 -0
  4. data/bin/kwalify +13 -0
  5. data/contrib/inline-require +179 -0
  6. data/contrib/kwalify +4160 -0
  7. data/doc/docstyle.css +188 -0
  8. data/doc/img/fig01.png +0 -0
  9. data/doc/users-guide.html +2050 -0
  10. data/doc-api/classes/CommandOptionError.html +184 -0
  11. data/doc-api/classes/CommandOptionParser.html +325 -0
  12. data/doc-api/classes/Kwalify/AssertionError.html +148 -0
  13. data/doc-api/classes/Kwalify/BaseError.html +297 -0
  14. data/doc-api/classes/Kwalify/BaseParser.html +461 -0
  15. data/doc-api/classes/Kwalify/CommandOptionError.html +168 -0
  16. data/doc-api/classes/Kwalify/ErrorHelper.html +223 -0
  17. data/doc-api/classes/Kwalify/HashInterface.html +118 -0
  18. data/doc-api/classes/Kwalify/Json.html +105 -0
  19. data/doc-api/classes/Kwalify/KwalifyError.html +111 -0
  20. data/doc-api/classes/Kwalify/Main.html +339 -0
  21. data/doc-api/classes/Kwalify/MetaValidator.html +448 -0
  22. data/doc-api/classes/Kwalify/Parser.html +155 -0
  23. data/doc-api/classes/Kwalify/PlainYamlParser/Alias.html +165 -0
  24. data/doc-api/classes/Kwalify/PlainYamlParser.html +523 -0
  25. data/doc-api/classes/Kwalify/Rule.html +433 -0
  26. data/doc-api/classes/Kwalify/SchemaError.html +148 -0
  27. data/doc-api/classes/Kwalify/SyntaxError.html +185 -0
  28. data/doc-api/classes/Kwalify/Types.html +302 -0
  29. data/doc-api/classes/Kwalify/Util/HashLike.html +246 -0
  30. data/doc-api/classes/Kwalify/Util/OrderedHash.html +330 -0
  31. data/doc-api/classes/Kwalify/Util.html +390 -0
  32. data/doc-api/classes/Kwalify/ValidationError.html +148 -0
  33. data/doc-api/classes/Kwalify/Validator.html +381 -0
  34. data/doc-api/classes/Kwalify/Yaml/Parser.html +1538 -0
  35. data/doc-api/classes/Kwalify/Yaml.html +194 -0
  36. data/doc-api/classes/Kwalify/YamlParser.html +542 -0
  37. data/doc-api/classes/Kwalify/YamlSyntaxError.html +119 -0
  38. data/doc-api/classes/Kwalify.html +292 -0
  39. data/doc-api/classes/Test/Unit.html +101 -0
  40. data/doc-api/classes/Test.html +107 -0
  41. data/doc-api/created.rid +1 -0
  42. data/doc-api/files/__/README_txt.html +172 -0
  43. data/doc-api/files/kwalify/errors_rb.html +114 -0
  44. data/doc-api/files/kwalify/main_rb.html +118 -0
  45. data/doc-api/files/kwalify/messages_rb.html +107 -0
  46. data/doc-api/files/kwalify/meta-validator_rb.html +117 -0
  47. data/doc-api/files/kwalify/parser/base_rb.html +116 -0
  48. data/doc-api/files/kwalify/parser/yaml_rb.html +117 -0
  49. data/doc-api/files/kwalify/rule_rb.html +116 -0
  50. data/doc-api/files/kwalify/types_rb.html +114 -0
  51. data/doc-api/files/kwalify/util/assert-text-equal_rb.html +115 -0
  52. data/doc-api/files/kwalify/util/hash-interface_rb.html +114 -0
  53. data/doc-api/files/kwalify/util/hashlike_rb.html +107 -0
  54. data/doc-api/files/kwalify/util/option-parser_rb.html +107 -0
  55. data/doc-api/files/kwalify/util/ordered-hash_rb.html +107 -0
  56. data/doc-api/files/kwalify/util/testcase-helper_rb.html +115 -0
  57. data/doc-api/files/kwalify/util_rb.html +107 -0
  58. data/doc-api/files/kwalify/validator_rb.html +117 -0
  59. data/doc-api/files/kwalify/yaml-parser_rb.html +117 -0
  60. data/doc-api/files/kwalify_rb.html +121 -0
  61. data/doc-api/fr_class_index.html +57 -0
  62. data/doc-api/fr_file_index.html +45 -0
  63. data/doc-api/fr_method_index.html +168 -0
  64. data/doc-api/index.html +24 -0
  65. data/doc-api/rdoc-style.css +208 -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/errors.rb +127 -0
  82. data/lib/kwalify/kwalify.schema.yaml +58 -0
  83. data/lib/kwalify/main.rb +442 -0
  84. data/lib/kwalify/messages.rb +173 -0
  85. data/lib/kwalify/meta-validator.rb +275 -0
  86. data/lib/kwalify/parser/base.rb +127 -0
  87. data/lib/kwalify/parser/yaml.rb +841 -0
  88. data/lib/kwalify/rule.rb +559 -0
  89. data/lib/kwalify/templates/genclass-java.eruby +222 -0
  90. data/lib/kwalify/templates/genclass-php.eruby +104 -0
  91. data/lib/kwalify/templates/genclass-ruby.eruby +113 -0
  92. data/lib/kwalify/types.rb +156 -0
  93. data/lib/kwalify/util/assert-text-equal.rb +46 -0
  94. data/lib/kwalify/util/hash-interface.rb +18 -0
  95. data/lib/kwalify/util/hashlike.rb +51 -0
  96. data/lib/kwalify/util/option-parser.rb +220 -0
  97. data/lib/kwalify/util/ordered-hash.rb +57 -0
  98. data/lib/kwalify/util/testcase-helper.rb +112 -0
  99. data/lib/kwalify/util.rb +158 -0
  100. data/lib/kwalify/validator.rb +282 -0
  101. data/lib/kwalify/yaml-parser.rb +870 -0
  102. data/lib/kwalify.rb +67 -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 +415 -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-util.rb +125 -0
  204. data/test/test-validator.rb +95 -0
  205. data/test/test-validator.yaml +986 -0
  206. data/test/test-yaml-parser.rb +47 -0
  207. data/test/test-yaml-parser.yaml +1226 -0
  208. data/test/test.rb +61 -0
  209. metadata +274 -0
@@ -0,0 +1,870 @@
1
+ ###
2
+ ### $Rev$
3
+ ### $Release: 0.7.2 $
4
+ ### copyright(c) 2005-2010 kuwata-lab all rights reserved.
5
+ ###
6
+
7
+ require 'kwalify/messages'
8
+ require 'kwalify/errors'
9
+ require 'kwalify/types'
10
+
11
+ require 'date'
12
+
13
+
14
+ module Kwalify
15
+
16
+ ##
17
+ ## base class of yaml parser
18
+ ##
19
+ ## ex.
20
+ ## str = ARGF.read()
21
+ ## parser = Kwalify::PlainYamlParser.new(str)
22
+ ## doc = parser.parse()
23
+ ## p doc
24
+ ##
25
+ class PlainYamlParser
26
+
27
+ class Alias
28
+ def initialize(label, linenum)
29
+ @label = label
30
+ @linenum = linenum
31
+ end
32
+ attr_reader :label, :linenum
33
+ end
34
+
35
+
36
+ def initialize(yaml_str)
37
+ @lines = yaml_str.to_a()
38
+ @line = nil
39
+ @linenum = 0
40
+ @anchors = {}
41
+ @aliases = {}
42
+ end
43
+
44
+
45
+ def parse()
46
+ data = parse_child(0)
47
+ if data.nil? && @end_flag == '---'
48
+ data = parse_child(0)
49
+ end
50
+ resolve_aliases(data) unless @aliases.empty?
51
+ return data
52
+ end
53
+
54
+
55
+ def has_next?
56
+ return @end_flag != 'EOF'
57
+ end
58
+
59
+
60
+ def parse_all
61
+ list = []
62
+ while has_next()
63
+ doc = parse()
64
+ list << doc
65
+ end
66
+ return list
67
+ end
68
+
69
+
70
+ protected
71
+
72
+
73
+ def create_sequence(linenum=nil)
74
+ return []
75
+ end
76
+
77
+ def add_to_seq(seq, value, linenum)
78
+ seq << value
79
+ end
80
+
81
+ def set_seq_at(seq, i, value, linenum)
82
+ seq[i] = value
83
+ end
84
+
85
+ def create_mapping(linenum=nil)
86
+ return {}
87
+ end
88
+
89
+ def add_to_map(map, key, value, linenum)
90
+ map[key] = value
91
+ end
92
+
93
+ def set_map_with(map, key, value, linenum)
94
+ map[key] = value
95
+ end
96
+
97
+ def set_default(map, value, linenum)
98
+ map.value = value
99
+ end
100
+
101
+ def merge_map(map, map2, linenum)
102
+ map2.each do |key, val|
103
+ map[key] = value unless map.key?(key)
104
+ end
105
+ end
106
+
107
+ def create_scalar(value, linenum=nil)
108
+ return value
109
+ end
110
+
111
+
112
+ def current_line
113
+ return @line
114
+ end
115
+
116
+ def current_linenum
117
+ return @linenum
118
+ end
119
+
120
+
121
+ private
122
+
123
+
124
+ def getline
125
+ line = _getline()
126
+ line = _getline() while line && line =~ /^\s*($|\#)/
127
+ return line
128
+ end
129
+
130
+ def _getline
131
+ @line = @lines[@linenum]
132
+ @linenum += 1
133
+ case @line
134
+ when nil ; @end_flag = 'EOF'
135
+ when /^\.\.\.$/ ; @end_flag = '...'; @line = nil
136
+ when /^---(\s+.*)?$/ ; @end_flag = '---'; @line = nil
137
+ else ; @end_flag = nil
138
+ end
139
+ return @line
140
+ end
141
+
142
+
143
+ def reset_sbuf(str)
144
+ @sbuf = str[-1] == ?\n ? str : str + "\n"
145
+ @index = -1
146
+ end
147
+
148
+
149
+ def _getchar
150
+ @index += 1
151
+ ch = @sbuf[@index]
152
+ while ch.nil?
153
+ break if (line = getline()).nil?
154
+ reset_sbuf(line)
155
+ @index += 1
156
+ ch = @sbuf[@index]
157
+ end
158
+ return ch
159
+ end
160
+
161
+ def getchar
162
+ ch = _getchar()
163
+ ch = _getchar() while ch && white?(ch)
164
+ return ch
165
+ end
166
+
167
+ def getchar_or_nl
168
+ ch = _getchar()
169
+ ch = _getchar() while ch && white?(ch) && ch != ?\n
170
+ return ch
171
+ end
172
+
173
+ def current_char
174
+ return @sbuf[@index]
175
+ end
176
+
177
+ def getlabel
178
+ if @sbuf[@index..-1] =~ /\A\w[-\w]*/
179
+ label = $&
180
+ @index += label.length
181
+ else
182
+ label = nil
183
+ end
184
+ return label
185
+ end
186
+
187
+ #--
188
+ #def syntax_error(error_symbol, linenum=@linenum)
189
+ # msg = Kwalify.msg(error_symbol) % [linenum]
190
+ # return Kwalify::YamlSyntaxError.new(msg, linenum,error_symbol)
191
+ #end
192
+ #++
193
+ def syntax_error(error_symbol, arg=nil, linenum=@linenum)
194
+ msg = Kwalify.msg(error_symbol)
195
+ msg = msg % arg.to_a unless arg.nil?
196
+ return Kwalify::YamlSyntaxError.new(msg, linenum, error_symbol)
197
+ end
198
+
199
+ def parse_child(column)
200
+ line = getline()
201
+ return create_scalar(nil) if !line
202
+ line =~ /^( *)(.*)/
203
+ indent = $1.length
204
+ return create_scalar(nil) if indent < column
205
+ value = $2
206
+ return parse_value(column, value, indent)
207
+ end
208
+
209
+
210
+ def parse_value(column, value, value_start_column)
211
+ case value
212
+ when /^-( |$)/
213
+ data = parse_sequence(value_start_column, value)
214
+ when /^(:?:?[-.\w]+\*?|'.*?'|".*?"|=|<<) *:( |$)/
215
+ #when /^:?["']?[-.\w]+["']? *:( |$)/ #'
216
+ data = parse_mapping(value_start_column, value)
217
+ when /^\[/, /^\{/
218
+ data = parse_flowstyle(column, value)
219
+ when /^\&[-\w]+( |$)/
220
+ data = parse_anchor(column, value)
221
+ when /^\*[-\w]+( |$)/
222
+ data = parse_alias(column, value)
223
+ when /^[|>]/
224
+ data = parse_block_text(column, value)
225
+ when /^!/
226
+ data = parse_tag(column, value)
227
+ when /^\#/
228
+ data = parse_child(column)
229
+ else
230
+ data = parse_scalar(column, value)
231
+ end
232
+ return data
233
+ end
234
+
235
+ def white?(ch)
236
+ return ch == ?\ || ch == ?\t || ch == ?\n || ch == ?\r
237
+ end
238
+
239
+
240
+ ##
241
+ ## flowstyle ::= flow_seq | flow_map | flow_scalar | alias
242
+ ##
243
+ ## flow_seq ::= '[' [ flow_seq_item { ',' sp flow_seq_item } ] ']'
244
+ ## flow_seq_item ::= flowstyle
245
+ ##
246
+ ## flow_map ::= '{' [ flow_map_item { ',' sp flow_map_item } ] '}'
247
+ ## flow_map_item ::= flowstyle ':' sp flowstyle
248
+ ##
249
+ ## flow_scalar ::= string | number | boolean | symbol | date
250
+ ##
251
+
252
+ def parse_flowstyle(column, value)
253
+ reset_sbuf(value)
254
+ getchar()
255
+ data = parse_flow(0)
256
+ ch = current_char
257
+ assert ch == ?] || ch == ?}
258
+ ch = getchar_or_nl()
259
+ unless ch == ?\n || ch == ?# || ch.nil?
260
+ #* key=:flow_hastail msg="flow style sequence is closed but got '%s'."
261
+ raise syntax_error(:flow_hastail, [ch.chr])
262
+ end
263
+ getline() if !ch.nil?
264
+ return data
265
+ end
266
+
267
+ def parse_flow(depth)
268
+ ch = current_char()
269
+ #ch = getchar()
270
+ if ch.nil?
271
+ #* key=:flow_eof msg="found EOF when parsing flow style."
272
+ rase syntax_error(:flow_eof)
273
+ end
274
+ ## alias
275
+ if ch == ?*
276
+ _getchar()
277
+ label = getlabel()
278
+ unless label
279
+ #* key=:flow_alias_label msg="alias name expected."
280
+ rase syntax_error(:flow_alias_label)
281
+ end
282
+ data = @anchors[label]
283
+ unless data
284
+ data = register_alias(label)
285
+ #raise syntax_error("anchor '#{label}' not found (cannot refer to backward or child anchor).")
286
+ end
287
+ return data
288
+ end
289
+ ## anchor
290
+ label = nil
291
+ if ch == ?&
292
+ _getchar()
293
+ label = getlabel()
294
+ unless label
295
+ #* key=:flow_anchor_label msg="anchor name expected."
296
+ rase syntax_error(:flow_anchor_label)
297
+ end
298
+ ch = current_char()
299
+ ch = getchar() if white?(ch)
300
+ end
301
+ ## flow data
302
+ if ch == ?[
303
+ data = parse_flow_seq(depth)
304
+ elsif ch == ?{
305
+ data = parse_flow_map(depth)
306
+ else
307
+ data = parse_flow_scalar(depth)
308
+ end
309
+ ## register anchor
310
+ register_anchor(label, data) if label
311
+ return data
312
+ end
313
+
314
+ def parse_flow_seq(depth)
315
+ assert current_char() == ?[
316
+ seq = create_sequence() # []
317
+ ch = getchar()
318
+ if ch != ?}
319
+ linenum = current_linenum()
320
+ #seq << parse_flow_seq_item(depth + 1)
321
+ add_to_seq(seq, parse_flow_seq_item(depth + 1), linenum)
322
+ while (ch = current_char()) == ?,
323
+ ch = getchar()
324
+ if ch == ?]
325
+ #* key=:flow_noseqitem msg="sequence item required (or last comma is extra)."
326
+ raise syntax_error(:flow_noseqitem)
327
+ end
328
+ #break if ch == ?]
329
+ linenum = current_linenum()
330
+ #seq << parse_flow_seq_item(depth + 1)
331
+ add_to_seq(seq, parse_flow_seq_item(depth + 1), linenum)
332
+ end
333
+ end
334
+ unless current_char() == ?]
335
+ #* key=:flow_seqnotclosed msg="flow style sequence requires ']'."
336
+ raise syntax_error(:flow_seqnotclosed)
337
+ end
338
+ getchar() if depth > 0
339
+ return seq
340
+ end
341
+
342
+ def parse_flow_seq_item(depth)
343
+ return parse_flow(depth)
344
+ end
345
+
346
+ def parse_flow_map(depth)
347
+ assert current_char() == ?{ #}
348
+ map = create_mapping() # {}
349
+ ch = getchar()
350
+ if ch != ?}
351
+ linenum = current_linenum()
352
+ key, value = parse_flow_map_item(depth + 1)
353
+ #map[key] = value
354
+ add_to_map(map, key, value, linenum)
355
+ while (ch = current_char()) == ?,
356
+ ch = getchar()
357
+ if ch == ?}
358
+ #* key=:flow_mapnoitem msg="mapping item required (or last comma is extra)."
359
+ raise syntax_error(:flow_mapnoitem)
360
+ end
361
+ #break if ch == ?}
362
+ linenum = current_linenum()
363
+ key, value = parse_flow_map_item(depth + 1)
364
+ #map[key] = value
365
+ add_to_map(map, key, value, linenum)
366
+ end
367
+ end
368
+ unless current_char() == ?}
369
+ #* key=:flow_mapnotclosed msg="flow style mapping requires '}'."
370
+ raise syntax_error(:flow_mapnotclosed)
371
+ end
372
+ getchar() if depth > 0
373
+ return map
374
+ end
375
+
376
+ def parse_flow_map_item(depth)
377
+ key = parse_flow(depth)
378
+ unless (ch = current_char()) == ?:
379
+ $stderr.puts "*** debug: key=#{key.inspect}"
380
+ s = ch ? "'#{ch.chr}'" : "EOF"
381
+ #* key=:flow_nocolon msg="':' expected but got %s."
382
+ raise syntax_error(:flow_nocolon, [s])
383
+ end
384
+ getchar()
385
+ value = parse_flow(depth)
386
+ return key, value
387
+ end
388
+
389
+ def parse_flow_scalar(depth)
390
+ case ch = current_char()
391
+ when ?", ?' #"
392
+ endch = ch
393
+ s = ''
394
+ while (ch = _getchar()) != nil && ch != endch
395
+ if ch == ?\\
396
+ ch = _getchar()
397
+ if ch.nil?
398
+ #* key=:flow_str_notclosed msg="%s: string not closed."
399
+ raise syntax_error(:flow_str_notclosed, endch == ?" ? "'\"'" : '"\'"')
400
+ end
401
+ if endch == ?"
402
+ case ch
403
+ when ?\\ ; s << "\\"
404
+ when ?" ; s << "\""
405
+ when ?n ; s << "\n"
406
+ when ?r ; s << "\r"
407
+ when ?t ; s << "\t"
408
+ when ?b ; s << "\b"
409
+ else ; s << "\\" << ch.chr
410
+ end
411
+ elsif endch == ?'
412
+ case ch
413
+ when ?\\ ; s << '\\'
414
+ when ?' ; s << '\''
415
+ else ; s << '\\' << ch.chr
416
+ end
417
+ end
418
+ else
419
+ s << ch.chr
420
+ end
421
+ end
422
+ getchar()
423
+ scalar = s
424
+ else
425
+ s = ch.chr
426
+ while (ch = _getchar()) != nil && ch != ?: && ch != ?, && ch != ?] && ch != ?}
427
+ s << ch.chr
428
+ end
429
+ scalar = to_scalar(s.strip)
430
+ end
431
+ return create_scalar(scalar)
432
+ end
433
+
434
+
435
+ def parse_tag(column, value)
436
+ assert value =~ /^!\S+/
437
+ value =~ /^!(\S+)((\s+)(.*))?$/
438
+ tag = $1
439
+ space = $3
440
+ value2 = $4
441
+ if value2 && !value2.empty?
442
+ value_start_column = column + 1 + tag.length + space.length
443
+ data = parse_value(column, value2, value_start_column)
444
+ else
445
+ data = parse_child(column)
446
+ end
447
+ return data
448
+ end
449
+
450
+
451
+ def parse_anchor(column, value)
452
+ assert value =~ /^\&([-\w]+)(( *)(.*))?$/
453
+ label = $1
454
+ space = $3
455
+ value2 = $4
456
+ if value2 && !value2.empty?
457
+ #column2 = column + 1 + label.length + space.length
458
+ #data = parse_value(column2, value2)
459
+ value_start_column = column + 1 + label.length + space.length
460
+ data = parse_value(column, value2, value_start_column)
461
+ else
462
+ #column2 = column + 1
463
+ #data = parse_child(column2)
464
+ data = parse_child(column)
465
+ end
466
+ register_anchor(label, data)
467
+ return data
468
+ end
469
+
470
+ def register_anchor(label, data)
471
+ if @anchors[label]
472
+ #* key=:anchor_duplicated msg="anchor '%s' is already used."
473
+ raise syntax_error(:anchor_duplicated, [label])
474
+ end
475
+ @anchors[label] = data
476
+ end
477
+
478
+ def parse_alias(column, value)
479
+ assert value =~ /^\*([-\w]+)(( *)(.*))?$/
480
+ label = $1
481
+ space = $3
482
+ value2 = $4
483
+ if value2 && !value2.empty? && value2[0] != ?\#
484
+ #* key=:alias_extradata msg="alias cannot take any data."
485
+ raise syntax_error(:alias_extradata)
486
+ end
487
+ data = @anchors[label]
488
+ unless data
489
+ data = register_alias(label)
490
+ #raise syntax_error("anchor '#{label}' not found (cannot refer to backward or child anchor).")
491
+ end
492
+ getline()
493
+ return data
494
+ end
495
+
496
+ def register_alias(label)
497
+ @aliases[label] ||= 0
498
+ @aliases[label] += 1
499
+ return Alias.new(label, @linenum)
500
+ end
501
+
502
+
503
+ def resolve_aliases(data)
504
+ @resolved ||= {}
505
+ return if @resolved[data.__id__]
506
+ @resolved[data.__id__] = data
507
+ case data
508
+ when Array
509
+ seq = data
510
+ seq.each_with_index do |val, i|
511
+ if val.is_a?(Alias)
512
+ anchor = val
513
+ if @anchors.key?(anchor.label)
514
+ #seq[i] = @anchors[anchor.label]
515
+ set_seq_at(seq, i, @anchors[anchor.label], anchor.linenum)
516
+ else
517
+ #* key=:anchor_notfound msg="anchor '%s' not found"
518
+ raise syntax_error(:anchor_notfound, [anchor.label], val.linenum)
519
+ end
520
+ elsif val.is_a?(Array) || val.is_a?(Hash)
521
+ resolve_aliases(val)
522
+ end
523
+ end
524
+ when Hash
525
+ map = data
526
+ map.each do |key, val|
527
+ if val.is_a?(Alias)
528
+ if @anchors.key?(val.label)
529
+ anchor = val
530
+ #map[key] = @anchors[anchor.label]
531
+ set_map_with(map, key, @anchors[anchor.label], anchor.linenum)
532
+ else
533
+ ## :anchor_notfound is already defined on above
534
+ raise syntax_error(:anchor_notfound, [val.label], val.linenum)
535
+ end
536
+ elsif val.is_a?(Array) || val.is_a?(Hash)
537
+ resolve_aliases(val)
538
+ end
539
+ end
540
+ else
541
+ assert !data.is_a?(Alias)
542
+ end
543
+ end
544
+
545
+
546
+ def parse_block_text(column, value)
547
+ assert value =~ /^[>|\|]/
548
+ value =~ /^([>|\|])([-+]?)(\d+)?\s*(.*)$/
549
+ char = $1
550
+ indicator = $2
551
+ sep = char == "|" ? "\n" : " "
552
+ margin = $3 && !$3.empty? ? $3.to_i : nil
553
+ #text = $4.empty? ? '' : $4 + sep
554
+ text = $4
555
+ s = ''
556
+ empty = ''
557
+ min_indent = -1
558
+ while line = _getline()
559
+ line =~ /^( *)(.*)/
560
+ indent = $1.length
561
+ if $2.empty?
562
+ empty << "\n"
563
+ elsif indent < column
564
+ break
565
+ else
566
+ min_indent = indent if min_indent < 0 || min_indent > indent
567
+ s << empty << line
568
+ empty = ''
569
+ end
570
+ end
571
+ s << empty if indicator == '+' && char != '>'
572
+ s[-1] = "" if indicator == '-'
573
+ min_indent = column + margin - 1 if margin
574
+ if min_indent > 0
575
+ sp = ' ' * min_indent
576
+ s.gsub!(/^#{sp}/, '')
577
+ end
578
+ if char == '>'
579
+ s.gsub!(/([^\n])\n([^\n])/, '\1 \2')
580
+ s.gsub!(/\n(\n+)/, '\1')
581
+ s << empty if indicator == '+'
582
+ end
583
+ getline() if current_line() =~ /^\s*\#/
584
+ return create_scalar(text + s)
585
+ end
586
+
587
+
588
+ def parse_sequence(column, value)
589
+ assert value =~ /^-(( +)(.*))?$/
590
+ seq = create_sequence() # []
591
+ while true
592
+ unless value =~ /^-(( +)(.*))?$/
593
+ #* key=:sequence_noitem msg="sequence item is expected."
594
+ raise syntax_error(:sequence_noitem)
595
+ end
596
+ value2 = $3
597
+ space = $2
598
+ column2 = column + 1
599
+ linenum = current_linenum()
600
+ #
601
+ if !value2 || value2.empty?
602
+ elem = parse_child(column2)
603
+ else
604
+ value_start_column = column2 + space.length
605
+ elem = parse_value(column2, value2, value_start_column)
606
+ end
607
+ add_to_seq(seq, elem, linenum) #seq << elem
608
+ #
609
+ line = current_line()
610
+ break unless line
611
+ line =~ /^( *)(.*)/
612
+ indent = $1.length
613
+ if indent < column
614
+ break
615
+ elsif indent > column
616
+ #* key=:sequence_badindent msg="illegal indent of sequence."
617
+ raise syntax_error(:sequence_badindent)
618
+ end
619
+ value = $2
620
+ end
621
+ return seq
622
+ end
623
+
624
+
625
+ def parse_mapping(column, value)
626
+ #assert value =~ /^(:?["']?[-.\w]+["']? *):(( +)(.*))?$/ #'
627
+ assert value =~ /^((?::?[-.\w]+\*?|'.*?'|".*?"|=|<<) *):(( +)(.*))?$/
628
+ map = create_mapping() # {}
629
+ while true
630
+ #unless value =~ /^(:?["']?[-.\w]+["']? *):(( +)(.*))?$/ #'
631
+ unless value =~ /^((?::?[-.\w]+\*?|'.*?'|".*?"|=|<<) *):(( +)(.*))?$/
632
+ #* key=:mapping_noitem msg="mapping item is expected."
633
+ raise syntax_error(:mapping_noitem)
634
+ end
635
+ v = $1.strip
636
+ key = to_scalar(v)
637
+ value2 = $4
638
+ column2 = column + 1
639
+ linenum = current_linenum()
640
+ #
641
+ if !value2 || value2.empty?
642
+ elem = parse_child(column2)
643
+ else
644
+ value_start_column = column2 + $1.length + $3.length
645
+ elem = parse_value(column2, value2, value_start_column)
646
+ end
647
+ case v
648
+ when '='
649
+ set_default(map, elem, linenum)
650
+ when '<<'
651
+ merge_map(map, elem, linenum)
652
+ else
653
+ add_to_map(map, key, elem, linenum) # map[key] = elem
654
+ end
655
+ #
656
+ line = current_line()
657
+ break unless line
658
+ line =~ /^( *)(.*)/
659
+ indent = $1.length
660
+ if indent < column
661
+ break
662
+ elsif indent > column
663
+ #* key=:mapping_badindent msg="illegal indent of mapping."
664
+ raise syntax_error(:mapping_badindent)
665
+ end
666
+ value = $2
667
+ end
668
+ return map
669
+ end
670
+
671
+
672
+ def parse_scalar(indent, value)
673
+ data = create_scalar(to_scalar(value))
674
+ getline()
675
+ return data
676
+ end
677
+
678
+
679
+ def to_scalar(str)
680
+ case str
681
+ when /^"(.*)"([ \t]*\#.*$)?/ ; return $1
682
+ when /^'(.*)'([ \t]*\#.*$)?/ ; return $1
683
+ when /^(.*\S)[ \t]*\#/ ; str = $1
684
+ end
685
+
686
+ case str
687
+ when /^-?\d+$/ ; return str.to_i # integer
688
+ when /^-?\d+\.\d+$/ ; return str.to_f # float
689
+ when "true", "yes", "on" ; return true # true
690
+ when "false", "no", "off" ; return false # false
691
+ when "null", "~" ; return nil # nil
692
+ #when /^"(.*)"$/ ; return $1 # "string"
693
+ #when /^'(.*)'$/ ; return $1 # 'string'
694
+ when /^:(\w+)$/ ; return $1.intern # :symbol
695
+ when /^(\d\d\d\d)-(\d\d)-(\d\d)$/ # date
696
+ year, month, day = $1.to_i, $2.to_i, $3.to_i
697
+ return Date.new(year, month, day)
698
+ when /^(\d\d\d\d)-(\d\d)-(\d\d)(?:[Tt]|[ \t]+)(\d\d?):(\d\d):(\d\d)(\.\d*)?(?:Z|[ \t]*([-+]\d\d?)(?::(\d\d))?)?$/
699
+ year, mon, mday, hour, min, sec, usec, tzone_h, tzone_m = $1, $2, $3, $4, $5, $6, $7, $8, $9
700
+ #Time.utc(sec, min, hour, mday, mon, year, wday, yday, isdst, zone)
701
+ #t = Time.utc(sec, min, hour, mday, mon, year, nil, nil, nil, nil)
702
+ #Time.utc(year[, mon[, day[, hour[, min[, sec[, usec]]]]]])
703
+ time = Time.utc(year, mon, day, hour, min, sec, usec)
704
+ if tzone_h
705
+ diff_sec = tzone_h.to_i * 60 * 60
706
+ if tzone_m
707
+ if diff_sec > 0 ; diff_sec += tzone_m.to_i * 60
708
+ else ; diff_sec -= tzone_m.to_i * 60
709
+ end
710
+ end
711
+ p diff_sec
712
+ time -= diff_sec
713
+ end
714
+ return time
715
+ end
716
+ return str
717
+ end
718
+
719
+
720
+ def assert(bool_expr)
721
+ raise "*** assertion error" unless bool_expr
722
+ end
723
+
724
+ end
725
+
726
+
727
+
728
+ ##
729
+ ## (OBSOLETE) yaml parser
730
+ ##
731
+ ## this class has been obsoleted. use Kwalify::Yaml::Parser instead.
732
+ ##
733
+ ## ex.
734
+ ## # load document with YamlParser
735
+ ## str = ARGF.read()
736
+ ## parser = Kwalify::YamlParser.new(str)
737
+ ## document = parser.parse()
738
+ ##
739
+ ## # validate document
740
+ ## schema = YAML.load(File.read('schema.yaml'))
741
+ ## validator = Kwalify::Validator.new(schema)
742
+ ## errors = validator.validate(document)
743
+ ##
744
+ ## # print validation result
745
+ ## if errors && !errors.empty?
746
+ ## parser.set_errors_linenum(errors)
747
+ ## errors.sort.each do |error|
748
+ ## print "line %d: path %s: %s" % [error.linenum, error.path, error.message]
749
+ ## end
750
+ ## end
751
+ ##
752
+ class YamlParser < PlainYamlParser
753
+
754
+ def initialize(*args)
755
+ super
756
+ @linenums_table = {} # object_id -> hash or array
757
+ end
758
+
759
+ def parse()
760
+ @doc = super()
761
+ return @doc
762
+ end
763
+
764
+ def path_linenum(path)
765
+ return 1 if path.empty? || path == '/'
766
+ elems = path.split('/')
767
+ elems.shift if path[0] == ?/ # delete empty string on head
768
+ last_elem = elems.pop
769
+ c = @doc # collection
770
+ elems.each do |elem|
771
+ if c.is_a?(Array)
772
+ c = c[elem.to_i]
773
+ elsif c.is_a?(Hash)
774
+ c = c[elem]
775
+ else
776
+ assert false
777
+ end
778
+ end
779
+ linenums = @linenums_table[c.__id__]
780
+ if c.is_a?(Array)
781
+ linenum = linenums[last_elem.to_i]
782
+ elsif c.is_a?(Hash)
783
+ linenum = linenums[last_elem]
784
+ end
785
+ return linenum
786
+ end
787
+
788
+ def set_errors_linenum(errors)
789
+ errors.each do |error|
790
+ error.linenum = path_linenum(error.path)
791
+ end
792
+ end
793
+
794
+ def set_error_linenums(errors)
795
+ warn "*** Kwalify::YamlParser#set_error_linenums() is obsolete. You should use set_errors_linenum() instead."
796
+ set_errors_linenum(errors)
797
+ end
798
+
799
+ protected
800
+
801
+ def create_sequence(linenum=current_linenum())
802
+ seq = []
803
+ @linenums_table[seq.__id__] = []
804
+ return seq
805
+ end
806
+
807
+ def add_to_seq(seq, value, linenum)
808
+ seq << value
809
+ @linenums_table[seq.__id__] << linenum
810
+ end
811
+
812
+ def set_seq_at(seq, i, value, linenum)
813
+ seq[i] = value
814
+ @linenums_table[seq.__id__][i] = linenum
815
+ end
816
+
817
+ def create_mapping(linenum=current_linenum())
818
+ map = {}
819
+ @linenums_table[map.__id__] = {}
820
+ return map
821
+ end
822
+
823
+ def add_to_map(map, key, value, linenum)
824
+ map[key] = value
825
+ @linenums_table[map.__id__][key] = linenum
826
+ end
827
+
828
+ def set_map_with(map, key, value, linenum)
829
+ map[key] = value
830
+ @linenums_table[map.__id__][key] = linenum
831
+ end
832
+
833
+ def set_default(map, value, linenum)
834
+ map.default = value
835
+ @linenums_table[map.__id__][:'='] = linenum
836
+ end
837
+
838
+ def merge_map(map, collection, linenum)
839
+ t = @linenums_table[map.__id__]
840
+ list = collection.is_a?(Array) ? collection : [ collection ]
841
+ list.each do |m|
842
+ t2 = @linenums_table[m.__id__]
843
+ m.each do |key, val|
844
+ unless map.key?(key)
845
+ map[key] = val
846
+ t[key] = t2[key]
847
+ end
848
+ end
849
+ end
850
+ end
851
+
852
+ def create_scalar(value, linenum=current_linenum())
853
+ data = super(value)
854
+ #return Scalar.new(data, linenum)
855
+ return data
856
+ end
857
+
858
+ end
859
+
860
+
861
+ ## alias of YamlParser class
862
+ class Parser < YamlParser
863
+ def initialize(yaml_str)
864
+ super(yaml_str)
865
+ #warn "*** class Kwalify::Parser is obsolete. Please use Kwalify::YamlParser instead."
866
+ end
867
+ end
868
+
869
+
870
+ end