tmtms-review 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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