Константин обнови решението на 23.11.2011 02:21 (преди около 13 години)
+class MarkupPrototype
+
+ def format str
+ result = ""
+ str.lines { |line| result += line_format(line) }
+ remove_newline result
+ end
+
+ def line_format str
+ ""
+ end
+
+ def remove_newline str
+ str.end_with?("\n") ? str.chop : str
+ end
+
+end
+
+class SpecialChars < MarkupPrototype
+
+ def initialize
+ @sym_table = {"&" => "&", "<" => "<", ">" => ">", "\"" => """}
+ end
+
+ private
+
+ def line_format line
+ result = line
+ @sym_table.each do |key, value|
+ result = replace(result, key, value)
+ end
+ result
+ end
+
+ def replace str, prev, repl
+ if str.start_with? ">" and prev == ">"
+ return ">" + str[1..-1].gsub(prev, repl)
+ end
+ str.gsub(prev, repl)
+ end
+
+end
+
+class FontMarkup < MarkupPrototype
+
+ private
+
+ def line_format str
+ unless /^\ {4}/.match str
+ if wrong_nesting_str(str).start_with?("**")
+ return strong(str)
+ elsif wrong_nesting_str(str).start_with?("_")
+ return em(str)
+ end
+ return em strong(str)
+ end
+ str
+ end
+
+ def strong str
+ result = str
+ until ((/\*\*.*\*\*/. match result) == nil) do
+ result = result.sub("**", "<strong>")
+ result = result.sub("**", "</strong>")
+ end
+ result
+ end
+
+ def em str
+ result = str
+ until ((/_.*_/. match result) == nil) do
+ result = result.sub("_", "<em>")
+ result = result.sub("_", "</em>")
+ end
+ result
+ end
+
+ def wrong_nesting_str str
+ (/(\*\*.*_.*\*\*.*_)|(_.*\*\*.*_.*\*\*)/.match str).to_s
+ end
+
+end
+
+class Link < MarkupPrototype
+
+ private
+
+ def line_format str
+ if (not /^\ {4}/.match str) and /\[.*\]\(.*\)/.match str
+ return str.gsub(/\[.*\]\(.*\)/, extract_link(str))
+ end
+ str
+ end
+
+ def correct? str
+ text_cond = (not extract_text(str).include? "]")
+ url_cond = (not extract_url(str).include? ")")
+ text_cond and url_cond
+ end
+
+ def extract_link str
+ link = (/\[.*\]\(.*\)/.match str).to_s
+ if correct? link
+ text, url = extract_text(link), extract_url(link)
+ return "<a href=\"" + url + "\">" + text + "</a>"
+ end
+ str
+ end
+
+ def extract_text str
+ result = (/\[.*\]/.match str).to_s[1..-2]
+ end
+
+ def extract_url str
+ result = (/\(.*\)/.match str).to_s[1..-2]
+ if /^".*"/.match result
+ return result.to_s[6..-7].strip
+ end
+ result
+ end
+
+end
+
+class Header < MarkupPrototype
+
+ private
+
+ def line_format str
+ if /^\s{0,3}\#{1,4}\ \s*\S+/.match str
+ h_num = header_type(str)
+ temp = str.sub(/^\s*\#{1,4}\s+/, "<h#{h_num}>")
+ return temp.strip.sub(/$/, "</h#{h_num}>") + "\n"
+ end
+ str
+ end
+
+ def header_type str
+ (/\#{1,4}\ +/.match str).to_s.count "#"
+ end
+
+end
+
+class Paragraph
+
+ def format str
+ str_arr = []
+ paragraphed_arr(str).each do |elem|
+ str_arr << elem
+ if str_arr[-1].start_with? "<p>"
+ str_arr[-1] = "<p>" + str_arr[-1][3..-1].strip + "</p>"
+ end
+ end
+ str_arr.join("\n")
+ end
+
+ private
+
+ def paragraphed_arr str
+ result = []
+ str.lines do |line|
+ if condition?(line)
+ result = construct_paragraph(result, line)
+ else
+ result << remove_newline(line)
+ end
+ end
+ result
+ end
+
+ def construct_paragraph array, line
+ result = array
+ if condition?(result[-1])
+ result[-1] += line
+ else
+ result << "<p>" + line
+ end
+ result
+ end
+
+ def remove_newline str
+ str.end_with?("\n") ? str.chop : str
+ end
+
+ def condition? str
+ cond = (/(^\s*\#{1,4}\ \s*\S+)|(^\ {4}.*)|(^> )|(^\d+\. )|(^\* )/.match str) == nil
+ (str != "") and (str != nil) and (str != "\n") and cond
+ end
+
+end
+
+class Blockquote
+
+ def format str
+ str_arr = []
+ quoted_arr(str).each do |elem|
+ str_arr << elem
+ if str_arr[-1].start_with? "<blockquote>"
+ str_arr[-1] = str_arr[-1].strip + "</blockquote>\n"
+ end
+ end
+ str_arr.join
+ end
+
+ private
+
+ def quoted_arr str
+ result = []
+ str.lines do |line|
+ if condition?(line)
+ result = construct_quote(result, line)
+ else
+ result << line
+ end
+ end
+ put_paragraphs(result)
+ end
+
+ def construct_quote array, line
+ result = array
+ if condition?(result[-1])
+ result[-1] += line[2..-1]
+ else
+ result << "<blockquote>" + line[2..-1]
+ end
+ result
+ end
+
+ def put_paragraphs array
+ result = []
+ array.each do |elem|
+ if elem.start_with? "<blockquote>"
+ result << "<blockquote>" + paragraphs(elem[12..-1])
+ else
+ result << elem
+ end
+ end
+ result
+ end
+
+ def paragraphs line
+ elems_arr = []
+ quote_elem(line). each do |elem|
+ temp = (/^ *\S/.match elem) ? ("<p>" + elem.strip + "</p>") : elem.strip
+ elems_arr << temp
+ end
+ elems_arr.join("\n")
+ end
+
+ def quote_elem line
+ result = []
+ line.split("\n").each do |elem|
+ if /^ *\S/.match elem
+ result = push_elem(result, elem)
+ else
+ result << elem
+ end
+ end
+ result
+ end
+
+ def push_elem array, elem
+ result = array
+ if /^ *\S/.match result[-1]
+ result[-1] += "\n" + elem
+ else
+ result << elem
+ end
+ result
+ end
+
+ def condition? str
+ /(^\> +)|(^<blockquote>)/.match str
+ end
+
+end
+
+class Codeblock
+
+ def format str
+ str_arr = []
+ coded_arr(str).each do |elem|
+ str_arr << elem
+ if str_arr[-1].start_with? "<pre><code>"
+ str_arr[-1] = remove_newline(str_arr[-1]) + "</code></pre>"
+ end
+ end
+ str_arr.join("\n")
+ end
+
+ private
+
+ def coded_arr str
+ result = []
+ str.lines do |line|
+ if condition?(line)
+ result = construct_codeblock(result, line)
+ else
+ result << remove_newline(line)
+ end
+ end
+ result
+ end
+
+ def construct_codeblock array, line
+ result = array
+ if condition?(result[-1])
+ result[-1] += line[4..-1]
+ else
+ result << "<pre><code>" + line[4..-1]
+ end
+ result
+ end
+
+ def remove_newline str
+ str.end_with?("\n") ? str.chop : str
+ end
+
+ def condition? str
+ /(^\ {4}.*)|(^<pre><code>)/.match str
+ end
+
+end
+
+class List
+
+ def format str
+ str_arr = []
+ listed_arr(str).each do |elem|
+ str_arr << elem
+ if str_arr[-1].start_with? "<ul>\n"
+ str_arr[-1] = str_arr[-1].strip + "\n</ul>"
+ elsif str_arr[-1].start_with? "<ol>\n"
+ str_arr[-1] = str_arr[-1].strip + "\n</ol>"
+ end
+ end
+ str_arr.join("\n")
+ end
+
+ private
+
+ def listed_arr str
+ result = []
+ str.lines do |line|
+ if /^\d+.\ /.match line
+ result = constr_ordered_list(result, line)
+ elsif /^\*\ /.match line
+ result = constr_unordered_list(result, line)
+ else
+ result << remove_newline(line)
+ end
+ end
+ result
+ end
+
+ def constr_ordered_list array, line
+ result = array
+ if result[-1] != nil and result[-1].start_with? "<ol>"
+ result[-1] += list_elem(line, "ol")
+ else
+ result << "<ol>\n" + list_elem(line, "ol")
+ end
+ result
+ end
+
+ def constr_unordered_list array, line
+ result = array
+ if result[-1] != nil and result[-1].start_with? "<ul>"
+ result[-1] += list_elem(line, "ul")
+ else
+ result << "<ul>\n" + list_elem(line, "ul")
+ end
+ result
+ end
+
+ def list_elem str, type
+ if type == "ol"
+ offset = (/(^\d+.\ )/.match str).to_s.length
+ return " <li>" + str[offset..-1].strip + "</li>\n"
+ else
+ " <li>" + str[2..-1].strip + "</li>\n"
+ end
+ end
+
+ def remove_newline str
+ str.end_with?("\n") ? str.chop : str
+ end
+
+ def condition? str
+ /(^\d+.\ )|(^\*\ )/.match str
+ end
+
+end
+
+class Formatter
+
+ def initialize output_str
+ @output_str = output_str
+ end
+
+ def to_html
+ result = SpecialChars.new.format @output_str
+ result = FontMarkup.new.format result
+ result = Link.new.format result
+ result = Paragraph.new.format result
+ result = Header.new.format result
+ result = Blockquote.new.format result
+ result = Codeblock.new.format result
+ List.new.format result
+ end
+
+ def to_s
+ to_html
+ end
+
+ def inspect
+ @output_str
+ end
+
+end