review 0.6.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.
- data/COPYING +515 -0
- data/ChangeLog +1278 -0
- data/README.rdoc +21 -0
- data/Rakefile +42 -0
- data/VERSION +1 -0
- data/bin/review-check +190 -0
- data/bin/review-checkdep +63 -0
- data/bin/review-compile +165 -0
- data/bin/review-epubmaker +525 -0
- data/bin/review-index +108 -0
- data/bin/review-preproc +140 -0
- data/bin/review-validate +51 -0
- data/bin/review-vol +106 -0
- data/doc/format.re +486 -0
- data/doc/format.txt +434 -0
- data/doc/format_idg.txt +194 -0
- data/doc/format_sjis.txt +313 -0
- data/doc/sample.css +91 -0
- data/doc/sample.yaml +46 -0
- data/lib/lineinput.rb +155 -0
- data/lib/review/book.rb +580 -0
- data/lib/review/builder.rb +274 -0
- data/lib/review/compat.rb +22 -0
- data/lib/review/compiler.rb +483 -0
- data/lib/review/epubbuilder.rb +692 -0
- data/lib/review/ewbbuilder.rb +382 -0
- data/lib/review/exception.rb +21 -0
- data/lib/review/htmlbuilder.rb +370 -0
- data/lib/review/htmllayout.rb +19 -0
- data/lib/review/htmlutils.rb +27 -0
- data/lib/review/idgxmlbuilder.rb +1078 -0
- data/lib/review/index.rb +224 -0
- data/lib/review/latexbuilder.rb +420 -0
- data/lib/review/latexindex.rb +35 -0
- data/lib/review/latexutils.rb +52 -0
- data/lib/review/preprocessor.rb +520 -0
- data/lib/review/textutils.rb +19 -0
- data/lib/review/tocparser.rb +333 -0
- data/lib/review/tocprinter.rb +220 -0
- data/lib/review/topbuilder.rb +572 -0
- data/lib/review/unfold.rb +138 -0
- data/lib/review/volume.rb +66 -0
- data/lib/review.rb +4 -0
- data/review.gemspec +93 -0
- data/setup.rb +1587 -0
- data/test/test_epubbuilder.rb +73 -0
- data/test/test_helper.rb +2 -0
- data/test/test_htmlbuilder.rb +42 -0
- data/test/test_latexbuilder.rb +74 -0
- metadata +122 -0
@@ -0,0 +1,35 @@
|
|
1
|
+
#
|
2
|
+
# $Id: latexindex.rb 2204 2006-03-18 06:10:26Z aamine $
|
3
|
+
#
|
4
|
+
# Copyright (c) 2002-2006 Minero Aoki
|
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
|
+
# For details of the GNU LGPL, see the file "COPYING".
|
10
|
+
#
|
11
|
+
|
12
|
+
module ReVIEW
|
13
|
+
|
14
|
+
class LaTeXIndex
|
15
|
+
|
16
|
+
def load(path)
|
17
|
+
table = {}
|
18
|
+
File.foreach(path) do |line|
|
19
|
+
key, value = *line.strip.split(/\t+/, 2)
|
20
|
+
table[key.sub(/\A%/, '')] = value
|
21
|
+
end
|
22
|
+
new(table)
|
23
|
+
end
|
24
|
+
|
25
|
+
def initialize(table)
|
26
|
+
@table = table
|
27
|
+
end
|
28
|
+
|
29
|
+
def [](key)
|
30
|
+
@table.fetch(key)
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
#
|
2
|
+
# $Id: latexutils.rb 2204 2006-03-18 06:10:26Z aamine $
|
3
|
+
#
|
4
|
+
# Copyright (c) 2002-2006 Minero Aoki
|
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
|
+
# For details of the GNU LGPL, see the file "COPYING".
|
10
|
+
#
|
11
|
+
|
12
|
+
module ReVIEW
|
13
|
+
|
14
|
+
module LaTeXUtils
|
15
|
+
|
16
|
+
MATACHARS = {
|
17
|
+
'#' => '\symbol{"23}',
|
18
|
+
"$" => '\symbol{"24}',
|
19
|
+
'%' => '\%',
|
20
|
+
'&' => '\&',
|
21
|
+
'{' => '\{',
|
22
|
+
'}' => '\}',
|
23
|
+
'_' => '\symbol{"5F}',
|
24
|
+
'^' => '\textasciicircum{}',
|
25
|
+
'~' => '\textasciitilde{}',
|
26
|
+
'|' => '\textbar{}',
|
27
|
+
'<' => '\symbol{"3C}',
|
28
|
+
'>' => '\symbol{"3E}',
|
29
|
+
"\\" => '\symbol{"5C}'
|
30
|
+
}
|
31
|
+
|
32
|
+
METACHARS_RE = /[#{Regexp.escape(MATACHARS.keys.join(''))}]/
|
33
|
+
|
34
|
+
def escape_latex(str)
|
35
|
+
str.gsub(METACHARS_RE) {|s|
|
36
|
+
MATACHARS[s] or raise "unknown trans char: #{s}"
|
37
|
+
}
|
38
|
+
end
|
39
|
+
|
40
|
+
alias escape escape_latex
|
41
|
+
|
42
|
+
def escape_index(str)
|
43
|
+
str.gsub(/[@!|"]/) {|s| '"' + s }
|
44
|
+
end
|
45
|
+
|
46
|
+
def macro(name, *args)
|
47
|
+
"\\#{name}" + args.map {|a| "{#{a}}" }.join('')
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
@@ -0,0 +1,520 @@
|
|
1
|
+
#
|
2
|
+
# $Id: preprocessor.rb 4250 2009-05-24 14:03:01Z aamine $
|
3
|
+
#
|
4
|
+
# Copyright (c) 2002-2009 Minero Aoki
|
5
|
+
# Copyright (c) 2010 Minero Aoki, Kenshi Muto
|
6
|
+
#
|
7
|
+
# This program is free software.
|
8
|
+
# You can distribute or modify this program under the terms of
|
9
|
+
# the GNU LGPL, Lesser General Public License version 2.1.
|
10
|
+
# For details of the GNU LGPL, see the file "COPYING".
|
11
|
+
#
|
12
|
+
|
13
|
+
require 'review/textutils'
|
14
|
+
require 'review/exception'
|
15
|
+
|
16
|
+
module ReVIEW
|
17
|
+
|
18
|
+
module ErrorUtils
|
19
|
+
|
20
|
+
def init_ErrorUtils(f)
|
21
|
+
@errutils_file = f
|
22
|
+
@errutils_err = false
|
23
|
+
end
|
24
|
+
|
25
|
+
def warn(msg)
|
26
|
+
if @param["outencoding"] =~ /^EUC$/
|
27
|
+
msg = NKF.nkf("-W -e", msg)
|
28
|
+
elsif @param["outencoding"] =~ /^SJIS$/
|
29
|
+
msg = NKF.nkf("-W -s", msg)
|
30
|
+
elsif @param["outencoding"] =~ /^JIS$/
|
31
|
+
msg = NKF.nkf("-W -j", msg)
|
32
|
+
end
|
33
|
+
$stderr.puts "#{location()}: warning: #{msg}"
|
34
|
+
end
|
35
|
+
|
36
|
+
def error(msg)
|
37
|
+
if @param["outencoding"] =~ /^EUC$/
|
38
|
+
msg = NKF.nkf("-W -e", msg)
|
39
|
+
elsif @param["outencoding"] =~ /^SJIS$/
|
40
|
+
msg = NKF.nkf("-W -s", msg)
|
41
|
+
elsif @param["outencoding"] =~ /^JIS$/
|
42
|
+
msg = NKF.nkf("-W -j", msg)
|
43
|
+
end
|
44
|
+
@errutils_err = true
|
45
|
+
raise ApplicationError, "#{location()}: #{msg}"
|
46
|
+
end
|
47
|
+
|
48
|
+
def location
|
49
|
+
"#{filename()}:#{lineno()}"
|
50
|
+
end
|
51
|
+
|
52
|
+
def filename
|
53
|
+
@errutils_file.path
|
54
|
+
end
|
55
|
+
|
56
|
+
def lineno
|
57
|
+
@errutils_file.lineno
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
|
62
|
+
|
63
|
+
class Preprocessor
|
64
|
+
|
65
|
+
include ErrorUtils
|
66
|
+
|
67
|
+
def Preprocessor.strip(f)
|
68
|
+
buf = ''
|
69
|
+
Strip.new(f).each do |line|
|
70
|
+
buf << line.rstrip << "\n"
|
71
|
+
end
|
72
|
+
buf
|
73
|
+
end
|
74
|
+
|
75
|
+
class Strip
|
76
|
+
def initialize(f)
|
77
|
+
@f = f
|
78
|
+
end
|
79
|
+
|
80
|
+
def path
|
81
|
+
@f.path
|
82
|
+
end
|
83
|
+
|
84
|
+
def lineno
|
85
|
+
@f.lineno
|
86
|
+
end
|
87
|
+
|
88
|
+
def gets
|
89
|
+
while line = @f.gets
|
90
|
+
return line unless /\A\#@/ =~ line
|
91
|
+
end
|
92
|
+
nil
|
93
|
+
end
|
94
|
+
|
95
|
+
def each
|
96
|
+
@f.each do |line|
|
97
|
+
yield line unless /\A\#@/ =~ line
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def initialize(repo)
|
103
|
+
@repository = repo
|
104
|
+
end
|
105
|
+
|
106
|
+
def setParameter(param)
|
107
|
+
@param = param
|
108
|
+
end
|
109
|
+
|
110
|
+
def process(inf, outf)
|
111
|
+
init_ErrorUtils inf
|
112
|
+
@f = outf
|
113
|
+
begin
|
114
|
+
preproc inf
|
115
|
+
rescue Errno::ENOENT => err
|
116
|
+
error err.message
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
private
|
121
|
+
|
122
|
+
TYPES = %w( file range )
|
123
|
+
|
124
|
+
def preproc(f)
|
125
|
+
init_vars
|
126
|
+
while line = f.gets
|
127
|
+
case line
|
128
|
+
when /\A\#@\#/, /\A\#\#\#\#/
|
129
|
+
@f.print line
|
130
|
+
|
131
|
+
when /\A\#@defvar/
|
132
|
+
@f.print line
|
133
|
+
direc = parse_directive(line, 2)
|
134
|
+
defvar(*direc.args)
|
135
|
+
|
136
|
+
when /\A\#@mapoutput/
|
137
|
+
direc = parse_directive(line, 1, 'stderr')
|
138
|
+
@f.print line
|
139
|
+
get_output(expand(direc.arg), direc['stderr']).each do |out|
|
140
|
+
@f.print out.string
|
141
|
+
end
|
142
|
+
skip_list f
|
143
|
+
|
144
|
+
when /\A\#@mapfile/
|
145
|
+
direc = parse_directive(line, 1, 'eval')
|
146
|
+
path = expand(direc.arg)
|
147
|
+
ent = @repository.fetch_file(path)
|
148
|
+
ent = evaluate(path, ent) if direc['eval']
|
149
|
+
replace_block f, line, ent, false # FIXME: turn off lineno: tmp
|
150
|
+
|
151
|
+
when /\A\#@map(?:range)?/
|
152
|
+
direc = parse_directive(line, 2, 'unindent')
|
153
|
+
path = expand(direc.args[0])
|
154
|
+
ent = @repository.fetch_range(path, direc.args[1]) or
|
155
|
+
error "unknown range: #{path}: #{direc.args[1]}"
|
156
|
+
ent = (direc['unindent'] ? unindent(ent, direc['unindent']) : ent)
|
157
|
+
replace_block f, line, ent, false # FIXME: turn off lineno: tmp
|
158
|
+
|
159
|
+
when /\A\#@end/
|
160
|
+
error 'unbaranced #@end'
|
161
|
+
|
162
|
+
when /\A\#@/
|
163
|
+
op = line.slice(/@(\w+)/, 1)
|
164
|
+
#error "unkown directive: #{line.strip}" unless known_directive?(op)
|
165
|
+
warn "unkown directive: #{line.strip}" unless known_directive?(op)
|
166
|
+
@f.print line
|
167
|
+
|
168
|
+
when /\A\s*\z/ # empty line
|
169
|
+
@f.puts
|
170
|
+
else
|
171
|
+
@f.print line
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
private
|
177
|
+
|
178
|
+
KNOWN_DIRECTIVES = %w( require provide warn ok )
|
179
|
+
|
180
|
+
def known_directive?(op)
|
181
|
+
KNOWN_DIRECTIVES.index(op)
|
182
|
+
end
|
183
|
+
|
184
|
+
def replace_block(f, directive_line, newlines, with_lineno)
|
185
|
+
@f.print directive_line
|
186
|
+
newlines.each do |line|
|
187
|
+
print_number line.number if with_lineno
|
188
|
+
@f.print line.string
|
189
|
+
end
|
190
|
+
skip_list f
|
191
|
+
end
|
192
|
+
|
193
|
+
def print_number(num)
|
194
|
+
@f.printf '%4s ', (num ? num.to_s : '')
|
195
|
+
end
|
196
|
+
|
197
|
+
def skip_list(f)
|
198
|
+
begline = f.lineno
|
199
|
+
while line = f.gets
|
200
|
+
case line
|
201
|
+
when %r[\A\#@end]
|
202
|
+
@f.print line
|
203
|
+
return
|
204
|
+
when %r[\A//\}]
|
205
|
+
warn '//} seen in list'
|
206
|
+
@f.print line
|
207
|
+
return
|
208
|
+
when %r[\A\#@\w]
|
209
|
+
warn "#{line.slice(/\A\#@\w+/)} seen in list"
|
210
|
+
@f.print line
|
211
|
+
when %r[\A\#@]
|
212
|
+
@f.print line
|
213
|
+
end
|
214
|
+
end
|
215
|
+
error "list reached end of file (beginning line = #{begline})"
|
216
|
+
end
|
217
|
+
|
218
|
+
class Directive
|
219
|
+
def initialize(op, args, opts)
|
220
|
+
@op = op
|
221
|
+
@args = args
|
222
|
+
@opts = opts
|
223
|
+
end
|
224
|
+
|
225
|
+
attr_reader :op
|
226
|
+
attr_reader :args
|
227
|
+
attr_reader :opts
|
228
|
+
|
229
|
+
def arg
|
230
|
+
@args.first
|
231
|
+
end
|
232
|
+
|
233
|
+
def opt
|
234
|
+
@opts.first
|
235
|
+
end
|
236
|
+
|
237
|
+
def [](key)
|
238
|
+
@opts[key]
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
def parse_directive(line, argc, *optdecl)
|
243
|
+
m = /\A\#@(\w+)\((.*?)\)(?:\[(.*?)\])?\z/.match(line.strip) or
|
244
|
+
error "wrong directive: #{line.strip}"
|
245
|
+
op = m[1]
|
246
|
+
args = m[2].split(/,\s*/)
|
247
|
+
opts = parse_optargs(m[3])
|
248
|
+
return if argc == 0 and args.empty?
|
249
|
+
if argc == -1
|
250
|
+
# Any number of arguments are allowed.
|
251
|
+
elsif args.size != argc
|
252
|
+
error "wrong arg size"
|
253
|
+
end
|
254
|
+
if opts
|
255
|
+
wrong_opts = opts.keys - optdecl
|
256
|
+
unless wrong_opts.empty?
|
257
|
+
error "wrong option: #{wrong_opts.keys.join(' ')}"
|
258
|
+
end
|
259
|
+
end
|
260
|
+
Directive.new(op, args, opts || {})
|
261
|
+
end
|
262
|
+
|
263
|
+
def parse_optargs(str)
|
264
|
+
return nil unless str
|
265
|
+
table = {}
|
266
|
+
str.split(/,\s*/).each do |a|
|
267
|
+
name, spec = a.split(/=/, 2)
|
268
|
+
table[name] = optarg_value(spec)
|
269
|
+
end
|
270
|
+
table
|
271
|
+
end
|
272
|
+
|
273
|
+
def optarg_value(spec)
|
274
|
+
case spec
|
275
|
+
when 'true' then true # [name=true]
|
276
|
+
when 'false' then false # [name=false]
|
277
|
+
when 'nil' then nil # [name=nil]
|
278
|
+
when nil then true # [name]
|
279
|
+
when /^\d+$/ then $&.to_i # [name=8]
|
280
|
+
else # [name=val]
|
281
|
+
spec
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
285
|
+
def init_vars
|
286
|
+
@vartable = {}
|
287
|
+
end
|
288
|
+
|
289
|
+
def defvar(name, value)
|
290
|
+
@vartable[name] = value
|
291
|
+
end
|
292
|
+
|
293
|
+
def expand(str)
|
294
|
+
str.gsub(/\$\w+/) {|name|
|
295
|
+
s = @vartable[name.sub('$', '')]
|
296
|
+
s ? expand(s) : name
|
297
|
+
}
|
298
|
+
end
|
299
|
+
|
300
|
+
def unindent(chunk, n)
|
301
|
+
n = minimum_indent(chunk) unless n.kind_of?(Integer)
|
302
|
+
re = /\A#{' ' * n}/
|
303
|
+
chunk.map {|line| line.edit {|s| s.sub(re,'') } }
|
304
|
+
end
|
305
|
+
|
306
|
+
INF_INDENT = 9999
|
307
|
+
|
308
|
+
def minimum_indent(chunk)
|
309
|
+
n = chunk.map {|line| line.empty? ? INF_INDENT : line.num_indent }.min
|
310
|
+
n == INF_INDENT ? 0 : n
|
311
|
+
end
|
312
|
+
|
313
|
+
def check_ruby_syntax(rbfile)
|
314
|
+
status = spawn {
|
315
|
+
exec("ruby -c #{rbfile} 2>&1 > /dev/null")
|
316
|
+
}
|
317
|
+
error "syntax check failed: #{rbfile}" unless status.exitstatus == 0
|
318
|
+
end
|
319
|
+
|
320
|
+
def spawn
|
321
|
+
pid, status = *Process.waitpid2(fork { yield })
|
322
|
+
status
|
323
|
+
end
|
324
|
+
|
325
|
+
def evaluate(path, chunk)
|
326
|
+
outputs = get_output("ruby #{path}", false).split(/\n/).map {|s| s.strip }
|
327
|
+
chunk.map {|line|
|
328
|
+
if /\# \$\d+/ =~ line.string
|
329
|
+
# map result into source.
|
330
|
+
line.edit {|s|
|
331
|
+
s.sub(/\$(\d+)/) { outputs[$1.to_i - 1] }
|
332
|
+
}
|
333
|
+
else
|
334
|
+
line
|
335
|
+
end
|
336
|
+
}
|
337
|
+
end
|
338
|
+
|
339
|
+
require 'open3'
|
340
|
+
|
341
|
+
def get_output(cmd, use_stderr)
|
342
|
+
out = err = nil
|
343
|
+
Open3.popen3(cmd) {|stdin, stdout, stderr|
|
344
|
+
out = stdout.readlines
|
345
|
+
if use_stderr
|
346
|
+
out.concat stderr.readlines
|
347
|
+
else
|
348
|
+
err = stderr.readlines
|
349
|
+
end
|
350
|
+
}
|
351
|
+
if err and not err.empty?
|
352
|
+
$stderr.puts "[unexpected stderr message]"
|
353
|
+
err.each do |line|
|
354
|
+
$stderr.print line
|
355
|
+
end
|
356
|
+
error "get_output: got unexpected output"
|
357
|
+
end
|
358
|
+
num = 0
|
359
|
+
out.map {|line| Line.new(num += 1, line) }
|
360
|
+
end
|
361
|
+
|
362
|
+
end
|
363
|
+
|
364
|
+
|
365
|
+
class Line
|
366
|
+
def initialize(number, string)
|
367
|
+
@number = number
|
368
|
+
@string = string
|
369
|
+
end
|
370
|
+
|
371
|
+
attr_reader :number
|
372
|
+
attr_reader :string
|
373
|
+
alias to_s string
|
374
|
+
|
375
|
+
def edit
|
376
|
+
self.class.new(@number, yield(@string))
|
377
|
+
end
|
378
|
+
|
379
|
+
def empty?
|
380
|
+
@string.strip.empty?
|
381
|
+
end
|
382
|
+
|
383
|
+
def num_indent
|
384
|
+
@string.slice(/\A\s*/).size
|
385
|
+
end
|
386
|
+
end
|
387
|
+
|
388
|
+
|
389
|
+
class Repository
|
390
|
+
|
391
|
+
include TextUtils
|
392
|
+
include ErrorUtils
|
393
|
+
|
394
|
+
def initialize
|
395
|
+
@repository = {}
|
396
|
+
end
|
397
|
+
|
398
|
+
def fetch_file(file)
|
399
|
+
file_descripter(file)['file']
|
400
|
+
end
|
401
|
+
|
402
|
+
def fetch_range(file, name)
|
403
|
+
fetch(file, 'range', name)
|
404
|
+
end
|
405
|
+
|
406
|
+
def fetch(file, type, name)
|
407
|
+
table = file_descripter(file)[type] or return nil
|
408
|
+
table[name]
|
409
|
+
end
|
410
|
+
|
411
|
+
private
|
412
|
+
|
413
|
+
def file_descripter(fname)
|
414
|
+
@repository[fname] ||= parse_file(fname)
|
415
|
+
end
|
416
|
+
|
417
|
+
def parse_file(fname)
|
418
|
+
File.open(fname) {|f|
|
419
|
+
init_ErrorUtils f
|
420
|
+
return _parse_file(f)
|
421
|
+
}
|
422
|
+
end
|
423
|
+
|
424
|
+
def _parse_file(f)
|
425
|
+
whole = []
|
426
|
+
repo = {'file' => whole}
|
427
|
+
curr = {'WHOLE' => whole}
|
428
|
+
lineno = 1
|
429
|
+
yacchack = false # remove ';'-only lines.
|
430
|
+
opened = [['(not opened)', '(not opened)']] * 3
|
431
|
+
|
432
|
+
f.each do |line|
|
433
|
+
case line
|
434
|
+
when /(?:\A\#@|\#@@)([a-z]+)_(begin|end)\((.*)\)/
|
435
|
+
type = check_type($1)
|
436
|
+
direction = $2
|
437
|
+
spec = check_spec($3)
|
438
|
+
case direction
|
439
|
+
when 'begin'
|
440
|
+
key = "#{type}/#{spec}"
|
441
|
+
error "begin x2: #{key}" if curr[key]
|
442
|
+
(repo[type] ||= {})[spec] = curr[key] = []
|
443
|
+
when 'end'
|
444
|
+
curr.delete("#{type}/#{spec}") or
|
445
|
+
error "end before begin: #{type}/#{spec}"
|
446
|
+
else
|
447
|
+
raise 'must not happen'
|
448
|
+
end
|
449
|
+
|
450
|
+
when %r<(?:\A\#@|\#@@)([a-z]+)/(\w+)\{>
|
451
|
+
type = check_type($1)
|
452
|
+
spec = check_spec($2)
|
453
|
+
key = "#{type}/#{spec}"
|
454
|
+
error "begin x2: #{key}" if curr[key]
|
455
|
+
(repo[type] ||= {})[spec] = curr[key] = []
|
456
|
+
opened.push [type, spec]
|
457
|
+
|
458
|
+
when %r<(?:\A\#@|\#@@)([a-z]+)/(\w+)\}>
|
459
|
+
type = check_type($1)
|
460
|
+
spec = check_spec($2)
|
461
|
+
curr.delete("#{type}/#{spec}") or
|
462
|
+
error "end before begin: #{type}/#{spec}"
|
463
|
+
opened.delete "#{type}/#{spec}"
|
464
|
+
|
465
|
+
when %r<(?:\A\#@|\#@@)\}>
|
466
|
+
type, spec = opened.last
|
467
|
+
curr.delete("#{type}/#{spec}") or
|
468
|
+
error "closed before open: #{type}/#{spec}"
|
469
|
+
opened.pop
|
470
|
+
|
471
|
+
when /(?:\A\#@|\#@@)yacchack/
|
472
|
+
yacchack = true
|
473
|
+
|
474
|
+
when /\A\#@-/ # does not increment line number.
|
475
|
+
line = canonical($')
|
476
|
+
curr.each_value do |list|
|
477
|
+
list.push Line.new(nil, line)
|
478
|
+
end
|
479
|
+
|
480
|
+
else
|
481
|
+
next if yacchack and line.strip == ';'
|
482
|
+
line = canonical(line)
|
483
|
+
curr.each_value do |list|
|
484
|
+
list.push Line.new(lineno, line)
|
485
|
+
end
|
486
|
+
lineno += 1
|
487
|
+
end
|
488
|
+
end
|
489
|
+
if curr.size > 1
|
490
|
+
curr.delete 'WHOLE'
|
491
|
+
curr.each do |range, lines|
|
492
|
+
$stderr.puts "#{filename()}: unclosed range: #{range} (begin @#{lines.first.number})"
|
493
|
+
end
|
494
|
+
raise ApplicationError, "ERROR"
|
495
|
+
end
|
496
|
+
|
497
|
+
repo
|
498
|
+
end
|
499
|
+
|
500
|
+
def canonical(line)
|
501
|
+
detab(line).rstrip + "\n"
|
502
|
+
end
|
503
|
+
|
504
|
+
def check_type(type)
|
505
|
+
unless Preprocessor::TYPES.index(type)
|
506
|
+
error "wrong type: #{type.inspect}"
|
507
|
+
end
|
508
|
+
type
|
509
|
+
end
|
510
|
+
|
511
|
+
def check_spec(spec)
|
512
|
+
unless /\A\w+\z/ =~ spec
|
513
|
+
error "wrong spec: #{spec.inspect}"
|
514
|
+
end
|
515
|
+
spec
|
516
|
+
end
|
517
|
+
|
518
|
+
end
|
519
|
+
|
520
|
+
end
|