# Copyright (C) garin <garin54@gmail.com> 2011 # See the included file COPYING for details. class InlineParser
token EM_OPEN EM_CLOSE ITALIC_OPEN ITALIC_CLOSE STRIKE_OPEN STRIKE_CLOSE CODE_OPEN CODE_CLOSE KBD_OPEN KBD_CLOSE LABEL_OPEN LABEL_CLOSE LABEL_LINK_OPEN LABEL_LINK_CLOSE REFERENCE_OPEN REFERENCE_CLOSE VERB_OPEN VERB_CLOSE FOOTNOTE_OPEN FOOTNOTE_CLOSE RUBY_OPEN RUBY_CLOSE VARIABLE_OPEN VARIABLE_CLOSE MEDIA_OPEN MEDIA_CLOSE MANUEDO_OPEN MANUEDO_CLOSE OTHER options no_result_var rule content : | elements elements : elements element { val[0].push(val[1]) } | element { val } element : emphasis | italic | strike | code | kbd | label | label_link | reference | ruby | variable | footnote | media | verb | manuedo | normal_strings # --- inline emphasis : EM_OPEN content EM_CLOSE { Emphasis.new(val[1]) } italic : ITALIC_OPEN content ITALIC_CLOSE { Italic.new(val[1]) } strike : STRIKE_OPEN content STRIKE_CLOSE { Strike.new(val[1]) } code : CODE_OPEN content CODE_CLOSE { Code.new(val[1]) } kbd : KBD_OPEN content KBD_CLOSE { Kbd.new(val[1]) } footnote : FOOTNOTE_OPEN content FOOTNOTE_CLOSE { @index[:footnote] ||= [] @index[:footnote] << {:content => val[1] } Footnote.new([val[1], @index[:footnote].size]) } # --- inline end # --- media media_string : OTHER | EM_OPEN | EM_CLOSE | ITALIC_OPEN | ITALIC_CLOSE | STRIKE_OPEN | STRIKE_CLOSE | CODE_OPEN | CODE_CLOSE | KBD_OPEN | KBD_CLOSE | RUBY_OPEN | RUBY_CLOSE | VARIABLE_OPEN | VARIABLE_CLOSE | MANUEDO_OPEN | MANUEDO_CLOSE | REFERENCE_OPEN | REFERENCE_CLOSE | LABEL_OPEN | LABEL_CLOSE | LABEL_LINK_OPEN | LABEL_LINK_CLOSE | FOOTNOTE_OPEN | FOOTNOTE_CLOSE | VERB_OPEN | VERB_CLOSE | MEDIA_OPEN media_strings : media_strings media_string { val.join } | media_string { mok_link_path(val[0], @options[:media_directory]) } media : MEDIA_OPEN media_strings MEDIA_CLOSE { mime = MimeMagic.by_extension(val[1].split(".").last) unless mime.nil? mediatype, subtype = mime.mediatype, mime.subtype else mediatype, subtype = "","" end Media.new([val[1],mediatype,subtype]) } # --- media end # --- ruby ruby_string : OTHER | EM_OPEN | EM_CLOSE | ITALIC_OPEN | ITALIC_CLOSE | STRIKE_OPEN | STRIKE_CLOSE | CODE_OPEN | CODE_CLOSE | KBD_OPEN | KBD_CLOSE | MEDIA_OPEN | MEDIA_CLOSE | MANUEDO_OPEN | MANUEDO_CLOSE | REFERENCE_OPEN | REFERENCE_CLOSE | LABEL_OPEN | LABEL_CLOSE | LABEL_LINK_OPEN | LABEL_LINK_CLOSE | RUBY_OPEN | FOOTNOTE_OPEN | FOOTNOTE_CLOSE | VERB_OPEN | VERB_CLOSE ruby_strings : ruby_strings ruby_string { val.join } | ruby_string { val[0] } ruby : RUBY_OPEN ruby_strings RUBY_CLOSE { base, text = val[1].split("|",2) text ||= base
# parser = InlineParser.new # text = parser.parse(text).map do |n| n.apply end
Ruby.new([base, text]) } # --- ruby end # --- variable start variable_string : OTHER | EM_OPEN | EM_CLOSE | ITALIC_OPEN | ITALIC_CLOSE | STRIKE_OPEN | STRIKE_CLOSE | CODE_OPEN | CODE_CLOSE | KBD_OPEN | KBD_CLOSE | MEDIA_OPEN | MEDIA_CLOSE | MANUEDO_OPEN | MANUEDO_CLOSE | REFERENCE_OPEN | REFERENCE_CLOSE | LABEL_OPEN | LABEL_CLOSE | LABEL_LINK_OPEN | LABEL_LINK_CLOSE | RUBY_OPEN | RUBY_CLOSE | VARIABLE_OPEN | FOOTNOTE_OPEN | FOOTNOTE_CLOSE | VERB_OPEN | VERB_CLOSE variable_strings : variable_strings variable_string { val.join } | variable_string { val[0] } variable : VARIABLE_OPEN variable_strings VARIABLE_CLOSE { base, text = val[1].split("=",2) @variables ||= {} @variables[base] = text unless text.nil? value = @variables[base] unless value.nil? parser = InlineParser.new(@options) value = parser.parse(value).map do |n| n.apply end else # 変数が未定義 value = base end Variable.new(value) } # --- variable end # --- manuedo manuedo : MANUEDO_OPEN content MANUEDO_CLOSE { Manuedo.new(val[1]) } # --- manuedo end # --- label label_string : OTHER { val[0] } label_strings : label_strings label_string { val.join } | label_string { val[0] } label : LABEL_OPEN label_strings LABEL_CLOSE { label, title = val[1].split("|",2) title ||= label @index[:label] ||= [] @index[:label] << {:title => title } Label.new([label.to_code, title, @index[:label].size]) } # --- labe end # --- label link: start label_link_string : OTHER { val[0] } label_link_strings : label_link_strings label_link_string { val.join } | label_link_string { val[0] } label_link : LABEL_LINK_OPEN label_link_strings LABEL_LINK_CLOSE { label, title = val[1].split("|",2) title ||= label LabelLink.new([label.to_code, title]) } # --- label link: end # --- reference : start reference_string : OTHER { val[0] } reference_strings : reference_strings reference_string { val.join } | reference_string { val[0] } reference : REFERENCE_OPEN reference_strings REFERENCE_CLOSE { title, uri = val[1].split("|",2) uri ||= title if uri.strip[-2,2] == ".%" and ! @options[:reference_extension].nil? uri.slice!(-2,2) uri = "#{uri}#{@options[:reference_extension]}" end uri = mok_link_path(uri, @options[:reference_directory]) Reference.new([title, uri]) } # --- reference : end # --- verb verb_string : OTHER | EM_OPEN | EM_CLOSE | ITALIC_OPEN | ITALIC_CLOSE | STRIKE_OPEN | STRIKE_CLOSE | CODE_OPEN | CODE_CLOSE | KBD_OPEN | KBD_CLOSE | MEDIA_OPEN | MEDIA_CLOSE | MANUEDO_OPEN | MANUEDO_CLOSE | REFERENCE_OPEN | REFERENCE_CLOSE | LABEL_OPEN | LABEL_CLOSE | LABEL_LINK_OPEN | LABEL_LINK_CLOSE | RUBY_OPEN | RUBY_CLOSE | VARIABLE_OPEN | VARIABLE_CLOSE | FOOTNOTE_OPEN | FOOTNOTE_CLOSE | VERB_OPEN verb_strings : verb_string | verb_strings verb_string { val } verb : VERB_OPEN verb_strings VERB_CLOSE { Verb.new(val[1])} # --- verb end # --- normal normal_strings : normal_string { Plain.new(val[0]) } | normal_strings normal_string { Plain.new([val[0].contents, val[1]]) } normal_string : OTHER { val[0] } # --- normal end
—- inner include ParserUtility
EM_OPEN = '((*' EM_OPEN_RE = /A#{Regexp.quote(EM_OPEN)}/ EM_CLOSE = '*))' EM_CLOSE_RE = /A#{Regexp.quote(EM_CLOSE)}/
ITALIC_OPEN = '((_' ITALIC_OPEN_RE = /A#{Regexp.quote(ITALIC_OPEN)}/ ITALIC_CLOSE = '_))' ITALIC_CLOSE_RE = /A#{Regexp.quote(ITALIC_CLOSE)}/
STRIKE_OPEN = '((-' STRIKE_OPEN_RE = /A#{Regexp.quote(STRIKE_OPEN)}/ STRIKE_CLOSE = '-))' STRIKE_CLOSE_RE = /A#{Regexp.quote(STRIKE_CLOSE)}/
CODE_OPEN = '(({' CODE_OPEN_RE = /A#{Regexp.quote(CODE_OPEN)}/ CODE_CLOSE = '}))' CODE_CLOSE_RE = /A#{Regexp.quote(CODE_CLOSE)}/
KBD_OPEN = '((%' KBD_OPEN_RE = /A#{Regexp.quote(KBD_OPEN)}/ KBD_CLOSE = '%))' KBD_CLOSE_RE = /A#{Regexp.quote(KBD_CLOSE)}/
RUBY_OPEN = '((^' RUBY_OPEN_RE = /A#{Regexp.quote(RUBY_OPEN)}/ RUBY_CLOSE = '^))' RUBY_CLOSE_RE = /A#{Regexp.quote(RUBY_CLOSE)}/
VARIABLE_OPEN = '((@' VARIABLE_OPEN_RE = /A#{Regexp.quote(VARIABLE_OPEN)}/ VARIABLE_CLOSE = '@))' VARIABLE_CLOSE_RE = /A#{Regexp.quote(VARIABLE_CLOSE)}/
FOOTNOTE_OPEN = '(([' FOOTNOTE_OPEN_RE = /A#{Regexp.quote(FOOTNOTE_OPEN)}/ FOOTNOTE_CLOSE = ']))' FOOTNOTE_CLOSE_RE = /A#{Regexp.quote(FOOTNOTE_CLOSE)}/
MEDIA_OPEN = '(($' MEDIA_OPEN_RE = /A#{Regexp.quote(MEDIA_OPEN)}/ MEDIA_CLOSE = '$))' MEDIA_CLOSE_RE = /A#{Regexp.quote(MEDIA_CLOSE)}/
LABEL_OPEN = '((>' LABEL_OPEN_RE = /A#{Regexp.quote(LABEL_OPEN)}/ LABEL_CLOSE = '<))' LABEL_CLOSE_RE = /A#{Regexp.quote(LABEL_CLOSE)}/
LABEL_HTML_OPEN = '((>' LABEL_HTML_OPEN_RE = /A#{Regexp.quote(LABEL_HTML_OPEN)}/ LABEL_HTML_CLOSE = '<))' LABEL_HTML_CLOSE_RE = /A#{Regexp.quote(LABEL_HTML_CLOSE)}/
LABEL_LINK_OPEN = '((=' LABEL_LINK_OPEN_RE = /A#{Regexp.quote(LABEL_LINK_OPEN)}/ LABEL_LINK_CLOSE = '=))' LABEL_LINK_CLOSE_RE = /A#{Regexp.quote(LABEL_LINK_CLOSE)}/
REFERENCE_OPEN = '((<' REFERENCE_OPEN_RE = /A#{Regexp.quote(REFERENCE_OPEN)}/ REFERENCE_CLOSE = '>))' REFERENCE_CLOSE_RE = /A#{Regexp.quote(REFERENCE_CLOSE)}/
REFERENCE_HTML_OPEN = '((<' REFERENCE_HTML_OPEN_RE = /A#{Regexp.quote(REFERENCE_HTML_OPEN)}/ REFERENCE_HTML_CLOSE = '>))' REFERENCE_HTML_CLOSE_RE = /A#{Regexp.quote(REFERENCE_HTML_CLOSE)}/
VERB_OPEN = “(('” VERB_OPEN_RE = /A#{Regexp.quote(VERB_OPEN)}/ VERB_CLOSE = “'))” VERB_CLOSE_RE = /A#{Regexp.quote(VERB_CLOSE)}/
MANUEDO_OPEN = “((/” MANUEDO_OPEN_RE = /A#{Regexp.quote(MANUEDO_OPEN)}/ MANUEDO_CLOSE = “/))” MANUEDO_CLOSE_RE = /A#{Regexp.quote(MANUEDO_CLOSE)}/
# URL = “URL:” # URL_RE = /A#{Regexp.quote(URL)}/
other_re_mode = Regexp::EXTENDED other_re_mode = Regexp::MULTILINE OTHER_RE = Regexp.new(
"\\A.+?(?=#{Regexp.quote(EM_OPEN)}|#{Regexp.quote(EM_CLOSE)}|#{Regexp.quote(ITALIC_OPEN)}|#{Regexp.quote(ITALIC_CLOSE)}|#{Regexp.quote(STRIKE_OPEN)}|#{Regexp.quote(STRIKE_CLOSE)}|#{Regexp.quote(CODE_OPEN)}|#{Regexp.quote(CODE_CLOSE)}|#{Regexp.quote(KBD_OPEN)}|#{Regexp.quote(KBD_CLOSE)}|#{Regexp.quote(FOOTNOTE_OPEN)}|#{Regexp.quote(FOOTNOTE_CLOSE)}|#{Regexp.quote(RUBY_OPEN)}|#{Regexp.quote(RUBY_CLOSE)}|#{Regexp.quote(VARIABLE_OPEN)}|#{Regexp.quote(VARIABLE_CLOSE)}|#{Regexp.quote(MEDIA_OPEN)}|#{Regexp.quote(MEDIA_CLOSE)}|#{Regexp.quote(LABEL_OPEN)}|#{Regexp.quote(LABEL_CLOSE)}|#{Regexp.quote(LABEL_LINK_OPEN)}|#{Regexp.quote(LABEL_LINK_CLOSE)}|#{Regexp.quote(LABEL_HTML_OPEN)}|#{Regexp.quote(LABEL_HTML_CLOSE)}|#{Regexp.quote(REFERENCE_OPEN)}|#{Regexp.quote(REFERENCE_CLOSE)}|#{Regexp.quote(REFERENCE_HTML_OPEN)}|#{Regexp.quote(REFERENCE_HTML_CLOSE)}|#{Regexp.quote(MANUEDO_OPEN)}|#{Regexp.quote(MANUEDO_CLOSE)}|#{Regexp.quote(VERB_OPEN)}|#{Regexp.quote(VERB_CLOSE)})", other_re_mode)
def on_error(token_id, value, stack)
raise Racc::ParseError, "mokinlinepaser: line #{@lineno} in document: syntax error on '#{stack[0][0].contents}#{stack[1]}'"
end
def parse(src, lineno = “?”)
@src = StringScanner.new(Array(src).join) @lineno = lineno @pre = "" @@yydebug = false @view_token_type = false do_parse
end
def initialize(options = {})
@options = options @variables = options[:variables] @variables ||= {} @index = {}
end attr_reader :index
def next_token
return [false, false] if @src.eos? if ret = @src.scan(EM_OPEN_RE) puts "i: EM_OPEN: #{ret}" if @view_token_type @pre << ret [:EM_OPEN, ret] elsif ret = @src.scan(EM_CLOSE_RE) puts "i: EM_CLOSE: #{ret}" if @view_token_type @pre << ret [:EM_CLOSE, ret] elsif ret = @src.scan(ITALIC_OPEN_RE) puts "i: ITALIC_OPEN: #{ret}" if @view_token_type @pre << ret [:ITALIC_OPEN, ret] elsif ret = @src.scan(ITALIC_CLOSE_RE) puts "i: ITALIC_CLOSE: #{ret}" if @view_token_type @pre << ret [:ITALIC_CLOSE, ret] elsif ret = @src.scan(STRIKE_OPEN_RE) @pre << ret puts "i: STRIKE_OPEN: #{ret}" if @view_token_type [:STRIKE_OPEN, ret] elsif ret = @src.scan(STRIKE_CLOSE_RE) @pre << ret puts "i: STRIKE_CLOSE: #{ret}" if @view_token_type [:STRIKE_CLOSE, ret] elsif ret = @src.scan(CODE_OPEN_RE) @pre << ret puts "i: CODE_OPEN: #{ret}" if @view_token_type [:CODE_OPEN, ret] elsif ret = @src.scan(CODE_CLOSE_RE) @pre << ret puts "i: CODE_CLOSE: #{ret}" if @view_token_type [:CODE_CLOSE, ret] elsif ret = @src.scan(KBD_OPEN_RE) @pre << ret puts "i: KBD_OPEN: #{ret}" if @view_token_type [:KBD_OPEN, ret] elsif ret = @src.scan(KBD_CLOSE_RE) @pre << ret puts "i: KBD_CLOSE: #{ret}" if @view_token_type [:KBD_CLOSE, ret] elsif ret = @src.scan(LABEL_OPEN_RE) @pre << ret puts "i: LABEL_OPEN: #{ret}" if @view_token_type [:LABEL_OPEN, ret] elsif ret = @src.scan(LABEL_CLOSE_RE) puts "i: LABEL_CLOSE: #{ret}" if @view_token_type @pre << ret [:LABEL_CLOSE, ret] elsif ret = @src.scan(LABEL_LINK_OPEN_RE) @pre << ret puts "i: LABEL_LINK_OPEN: #{ret}" if @view_token_type [:LABEL_LINK_OPEN, ret] elsif ret = @src.scan(LABEL_LINK_CLOSE_RE) puts "i: LABEL_LINK_CLOSE: #{ret}" if @view_token_type @pre << ret [:LABEL_LINK_CLOSE, ret] elsif ret = @src.scan(LABEL_HTML_OPEN_RE) @pre << ret puts "i: LABEL_OPEN: #{ret}" if @view_token_type [:LABEL_OPEN, ret] elsif ret = @src.scan(LABEL_HTML_CLOSE_RE) puts "i: LABEL_CLOSE: #{ret}" if @view_token_type @pre << ret [:LABEL_CLOSE, ret] elsif ret = @src.scan(REFERENCE_OPEN_RE) puts "i: REFERENCE_OPEN: #{ret}" if @view_token_type @pre << ret [:REFERENCE_OPEN, ret] elsif ret = @src.scan(REFERENCE_CLOSE_RE) puts "i: REFERENCE_CLOSE: #{ret}" if @view_token_type @pre << ret [:REFERENCE_CLOSE, ret] elsif ret = @src.scan(REFERENCE_HTML_OPEN_RE) puts "i: REFERENCE_HTML_OPEN: #{ret}" if @view_token_type @pre << ret [:REFERENCE_OPEN, ret] elsif ret = @src.scan(REFERENCE_HTML_CLOSE_RE) puts "i: REFERENCE_HTML_CLOSE: #{ret}" if @view_token_type @pre << ret [:REFERENCE_CLOSE, ret] elsif ret = @src.scan(VERB_OPEN_RE) puts "i: VERB_OPEN: #{ret}" if @view_token_type @pre << ret [:VERB_OPEN, ret] elsif ret = @src.scan(VERB_CLOSE_RE) puts "i: VERB_CLOSE: #{ret}" if @view_token_type @pre << ret [:VERB_CLOSE, ret] elsif ret = @src.scan(RUBY_OPEN_RE) puts "i: RUBY_OPEN: #{ret}" if @view_token_type @pre << ret [:RUBY_OPEN, ret] elsif ret = @src.scan(RUBY_CLOSE_RE) puts "i: RUBY_CLOSE: #{ret}" if @view_token_type @pre << ret [:RUBY_CLOSE, ret] elsif ret = @src.scan(VARIABLE_OPEN_RE) puts "i: VARIABLE_OPEN: #{ret}" if @view_token_type @pre << ret [:VARIABLE_OPEN, ret] elsif ret = @src.scan(VARIABLE_CLOSE_RE) puts "i: VARIABLE_CLOSE: #{ret}" if @view_token_type @pre << ret [:VARIABLE_CLOSE, ret] elsif ret = @src.scan(FOOTNOTE_OPEN_RE) puts "i: FOOTNOTE_OPEN: #{ret}" if @view_token_type @pre << ret [:FOOTNOTE_OPEN, ret] elsif ret = @src.scan(FOOTNOTE_CLOSE_RE) puts "i: FOOTNOTE_CLOSE: #{ret}" if @view_token_type @pre << ret [:FOOTNOTE_CLOSE, ret] elsif ret = @src.scan(MEDIA_OPEN_RE) puts "i: MEDIA_OPEN: #{ret}" if @view_token_type @pre << ret [:MEDIA_OPEN, ret] elsif ret = @src.scan(MEDIA_CLOSE_RE) puts "i: MEDIA_CLOSE: #{ret}" if @view_token_type @pre << ret [:MEDIA_CLOSE, ret] elsif ret = @src.scan(MANUEDO_OPEN_RE) puts "i: MANUEDO_OPEN: #{ret}" if @view_token_type @pre << ret [:MANUEDO_OPEN, ret] elsif ret = @src.scan(MANUEDO_CLOSE_RE) puts "i: MANUED_CLOSE: #{ret}" if @view_token_type @pre << ret [:MANUEDO_CLOSE, ret] elsif ret = @src.scan(OTHER_RE) puts "i: OTHER_RE: #{ret}" if @view_token_type @pre << ret [:OTHER, ret] else puts "i: OTHER_RE(else): #{ret}" if @view_token_type ret = @src.rest @pre << ret @src.terminate [:OTHER, ret] end
end
# reference, media エレメントのリンク用のパスを生成する def mok_link_path(path, base_dir = nil)
path = path.strip return path if base_dir.nil? or base_dir.empty? # non base_dir return path if path =~ /^.*:\/\/.*/ # uri return path if path =~ /^\/.*/ or path =~ /^\.\/.*/ # root or currentdirectory File.join(base_dir,path)
end
—- header require “parserutility” require 'strscan' require 'mimemagic' require 'erb' require 'mokelement'
module Mok
—- footer
if __FILE__ == $0 mok = InlineParser.new src = $stdin.readline nodes = mok.parse(src) puts "----- output -----" nodes.each do |n| puts n.apply end end
end # end of module Mok