zenml 1.0.1 → 1.1.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.
@@ -6,9 +6,9 @@ require 'rexml/document'
6
6
  include REXML
7
7
 
8
8
 
9
- class ZenithalParser
9
+ module ZenithalParserMethod
10
10
 
11
- TAG_START = "\\"
11
+ ELEMENT_START = "\\"
12
12
  MACRO_START = "&"
13
13
  ESCAPE_START = "`"
14
14
  ATTRIBUTE_START = "|"
@@ -19,262 +19,297 @@ class ZenithalParser
19
19
  ATTRIBUTE_SEPARATOR = ","
20
20
  CONTENT_START = "<"
21
21
  CONTENT_END = ">"
22
- BRACE_START = "{"
23
- BRACE_END = "}"
24
- BRACKET_START = "["
25
- BRACKET_END = "]"
26
- SLASH_START = "/"
27
- SLASH_END = "/"
22
+ SPECIAL_ELEMENT_STARTS = {:brace => "{", :bracket => "[", :slash => "/"}
23
+ SPECIAL_ELEMENT_ENDS = {:brace => "}", :bracket => "]", :slash => "/"}
28
24
  COMMENT_DELIMITER = "#"
29
- INSTRUCTION_MARK = "?"
30
- TRIM_MARK = "*"
31
- VERBAL_MARK = "~"
32
- MULTIPLE_MARK = "+"
33
25
  SYSTEM_INSTRUCTION_NAME = "zml"
34
- ENTITIES = {"amp" => "&", "lt" => "<", "gt" => ">", "apos" => "'", "quot" => "\"",
35
- "lcub" => "{", "rcub" => "}", "lbrace" => "{", "rbrace" => "}", "lsqb" => "[", "rsqb" => "]", "lbrack" => "[", "rbrack" => "]",
36
- "sol" => "/", "bsol" => "\\", "verbar" => "|", "vert" => "|", "grave" => "`", "num" => "#"}
37
- ESCAPES = ["&", "<", ">", "'", "\"", "{", "}", "[", "]", "/", "\\", "|", "`", "#"]
38
- VALID_START_CHARS = [0x3A, 0x41..0x5A, 0x5F, 0x61..0x7A, 0xC0..0xD6, 0xD8..0xF6, 0xF8..0x2FF, 0x370..0x37D, 0x37F..0x1FFF, 0x200C..0x200D,
39
- 0x2070..0x218F, 0x2C00..0x2FEF, 0x3001..0xD7FF, 0xF900..0xFDCF, 0xFDF0..0xFFFD, 0x10000..0xEFFFF]
40
- VALID_MIDDLE_CHARS = [0x2D, 0x2E, 0x30..0x39, 0xB7, 0x0300..0x036F, 0x203F..0x2040]
41
-
42
- attr_writer :brace_name
43
- attr_writer :bracket_name
44
- attr_writer :slash_name
45
-
46
- def initialize(source)
47
- @source = StringReader.new(source)
48
- @version = nil
49
- @brace_name = nil
50
- @bracket_name = nil
51
- @slash_name = nil
52
- @macros = {}
26
+ MARKS = {:instruction => "?", :trim => "*", :verbal => "~", :multiple => "+"}
27
+ ESCAPE_CHARS = ["&", "<", ">", "'", "\"", "{", "}", "[", "]", "/", "\\", "|", "`", "#"]
28
+ SPACE_CHARS = [0x20, 0x9, 0xD, 0xA]
29
+ VALID_FIRST_IDENTIFIER_CHARS = [
30
+ 0x3A, 0x5F,
31
+ 0x41..0x5A, 0x61..0x7A, 0xC0..0xD6, 0xD8..0xF6, 0xF8..0x2FF,
32
+ 0x370..0x37D, 0x37F..0x1FFF,
33
+ 0x200C..0x200D, 0x2070..0x218F, 0x2C00..0x2FEF,
34
+ 0x3001..0xD7FF, 0xF900..0xFDCF, 0xFDF0..0xFFFD, 0x10000..0xEFFFF
35
+ ]
36
+ VALID_MIDDLE_IDENTIFIER_CHARS = [
37
+ 0x2D, 0x2E, 0x3A, 0x5F, 0xB7,
38
+ 0x30..0x39,
39
+ 0x41..0x5A, 0x61..0x7A, 0xC0..0xD6, 0xD8..0xF6, 0xF8..0x2FF,
40
+ 0x300..0x36F,
41
+ 0x370..0x37D, 0x37F..0x1FFF,
42
+ 0x200C..0x200D, 0x2070..0x218F, 0x2C00..0x2FEF,
43
+ 0x203F..0x2040,
44
+ 0x3001..0xD7FF, 0xF900..0xFDCF, 0xFDF0..0xFFFD, 0x10000..0xEFFFF
45
+ ]
46
+
47
+ private
48
+
49
+ def parse_document
50
+ parser = Parser.build(self) do
51
+ document = Document.new
52
+ children = !parse_nodes(false)
53
+ !parse_eof
54
+ children.each do |child|
55
+ document.add(child)
56
+ end
57
+ next document
58
+ end
59
+ return parser
53
60
  end
54
61
 
55
- def parse
56
- document = Document.new
57
- children = parse_nodes
58
- children.each do |child|
59
- document.add(child)
62
+ def parse_nodes(verbal)
63
+ parser = Parser.build(self) do
64
+ parsers = [parse_text(verbal)]
65
+ unless verbal
66
+ parsers.push(parse_element, parse_line_comment, parse_block_comment)
67
+ @special_element_names.each do |kind, name|
68
+ parsers.push(parse_special_element(kind))
69
+ end
70
+ end
71
+ nodes = Nodes[]
72
+ raw_nodes = !parsers.inject(:|).many
73
+ raw_nodes.each do |raw_node|
74
+ nodes << raw_node
75
+ end
76
+ next nodes
60
77
  end
61
- return document
62
- end
63
-
64
- def parse_nodes(option = {}, in_slash = false)
65
- children = []
66
- while char = @source.read
67
- if char == TAG_START || char == MACRO_START
68
- @source.unread
69
- children.concat(parse_element)
70
- elsif @brace_name && char == BRACE_START
71
- @source.unread
72
- children << parse_brace
73
- elsif @bracket_name && char == BRACKET_START
74
- @source.unread
75
- children << parse_bracket
76
- elsif @slash_name && !in_slash && char == SLASH_START
77
- @source.unread
78
- children << parse_slash
79
- elsif char == COMMENT_DELIMITER
80
- @source.unread
81
- children << parse_comment
82
- elsif char == CONTENT_END || (@brace_name && char == BRACE_END) || (@bracket_name && char == BRACKET_END) || (@slash_name && char == SLASH_END)
83
- @source.unread
84
- break
85
- else
86
- @source.unread
87
- children << parse_text(option)
78
+ return parser
79
+ end
80
+
81
+ def parse_element
82
+ parser = Parser.build(self) do
83
+ start_char = !parse_char_any([ELEMENT_START, MACRO_START])
84
+ name = !parse_identifier
85
+ marks = !parse_marks
86
+ attributes = !parse_attributes.maybe || {}
87
+ children_list = !parse_children_list(marks.include?(:verbal))
88
+ if name == SYSTEM_INSTRUCTION_NAME
89
+ !parse_space
90
+ end
91
+ if start_char == MACRO_START
92
+ marks.push(:macro)
88
93
  end
94
+ next create_nodes(name, marks, attributes, children_list)
89
95
  end
90
- return children
96
+ return parser
91
97
  end
92
98
 
93
- def parse_verbal_nodes(option = {})
94
- children = []
95
- while char = @source.read
96
- next_char = @source.peek
97
- if char == CONTENT_END
98
- @source.unread
99
- break
100
- else
101
- @source.unread
102
- children << parse_verbal_text(option)
99
+ def parse_special_element(kind)
100
+ parser = Parser.build(self) do
101
+ unless @special_element_names[kind]
102
+ !parse_none
103
103
  end
104
+ !parse_char(SPECIAL_ELEMENT_STARTS[kind])
105
+ children = !parse_nodes(false)
106
+ !parse_char(SPECIAL_ELEMENT_ENDS[kind])
107
+ next create_nodes(@special_element_names[kind], [], {}, [children])
104
108
  end
105
- return children
109
+ return parser
106
110
  end
107
111
 
108
- def parse_element
109
- first_char = @source.read
110
- unless first_char == TAG_START || first_char == MACRO_START
111
- raise ZenithalParseError.new(@source)
112
+ def parse_marks
113
+ return parse_mark.many
114
+ end
115
+
116
+ def parse_mark
117
+ parsers = MARKS.map do |mark, query|
118
+ next parse_char(query).map{|_| mark}
112
119
  end
113
- name, option = parse_element_name
114
- if first_char == MACRO_START
115
- option[:macro] = true
120
+ return parsers.inject(:|)
121
+ end
122
+
123
+ def parse_attributes
124
+ parser = Parser.build(self) do
125
+ !parse_char(ATTRIBUTE_START)
126
+ first_attribute = !parse_attribute(false)
127
+ rest_attribtues = !parse_attribute(true).many
128
+ attributes = first_attribute.merge(*rest_attribtues)
129
+ !parse_char(ATTRIBUTE_END)
130
+ next attributes
131
+ end
132
+ return parser
133
+ end
134
+
135
+ def parse_attribute(comma)
136
+ parser = Parser.build(self) do
137
+ !parse_char(ATTRIBUTE_SEPARATOR) if comma
138
+ !parse_space
139
+ name = !parse_identifier
140
+ !parse_space
141
+ value = !parse_attribute_value.maybe || name
142
+ !parse_space
143
+ next {name => value}
144
+ end
145
+ return parser
146
+ end
147
+
148
+ def parse_attribute_value
149
+ parser = Parser.build(self) do
150
+ !parse_char(ATTRIBUTE_EQUAL)
151
+ !parse_space
152
+ value = !parse_quoted_string
153
+ next value
116
154
  end
117
- attributes = parse_attributes
118
- children_list = parse_children_list(option)
119
- nodes = create_nodes(name, attributes, children_list, option)
120
- return nodes
155
+ return parser
121
156
  end
122
157
 
123
- def parse_element_name
124
- name, marks, option = "", [], {}
125
- while char = @source.read
126
- if char == ATTRIBUTE_START || char == CONTENT_START || char == CONTENT_END || char =~ /\s/
127
- @source.unread
128
- break
129
- elsif char == INSTRUCTION_MARK || char == TRIM_MARK || char == VERBAL_MARK || char == MULTIPLE_MARK
130
- marks << char
131
- elsif name.empty? && marks.empty? && ZenithalParser.valid_start_char?(char)
132
- name << char
133
- elsif !name.empty? && marks.empty? && ZenithalParser.valid_char?(char)
134
- name << char
135
- else
136
- raise ZenithalParseError.new(@source)
137
- end
158
+ def parse_quoted_string
159
+ parser = Parser.build(self) do
160
+ !parse_char(ATTRIBUTE_VALUE_START)
161
+ texts = !(parse_quoted_string_plain | parse_escape).many
162
+ !parse_char(ATTRIBUTE_VALUE_END)
163
+ next texts.join
138
164
  end
139
- skip_spaces
140
- if marks.include?(INSTRUCTION_MARK)
141
- option[:instruction] = true
165
+ return parser
166
+ end
167
+
168
+ def parse_quoted_string_plain
169
+ parser = Parser.build(self) do
170
+ chars = !parse_char_out([ATTRIBUTE_VALUE_END, ESCAPE_START]).many(1)
171
+ next chars.join
142
172
  end
143
- if marks.include?(TRIM_MARK)
144
- option[:trim_indents] = true
173
+ return parser
174
+ end
175
+
176
+ def parse_children_list(verbal)
177
+ parser = Parser.build(self) do
178
+ first_children = !(parse_empty_children | parse_children(verbal))
179
+ rest_children_list = !parse_children(verbal).many
180
+ children_list = [first_children] + rest_children_list
181
+ next children_list
182
+ end
183
+ return parser
184
+ end
185
+
186
+ def parse_children(verbal)
187
+ parser = Parser.build(self) do
188
+ !parse_char(CONTENT_START)
189
+ children = !parse_nodes(verbal)
190
+ !parse_char(CONTENT_END)
191
+ next children
145
192
  end
146
- if marks.include?(VERBAL_MARK)
147
- option[:verbal] = true
193
+ return parser
194
+ end
195
+
196
+ def parse_empty_children
197
+ parser = Parser.build(self) do
198
+ !parse_char(CONTENT_END)
199
+ next Nodes[]
148
200
  end
149
- if marks.include?(MULTIPLE_MARK)
150
- option[:multiple] = true
201
+ return parser
202
+ end
203
+
204
+ def parse_text(verbal)
205
+ parser = Parser.build(self) do
206
+ texts = !(parse_text_plain(verbal) | parse_escape).many(1)
207
+ next Text.new(texts.join, true, nil, false)
151
208
  end
152
- return name, option
209
+ return parser
153
210
  end
154
211
 
155
- def parse_attributes
156
- attributes = {}
157
- if @source.read == ATTRIBUTE_START
158
- current_key = nil
159
- skip_spaces
160
- loop do
161
- key, value = parse_attribute
162
- attributes[key] = value
163
- char = @source.read
164
- if char == ATTRIBUTE_SEPARATOR
165
- skip_spaces
166
- elsif char == ATTRIBUTE_END
167
- @source.unread
168
- break
169
- else
170
- raise ZenithalParseError.new(@source)
212
+ def parse_text_plain(verbal)
213
+ parser = Parser.build(self) do
214
+ out_chars = [ESCAPE_START, CONTENT_END]
215
+ unless verbal
216
+ out_chars.push(ELEMENT_START, MACRO_START, CONTENT_START, COMMENT_DELIMITER)
217
+ @special_element_names.each do |kind, name|
218
+ out_chars.push(SPECIAL_ELEMENT_STARTS[kind], SPECIAL_ELEMENT_ENDS[kind]) if name
171
219
  end
172
220
  end
173
- unless @source.read == ATTRIBUTE_END
174
- raise ZenithalParseError.new(@source)
175
- end
176
- else
177
- @source.unread
221
+ chars = !parse_char_out(out_chars).many(1)
222
+ next chars.join
178
223
  end
179
- return attributes
224
+ return parser
180
225
  end
181
226
 
182
- def parse_attribute
183
- key = parse_attribute_key
184
- skip_spaces
185
- if @source.read == ATTRIBUTE_EQUAL
186
- skip_spaces
187
- value = parse_attribute_value
188
- else
189
- @source.unread
190
- value = key
227
+ def parse_line_comment
228
+ parser = Parser.build(self) do
229
+ !parse_char(COMMENT_DELIMITER)
230
+ !parse_char(COMMENT_DELIMITER)
231
+ content = !parse_line_comment_content
232
+ next Comment.new(" " + content.strip + " ")
191
233
  end
192
- skip_spaces
193
- return key, value
194
- end
195
-
196
- def parse_attribute_key
197
- key = ""
198
- while char = @source.read
199
- if char == ATTRIBUTE_EQUAL || char == ATTRIBUTE_END || char =~ /\s/
200
- @source.unread
201
- break
202
- elsif key.empty? && ZenithalParser.valid_start_char?(char)
203
- key << char
204
- elsif !key.empty? && ZenithalParser.valid_char?(char)
205
- key << char
206
- else
207
- raise ZenithalParseError.new(@source)
208
- end
234
+ return parser
235
+ end
236
+
237
+ def parse_line_comment_content
238
+ parser = Parser.build(self) do
239
+ chars = !parse_char_out(["\n"]).many
240
+ !parse_char("\n")
241
+ next chars.join
209
242
  end
210
- return key
243
+ return parser
211
244
  end
212
245
 
213
- def parse_attribute_value
214
- unless @source.read == ATTRIBUTE_VALUE_START
215
- raise ZenithalParseError.new(@source)
246
+ def parse_block_comment
247
+ parser = Parser.build(self) do
248
+ !parse_char(COMMENT_DELIMITER)
249
+ !parse_char(CONTENT_START)
250
+ content = !parse_block_comment_content
251
+ !parse_char(CONTENT_END)
252
+ !parse_char(COMMENT_DELIMITER)
253
+ next Comment.new(" " + content.strip + " ")
216
254
  end
217
- value = ""
218
- while char = @source.read
219
- next_char = @source.peek
220
- if char == ATTRIBUTE_VALUE_END
221
- break
222
- elsif char == ESCAPE_START && ESCAPES.include?(next_char)
223
- @source.unread
224
- value << parse_escape_string
225
- else
226
- value << char
227
- end
255
+ return parser
256
+ end
257
+
258
+ def parse_block_comment_content
259
+ parser = Parser.build(self) do
260
+ chars = !parse_char_out([CONTENT_END]).many
261
+ next chars.join
228
262
  end
229
- return value
230
- end
231
-
232
- def parse_children_list(option = {})
233
- children_list = []
234
- first_char = @source.read
235
- if first_char == CONTENT_START
236
- loop do
237
- children = []
238
- if option[:verbal] || option[:instruction]
239
- children = parse_verbal_nodes(option)
240
- else
241
- children = parse_nodes(option)
242
- end
243
- if option[:trim_indents]
244
- trim_indents(children)
245
- end
246
- children_list << children
247
- unless @source.read == CONTENT_END
248
- raise ZenithalParseError.new(@source)
249
- end
250
- space_count = skip_spaces
251
- unless @source.read == CONTENT_START
252
- @source.unread(space_count + 1)
253
- break
254
- end
255
- end
256
- elsif first_char == CONTENT_END
257
- children_list << []
258
- else
259
- raise ZenithalParseError.new(@source)
263
+ return parser
264
+ end
265
+
266
+ def parse_escape
267
+ parser = Parser.build(self) do
268
+ !parse_char(ESCAPE_START)
269
+ char = !parse_char_any(ESCAPE_CHARS)
270
+ next char
260
271
  end
261
- return children_list
272
+ return parser
273
+ end
274
+
275
+ def parse_identifier
276
+ parser = Parser.build(self) do
277
+ first_char = !parse_first_identifier_char
278
+ rest_chars = !parse_middle_identifier_char.many
279
+ identifier = first_char + rest_chars.join
280
+ next identifier
281
+ end
282
+ return parser
283
+ end
284
+
285
+ def parse_first_identifier_char
286
+ return parse_char_any(VALID_FIRST_IDENTIFIER_CHARS)
262
287
  end
263
288
 
264
- def create_nodes(name, attributes, children_list, option = {})
265
- nodes = []
266
- unless option[:macro]
267
- if option[:instruction]
289
+ def parse_middle_identifier_char
290
+ return parse_char_any(VALID_MIDDLE_IDENTIFIER_CHARS)
291
+ end
292
+
293
+ def parse_space
294
+ return parse_char_any(SPACE_CHARS).many
295
+ end
296
+
297
+ def create_nodes(name, marks, attributes, children_list)
298
+ nodes = Nodes[]
299
+ unless marks.include?(:macro)
300
+ if marks.include?(:trim)
301
+ children_list.each do |children|
302
+ children.trim_indents
303
+ end
304
+ end
305
+ if marks.include?(:instruction)
268
306
  unless children_list.size <= 1
269
- raise ZenithalParseError.new(@source)
307
+ throw(:error, error_message("Processing instruction cannot have more than one argument"))
270
308
  end
271
309
  nodes = create_instructions(name, attributes, children_list.first)
272
- if name == SYSTEM_INSTRUCTION_NAME
273
- skip_spaces
274
- end
275
310
  else
276
- unless option[:multiple] || children_list.size <= 1
277
- raise ZenithalParseError.new(@source)
311
+ unless marks.include?(:multiple) || children_list.size <= 1
312
+ throw(:error, error_message("Normal node cannot have more than one argument"))
278
313
  end
279
314
  nodes = create_elements(name, attributes, children_list)
280
315
  end
@@ -284,28 +319,13 @@ class ZenithalParser
284
319
  return nodes
285
320
  end
286
321
 
287
- def create_elements(name, attributes, children_list)
288
- elements = []
289
- children_list.each do |children|
290
- element = Element.new(name)
291
- attributes.each do |key, value|
292
- element.add_attribute(key, value)
293
- end
294
- children.each do |child|
295
- element.add(child)
296
- end
297
- elements << element
298
- end
299
- return elements
300
- end
301
-
302
322
  def create_instructions(target, attributes, children)
303
- instructions = []
323
+ instructions = Nodes[]
304
324
  if target == SYSTEM_INSTRUCTION_NAME
305
325
  @version = attributes["version"] if attributes["version"]
306
- @brace_name = attributes["brace"] if attributes["brace"]
307
- @bracket_name = attributes["bracket"] if attributes["bracket"]
308
- @slash_name = attributes["slash"] if attributes["slash"]
326
+ @special_element_names[:brace] = attributes["brace"] if attributes["brace"]
327
+ @special_element_names[:bracket] = attributes["bracket"] if attributes["bracket"]
328
+ @special_element_names[:slash] = attributes["slash"] if attributes["slash"]
309
329
  elsif target == "xml"
310
330
  instruction = XMLDecl.new
311
331
  instruction.version = attributes["version"] || XMLDecl::DEFAULT_VERSION
@@ -327,224 +347,78 @@ class ZenithalParser
327
347
  return instructions
328
348
  end
329
349
 
350
+ def create_elements(name, attributes, children_list)
351
+ elements = Nodes[]
352
+ children_list.each do |children|
353
+ element = Element.new(name)
354
+ attributes.each do |key, value|
355
+ element.add_attribute(key, value)
356
+ end
357
+ children.each do |child|
358
+ element.add(child)
359
+ end
360
+ elements << element
361
+ end
362
+ return elements
363
+ end
364
+
330
365
  def process_macro(name, attributes, children_list)
331
- elements = []
366
+ elements = Nodes[]
332
367
  if @macros.key?(name)
333
- elements = @macros[name].call(attributes, children_list)
334
- elsif ENTITIES.key?(name)
335
- text = Text.new(ENTITIES[name], true, nil, false)
336
- elements << text
368
+ raw_elements = @macros[name].call(attributes, children_list)
369
+ raw_elements.each do |raw_element|
370
+ elements << raw_element
371
+ end
337
372
  else
338
- raise ZenithalParseError.new(@source)
373
+ throw(:error, error_message("No such macro"))
339
374
  end
340
375
  return elements
341
376
  end
342
377
 
343
- def register_macro(name, &block)
344
- @macros.store(name, block)
378
+ def error_message(message)
379
+ return "[line #{@source.lineno}] #{message}"
345
380
  end
346
381
 
347
- def parse_brace
348
- unless @source.read == BRACE_START
349
- raise ZenithalParseError.new(@source)
350
- end
351
- children = parse_nodes
352
- unless @source.read == BRACE_END
353
- raise ZenithalParseError.new(@source)
354
- end
355
- element = Element.new(@brace_name)
356
- children.each do |child|
357
- element.add(child)
358
- end
359
- return element
360
- end
382
+ end
361
383
 
362
- def parse_bracket
363
- unless @source.read == BRACKET_START
364
- raise ZenithalParseError.new(@source)
365
- end
366
- children = parse_nodes
367
- unless @source.read == BRACKET_END
368
- raise ZenithalParseError.new(@source)
369
- end
370
- element = Element.new(@bracket_name)
371
- children.each do |child|
372
- element.add(child)
373
- end
374
- return element
375
- end
376
384
 
377
- def parse_slash
378
- unless @source.read == SLASH_START
379
- raise ZenithalParseError.new(@source)
380
- end
381
- children = parse_nodes({}, true)
382
- unless @source.read == SLASH_END
383
- raise ZenithalParseError.new(@source)
384
- end
385
- element = Element.new(@slash_name)
386
- children.each do |child|
387
- element.add(child)
388
- end
389
- return element
390
- end
385
+ class ZenithalParser
391
386
 
392
- def parse_comment
393
- unless @source.read == COMMENT_DELIMITER
394
- raise ZenithalParseError.new(@source)
395
- end
396
- char = @source.read
397
- string = ""
398
- if char == COMMENT_DELIMITER
399
- while char = @source.read
400
- if char == "\n"
401
- @source.unread
402
- break
403
- else
404
- string << char
405
- end
406
- end
407
- elsif char == CONTENT_START
408
- while char = @source.read
409
- if char == CONTENT_END
410
- next_char = @source.read
411
- if next_char == COMMENT_DELIMITER
412
- break
413
- else
414
- string << char
415
- @source.unread
416
- end
417
- else
418
- string << char
419
- end
420
- end
421
- else
422
- raise ZenithalParseError.new(@source)
423
- end
424
- comment = Comment.new(" #{string.strip} ")
425
- return comment
426
- end
427
-
428
- def parse_text(option = {})
429
- string = ""
430
- while char = @source.read
431
- next_char = @source.peek
432
- if char == TAG_START || char == MACRO_START
433
- @source.unread
434
- break
435
- elsif (@brace_name && char == BRACE_START) || (@bracket_name && char == BRACKET_START) || (@slash_name && char == SLASH_START)
436
- @source.unread
437
- break
438
- elsif char == CONTENT_END
439
- @source.unread
440
- break
441
- elsif (@brace_name && char == BRACE_END) || (@bracket_name && char == BRACKET_END) || (@slash_name && char == SLASH_END)
442
- @source.unread
443
- break
444
- elsif char == COMMENT_DELIMITER
445
- @source.unread
446
- break
447
- elsif char == ESCAPE_START && ESCAPES.include?(next_char)
448
- @source.unread
449
- string << parse_escape_string
450
- else
451
- string << char
452
- end
453
- end
454
- text = Text.new(string, true, nil, false)
455
- return text
456
- end
457
-
458
- def parse_verbal_text(option = {})
459
- string = ""
460
- while char = @source.read
461
- next_char = @source.peek
462
- if char == CONTENT_END
463
- @source.unread
464
- break
465
- elsif char == ESCAPE_START && ESCAPES.include?(next_char)
466
- @source.unread
467
- string << parse_escape_string
468
- else
469
- string << char
470
- end
471
- end
472
- text = Text.new(string, true, nil, false)
473
- return text
474
- end
387
+ include CommonParserMethod
388
+ include ZenithalParserMethod
475
389
 
476
- def parse_escape_string
477
- unless @source.read == ESCAPE_START
478
- raise ZenithalParseError.new(@source)
479
- end
480
- char = @source.read
481
- return char
482
- end
390
+ attr_reader :source
483
391
 
484
- def skip_spaces
485
- count = 0
486
- while @source.read =~ /\s/
487
- count += 1
488
- end
489
- @source.unread
490
- return count
392
+ def initialize(source)
393
+ @source = StringReader.new(source)
394
+ @version = nil
395
+ @special_element_names = {:brace => nil, :bracket => nil, :slash => nil}
396
+ @macros = {}
491
397
  end
492
398
 
493
- def trim_spaces(children)
494
- if children.first.is_a?(Text)
495
- children.first.value = children.first.value.lstrip
496
- end
497
- if children.last.is_a?(Text)
498
- children.last.value = children.last.value.rstrip
399
+ def parse
400
+ result = parse_document.exec
401
+ if result.success?
402
+ return result.value
403
+ else
404
+ raise ZenithalParseError.new(result.message)
499
405
  end
500
406
  end
501
407
 
502
- def trim_indents(children)
503
- texts = []
504
- if children.last.is_a?(Text)
505
- children.last.value = children.last.value.rstrip
506
- end
507
- children.each do |child|
508
- case child
509
- when Text
510
- texts << child
511
- when Parent
512
- texts.concat(ZenithalParser.get_all_texts(child))
513
- end
514
- end
515
- indent_length = 10000
516
- texts.each do |text|
517
- text.value.scan(/\n(\x20+)/) do |match|
518
- indent_length = [match[0].length, indent_length].min
519
- end
520
- end
521
- texts.each do |text|
522
- text.value = text.value.gsub(/\n(\x20+)/){"\n" + " " * ($1.length - indent_length)}
523
- end
524
- if children.first.is_a?(Text)
525
- children.first.value = children.first.value.lstrip
526
- end
408
+ def register_macro(name, &block)
409
+ @macros.store(name, block)
527
410
  end
528
411
 
529
- def self.valid_start_char?(char)
530
- return VALID_START_CHARS.any?{|s| s === char.ord}
412
+ def brace_name=(name)
413
+ @special_element_names[:brace] = name
531
414
  end
532
415
 
533
- def self.valid_char?(char)
534
- return VALID_START_CHARS.any?{|s| s === char.ord} || VALID_MIDDLE_CHARS.any?{|s| s === char.ord}
416
+ def bracket_name=(name)
417
+ @special_element_names[:bracket] = name
535
418
  end
536
419
 
537
- def self.get_all_texts
538
- texts = []
539
- self.children.each do |child|
540
- case child
541
- when Text
542
- texts << child
543
- when Parent
544
- texts.concat(get_all_texts(child))
545
- end
546
- end
547
- return texts
420
+ def slash_name=(name)
421
+ @special_element_names[:slash] = name
548
422
  end
549
423
 
550
424
  end