require 'cgi' require 'uri' require 'stringio' %%{ machine dtext; name_boundary = ':' | ';' | ',' | '.' | '!' | '?' | ')' | ']' | '<' | '>'; newline = '\r\n' | '\r' | '\n'; mention = '@' . graph+; action mark_a1 { a1 = p } action mark_a2 { a2 = p } action mark_b1 { b1 = p } action mark_b2 { b2 = p } nonquote = ^'"'; nonbracket = ^']'; nonpipe = ^'|'; nonpipebracket = nonpipe & nonbracket; noncurly = ^'}'; url = 'http' 's'? '://' graph+; basic_textile_link = '"' nonquote+ >mark_a1 '"' >mark_a2 ':' url >mark_b1 @mark_b2; bracketed_textile_link = '"' nonquote+ >mark_a1 '"' >mark_a2 ':[' url >mark_b1 @mark_b2 :>> ']'; basic_wiki_link = '[[' nonpipebracket+ >mark_a1 @mark_a2 ']]'; aliased_wiki_link = '[[' nonpipebracket+ >mark_a1 @mark_a2 '|' nonbracket+ >mark_b1 @mark_b2 ']]'; post_link = '{{' noncurly+ >mark_a1 @mark_a2 '}}'; post_id = 'post #' digit+ >mark_a1 @mark_a2; forum_post_id = 'forum #' digit+ >mark_a1 @mark_a2; forum_topic_id = 'topic #' digit+ >mark_a1 @mark_a2; forum_topic_paged_id = 'topic #' digit+ >mark_a1 @mark_a2 '/p' digit+ >mark_b1 @mark_b2; comment_id = 'comment #' digit+ >mark_a1 @mark_a2; pool_id = 'pool #' digit+ >mark_a1 @mark_a2; user_id = 'user #' digit+ >mark_a1 @mark_a2; artist_id = 'artist #' digit+ >mark_a1 @mark_a2; github_issue_id = 'issue #' digit+ >mark_a1 @mark_a2; pixiv_id = 'pixiv #' digit+ >mark_a1 @mark_a2; pixiv_paged_id = 'pixiv #' digit+ >mark_a1 @mark_a2 '/p' digit+ >mark_b1 @mark_b2; inline := |* post_id => { id = data[a1..a2] output << 'post #' + id + '' }; forum_post_id => { id = data[a1..a2] output << 'forum #' + id + '' }; forum_topic_id => { id = data[a1..a2] output << 'topic #' + id + '' }; forum_topic_paged_id => { id = data[a1..a2] page = data[b1..b2] output << 'topic #' + id + '/p' + page + '' }; comment_id => { id = data[a1..a2] output << 'comment #' + id + '' }; pool_id => { id = data[a1..a2] output << 'pool #' + id + '' }; user_id => { id = data[a1..a2] output << 'user #' + id + '' }; artist_id => { id = data[a1..a2] output << 'artist #' + id + '' }; github_issue_id => { id = data[a1..a2] output << 'issue #' + id + '' }; pixiv_id => { id = data[a1..a2] output << 'pixiv #' + id + '' }; pixiv_paged_id => { id = data[a1..a2] page = data[b1..b2] output << 'pixiv #' + id + '/p' + page + '' }; post_link => { tags = data[a1..a2] output << '' + h(tags) + '' }; basic_wiki_link => { name = data[a1..a2] title = name.tr(" ", "_").downcase output << '' + h(name) + '' }; aliased_wiki_link => { name = data[b1..b2] title = data[a1..a2].tr(" ", "_").downcase output << '' + h(name) + '' }; basic_textile_link => { text = data[a1..a2] url = data[b1..b2] output << '' + text + '' }; bracketed_textile_link => { text = data[a1..a2] url = data[b1..b2] output << '' + text + '' }; url => { url = data[ts...te] output << '' + url + '' }; mention name_boundary => { name = data[ts+1...te-1] output << '@' + h(name) + '' + data[p] }; mention => { name = data[ts+1...te] output << '@' + h(name) + '' }; '[b]' => { dstack << :b output << '' }; '[/b]' => { raise SyntaxError.new("invalid [/b] tag") unless dstack[-1] == :b dstack.pop output << "" }; '[i]' => { dstack << :i output << '' }; '[/i]' => { raise ParseError.new("invalid [/i] tag") unless dstack[-1] == :i dstack.pop output << "" }; '[s]' => { dstack << :s output << "" }; '[/s]' => { raise ParseError.new("invalid [/s] tag") unless dstack[-1] == :s dstack.pop output << "" }; '[u]' => { dstack << :u output << "" }; '[/u]' => { raise ParseError.new("invalid [/u] tag") unless dstack[-1] == :u dstack.pop output << "" }; '[tn]' => { dstack << :tn output << '

' }; '[/tn]' => { raise ParseError.new("invalid [/tn] tag") unless dstack[-1] == :tn dstack.pop output << "

" }; '[/quote]' => { raise ParseError.new("invalid [/quote] tag") unless dstack[-1] == :quote dstack.pop output << "" fret; }; '[spoiler]' => { dstack << :inline_spoiler output << '' }; '[/spoiler]' => { if dstack[-1] == :inline_spoiler output << "" dstack.pop elsif dstack[-1] == :block_spoiler output << "" dstack.pop fret; else raise SyntaxError.new("invalid [/spoiler] tag") end }; '[/expand]' => { if dstack[-1] == :expand output << '' dstack.pop fret; else raise SyntaxError.new("invalid [/expand] tag") end }; '&' => { output << "&" }; '<' => { output << "<" }; '>' => { output << ">" }; newline{2,} { output << close_stack(output, dstack) fret; }; newline => { output << "
" }; '\0' => { fhold; fret; }; any => { output << data[p] }; *|; code := |* '[/code]' => { if dstack[-1] == :block_code dstack.pop output << "" else raise SyntaxError.new("invalid [/code] tag") end fret; }; '\0' => { fhold; fret; }; '[' => { output << "[" }; (^'[')+ => { output << data[ts...te] }; *|; ws = ' ' | '\t'; header = 'h' [123456] >mark_a1 @mark_a2 '.' ws* print+ >mark_b1 @mark_b2; aliased_expand = '[expand=' (nonbracket+ >mark_a1 @mark_a2) ']'; main := |* header => { if flags[:inline] header = "6" else header = data[a1..a2] end text = data[b1..b2] output << '' + h(text) + '' }; '[quote]' => { output << "
" dstack << :quote fcall inline; }; '[spoiler]' => { output << '
' dstack << :block_spoiler fcall inline; }; '[code]' => { output << '
'
      dstack << :block_code
      fcall code;
    };

    '[expand]' => {
      output << '
" else raise SyntaxError.new("Invalid element #{obj}") end end end def self.parse(s) stack = [] dstack = [] output = StringIO.new data = s + "\0" eof = data.size flags = {} %% write init; %% write exec; output.string end end