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.
- checksums.yaml +4 -4
- data/.rubocop.yml +1 -43
- data/.travis.yml +0 -9
- data/ChangeLog +0 -16
- data/Gemfile +1 -0
- data/README.rdoc +1 -1
- data/Rakefile +12 -1
- data/bin/review-check +21 -8
- data/bin/review-compile +15 -9
- data/bin/review-epubmaker-legacy +6 -6
- data/bin/review-index +13 -2
- data/bin/review-init +18 -12
- data/bin/review-preproc +14 -1
- data/bin/review-validate +1 -1
- data/bin/review-vol +13 -1
- data/doc/quickstart.ja.md +1 -1
- data/doc/quickstart.md +1 -1
- data/lib/epubmaker/content.rb +3 -3
- data/lib/epubmaker/epubcommon.rb +108 -91
- data/lib/epubmaker/epubv2.rb +67 -14
- data/lib/epubmaker/epubv3.rb +59 -25
- data/lib/epubmaker/producer.rb +1 -13
- data/lib/lineinput.rb +0 -48
- data/lib/review/book/base.rb +4 -12
- data/lib/review/book/compilable.rb +3 -1
- data/lib/review/book/index.rb +4 -4
- data/lib/review/builder.rb +54 -47
- data/lib/review/compiler.rb +4662 -326
- data/lib/review/compiler/literals_1_8.kpeg +19 -0
- data/lib/review/compiler/literals_1_8.rb +432 -0
- data/lib/review/compiler/literals_1_9.kpeg +22 -0
- data/lib/review/compiler/literals_1_9.rb +435 -0
- data/lib/review/configure.rb +8 -20
- data/lib/review/epubbuilder.rb +1 -1
- data/lib/review/epubmaker.rb +122 -52
- data/lib/review/ewbbuilder.rb +4 -4
- data/lib/review/exception.rb +1 -1
- data/lib/review/extentions.rb +1 -0
- data/lib/review/extentions/array.rb +25 -0
- data/lib/review/htmlbuilder.rb +286 -275
- data/lib/review/htmllayout.rb +0 -2
- data/lib/review/htmlutils.rb +4 -4
- data/lib/review/i18n.rb +2 -6
- data/lib/review/i18n.yml +1 -1
- data/lib/review/idgxmlbuilder.rb +239 -204
- data/lib/review/inaobuilder.rb +75 -73
- data/lib/review/latexbuilder.rb +265 -219
- data/lib/review/latexutils.rb +6 -6
- data/lib/review/layout.tex.erb +1 -1
- data/lib/review/location.rb +24 -0
- data/lib/review/markdownbuilder.rb +124 -79
- data/lib/review/node.rb +267 -0
- data/lib/review/pdfmaker.rb +92 -92
- data/lib/review/preprocessor.rb +51 -14
- data/lib/review/review.kpeg +724 -0
- data/lib/review/textbuilder.rb +1 -1
- data/lib/review/textutils.rb +24 -18
- data/lib/review/tocparser.rb +3 -3
- data/lib/review/tocprinter.rb +31 -8
- data/lib/review/topbuilder.rb +119 -111
- data/lib/review/unfold.rb +2 -2
- data/lib/review/version.rb +1 -1
- data/review.gemspec +2 -2
- data/rubocop-todo.yml +456 -0
- data/test/assets/test_template.tex +1 -1
- data/test/sample-book/src/config.yml +0 -1
- data/test/test.re +1 -1
- data/test/test_book.rb +4 -4
- data/test/test_book_chapter.rb +70 -0
- data/test/test_book_part.rb +1 -1
- data/test/test_builder.rb +6 -28
- data/test/test_compiler.rb +59 -14
- data/test/test_helper.rb +47 -4
- data/test/test_htmlbuilder.rb +104 -73
- data/test/test_i18n.rb +5 -3
- data/test/test_idgxmlbuilder.rb +5 -2
- data/test/test_inaobuilder.rb +4 -2
- data/test/test_latexbuilder.rb +18 -37
- data/test/test_lineinput.rb +25 -4
- data/test/test_markdownbuilder.rb +4 -22
- data/test/test_pdfmaker.rb +12 -11
- data/test/test_textutils.rb +0 -36
- data/test/test_topbuilder.rb +2 -0
- metadata +14 -28
- data/.rubocop_todo.yml +0 -605
- data/Dockerfile +0 -22
- data/doc/NEWS.ja.md +0 -362
- data/doc/NEWS.md +0 -366
- data/lib/review/htmltoc.rb +0 -45
- data/lib/review/template.rb +0 -21
- data/templates/html/layout-html5.html.erb +0 -17
- data/templates/html/layout-xhtml1.html.erb +0 -20
- data/templates/ncx/epubv2.ncx.erb +0 -11
- data/templates/opf/epubv2.opf.erb +0 -21
- data/templates/opf/epubv3.opf.erb +0 -18
- data/templates/xml/container.xml.erb +0 -6
- data/test/assets/test.xml.erb +0 -3
- data/test/sample-book/src/config-epub2.yml +0 -186
- data/test/test_configure.rb +0 -50
- data/test/test_htmltoc.rb +0 -32
- data/test/test_template.rb +0 -26
data/lib/review/pdfmaker.rb
CHANGED
@@ -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
|
45
|
-
|
46
|
-
|
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
|
-
"./#{
|
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
|
-
|
92
|
+
config = ReVIEW::Configure.values
|
98
93
|
cmd_config, yamlfile = parse_opts(args)
|
99
94
|
|
100
|
-
|
95
|
+
config.merge!(YAML.load_file(yamlfile))
|
101
96
|
# YAML configs will be overridden by command line options.
|
102
|
-
|
103
|
-
I18n.setup(
|
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
|
-
|
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 =
|
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"]
|
131
|
-
@chaps_fnames["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(
|
133
|
+
check_compile_status(config["ignore-errors"])
|
138
134
|
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
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
|
-
|
145
|
-
if
|
146
|
-
|
140
|
+
config["usepackage"] = ""
|
141
|
+
if config["texstyle"]
|
142
|
+
config["usepackage"] = "\\usepackage{#{config['texstyle']}}"
|
147
143
|
end
|
148
144
|
|
149
|
-
copy_images("./images",
|
150
|
-
copyStyToDir(
|
151
|
-
copyStyToDir(
|
152
|
-
copyStyToDir(
|
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)
|
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 =
|
172
|
-
dvicommand =
|
173
|
-
dvioptions =
|
174
|
-
|
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
|
-
|
182
|
+
if File.exist?("book.dvi")
|
182
183
|
system_or_raise("#{dvicommand} #{dvioptions} book.dvi")
|
183
184
|
end
|
184
|
-
|
185
|
-
call_hook("hook_afterdvipdf")
|
186
|
-
|
187
|
-
FileUtils.cp(
|
185
|
+
}
|
186
|
+
call_hook("hook_afterdvipdf", config)
|
187
|
+
|
188
|
+
FileUtils.cp("#{@path}/book.pdf", "#{@basedir}/#{bookname}.pdf")
|
188
189
|
|
189
|
-
unless
|
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=#{
|
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
|
243
|
-
return "#{ReVIEW::I18n.t(role)} & #{escape_latex(join_with_separator(
|
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
|
-
|
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
|
260
|
-
author_names = join_with_separator(
|
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
|
264
|
-
csl_names = join_with_separator(
|
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
|
268
|
-
trl_names = join_with_separator(
|
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 =
|
276
|
-
documentclass =
|
277
|
-
documentclassoption =
|
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(
|
283
|
-
custom_originaltitlepage = make_custom_page(
|
284
|
-
custom_creditpage = make_custom_page(
|
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(
|
287
|
-
custom_advfilepage = make_custom_page(
|
288
|
-
if
|
289
|
-
custom_colophonpage = make_custom_page(
|
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(
|
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 =
|
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
|
-
|
305
|
+
$stderr.puts "No such directory - #{dirname}"
|
307
306
|
return
|
308
307
|
end
|
309
308
|
|
310
|
-
Dir.open(dirname)
|
311
|
-
dir.each
|
312
|
-
if
|
313
|
-
|
314
|
-
|
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
|
-
|
317
|
-
|
316
|
+
}
|
317
|
+
}
|
318
318
|
end
|
319
319
|
|
320
|
-
def call_hook(hookname)
|
321
|
-
if
|
322
|
-
hook = File.absolute_path(
|
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
|
data/lib/review/preprocessor.rb
CHANGED
@@ -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
|
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
|
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/
|
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
|
-
|
210
|
-
|
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'
|
264
|
-
when 'false' then false
|
265
|
-
when 'nil'
|
266
|
-
when nil
|
267
|
-
when /^\d+$/ then $&.to_i
|
268
|
-
else
|
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
|
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\#@-/
|
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
|