# 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 = '((&gt;' LABEL_HTML_OPEN_RE = /A#{Regexp.quote(LABEL_HTML_OPEN)}/ LABEL_HTML_CLOSE = '&lt;))' 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