tmtms-review 1.0.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 +7 -0
- data/.travis.yml +9 -0
- data/COPYING +515 -0
- data/ChangeLog +2083 -0
- data/README.rdoc +50 -0
- data/Rakefile +56 -0
- data/VERSION +1 -0
- data/bin/review-check +178 -0
- data/bin/review-checkdep +63 -0
- data/bin/review-compile +205 -0
- data/bin/review-epubmaker +661 -0
- data/bin/review-epubmaker-ng +176 -0
- data/bin/review-index +118 -0
- data/bin/review-pdfmaker +208 -0
- data/bin/review-preproc +142 -0
- data/bin/review-validate +51 -0
- data/bin/review-vol +102 -0
- data/debian/README.Debian +12 -0
- data/debian/README.source +5 -0
- data/debian/changelog +5 -0
- data/debian/compat +1 -0
- data/debian/control +22 -0
- data/debian/copyright +62 -0
- data/debian/docs +6 -0
- data/debian/manpage.1.ex +59 -0
- data/debian/patches/path.diff +91 -0
- data/debian/patches/series +1 -0
- data/debian/review.install +13 -0
- data/debian/review.links +4 -0
- data/debian/rules +13 -0
- data/debian/source/format +1 -0
- data/doc/format.rdoc +582 -0
- data/doc/format_idg.rdoc +180 -0
- data/doc/libepubmaker/sample.yaml +90 -0
- data/doc/quickstart.rdoc +188 -0
- data/doc/ruby-uuid/README +11 -0
- data/doc/ruby-uuid/README.ja +34 -0
- data/doc/sample.css +108 -0
- data/doc/sample.yaml +62 -0
- data/lib/epubmaker.rb +28 -0
- data/lib/epubmaker/content.rb +82 -0
- data/lib/epubmaker/epubv2.rb +418 -0
- data/lib/epubmaker/epubv3.rb +249 -0
- data/lib/epubmaker/producer.rb +204 -0
- data/lib/epubmaker/resource.rb +66 -0
- data/lib/lineinput.rb +155 -0
- data/lib/review.rb +3 -0
- data/lib/review/book.rb +46 -0
- data/lib/review/book/base.rb +235 -0
- data/lib/review/book/chapter.rb +81 -0
- data/lib/review/book/compilable.rb +159 -0
- data/lib/review/book/index.rb +339 -0
- data/lib/review/book/page_metric.rb +38 -0
- data/lib/review/book/parameters.rb +97 -0
- data/lib/review/book/part.rb +44 -0
- data/lib/review/book/volume.rb +65 -0
- data/lib/review/builder.rb +444 -0
- data/lib/review/compiler.rb +550 -0
- data/lib/review/configure.rb +38 -0
- data/lib/review/epubbuilder.rb +18 -0
- data/lib/review/exception.rb +21 -0
- data/lib/review/extentions.rb +3 -0
- data/lib/review/extentions/object.rb +9 -0
- data/lib/review/extentions/string.rb +33 -0
- data/lib/review/htmlbuilder.rb +1097 -0
- data/lib/review/htmllayout.rb +19 -0
- data/lib/review/htmlutils.rb +36 -0
- data/lib/review/i18n.rb +30 -0
- data/lib/review/i18n.yaml +34 -0
- data/lib/review/idgxmlbuilder.rb +1145 -0
- data/lib/review/latexbuilder.rb +815 -0
- data/lib/review/latexindex.rb +35 -0
- data/lib/review/latexutils.rb +79 -0
- data/lib/review/preprocessor.rb +563 -0
- data/lib/review/review.tex.erb +232 -0
- data/lib/review/textbuilder.rb +17 -0
- data/lib/review/textutils.rb +66 -0
- data/lib/review/tocparser.rb +342 -0
- data/lib/review/tocprinter.rb +221 -0
- data/lib/review/topbuilder.rb +785 -0
- data/lib/review/unfold.rb +138 -0
- data/lib/uuid.rb +312 -0
- data/review.gemspec +141 -0
- data/test/CHAPS +2 -0
- data/test/bib.re +13 -0
- data/test/book_test_helper.rb +35 -0
- data/test/test.re +43 -0
- data/test/test_book.rb +598 -0
- data/test/test_book_chapter.rb +418 -0
- data/test/test_book_parameter.rb +42 -0
- data/test/test_book_part.rb +50 -0
- data/test/test_builder.rb +144 -0
- data/test/test_compiler.rb +44 -0
- data/test/test_epubmaker.rb +507 -0
- data/test/test_helper.rb +27 -0
- data/test/test_htmlbuilder.rb +554 -0
- data/test/test_htmlutils.rb +28 -0
- data/test/test_i18n.rb +64 -0
- data/test/test_idgxmlbuilder.rb +589 -0
- data/test/test_index.rb +31 -0
- data/test/test_latexbuilder.rb +656 -0
- data/test/test_lineinput.rb +198 -0
- data/test/test_preprocessor.rb +23 -0
- data/test/test_textutils.rb +68 -0
- data/test/test_topbuilder.rb +244 -0
- data/test/test_uuid.rb +156 -0
- metadata +161 -0
|
@@ -0,0 +1,550 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
#
|
|
3
|
+
# Copyright (c) 2002-2007 Minero Aoki
|
|
4
|
+
# Copyright (c) 2009-2010 Minero Aoki, Kenshi Muto
|
|
5
|
+
#
|
|
6
|
+
# This program is free software.
|
|
7
|
+
# You can distribute or modify this program under the terms of
|
|
8
|
+
# the GNU LGPL, Lesser General Public License version 2.1.
|
|
9
|
+
#
|
|
10
|
+
|
|
11
|
+
require 'review/extentions'
|
|
12
|
+
require 'review/preprocessor'
|
|
13
|
+
require 'review/exception'
|
|
14
|
+
require 'lineinput'
|
|
15
|
+
|
|
16
|
+
module ReVIEW
|
|
17
|
+
|
|
18
|
+
class Location
|
|
19
|
+
def initialize(filename, f)
|
|
20
|
+
@filename = filename
|
|
21
|
+
@f = f
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
attr_reader :filename
|
|
25
|
+
|
|
26
|
+
def lineno
|
|
27
|
+
@f.lineno
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def string
|
|
31
|
+
begin
|
|
32
|
+
"#{@filename}:#{@f.lineno}"
|
|
33
|
+
rescue
|
|
34
|
+
"#{@filename}:nil"
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
alias to_s string
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class Compiler
|
|
43
|
+
|
|
44
|
+
def initialize(strategy)
|
|
45
|
+
@strategy = strategy
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
attr_reader :strategy
|
|
49
|
+
|
|
50
|
+
def compile(chap)
|
|
51
|
+
@chapter = chap
|
|
52
|
+
do_compile
|
|
53
|
+
@strategy.result
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
class SyntaxElement
|
|
57
|
+
def initialize(name, type, argc, &block)
|
|
58
|
+
@name = name
|
|
59
|
+
@type = type
|
|
60
|
+
@argc_spec = argc
|
|
61
|
+
@checker = block
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
attr_reader :name
|
|
65
|
+
|
|
66
|
+
def check_args(args)
|
|
67
|
+
unless @argc_spec === args.size
|
|
68
|
+
raise CompileError, "wrong # of parameters (block command //#{@name}, expect #{@argc_spec} but #{args.size})"
|
|
69
|
+
end
|
|
70
|
+
@checker.call(*args) if @checker
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def min_argc
|
|
74
|
+
case @argc_spec
|
|
75
|
+
when Range then @argc_spec.begin
|
|
76
|
+
when Integer then @argc_spec
|
|
77
|
+
else
|
|
78
|
+
raise TypeError, "argc_spec is not Range/Integer: #{inspect()}"
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def block_required?
|
|
83
|
+
@type == :block
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def block_allowed?
|
|
87
|
+
@type == :block or @type == :optional
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
SYNTAX = {}
|
|
92
|
+
|
|
93
|
+
def Compiler.defblock(name, argc, optional = false, &block)
|
|
94
|
+
defsyntax name, (optional ? :optional : :block), argc, &block
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def Compiler.defsingle(name, argc, &block)
|
|
98
|
+
defsyntax name, :line, argc, &block
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def Compiler.defsyntax(name, type, argc, &block)
|
|
102
|
+
SYNTAX[name] = SyntaxElement.new(name, type, argc, &block)
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def syntax_defined?(name)
|
|
106
|
+
SYNTAX.key?(name.to_sym)
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def syntax_descriptor(name)
|
|
110
|
+
SYNTAX[name.to_sym]
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
class InlineSyntaxElement
|
|
114
|
+
def initialize(name)
|
|
115
|
+
@name = name
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
attr_reader :name
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
INLINE = {}
|
|
122
|
+
|
|
123
|
+
def Compiler.definline(name)
|
|
124
|
+
INLINE[name] = InlineSyntaxElement.new(name)
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def inline_defined?(name)
|
|
128
|
+
INLINE.key?(name.to_sym)
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
defblock :read, 0
|
|
132
|
+
defblock :lead, 0
|
|
133
|
+
defblock :list, 2
|
|
134
|
+
defblock :emlist, 0..1
|
|
135
|
+
defblock :cmd, 0..1
|
|
136
|
+
defblock :table, 0..2
|
|
137
|
+
defblock :quote, 0
|
|
138
|
+
defblock :image, 2..3, true
|
|
139
|
+
defblock :source, 1
|
|
140
|
+
defblock :listnum, 2
|
|
141
|
+
defblock :emlistnum, 0..1
|
|
142
|
+
defblock :bibpaper, 2..3, true
|
|
143
|
+
defblock :doorquote, 1
|
|
144
|
+
defblock :talk, 0
|
|
145
|
+
defblock :texequation, 0
|
|
146
|
+
defblock :graph, 1..3
|
|
147
|
+
|
|
148
|
+
defblock :address, 0
|
|
149
|
+
defblock :blockquote, 0
|
|
150
|
+
defblock :bpo, 0
|
|
151
|
+
defblock :flushright, 0
|
|
152
|
+
defblock :centering, 0
|
|
153
|
+
defblock :note, 0..1
|
|
154
|
+
defblock :box, 0..1
|
|
155
|
+
|
|
156
|
+
defsingle :footnote, 2
|
|
157
|
+
defsingle :comment, 1
|
|
158
|
+
defsingle :noindent, 0
|
|
159
|
+
defsingle :linebreak, 0
|
|
160
|
+
defsingle :pagebreak, 0
|
|
161
|
+
defsingle :indepimage, 1..3
|
|
162
|
+
defsingle :numberlessimage, 1..3
|
|
163
|
+
defsingle :hr, 0
|
|
164
|
+
defsingle :parasep, 0
|
|
165
|
+
defsingle :label, 1
|
|
166
|
+
defsingle :raw, 1
|
|
167
|
+
defsingle :tsize, 1
|
|
168
|
+
defsingle :include, 1
|
|
169
|
+
defsingle :olnum, 1
|
|
170
|
+
|
|
171
|
+
definline :chapref
|
|
172
|
+
definline :chap
|
|
173
|
+
definline :title
|
|
174
|
+
definline :img
|
|
175
|
+
definline :icon
|
|
176
|
+
definline :list
|
|
177
|
+
definline :table
|
|
178
|
+
definline :fn
|
|
179
|
+
definline :kw
|
|
180
|
+
definline :ruby
|
|
181
|
+
definline :bou
|
|
182
|
+
definline :ami
|
|
183
|
+
definline :b
|
|
184
|
+
definline :dtp
|
|
185
|
+
definline :code
|
|
186
|
+
definline :bib
|
|
187
|
+
definline :hd
|
|
188
|
+
definline :href
|
|
189
|
+
definline :recipe
|
|
190
|
+
|
|
191
|
+
definline :abbr
|
|
192
|
+
definline :acronym
|
|
193
|
+
definline :cite
|
|
194
|
+
definline :dfn
|
|
195
|
+
definline :em
|
|
196
|
+
definline :kbd
|
|
197
|
+
definline :q
|
|
198
|
+
definline :samp
|
|
199
|
+
definline :strong
|
|
200
|
+
definline :var
|
|
201
|
+
definline :big
|
|
202
|
+
definline :small
|
|
203
|
+
definline :del
|
|
204
|
+
definline :ins
|
|
205
|
+
definline :sup
|
|
206
|
+
definline :sub
|
|
207
|
+
definline :tt
|
|
208
|
+
definline :i
|
|
209
|
+
definline :tti
|
|
210
|
+
definline :ttb
|
|
211
|
+
definline :u
|
|
212
|
+
definline :raw
|
|
213
|
+
definline :br
|
|
214
|
+
definline :m
|
|
215
|
+
definline :uchar
|
|
216
|
+
definline :idx
|
|
217
|
+
definline :hidx
|
|
218
|
+
definline :comment
|
|
219
|
+
definline :include
|
|
220
|
+
|
|
221
|
+
private
|
|
222
|
+
|
|
223
|
+
def do_compile
|
|
224
|
+
f = LineInput.new(Preprocessor::Strip.new(StringIO.new(@chapter.content)))
|
|
225
|
+
@strategy.bind self, @chapter, Location.new(@chapter.basename, f)
|
|
226
|
+
tagged_section_init
|
|
227
|
+
while f.next?
|
|
228
|
+
case f.peek
|
|
229
|
+
when /\A\#@/
|
|
230
|
+
f.gets # Nothing to do
|
|
231
|
+
when /\A=+[\[\s\{]/
|
|
232
|
+
compile_headline f.gets
|
|
233
|
+
when %r<\A\s+\*>
|
|
234
|
+
compile_ulist f
|
|
235
|
+
when %r<\A\s+\d+\.>
|
|
236
|
+
compile_olist f
|
|
237
|
+
when %r<\A\s*:\s>
|
|
238
|
+
compile_dlist f
|
|
239
|
+
when %r<\A//\}>
|
|
240
|
+
f.gets
|
|
241
|
+
error 'block end seen but not opened'
|
|
242
|
+
when %r<\A//[a-z]+>
|
|
243
|
+
name, args, lines = read_command(f)
|
|
244
|
+
syntax = syntax_descriptor(name)
|
|
245
|
+
unless syntax
|
|
246
|
+
error "unknown command: //#{name}"
|
|
247
|
+
compile_unknown_command args, lines
|
|
248
|
+
next
|
|
249
|
+
end
|
|
250
|
+
compile_command syntax, args, lines
|
|
251
|
+
when %r<\A//>
|
|
252
|
+
line = f.gets
|
|
253
|
+
warn "`//' seen but is not valid command: #{line.strip.inspect}"
|
|
254
|
+
if block_open?(line)
|
|
255
|
+
warn "skipping block..."
|
|
256
|
+
read_block(f)
|
|
257
|
+
end
|
|
258
|
+
else
|
|
259
|
+
if f.peek.strip.empty?
|
|
260
|
+
f.gets
|
|
261
|
+
next
|
|
262
|
+
end
|
|
263
|
+
compile_paragraph f
|
|
264
|
+
end
|
|
265
|
+
end
|
|
266
|
+
close_all_tagged_section
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
def compile_headline(line)
|
|
270
|
+
@headline_indexs ||= [@chapter.number.to_i - 1]
|
|
271
|
+
m = /\A(=+)(?:\[(.+?)\])?(?:\{(.+?)\})?(.*)/.match(line)
|
|
272
|
+
level = m[1].size
|
|
273
|
+
tag = m[2]
|
|
274
|
+
label = m[3]
|
|
275
|
+
caption = m[4].strip
|
|
276
|
+
index = level - 1
|
|
277
|
+
if tag
|
|
278
|
+
if tag !~ /\A\//
|
|
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
|
+
unless prev_tag_info.first == open_tag
|
|
285
|
+
raise CompileError, "#{open_tag} is not opened."
|
|
286
|
+
end
|
|
287
|
+
close_tagged_section(*prev_tag_info)
|
|
288
|
+
end
|
|
289
|
+
else
|
|
290
|
+
if @headline_indexs.size > (index + 1)
|
|
291
|
+
@headline_indexs = @headline_indexs[0..index]
|
|
292
|
+
end
|
|
293
|
+
@headline_indexs[index] = 0 if @headline_indexs[index].nil?
|
|
294
|
+
@headline_indexs[index] += 1
|
|
295
|
+
close_current_tagged_section(level)
|
|
296
|
+
if ReVIEW.book.param["hdnumberingmode"]
|
|
297
|
+
caption = @chapter.on_CHAPS? ? "#{@headline_indexs.join('.')} #{caption}" : caption
|
|
298
|
+
warn "--hdnumberingmode is deprecated. use --level option."
|
|
299
|
+
end
|
|
300
|
+
@strategy.headline level, label, caption
|
|
301
|
+
end
|
|
302
|
+
end
|
|
303
|
+
|
|
304
|
+
def close_current_tagged_section(level)
|
|
305
|
+
while @tagged_section.last and @tagged_section.last[1] >= level
|
|
306
|
+
close_tagged_section(* @tagged_section.pop)
|
|
307
|
+
end
|
|
308
|
+
end
|
|
309
|
+
|
|
310
|
+
def headline(level, label, caption)
|
|
311
|
+
@strategy.headline level, label, caption
|
|
312
|
+
end
|
|
313
|
+
|
|
314
|
+
def tagged_section_init
|
|
315
|
+
@tagged_section = []
|
|
316
|
+
end
|
|
317
|
+
|
|
318
|
+
def open_tagged_section(tag, level, label, caption)
|
|
319
|
+
mid = "#{tag}_begin"
|
|
320
|
+
unless @strategy.respond_to?(mid)
|
|
321
|
+
error "strategy does not support tagged section: #{tag}"
|
|
322
|
+
headline level, label, caption
|
|
323
|
+
return
|
|
324
|
+
end
|
|
325
|
+
@tagged_section.push [tag, level]
|
|
326
|
+
@strategy.__send__ mid, level, label, caption
|
|
327
|
+
end
|
|
328
|
+
|
|
329
|
+
def close_tagged_section(tag, level)
|
|
330
|
+
mid = "#{tag}_end"
|
|
331
|
+
if @strategy.respond_to?(mid)
|
|
332
|
+
@strategy.__send__ mid, level
|
|
333
|
+
else
|
|
334
|
+
error "strategy does not support block op: #{mid}"
|
|
335
|
+
end
|
|
336
|
+
end
|
|
337
|
+
|
|
338
|
+
def close_all_tagged_section
|
|
339
|
+
until @tagged_section.empty?
|
|
340
|
+
close_tagged_section(* @tagged_section.pop)
|
|
341
|
+
end
|
|
342
|
+
end
|
|
343
|
+
|
|
344
|
+
def compile_ulist(f)
|
|
345
|
+
level = 0
|
|
346
|
+
f.while_match(/\A\s+\*|\A\#@/) do |line|
|
|
347
|
+
next if line =~ /\A\#@/
|
|
348
|
+
|
|
349
|
+
buf = [text(line.sub(/\*+/, '').strip)]
|
|
350
|
+
f.while_match(/\A\s+(?!\*)\S/) do |cont|
|
|
351
|
+
buf.push text(cont.strip)
|
|
352
|
+
end
|
|
353
|
+
|
|
354
|
+
line =~ /\A\s+(\*+)/
|
|
355
|
+
current_level = $1.size
|
|
356
|
+
if level == current_level
|
|
357
|
+
@strategy.ul_item_end
|
|
358
|
+
# body
|
|
359
|
+
@strategy.ul_item_begin buf
|
|
360
|
+
elsif level < current_level # down
|
|
361
|
+
level_diff = current_level - level
|
|
362
|
+
level = current_level
|
|
363
|
+
(1..(level_diff - 1)).to_a.reverse.each do |i|
|
|
364
|
+
@strategy.ul_begin {i}
|
|
365
|
+
@strategy.ul_item_begin []
|
|
366
|
+
end
|
|
367
|
+
@strategy.ul_begin {level}
|
|
368
|
+
@strategy.ul_item_begin buf
|
|
369
|
+
elsif level > current_level # up
|
|
370
|
+
level_diff = level - current_level
|
|
371
|
+
level = current_level
|
|
372
|
+
(1..level_diff).to_a.reverse.each do |i|
|
|
373
|
+
@strategy.ul_item_end
|
|
374
|
+
@strategy.ul_end {level + i}
|
|
375
|
+
@strategy.ul_item_end
|
|
376
|
+
end
|
|
377
|
+
# body
|
|
378
|
+
@strategy.ul_item_begin buf
|
|
379
|
+
end
|
|
380
|
+
end
|
|
381
|
+
|
|
382
|
+
(1..level).to_a.reverse.each do |i|
|
|
383
|
+
@strategy.ul_item_end
|
|
384
|
+
@strategy.ul_end {i}
|
|
385
|
+
end
|
|
386
|
+
end
|
|
387
|
+
|
|
388
|
+
def compile_olist(f)
|
|
389
|
+
@strategy.ol_begin
|
|
390
|
+
f.while_match(/\A\s+\d+\.|\A\#@/) do |line|
|
|
391
|
+
next if line =~ /\A\#@/
|
|
392
|
+
|
|
393
|
+
num = line.match(/(\d+)\./)[1]
|
|
394
|
+
buf = [text(line.sub(/\d+\./, '').strip)]
|
|
395
|
+
f.while_match(/\A\s+(?!\d+\.)\S/) do |cont|
|
|
396
|
+
buf.push text(cont.strip)
|
|
397
|
+
end
|
|
398
|
+
@strategy.ol_item buf, num
|
|
399
|
+
end
|
|
400
|
+
@strategy.ol_end
|
|
401
|
+
end
|
|
402
|
+
|
|
403
|
+
def compile_dlist(f)
|
|
404
|
+
@strategy.dl_begin
|
|
405
|
+
while /\A\s*:/ =~ f.peek
|
|
406
|
+
@strategy.dt text(f.gets.sub(/\A\s*:/, '').strip)
|
|
407
|
+
@strategy.dd f.break(/\A(\S|\s*:)/).map {|line| text(line.strip) }
|
|
408
|
+
f.skip_blank_lines
|
|
409
|
+
end
|
|
410
|
+
@strategy.dl_end
|
|
411
|
+
end
|
|
412
|
+
|
|
413
|
+
def compile_paragraph(f)
|
|
414
|
+
buf = []
|
|
415
|
+
f.until_match(%r<\A//|\A\#@>) do |line|
|
|
416
|
+
break if line.strip.empty?
|
|
417
|
+
buf.push text(line.sub(/^(\t+)\s*/) {|m| "<!ESCAPETAB!>" * m.size}.strip.gsub(/<!ESCAPETAB!>/, "\t"))
|
|
418
|
+
end
|
|
419
|
+
@strategy.paragraph buf
|
|
420
|
+
end
|
|
421
|
+
|
|
422
|
+
def read_command(f)
|
|
423
|
+
line = f.gets
|
|
424
|
+
name = line.slice(/[a-z]+/).intern
|
|
425
|
+
args = parse_args(line.sub(%r<\A//[a-z]+>, '').rstrip.chomp('{'), name)
|
|
426
|
+
lines = block_open?(line) ? read_block(f) : nil
|
|
427
|
+
return name, args, lines
|
|
428
|
+
end
|
|
429
|
+
|
|
430
|
+
def block_open?(line)
|
|
431
|
+
line.rstrip[-1,1] == '{'
|
|
432
|
+
end
|
|
433
|
+
|
|
434
|
+
def read_block(f)
|
|
435
|
+
head = f.lineno
|
|
436
|
+
buf = []
|
|
437
|
+
f.until_match(%r<\A//\}>) do |line|
|
|
438
|
+
unless line =~ /\A\#@/
|
|
439
|
+
buf.push text(line.rstrip)
|
|
440
|
+
end
|
|
441
|
+
end
|
|
442
|
+
unless %r<\A//\}> =~ f.peek
|
|
443
|
+
error "unexpected EOF (block begins at: #{head})"
|
|
444
|
+
return buf
|
|
445
|
+
end
|
|
446
|
+
f.gets # discard terminator
|
|
447
|
+
buf
|
|
448
|
+
end
|
|
449
|
+
|
|
450
|
+
def parse_args(str, name=nil)
|
|
451
|
+
return [] if str.empty?
|
|
452
|
+
scanner = StringScanner.new(str)
|
|
453
|
+
words = []
|
|
454
|
+
while word = scanner.scan(/(\[\]|\[.*?[^\\]\])/)
|
|
455
|
+
w2 = word[1..-2].gsub(/\\(.)/){
|
|
456
|
+
ch = $1
|
|
457
|
+
(ch == "]" or ch == "\\") ? ch : "\\" + ch
|
|
458
|
+
}
|
|
459
|
+
words << w2
|
|
460
|
+
end
|
|
461
|
+
if !scanner.eos?
|
|
462
|
+
error "argument syntax error: #{scanner.rest} in #{str.inspect}"
|
|
463
|
+
return []
|
|
464
|
+
end
|
|
465
|
+
return words
|
|
466
|
+
end
|
|
467
|
+
|
|
468
|
+
def compile_command(syntax, args, lines)
|
|
469
|
+
unless @strategy.respond_to?(syntax.name)
|
|
470
|
+
error "strategy does not support command: //#{syntax.name}"
|
|
471
|
+
compile_unknown_command args, lines
|
|
472
|
+
return
|
|
473
|
+
end
|
|
474
|
+
begin
|
|
475
|
+
syntax.check_args args
|
|
476
|
+
rescue CompileError => err
|
|
477
|
+
error err.message
|
|
478
|
+
args = ['(NoArgument)'] * syntax.min_argc
|
|
479
|
+
end
|
|
480
|
+
if syntax.block_allowed?
|
|
481
|
+
compile_block syntax, args, lines
|
|
482
|
+
else
|
|
483
|
+
if lines
|
|
484
|
+
error "block is not allowed for command //#{syntax.name}; ignore"
|
|
485
|
+
end
|
|
486
|
+
compile_single syntax, args
|
|
487
|
+
end
|
|
488
|
+
end
|
|
489
|
+
|
|
490
|
+
def compile_unknown_command(args, lines)
|
|
491
|
+
@strategy.unknown_command args, lines
|
|
492
|
+
end
|
|
493
|
+
|
|
494
|
+
def compile_block(syntax, args, lines)
|
|
495
|
+
@strategy.__send__(syntax.name, (lines || default_block(syntax)), *args)
|
|
496
|
+
end
|
|
497
|
+
|
|
498
|
+
def default_block(syntax)
|
|
499
|
+
if syntax.block_required?
|
|
500
|
+
error "block is required for //#{syntax.name}; use empty block"
|
|
501
|
+
end
|
|
502
|
+
[]
|
|
503
|
+
end
|
|
504
|
+
|
|
505
|
+
def compile_single(syntax, args)
|
|
506
|
+
@strategy.__send__(syntax.name, *args)
|
|
507
|
+
end
|
|
508
|
+
|
|
509
|
+
def text(str)
|
|
510
|
+
return '' if str.empty?
|
|
511
|
+
words = str.split(/(@<\w+>\{(?:[^\}\\]|\\.)*?\})/, -1)
|
|
512
|
+
words.each do |w|
|
|
513
|
+
error "`@<xxx>' seen but is not valid inline op: #{w}" if w.scan(/@<\w+>/).size > 1 && !/\A@<raw>/.match(w)
|
|
514
|
+
end
|
|
515
|
+
result = @strategy.nofunc_text(words.shift)
|
|
516
|
+
until words.empty?
|
|
517
|
+
result << compile_inline(words.shift.gsub(/\\\}/, '}'))
|
|
518
|
+
result << @strategy.nofunc_text(words.shift)
|
|
519
|
+
end
|
|
520
|
+
result
|
|
521
|
+
rescue => err
|
|
522
|
+
error err.message
|
|
523
|
+
end
|
|
524
|
+
public :text # called from strategy
|
|
525
|
+
|
|
526
|
+
def compile_inline(str)
|
|
527
|
+
op, arg = /\A@<(\w+)>\{(.*?)\}\z/.match(str).captures
|
|
528
|
+
unless inline_defined?(op)
|
|
529
|
+
raise CompileError, "no such inline op: #{op}"
|
|
530
|
+
end
|
|
531
|
+
unless @strategy.respond_to?("inline_#{op}")
|
|
532
|
+
raise "strategy does not support inline op: @<#{op}>"
|
|
533
|
+
end
|
|
534
|
+
@strategy.__send__("inline_#{op}", arg)
|
|
535
|
+
rescue => err
|
|
536
|
+
error err.message
|
|
537
|
+
@strategy.nofunc_text(str)
|
|
538
|
+
end
|
|
539
|
+
|
|
540
|
+
def warn(msg)
|
|
541
|
+
@strategy.warn msg
|
|
542
|
+
end
|
|
543
|
+
|
|
544
|
+
def error(msg)
|
|
545
|
+
@strategy.error msg
|
|
546
|
+
end
|
|
547
|
+
|
|
548
|
+
end
|
|
549
|
+
|
|
550
|
+
end # module ReVIEW
|