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