d-mark 1.0.0a1 → 1.0.0b2

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.
@@ -1,28 +1,57 @@
1
1
  module DMark
2
2
  class Translator
3
- attr_reader :out
3
+ class UnhandledNode < StandardError
4
+ attr_reader :node
4
5
 
5
- def initialize(nodes)
6
- @nodes = nodes
6
+ def initialize(node)
7
+ @node = node
8
+ end
9
+
10
+ def message
11
+ case @node
12
+ when String
13
+ 'Unhandled string node'
14
+ when DMark::ElementNode
15
+ "Unhandled element node #{@node.name.inspect}"
16
+ else
17
+ "Unhandled node #{@node.inspect}"
18
+ end
19
+ end
20
+ end
21
+
22
+ def self.translate(nodes, context = {})
23
+ new.translate(nodes, context)
24
+ end
7
25
 
8
- @out = ''
26
+ def translate(nodes, context = {})
27
+ [nodes.map { |node| handle(node, context) }].flatten.join('')
9
28
  end
10
29
 
11
- def run
12
- @nodes.each do |node|
13
- handle(node)
30
+ def handle(node, context = {})
31
+ case node
32
+ when String
33
+ handle_string(node, context)
34
+ when DMark::ElementNode
35
+ handle_element(node, context)
36
+ else
37
+ raise ArgumentError, "Cannot handle #{node.class}"
14
38
  end
15
- @out
16
39
  end
17
40
 
18
- private
41
+ # @abstract
42
+ def handle_string(string, _context)
43
+ raise DMark::Translator::UnhandledNode.new(string)
44
+ end
19
45
 
20
- def handle(_node)
21
- raise NotImplementedError
46
+ # @abstract
47
+ def handle_element(element, _context)
48
+ raise DMark::Translator::UnhandledNode.new(element)
22
49
  end
23
50
 
24
- def handle_children(node)
25
- node.children.each { |child| handle(child) }
51
+ private
52
+
53
+ def handle_children(node, context)
54
+ node.children.map { |child| handle(child, context) }
26
55
  end
27
56
  end
28
57
  end
@@ -1,3 +1,3 @@
1
1
  module DMark
2
- VERSION = '1.0.0a1'.freeze
2
+ VERSION = '1.0.0b2'.freeze
3
3
  end
@@ -1 +1 @@
1
- p. I’m a %em{trivial} example!
1
+ #p I’m a %em{trivial} example!
@@ -1,34 +1,88 @@
1
- def parse(s)
2
- DMark::Parser.new(s).parse
1
+ def parse(str)
2
+ DMark::Parser.new(str).parse
3
3
  end
4
4
 
5
5
  def element(name, attributes, children)
6
6
  DMark::ElementNode.new(name, attributes, children)
7
7
  end
8
8
 
9
+ describe DMark::Parser::ParserError do
10
+ subject(:error) do
11
+ DMark::Parser.new(content).parse
12
+ rescue described_class => e
13
+ break e
14
+ end
15
+
16
+ let(:content) do
17
+ "#p Stuff\n\n#p More stuff }"
18
+ end
19
+
20
+ describe '#message' do
21
+ subject { error.message }
22
+
23
+ it { is_expected.to eq('parse error at line 3, col 15: unexpected } -- try escaping it as "%}"') }
24
+ end
25
+
26
+ describe '#fancy_message' do
27
+ subject { error.fancy_message }
28
+
29
+ it { is_expected.to eq("parse error at line 3, col 15: unexpected } -- try escaping it as \"%}\"\n\n#p More stuff }\n\e[31m ↑\e[0m") }
30
+ end
31
+ end
32
+
9
33
  describe 'DMark::Parser#parser' do
10
34
  it 'parses' do
11
35
  expect(parse('')).to eq []
12
- expect(parse('p.')).to eq [element('p', {}, [])]
13
- expect(parse('p. hi')).to eq [element('p', {}, ['hi'])]
14
- expect(parse('p. hi %%')).to eq [element('p', {}, ['hi ', '%'])]
15
- expect(parse('p. hi %}')).to eq [element('p', {}, ['hi ', '}'])]
36
+ expect(parse('#p')).to eq [element('p', {}, [])]
37
+ expect(parse('#p hi')).to eq [element('p', {}, ['hi'])]
38
+ expect(parse('#p hi %%')).to eq [element('p', {}, ['hi ', '%'])]
39
+ expect(parse('#p hi %}')).to eq [element('p', {}, ['hi ', '}'])]
40
+ end
41
+
42
+ it 'parses element with name containing dash' do
43
+ expect(parse('#intro-para hi')).to eq [
44
+ element('intro-para', {}, ['hi'])
45
+ ]
46
+ end
47
+
48
+ it 'parses element with name containing underscore' do
49
+ expect(parse('#intro_para hi')).to eq [
50
+ element('intro_para', {}, ['hi'])
51
+ ]
52
+ end
53
+
54
+ it 'parses element with name containing uppercase letters' do
55
+ expect(parse('#IntroPara hi')).to eq [
56
+ element('IntroPara', {}, ['hi'])
57
+ ]
58
+ end
59
+
60
+ it 'does not parse element with name starting with a dash' do
61
+ expect { parse('#-intro hi there') }.to raise_error(DMark::Parser::ParserError)
62
+ end
63
+
64
+ it 'does not parse element with name starting with an underscore' do
65
+ expect { parse('#_intro hi there') }.to raise_error(DMark::Parser::ParserError)
66
+ end
67
+
68
+ it 'does not parse element with name starting with a digit' do
69
+ expect { parse('#4ever best friends') }.to raise_error(DMark::Parser::ParserError)
16
70
  end
17
71
 
18
72
  it 'parses escaped % in block' do
19
- expect(parse('p. %%')).to eq [
73
+ expect(parse('#p %%')).to eq [
20
74
  element('p', {}, ['%'])
21
75
  ]
22
76
  end
23
77
 
24
78
  it 'parses escaped } in block' do
25
- expect(parse('p. %}')).to eq [
79
+ expect(parse('#p %}')).to eq [
26
80
  element('p', {}, ['}'])
27
81
  ]
28
82
  end
29
83
 
30
84
  it 'parses escaped % in inline block' do
31
- expect(parse('p. %foo{%%}')).to eq [
85
+ expect(parse('#p %foo{%%}')).to eq [
32
86
  element(
33
87
  'p', {},
34
88
  [
@@ -39,17 +93,18 @@ describe 'DMark::Parser#parser' do
39
93
  end
40
94
 
41
95
  it 'parses escaped } in inline block' do
42
- expect(parse('p. %foo{%}}')).to eq [
96
+ expect(parse('#p %foo{%}}')).to eq [
43
97
  element(
44
98
  'p', {},
45
99
  [
46
100
  element('foo', {}, ['}'])
47
- ])
101
+ ]
102
+ )
48
103
  ]
49
104
  end
50
105
 
51
106
  it 'parses block with text and element content' do
52
- expect(parse('p. hi %em{ho}')).to eq [
107
+ expect(parse('#p hi %em{ho}')).to eq [
53
108
  element(
54
109
  'p', {}, [
55
110
  'hi ',
@@ -60,7 +115,7 @@ describe 'DMark::Parser#parser' do
60
115
  end
61
116
 
62
117
  it 'parses block with text and element content, followed by newline' do
63
- expect(parse("p. hi %em{ho}\n")).to eq [
118
+ expect(parse("#p hi %em{ho}\n")).to eq [
64
119
  element(
65
120
  'p', {}, [
66
121
  'hi ',
@@ -71,7 +126,7 @@ describe 'DMark::Parser#parser' do
71
126
  end
72
127
 
73
128
  it 'parses children' do
74
- expect(parse("p. hi %em{ho}\n p. child p")).to eq [
129
+ expect(parse("#p hi %em{ho}\n #p child p")).to eq [
75
130
  element(
76
131
  'p', {}, [
77
132
  'hi ',
@@ -83,7 +138,7 @@ describe 'DMark::Parser#parser' do
83
138
  end
84
139
 
85
140
  it 'parses children multiple levels deep' do
86
- expect(parse("p. hi %em{ho}\n p. child p\n p. subchild p")).to eq [
141
+ expect(parse("#p hi %em{ho}\n #p child p\n #p subchild p")).to eq [
87
142
  element(
88
143
  'p', {}, [
89
144
  'hi ',
@@ -104,7 +159,7 @@ describe 'DMark::Parser#parser' do
104
159
  end
105
160
 
106
161
  it 'ignores blanks' do
107
- expect(parse("p. foo\n \n p. bar\n \n\n p. qux")).to eq [
162
+ expect(parse("#p foo\n \n #p bar\n \n\n #p qux")).to eq [
108
163
  element(
109
164
  'p', {}, [
110
165
  'foo',
@@ -124,106 +179,140 @@ describe 'DMark::Parser#parser' do
124
179
  end
125
180
 
126
181
  it 'reads multiple consecutive blocks' do
127
- expect(parse("p. foo\np. bar")).to eq [
182
+ expect(parse("#p foo\n#p bar")).to eq [
128
183
  element('p', {}, ['foo']),
129
184
  element('p', {}, ['bar'])
130
185
  ]
131
186
  end
132
187
 
133
188
  it 'includes raw content' do
134
- expect(parse("p. foo\n donkey")).to eq [
135
- element('p', {}, %W(foo \n donkey))
189
+ expect(parse("#p foo\n donkey")).to eq [
190
+ element('p', {}, %W[foo \n donkey])
136
191
  ]
137
192
  end
138
193
 
139
194
  it 'includes raw content including initial indentation' do
140
- expect(parse("p. foo\n donkey")).to eq [
195
+ expect(parse("#p foo\n donkey")).to eq [
141
196
  element('p', {}, ['foo', "\n", ' donkey'])
142
197
  ]
143
198
  end
144
199
 
145
200
  it 'includes raw content from multiple lines' do
146
- expect(parse("p. foo\n donkey\n giraffe\n zebra\n")).to eq [
201
+ expect(parse("#p foo\n donkey\n giraffe\n zebra\n")).to eq [
147
202
  element('p', {}, ['foo', "\n", ' donkey', "\n", 'giraffe', "\n", ' zebra'])
148
203
  ]
149
204
  end
150
205
 
151
206
  it 'includes empty lines in raw content' do
152
- expect(parse("p. foo\n\n donkey\n\n giraffe\n")).to eq [
207
+ expect(parse("#p foo\n\n donkey\n\n giraffe\n")).to eq [
153
208
  element('p', {}, ['foo', "\n", "\n", 'donkey', "\n", "\n", ' giraffe'])
154
209
  ]
155
210
  end
156
211
 
157
212
  it 'does not include line break after empty block element and before data lines' do
158
- expect(parse("p.\n donkey\n")).to eq [
213
+ expect(parse("#p\n donkey\n")).to eq [
159
214
  element('p', {}, ['donkey'])
160
215
  ]
161
216
  end
162
217
 
163
218
  it 'parses inline element in data lines' do
164
- expect(parse("p.\n %emph{donkey}\n")).to eq [
165
- element('p', {}, [
166
- element('emph', {}, ['donkey'])
167
- ])
219
+ expect(parse("#p\n %em{donkey}")).to eq [
220
+ element('p', {}, [element('em', {}, ['donkey'])])
168
221
  ]
169
222
  end
170
223
 
171
224
  it 'parses empty attributes' do
172
- expect(parse('p[]. hi')).to eq [
225
+ expect(parse('#p[] hi')).to eq [
173
226
  element('p', {}, ['hi'])
174
227
  ]
175
228
  end
176
229
 
177
230
  it 'parses single attribute' do
178
- expect(parse('p[foo=bar]. hi')).to eq [
231
+ expect(parse('#p[foo=bar] hi')).to eq [
179
232
  element('p', { 'foo' => 'bar' }, ['hi'])
180
233
  ]
181
234
  end
182
235
 
236
+ it 'parses attribute with dash' do
237
+ expect(parse('#p[intended-audience=learner] hi')).to eq [
238
+ element('p', { 'intended-audience' => 'learner' }, ['hi'])
239
+ ]
240
+ end
241
+
242
+ it 'parses attribute with numbers' do
243
+ expect(parse('#p[is-over-9000=yup] hi')).to eq [
244
+ element('p', { 'is-over-9000' => 'yup' }, ['hi'])
245
+ ]
246
+ end
247
+
248
+ it 'parses attribute with underscore' do
249
+ expect(parse('#p[intended_audience=learner] hi')).to eq [
250
+ element('p', { 'intended_audience' => 'learner' }, ['hi'])
251
+ ]
252
+ end
253
+
254
+ it 'parses attribute with uppercase letters' do
255
+ expect(parse('#p[IntendedAudience=learner] hi')).to eq [
256
+ element('p', { 'IntendedAudience' => 'learner' }, ['hi'])
257
+ ]
258
+ end
259
+
260
+ it 'does not parse atttributes starting with -' do
261
+ expect { parse('#p[-this=is dog] hello yes') }.to raise_error(DMark::Parser::ParserError)
262
+ end
263
+
264
+ it 'does not parse atttributes starting with _' do
265
+ expect { parse('#p[_this=is dog] hello yes') }.to raise_error(DMark::Parser::ParserError)
266
+ end
267
+
268
+ it 'does not parse atttributes starting with a digit' do
269
+ expect { parse('#p[4this=is dog] hello yes') }.to raise_error(DMark::Parser::ParserError)
270
+ end
271
+
183
272
  it 'parses single value-less attribute' do
184
- expect(parse('p[foo]. hi')).to eq [
273
+ expect(parse('#p[foo] hi')).to eq [
185
274
  element('p', { 'foo' => 'foo' }, ['hi'])
186
275
  ]
187
276
  end
188
277
 
189
278
  it 'parses multiple attributes' do
190
- expect(parse('p[foo=bar,qux=donkey]. hi')).to eq [
279
+ expect(parse('#p[foo=bar,qux=donkey] hi')).to eq [
191
280
  element('p', { 'foo' => 'bar', 'qux' => 'donkey' }, ['hi'])
192
281
  ]
193
282
  end
194
283
 
195
284
  it 'parses multiple value-less attributes' do
196
- expect(parse('p[foo,qux]. hi')).to eq [
285
+ expect(parse('#p[foo,qux] hi')).to eq [
197
286
  element('p', { 'foo' => 'foo', 'qux' => 'qux' }, ['hi'])
198
287
  ]
199
288
  end
200
289
 
201
290
  it 'parses escaped attributes' do
202
- expect(parse('p[foo=%],bar=%%,donkey=%,]. hi')).to eq [
291
+ expect(parse('#p[foo=%],bar=%%,donkey=%,] hi')).to eq [
203
292
  element('p', { 'foo' => ']', 'bar' => '%', 'donkey' => ',' }, ['hi'])
204
293
  ]
205
294
  end
206
295
 
207
296
  it 'parses attributes in empty block' do
208
- expect(parse("p[foo=bar].\n hi")).to eq [
297
+ expect(parse("#p[foo=bar]\n hi")).to eq [
209
298
  element('p', { 'foo' => 'bar' }, ['hi'])
210
299
  ]
211
300
  end
212
301
 
213
302
  it 'parses block start on next line properly' do
214
- expect(parse("p.\n this is not a child block.")).to eq [
303
+ expect(parse("#p\n this is not a child block.")).to eq [
215
304
  element('p', {}, ['this is not a child block.'])
216
305
  ]
217
306
  end
218
307
 
219
308
  it 'parses block start on next line with spacey' do
220
- expect(parse("p.\n foo.bar")).to eq [
309
+ expect(parse("#p\n foo.bar")).to eq [
221
310
  element('p', {}, ['foo.bar'])
222
311
  ]
223
312
  end
224
313
 
225
314
  it 'parses child block without content' do
226
- expect(parse("ul.\n li.\n p. You can.")).to eq [
315
+ expect(parse("#ul\n #li\n #p You can.")).to eq [
227
316
  element(
228
317
  'ul', {},
229
318
  [
@@ -239,7 +328,7 @@ describe 'DMark::Parser#parser' do
239
328
  end
240
329
 
241
330
  it 'parses child block without content at end' do
242
- expect(parse("ul.\n li.")).to eq [
331
+ expect(parse("#ul\n #li")).to eq [
243
332
  element(
244
333
  'ul', {},
245
334
  [
@@ -250,7 +339,7 @@ describe 'DMark::Parser#parser' do
250
339
  end
251
340
 
252
341
  it 'parses child block with attributes' do
253
- expect(parse("ul.\n li[foo].")).to eq [
342
+ expect(parse("#ul\n #li[foo]")).to eq [
254
343
  element(
255
344
  'ul', {},
256
345
  [
@@ -261,42 +350,55 @@ describe 'DMark::Parser#parser' do
261
350
  end
262
351
 
263
352
  it 'parses document starting with blank lines' do
264
- expect(parse(" \n \np. Hi!")).to eq [
353
+ expect(parse(" \n \n#p Hi!")).to eq [
265
354
  element('p', {}, ['Hi!'])
266
355
  ]
267
356
  end
268
357
 
358
+ it 'parses escaped indented line' do
359
+ expect(parse("#listing\n %#h1 Foo\n")).to eq [
360
+ element('listing', {}, ['#', 'h1 Foo'])
361
+ ]
362
+ end
363
+
364
+ it 'parses escaped indented line with attributes' do
365
+ expect(parse("#listing\n %#h1[donkey] Foo\n")).to eq [
366
+ element('listing', {}, ['#', 'h1[donkey] Foo'])
367
+ ]
368
+ end
369
+
269
370
  it 'does not parse percent escapes' do
270
- expect { parse('p. %ref[url=https://github.com/pulls?q=is%3Aopen+user%3Ananoc]{eek}') }
371
+ expect { parse('#p %ref[url=https://github.com/pulls?q=is%3Aopen+user%3Ananoc]{eek}') }
271
372
  .to raise_error(DMark::Parser::ParserError, 'parse error at line 1, col 43: expected "%", "," or "]" after "%", but got "3"')
272
373
  end
273
374
 
274
375
  it 'does not parse attribute values ending with an end-of-file' do
275
- expect { parse('p. %ref[url=hello') }
376
+ expect { parse('#p %ref[url=hello') }
276
377
  .to raise_error(DMark::Parser::ParserError, 'parse error at line 1, col 18: unexpected file end in attribute value')
277
378
  end
278
379
 
279
380
  it 'does not parse attribute values ending with a line break' do
280
- expect { parse("p. %ref[url=hello\n") }
381
+ expect { parse("#p %ref[url=hello\n") }
281
382
  .to raise_error(DMark::Parser::ParserError, 'parse error at line 1, col 18: unexpected line break in attribute value')
282
383
  end
283
384
 
284
385
  it 'does not parse escaped attribute values ending with an end-of-file' do
285
- expect { parse('p. %ref[url=hello%') }
386
+ expect { parse('#p %ref[url=hello%') }
286
387
  .to raise_error(DMark::Parser::ParserError, 'parse error at line 1, col 19: unexpected file end in attribute value')
287
388
  end
288
389
 
289
390
  it 'does not parse escaped attribute values ending with a line break' do
290
- expect { parse("p. %ref[url=hello%\n") }
391
+ expect { parse("#p %ref[url=hello%\n") }
291
392
  .to raise_error(DMark::Parser::ParserError, 'parse error at line 1, col 19: unexpected line break in attribute value')
292
393
  end
293
394
 
294
395
  it 'does not parse' do
396
+ expect { parse('#') }.to raise_error(DMark::Parser::ParserError)
295
397
  expect { parse('p') }.to raise_error(DMark::Parser::ParserError)
296
398
  expect { parse('0') }.to raise_error(DMark::Parser::ParserError)
297
399
  expect { parse('p0') }.to raise_error(DMark::Parser::ParserError)
298
- expect { parse('0.') }.to raise_error(DMark::Parser::ParserError)
299
- expect { parse('p. %') }.to raise_error(DMark::Parser::ParserError)
300
- expect { parse('p. }') }.to raise_error(DMark::Parser::ParserError)
400
+ expect { parse('#0') }.to raise_error(DMark::Parser::ParserError)
401
+ expect { parse('#p %') }.to raise_error(DMark::Parser::ParserError)
402
+ expect { parse('#p }') }.to raise_error(DMark::Parser::ParserError)
301
403
  end
302
404
  end