testdata/mybook/lib/ruby/review-compiler.rb in review-retrovert-0.9.7 vs testdata/mybook/lib/ruby/review-compiler.rb in review-retrovert-0.9.8
- old
+ new
@@ -13,59 +13,632 @@
defined?(Compiler) or raise "internal error: Compiler not found."
class Compiler
+ #------------------------------ original code
+
+=begin
+
+ def initialize(strategy)
+ @strategy = strategy
+ end
+
+ attr_reader :strategy
+
+ def compile(chap)
+ @chapter = chap
+ do_compile
+ @strategy.result
+ end
+
+ class SyntaxElement
+ def initialize(name, type, argc, &block)
+ @name = name
+ @type = type
+ @argc_spec = argc
+ @checker = block
+ end
+
+ attr_reader :name
+
+ def check_args(args)
+ unless @argc_spec === args.size
+ raise CompileError, "wrong # of parameters (block command //#{@name}, expect #{@argc_spec} but #{args.size})"
+ end
+ if @checker
+ @checker.call(*args)
+ end
+ end
+
+ def min_argc
+ case @argc_spec
+ when Range then @argc_spec.begin
+ when Integer then @argc_spec
+ else
+ raise TypeError, "argc_spec is not Range/Integer: #{inspect}"
+ end
+ end
+
+ def block_required?
+ @type == :block
+ end
+
+ def block_allowed?
+ @type == :block or @type == :optional
+ end
+ end
+
+ SYNTAX = {}
+
+ def self.defblock(name, argc, optional = false, &block)
+ defsyntax name, (optional ? :optional : :block), argc, &block
+ end
+
+ def self.defsingle(name, argc, &block)
+ defsyntax name, :line, argc, &block
+ end
+
+ def self.defsyntax(name, type, argc, &block)
+ SYNTAX[name] = SyntaxElement.new(name, type, argc, &block)
+ end
+
+ def self.definline(name)
+ INLINE[name] = InlineSyntaxElement.new(name)
+ end
+
+ def syntax_defined?(name)
+ SYNTAX.key?(name.to_sym)
+ end
+
+ def syntax_descriptor(name)
+ SYNTAX[name.to_sym]
+ end
+
+ class InlineSyntaxElement
+ def initialize(name)
+ @name = name
+ end
+
+ attr_reader :name
+ end
+
+ INLINE = {}
+
+ def inline_defined?(name)
+ INLINE.key?(name.to_sym)
+ end
+
+ defblock :read, 0
+ defblock :lead, 0
+ defblock :list, 2..3
+ defblock :emlist, 0..2
+ defblock :cmd, 0..1
+ defblock :table, 0..2
+ defblock :imgtable, 0..2
+ defblock :emtable, 0..1
+ defblock :quote, 0
+ defblock :image, 2..3, true
+ defblock :source, 0..2
+ defblock :listnum, 2..3
+ defblock :emlistnum, 0..2
+ defblock :bibpaper, 2..3, true
+ defblock :doorquote, 1
+ defblock :talk, 0
+ defblock :texequation, 0
+ defblock :graph, 1..3
+ defblock :indepimage, 1..3, true
+ defblock :numberlessimage, 1..3, true
+
+ defblock :address, 0
+ defblock :blockquote, 0
+ defblock :bpo, 0
+ defblock :flushright, 0
+ defblock :centering, 0
+ defblock :note, 0..1
+ defblock :memo, 0..1
+ defblock :info, 0..1
+ defblock :important, 0..1
+ defblock :caution, 0..1
+ defblock :notice, 0..1
+ defblock :warning, 0..1
+ defblock :tip, 0..1
+ defblock :box, 0..1
+ defblock :comment, 0..1, true
+ defblock :embed, 0..1
+
+ defsingle :footnote, 2
+ defsingle :noindent, 0
+ defsingle :blankline, 0
+ defsingle :pagebreak, 0
+ defsingle :hr, 0
+ defsingle :parasep, 0
+ defsingle :label, 1
+ defsingle :raw, 1
+ defsingle :tsize, 1
+ defsingle :include, 1
+ defsingle :olnum, 1
+ defsingle :firstlinenum, 1
+
+ definline :chapref
+ definline :chap
+ definline :title
+ definline :img
+ definline :imgref
+ definline :icon
+ definline :list
+ definline :table
+ definline :fn
+ definline :kw
+ definline :ruby
+ definline :bou
+ definline :ami
+ definline :b
+ definline :dtp
+ definline :code
+ definline :bib
+ definline :hd
+ definline :href
+ definline :recipe
+ definline :column
+ definline :tcy
+
+ definline :abbr
+ definline :acronym
+ definline :cite
+ definline :dfn
+ definline :em
+ definline :kbd
+ definline :q
+ definline :samp
+ definline :strong
+ definline :var
+ definline :big
+ definline :small
+ definline :del
+ definline :ins
+ definline :sup
+ definline :sub
+ definline :tt
+ definline :i
+ definline :tti
+ definline :ttb
+ definline :u
+ definline :raw
+ definline :br
+ definline :m
+ definline :uchar
+ definline :idx
+ definline :hidx
+ definline :comment
+ definline :include
+ definline :tcy
+ definline :embed
+ definline :pageref
+
+ private
+
+ def do_compile
+ f = LineInput.new(StringIO.new(@chapter.content))
+ @strategy.bind self, @chapter, Location.new(@chapter.basename, f)
+ tagged_section_init
+ while f.next?
+ case f.peek
+ when /\A\#@/
+ f.gets # Nothing to do
+ when /\A=+[\[\s\{]/
+ compile_headline f.gets
+ when /\A\s+\*/
+ compile_ulist f
+ when /\A\s+\d+\./
+ compile_olist f
+ when /\A\s*:\s/
+ compile_dlist f
+ when %r{\A//\}}
+ f.gets
+ error 'block end seen but not opened'
+ when %r{\A//[a-z]+}
+ name, args, lines = read_command(f)
+ syntax = syntax_descriptor(name)
+ unless syntax
+ error "unknown command: //#{name}"
+ compile_unknown_command args, lines
+ next
+ end
+ compile_command syntax, args, lines
+ when %r{\A//}
+ line = f.gets
+ warn "`//' seen but is not valid command: #{line.strip.inspect}"
+ if block_open?(line)
+ warn 'skipping block...'
+ read_block(f, false)
+ end
+ else
+ if f.peek.strip.empty?
+ f.gets
+ next
+ end
+ compile_paragraph f
+ end
+ end
+ close_all_tagged_section
+ end
+
+ def compile_headline(line)
+ @headline_indexs ||= [@chapter.number.to_i - 1]
+ m = /\A(=+)(?:\[(.+?)\])?(?:\{(.+?)\})?(.*)/.match(line)
+ level = m[1].size
+ tag = m[2]
+ label = m[3]
+ caption = m[4].strip
+ index = level - 1
+ if tag
+ if tag !~ %r{\A/}
+ if caption.empty?
+ warn 'headline is empty.'
+ end
+ close_current_tagged_section(level)
+ open_tagged_section(tag, level, label, caption)
+ else
+ open_tag = tag[1..-1]
+ prev_tag_info = @tagged_section.pop
+ if prev_tag_info.nil? || prev_tag_info.first != open_tag
+ error "#{open_tag} is not opened."
+ end
+ close_tagged_section(*prev_tag_info)
+ end
+ else
+ if caption.empty?
+ warn 'headline is empty.'
+ end
+ if @headline_indexs.size > (index + 1)
+ @headline_indexs = @headline_indexs[0..index]
+ end
+ if @headline_indexs[index].nil?
+ @headline_indexs[index] = 0
+ end
+ @headline_indexs[index] += 1
+ close_current_tagged_section(level)
+ @strategy.headline level, label, caption
+ end
+ end
+
+ def close_current_tagged_section(level)
+ while @tagged_section.last and @tagged_section.last[1] >= level
+ close_tagged_section(* @tagged_section.pop)
+ end
+ end
+
+ def headline(level, label, caption)
+ @strategy.headline level, label, caption
+ end
+
+ def tagged_section_init
+ @tagged_section = []
+ end
+
+ def open_tagged_section(tag, level, label, caption)
+ mid = "#{tag}_begin"
+ unless @strategy.respond_to?(mid)
+ error "strategy does not support tagged section: #{tag}"
+ headline level, label, caption
+ return
+ end
+ @tagged_section.push [tag, level]
+ @strategy.__send__ mid, level, label, caption
+ end
+
+ def close_tagged_section(tag, level)
+ mid = "#{tag}_end"
+ if @strategy.respond_to?(mid)
+ @strategy.__send__ mid, level
+ else
+ error "strategy does not support block op: #{mid}"
+ end
+ end
+
+ def close_all_tagged_section
+ until @tagged_section.empty?
+ close_tagged_section(* @tagged_section.pop)
+ end
+ end
+
+ def compile_ulist(f)
+ level = 0
+ f.while_match(/\A\s+\*|\A\#@/) do |line|
+ next if line =~ /\A\#@/
+
+ buf = [text(line.sub(/\*+/, '').strip)]
+ f.while_match(/\A\s+(?!\*)\S/) do |cont|
+ buf.push text(cont.strip)
+ end
+
+ line =~ /\A\s+(\*+)/
+ current_level = $1.size
+ if level == current_level
+ @strategy.ul_item_end
+ # body
+ @strategy.ul_item_begin buf
+ elsif level < current_level # down
+ level_diff = current_level - level
+ level = current_level
+ (1..(level_diff - 1)).to_a.reverse_each do |i|
+ @strategy.ul_begin { i }
+ @strategy.ul_item_begin []
+ end
+ @strategy.ul_begin { level }
+ @strategy.ul_item_begin buf
+ elsif level > current_level # up
+ level_diff = level - current_level
+ level = current_level
+ (1..level_diff).to_a.reverse_each do |i|
+ @strategy.ul_item_end
+ @strategy.ul_end { level + i }
+ end
+ @strategy.ul_item_end
+ # body
+ @strategy.ul_item_begin buf
+ end
+ end
+
+ (1..level).to_a.reverse_each do |i|
+ @strategy.ul_item_end
+ @strategy.ul_end { i }
+ end
+ end
+
+ def compile_olist(f)
+ @strategy.ol_begin
+ f.while_match(/\A\s+\d+\.|\A\#@/) do |line|
+ next if line =~ /\A\#@/
+
+ num = line.match(/(\d+)\./)[1]
+ buf = [text(line.sub(/\d+\./, '').strip)]
+ f.while_match(/\A\s+(?!\d+\.)\S/) do |cont|
+ buf.push text(cont.strip)
+ end
+ @strategy.ol_item buf, num
+ end
+ @strategy.ol_end
+ end
+
+ def compile_dlist(f)
+ @strategy.dl_begin
+ while /\A\s*:/ =~ f.peek
+ @strategy.dt text(f.gets.sub(/\A\s*:/, '').strip)
+ desc = f.break(/\A(\S|\s*:|\s+\d+\.\s|\s+\*\s)/).map { |line| text(line.strip) }
+ @strategy.dd(desc)
+ f.skip_blank_lines
+ f.skip_comment_lines
+ end
+ @strategy.dl_end
+ end
+
+ def compile_paragraph(f)
+ buf = []
+ f.until_match(%r{\A//|\A\#@}) do |line|
+ break if line.strip.empty?
+ buf.push text(line.sub(/^(\t+)\s*/) { |m| '<!ESCAPETAB!>' * m.size }.strip.gsub('<!ESCAPETAB!>', "\t"))
+ end
+ @strategy.paragraph buf
+ end
+
+ def read_command(f)
+ line = f.gets
+ name = line.slice(/[a-z]+/).to_sym
+ ignore_inline = (name == :embed)
+ args = parse_args(line.sub(%r{\A//[a-z]+}, '').rstrip.chomp('{'), name)
+ @strategy.doc_status[name] = true
+ lines = block_open?(line) ? read_block(f, ignore_inline) : nil
+ @strategy.doc_status[name] = nil
+ [name, args, lines]
+ end
+
+ def block_open?(line)
+ line.rstrip[-1, 1] == '{'
+ end
+
+ def read_block(f, ignore_inline)
+ head = f.lineno
+ buf = []
+ f.until_match(%r{\A//\}}) do |line|
+ if ignore_inline
+ buf.push line
+ elsif line !~ /\A\#@/
+ buf.push text(line.rstrip)
+ end
+ end
+ unless %r{\A//\}} =~ f.peek
+ error "unexpected EOF (block begins at: #{head})"
+ return buf
+ end
+ f.gets # discard terminator
+ buf
+ end
+
+ def parse_args(str, _name = nil)
+ return [] if str.empty?
+ scanner = StringScanner.new(str)
+ words = []
+ while word = scanner.scan(/(\[\]|\[.*?[^\\]\])/)
+ w2 = word[1..-2].gsub(/\\(.)/) do
+ ch = $1
+ (ch == ']' or ch == '\\') ? ch : '\\' + ch
+ end
+ words << w2
+ end
+ unless scanner.eos?
+ error "argument syntax error: #{scanner.rest} in #{str.inspect}"
+ return []
+ end
+ words
+ end
+
+ def compile_command(syntax, args, lines)
+ unless @strategy.respond_to?(syntax.name)
+ error "strategy does not support command: //#{syntax.name}"
+ compile_unknown_command args, lines
+ return
+ end
+ begin
+ syntax.check_args args
+ rescue CompileError => err
+ error err.message
+ args = ['(NoArgument)'] * syntax.min_argc
+ end
+ if syntax.block_allowed?
+ compile_block syntax, args, lines
+ else
+ if lines
+ error "block is not allowed for command //#{syntax.name}; ignore"
+ end
+ compile_single syntax, args
+ end
+ end
+
+ def compile_unknown_command(args, lines)
+ @strategy.unknown_command args, lines
+ end
+
+ def compile_block(syntax, args, lines)
+ @strategy.__send__(syntax.name, (lines || default_block(syntax)), *args)
+ end
+
+ def default_block(syntax)
+ if syntax.block_required?
+ error "block is required for //#{syntax.name}; use empty block"
+ end
+ []
+ end
+
+ def compile_single(syntax, args)
+ @strategy.__send__(syntax.name, *args)
+ end
+
+ def replace_fence(str)
+ str.gsub(/@<(\w+)>([$|])(.+?)(\2)/) do
+ op = $1
+ arg = $3.gsub('@', "\x01").gsub('\\}') { '\\\\}' }.gsub('}') { '\}' }.sub(/(?:\\)+$/) { |m| '\\\\' * m.size }
+ "@<#{op}>{#{arg}}"
+ end
+ end
+
+ def text(str)
+ return '' if str.empty?
+ words = replace_fence(str).split(/(@<\w+>\{(?:[^\}\\]|\\.)*?\})/, -1)
+ words.each do |w|
+ if w.scan(/@<\w+>/).size > 1 && !/\A@<raw>/.match(w)
+ error "`@<xxx>' seen but is not valid inline op: #{w}"
+ end
+ end
+ result = @strategy.nofunc_text(words.shift)
+ until words.empty?
+ result << compile_inline(words.shift.gsub(/\\\}/, '}').gsub(/\\\\/, '\\'))
+ result << @strategy.nofunc_text(words.shift)
+ end
+ result.gsub("\x01", '@')
+ rescue => err
+ error err.message
+ end
+ public :text # called from strategy
+
+ def compile_inline(str)
+ op, arg = /\A@<(\w+)>\{(.*?)\}\z/.match(str).captures
+ unless inline_defined?(op)
+ raise CompileError, "no such inline op: #{op}"
+ end
+ unless @strategy.respond_to?("inline_#{op}")
+ raise "strategy does not support inline op: @<#{op}>"
+ end
+ @strategy.__send__("inline_#{op}", arg)
+ rescue => err
+ error err.message
+ @strategy.nofunc_text(str)
+ end
+
+ def warn(msg)
+ @strategy.warn msg
+ end
+
+ def error(msg)
+ @strategy.error msg
+ end
+
+=end
+
+ #------------------------------
+
## ブロック命令
defblock :program, 0..3 ## プログラム
defblock :terminal, 0..3 ## ターミナル
+ defblock :output, 0..3 ## 出力結果
defblock :sideimage, 2..3 ## テキストの横に画像を表示
defblock :abstract, 0 ## 章の概要
+ defblock :chapterauthor, 1 ## 章の著者
+ defblock :talklist, 0..1 ## 会話リスト
+ defblock :talk, 1..3, true ## 会話項目
+ defblock :t, 1..3, true ## 会話項目(ショートカット用)
+ defblock :desclist, 0..1 ## キーと説明文のリスト
+ defblock :desc, 1..2, true ## キーと説明文のリスト
defblock :list, 0..3 ## (上書き)
defblock :listnum, 0..3 ## (上書き)
defblock :note, 0..2 ## (上書き)
defblock :texequation, 0..2 ## (上書き)
+ defblock :table, 0..3 ## (上書き)
+ defblock :imgtable, 0..3 ## (上書き)
defsingle :makechaptitlepage, 0..1 ## 章扉をつける
defsingle :needvspace, 2 ## 縦方向のスペースがなければ改ページ
defsingle :paragraphend, 0 ## 段の終わりにスペースを入れる
defsingle :subparagraphend, 0## 小段の終わりにスペースを入れる(あれば)
+ defsingle :vspace, 2 ## 縦方向の空きを入れる(\vspace)
+ defsingle :addvspace, 2 ## 縦方向の空きを入れる(\addvspace)
+ defsingle :tsize, 1..2 ## (上書き)
## インライン命令
+ definline :par ## 箇条書き内で改段落するのに使う
definline :balloon ## コード内でのふきだし説明(Re:VIEW3から追加)
definline :eq ## 数式を参照
definline :secref ## 節(Section)や項(Subsection)を参照
definline :noteref ## ノートを参照
definline :hlink ## @<href>{}の代わり
+ definline :term ## @<idx>{}かつゴシック体
+ definline :termnoidx ## ゴシック体にするだけで索引には登録しない
definline :file ## ファイル名
definline :userinput ## ユーザ入力
definline :nop ## 引数をそのまま表示 (No Operation)
definline :letitgo ## (nopのエイリアス名)
definline :foldhere ## 折り返し箇所を手動で指定
definline :cursor ## ターミナルでのカーソル
+ definline :qq ## 「``」と「''」で囲う
definline :weak ## 目立たせない(@<strong>{} の反対)
definline :small ## 文字サイズを小さく
definline :xsmall ## 文字サイズをもっと小さく
definline :xxsmall ## 文字サイズをもっともっと小さく
definline :large ## 文字サイズを大きく
definline :xlarge ## 文字サイズをもっと大きく
definline :xxlarge ## 文字サイズをもっともっと大きく
definline :xstrong ## 文字を大きくした@<strong>{}
definline :xxstrong ## 文字をもっと大きくした@<strong>{}
+ definline :w ## キーに対応した単語に展開
+ definline :wb ## キーに対応した単語に展開、かつ太字で表示
+ definline :W ## キーに対応した単語に展開、かつ強調表示
private
## パーサを再帰呼び出しに対応させる
def do_compile
f = LineInput.new(StringIO.new(@chapter.content))
@strategy.bind self, @chapter, Location.new(@chapter.basename, f)
- tagged_section_init
+ tagged_section_init()
parse_document(f, false)
- close_all_tagged_section
+ close_all_tagged_section()
end
+ BLOCK_END_REXP = /\A\/\/\}\s*$/
+
def parse_document(f, block_cmd)
while f.next?
case f.peek
when /\A\#@/
f.gets # Nothing to do
@@ -81,11 +654,12 @@
compile_list(f) #+
when /\A\s+\d+\./
compile_olist f
when /\A\s*:\s/
compile_dlist f
- when %r{\A//\}}
+ #when %r{\A//\}} #-
+ when BLOCK_END_REXP #+
return if block_cmd #+
f.gets
#error 'block end seen but not opened' #-
error "'//}': block-end found, but no block command opened." #+
#when %r{\A//[a-z]+} #-
@@ -124,39 +698,63 @@
## ・またタブ文字の展開は、本来はBuilderではなくCompilerで行うべきだが、
## Re:VIEWの設計がまずいのでそうなっていない。
## ・'//table' と '//embed' ではタブ文字の展開は行わない。
def read_block_for(cmdname, f) # 追加
disable_comment = cmdname == :embed # '//embed' では行コメントを読み飛ばさない
- ignore_inline = cmdname == :embed # '//embed' ではインライン命令を解釈しない
+ ignore_inline = _ignore_inline?(cmdname) # '//embed' と '//table' ではインライン命令を解釈しない
enable_detab = cmdname !~ /\A(?:em)?table\z/ # '//table' ではタブ展開しない
f.enable_comment(false) if disable_comment
- lines = read_block(f, ignore_inline, enable_detab)
+ lines = read_block(f, ignore_inline, enable_detab) { "//#{cmdname}" }
f.enable_comment(true) if disable_comment
return lines
end
+ def _ignore_inline?(cmdname)
+ return RAW_BLOCK_COMMANDS[cmdname]
+ end
def read_block(f, ignore_inline, enable_detab=true) # 上書き
head = f.lineno
buf = []
builder = @strategy #+
- f.until_match(%r{\A//\}}) do |line|
+ #f.until_match(%r{\A//\}}) do |line| #-
+ f.until_match(BLOCK_END_REXP) do |line| #+
if ignore_inline
buf.push line
elsif line !~ /\A\#@/
#buf.push text(line.rstrip) #-
line = line.rstrip #+
line = builder.detab(line) if enable_detab #+
- buf << text(line) #+
+ buf << parse_text(line) #+
end
end
- unless %r{\A//\}} =~ f.peek
- error "unexpected EOF (block begins at: #{head})"
+ #unless %r{\A//\}} =~ f.peek #-
+ unless f.peek() =~ BLOCK_END_REXP #+
+ if block_given? #+
+ error "#{yield} (at line #{head}): block command not closed." #+
+ else #+
+ error "unexpected EOF (block begins at: #{head})"
+ end #+
return buf
end
f.gets # discard terminator
buf
end
+ RAW_BLOCK_COMMANDS = {
+ embed: true,
+ raw: true,
+ table: true,
+ list: true,
+ emlist: true,
+ listnum: true,
+ emlistnum: true,
+ source: true,
+ program: true,
+ terminal: true,
+ cmd: true,
+ output: true,
+ }
+
## ブロック命令を入れ子可能に変更('//note' と '//quote')
def parse_block_command(f)
line = f.gets()
lineno = f.lineno
@@ -186,11 +784,11 @@
if curly
builder.__send__(handler, *args) do
parse_document(f, cmdname)
end
s = f.peek()
- f.peek() =~ /\A\/\/}/ or
+ f.peek() =~ BLOCK_END_REXP or
error "'//#{cmdname}': not closed (reached to EOF)"
f.gets() ## '//}' を読み捨てる
else
builder.__send__(handler, *args)
end
@@ -273,10 +871,61 @@
char == '*' ? st.ul_item_end() : st.ol_item_end()
#
return line
end
+ def compile_dlist(f)
+ @strategy.dl_begin()
+ while /\A\s*:/ =~ f.peek()
+ dtext = f.gets.sub(/\A\s*:/, '').strip
+ @strategy.dt(parse_text(dtext))
+ buf = []
+ li_rexp = LIST_ITEM_REXP
+ indent = nil
+ first_p = true
+ @strategy.dl_dd_begin()
+ while (line = f.peek()) =~ /\A( [ \t]+|\t\s*)/ || line =~ /\A\s*$/
+ indent ||= $1
+ if indent
+ line =~ /\A\s*$/ || line.start_with?(indent) or
+ warn "` : #{dtext}': indent mismatched (maybe space and tab are mixed)"
+ if _dl_start_list?(line, indent, li_rexp)
+ buf, first_p = _dl_par(buf, first_p) unless buf.empty?
+ compile_list(f)
+ @strategy.noindent # disable indent just after ordered/unordered list
+ next
+ end
+ end
+ if line =~ /\A\s*$/
+ buf, first_p = _dl_par(buf, first_p) unless buf.empty?
+ else
+ buf << parse_text(line)
+ end
+ f.gets()
+ end
+ _, _ = _dl_par(buf, first_p) unless buf.empty?
+ @strategy.dl_dd_end()
+ f.skip_blank_lines()
+ f.skip_comment_lines()
+ end
+ @strategy.dl_end()
+ end
+
+ def _dl_start_list?(line, indent, li_rexp)
+ return line.start_with?(indent) && line.sub(indent, '') =~ li_rexp && line =~ li_rexp
+ end
+
+ def _dl_par(buf, first_p)
+ return buf, first_p if buf.empty?
+ if first_p
+ @strategy.puts buf.join
+ else
+ @strategy.paragraph(buf)
+ end
+ return [], false
+ end
+
public
## 入れ子のインライン命令をパースできるよう上書き
def parse_text(line)
stack = []
@@ -364,14 +1013,13 @@
end
}
elsif strategy.respond_to?("inline_#{op}")
children.empty? || children.all? {|x| x.is_a?(String) } or
error "'@<#{op}>' does not support nested inline commands."
-
buf << strategy.__send__("inline_#{op}", children[0])
else
- error "strategy does not support inline op: @<#{op}>"
+ error "strategy does not support inline op: @<#{op}> (strategy.class=#{strategy.class})"
end
else
raise "internal error: x=#{x.inspect}"
end
end
@@ -380,11 +1028,11 @@
def ignore_nested_inline_command?(tag_name)
return IGNORE_NESTED_INLINE_COMMANDS.include?(tag_name)
end
- IGNORE_NESTED_INLINE_COMMANDS = Set.new(['m', 'raw', 'embed'])
+ IGNORE_NESTED_INLINE_COMMANDS = Set.new(['m', 'raw', 'embed', 'idx', 'hidx', 'term'])
end
## コメント「#@#」を読み飛ばす(ただし //embed では読み飛ばさない)
@@ -422,26 +1070,26 @@
end
class Book::ListIndex
- ## '//program' と '//terminal' をサポートするよう拡張
+ ## '//program' と '//terminal' と '//output' をサポートするよう拡張
def self.item_type # override
#'(list|listnum)' # original
- '(list|listnum|program|terminal)'
+ '(list|listnum|program|terminal|output)'
end
## '//list' や '//terminal' のラベル(第1引数)を省略できるよう拡張
def self.parse(src, *args) # override
items = []
seq = 1
- src.grep(%r{\A//#{item_type}}) do |line|
+ src.grep(%r{\A//#{item_type()}}) do |line|
if id = line.slice(/\[(.*?)\]/, 1)
next if id.empty? # 追加
- items.push item_class.new(id, seq)
+ items.push item_class().new(id, seq)
seq += 1
- ReVIEW.logger.warn "warning: no ID of #{item_type} in #{line}" if id.empty?
+ ReVIEW.logger.warn "warning: no ID of #{item_type()} in #{line}" if id.empty?
end
end
new(items, *args)
end
@@ -489,21 +1137,23 @@
def equation(id)
equation_index()[id]
end
def equation_index
- @equation_index ||= Book::EquationIndex.parse(lines)
+ @equation_index ||= Book::EquationIndex.parse(lines())
@equation_index
end
def content # override
## //list[?] や //terminal[?] の '?' をランダム文字列に置き換える。
## こうすると、重複しないラベルをいちいち指定しなくても、ソースコードや
## ターミナルにリスト番号がつく。ただし @<list>{} での参照はできない。
unless @_done
- pat = Book::ListIndex.item_type # == '(list|listnum|program|terminal)'
- @content = @content.gsub(/^\/\/#{pat}\[\?\]/) { "//#{$1}[#{_random_label()}]" }
+ pat1 = Book::ListIndex.item_type # == '(list|listnum|program|terminal|output)'
+ pat2 = Book::TableIndex.item_type # == '(table|imgtable)'
+ pat = "#{pat1[1..-2]}|#{pat2[1..-2]}"
+ @content = @content.gsub(/^\/\/(#{pat})\[\?\]/) { "//#{$1}[#{_random_label()}]" }
## 改行コードを「\n」に統一する
@content = @content.gsub(/\r\n/, "\n")
## (experimental) 範囲コメント('#@+++' '#@---')を行コメント('#@#')に変換
@content = @content.gsub(/^\#\@\+\+\+$.*?^\#\@\-\-\-$/m) { $&.gsub(/^/, '#@#') }
@_done = true
@@ -512,11 +1162,14 @@
end
module_function
def _random_label
- "_" + rand().to_s[2..10]
+ #"_" + rand().to_s[2..10]
+ "_" + RANDOM.rand().to_s[2..10]
end
+
+ RANDOM = Random.new(22360679)
end
end