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