review 1.7.2 → 2.0.0.beta1

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 (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