pseudohikiparser 0.0.4 → 0.0.5.develop

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 38493340a4c48448be37a5e6d800bf6ad0fa8f8f
4
- data.tar.gz: 43c9ef5c8dadb24c502d0bdb8f3dd47a511c3157
3
+ metadata.gz: 5be7e5cb75ef3ae2d601b8dbf03aa768c5d0e1c1
4
+ data.tar.gz: 6d9de4c42fa1339e0922903764c07cd8edc13c5e
5
5
  SHA512:
6
- metadata.gz: 381ec4accb316ca35c4c82d49e9189657a330cfb318e12c0783f528b1c5b41a156352538c0297e232358cb43058c4a3ca90a6fefaf11bea378244a2b3da0c497
7
- data.tar.gz: 552e791bf5fc72f022d499a069911b2705d121304c2a23381b3e6e7039287d6019a9dfea005f769c2fe05ea63ecfc99411ccf9bf1fdd8781a19645a23e761bc6
6
+ metadata.gz: 2d5e5d27c733773551dc372df645933aa14d3db2c5c212d73646010db2fc59c6756ba13c37c662b9f5e56f48ebba4e98f014c853d8ab9c7e9cb53c628f645dbc
7
+ data.tar.gz: 9bb79366e51c3ca3f0c6d51abdcdd7f399d905130113ae86d0311f16734258da159f3824727c436ca6551f3a233fb2cf50bd861f65233ec76d617e4e426a6445
data/README.ja.md CHANGED
@@ -1,7 +1,7 @@
1
1
  PseudoHikiParser
2
2
  ================
3
3
 
4
- PseudoHikiParserは[Hiki](https://github.com/hiki/hikidoc)に似た記法で書かれたテキストをHTML・Markdownその他のファイル形式に変換するコンバータです。
4
+ PseudoHikiParserは[Hiki](https://github.com/hiki/hikidoc)に似た記法で書かれたテキストをパースし、HTML・Markdownその他のファイル形式に変換します。
5
5
 
6
6
  このツールは次の目的を念頭において作成中です。
7
7
 
@@ -26,7 +26,7 @@ gem install pseudohikiparser
26
26
  もし実験的な機能も試してみたければ、
27
27
 
28
28
  ```
29
- gem install pseudohikiparser --pre
29
+ gem install pseudohikiparser --version 0.0.5.develop
30
30
  ```
31
31
 
32
32
  ## 使い方
@@ -44,16 +44,16 @@ gem install pseudohikiparser --pre
44
44
 
45
45
  このサンプルは[develop branch](https://github.com/nico-hn/PseudoHikiParser/tree/develop/samples)に置いてあります。
46
46
 
47
- ### pseudohiki2html.rb
47
+ ### pseudohiki2html
48
48
 
49
- _(pseudohiki2html.rbは現時点ではPseudoHikiParserの利用例として提供されており、現段階ではオプションも継続的に変更されることにご注意ください。)_
49
+ _(pseudohiki2htmlは現時点ではPseudoHikiParserの利用例として提供されており、現段階ではオプションも継続的に変更されることにご注意ください。)_
50
50
 
51
- インストールが終わると**pseudohiki2html.rb**というコマンドが使えるようになります。
51
+ インストールが終わると**pseudohiki2html**というコマンドが使えるようになります。
52
52
 
53
53
  コマンドラインで次のように入力すると
54
54
 
55
55
  ```
56
- pseudohiki2html.rb <<TEXT
56
+ pseudohiki2html <<TEXT
57
57
  !! The first heading
58
58
  The first paragraph
59
59
  TEXT
@@ -88,7 +88,7 @@ The first paragraph
88
88
  `--output`オプションを使ってファイル名を指定した場合は
89
89
 
90
90
  ```
91
- pseudohiki2html.rb --output first_example.html <<TEXT
91
+ pseudohiki2html --output first_example.html <<TEXT
92
92
  !! The first heading
93
93
  The first paragraph
94
94
  TEXT
@@ -96,10 +96,12 @@ TEXT
96
96
 
97
97
  で、結果がfirst\_example.htmlに保存されます。
98
98
 
99
- その他のオプションについては`pseudohiki2html.rb --help`でご参照ください。
99
+ その他のオプションについては`pseudohiki2html --help`でご参照ください。
100
100
 
101
101
  #### 非互換な変更
102
102
 
103
+ version 0.0.4まで、コマンド名は`pseudohiki2html.rb`でした。
104
+
103
105
  version 0.0.0.9.developから、コマンドラインのオプション名を以下の通り変更しています。
104
106
 
105
107
  |元の名前 |新しい名前 |注記 |
@@ -406,7 +408,7 @@ paragraph 3
406
408
 
407
409
  以下のクラスのうちの一部は、部分的にしか実装されていないかテストが十分ではないことにご注意ください。
408
410
 
409
- ### [HtmlFormat](https://github.com/nico-hn/PseudoHikiParser/blob/develop/lib/pseudohiki/htmlformat.rb#L8), [XhtmlFormat](https://github.com/nico-hn/PseudoHikiParser/blob/develop/lib/pseudohiki/htmlformat.rb#L312)
411
+ ### [HtmlFormat](https://github.com/nico-hn/PseudoHikiParser/blob/develop/lib/pseudohiki/htmlformat.rb#L8), [XhtmlFormat](https://github.com/nico-hn/PseudoHikiParser/blob/develop/lib/pseudohiki/htmlformat.rb#L320)
410
412
 
411
413
  これらのクラスのクラスメソッド(HtmlFormat|XhtmlFormat).formatは[HtmlElement](https://github.com/nico-hn/PseudoHikiParser/blob/develop/lib/htmlelement.rb)オブジェクトを木にしたものを返すため、以下の例のように後から手を加えることができます。
412
414
 
@@ -450,11 +452,11 @@ paragraph 2 that contains <a class="pdf" href="http://www.example.org/example.pd
450
452
  </div>
451
453
  ```
452
454
 
453
- ### [Xhtml5Format](https://github.com/nico-hn/PseudoHikiParser/blob/develop/lib/pseudohiki/htmlformat.rb#L318)
455
+ ### [Xhtml5Format](https://github.com/nico-hn/PseudoHikiParser/blob/develop/lib/pseudohiki/htmlformat.rb#L326)
454
456
 
455
457
  HTML5への変換用のVisitorクラスです。
456
458
 
457
- 現時点では\<section\>要素の扱い以外に[XhtmlFormat](https://github.com/nico-hn/PseudoHikiParser/blob/develop/lib/pseudohiki/htmlformat.rb#L312)との違いは余りありません。
459
+ 現時点では\<section\>要素の扱い以外に[XhtmlFormat](https://github.com/nico-hn/PseudoHikiParser/blob/develop/lib/pseudohiki/htmlformat.rb#L320)との違いは余りありません。
458
460
 
459
461
  ### [PlainTextFormat](https://github.com/nico-hn/PseudoHikiParser/blob/develop/lib/pseudohiki/plaintextformat.rb)
460
462
 
data/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  PseudoHikiParser
2
2
  ================
3
3
 
4
- PseudoHikiParser is a converter of texts written in a [Hiki](https://github.com/hiki/hikidoc) like notation, into HTML, Markdown or other formats.
4
+ PseudoHikiParser parses texts written in a [Hiki](https://github.com/hiki/hikidoc) like notation, and converts them into HTML, Markdown or other formats.
5
5
 
6
6
  I am writing this tool with following objectives in mind,
7
7
 
@@ -26,7 +26,7 @@ gem install pseudohikiparser
26
26
  or if you also want to try out experimental features,
27
27
 
28
28
  ```
29
- gem install pseudohikiparser --pre
29
+ gem install pseudohikiparser --version 0.0.5.develop
30
30
  ```
31
31
 
32
32
  ## Usage
@@ -44,16 +44,16 @@ And results of conversion
44
44
 
45
45
  You will find these samples in [develop branch](https://github.com/nico-hn/PseudoHikiParser/tree/develop/samples).
46
46
 
47
- ### pseudohiki2html.rb
47
+ ### pseudohiki2html
48
48
 
49
- _(Please note that pseudohiki2html.rb is currently provided as a showcase of PseudoHikiParser, and the options will be continuously changed at this stage of development.)_
49
+ _(Please note that pseudohiki2html is currently provided as a showcase of PseudoHikiParser, and the options will be continuously changed at this stage of development.)_
50
50
 
51
- After the installation of PseudoHikiParser, you can use a command: **pseudohiki2html.rb**.
51
+ After the installation of PseudoHikiParser, you can use a command: **pseudohiki2html**.
52
52
 
53
53
  Type the following lines at the command prompt:
54
54
 
55
55
  ```
56
- pseudohiki2html.rb <<TEXT
56
+ pseudohiki2html <<TEXT
57
57
  !! The first heading
58
58
  The first paragraph
59
59
  TEXT
@@ -88,7 +88,7 @@ The first paragraph
88
88
  And if you specify a file name with `--output` option:
89
89
 
90
90
  ```
91
- pseudohiki2html.rb --output first_example.html <<TEXT
91
+ pseudohiki2html --output first_example.html <<TEXT
92
92
  !! The first heading
93
93
  The first paragraph
94
94
  TEXT
@@ -96,10 +96,12 @@ TEXT
96
96
 
97
97
  the result will be saved in first\_example.html.
98
98
 
99
- For more options, please try `pseudohiki2html.rb --help`
99
+ For more options, please try `pseudohiki2html --help`
100
100
 
101
101
  #### Incompatible changes
102
102
 
103
+ Until version 0.0.4, the name of the command was `pseudohiki2html.rb`.
104
+
103
105
  From version 0.0.0.9.develop, command line options are renamed as follows:
104
106
 
105
107
  |old name |new name |note |
@@ -406,7 +408,7 @@ paragraph 3
406
408
 
407
409
  Please note that some of the following classes are implemented partly or not tested well.
408
410
 
409
- ### [HtmlFormat](https://github.com/nico-hn/PseudoHikiParser/blob/develop/lib/pseudohiki/htmlformat.rb#L8), [XhtmlFormat](https://github.com/nico-hn/PseudoHikiParser/blob/develop/lib/pseudohiki/htmlformat.rb#L312)
411
+ ### [HtmlFormat](https://github.com/nico-hn/PseudoHikiParser/blob/develop/lib/pseudohiki/htmlformat.rb#L8), [XhtmlFormat](https://github.com/nico-hn/PseudoHikiParser/blob/develop/lib/pseudohiki/htmlformat.rb#L320)
410
412
 
411
413
  Their class method (HtmlFormat|XhtmlFormat).format returns a tree of [HtmlElement](https://github.com/nico-hn/PseudoHikiParser/blob/develop/lib/htmlelement.rb) objects, and you can traverse the tree as in the following example.
412
414
 
@@ -450,11 +452,11 @@ paragraph 2 that contains <a class="pdf" href="http://www.example.org/example.pd
450
452
  </div>
451
453
  ```
452
454
 
453
- ### [Xhtml5Format](https://github.com/nico-hn/PseudoHikiParser/blob/develop/lib/pseudohiki/htmlformat.rb#L318)
455
+ ### [Xhtml5Format](https://github.com/nico-hn/PseudoHikiParser/blob/develop/lib/pseudohiki/htmlformat.rb#L326)
454
456
 
455
457
  This visitor is for HTML5.
456
458
 
457
- Currently there aren't many differences with [XhtmlFormat](https://github.com/nico-hn/PseudoHikiParser/blob/develop/lib/pseudohiki/htmlformat.rb#L312) except for the handling of \<section\> elements.
459
+ Currently there aren't many differences with [XhtmlFormat](https://github.com/nico-hn/PseudoHikiParser/blob/develop/lib/pseudohiki/htmlformat.rb#L320) except for the handling of \<section\> elements.
458
460
 
459
461
  ### [PlainTextFormat](https://github.com/nico-hn/PseudoHikiParser/blob/develop/lib/pseudohiki/plaintextformat.rb)
460
462
 
@@ -548,7 +550,7 @@ The first paragraph
548
550
 
549
551
  #### Limitations
550
552
 
551
- You can not convert malformed lists with this visitor class. That means list items must be nested hierarchically and if you skip a level in the sequence of items, the result of coversions will be corrupted.
553
+ You cannot convert malformed lists with this visitor class. That means list items must be nested hierarchically and if you skip a level in the sequence of items, the result of coversions will be corrupted.
552
554
 
553
555
  The following is an example of malformed list in which the first level is skipped:
554
556
 
@@ -3,7 +3,7 @@
3
3
  require 'pseudohiki/converter'
4
4
 
5
5
  options = PseudoHiki::OptionManager.new
6
- options.set_options_from_command_line
6
+ options.parse_command_line_options
7
7
 
8
8
  if $KCODE
9
9
  PseudoHiki::OptionManager::ENCODING_REGEXP.each do |pat, encoding|
@@ -0,0 +1,132 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'htmlelement'
4
+ require 'uri'
5
+
6
+ class HtmlElement
7
+ module Utils
8
+ def self.collect_elements(tree)
9
+ [].tap do |elms|
10
+ tree.traverse do |elm|
11
+ matched = yield elm
12
+ elms.push elm if matched
13
+ end
14
+ end
15
+ end
16
+
17
+ def self.collect_elements_by_name(tree, name)
18
+ collect_elements(tree) do |elm|
19
+ elm.kind_of? HtmlElement and elm.tagname == name
20
+ end
21
+ end
22
+
23
+ class LinkManager
24
+ SEP = "/".freeze
25
+ SCHEME_RE = /^(https?|ftp):\/\//
26
+ DEFAULT_SCHEME = 'http://'
27
+
28
+ def initialize(domain_name, from_host_names=[], scheme=DEFAULT_SCHEME)
29
+ domain_name += SEP unless domain_name.end_with?(SEP)
30
+ domain_name = scheme + domain_name unless SCHEME_RE =~ domain_name
31
+ @domain_name = URI.parse(domain_name)
32
+ @domain_name_re = Regexp.compile(Regexp.escape(domain_name))
33
+ unless from_host_names.empty?
34
+ @from_host_names_re = compile_from_names_re(from_host_names)
35
+ end
36
+ end
37
+
38
+ def unify_host_names(url)
39
+ return url unless @from_host_names_re
40
+ url.sub(@from_host_names_re, @domain_name.host)
41
+ end
42
+
43
+ def convert_to_relative_path(url)
44
+ return url unless SCHEME_RE =~ url
45
+ return "./".freeze if default_domain?(url)
46
+ (URI.parse(url) - @domain_name).to_s
47
+ end
48
+
49
+ def use_relative_path_for_in_domain_links(html)
50
+ links = Utils.collect_elements_by_name(html, "a".freeze)
51
+ links.each do |a|
52
+ href = a["href"]
53
+ href = unify_host_names(href)
54
+ href = convert_to_relative_path(href)
55
+ a["href"] = href
56
+ end
57
+ html
58
+ end
59
+
60
+ def external_link?(url)
61
+ if SCHEME_RE.match(url)
62
+ URI.parse(url).host != @domain_name.host
63
+ end
64
+ end
65
+
66
+ private
67
+
68
+ def compile_from_names_re(from_host_names)
69
+ escaped_names = from_host_names.map {|name| Regexp.escape(name) }
70
+ Regexp.compile(escaped_names.join("|"))
71
+ end
72
+
73
+ def default_domain?(url)
74
+ url += SEP unless url.end_with?(SEP)
75
+ (URI.parse(url) - @domain_name).to_s.empty?
76
+ end
77
+ end
78
+
79
+ class TableManager
80
+ TH, TD, CAPTION = %w(th td caption)
81
+ ROWSPAN, COLSPAN = %w(rowspan colspan)
82
+ SCOPE, COL, ROW = %w(scope col row)
83
+
84
+ def self.assign_scope(table)
85
+ @manager.assign_scope(table)
86
+ end
87
+
88
+ def guess_header_scope(table)
89
+ col_scope = COL
90
+ row_scope = ROW
91
+
92
+ cell_with_index(table) do |cell, i, j|
93
+ return if span_set?(cell, ROWSPAN) or span_set?(cell, COLSPAN)
94
+ col_scope = nil unless (i == 0) == (cell.tagname == TH)
95
+ row_scope = nil unless (j == 0) == (cell.tagname == TH)
96
+ end
97
+
98
+ col_scope or row_scope
99
+ end
100
+
101
+ def assign_scope(table)
102
+ scope = guess_header_scope(table)
103
+ return table unless scope
104
+ Utils.collect_elements_by_name(table, TH).each do |th|
105
+ th[SCOPE] = scope
106
+ end
107
+ table
108
+ end
109
+
110
+ private
111
+
112
+ def cell_with_index(table)
113
+ children_except_caption(table).each_with_index do |tr, i|
114
+ tr.children.each_with_index do |cell, j|
115
+ yield cell, i, j
116
+ end
117
+ end
118
+ end
119
+
120
+ def children_except_caption(table)
121
+ children = table.children
122
+ children[0].tagname == CAPTION ? children[1..-1] : children
123
+ end
124
+
125
+ def span_set?(cell, span)
126
+ cell[span] && cell[span] > 1
127
+ end
128
+
129
+ @manager = new
130
+ end
131
+ end
132
+ end
data/lib/htmlelement.rb CHANGED
@@ -41,7 +41,7 @@ class HtmlElement
41
41
 
42
42
  ELEMENT_TYPES = {
43
43
  :BLOCK => %w(html body div table colgroup thead tbody ul ol dl head p pre blockquote style),
44
- :HEADING_TYPE_BLOCK => %w(dt dd tr title h1 h2 h3 h4 h5 h6),
44
+ :HEADING_TYPE_BLOCK => %w(dt dd tr title caption h1 h2 h3 h4 h5 h6),
45
45
  :LIST_ITEM_TYPE_BLOCK => %w(li col),
46
46
  :EMPTY_BLOCK => %w(img meta link base input hr)
47
47
  }
@@ -38,14 +38,21 @@ module PseudoHiki
38
38
  end
39
39
 
40
40
  class BlockStack < TreeStack
41
- def pop
42
- current_node.parse_leafs
43
- super
41
+ attr_reader :stack
42
+
43
+ def pop_with_breaker(breaker=nil)
44
+ current_node.parse_leafs(breaker)
45
+ pop
46
+ end
47
+
48
+ def current_heading_level
49
+ i = @stack.rindex {|node| node.kind_of? BlockElement::HeadingNode }
50
+ @stack[i].level || 0
44
51
  end
45
52
  end
46
53
 
47
54
  class BlockLeaf < BlockStack::Leaf
48
- attr_accessor :level, :node_id
55
+ attr_accessor :level, :node_id, :decorator
49
56
 
50
57
  class << self
51
58
  attr_accessor :head_re
@@ -81,7 +88,7 @@ module PseudoHiki
81
88
  super(stack)
82
89
  end
83
90
 
84
- def parse_leafs
91
+ def parse_leafs(breaker)
85
92
  parsed = InlineParser.parse(join)
86
93
  clear
87
94
  concat(parsed)
@@ -131,6 +138,10 @@ module PseudoHiki
131
138
  first.level if first # @cached_level ||= (first.level if first)
132
139
  end
133
140
 
141
+ def decorator
142
+ first.decorator if first
143
+ end
144
+
134
145
  def push_self(stack)
135
146
  @stack = stack
136
147
  super(stack)
@@ -140,11 +151,11 @@ module PseudoHiki
140
151
  not (kind_of? breaker.block and level == breaker.level)
141
152
  end
142
153
 
143
- def parse_leafs; end
154
+ def parse_leafs(breaker); end
144
155
 
145
156
  def add_leaf(line, blockparser)
146
157
  leaf = create_leaf(line, blockparser)
147
- blockparser.stack.pop while blockparser.breakable?(leaf)
158
+ blockparser.stack.pop_with_breaker(leaf) while blockparser.breakable?(leaf)
148
159
  blockparser.stack.push leaf
149
160
  end
150
161
 
@@ -156,8 +167,8 @@ module PseudoHiki
156
167
  end
157
168
 
158
169
  class NonNestedBlockNode < BlockNode
159
- def parse_leafs
160
- each {|leaf| leaf.parse_leafs }
170
+ def parse_leafs(breaker)
171
+ each {|leaf| leaf.parse_leafs(breaker) }
161
172
  end
162
173
  end
163
174
 
@@ -175,13 +186,15 @@ module PseudoHiki
175
186
  end
176
187
  end
177
188
 
189
+ class UnmatchedSectioningTagError < StandardError; end
190
+
178
191
  module BlockElement
179
192
  {
180
- BlockLeaf => %w(DescLeaf VerbatimLeaf TableLeaf CommentOutLeaf BlockNodeEnd HrLeaf),
193
+ BlockLeaf => %w(DescLeaf VerbatimLeaf TableLeaf CommentOutLeaf BlockNodeEnd HrLeaf DecoratorLeaf SectioningNodeEnd),
181
194
  NonNestedBlockLeaf => %w(QuoteLeaf ParagraphLeaf),
182
195
  NestedBlockLeaf => %w(HeadingLeaf),
183
196
  ListTypeLeaf => %w(ListLeaf EnumLeaf),
184
- BlockNode => %w(DescNode VerbatimNode TableNode CommentOutNode HrNode),
197
+ BlockNode => %w(DescNode VerbatimNode TableNode CommentOutNode HrNode DecoratorNode SectioningNode),
185
198
  NonNestedBlockNode => %w(QuoteNode ParagraphNode),
186
199
  NestedBlockNode => %w(HeadingNode),
187
200
  ListTypeBlockNode => %w(ListNode EnumNode),
@@ -204,15 +217,100 @@ module PseudoHiki
204
217
  attr_accessor :in_block_tag
205
218
 
206
219
  def add_leaf(line, blockparser)
207
- return @stack.pop if VERBATIM_END =~ line
220
+ return @stack.pop_with_breaker if VERBATIM_END =~ line
208
221
  return super(line, blockparser) unless @in_block_tag
209
222
  line = " #{line}" if BlockNodeEnd.head_re =~ line and not @in_block_tag
210
223
  @stack.push VerbatimLeaf.create(line, @in_block_tag)
211
224
  end
212
225
  end
213
226
 
227
+ class DecoratorNode
228
+ DECORATOR_PAT = /\A(?:([^\[\]:]+))?(?:\[([^\[\]]+)\])?(?::\s*(\S.*))?/o
229
+ LABEL_SEP = [":"]
230
+
231
+ class DecoratorItem < Struct.new(:string, :type, :id, :value)
232
+ def self.create(leaf)
233
+ m = DECORATOR_PAT.match(leaf.join)
234
+ return nil unless m
235
+ args = m.to_a
236
+ if i = leaf.index(LABEL_SEP)
237
+ value = leaf.dup
238
+ value.shift(i + 1)
239
+ args[-1] = lstrip_value!(value)
240
+ end
241
+ self.new(*args)
242
+ end
243
+
244
+ def self.lstrip_value!(value)
245
+ head_val = value[0][0]
246
+ if head_val.kind_of? String and head_val.start_with? " ".freeze
247
+ value[0][0] = head_val.lstrip
248
+ end
249
+ value
250
+ end
251
+ end
252
+
253
+ def parse_leafs(breaker)
254
+ decorator = {}
255
+ breaker.decorator = decorator
256
+ @stack.remove_current_node.each do |leaf|
257
+ if item = DecoratorItem.create(leaf)
258
+ decorator[item.type || :id] = item
259
+ end
260
+ end
261
+ end
262
+
263
+ def breakable?(breaker)
264
+ return super if breaker.kind_of? DecoratorLeaf
265
+ parse_leafs(breaker)
266
+ @stack.current_node.breakable?(breaker)
267
+ end
268
+ end
269
+
270
+ class DecoratorLeaf
271
+ def push_sectioning_node(stack, node_class)
272
+ node = node_class.new
273
+ m = DecoratorNode::DECORATOR_PAT.match(join)
274
+ node.node_id = m[2]
275
+ node.section_level = stack.current_heading_level if node.kind_of? SectioningNode
276
+ stack.push(node)
277
+ end
278
+
279
+ def push_self(stack)
280
+ decorator_type = self[0][0]
281
+ if decorator_type.start_with? "begin[".freeze
282
+ push_sectioning_node(stack, SectioningNode)
283
+ elsif decorator_type.start_with? "end[".freeze
284
+ push_sectioning_node(stack, SectioningNodeEnd)
285
+ else
286
+ super
287
+ end
288
+ end
289
+ end
290
+
291
+ class SectioningNode
292
+ attr_accessor :section_level
293
+
294
+ def breakable?(breaker)
295
+ breaker.kind_of? HeadingLeaf and @section_level >= breaker.level
296
+ end
297
+ end
298
+
299
+ class SectioningNodeEnd
300
+ def push_self(stack)
301
+ n = stack.stack.rindex do |node|
302
+ node.kind_of? SectioningNode and node.node_id == node_id
303
+ end
304
+ raise UnmatchedSectioningTagError unless n
305
+ stack.pop until stack.stack.length == n
306
+ rescue UnmatchedSectioningTagError => e
307
+ STDERR.puts "#{e}: The start tag for '#{node_id}' is not found."
308
+ # FIXME: The handling of this error should be changed appropriately.
309
+ end
310
+ end
311
+
214
312
  class QuoteNode
215
- def parse_leafs
313
+ def parse_leafs(breaker)
216
314
  self[0] = BlockParser.parse(self[0], AutoLink::Off)
217
315
  end
218
316
  end
@@ -273,7 +371,8 @@ module PseudoHiki
273
371
  [HrLeaf, HrNode],
274
372
  [ListLeaf, ListNode],
275
373
  [EnumLeaf, EnumNode],
276
- [BlockNodeEnd, BlockNodeEnd] # special case
374
+ [BlockNodeEnd, BlockNodeEnd], # special case
375
+ [DecoratorLeaf, DecoratorNode]
277
376
  ].each do |leaf, node|
278
377
  PARENT_NODE[leaf] = node
279
378
  end
@@ -286,6 +385,7 @@ module PseudoHiki
286
385
  ['!', HeadingLeaf],
287
386
  ['""', QuoteLeaf],
288
387
  ['||', TableLeaf],
388
+ ['//@', DecoratorLeaf],
289
389
  ['//', CommentOutLeaf],
290
390
  ['----\s*$', HrLeaf]]
291
391
 
@@ -331,7 +431,7 @@ module PseudoHiki
331
431
  def read_lines(lines)
332
432
  each_line = lines.respond_to?(:each_line) ? :each_line : :each
333
433
  lines.send(each_line) {|line| @stack.current_node.add_leaf(line, self) }
334
- @stack.pop
434
+ @stack.pop_with_breaker
335
435
  end
336
436
  end
337
437