review-peg 0.1.0

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