Class: DMark::Parser
- Inherits:
-
Object
show all
- Defined in:
- lib/d-mark/parser.rb
Defined Under Namespace
Classes: ElementNode, ParserError
Instance Attribute Summary collapse
Instance Method Summary
collapse
Constructor Details
#initialize(input) ⇒ Parser
Returns a new instance of Parser.
53
54
55
56
57
58
59
60
|
# File 'lib/d-mark/parser.rb', line 53
def initialize(input)
@input = input
@input_chars = @input.chars
@pos = 0
@col_nr = 0
@line_nr = 0
end
|
Instance Attribute Details
#pos ⇒ Object
Returns the value of attribute pos.
51
52
53
|
# File 'lib/d-mark/parser.rb', line 51
def pos
@pos
end
|
Instance Method Details
#advance ⇒ Object
87
88
89
90
91
92
93
94
95
|
# File 'lib/d-mark/parser.rb', line 87
def advance
if !eof? && @input_chars[@pos] == "\n"
@line_nr += 1
@col_nr = 0
end
@pos += 1
@col_nr += 1
end
|
#detect_indentation ⇒ Object
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
|
# File 'lib/d-mark/parser.rb', line 207
def detect_indentation
indentation_chars = 0
pos = @pos
loop do
case peek_char(pos)
when ' '
pos += 1
indentation_chars += 1
else
break
end
end
indentation_chars / 2
end
|
#eof?(pos = @pos) ⇒ Boolean
83
84
85
|
# File 'lib/d-mark/parser.rb', line 83
def eof?(pos = @pos)
pos >= @input_chars.size
end
|
#parse ⇒ Object
62
63
64
65
66
67
68
69
70
71
|
# File 'lib/d-mark/parser.rb', line 62
def parse
res = []
loop do
break if eof?
res << read_block_with_children
end
res
end
|
#peek_char(pos = @pos) ⇒ Object
75
76
77
78
79
80
81
|
# File 'lib/d-mark/parser.rb', line 75
def peek_char(pos = @pos)
if eof?
nil
else
@input_chars[pos]
end
end
|
#raise_parse_error(msg) ⇒ Object
456
457
458
|
# File 'lib/d-mark/parser.rb', line 456
def raise_parse_error(msg)
raise ParserError.new(@line_nr, @col_nr, msg)
end
|
#read_attribute_key ⇒ Object
353
354
355
|
# File 'lib/d-mark/parser.rb', line 353
def read_attribute_key
read_identifier
end
|
#read_attribute_value ⇒ Object
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
|
# File 'lib/d-mark/parser.rb', line 357
def read_attribute_value
res = ''
is_escaping = false
loop do
char = peek_char
if is_escaping
case char
when nil, "\n"
break
else
advance
res << char
is_escaping = false
end
else
case char
when nil, "\n", ']', ','
break
when '%'
advance
is_escaping = true
else
advance
res << char
end
end
end
res.to_s
end
|
#read_attributes ⇒ Object
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
|
# File 'lib/d-mark/parser.rb', line 321
def read_attributes
read_char('[')
res = {}
at_start = true
loop do
char = peek_char
case char
when ']'
advance
break
else
read_char(',') unless at_start
key = read_attribute_key
if peek_char == '='
read_char('=')
value = read_attribute_value
else
value = key
end
res[key] = value
at_start = false
end
end
res
end
|
#read_block_with_children(indentation = 0) ⇒ Object
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
|
# File 'lib/d-mark/parser.rb', line 109
def read_block_with_children(indentation = 0)
res = read_single_block
pending_blanks = 0
until eof?
blank_pos = try_read_blank_line
if blank_pos
@pos = blank_pos
@line_nr += 1
@col_nr = 0
pending_blanks += 1
else
sub_indentation = detect_indentation
break if sub_indentation < indentation + 1
read_indentation(indentation + 1)
if try_read_block_start
res.children << read_block_with_children(indentation + 1)
else
res.children << "\n" unless res.children.empty?
pending_blanks.times { res.children << "\n" }
pending_blanks = 0
res.children.concat(read_inline_content)
read_end_of_inline_content
end
end
end
res
end
|
#read_char(c) ⇒ Object
97
98
99
100
101
102
103
104
105
|
# File 'lib/d-mark/parser.rb', line 97
def read_char(c)
char = peek_char
if char != c
raise_parse_error("expected #{c.inspect}, but got #{char.nil? ? 'EOF' : char.inspect}")
else
advance
char
end
end
|
#read_end_of_inline_content ⇒ Object
275
276
277
278
279
280
281
282
283
284
285
|
# File 'lib/d-mark/parser.rb', line 275
def read_end_of_inline_content
char = peek_char
case char
when "\n", nil
advance
when '}'
raise_parse_error('unexpected } -- try escaping it as "%}"')
else
raise_parse_error('unexpected content')
end
end
|
#read_identifier ⇒ Object
287
288
289
290
291
|
# File 'lib/d-mark/parser.rb', line 287
def read_identifier
a = read_identifier_head
b = read_identifier_tail
"#{a}#{b}"
end
|
#read_identifier_head ⇒ Object
293
294
295
296
297
298
299
300
301
302
|
# File 'lib/d-mark/parser.rb', line 293
def read_identifier_head
char = peek_char
case char
when 'a'..'z'
advance
char
else
raise_parse_error("expected an identifier, but got #{char.inspect}")
end
end
|
#read_identifier_tail ⇒ Object
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
|
# File 'lib/d-mark/parser.rb', line 304
def read_identifier_tail
res = ''
loop do
char = peek_char
case char
when 'a'..'z', '-', '0'..'9'
advance
res << char
else
break
end
end
res.to_s
end
|
#read_indentation(indentation) ⇒ Object
244
245
246
247
248
249
|
# File 'lib/d-mark/parser.rb', line 244
def read_indentation(indentation)
indentation.times do
read_char(' ')
read_char(' ')
end
end
|
#read_inline_content ⇒ Object
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
|
# File 'lib/d-mark/parser.rb', line 390
def read_inline_content
res = []
loop do
char = peek_char
case char
when "\n", nil
break
when '}'
break
when '%'
advance
res << read_percent_body
else
res << read_string
end
end
res
end
|
#read_inline_element ⇒ Object
441
442
443
444
445
446
447
448
449
450
451
452
453
454
|
# File 'lib/d-mark/parser.rb', line 441
def read_inline_element
name = read_identifier
attributes =
if peek_char == '['
read_attributes
else
{}
end
read_char('{')
contents = read_inline_content
read_char('}')
ElementNode.new(name, attributes, contents)
end
|
#read_percent_body ⇒ Object
428
429
430
431
432
433
434
435
436
437
438
439
|
# File 'lib/d-mark/parser.rb', line 428
def read_percent_body
char = peek_char
case char
when '%', '}'
advance
char.to_s
when nil, "\n"
raise_parse_error("expected something after %")
else
read_inline_element
end
end
|
#read_single_block ⇒ Object
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
|
# File 'lib/d-mark/parser.rb', line 251
def read_single_block
identifier = read_identifier
attributes =
if peek_char == '['
read_attributes
else
{}
end
read_char('.')
case peek_char
when nil, "\n"
advance
ElementNode.new(identifier, attributes, [])
else
read_char(' ')
content = read_inline_content
read_end_of_inline_content
ElementNode.new(identifier, attributes, content)
end
end
|
#read_string ⇒ Object
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
|
# File 'lib/d-mark/parser.rb', line 411
def read_string
res = ''
loop do
char = peek_char
case char
when nil, "\n", '%', '}'
break
else
advance
res << char
end
end
res.to_s
end
|
#read_until_eol_or_eof ⇒ Object
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
|
# File 'lib/d-mark/parser.rb', line 224
def read_until_eol_or_eof
res = ''
loop do
char = peek_char
case char
when "\n"
advance
break
when nil
break
else
advance
res << char
end
end
res.to_s
end
|
#try_read_blank_line ⇒ Object
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
|
# File 'lib/d-mark/parser.rb', line 141
def try_read_blank_line
pos = @pos
loop do
case peek_char(pos)
when ' '
pos += 1
when nil
break pos + 1
when "\n"
break pos + 1
else
break nil
end
end
end
|
#try_read_block_start ⇒ Object
FIXME: ugly and duplicated
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
|
# File 'lib/d-mark/parser.rb', line 159
def try_read_block_start
old_pos = @pos
success =
if try_read_identifier_head
if try_read_identifier_tail
case peek_char
when '['
true
when '.'
advance
[' ', "\n", nil].include?(peek_char)
end
end
end
@pos = old_pos
success
end
|
#try_read_identifier_head ⇒ Object
FIXME: ugly and duplicated
180
181
182
183
184
185
186
187
|
# File 'lib/d-mark/parser.rb', line 180
def try_read_identifier_head
char = peek_char
case char
when 'a'..'z'
advance
char
end
end
|
#try_read_identifier_tail ⇒ Object
FIXME: ugly and duplicated
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
|
# File 'lib/d-mark/parser.rb', line 190
def try_read_identifier_tail
res = ''
loop do
char = peek_char
case char
when 'a'..'z', '-', '0'..'9'
advance
res << char
else
break
end
end
res.to_s
end
|