review 1.7.2 → 2.0.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (101) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +1 -43
  3. data/.travis.yml +0 -9
  4. data/ChangeLog +0 -16
  5. data/Gemfile +1 -0
  6. data/README.rdoc +1 -1
  7. data/Rakefile +12 -1
  8. data/bin/review-check +21 -8
  9. data/bin/review-compile +15 -9
  10. data/bin/review-epubmaker-legacy +6 -6
  11. data/bin/review-index +13 -2
  12. data/bin/review-init +18 -12
  13. data/bin/review-preproc +14 -1
  14. data/bin/review-validate +1 -1
  15. data/bin/review-vol +13 -1
  16. data/doc/quickstart.ja.md +1 -1
  17. data/doc/quickstart.md +1 -1
  18. data/lib/epubmaker/content.rb +3 -3
  19. data/lib/epubmaker/epubcommon.rb +108 -91
  20. data/lib/epubmaker/epubv2.rb +67 -14
  21. data/lib/epubmaker/epubv3.rb +59 -25
  22. data/lib/epubmaker/producer.rb +1 -13
  23. data/lib/lineinput.rb +0 -48
  24. data/lib/review/book/base.rb +4 -12
  25. data/lib/review/book/compilable.rb +3 -1
  26. data/lib/review/book/index.rb +4 -4
  27. data/lib/review/builder.rb +54 -47
  28. data/lib/review/compiler.rb +4662 -326
  29. data/lib/review/compiler/literals_1_8.kpeg +19 -0
  30. data/lib/review/compiler/literals_1_8.rb +432 -0
  31. data/lib/review/compiler/literals_1_9.kpeg +22 -0
  32. data/lib/review/compiler/literals_1_9.rb +435 -0
  33. data/lib/review/configure.rb +8 -20
  34. data/lib/review/epubbuilder.rb +1 -1
  35. data/lib/review/epubmaker.rb +122 -52
  36. data/lib/review/ewbbuilder.rb +4 -4
  37. data/lib/review/exception.rb +1 -1
  38. data/lib/review/extentions.rb +1 -0
  39. data/lib/review/extentions/array.rb +25 -0
  40. data/lib/review/htmlbuilder.rb +286 -275
  41. data/lib/review/htmllayout.rb +0 -2
  42. data/lib/review/htmlutils.rb +4 -4
  43. data/lib/review/i18n.rb +2 -6
  44. data/lib/review/i18n.yml +1 -1
  45. data/lib/review/idgxmlbuilder.rb +239 -204
  46. data/lib/review/inaobuilder.rb +75 -73
  47. data/lib/review/latexbuilder.rb +265 -219
  48. data/lib/review/latexutils.rb +6 -6
  49. data/lib/review/layout.tex.erb +1 -1
  50. data/lib/review/location.rb +24 -0
  51. data/lib/review/markdownbuilder.rb +124 -79
  52. data/lib/review/node.rb +267 -0
  53. data/lib/review/pdfmaker.rb +92 -92
  54. data/lib/review/preprocessor.rb +51 -14
  55. data/lib/review/review.kpeg +724 -0
  56. data/lib/review/textbuilder.rb +1 -1
  57. data/lib/review/textutils.rb +24 -18
  58. data/lib/review/tocparser.rb +3 -3
  59. data/lib/review/tocprinter.rb +31 -8
  60. data/lib/review/topbuilder.rb +119 -111
  61. data/lib/review/unfold.rb +2 -2
  62. data/lib/review/version.rb +1 -1
  63. data/review.gemspec +2 -2
  64. data/rubocop-todo.yml +456 -0
  65. data/test/assets/test_template.tex +1 -1
  66. data/test/sample-book/src/config.yml +0 -1
  67. data/test/test.re +1 -1
  68. data/test/test_book.rb +4 -4
  69. data/test/test_book_chapter.rb +70 -0
  70. data/test/test_book_part.rb +1 -1
  71. data/test/test_builder.rb +6 -28
  72. data/test/test_compiler.rb +59 -14
  73. data/test/test_helper.rb +47 -4
  74. data/test/test_htmlbuilder.rb +104 -73
  75. data/test/test_i18n.rb +5 -3
  76. data/test/test_idgxmlbuilder.rb +5 -2
  77. data/test/test_inaobuilder.rb +4 -2
  78. data/test/test_latexbuilder.rb +18 -37
  79. data/test/test_lineinput.rb +25 -4
  80. data/test/test_markdownbuilder.rb +4 -22
  81. data/test/test_pdfmaker.rb +12 -11
  82. data/test/test_textutils.rb +0 -36
  83. data/test/test_topbuilder.rb +2 -0
  84. metadata +14 -28
  85. data/.rubocop_todo.yml +0 -605
  86. data/Dockerfile +0 -22
  87. data/doc/NEWS.ja.md +0 -362
  88. data/doc/NEWS.md +0 -366
  89. data/lib/review/htmltoc.rb +0 -45
  90. data/lib/review/template.rb +0 -21
  91. data/templates/html/layout-html5.html.erb +0 -17
  92. data/templates/html/layout-xhtml1.html.erb +0 -20
  93. data/templates/ncx/epubv2.ncx.erb +0 -11
  94. data/templates/opf/epubv2.opf.erb +0 -21
  95. data/templates/opf/epubv3.opf.erb +0 -18
  96. data/templates/xml/container.xml.erb +0 -6
  97. data/test/assets/test.xml.erb +0 -3
  98. data/test/sample-book/src/config-epub2.yml +0 -186
  99. data/test/test_configure.rb +0 -50
  100. data/test/test_htmltoc.rb +0 -32
  101. data/test/test_template.rb +0 -26
@@ -22,8 +22,6 @@ module ReVIEW
22
22
  include FileUtils
23
23
  include ReVIEW::LaTeXUtils
24
24
 
25
- attr_accessor :config, :basedir
26
-
27
25
  def initialize
28
26
  @basedir = Dir.pwd
29
27
  end
@@ -41,16 +39,13 @@ module ReVIEW
41
39
  $stderr.puts "#{File.basename($0, '.*')}: warning: #{msg}"
42
40
  end
43
41
 
44
- def pdf_filepath
45
- File.join(@basedir, @config["bookname"]+".pdf")
46
- end
47
-
48
- def remove_old_file
49
- FileUtils.rm_f(pdf_filepath)
42
+ def check_book(config)
43
+ pdf_file = config["bookname"]+".pdf"
44
+ File.unlink(pdf_file) if File.exist?(pdf_file)
50
45
  end
51
46
 
52
- def build_path
53
- "./#{@config["bookname"]}-pdf"
47
+ def build_path(config)
48
+ "./#{config["bookname"]}-pdf"
54
49
  end
55
50
 
56
51
  def check_compile_status(ignore_errors)
@@ -94,30 +89,31 @@ module ReVIEW
94
89
  end
95
90
 
96
91
  def execute(*args)
97
- @config = ReVIEW::Configure.values
92
+ config = ReVIEW::Configure.values
98
93
  cmd_config, yamlfile = parse_opts(args)
99
94
 
100
- @config.merge!(YAML.load_file(yamlfile))
95
+ config.merge!(YAML.load_file(yamlfile))
101
96
  # YAML configs will be overridden by command line options.
102
- @config.merge!(cmd_config)
103
- I18n.setup(@config["language"])
104
- generate_pdf(yamlfile)
97
+ config.merge!(cmd_config)
98
+ I18n.setup(config["language"])
99
+ generate_pdf(config, yamlfile)
105
100
  end
106
101
 
107
- def generate_pdf(yamlfile)
108
- remove_old_file
109
- @path = build_path()
102
+ def generate_pdf(config, yamlfile)
103
+ check_book(config)
104
+ @path = build_path(config)
105
+ bookname = config["bookname"]
110
106
  Dir.mkdir(@path)
111
107
 
112
108
  @chaps_fnames = Hash.new{|h, key| h[key] = ""}
113
109
  @compile_errors = nil
114
110
 
115
111
  book = ReVIEW::Book.load(@basedir)
116
- book.config = @config
112
+ book.config = config
117
113
  book.parts.each do |part|
118
114
  if part.name.present?
119
115
  if part.file?
120
- output_chaps(part.name, yamlfile)
116
+ output_chaps(part.name, config, yamlfile)
121
117
  @chaps_fnames["CHAPS"] << %Q|\\input{#{part.name}.tex}\n|
122
118
  else
123
119
  @chaps_fnames["CHAPS"] << %Q|\\part{#{part.name}}\n|
@@ -126,39 +122,40 @@ module ReVIEW
126
122
 
127
123
  part.chapters.each do |chap|
128
124
  filename = File.basename(chap.path, ".*")
129
- output_chaps(filename, yamlfile)
130
- @chaps_fnames["PREDEF"] << "\\input{#{filename}.tex}\n" if chap.on_PREDEF?
131
- @chaps_fnames["CHAPS"] << "\\input{#{filename}.tex}\n" if chap.on_CHAPS?
125
+ output_chaps(filename, config, yamlfile)
126
+ @chaps_fnames["PREDEF"] << "\\input{#{filename}.tex}\n" if chap.on_PREDEF?
127
+ @chaps_fnames["CHAPS"] << "\\input{#{filename}.tex}\n" if chap.on_CHAPS?
132
128
  @chaps_fnames["APPENDIX"] << "\\input{#{filename}.tex}\n" if chap.on_APPENDIX?
133
129
  @chaps_fnames["POSTDEF"] << "\\input{#{filename}.tex}\n" if chap.on_POSTDEF?
134
130
  end
135
131
  end
136
132
 
137
- check_compile_status(@config["ignore-errors"])
133
+ check_compile_status(config["ignore-errors"])
138
134
 
139
- @config["pre_str"] = @chaps_fnames["PREDEF"]
140
- @config["chap_str"] = @chaps_fnames["CHAPS"]
141
- @config["appendix_str"] = @chaps_fnames["APPENDIX"]
142
- @config["post_str"] = @chaps_fnames["POSTDEF"]
135
+ config["pre_str"] = @chaps_fnames["PREDEF"]
136
+ config["chap_str"] = @chaps_fnames["CHAPS"]
137
+ config["appendix_str"] = @chaps_fnames["APPENDIX"]
138
+ config["post_str"] = @chaps_fnames["POSTDEF"]
143
139
 
144
- @config["usepackage"] = ""
145
- if @config["texstyle"]
146
- @config["usepackage"] = "\\usepackage{#{@config['texstyle']}}"
140
+ config["usepackage"] = ""
141
+ if config["texstyle"]
142
+ config["usepackage"] = "\\usepackage{#{config['texstyle']}}"
147
143
  end
148
144
 
149
- copy_images("./images", File.join(@path, "images"))
150
- copyStyToDir(File.join(Dir.pwd, "sty"), @path)
151
- copyStyToDir(File.join(Dir.pwd, "sty"), @path, "fd")
152
- copyStyToDir(File.join(Dir.pwd, "sty"), @path, "cls")
145
+ copy_images("./images", "#{@path}/images")
146
+ copyStyToDir(Dir.pwd + "/sty", @path)
147
+ copyStyToDir(Dir.pwd + "/sty", @path, "fd")
148
+ copyStyToDir(Dir.pwd + "/sty", @path, "cls")
153
149
  copyStyToDir(Dir.pwd, @path, "tex")
154
150
 
155
- Dir.chdir(@path) do
156
- template = get_template
151
+ Dir.chdir(@path) {
152
+ template = get_template(config)
157
153
  File.open("./book.tex", "wb"){|f| f.write(template)}
158
154
 
159
- call_hook("hook_beforetexcompile")
155
+ call_hook("hook_beforetexcompile", config)
160
156
 
161
157
  ## do compile
158
+ enc = config["params"].to_s.split(/\s+/).find{|i| i =~ /\A--outencoding=/ }
162
159
  kanji = 'utf8'
163
160
  texcommand = "platex"
164
161
  texoptions = "-kanji=#{kanji}"
@@ -168,32 +165,36 @@ module ReVIEW
168
165
  if ENV["REVIEW_SAFE_MODE"].to_i & 4 > 0
169
166
  warn "command configuration is prohibited in safe mode. ignored."
170
167
  else
171
- texcommand = @config["texcommand"] if @config["texcommand"]
172
- dvicommand = @config["dvicommand"] if @config["dvicommand"]
173
- dvioptions = @config["dvioptions"] if @config["dvioptions"]
174
- texoptions = @config["texoptions"] if @config["texoptions"]
168
+ texcommand = config["texcommand"] if config["texcommand"]
169
+ dvicommand = config["dvicommand"] if config["dvicommand"]
170
+ dvioptions = config["dvioptions"] if config["dvioptions"]
171
+ if enc
172
+ kanji = enc.split(/\=/).last.gsub(/-/, '').downcase
173
+ texoptions = "-kanji=#{kanji}"
174
+ end
175
+ texoptions = config["texoptions"] if config["texoptions"]
175
176
  end
176
177
  3.times do
177
178
  system_or_raise("#{texcommand} #{texoptions} book.tex")
178
179
  end
179
- call_hook("hook_aftertexcompile")
180
+ call_hook("hook_aftertexcompile", config)
180
181
 
181
- if File.exist?("book.dvi")
182
+ if File.exist?("book.dvi")
182
183
  system_or_raise("#{dvicommand} #{dvioptions} book.dvi")
183
184
  end
184
- end
185
- call_hook("hook_afterdvipdf")
186
-
187
- FileUtils.cp(File.join(@path, "book.pdf"), pdf_filepath)
185
+ }
186
+ call_hook("hook_afterdvipdf", config)
187
+
188
+ FileUtils.cp("#{@path}/book.pdf", "#{@basedir}/#{bookname}.pdf")
188
189
 
189
- unless @config["debug"]
190
+ unless config["debug"]
190
191
  remove_entry_secure @path
191
192
  end
192
193
  end
193
194
 
194
- def output_chaps(filename, yamlfile)
195
+ def output_chaps(filename, config, yamlfile)
195
196
  $stderr.puts "compiling #{filename}.tex"
196
- cmd = "#{ReVIEW::MakerHelper.bindir}/review-compile --yaml=#{yamlfile} --target=latex --level=#{@config["secnolevel"]} --toclevel=#{@config["toclevel"]} #{@config["params"]} #{filename}.re > #{@path}/#{filename}.tex"
197
+ cmd = "#{ReVIEW::MakerHelper.bindir}/review-compile --yaml=#{yamlfile} --target=latex --level=#{config["secnolevel"]} --toclevel=#{config["toclevel"]} #{config["params"]} #{filename}.re > #{@path}/#{filename}.tex"
197
198
  if system cmd
198
199
  # OK
199
200
  else
@@ -202,8 +203,6 @@ module ReVIEW
202
203
  end
203
204
  end
204
205
 
205
- # PDFMaker#copy_images should copy image files _AND_ execute extractbb (or ebb).
206
- #
207
206
  def copy_images(from, to)
208
207
  if File.exist?(from)
209
208
  Dir.mkdir(to)
@@ -238,57 +237,57 @@ module ReVIEW
238
237
  end
239
238
  end
240
239
 
241
- def make_colophon_role(role)
242
- if @config[role].present?
243
- return "#{ReVIEW::I18n.t(role)} & #{escape_latex(join_with_separator(@config[role], ReVIEW::I18n.t("names_splitter")))} \\\\\n"
240
+ def make_colophon_role(role, config)
241
+ if config[role].present?
242
+ return "#{ReVIEW::I18n.t(role)} & #{escape_latex(join_with_separator(config[role], ReVIEW::I18n.t("names_splitter")))} \\\\\n"
244
243
  else
245
244
  ""
246
245
  end
247
246
  end
248
247
 
249
- def make_colophon
248
+ def make_colophon(config)
250
249
  colophon = ""
251
- @config["colophon_order"].each do |role|
252
- colophon += make_colophon_role(role)
250
+ config["colophon_order"].each do |role|
251
+ colophon += make_colophon_role(role, config)
253
252
  end
254
253
  colophon
255
254
  end
256
255
 
257
- def make_authors
256
+ def make_authors(config)
258
257
  authors = ""
259
- if @config["aut"].present?
260
- author_names = join_with_separator(@config["aut"], ReVIEW::I18n.t("names_splitter"))
258
+ if config["aut"].present?
259
+ author_names = join_with_separator(config["aut"], ReVIEW::I18n.t("names_splitter"))
261
260
  authors = ReVIEW::I18n.t("author_with_label", author_names)
262
261
  end
263
- if @config["csl"].present?
264
- csl_names = join_with_separator(@config["csl"], ReVIEW::I18n.t("names_splitter"))
262
+ if config["csl"].present?
263
+ csl_names = join_with_separator(config["csl"], ReVIEW::I18n.t("names_splitter"))
265
264
  authors += " \\\\\n"+ ReVIEW::I18n.t("supervisor_with_label", csl_names)
266
265
  end
267
- if @config["trl"].present?
268
- trl_names = join_with_separator(@config["trl"], ReVIEW::I18n.t("names_splitter"))
266
+ if config["trl"].present?
267
+ trl_names = join_with_separator(config["trl"], ReVIEW::I18n.t("names_splitter"))
269
268
  authors += " \\\\\n"+ ReVIEW::I18n.t("translator_with_label", trl_names)
270
269
  end
271
270
  authors
272
271
  end
273
272
 
274
- def get_template
275
- dclass = @config["texdocumentclass"] || []
276
- documentclass = dclass[0] || "jsbook"
277
- documentclassoption = dclass[1] || "oneside"
273
+ def get_template(config)
274
+ dclass = config["texdocumentclass"] || []
275
+ documentclass = dclass[0] || "jsbook"
276
+ documentclassoption = dclass[1] || "oneside"
278
277
 
279
- okuduke = make_colophon
280
- authors = make_authors
278
+ okuduke = make_colophon(config)
279
+ authors = make_authors(config)
281
280
 
282
- custom_titlepage = make_custom_page(@config["cover"]) || make_custom_page(@config["coverfile"])
283
- custom_originaltitlepage = make_custom_page(@config["originaltitlefile"])
284
- custom_creditpage = make_custom_page(@config["creditfile"])
281
+ custom_titlepage = make_custom_page(config["coverfile"])
282
+ custom_originaltitlepage = make_custom_page(config["originaltitlefile"])
283
+ custom_creditpage = make_custom_page(config["creditfile"])
285
284
 
286
- custom_profilepage = make_custom_page(@config["profile"])
287
- custom_advfilepage = make_custom_page(@config["advfile"])
288
- if @config["colophon"] && @config["colophon"].kind_of?(String)
289
- custom_colophonpage = make_custom_page(@config["colophon"])
285
+ custom_profilepage = make_custom_page(config["profile"])
286
+ custom_advfilepage = make_custom_page(config["advfile"])
287
+ if config["colophon"] && config["colophon"].kind_of?(String)
288
+ custom_colophonpage = make_custom_page(config["colophon"])
290
289
  end
291
- custom_backcoverpage = make_custom_page(@config["backcover"])
290
+ custom_backcoverpage = make_custom_page(config["backcover"])
292
291
 
293
292
  template = File.expand_path('layout.tex.erb', File.dirname(__FILE__))
294
293
  layout_file = File.join(@basedir, "layouts", "layout.tex.erb")
@@ -297,29 +296,30 @@ module ReVIEW
297
296
  end
298
297
 
299
298
  erb = ERB.new(File.open(template).read)
300
- values = @config # must be 'values' for legacy files
299
+ values = config # must be 'values' for legacy files
301
300
  erb.result(binding)
302
301
  end
303
302
 
304
303
  def copyStyToDir(dirname, copybase, extname = "sty")
305
304
  unless File.directory?(dirname)
306
- warn "No such directory - #{dirname}"
305
+ $stderr.puts "No such directory - #{dirname}"
307
306
  return
308
307
  end
309
308
 
310
- Dir.open(dirname) do |dir|
311
- dir.each do |fname|
312
- if File.extname(fname).downcase == "."+extname
313
- FileUtils.mkdir_p(copybase)
314
- FileUtils.cp File.join(dirname, fname), copybase
309
+ Dir.open(dirname) {|dir|
310
+ dir.each {|fname|
311
+ next if fname =~ /^\./
312
+ if fname =~ /\.(#{extname})$/i
313
+ Dir.mkdir(copybase) unless File.exist?(copybase)
314
+ FileUtils.cp "#{dirname}/#{fname}", copybase
315
315
  end
316
- end
317
- end
316
+ }
317
+ }
318
318
  end
319
319
 
320
- def call_hook(hookname)
321
- if @config["pdfmaker"].instance_of?(Hash) && @config["pdfmaker"][hookname]
322
- hook = File.absolute_path(@config["pdfmaker"][hookname], @basedir)
320
+ def call_hook(hookname, config)
321
+ if config["pdfmaker"].instance_of?(Hash) && config["pdfmaker"][hookname]
322
+ hook = File.absolute_path(config["pdfmaker"][hookname], @basedir)
323
323
  if ENV["REVIEW_SAFE_MODE"].to_i & 1 > 0
324
324
  warn "hook configuration is prohibited in safe mode. ignored."
325
325
  else
@@ -24,10 +24,24 @@ module ReVIEW
24
24
  end
25
25
 
26
26
  def warn(msg)
27
+ if @config["outencoding"] =~ /^EUC$/
28
+ msg = NKF.nkf("-W -e", msg)
29
+ elsif @config["outencoding"] =~ /^SJIS$/
30
+ msg = NKF.nkf("-W -s", msg)
31
+ elsif @config["outencoding"] =~ /^JIS$/
32
+ msg = NKF.nkf("-W -j", msg)
33
+ end
27
34
  $stderr.puts "#{location()}: warning: #{msg}"
28
35
  end
29
36
 
30
37
  def error(msg)
38
+ if @config["outencoding"] =~ /^EUC$/
39
+ msg = NKF.nkf("-W -e", msg)
40
+ elsif @config["outencoding"] =~ /^SJIS$/
41
+ msg = NKF.nkf("-W -s", msg)
42
+ elsif @config["outencoding"] =~ /^JIS$/
43
+ msg = NKF.nkf("-W -j", msg)
44
+ end
31
45
  @errutils_err = true
32
46
  raise ApplicationError, "#{location()}: #{msg}"
33
47
  end
@@ -134,7 +148,7 @@ module ReVIEW
134
148
  path = expand(direc.arg)
135
149
  ent = @repository.fetch_file(path)
136
150
  ent = evaluate(path, ent) if direc['eval']
137
- replace_block(f, line, ent, false) # FIXME: turn off lineno: tmp
151
+ replace_block f, line, ent, false # FIXME: turn off lineno: tmp
138
152
 
139
153
  when /\A\#@map(?:range)?/
140
154
  direc = parse_directive(line, 2, 'unindent')
@@ -142,7 +156,7 @@ module ReVIEW
142
156
  ent = @repository.fetch_range(path, direc.args[1]) or
143
157
  error "unknown range: #{path}: #{direc.args[1]}"
144
158
  ent = (direc['unindent'] ? unindent(ent, direc['unindent']) : ent)
145
- replace_block(f, line, ent, false) # FIXME: turn off lineno: tmp
159
+ replace_block f, line, ent, false # FIXME: turn off lineno: tmp
146
160
 
147
161
  when /\A\#@end/
148
162
  error 'unbaranced #@end'
@@ -153,7 +167,7 @@ module ReVIEW
153
167
  warn "unkown directive: #{line.strip}" unless known_directive?(op)
154
168
  @f.print line
155
169
 
156
- when /\A\s*\z/ # empty line
170
+ when /\A\s*\z/ # empty line
157
171
  @f.puts
158
172
  else
159
173
  @f.print line
@@ -169,11 +183,34 @@ module ReVIEW
169
183
  KNOWN_DIRECTIVES.index(op)
170
184
  end
171
185
 
186
+ def convert_outencoding(*s)
187
+ ine = ""
188
+ if @config["inencoding"] =~ /^EUC$/i
189
+ ine = "-E,"
190
+ elsif @config["inencoding"] =~ /^SJIS$/i
191
+ ine = "-S,"
192
+ elsif @config["inencoding"] =~ /^JIS$/i
193
+ ine = "-J,"
194
+ elsif @config["inencoding"] =~ /^UTF\-8$/i
195
+ ine = "-W,"
196
+ end
197
+
198
+ if @config["outencoding"] =~ /^EUC$/i
199
+ NKF.nkf("#{ine} -m0x -e", *s)
200
+ elsif @config["outencoding"] =~ /^SJIS$/i
201
+ NKF.nkf("#{ine} -m0x -s", *s)
202
+ elsif @config["outencoding"] =~ /^JIS$/i
203
+ NKF.nkf("#{ine} -m0x -j", *s)
204
+ else
205
+ NKF.nkf("#{ine} -m0x -w", *s)
206
+ end
207
+ end
208
+
172
209
  def replace_block(f, directive_line, newlines, with_lineno)
173
210
  @f.print directive_line
174
211
  newlines.each do |line|
175
212
  print_number line.number if with_lineno
176
- @f.print line.string
213
+ @f.print convert_outencoding(line.string)
177
214
  end
178
215
  skip_list f
179
216
  end
@@ -206,8 +243,8 @@ module ReVIEW
206
243
  class Directive
207
244
  def initialize(op, args, opts)
208
245
  @op = op
209
- @args = args
210
- @opts = opts
246
+ @args = args
247
+ @opts = opts
211
248
  end
212
249
 
213
250
  attr_reader :op
@@ -260,12 +297,12 @@ module ReVIEW
260
297
 
261
298
  def optarg_value(spec)
262
299
  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]
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]
269
306
  spec
270
307
  end
271
308
  end
@@ -428,7 +465,7 @@ module ReVIEW
428
465
  repo = {'file' => whole}
429
466
  curr = {'WHOLE' => whole}
430
467
  lineno = 1
431
- yacchack = false # remove ';'-only lines.
468
+ yacchack = false # remove ';'-only lines.
432
469
  opened = [['(not opened)', '(not opened)']] * 3
433
470
 
434
471
  f.each do |line|
@@ -473,7 +510,7 @@ module ReVIEW
473
510
  when /(?:\A\#@|\#@@)yacchack/
474
511
  yacchack = true
475
512
 
476
- when /\A\#@-/ # does not increment line number.
513
+ when /\A\#@-/ # does not increment line number.
477
514
  line = canonical($')
478
515
  curr.each_value do |list|
479
516
  list.push Line.new(nil, line)
@@ -0,0 +1,724 @@
1
+ %% name = ReVIEW::Compiler
2
+
3
+ %% {
4
+ class Error; end
5
+ class Position
6
+ attr_accessor :pos, :line, :col
7
+ def initialize(compiler)
8
+ @pos = compiler.pos
9
+ @line = compiler.current_line
10
+ @col = compiler.current_column
11
+ end
12
+ end
13
+
14
+ require 'review/location'
15
+ require 'review/extentions'
16
+ require 'review/preprocessor'
17
+ require 'review/exception'
18
+ require 'review/node'
19
+ require 'lineinput'
20
+ if RUBY_VERSION > '1.9' then
21
+ require 'review/compiler/literals_1_9'
22
+ else
23
+ require 'review/compiler/literals_1_8'
24
+ end
25
+
26
+ ## redifine Compiler.new
27
+ def initialize(strategy)
28
+ @strategy = strategy
29
+ @current_column = nil
30
+ @chapter = nil
31
+ end
32
+
33
+ attr_accessor :strategy
34
+
35
+ def compile(chap)
36
+ @chapter = chap
37
+ do_compile
38
+ @strategy.result
39
+ end
40
+
41
+ def do_compile
42
+ @strategy.bind self, @chapter, ReVIEW::Location.new(@chapter.basename, self)
43
+ setup_parser(@chapter.content)
44
+ parse()
45
+ convert_ast
46
+ end
47
+
48
+ def convert_ast
49
+ ast = @strategy.ast
50
+ convert_column(ast)
51
+ if $DEBUG
52
+ File.open("review-dump.json","w") do |f|
53
+ f.write(ast.to_json)
54
+ end
55
+ end
56
+ @strategy.output << ast.to_doc
57
+ end
58
+
59
+ def flush_column(new_content)
60
+ if @current_column
61
+ new_content << @current_column
62
+ @current_column = nil
63
+ end
64
+ end
65
+
66
+ def convert_column(ast)
67
+ @column_stack = []
68
+ content = ast.content
69
+ new_content = []
70
+ @current_content = new_content
71
+ content.each do |elem|
72
+ if elem.kind_of?(ReVIEW::HeadlineNode) && elem.cmd && elem.cmd.to_doc == "column"
73
+ flush_column(new_content)
74
+ @current_content = []
75
+ @current_column = ReVIEW::ColumnNode.new(elem.compiler, elem.position, elem.level,
76
+ elem.label, elem.content, @current_content)
77
+ next
78
+ elsif elem.kind_of?(ReVIEW::HeadlineNode) && elem.cmd && elem.cmd.to_doc =~ %r|^/|
79
+ cmd_name = elem.cmd.to_doc[1..-1]
80
+ if cmd_name != "column"
81
+ raise ReVIEW::CompileError, "#{cmd_name} is not opened."
82
+ end
83
+ flush_column(new_content)
84
+ @current_content = new_content
85
+ next
86
+ elsif elem.kind_of?(ReVIEW::HeadlineNode) && @current_column && elem.level <= @current_column.level
87
+ flush_column(new_content)
88
+ @current_content = new_content
89
+ end
90
+ @current_content << elem
91
+ end
92
+ flush_column(new_content)
93
+ ast.content = new_content
94
+ ast
95
+ end
96
+
97
+ def compile_text(text)
98
+ @strategy.nofunc_text(text)
99
+ end
100
+
101
+ class SyntaxElement
102
+ def initialize(name, type, argc, esc, &block)
103
+ @name = name
104
+ @type = type
105
+ @argc_spec = argc
106
+ @esc_patterns = esc
107
+ @checker = block
108
+ end
109
+
110
+ attr_reader :name
111
+
112
+ def check_args(args)
113
+ unless @argc_spec === args.size
114
+ raise ReVIEW::CompileError, "wrong # of parameters (block command //#{@name}, expect #{@argc_spec} but #{args.size})"
115
+ end
116
+ @checker.call(*args) if @checker
117
+ end
118
+
119
+ def min_argc
120
+ case @argc_spec
121
+ when Range then @argc_spec.begin
122
+ when Integer then @argc_spec
123
+ else
124
+ raise TypeError, "argc_spec is not Range/Integer: #{inspect()}"
125
+ end
126
+ end
127
+
128
+ def parse_args(args)
129
+ if @esc_patterns
130
+ args.map.with_index do |pattern, i|
131
+ if @esc_patterns[i]
132
+ args[i].__send__("to_#{@esc_patterns[i]}")
133
+ else
134
+ args[i].to_doc
135
+ end
136
+ end
137
+ else
138
+ args.map(&:to_doc)
139
+ end
140
+ end
141
+
142
+ def block_required?
143
+ @type == :block or @type == :code_block
144
+ end
145
+
146
+ def block_allowed?
147
+ @type == :block or @type == :code_block or @type == :optional or @type == :optional_code_block
148
+ end
149
+
150
+ def code_block?
151
+ @type == :code_block or @type == :optional_code_block
152
+ end
153
+ end
154
+
155
+ SYNTAX = {}
156
+
157
+ def self.defblock(name, argc, optional = false, esc = nil, &block)
158
+ defsyntax(name, (optional ? :optional : :block), argc, esc, &block)
159
+ end
160
+
161
+ def self.defcodeblock(name, argc, optional = false, esc = nil, &block)
162
+ defsyntax(name, (optional ? :optional_code_block : :code_block), argc, esc, &block)
163
+ end
164
+
165
+ def self.defsingle(name, argc, esc = nil, &block)
166
+ defsyntax name, :line, argc, esc, &block
167
+ end
168
+
169
+ def self.defsyntax(name, type, argc, esc = nil, &block)
170
+ SYNTAX[name] = SyntaxElement.new(name, type, argc, esc, &block)
171
+ end
172
+
173
+ def syntax_defined?(name)
174
+ SYNTAX.key?(name.to_sym)
175
+ end
176
+
177
+ def syntax_descriptor(name)
178
+ SYNTAX[name.to_sym]
179
+ end
180
+
181
+ class InlineSyntaxElement
182
+ def initialize(name)
183
+ @name = name
184
+ end
185
+
186
+ attr_reader :name
187
+ end
188
+
189
+ INLINE = {}
190
+
191
+ def self.definline(name)
192
+ INLINE[name] = InlineSyntaxElement.new(name)
193
+ end
194
+
195
+ def inline_defined?(name)
196
+ INLINE.key?(name.to_sym)
197
+ end
198
+
199
+ defblock :read, 0
200
+ defblock :lead, 0
201
+ defblock :quote, 0
202
+ defblock :bibpaper, 2..3, true, [:raw, :doc, :doc]
203
+ defblock :doorquote, 1, false, [:doc]
204
+ defblock :talk, 0
205
+ defblock :graph, 1..3, false, [:raw, :raw, :doc]
206
+
207
+ defcodeblock :emlist, 0..2, false, [:doc, :raw]
208
+ defcodeblock :cmd, 0..1, false, [:doc]
209
+ defcodeblock :source, 0..1, false, [:doc]
210
+ defcodeblock :list, 2..3, false, [:raw, :doc, :raw]
211
+ defcodeblock :listnum, 2..3, false, [:raw, :doc, :raw]
212
+ defcodeblock :emlistnum, 0..2, false, [:doc, :raw]
213
+ defcodeblock :texequation, 0, false
214
+ defcodeblock :table, 0..2, [:raw, :doc]
215
+ defcodeblock :image, 2..3, true, [:raw,:doc,:raw]
216
+ defcodeblock :box, 0..1, false, [:doc]
217
+
218
+ defblock :address, 0
219
+ defblock :blockquote, 0
220
+ defblock :bpo, 0
221
+ defblock :flushright, 0
222
+ defblock :centering, 0
223
+ defblock :note, 0..1
224
+ defblock :comment, 0..1, true
225
+
226
+ defsingle :footnote, 2, [:raw, :doc]
227
+ defsingle :noindent, 0
228
+ defsingle :linebreak, 0
229
+ defsingle :pagebreak, 0
230
+ defsingle :indepimage, 1..3, [:raw, :doc, :raw]
231
+ defsingle :numberlessimage, 1..3, [:raw, :doc, :raw]
232
+ defsingle :hr, 0
233
+ defsingle :parasep, 0
234
+ defsingle :label, 1, [:raw]
235
+ defsingle :raw, 1, [:raw]
236
+ defsingle :tsize, 1, [:raw]
237
+ defsingle :include, 1, [:raw]
238
+ defsingle :olnum, 1, [:raw]
239
+
240
+ definline :chapref
241
+ definline :chap
242
+ definline :title
243
+ definline :img
244
+ definline :imgref
245
+ definline :icon
246
+ definline :list
247
+ definline :table
248
+ definline :fn
249
+ definline :kw
250
+ definline :ruby
251
+ definline :bou
252
+ definline :ami
253
+ definline :b
254
+ definline :dtp
255
+ definline :code
256
+ definline :bib
257
+ definline :hd
258
+ definline :href
259
+ definline :recipe
260
+ definline :column
261
+
262
+ definline :abbr
263
+ definline :acronym
264
+ definline :cite
265
+ definline :dfn
266
+ definline :em
267
+ definline :kbd
268
+ definline :q
269
+ definline :samp
270
+ definline :strong
271
+ definline :var
272
+ definline :big
273
+ definline :small
274
+ definline :del
275
+ definline :ins
276
+ definline :sup
277
+ definline :sub
278
+ definline :tt
279
+ definline :i
280
+ definline :tti
281
+ definline :ttb
282
+ definline :u
283
+ definline :raw
284
+ definline :br
285
+ definline :m
286
+ definline :uchar
287
+ definline :idx
288
+ definline :hidx
289
+ definline :comment
290
+ definline :include
291
+
292
+
293
+ def compile_column(level, label, caption, content)
294
+ buf = ""
295
+ buf << @strategy.__send__("column_begin", level, label, caption)
296
+ buf << content.to_doc
297
+ buf << @strategy.__send__("column_end", level)
298
+ buf
299
+ end
300
+
301
+ def compile_command(name, args, lines, node)
302
+ syntax = syntax_descriptor(name)
303
+ if !syntax || (!@strategy.respond_to?(syntax.name) && !@strategy.respond_to?("node_#{syntax.name}"))
304
+ error "strategy does not support command: //#{name}"
305
+ compile_unknown_command args, lines
306
+ return
307
+ end
308
+ begin
309
+ syntax.check_args args
310
+ rescue ReVIEW::CompileError => err
311
+ error err.message
312
+ args = ['(NoArgument)'] * syntax.min_argc
313
+ end
314
+ if syntax.block_allowed?
315
+ compile_block(syntax, args, lines, node)
316
+ else
317
+ if lines
318
+ error "block is not allowed for command //#{syntax.name}; ignore"
319
+ end
320
+ compile_single(syntax, args, node)
321
+ end
322
+ end
323
+
324
+ def compile_headline(level, tag, label, caption)
325
+ buf = ""
326
+ @headline_indexs ||= [0] ## XXX
327
+ caption ||= ""
328
+ caption.strip!
329
+ index = level - 1
330
+ if @headline_indexs.size > (index + 1)
331
+ @headline_indexs = @headline_indexs[0..index]
332
+ end
333
+ @headline_indexs[index] = 0 if @headline_indexs[index].nil?
334
+ @headline_indexs[index] += 1
335
+ buf << @strategy.headline(level, label, caption)
336
+ buf
337
+ end
338
+
339
+ def comment(text)
340
+ @strategy.comment(text)
341
+ end
342
+
343
+ def compile_ulist(content)
344
+ buf0 = ""
345
+ level = 0
346
+ content.each do |element|
347
+ current_level, buf = element.level, element.to_doc
348
+ if level == current_level
349
+ buf0 << @strategy.ul_item_end
350
+ # body
351
+ buf0 << @strategy.ul_item_begin([buf])
352
+ elsif level < current_level # down
353
+ level_diff = current_level - level
354
+ level = current_level
355
+ (1..(level_diff - 1)).to_a.reverse.each do |i|
356
+ buf0 << @strategy.ul_begin{i}
357
+ buf0 << @strategy.ul_item_begin([])
358
+ end
359
+ buf0 << @strategy.ul_begin{level}
360
+ buf0 << @strategy.ul_item_begin([buf])
361
+ elsif level > current_level # up
362
+ level_diff = level - current_level
363
+ level = current_level
364
+ (1..level_diff).to_a.reverse.each do |i|
365
+ buf0 << @strategy.ul_item_end
366
+ buf0 << @strategy.ul_end{level + i}
367
+ end
368
+ buf0 << @strategy.ul_item_end
369
+ # body
370
+ buf0 <<@strategy.ul_item_begin([buf])
371
+ end
372
+ end
373
+
374
+ (1..level).to_a.reverse.each do |i|
375
+ buf0 << @strategy.ul_item_end
376
+ buf0 << @strategy.ul_end{i}
377
+ end
378
+ buf0
379
+ end
380
+
381
+ def compile_olist(content)
382
+ buf0 = ""
383
+ buf0 << @strategy.ol_begin
384
+ content.each do |element|
385
+ ## XXX 1st arg should be String, not Array
386
+ buf0 << @strategy.ol_item(element.to_doc.split(/\n/), element.num)
387
+ end
388
+ buf0 << @strategy.ol_end
389
+ buf0
390
+ end
391
+
392
+ def compile_dlist(content)
393
+ buf = ""
394
+ buf << @strategy.dl_begin
395
+ content.each do |element|
396
+ buf << @strategy.dt(element.text.to_doc)
397
+ buf << @strategy.dd(element.content.map{|s| s.to_doc})
398
+ end
399
+ buf << @strategy.dl_end
400
+ buf
401
+ end
402
+
403
+
404
+ def compile_unknown_command(args, lines)
405
+ @strategy.unknown_command(args, lines)
406
+ end
407
+
408
+ def compile_block(syntax, args, lines, node)
409
+ node_name = "node_#{syntax.name}".to_sym
410
+ if @strategy.respond_to?(node_name)
411
+ @strategy.__send__(node_name, node)
412
+ else
413
+ args_conv = syntax.parse_args(args)
414
+ @strategy.__send__(syntax.name, (lines || default_block(syntax)), *args_conv)
415
+ end
416
+ end
417
+
418
+ def default_block(syntax)
419
+ if syntax.block_required?
420
+ error "block is required for //#{syntax.name}; use empty block"
421
+ end
422
+ []
423
+ end
424
+
425
+ def compile_single(syntax, args, node)
426
+ node_name = "node_#{syntax.name}".to_sym
427
+ if @strategy.respond_to?(node_name)
428
+ @strategy.__send__(node_name, node)
429
+ else
430
+ args_conv = syntax.parse_args(args)
431
+ @strategy.__send__(syntax.name, *args_conv)
432
+ end
433
+ end
434
+
435
+
436
+ def compile_inline(op, args)
437
+ unless inline_defined?(op)
438
+ raise ReVIEW::CompileError, "no such inline op: #{op}"
439
+ end
440
+ if @strategy.respond_to?("node_inline_#{op}")
441
+ return @strategy.__send__("node_inline_#{op}", args)
442
+ end
443
+ unless @strategy.respond_to?("inline_#{op}")
444
+ raise "strategy does not support inline op: @<#{op}>"
445
+ end
446
+ if !args
447
+ @strategy.__send__("inline_#{op}", "")
448
+ else
449
+ @strategy.__send__("inline_#{op}", *(args.map(&:to_doc)))
450
+ end
451
+ rescue => err
452
+ error err.message
453
+ end
454
+
455
+ def compile_paragraph(buf)
456
+ @strategy.paragraph buf
457
+ end
458
+
459
+ def compile_raw(builders, content)
460
+ c = @strategy.class.to_s.gsub(/ReVIEW::/, '').gsub(/Builder/, '').downcase
461
+ if !builders || builders.include?(c)
462
+ content.gsub("\\n", "\n")
463
+ else
464
+ ""
465
+ end
466
+ end
467
+
468
+ def warn(msg)
469
+ @strategy.warn msg
470
+ end
471
+
472
+ def error(msg)
473
+ @strategy.error msg
474
+ end
475
+
476
+ def check_indent(s)
477
+ s.size >= @list_stack.last.size
478
+ end
479
+
480
+ def check_nested_indent(s)
481
+ s.size >= @list_stack.last.size + 2
482
+ end
483
+
484
+ def position
485
+ Position.new(self)
486
+ end
487
+
488
+
489
+ }
490
+
491
+ %% ast-location = ::ReVIEW
492
+ %% headline = ast HeadlineNode(compiler, position, level, cmd, label, content)
493
+ %% paragraph = ast ParagraphNode(compiler, position, content)
494
+ %% block_element = ast BlockElementNode(compiler, position, name, args, content)
495
+ %% code_block_element = ast CodeBlockElementNode(compiler, position, name, args, content)
496
+ %% inline_element = ast InlineElementNode(compiler, position, symbol, content)
497
+ %% inline_element_content = ast InlineElementContentNode(compiler, position, content)
498
+ %% text = ast TextNode(compiler, position, content)
499
+ %% raw = ast RawNode(compiler, builder, position, content)
500
+ %% brace = ast BraceNode(compiler, position, content)
501
+ %% singleline_content = ast SinglelineContentNode(compiler, position, content)
502
+ %% singleline_comment = ast SinglelineCommentNode(compiler, position, content)
503
+ %% ulist = ast UlistNode(compiler, position, content)
504
+ %% ulist_element = ast UlistElementNode(compiler, position, level, content)
505
+ %% olist = ast OlistNode(compiler, position, content)
506
+ %% olist_element = ast OlistElementNode(compiler, position, num, content)
507
+ %% dlist = ast DlistNode(compiler, position, content)
508
+ %% dlist_element = ast DlistElementNode(compiler, position, text, content)
509
+ %% bracket_arg = ast BracketArgNode(compiler, position, content)
510
+ %% document = ast DocumentNode(compiler, position, content)
511
+ %% column = ast ColumnNode(compiler, position, level, label, caption, content)
512
+ %% newline = ast NewLineNode(compiler, position, content)
513
+
514
+ # %% dummy
515
+
516
+ root = Start
517
+
518
+ Start = &. { @list_stack = Array.new } Document:c { @strategy.ast = c }
519
+
520
+ ## a Document is a set of Blocks
521
+ Document = BOM? Block*:c ~document(self, position, c)
522
+
523
+ ## ignore leading blank lines
524
+ Block = BlankLine*:c { c }
525
+ ( SinglelineComment:c
526
+ | Headline:c
527
+ | BlockElement:c
528
+ | Ulist:c
529
+ | Olist:c
530
+ | Dlist:c
531
+ | Paragraph:c
532
+ ) { c }
533
+
534
+ BlankLine = Newline
535
+
536
+ SinglelineComment = ("#@" < NonNewline+ > EOL) ~singleline_comment(self, position, text)
537
+
538
+ Headline = HeadlinePrefix:level BracketArg?:cmd BraceArg?:label Space* SinglelineContent?:caption EOL ~headline(self, position, level, cmd, label, caption)
539
+
540
+ HeadlinePrefix = < /={1,5}/ > { text.length }
541
+
542
+ Paragraph = ParagraphLine+:c ~paragraph(self, position, c.flatten)
543
+
544
+ ParagraphLine = !Headline !SinglelineComment !BlockElement !Ulist !Olist !Dlist SinglelineContent:c Newline { c }
545
+
546
+ # There are 3 types of Block Element: raw Block, Code Block, and Normal Block
547
+ BlockElement = ( "//raw[" RawBlockBuilderSelect?:b RawBlockElementArg*:r1 "]" Space* EOL
548
+ ~raw(self, position, b, r1)
549
+ | !"//raw" "//" ElementName:symbol &{ syntax = syntax_descriptor(symbol); syntax && syntax.code_block? } BracketArg*:args "{" Space* Newline CodeBlockElementContents?:contents "//}" Space* EOL
550
+ ~code_block_element(self, position, symbol, args, contents)
551
+ | !"//raw" "//" ElementName:symbol BracketArg*:args "{" Space* Newline BlockElementContents?:contents "//}" Space* EOL
552
+ ~block_element(self, position, symbol, args, contents)
553
+ | !"//raw" "//" ElementName:symbol BracketArg*:args Space* EOL ~block_element(self, position, symbol, args, nil)
554
+ )
555
+
556
+ RawBlockBuilderSelect = "|" Space* RawBlockBuilderSelectSub:c Space* "|" { c }
557
+
558
+ RawBlockBuilderSelectSub = ( < AlphanumericAscii+ >:c1 Space* "," Space* RawBlockBuilderSelectSub:c2
559
+ { [text] + c2 }
560
+ | < AlphanumericAscii+ >:c1
561
+ { [text] }
562
+ )
563
+
564
+ RawBlockElementArg = !"]" ( "\\]" { "]" }
565
+ | "\\n" { "\n" }
566
+ | < NonNewline > { text }
567
+ )
568
+
569
+ BracketArg = "[" BracketArgInline*:content "]" ~bracket_arg(self, position, content)
570
+
571
+ ## XXX '\' (excpet '\]' and '\\' ) => '\' is ???
572
+ BracketArgInline = ( InlineElement:c { c }
573
+ | "\\]" ~text(self, position, "]")
574
+ | "\\\\" ~text(self, position, "\\")
575
+ | < /[^\r\n\]]/ > ~text(self, position, text)
576
+ )
577
+
578
+ BraceArg = "{" < /([^\r\n}\\]|\\[^\r\n])*/ > "}" { text }
579
+
580
+ ## Standard BlockElement has nested blocks. Texts in content of block are parsed as Paragraph.
581
+ BlockElementContents = BlockElementContent+:c { c }
582
+
583
+ ### Headline is prohibited.
584
+ ### Note: do not allow "//}" at front of Paragraph.
585
+ BlockElementContent = ( SinglelineComment:c { c }
586
+ | BlockElement:c { c }
587
+ | Ulist:c
588
+ | Dlist:c
589
+ | Olist:c
590
+ | BlankLine:c { c }
591
+ | BlockElementParagraph:c { c }
592
+ )
593
+
594
+ ## it's like Paragraph, but it's in a block, so do not allow '//}\n'
595
+ BlockElementParagraph = BlockElementParagraphLine+:c ~paragraph(self, position, c.flatten)
596
+ BlockElementParagraphLine = !"//}" !BlankLine !SinglelineComment !BlockElement !Ulist !Olist !Dlist SinglelineContent:c Newline { c }
597
+
598
+ ## In CodeBlockElementContents, newline should no be ingored. So we use TextNode instead of NewLineNode.
599
+ CodeBlockElementContents = CodeBlockElementContent+:c { c }
600
+ CodeBlockElementContent = ( SinglelineComment:c { c }
601
+ | BlankLine:c { ::ReVIEW::TextNode.new(self, position, "\n") }
602
+ | !"//}" SinglelineContent:c Newline { [c, ::ReVIEW::TextNode.new(self, position, "\n")] }
603
+ )
604
+
605
+ ## Ulist and Olist
606
+
607
+ Bullet = "*"
608
+ Enumerator = < /[0-9]+/ > { num = text } "." { num.to_i }
609
+
610
+ Ulist = Indent+:s Bullet+:b Space+ { @list_stack.push(s) } UlistItemBlock:item
611
+ { if b.size > 1 then item.level = b.size end }
612
+ (UlistItem | UlistItemMore | NestedList)*:items &{ s == @list_stack.pop }
613
+ ~ulist(self, position, items.unshift(item))
614
+
615
+ Olist = Indent+:s Enumerator:e Space+ { @list_stack.push(s) } OlistItemBlock:item
616
+ { item.num = e }
617
+ (OlistItem | NestedList)*:items &{ s == @list_stack.pop }
618
+ ~olist(self, position, items.unshift(item))
619
+
620
+ UlistItemBlock = ListItemFirstLine:c ListItemLine*:d ~ulist_element(self, position, @list_stack.size, d.unshift(c))
621
+ OlistItemBlock = ListItemFirstLine:c ListItemLine*:d ~olist_element(self, position, 0, d.unshift(c))
622
+
623
+ ListItemFirstLine = SinglelineContent:c Newline { c }
624
+ ListItemLine = Indent+:s !Bullet !Enumerator !Space SinglelineContent:c &{ check_indent(s) } Newline { c }
625
+
626
+ UlistItemMore = Indent+:s Bullet Bullet+:b Space+ &{ check_indent(s) } UlistItemBlock:item { item.level = b.size+1; item }
627
+
628
+ UlistItem = Indent+:s Bullet Space+ &{ check_indent(s) } UlistItemBlock:item { item }
629
+ OlistItem = Indent+:s Enumerator:e Space+ &{ check_indent(s) } OlistItemBlock:item { item.num = e; item }
630
+
631
+ ## NestedList is markdown-like indented syntax.
632
+ ## You can write nested Ulist and Olist with this syntax.
633
+
634
+ NestedList = (NestedUlist | NestedOlist)
635
+
636
+ NestedUlist = Indent+:s Bullet Space+ &{ check_nested_indent(s) } { @list_stack.push(s) } UlistItemBlock:item
637
+ (UlistItem | NestedList)*:items &{ s == @list_stack.pop }
638
+ ~ulist(self, position, items.unshift(item))
639
+ NestedOlist = Indent+:s Enumerator:e Space+ &{ check_nested_indent(s) } { @list_stack.push(s) } OlistItemBlock:item
640
+ { item.num = e }
641
+ (OlistItem | NestedList)*:items &{ s == @list_stack.pop }
642
+ ~olist(self, position, items.unshift(item))
643
+
644
+ # Dlist
645
+ Dlist = (DlistElement | SinglelineComment)+:content ~dlist(self, position, content)
646
+
647
+ DlistElement = Indent* ":" Space+ SinglelineContent:text Newline DlistElementContent+:content ~dlist_element(self, position, text, content)
648
+
649
+ DlistElementContent = (SinglelineComment:c { c }
650
+ |Space+ SinglelineContent:c Newline { c }
651
+ )
652
+
653
+ SinglelineContent = Inline+:c ~singleline_content(self, position, c)
654
+
655
+
656
+ # Inline Element and Non Inline Element
657
+
658
+ Inline = ( InlineElement | NonInlineElement)
659
+
660
+ NonInlineElement = !InlineElement < NonNewline > ~text(self, position, text)
661
+
662
+ InlineElement = ( RawInlineElement:c { c }
663
+ | !RawInlineElement "@<" InlineElementSymbol:symbol ">" "{" InlineElementContents?:contents "}" ~inline_element(self, position, symbol,contents)
664
+ )
665
+
666
+ RawInlineElement = "@<raw>{" RawBlockBuilderSelect?:builders RawInlineElementContent+:c "}" ~raw(self, position, builders,c)
667
+
668
+ RawInlineElementContent = ( "\\}" { "}" }
669
+ | < /[^\r\n\}]/ > { text }
670
+ )
671
+
672
+ InlineElementSymbol = < AlphanumericAscii+ > { text }
673
+
674
+ InlineElementContents = !"}" InlineElementContentsSub:c { c }
675
+
676
+ InlineElementContentsSub = !"}" ( Space* InlineElementContent:c1 Space* "," InlineElementContentsSub:c2 { [c1]+c2 }
677
+ | Space* InlineElementContent:c1 Space* { [c1] }
678
+ )
679
+
680
+
681
+ InlineElementContent = InlineElementContentSub+:d { d }
682
+
683
+ InlineElementContentSub = ( InlineElement:c { c }
684
+ | !InlineElement QuotedInlineText:content ~inline_element_content(self, position, content)
685
+ | !InlineElement InlineElementContentText+:content ~inline_element_content(self, position, content)
686
+ )
687
+
688
+ ## Quoted Inline Text is "..."
689
+ ## In Quoted Inline Text, "\\" is "\", "\'" is "'", other characters are kept as is.
690
+ QuotedInlineText = "\""
691
+ ( "\\\"" { "\"" }
692
+ | "\\\\" { "\\" }
693
+ | < /[^"\r\n\\]/ > { text }
694
+ )+:str "\"" ~text(self, position, str.join(""))
695
+
696
+ ## XXX '\' (excpet '\}' and '\,' and '\\' ) => '\' is OK?
697
+ InlineElementContentText = ( "\\}" ~text(self, position, "}")
698
+ | "\\," ~text(self, position, ",")
699
+ | "\\\\" ~text(self, position, "\\" )
700
+ | "\\" ~text(self, position, "\\" )
701
+ | !InlineElement < /[^\r\n\\},]/> ~text(self, position, text)
702
+ )
703
+
704
+ NonNewline = /[^\r\n]/
705
+
706
+ Space = /[ \t]/
707
+
708
+ Indent = " "
709
+
710
+ EOL = (Newline|EOF)
711
+
712
+ EOF = !.
713
+
714
+ ElementName = < LowerAlphabetAscii+ > { text }
715
+
716
+ %literals = ReVIEW::Compiler::Literals
717
+ Alphanumeric = %literals.Alphanumeric
718
+ AlphanumericAscii = %literals.AlphanumericAscii
719
+ LowerAlphabetAscii = %literals.LowerAlphabetAscii
720
+ Digit = %literals.Digit
721
+ BOM = %literals.BOM
722
+ Newline = %literals.Newline:n ~newline(self, position, "\n")
723
+ NonAlphanumeric = %literals.NonAlphanumeric
724
+ Spacechar = %literals.Spacechar