class ReVIEW::Compiler
Constants
- INLINE
- SYNTAX
Attributes
strategy[R]
Public Class Methods
defblock(name, argc, optional = false, &block)
click to toggle source
# File lib/review/compiler.rb, line 86 def self.defblock(name, argc, optional = false, &block) defsyntax name, (optional ? :optional : :block), argc, &block end
definline(name)
click to toggle source
# File lib/review/compiler.rb, line 98 def self.definline(name) INLINE[name] = InlineSyntaxElement.new(name) end
defsingle(name, argc, &block)
click to toggle source
# File lib/review/compiler.rb, line 90 def self.defsingle(name, argc, &block) defsyntax name, :line, argc, &block end
defsyntax(name, type, argc, &block)
click to toggle source
# File lib/review/compiler.rb, line 94 def self.defsyntax(name, type, argc, &block) SYNTAX[name] = SyntaxElement.new(name, type, argc, &block) end
new(strategy)
click to toggle source
# File lib/review/compiler.rb, line 39 def initialize(strategy) @strategy = strategy end
Public Instance Methods
compile(chap)
click to toggle source
# File lib/review/compiler.rb, line 45 def compile(chap) @chapter = chap do_compile @strategy.result end
inline_defined?(name)
click to toggle source
# File lib/review/compiler.rb, line 120 def inline_defined?(name) INLINE.key?(name.to_sym) end
syntax_defined?(name)
click to toggle source
# File lib/review/compiler.rb, line 102 def syntax_defined?(name) SYNTAX.key?(name.to_sym) end
syntax_descriptor(name)
click to toggle source
# File lib/review/compiler.rb, line 106 def syntax_descriptor(name) SYNTAX[name.to_sym] end
text(str)
click to toggle source
# File lib/review/compiler.rb, line 516 def text(str) return '' if str.empty? words = replace_fence(str).split(/(@<\w+>\{(?:[^\}\]|\.)*?\})/, -1) words.each { |w| error "`@<xxx>' seen but is not valid inline op: #{w}" if w.scan(/@<\w+>/).size > 1 && !/\A@<raw>/.match(w) } 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
Private Instance Methods
block_open?(line)
click to toggle source
# File lib/review/compiler.rb, line 431 def block_open?(line) line.rstrip[-1, 1] == '{' end
close_all_tagged_section()
click to toggle source
# File lib/review/compiler.rb, line 342 def close_all_tagged_section close_tagged_section(* @tagged_section.pop) until @tagged_section.empty? end
close_current_tagged_section(level)
click to toggle source
# File lib/review/compiler.rb, line 308 def close_current_tagged_section(level) while @tagged_section.last and @tagged_section.last[1] >= level close_tagged_section(* @tagged_section.pop) end end
close_tagged_section(tag, level)
click to toggle source
# File lib/review/compiler.rb, line 333 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
compile_block(syntax, args, lines)
click to toggle source
# File lib/review/compiler.rb, line 495 def compile_block(syntax, args, lines) @strategy.__send__(syntax.name, (lines || default_block(syntax)), *args) end
compile_command(syntax, args, lines)
click to toggle source
# File lib/review/compiler.rb, line 471 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 error "block is not allowed for command //#{syntax.name}; ignore" if lines compile_single syntax, args end end
compile_dlist(f)
click to toggle source
# File lib/review/compiler.rb, line 401 def compile_dlist(f) @strategy.dl_begin while /\A\s*:/ =~ f.peek @strategy.dt text(f.gets.sub(/\A\s*:/, '').strip) @strategy.dd(f.break(/\A(\S|\s*:|\s+\d+\.\s|\s+\*\s)/).map { |line| text(line.strip) }) f.skip_blank_lines f.skip_comment_lines end @strategy.dl_end end
compile_headline(line)
click to toggle source
# File lib/review/compiler.rb, line 279 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/} warn 'headline is empty.' if caption.empty? close_current_tagged_section(level) open_tagged_section(tag, level, label, caption) else open_tag = tag[1..-1] prev_tag_info = @tagged_section.pop error "#{open_tag} is not opened." if prev_tag_info.nil? || prev_tag_info.first != open_tag close_tagged_section(*prev_tag_info) end else warn 'headline is empty.' if caption.empty? @headline_indexs = @headline_indexs[0..index] if @headline_indexs.size > (index + 1) @headline_indexs[index] = 0 if @headline_indexs[index].nil? @headline_indexs[index] += 1 close_current_tagged_section(level) @strategy.headline level, label, caption end end
compile_inline(str)
click to toggle source
# File lib/review/compiler.rb, line 531 def compile_inline(str) op, arg = /\A@<(\w+)>\{(.*?)\}\z/.match(str).captures raise CompileError, "no such inline op: #{op}" unless inline_defined?(op) raise "strategy does not support inline op: @<#{op}>" unless @strategy.respond_to?("inline_#{op}") @strategy.__send__("inline_#{op}", arg) rescue => err error err.message @strategy.nofunc_text(str) end
compile_olist(f)
click to toggle source
# File lib/review/compiler.rb, line 388 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/) { |cont| buf.push text(cont.strip) } @strategy.ol_item buf, num end @strategy.ol_end end
compile_paragraph(f)
click to toggle source
# File lib/review/compiler.rb, line 412 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
compile_single(syntax, args)
click to toggle source
# File lib/review/compiler.rb, line 504 def compile_single(syntax, args) @strategy.__send__(syntax.name, *args) end
compile_ulist(f)
click to toggle source
# File lib/review/compiler.rb, line 346 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/) { |cont| buf.push text(cont.strip) } 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
compile_unknown_command(args, lines)
click to toggle source
# File lib/review/compiler.rb, line 491 def compile_unknown_command(args, lines) @strategy.unknown_command args, lines end
default_block(syntax)
click to toggle source
# File lib/review/compiler.rb, line 499 def default_block(syntax) error "block is required for //#{syntax.name}; use empty block" if syntax.block_required? [] end
do_compile()
click to toggle source
# File lib/review/compiler.rb, line 233 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
error(msg)
click to toggle source
# File lib/review/compiler.rb, line 545 def error(msg) @strategy.error msg end
headline(level, label, caption)
click to toggle source
# File lib/review/compiler.rb, line 314 def headline(level, label, caption) @strategy.headline level, label, caption end
open_tagged_section(tag, level, label, caption)
click to toggle source
# File lib/review/compiler.rb, line 322 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
parse_args(str, _name = nil)
click to toggle source
# File lib/review/compiler.rb, line 453 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
read_block(f, ignore_inline)
click to toggle source
# File lib/review/compiler.rb, line 435 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
read_command(f)
click to toggle source
# File lib/review/compiler.rb, line 421 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) lines = block_open?(line) ? read_block(f, ignore_inline) : nil [name, args, lines] end
replace_fence(str)
click to toggle source
# File lib/review/compiler.rb, line 508 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
tagged_section_init()
click to toggle source
# File lib/review/compiler.rb, line 318 def tagged_section_init @tagged_section = [] end
warn(msg)
click to toggle source
# File lib/review/compiler.rb, line 541 def warn(msg) @strategy.warn msg end