review-retrovert 0.9.11 → 0.10.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.
- checksums.yaml +4 -4
- data/.editorconfig +1 -1
- data/.pinact.yaml +12 -0
- data/.ruby-version +1 -1
- data/Dockerfile +2 -1
- data/Dockerfile.local +13 -0
- data/Gemfile.lock +62 -35
- data/Rakefile +9 -0
- data/aqua.yaml +20 -0
- data/hooks/post_push +14 -1
- data/lib/review/retrovert/cli.rb +6 -0
- data/lib/review/retrovert/converter.rb +38 -0
- data/lib/review/retrovert/version.rb +1 -1
- data/lib/review/retrovert/yamlconfig.rb +38 -1
- data/review-retrovert.gemspec +18 -4
- data/zizmor.yml +5 -0
- metadata +53 -127
- data/.github/FUNDING.yml +0 -4
- data/.github/workflows/docker-build.yml +0 -26
- data/.github/workflows/release.yml +0 -26
- data/.github/workflows/retrovert.yml +0 -91
- data/testdata/mybook/.gitignore +0 -13
- data/testdata/mybook/.textlintrc +0 -11
- data/testdata/mybook/README.md +0 -43
- data/testdata/mybook/Rakefile +0 -24
- data/testdata/mybook/catalog.yml +0 -60
- data/testdata/mybook/config-noretrovert.yml +0 -404
- data/testdata/mybook/config-retrovert.yml +0 -16
- data/testdata/mybook/config-starter.yml +0 -264
- data/testdata/mybook/config.yml +0 -404
- data/testdata/mybook/contents/00-preface.re +0 -129
- data/testdata/mybook/contents/01-install.re +0 -305
- data/testdata/mybook/contents/02-tutorial.re +0 -1228
- data/testdata/mybook/contents/03-syntax.re +0 -4610
- data/testdata/mybook/contents/04-customize.re +0 -1064
- data/testdata/mybook/contents/05-faq.re +0 -606
- data/testdata/mybook/contents/06-bestpractice.re +0 -1343
- data/testdata/mybook/contents/91-compare.re +0 -418
- data/testdata/mybook/contents/92-filelist.re +0 -125
- data/testdata/mybook/contents/93-background.re +0 -267
- data/testdata/mybook/contents/99-postface.re +0 -39
- data/testdata/mybook/contents/r0-inner.re +0 -2
- data/testdata/mybook/contents/r0-root.re +0 -49
- data/testdata/mybook/contents/table.csv +0 -4
- data/testdata/mybook/contents/test.txt +0 -1
- data/testdata/mybook/contents/ut.re +0 -5
- data/testdata/mybook/css/normalize.css +0 -349
- data/testdata/mybook/css/webstyle.css +0 -692
- data/testdata/mybook/data/terms.txt +0 -3
- data/testdata/mybook/data/words.txt +0 -15
- data/testdata/mybook/images/03-syntax/favicon-16x16.png +0 -0
- data/testdata/mybook/images/03-syntax/figure_heretop.png +0 -0
- data/testdata/mybook/images/03-syntax/index-page.png +0 -0
- data/testdata/mybook/images/03-syntax/order-detail.png +0 -0
- data/testdata/mybook/images/03-syntax/tw-icon1.jpg +0 -0
- data/testdata/mybook/images/03-syntax/tw-icon2.jpg +0 -0
- data/testdata/mybook/images/03-syntax/tw-icon3.jpg +0 -0
- data/testdata/mybook/images/03-syntax/tw-icon4.jpg +0 -0
- data/testdata/mybook/images/04-customize/caption_pagebreak.png +0 -0
- data/testdata/mybook/images/04-customize/chaptitlepage_sample.png +0 -0
- data/testdata/mybook/images/04-customize/section_decoration_samples.png +0 -0
- data/testdata/mybook/images/05-faq/codeblock_rpadding1.png +0 -0
- data/testdata/mybook/images/05-faq/codeblock_rpadding2.png +0 -0
- data/testdata/mybook/images/05-faq/dummy-image.png +0 -0
- data/testdata/mybook/images/06-bestpractice/figure_heretop.png +0 -0
- data/testdata/mybook/images/06-bestpractice/font_beramono.png +0 -0
- data/testdata/mybook/images/06-bestpractice/heading_design1.png +0 -0
- data/testdata/mybook/images/06-bestpractice/heading_design2.png +0 -0
- data/testdata/mybook/images/06-bestpractice/margin_book.png +0 -0
- data/testdata/mybook/images/06-bestpractice/multiline-title.png +0 -0
- data/testdata/mybook/images/06-bestpractice/preface_numbered.png +0 -0
- data/testdata/mybook/images/06-bestpractice/program_border.png +0 -0
- data/testdata/mybook/images/06-bestpractice/sechead_design_4.png +0 -0
- data/testdata/mybook/images/06-bestpractice/section_title_wlines.png +0 -0
- data/testdata/mybook/images/06-bestpractice/titlepage-samples.png +0 -0
- data/testdata/mybook/images/93-background/bug913.png +0 -0
- data/testdata/mybook/images/93-background/slide2.png +0 -0
- data/testdata/mybook/images/avatar-b.png +0 -0
- data/testdata/mybook/images/avatar-g.png +0 -0
- data/testdata/mybook/images/avatar-r.png +0 -0
- data/testdata/mybook/images/caution-icon.png +0 -0
- data/testdata/mybook/images/cover_a5.pdf +0 -0
- data/testdata/mybook/images/cover_b5.pdf +0 -0
- data/testdata/mybook/images/info-icon.png +0 -0
- data/testdata/mybook/images/tw-icon.jpg +0 -0
- data/testdata/mybook/images/warning-icon.png +0 -0
- data/testdata/mybook/layouts/layout.epub.erb +0 -29
- data/testdata/mybook/layouts/layout.html5.erb +0 -108
- data/testdata/mybook/layouts/layout.tex.erb +0 -432
- data/testdata/mybook/layouts/layout.tex.erb.orig +0 -386
- data/testdata/mybook/lib/hooks/beforetexcompile.rb +0 -55
- data/testdata/mybook/lib/ruby/review-book.rb +0 -64
- data/testdata/mybook/lib/ruby/review-builder.rb +0 -1342
- data/testdata/mybook/lib/ruby/review-cli.rb +0 -58
- data/testdata/mybook/lib/ruby/review-compiler.rb +0 -1176
- data/testdata/mybook/lib/ruby/review-epubbuilder.rb +0 -33
- data/testdata/mybook/lib/ruby/review-epubmaker.rb +0 -609
- data/testdata/mybook/lib/ruby/review-htmlbuilder.rb +0 -949
- data/testdata/mybook/lib/ruby/review-latexbuilder.rb +0 -1065
- data/testdata/mybook/lib/ruby/review-maker.rb +0 -346
- data/testdata/mybook/lib/ruby/review-markdownbuilder.rb +0 -945
- data/testdata/mybook/lib/ruby/review-markdownmaker.rb +0 -91
- data/testdata/mybook/lib/ruby/review-monkeypatch.rb +0 -93
- data/testdata/mybook/lib/ruby/review-pdfmaker.rb +0 -546
- data/testdata/mybook/lib/ruby/review-textbuilder.rb +0 -36
- data/testdata/mybook/lib/ruby/review-tocparser.rb +0 -285
- data/testdata/mybook/lib/ruby/review-webmaker.rb +0 -448
- data/testdata/mybook/lib/tasks/mytasks.rake +0 -31
- data/testdata/mybook/lib/tasks/review.rake +0 -156
- data/testdata/mybook/lib/tasks/review.rake.orig +0 -72
- data/testdata/mybook/lib/tasks/starter.rake +0 -470
- data/testdata/mybook/locale.yml +0 -6
- data/testdata/mybook/review-ext.rb +0 -206
- data/testdata/mybook/sty/indexstyle.ist +0 -25
- data/testdata/mybook/sty/jumoline.sty +0 -310
- data/testdata/mybook/sty/mycolophon.sty +0 -81
- data/testdata/mybook/sty/mystyle.sty +0 -8
- data/testdata/mybook/sty/mytextsize.sty +0 -94
- data/testdata/mybook/sty/mytitlepage.sty +0 -126
- data/testdata/mybook/sty/review-base.sty +0 -276
- data/testdata/mybook/sty/reviewmacro.sty +0 -60
- data/testdata/mybook/sty/starter-codeblock.sty +0 -463
- data/testdata/mybook/sty/starter-color.sty +0 -134
- data/testdata/mybook/sty/starter-font.sty +0 -112
- data/testdata/mybook/sty/starter-heading.sty +0 -561
- data/testdata/mybook/sty/starter-misc.sty +0 -894
- data/testdata/mybook/sty/starter-note.sty +0 -180
- data/testdata/mybook/sty/starter-section.sty +0 -262
- data/testdata/mybook/sty/starter-talklist.sty +0 -105
- data/testdata/mybook/sty/starter-toc.sty +0 -72
- data/testdata/mybook/sty/starter-util.sty +0 -39
- data/testdata/mybook/sty/starter.sty +0 -36
- data/testdata/mybook/sty/ut.sty +0 -26
- data/testdata/mybook/style.css +0 -597
- data/testdata/mybook/ut-catalog.yml +0 -61
- data/testdata/mybook/ut-config-starter.yml +0 -3
- data/testdata/mybook/ut-config.yml +0 -404
|
@@ -1,1176 +0,0 @@
|
|
|
1
|
-
# -*- coding: utf-8 -*-
|
|
2
|
-
|
|
3
|
-
###
|
|
4
|
-
### ReVIEW::Compilerクラスとその関連クラスを拡張する
|
|
5
|
-
###
|
|
6
|
-
|
|
7
|
-
require 'set'
|
|
8
|
-
require 'review/compiler'
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
module ReVIEW
|
|
12
|
-
|
|
13
|
-
defined?(Compiler) or raise "internal error: Compiler not found."
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
class Compiler
|
|
17
|
-
|
|
18
|
-
#------------------------------ original code
|
|
19
|
-
|
|
20
|
-
=begin
|
|
21
|
-
|
|
22
|
-
def initialize(strategy)
|
|
23
|
-
@strategy = strategy
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
attr_reader :strategy
|
|
27
|
-
|
|
28
|
-
def compile(chap)
|
|
29
|
-
@chapter = chap
|
|
30
|
-
do_compile
|
|
31
|
-
@strategy.result
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
class SyntaxElement
|
|
35
|
-
def initialize(name, type, argc, &block)
|
|
36
|
-
@name = name
|
|
37
|
-
@type = type
|
|
38
|
-
@argc_spec = argc
|
|
39
|
-
@checker = block
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
attr_reader :name
|
|
43
|
-
|
|
44
|
-
def check_args(args)
|
|
45
|
-
unless @argc_spec === args.size
|
|
46
|
-
raise CompileError, "wrong # of parameters (block command //#{@name}, expect #{@argc_spec} but #{args.size})"
|
|
47
|
-
end
|
|
48
|
-
if @checker
|
|
49
|
-
@checker.call(*args)
|
|
50
|
-
end
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
def min_argc
|
|
54
|
-
case @argc_spec
|
|
55
|
-
when Range then @argc_spec.begin
|
|
56
|
-
when Integer then @argc_spec
|
|
57
|
-
else
|
|
58
|
-
raise TypeError, "argc_spec is not Range/Integer: #{inspect}"
|
|
59
|
-
end
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
def block_required?
|
|
63
|
-
@type == :block
|
|
64
|
-
end
|
|
65
|
-
|
|
66
|
-
def block_allowed?
|
|
67
|
-
@type == :block or @type == :optional
|
|
68
|
-
end
|
|
69
|
-
end
|
|
70
|
-
|
|
71
|
-
SYNTAX = {}
|
|
72
|
-
|
|
73
|
-
def self.defblock(name, argc, optional = false, &block)
|
|
74
|
-
defsyntax name, (optional ? :optional : :block), argc, &block
|
|
75
|
-
end
|
|
76
|
-
|
|
77
|
-
def self.defsingle(name, argc, &block)
|
|
78
|
-
defsyntax name, :line, argc, &block
|
|
79
|
-
end
|
|
80
|
-
|
|
81
|
-
def self.defsyntax(name, type, argc, &block)
|
|
82
|
-
SYNTAX[name] = SyntaxElement.new(name, type, argc, &block)
|
|
83
|
-
end
|
|
84
|
-
|
|
85
|
-
def self.definline(name)
|
|
86
|
-
INLINE[name] = InlineSyntaxElement.new(name)
|
|
87
|
-
end
|
|
88
|
-
|
|
89
|
-
def syntax_defined?(name)
|
|
90
|
-
SYNTAX.key?(name.to_sym)
|
|
91
|
-
end
|
|
92
|
-
|
|
93
|
-
def syntax_descriptor(name)
|
|
94
|
-
SYNTAX[name.to_sym]
|
|
95
|
-
end
|
|
96
|
-
|
|
97
|
-
class InlineSyntaxElement
|
|
98
|
-
def initialize(name)
|
|
99
|
-
@name = name
|
|
100
|
-
end
|
|
101
|
-
|
|
102
|
-
attr_reader :name
|
|
103
|
-
end
|
|
104
|
-
|
|
105
|
-
INLINE = {}
|
|
106
|
-
|
|
107
|
-
def inline_defined?(name)
|
|
108
|
-
INLINE.key?(name.to_sym)
|
|
109
|
-
end
|
|
110
|
-
|
|
111
|
-
defblock :read, 0
|
|
112
|
-
defblock :lead, 0
|
|
113
|
-
defblock :list, 2..3
|
|
114
|
-
defblock :emlist, 0..2
|
|
115
|
-
defblock :cmd, 0..1
|
|
116
|
-
defblock :table, 0..2
|
|
117
|
-
defblock :imgtable, 0..2
|
|
118
|
-
defblock :emtable, 0..1
|
|
119
|
-
defblock :quote, 0
|
|
120
|
-
defblock :image, 2..3, true
|
|
121
|
-
defblock :source, 0..2
|
|
122
|
-
defblock :listnum, 2..3
|
|
123
|
-
defblock :emlistnum, 0..2
|
|
124
|
-
defblock :bibpaper, 2..3, true
|
|
125
|
-
defblock :doorquote, 1
|
|
126
|
-
defblock :talk, 0
|
|
127
|
-
defblock :texequation, 0
|
|
128
|
-
defblock :graph, 1..3
|
|
129
|
-
defblock :indepimage, 1..3, true
|
|
130
|
-
defblock :numberlessimage, 1..3, true
|
|
131
|
-
|
|
132
|
-
defblock :address, 0
|
|
133
|
-
defblock :blockquote, 0
|
|
134
|
-
defblock :bpo, 0
|
|
135
|
-
defblock :flushright, 0
|
|
136
|
-
defblock :centering, 0
|
|
137
|
-
defblock :note, 0..1
|
|
138
|
-
defblock :memo, 0..1
|
|
139
|
-
defblock :info, 0..1
|
|
140
|
-
defblock :important, 0..1
|
|
141
|
-
defblock :caution, 0..1
|
|
142
|
-
defblock :notice, 0..1
|
|
143
|
-
defblock :warning, 0..1
|
|
144
|
-
defblock :tip, 0..1
|
|
145
|
-
defblock :box, 0..1
|
|
146
|
-
defblock :comment, 0..1, true
|
|
147
|
-
defblock :embed, 0..1
|
|
148
|
-
|
|
149
|
-
defsingle :footnote, 2
|
|
150
|
-
defsingle :noindent, 0
|
|
151
|
-
defsingle :blankline, 0
|
|
152
|
-
defsingle :pagebreak, 0
|
|
153
|
-
defsingle :hr, 0
|
|
154
|
-
defsingle :parasep, 0
|
|
155
|
-
defsingle :label, 1
|
|
156
|
-
defsingle :raw, 1
|
|
157
|
-
defsingle :tsize, 1
|
|
158
|
-
defsingle :include, 1
|
|
159
|
-
defsingle :olnum, 1
|
|
160
|
-
defsingle :firstlinenum, 1
|
|
161
|
-
|
|
162
|
-
definline :chapref
|
|
163
|
-
definline :chap
|
|
164
|
-
definline :title
|
|
165
|
-
definline :img
|
|
166
|
-
definline :imgref
|
|
167
|
-
definline :icon
|
|
168
|
-
definline :list
|
|
169
|
-
definline :table
|
|
170
|
-
definline :fn
|
|
171
|
-
definline :kw
|
|
172
|
-
definline :ruby
|
|
173
|
-
definline :bou
|
|
174
|
-
definline :ami
|
|
175
|
-
definline :b
|
|
176
|
-
definline :dtp
|
|
177
|
-
definline :code
|
|
178
|
-
definline :bib
|
|
179
|
-
definline :hd
|
|
180
|
-
definline :href
|
|
181
|
-
definline :recipe
|
|
182
|
-
definline :column
|
|
183
|
-
definline :tcy
|
|
184
|
-
|
|
185
|
-
definline :abbr
|
|
186
|
-
definline :acronym
|
|
187
|
-
definline :cite
|
|
188
|
-
definline :dfn
|
|
189
|
-
definline :em
|
|
190
|
-
definline :kbd
|
|
191
|
-
definline :q
|
|
192
|
-
definline :samp
|
|
193
|
-
definline :strong
|
|
194
|
-
definline :var
|
|
195
|
-
definline :big
|
|
196
|
-
definline :small
|
|
197
|
-
definline :del
|
|
198
|
-
definline :ins
|
|
199
|
-
definline :sup
|
|
200
|
-
definline :sub
|
|
201
|
-
definline :tt
|
|
202
|
-
definline :i
|
|
203
|
-
definline :tti
|
|
204
|
-
definline :ttb
|
|
205
|
-
definline :u
|
|
206
|
-
definline :raw
|
|
207
|
-
definline :br
|
|
208
|
-
definline :m
|
|
209
|
-
definline :uchar
|
|
210
|
-
definline :idx
|
|
211
|
-
definline :hidx
|
|
212
|
-
definline :comment
|
|
213
|
-
definline :include
|
|
214
|
-
definline :tcy
|
|
215
|
-
definline :embed
|
|
216
|
-
definline :pageref
|
|
217
|
-
|
|
218
|
-
private
|
|
219
|
-
|
|
220
|
-
def do_compile
|
|
221
|
-
f = LineInput.new(StringIO.new(@chapter.content))
|
|
222
|
-
@strategy.bind self, @chapter, Location.new(@chapter.basename, f)
|
|
223
|
-
tagged_section_init
|
|
224
|
-
while f.next?
|
|
225
|
-
case f.peek
|
|
226
|
-
when /\A\#@/
|
|
227
|
-
f.gets # Nothing to do
|
|
228
|
-
when /\A=+[\[\s\{]/
|
|
229
|
-
compile_headline f.gets
|
|
230
|
-
when /\A\s+\*/
|
|
231
|
-
compile_ulist f
|
|
232
|
-
when /\A\s+\d+\./
|
|
233
|
-
compile_olist f
|
|
234
|
-
when /\A\s*:\s/
|
|
235
|
-
compile_dlist f
|
|
236
|
-
when %r{\A//\}}
|
|
237
|
-
f.gets
|
|
238
|
-
error 'block end seen but not opened'
|
|
239
|
-
when %r{\A//[a-z]+}
|
|
240
|
-
name, args, lines = read_command(f)
|
|
241
|
-
syntax = syntax_descriptor(name)
|
|
242
|
-
unless syntax
|
|
243
|
-
error "unknown command: //#{name}"
|
|
244
|
-
compile_unknown_command args, lines
|
|
245
|
-
next
|
|
246
|
-
end
|
|
247
|
-
compile_command syntax, args, lines
|
|
248
|
-
when %r{\A//}
|
|
249
|
-
line = f.gets
|
|
250
|
-
warn "`//' seen but is not valid command: #{line.strip.inspect}"
|
|
251
|
-
if block_open?(line)
|
|
252
|
-
warn 'skipping block...'
|
|
253
|
-
read_block(f, false)
|
|
254
|
-
end
|
|
255
|
-
else
|
|
256
|
-
if f.peek.strip.empty?
|
|
257
|
-
f.gets
|
|
258
|
-
next
|
|
259
|
-
end
|
|
260
|
-
compile_paragraph f
|
|
261
|
-
end
|
|
262
|
-
end
|
|
263
|
-
close_all_tagged_section
|
|
264
|
-
end
|
|
265
|
-
|
|
266
|
-
def compile_headline(line)
|
|
267
|
-
@headline_indexs ||= [@chapter.number.to_i - 1]
|
|
268
|
-
m = /\A(=+)(?:\[(.+?)\])?(?:\{(.+?)\})?(.*)/.match(line)
|
|
269
|
-
level = m[1].size
|
|
270
|
-
tag = m[2]
|
|
271
|
-
label = m[3]
|
|
272
|
-
caption = m[4].strip
|
|
273
|
-
index = level - 1
|
|
274
|
-
if tag
|
|
275
|
-
if tag !~ %r{\A/}
|
|
276
|
-
if caption.empty?
|
|
277
|
-
warn 'headline is empty.'
|
|
278
|
-
end
|
|
279
|
-
close_current_tagged_section(level)
|
|
280
|
-
open_tagged_section(tag, level, label, caption)
|
|
281
|
-
else
|
|
282
|
-
open_tag = tag[1..-1]
|
|
283
|
-
prev_tag_info = @tagged_section.pop
|
|
284
|
-
if prev_tag_info.nil? || prev_tag_info.first != open_tag
|
|
285
|
-
error "#{open_tag} is not opened."
|
|
286
|
-
end
|
|
287
|
-
close_tagged_section(*prev_tag_info)
|
|
288
|
-
end
|
|
289
|
-
else
|
|
290
|
-
if caption.empty?
|
|
291
|
-
warn 'headline is empty.'
|
|
292
|
-
end
|
|
293
|
-
if @headline_indexs.size > (index + 1)
|
|
294
|
-
@headline_indexs = @headline_indexs[0..index]
|
|
295
|
-
end
|
|
296
|
-
if @headline_indexs[index].nil?
|
|
297
|
-
@headline_indexs[index] = 0
|
|
298
|
-
end
|
|
299
|
-
@headline_indexs[index] += 1
|
|
300
|
-
close_current_tagged_section(level)
|
|
301
|
-
@strategy.headline level, label, caption
|
|
302
|
-
end
|
|
303
|
-
end
|
|
304
|
-
|
|
305
|
-
def close_current_tagged_section(level)
|
|
306
|
-
while @tagged_section.last and @tagged_section.last[1] >= level
|
|
307
|
-
close_tagged_section(* @tagged_section.pop)
|
|
308
|
-
end
|
|
309
|
-
end
|
|
310
|
-
|
|
311
|
-
def headline(level, label, caption)
|
|
312
|
-
@strategy.headline level, label, caption
|
|
313
|
-
end
|
|
314
|
-
|
|
315
|
-
def tagged_section_init
|
|
316
|
-
@tagged_section = []
|
|
317
|
-
end
|
|
318
|
-
|
|
319
|
-
def open_tagged_section(tag, level, label, caption)
|
|
320
|
-
mid = "#{tag}_begin"
|
|
321
|
-
unless @strategy.respond_to?(mid)
|
|
322
|
-
error "strategy does not support tagged section: #{tag}"
|
|
323
|
-
headline level, label, caption
|
|
324
|
-
return
|
|
325
|
-
end
|
|
326
|
-
@tagged_section.push [tag, level]
|
|
327
|
-
@strategy.__send__ mid, level, label, caption
|
|
328
|
-
end
|
|
329
|
-
|
|
330
|
-
def close_tagged_section(tag, level)
|
|
331
|
-
mid = "#{tag}_end"
|
|
332
|
-
if @strategy.respond_to?(mid)
|
|
333
|
-
@strategy.__send__ mid, level
|
|
334
|
-
else
|
|
335
|
-
error "strategy does not support block op: #{mid}"
|
|
336
|
-
end
|
|
337
|
-
end
|
|
338
|
-
|
|
339
|
-
def close_all_tagged_section
|
|
340
|
-
until @tagged_section.empty?
|
|
341
|
-
close_tagged_section(* @tagged_section.pop)
|
|
342
|
-
end
|
|
343
|
-
end
|
|
344
|
-
|
|
345
|
-
def compile_ulist(f)
|
|
346
|
-
level = 0
|
|
347
|
-
f.while_match(/\A\s+\*|\A\#@/) do |line|
|
|
348
|
-
next if line =~ /\A\#@/
|
|
349
|
-
|
|
350
|
-
buf = [text(line.sub(/\*+/, '').strip)]
|
|
351
|
-
f.while_match(/\A\s+(?!\*)\S/) do |cont|
|
|
352
|
-
buf.push text(cont.strip)
|
|
353
|
-
end
|
|
354
|
-
|
|
355
|
-
line =~ /\A\s+(\*+)/
|
|
356
|
-
current_level = $1.size
|
|
357
|
-
if level == current_level
|
|
358
|
-
@strategy.ul_item_end
|
|
359
|
-
# body
|
|
360
|
-
@strategy.ul_item_begin buf
|
|
361
|
-
elsif level < current_level # down
|
|
362
|
-
level_diff = current_level - level
|
|
363
|
-
level = current_level
|
|
364
|
-
(1..(level_diff - 1)).to_a.reverse_each do |i|
|
|
365
|
-
@strategy.ul_begin { i }
|
|
366
|
-
@strategy.ul_item_begin []
|
|
367
|
-
end
|
|
368
|
-
@strategy.ul_begin { level }
|
|
369
|
-
@strategy.ul_item_begin buf
|
|
370
|
-
elsif level > current_level # up
|
|
371
|
-
level_diff = level - current_level
|
|
372
|
-
level = current_level
|
|
373
|
-
(1..level_diff).to_a.reverse_each do |i|
|
|
374
|
-
@strategy.ul_item_end
|
|
375
|
-
@strategy.ul_end { level + i }
|
|
376
|
-
end
|
|
377
|
-
@strategy.ul_item_end
|
|
378
|
-
# body
|
|
379
|
-
@strategy.ul_item_begin buf
|
|
380
|
-
end
|
|
381
|
-
end
|
|
382
|
-
|
|
383
|
-
(1..level).to_a.reverse_each do |i|
|
|
384
|
-
@strategy.ul_item_end
|
|
385
|
-
@strategy.ul_end { i }
|
|
386
|
-
end
|
|
387
|
-
end
|
|
388
|
-
|
|
389
|
-
def compile_olist(f)
|
|
390
|
-
@strategy.ol_begin
|
|
391
|
-
f.while_match(/\A\s+\d+\.|\A\#@/) do |line|
|
|
392
|
-
next if line =~ /\A\#@/
|
|
393
|
-
|
|
394
|
-
num = line.match(/(\d+)\./)[1]
|
|
395
|
-
buf = [text(line.sub(/\d+\./, '').strip)]
|
|
396
|
-
f.while_match(/\A\s+(?!\d+\.)\S/) do |cont|
|
|
397
|
-
buf.push text(cont.strip)
|
|
398
|
-
end
|
|
399
|
-
@strategy.ol_item buf, num
|
|
400
|
-
end
|
|
401
|
-
@strategy.ol_end
|
|
402
|
-
end
|
|
403
|
-
|
|
404
|
-
def compile_dlist(f)
|
|
405
|
-
@strategy.dl_begin
|
|
406
|
-
while /\A\s*:/ =~ f.peek
|
|
407
|
-
@strategy.dt text(f.gets.sub(/\A\s*:/, '').strip)
|
|
408
|
-
desc = f.break(/\A(\S|\s*:|\s+\d+\.\s|\s+\*\s)/).map { |line| text(line.strip) }
|
|
409
|
-
@strategy.dd(desc)
|
|
410
|
-
f.skip_blank_lines
|
|
411
|
-
f.skip_comment_lines
|
|
412
|
-
end
|
|
413
|
-
@strategy.dl_end
|
|
414
|
-
end
|
|
415
|
-
|
|
416
|
-
def compile_paragraph(f)
|
|
417
|
-
buf = []
|
|
418
|
-
f.until_match(%r{\A//|\A\#@}) do |line|
|
|
419
|
-
break if line.strip.empty?
|
|
420
|
-
buf.push text(line.sub(/^(\t+)\s*/) { |m| '<!ESCAPETAB!>' * m.size }.strip.gsub('<!ESCAPETAB!>', "\t"))
|
|
421
|
-
end
|
|
422
|
-
@strategy.paragraph buf
|
|
423
|
-
end
|
|
424
|
-
|
|
425
|
-
def read_command(f)
|
|
426
|
-
line = f.gets
|
|
427
|
-
name = line.slice(/[a-z]+/).to_sym
|
|
428
|
-
ignore_inline = (name == :embed)
|
|
429
|
-
args = parse_args(line.sub(%r{\A//[a-z]+}, '').rstrip.chomp('{'), name)
|
|
430
|
-
@strategy.doc_status[name] = true
|
|
431
|
-
lines = block_open?(line) ? read_block(f, ignore_inline) : nil
|
|
432
|
-
@strategy.doc_status[name] = nil
|
|
433
|
-
[name, args, lines]
|
|
434
|
-
end
|
|
435
|
-
|
|
436
|
-
def block_open?(line)
|
|
437
|
-
line.rstrip[-1, 1] == '{'
|
|
438
|
-
end
|
|
439
|
-
|
|
440
|
-
def read_block(f, ignore_inline)
|
|
441
|
-
head = f.lineno
|
|
442
|
-
buf = []
|
|
443
|
-
f.until_match(%r{\A//\}}) do |line|
|
|
444
|
-
if ignore_inline
|
|
445
|
-
buf.push line
|
|
446
|
-
elsif line !~ /\A\#@/
|
|
447
|
-
buf.push text(line.rstrip)
|
|
448
|
-
end
|
|
449
|
-
end
|
|
450
|
-
unless %r{\A//\}} =~ f.peek
|
|
451
|
-
error "unexpected EOF (block begins at: #{head})"
|
|
452
|
-
return buf
|
|
453
|
-
end
|
|
454
|
-
f.gets # discard terminator
|
|
455
|
-
buf
|
|
456
|
-
end
|
|
457
|
-
|
|
458
|
-
def parse_args(str, _name = nil)
|
|
459
|
-
return [] if str.empty?
|
|
460
|
-
scanner = StringScanner.new(str)
|
|
461
|
-
words = []
|
|
462
|
-
while word = scanner.scan(/(\[\]|\[.*?[^\\]\])/)
|
|
463
|
-
w2 = word[1..-2].gsub(/\\(.)/) do
|
|
464
|
-
ch = $1
|
|
465
|
-
(ch == ']' or ch == '\\') ? ch : '\\' + ch
|
|
466
|
-
end
|
|
467
|
-
words << w2
|
|
468
|
-
end
|
|
469
|
-
unless scanner.eos?
|
|
470
|
-
error "argument syntax error: #{scanner.rest} in #{str.inspect}"
|
|
471
|
-
return []
|
|
472
|
-
end
|
|
473
|
-
words
|
|
474
|
-
end
|
|
475
|
-
|
|
476
|
-
def compile_command(syntax, args, lines)
|
|
477
|
-
unless @strategy.respond_to?(syntax.name)
|
|
478
|
-
error "strategy does not support command: //#{syntax.name}"
|
|
479
|
-
compile_unknown_command args, lines
|
|
480
|
-
return
|
|
481
|
-
end
|
|
482
|
-
begin
|
|
483
|
-
syntax.check_args args
|
|
484
|
-
rescue CompileError => err
|
|
485
|
-
error err.message
|
|
486
|
-
args = ['(NoArgument)'] * syntax.min_argc
|
|
487
|
-
end
|
|
488
|
-
if syntax.block_allowed?
|
|
489
|
-
compile_block syntax, args, lines
|
|
490
|
-
else
|
|
491
|
-
if lines
|
|
492
|
-
error "block is not allowed for command //#{syntax.name}; ignore"
|
|
493
|
-
end
|
|
494
|
-
compile_single syntax, args
|
|
495
|
-
end
|
|
496
|
-
end
|
|
497
|
-
|
|
498
|
-
def compile_unknown_command(args, lines)
|
|
499
|
-
@strategy.unknown_command args, lines
|
|
500
|
-
end
|
|
501
|
-
|
|
502
|
-
def compile_block(syntax, args, lines)
|
|
503
|
-
@strategy.__send__(syntax.name, (lines || default_block(syntax)), *args)
|
|
504
|
-
end
|
|
505
|
-
|
|
506
|
-
def default_block(syntax)
|
|
507
|
-
if syntax.block_required?
|
|
508
|
-
error "block is required for //#{syntax.name}; use empty block"
|
|
509
|
-
end
|
|
510
|
-
[]
|
|
511
|
-
end
|
|
512
|
-
|
|
513
|
-
def compile_single(syntax, args)
|
|
514
|
-
@strategy.__send__(syntax.name, *args)
|
|
515
|
-
end
|
|
516
|
-
|
|
517
|
-
def replace_fence(str)
|
|
518
|
-
str.gsub(/@<(\w+)>([$|])(.+?)(\2)/) do
|
|
519
|
-
op = $1
|
|
520
|
-
arg = $3.gsub('@', "\x01").gsub('\\}') { '\\\\}' }.gsub('}') { '\}' }.sub(/(?:\\)+$/) { |m| '\\\\' * m.size }
|
|
521
|
-
"@<#{op}>{#{arg}}"
|
|
522
|
-
end
|
|
523
|
-
end
|
|
524
|
-
|
|
525
|
-
def text(str)
|
|
526
|
-
return '' if str.empty?
|
|
527
|
-
words = replace_fence(str).split(/(@<\w+>\{(?:[^\}\\]|\\.)*?\})/, -1)
|
|
528
|
-
words.each do |w|
|
|
529
|
-
if w.scan(/@<\w+>/).size > 1 && !/\A@<raw>/.match(w)
|
|
530
|
-
error "`@<xxx>' seen but is not valid inline op: #{w}"
|
|
531
|
-
end
|
|
532
|
-
end
|
|
533
|
-
result = @strategy.nofunc_text(words.shift)
|
|
534
|
-
until words.empty?
|
|
535
|
-
result << compile_inline(words.shift.gsub(/\\\}/, '}').gsub(/\\\\/, '\\'))
|
|
536
|
-
result << @strategy.nofunc_text(words.shift)
|
|
537
|
-
end
|
|
538
|
-
result.gsub("\x01", '@')
|
|
539
|
-
rescue => err
|
|
540
|
-
error err.message
|
|
541
|
-
end
|
|
542
|
-
public :text # called from strategy
|
|
543
|
-
|
|
544
|
-
def compile_inline(str)
|
|
545
|
-
op, arg = /\A@<(\w+)>\{(.*?)\}\z/.match(str).captures
|
|
546
|
-
unless inline_defined?(op)
|
|
547
|
-
raise CompileError, "no such inline op: #{op}"
|
|
548
|
-
end
|
|
549
|
-
unless @strategy.respond_to?("inline_#{op}")
|
|
550
|
-
raise "strategy does not support inline op: @<#{op}>"
|
|
551
|
-
end
|
|
552
|
-
@strategy.__send__("inline_#{op}", arg)
|
|
553
|
-
rescue => err
|
|
554
|
-
error err.message
|
|
555
|
-
@strategy.nofunc_text(str)
|
|
556
|
-
end
|
|
557
|
-
|
|
558
|
-
def warn(msg)
|
|
559
|
-
@strategy.warn msg
|
|
560
|
-
end
|
|
561
|
-
|
|
562
|
-
def error(msg)
|
|
563
|
-
@strategy.error msg
|
|
564
|
-
end
|
|
565
|
-
|
|
566
|
-
=end
|
|
567
|
-
|
|
568
|
-
#------------------------------
|
|
569
|
-
|
|
570
|
-
## ブロック命令
|
|
571
|
-
defblock :program, 0..3 ## プログラム
|
|
572
|
-
defblock :terminal, 0..3 ## ターミナル
|
|
573
|
-
defblock :output, 0..3 ## 出力結果
|
|
574
|
-
defblock :sideimage, 2..3 ## テキストの横に画像を表示
|
|
575
|
-
defblock :abstract, 0 ## 章の概要
|
|
576
|
-
defblock :chapterauthor, 1 ## 章の著者
|
|
577
|
-
defblock :talklist, 0..1 ## 会話リスト
|
|
578
|
-
defblock :talk, 1..3, true ## 会話項目
|
|
579
|
-
defblock :t, 1..3, true ## 会話項目(ショートカット用)
|
|
580
|
-
defblock :desclist, 0..1 ## キーと説明文のリスト
|
|
581
|
-
defblock :desc, 1..2, true ## キーと説明文のリスト
|
|
582
|
-
defblock :list, 0..3 ## (上書き)
|
|
583
|
-
defblock :listnum, 0..3 ## (上書き)
|
|
584
|
-
defblock :note, 0..2 ## (上書き)
|
|
585
|
-
defblock :texequation, 0..2 ## (上書き)
|
|
586
|
-
defblock :table, 0..3 ## (上書き)
|
|
587
|
-
defblock :imgtable, 0..3 ## (上書き)
|
|
588
|
-
|
|
589
|
-
defsingle :makechaptitlepage, 0..1 ## 章扉をつける
|
|
590
|
-
defsingle :needvspace, 2 ## 縦方向のスペースがなければ改ページ
|
|
591
|
-
defsingle :paragraphend, 0 ## 段の終わりにスペースを入れる
|
|
592
|
-
defsingle :subparagraphend, 0## 小段の終わりにスペースを入れる(あれば)
|
|
593
|
-
defsingle :vspace, 2 ## 縦方向の空きを入れる(\vspace)
|
|
594
|
-
defsingle :addvspace, 2 ## 縦方向の空きを入れる(\addvspace)
|
|
595
|
-
defsingle :tsize, 1..2 ## (上書き)
|
|
596
|
-
|
|
597
|
-
## インライン命令
|
|
598
|
-
definline :par ## 箇条書き内で改段落するのに使う
|
|
599
|
-
definline :balloon ## コード内でのふきだし説明(Re:VIEW3から追加)
|
|
600
|
-
definline :eq ## 数式を参照
|
|
601
|
-
definline :secref ## 節(Section)や項(Subsection)を参照
|
|
602
|
-
definline :noteref ## ノートを参照
|
|
603
|
-
definline :hlink ## @<href>{}の代わり
|
|
604
|
-
definline :term ## @<idx>{}かつゴシック体
|
|
605
|
-
definline :termnoidx ## ゴシック体にするだけで索引には登録しない
|
|
606
|
-
definline :file ## ファイル名
|
|
607
|
-
definline :userinput ## ユーザ入力
|
|
608
|
-
definline :nop ## 引数をそのまま表示 (No Operation)
|
|
609
|
-
definline :letitgo ## (nopのエイリアス名)
|
|
610
|
-
definline :foldhere ## 折り返し箇所を手動で指定
|
|
611
|
-
definline :cursor ## ターミナルでのカーソル
|
|
612
|
-
definline :qq ## 「``」と「''」で囲う
|
|
613
|
-
definline :weak ## 目立たせない(@<strong>{} の反対)
|
|
614
|
-
definline :small ## 文字サイズを小さく
|
|
615
|
-
definline :xsmall ## 文字サイズをもっと小さく
|
|
616
|
-
definline :xxsmall ## 文字サイズをもっともっと小さく
|
|
617
|
-
definline :large ## 文字サイズを大きく
|
|
618
|
-
definline :xlarge ## 文字サイズをもっと大きく
|
|
619
|
-
definline :xxlarge ## 文字サイズをもっともっと大きく
|
|
620
|
-
definline :xstrong ## 文字を大きくした@<strong>{}
|
|
621
|
-
definline :xxstrong ## 文字をもっと大きくした@<strong>{}
|
|
622
|
-
definline :w ## キーに対応した単語に展開
|
|
623
|
-
definline :wb ## キーに対応した単語に展開、かつ太字で表示
|
|
624
|
-
definline :W ## キーに対応した単語に展開、かつ強調表示
|
|
625
|
-
|
|
626
|
-
private
|
|
627
|
-
|
|
628
|
-
## パーサを再帰呼び出しに対応させる
|
|
629
|
-
|
|
630
|
-
def do_compile
|
|
631
|
-
f = LineInput.new(StringIO.new(@chapter.content))
|
|
632
|
-
@strategy.bind self, @chapter, Location.new(@chapter.basename, f)
|
|
633
|
-
tagged_section_init()
|
|
634
|
-
parse_document(f, false)
|
|
635
|
-
close_all_tagged_section()
|
|
636
|
-
end
|
|
637
|
-
|
|
638
|
-
BLOCK_END_REXP = /\A\/\/\}\s*$/
|
|
639
|
-
|
|
640
|
-
def parse_document(f, block_cmd)
|
|
641
|
-
while f.next?
|
|
642
|
-
case f.peek
|
|
643
|
-
when /\A\#@/
|
|
644
|
-
f.gets # Nothing to do
|
|
645
|
-
when /\A=+[\[\s\{]/
|
|
646
|
-
if block_cmd #+
|
|
647
|
-
line = f.gets #+
|
|
648
|
-
error "'#{line.strip}': should close '//#{block_cmd}' block before sectioning." #+
|
|
649
|
-
end #+
|
|
650
|
-
compile_headline f.gets
|
|
651
|
-
#when /\A\s+\*/ #-
|
|
652
|
-
# compile_ulist f #-
|
|
653
|
-
when LIST_ITEM_REXP #+
|
|
654
|
-
compile_list(f) #+
|
|
655
|
-
when /\A\s+\d+\./
|
|
656
|
-
compile_olist f
|
|
657
|
-
when /\A\s*:\s/
|
|
658
|
-
compile_dlist f
|
|
659
|
-
#when %r{\A//\}} #-
|
|
660
|
-
when BLOCK_END_REXP #+
|
|
661
|
-
return if block_cmd #+
|
|
662
|
-
f.gets
|
|
663
|
-
#error 'block end seen but not opened' #-
|
|
664
|
-
error "'//}': block-end found, but no block command opened." #+
|
|
665
|
-
#when %r{\A//[a-z]+} #-
|
|
666
|
-
# name, args, lines = read_command(f) #-
|
|
667
|
-
# syntax = syntax_descriptor(name) #-
|
|
668
|
-
# unless syntax #-
|
|
669
|
-
# error "unknown command: //#{name}" #-
|
|
670
|
-
# compile_unknown_command args, lines #-
|
|
671
|
-
# next #-
|
|
672
|
-
# end #-
|
|
673
|
-
# compile_command syntax, args, lines #-
|
|
674
|
-
when /\A\/\/\w+/ #+
|
|
675
|
-
parse_block_command(f) #+
|
|
676
|
-
when %r{\A//}
|
|
677
|
-
line = f.gets
|
|
678
|
-
warn "`//' seen but is not valid command: #{line.strip.inspect}"
|
|
679
|
-
if block_open?(line)
|
|
680
|
-
warn 'skipping block...'
|
|
681
|
-
read_block(f, false)
|
|
682
|
-
end
|
|
683
|
-
else
|
|
684
|
-
if f.peek.strip.empty?
|
|
685
|
-
f.gets
|
|
686
|
-
next
|
|
687
|
-
end
|
|
688
|
-
compile_paragraph f
|
|
689
|
-
end
|
|
690
|
-
end
|
|
691
|
-
end
|
|
692
|
-
|
|
693
|
-
## コードブロックのタブ展開を、LaTeXコマンドの展開より先に行うよう変更。
|
|
694
|
-
##
|
|
695
|
-
## ・たとえば '\\' を '\\textbackslash{}' に展開してからタブを空白文字に
|
|
696
|
-
## 展開しても、正しい展開にはならないことは明らか。先にタブを空白文字に
|
|
697
|
-
## 置き換えてから、'\\' を '\\textbackslash{}' に展開すべき。
|
|
698
|
-
## ・またタブ文字の展開は、本来はBuilderではなくCompilerで行うべきだが、
|
|
699
|
-
## Re:VIEWの設計がまずいのでそうなっていない。
|
|
700
|
-
## ・'//table' と '//embed' ではタブ文字の展開は行わない。
|
|
701
|
-
def read_block_for(cmdname, f) # 追加
|
|
702
|
-
disable_comment = cmdname == :embed # '//embed' では行コメントを読み飛ばさない
|
|
703
|
-
ignore_inline = _ignore_inline?(cmdname) # '//embed' と '//table' ではインライン命令を解釈しない
|
|
704
|
-
enable_detab = cmdname !~ /\A(?:em)?table\z/ # '//table' ではタブ展開しない
|
|
705
|
-
f.enable_comment(false) if disable_comment
|
|
706
|
-
lines = read_block(f, ignore_inline, enable_detab) { "//#{cmdname}" }
|
|
707
|
-
f.enable_comment(true) if disable_comment
|
|
708
|
-
return lines
|
|
709
|
-
end
|
|
710
|
-
def _ignore_inline?(cmdname)
|
|
711
|
-
return RAW_BLOCK_COMMANDS[cmdname]
|
|
712
|
-
end
|
|
713
|
-
def read_block(f, ignore_inline, enable_detab=true) # 上書き
|
|
714
|
-
head = f.lineno
|
|
715
|
-
buf = []
|
|
716
|
-
builder = @strategy #+
|
|
717
|
-
#f.until_match(%r{\A//\}}) do |line| #-
|
|
718
|
-
f.until_match(BLOCK_END_REXP) do |line| #+
|
|
719
|
-
if ignore_inline
|
|
720
|
-
buf.push line
|
|
721
|
-
elsif line !~ /\A\#@/
|
|
722
|
-
#buf.push text(line.rstrip) #-
|
|
723
|
-
line = line.rstrip #+
|
|
724
|
-
line = builder.detab(line) if enable_detab #+
|
|
725
|
-
buf << parse_text(line) #+
|
|
726
|
-
end
|
|
727
|
-
end
|
|
728
|
-
#unless %r{\A//\}} =~ f.peek #-
|
|
729
|
-
unless f.peek() =~ BLOCK_END_REXP #+
|
|
730
|
-
if block_given? #+
|
|
731
|
-
error "#{yield} (at line #{head}): block command not closed." #+
|
|
732
|
-
else #+
|
|
733
|
-
error "unexpected EOF (block begins at: #{head})"
|
|
734
|
-
end #+
|
|
735
|
-
return buf
|
|
736
|
-
end
|
|
737
|
-
f.gets # discard terminator
|
|
738
|
-
buf
|
|
739
|
-
end
|
|
740
|
-
|
|
741
|
-
RAW_BLOCK_COMMANDS = {
|
|
742
|
-
embed: true,
|
|
743
|
-
raw: true,
|
|
744
|
-
table: true,
|
|
745
|
-
list: true,
|
|
746
|
-
emlist: true,
|
|
747
|
-
listnum: true,
|
|
748
|
-
emlistnum: true,
|
|
749
|
-
source: true,
|
|
750
|
-
program: true,
|
|
751
|
-
terminal: true,
|
|
752
|
-
cmd: true,
|
|
753
|
-
output: true,
|
|
754
|
-
}
|
|
755
|
-
|
|
756
|
-
## ブロック命令を入れ子可能に変更('//note' と '//quote')
|
|
757
|
-
|
|
758
|
-
def parse_block_command(f)
|
|
759
|
-
line = f.gets()
|
|
760
|
-
lineno = f.lineno
|
|
761
|
-
line =~ /\A\/\/(\w+)(\[.*\])?(\{)?$/ or
|
|
762
|
-
error "'#{line.strip}': invalid block command format."
|
|
763
|
-
cmdname = $1.intern; argstr = $2; curly = $3
|
|
764
|
-
##
|
|
765
|
-
prev = @strategy.doc_status[cmdname]
|
|
766
|
-
@strategy.doc_status[cmdname] = true
|
|
767
|
-
## 引数を取り出す
|
|
768
|
-
syntax = syntax_descriptor(cmdname) or
|
|
769
|
-
error "'//#{cmdname}': unknown command"
|
|
770
|
-
args = parse_args(argstr || "", cmdname)
|
|
771
|
-
begin
|
|
772
|
-
syntax.check_args args
|
|
773
|
-
rescue CompileError => err
|
|
774
|
-
error err.message
|
|
775
|
-
end
|
|
776
|
-
## ブロックをとらないコマンドにブロックが指定されていたらエラー
|
|
777
|
-
if curly && !syntax.block_allowed?
|
|
778
|
-
error "'//#{cmdname}': this command should not take block (but given)."
|
|
779
|
-
end
|
|
780
|
-
## ブロックの入れ子をサポートしてあれば、再帰的にパースする
|
|
781
|
-
handler = "on_#{cmdname}_block"
|
|
782
|
-
builder = @strategy
|
|
783
|
-
if builder.respond_to?(handler)
|
|
784
|
-
if curly
|
|
785
|
-
builder.__send__(handler, *args) do
|
|
786
|
-
parse_document(f, cmdname)
|
|
787
|
-
end
|
|
788
|
-
s = f.peek()
|
|
789
|
-
f.peek() =~ BLOCK_END_REXP or
|
|
790
|
-
error "'//#{cmdname}': not closed (reached to EOF)"
|
|
791
|
-
f.gets() ## '//}' を読み捨てる
|
|
792
|
-
else
|
|
793
|
-
builder.__send__(handler, *args)
|
|
794
|
-
end
|
|
795
|
-
## そうでなければ、従来と同じようにパースする
|
|
796
|
-
elsif builder.respond_to?(cmdname)
|
|
797
|
-
if !syntax.block_allowed?
|
|
798
|
-
builder.__send__(cmdname, *args)
|
|
799
|
-
elsif curly
|
|
800
|
-
lines = read_block_for(cmdname, f)
|
|
801
|
-
builder.__send__(cmdname, lines, *args)
|
|
802
|
-
else
|
|
803
|
-
lines = default_block(syntax)
|
|
804
|
-
builder.__send__(cmdname, lines, *args)
|
|
805
|
-
end
|
|
806
|
-
else
|
|
807
|
-
error "'//#{cmdname}': #{builder.class.name} not support this command"
|
|
808
|
-
end
|
|
809
|
-
##
|
|
810
|
-
@strategy.doc_status[cmdname] = prev
|
|
811
|
-
end
|
|
812
|
-
|
|
813
|
-
## 箇条書きの文法を拡張
|
|
814
|
-
|
|
815
|
-
LIST_ITEM_REXP = /\A( +)(\*+|\-+) +/ # '*' は unordred list、'-' は ordered list
|
|
816
|
-
|
|
817
|
-
def compile_list(f)
|
|
818
|
-
line = f.gets()
|
|
819
|
-
line =~ LIST_ITEM_REXP
|
|
820
|
-
indent = $1
|
|
821
|
-
char = $2[0]
|
|
822
|
-
$2.length == 1 or
|
|
823
|
-
error "#{$2[0]=='*'?'un':''}ordered list should start with level 1"
|
|
824
|
-
line = parse_list(f, line, indent, char, 1)
|
|
825
|
-
f.ungets(line)
|
|
826
|
-
end
|
|
827
|
-
|
|
828
|
-
def parse_list(f, line, indent, char, level)
|
|
829
|
-
if char != '*' && line =~ LIST_ITEM_REXP
|
|
830
|
-
start_num, _ = $'.lstrip().split(/\s+/, 2)
|
|
831
|
-
end
|
|
832
|
-
st = @strategy
|
|
833
|
-
char == '*' ? st.ul_begin { level } : st.ol_begin(start_num) { level }
|
|
834
|
-
while line =~ LIST_ITEM_REXP # /\A( +)(\*+|\-+) +/
|
|
835
|
-
$1 == indent or
|
|
836
|
-
error "mismatched indentation of #{$2[0]=='*'?'un':''}ordered list"
|
|
837
|
-
mark = $2
|
|
838
|
-
text = $'
|
|
839
|
-
if mark.length == level
|
|
840
|
-
break unless mark[0] == char
|
|
841
|
-
line = parse_item(f, text.lstrip(), indent, char, level)
|
|
842
|
-
elsif mark.length < level
|
|
843
|
-
break
|
|
844
|
-
else
|
|
845
|
-
raise "internal error"
|
|
846
|
-
end
|
|
847
|
-
end
|
|
848
|
-
char == '*' ? st.ul_end { level } : st.ol_end { level }
|
|
849
|
-
return line
|
|
850
|
-
end
|
|
851
|
-
|
|
852
|
-
def parse_item(f, text, indent, char, level)
|
|
853
|
-
if char != '*'
|
|
854
|
-
num, text = text.split(/\s+/, 2)
|
|
855
|
-
text ||= ''
|
|
856
|
-
end
|
|
857
|
-
#
|
|
858
|
-
buf = [parse_text(text)]
|
|
859
|
-
while (line = f.gets()) && line =~ /\A( +)/ && $1.length > indent.length
|
|
860
|
-
buf << parse_text(line)
|
|
861
|
-
end
|
|
862
|
-
#
|
|
863
|
-
st = @strategy
|
|
864
|
-
char == '*' ? st.ul_item_begin(buf) : st.ol_item_begin(buf, num)
|
|
865
|
-
rexp = LIST_ITEM_REXP # /\A( +)(\*+|\-+) +/
|
|
866
|
-
while line =~ rexp && $2.length > level
|
|
867
|
-
$2.length == level + 1 or
|
|
868
|
-
error "invalid indentation level of (un)ordred list"
|
|
869
|
-
line = parse_list(f, line, indent, $2[0], $2.length)
|
|
870
|
-
end
|
|
871
|
-
char == '*' ? st.ul_item_end() : st.ol_item_end()
|
|
872
|
-
#
|
|
873
|
-
return line
|
|
874
|
-
end
|
|
875
|
-
|
|
876
|
-
def compile_dlist(f)
|
|
877
|
-
@strategy.dl_begin()
|
|
878
|
-
while /\A\s*:/ =~ f.peek()
|
|
879
|
-
dtext = f.gets.sub(/\A\s*:/, '').strip
|
|
880
|
-
@strategy.dt(parse_text(dtext))
|
|
881
|
-
buf = []
|
|
882
|
-
li_rexp = LIST_ITEM_REXP
|
|
883
|
-
indent = nil
|
|
884
|
-
first_p = true
|
|
885
|
-
@strategy.dl_dd_begin()
|
|
886
|
-
while (line = f.peek()) =~ /\A( [ \t]+|\t\s*)/ || line =~ /\A\s*$/
|
|
887
|
-
indent ||= $1
|
|
888
|
-
if indent
|
|
889
|
-
line =~ /\A\s*$/ || line.start_with?(indent) or
|
|
890
|
-
warn "` : #{dtext}': indent mismatched (maybe space and tab are mixed)"
|
|
891
|
-
if _dl_start_list?(line, indent, li_rexp)
|
|
892
|
-
buf, first_p = _dl_par(buf, first_p) unless buf.empty?
|
|
893
|
-
compile_list(f)
|
|
894
|
-
@strategy.noindent # disable indent just after ordered/unordered list
|
|
895
|
-
next
|
|
896
|
-
end
|
|
897
|
-
end
|
|
898
|
-
if line =~ /\A\s*$/
|
|
899
|
-
buf, first_p = _dl_par(buf, first_p) unless buf.empty?
|
|
900
|
-
else
|
|
901
|
-
buf << parse_text(line)
|
|
902
|
-
end
|
|
903
|
-
f.gets()
|
|
904
|
-
end
|
|
905
|
-
_, _ = _dl_par(buf, first_p) unless buf.empty?
|
|
906
|
-
@strategy.dl_dd_end()
|
|
907
|
-
f.skip_blank_lines()
|
|
908
|
-
f.skip_comment_lines()
|
|
909
|
-
end
|
|
910
|
-
@strategy.dl_end()
|
|
911
|
-
end
|
|
912
|
-
|
|
913
|
-
def _dl_start_list?(line, indent, li_rexp)
|
|
914
|
-
return line.start_with?(indent) && line.sub(indent, '') =~ li_rexp && line =~ li_rexp
|
|
915
|
-
end
|
|
916
|
-
|
|
917
|
-
def _dl_par(buf, first_p)
|
|
918
|
-
return buf, first_p if buf.empty?
|
|
919
|
-
if first_p
|
|
920
|
-
@strategy.puts buf.join
|
|
921
|
-
else
|
|
922
|
-
@strategy.paragraph(buf)
|
|
923
|
-
end
|
|
924
|
-
return [], false
|
|
925
|
-
end
|
|
926
|
-
|
|
927
|
-
public
|
|
928
|
-
|
|
929
|
-
## 入れ子のインライン命令をパースできるよう上書き
|
|
930
|
-
def parse_text(line)
|
|
931
|
-
stack = []
|
|
932
|
-
tag_name = nil
|
|
933
|
-
close_char = nil
|
|
934
|
-
items = [""]
|
|
935
|
-
nestable = true
|
|
936
|
-
scan_inline_command(line) do |text, s1, s2, s3|
|
|
937
|
-
if s1 # ex: '@<code>{', '@<b>{', '@<m>$'
|
|
938
|
-
if nestable
|
|
939
|
-
items << text
|
|
940
|
-
stack.push([tag_name, close_char, items])
|
|
941
|
-
s1 =~ /\A@<(\w+)>([{$|])\z/ or raise "internal error"
|
|
942
|
-
tag_name = $1
|
|
943
|
-
close_char = $2 == '{' ? '}' : $2
|
|
944
|
-
items = [""]
|
|
945
|
-
nestable = false if ignore_nested_inline_command?(tag_name)
|
|
946
|
-
else
|
|
947
|
-
items[-1] << text << s1
|
|
948
|
-
end
|
|
949
|
-
elsif s2 # '\}' or '\\' (not '\$' nor '\|')
|
|
950
|
-
text << (close_char == '}' ? s2[1] : s2)
|
|
951
|
-
items[-1] << text
|
|
952
|
-
elsif s3 # '}', '$', or '|'
|
|
953
|
-
items[-1] << text
|
|
954
|
-
if close_char == s3
|
|
955
|
-
items.delete_if {|x| x.empty? }
|
|
956
|
-
elem = [tag_name, {}, items]
|
|
957
|
-
tag_name, close_char, items = stack.pop()
|
|
958
|
-
items << elem << ""
|
|
959
|
-
nestable = true
|
|
960
|
-
else
|
|
961
|
-
items[-1] << s3
|
|
962
|
-
end
|
|
963
|
-
else
|
|
964
|
-
if items.length == 1 && items[-1].empty?
|
|
965
|
-
items[-1] = text
|
|
966
|
-
else
|
|
967
|
-
items[-1] << text
|
|
968
|
-
end
|
|
969
|
-
end
|
|
970
|
-
end
|
|
971
|
-
if tag_name
|
|
972
|
-
error "inline command '@<#{tag_name}>' not closed."
|
|
973
|
-
end
|
|
974
|
-
items.delete_if {|x| x.empty? }
|
|
975
|
-
#
|
|
976
|
-
return compile_inline_command(items)
|
|
977
|
-
end
|
|
978
|
-
|
|
979
|
-
alias text parse_text
|
|
980
|
-
|
|
981
|
-
private
|
|
982
|
-
|
|
983
|
-
def scan_inline_command(line)
|
|
984
|
-
pos = 0
|
|
985
|
-
line.scan(/(\@<\w+>[{$|])|(\\[\\}])|([}$|])/) do
|
|
986
|
-
m = Regexp.last_match
|
|
987
|
-
text = line[pos, m.begin(0)-pos]
|
|
988
|
-
pos = m.end(0)
|
|
989
|
-
yield text, $1, $2, $3
|
|
990
|
-
end
|
|
991
|
-
remained = pos == 0 ? line : line[pos..-1]
|
|
992
|
-
yield remained, nil, nil, nil
|
|
993
|
-
end
|
|
994
|
-
|
|
995
|
-
def compile_inline_command(items)
|
|
996
|
-
buf = ""
|
|
997
|
-
strategy = @strategy
|
|
998
|
-
items.each do |x|
|
|
999
|
-
case x
|
|
1000
|
-
when String
|
|
1001
|
-
buf << strategy.nofunc_text(x)
|
|
1002
|
-
when Array
|
|
1003
|
-
tag_name, attrs, children = x
|
|
1004
|
-
op = tag_name
|
|
1005
|
-
inline_defined?(op) or
|
|
1006
|
-
raise CompileError, "no such inline op: #{op}"
|
|
1007
|
-
if strategy.respond_to?("on_inline_#{op}")
|
|
1008
|
-
buf << strategy.__send__("on_inline_#{op}") {|both_p|
|
|
1009
|
-
if !both_p
|
|
1010
|
-
compile_inline_command(children)
|
|
1011
|
-
else
|
|
1012
|
-
[compile_inline_command(children), children]
|
|
1013
|
-
end
|
|
1014
|
-
}
|
|
1015
|
-
elsif strategy.respond_to?("inline_#{op}")
|
|
1016
|
-
children.empty? || children.all? {|x| x.is_a?(String) } or
|
|
1017
|
-
error "'@<#{op}>' does not support nested inline commands."
|
|
1018
|
-
buf << strategy.__send__("inline_#{op}", children[0])
|
|
1019
|
-
else
|
|
1020
|
-
error "strategy does not support inline op: @<#{op}> (strategy.class=#{strategy.class})"
|
|
1021
|
-
end
|
|
1022
|
-
else
|
|
1023
|
-
raise "internal error: x=#{x.inspect}"
|
|
1024
|
-
end
|
|
1025
|
-
end
|
|
1026
|
-
buf
|
|
1027
|
-
end
|
|
1028
|
-
|
|
1029
|
-
def ignore_nested_inline_command?(tag_name)
|
|
1030
|
-
return IGNORE_NESTED_INLINE_COMMANDS.include?(tag_name)
|
|
1031
|
-
end
|
|
1032
|
-
|
|
1033
|
-
IGNORE_NESTED_INLINE_COMMANDS = Set.new(['m', 'raw', 'embed', 'idx', 'hidx', 'term'])
|
|
1034
|
-
|
|
1035
|
-
end
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
## コメント「#@#」を読み飛ばす(ただし //embed では読み飛ばさない)
|
|
1039
|
-
class LineInput
|
|
1040
|
-
|
|
1041
|
-
def initialize(f)
|
|
1042
|
-
super
|
|
1043
|
-
@enable_comment = true
|
|
1044
|
-
end
|
|
1045
|
-
|
|
1046
|
-
def enable_comment(flag)
|
|
1047
|
-
@enable_comment = flag
|
|
1048
|
-
end
|
|
1049
|
-
|
|
1050
|
-
def gets
|
|
1051
|
-
line = super
|
|
1052
|
-
if @enable_comment
|
|
1053
|
-
while line && line =~ /\A\#\@\#/
|
|
1054
|
-
line = super
|
|
1055
|
-
end
|
|
1056
|
-
end
|
|
1057
|
-
return line
|
|
1058
|
-
end
|
|
1059
|
-
|
|
1060
|
-
end
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
class Catalog
|
|
1064
|
-
|
|
1065
|
-
def parts_with_chaps
|
|
1066
|
-
## catalog.ymlの「CHAPS:」がnullのときエラーになるのを防ぐ
|
|
1067
|
-
(@yaml['CHAPS'] || []).flatten.compact
|
|
1068
|
-
end
|
|
1069
|
-
|
|
1070
|
-
end
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
class Book::ListIndex
|
|
1074
|
-
|
|
1075
|
-
## '//program' と '//terminal' と '//output' をサポートするよう拡張
|
|
1076
|
-
def self.item_type # override
|
|
1077
|
-
#'(list|listnum)' # original
|
|
1078
|
-
'(list|listnum|program|terminal|output)'
|
|
1079
|
-
end
|
|
1080
|
-
|
|
1081
|
-
## '//list' や '//terminal' のラベル(第1引数)を省略できるよう拡張
|
|
1082
|
-
def self.parse(src, *args) # override
|
|
1083
|
-
items = []
|
|
1084
|
-
seq = 1
|
|
1085
|
-
src.grep(%r{\A//#{item_type()}}) do |line|
|
|
1086
|
-
if id = line.slice(/\[(.*?)\]/, 1)
|
|
1087
|
-
next if id.empty? # 追加
|
|
1088
|
-
items.push item_class().new(id, seq)
|
|
1089
|
-
seq += 1
|
|
1090
|
-
ReVIEW.logger.warn "warning: no ID of #{item_type()} in #{line}" if id.empty?
|
|
1091
|
-
end
|
|
1092
|
-
end
|
|
1093
|
-
new(items, *args)
|
|
1094
|
-
end
|
|
1095
|
-
|
|
1096
|
-
end
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
## ノートブロック(//note)のラベル用
|
|
1100
|
-
class Book::NoteIndex < Book::Index # create new class for '//note'
|
|
1101
|
-
Item = Struct.new(:id, :caption)
|
|
1102
|
-
|
|
1103
|
-
def self.parse(src_lines)
|
|
1104
|
-
rexp = /\A\/\/note\[([^\]]+)\]\[(.*?[^\\])\]/ # $1: label, $2: caption
|
|
1105
|
-
items = src_lines.grep(rexp) {|line|
|
|
1106
|
-
label = $1.strip(); caption = $2.gsub(/\\\]/, ']')
|
|
1107
|
-
next if label.empty?
|
|
1108
|
-
label =~ /\A[-_a-zA-Z0-9]+\z/ or
|
|
1109
|
-
error "'#{line}': invalid label (only alphabet, number, '_' or '-' available)"
|
|
1110
|
-
Item.new(label, caption)
|
|
1111
|
-
}.compact()
|
|
1112
|
-
self.new(items)
|
|
1113
|
-
end
|
|
1114
|
-
|
|
1115
|
-
end
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
## 数式(//texequation)のラベル用
|
|
1119
|
-
class Book::EquationIndex < Book::Index
|
|
1120
|
-
def self.item_type
|
|
1121
|
-
'(texequation)'
|
|
1122
|
-
end
|
|
1123
|
-
end
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
module Book::Compilable
|
|
1127
|
-
|
|
1128
|
-
def note(id)
|
|
1129
|
-
note_index()[id]
|
|
1130
|
-
end
|
|
1131
|
-
|
|
1132
|
-
def note_index
|
|
1133
|
-
@note_index ||= Book::NoteIndex.parse(lines())
|
|
1134
|
-
@note_index
|
|
1135
|
-
end
|
|
1136
|
-
|
|
1137
|
-
def equation(id)
|
|
1138
|
-
equation_index()[id]
|
|
1139
|
-
end
|
|
1140
|
-
|
|
1141
|
-
def equation_index
|
|
1142
|
-
@equation_index ||= Book::EquationIndex.parse(lines())
|
|
1143
|
-
@equation_index
|
|
1144
|
-
end
|
|
1145
|
-
|
|
1146
|
-
def content # override
|
|
1147
|
-
## //list[?] や //terminal[?] の '?' をランダム文字列に置き換える。
|
|
1148
|
-
## こうすると、重複しないラベルをいちいち指定しなくても、ソースコードや
|
|
1149
|
-
## ターミナルにリスト番号がつく。ただし @<list>{} での参照はできない。
|
|
1150
|
-
unless @_done
|
|
1151
|
-
pat1 = Book::ListIndex.item_type # == '(list|listnum|program|terminal|output)'
|
|
1152
|
-
pat2 = Book::TableIndex.item_type # == '(table|imgtable)'
|
|
1153
|
-
pat = "#{pat1[1..-2]}|#{pat2[1..-2]}"
|
|
1154
|
-
@content = @content.gsub(/^\/\/(#{pat})\[\?\]/) { "//#{$1}[#{_random_label()}]" }
|
|
1155
|
-
## 改行コードを「\n」に統一する
|
|
1156
|
-
@content = @content.gsub(/\r\n/, "\n")
|
|
1157
|
-
## (experimental) 範囲コメント('#@+++' '#@---')を行コメント('#@#')に変換
|
|
1158
|
-
@content = @content.gsub(/^\#\@\+\+\+$.*?^\#\@\-\-\-$/m) { $&.gsub(/^/, '#@#') }
|
|
1159
|
-
@_done = true
|
|
1160
|
-
end
|
|
1161
|
-
@content
|
|
1162
|
-
end
|
|
1163
|
-
|
|
1164
|
-
module_function
|
|
1165
|
-
|
|
1166
|
-
def _random_label
|
|
1167
|
-
#"_" + rand().to_s[2..10]
|
|
1168
|
-
"_" + RANDOM.rand().to_s[2..10]
|
|
1169
|
-
end
|
|
1170
|
-
|
|
1171
|
-
RANDOM = Random.new(22360679)
|
|
1172
|
-
|
|
1173
|
-
end
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
end
|