%% name = Dang::Parser
%% {
def initialize(str, debug=false) setup_parser(str, debug) @doctype = nil @output = "" end DOC_TYPES = { "html" => "<!doctype html>", "html5" => "<!doctype html>", "html4" => '<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">', "html4 transitional" => '<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">', "html4 strict" => '<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">', "html4 frameset" => '<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">', "xhtml 1" => '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">', "xhtml 1 transitional" => '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">', "xhtml 1 strict" => '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">', "xhtml 1 frameset" => '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">', "xhtml 1.1" => '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">', "xhtml 1.1 basic" => '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML Basic 1.1//EN" "http://www.w3.org/TR/xhtml-basic/xhtml-basic11.dtd">', "xhtml 1.2 mobile" => '<!DOCTYPE html PUBLIC "-//WAPFORUM//DTD XHTML Mobile 1.2//EN" "http://www.openmobilealliance.org/tech/DTD/xhtml-mobile12.dtd">', "xhtml rdfa" => '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML+RDFa 1.0//EN" "http://www.w3.org/MarkUp/DTD/xhtml-rdfa-1.dtd">', "xhtml 5" => '<!DOCTYPE html>', "xml iso-8859-1" => "<?xml version='1.0' encoding='iso-8859-1' ?>" } def html_doctype return "" unless @doctype unless DOC_TYPES.key? @doctype warn "doctype '#{@doctype}' not understood, using 'html'" @doctype = "html" end DOC_TYPES[@doctype].dup end def compile strings = @output.flatten.map do |i| case i when Literal "_out << #{i.str.dump}" when Code if i.print "_out << (#{i.str}).to_s" else i.str end when Filter "_out << Dang.run_filter('#{i.name}', #{i.str.dump}).to_s" end end "_out = '';\n" + strings.join(";") + ";_out" end def output(env=nil) out = eval(compile, env || binding).strip doctype = html_doctype if doctype.empty? str = out else if out.empty? str = doctype else str = doctype << "\n" << out end end str end def attrs(at,sel=[]) out = [] classes = [] (at+sel).flatten.each do |pair| key = pair.key val = pair.value if key == "class" val = val.str if val.kind_of? Literal classes.unshift val elsif val == true out << "#{key}" out << " " else out << "#{key}='" out << val out << "'" out << " " end end unless classes.empty? expanded = ["class='"] classes.each do |c| expanded << c expanded << " " end expanded[-1] = "'" classes = expanded if out.empty? return classes end out = classes + [" "] + out end if out.last == " " out.pop end out end class Literal def initialize(str) @str = str end attr_reader :str end class Code def initialize(str, print) @str = str @print = print end attr_reader :str, :print end class Filter def initialize(name, str) @name = name @str = str end attr_reader :name, :str end def joinm(*elems) elems.flatten.map do |i| if i.kind_of? String Literal.new(i) else i end end end Pair = Struct.new(:key, :value) def join(f,b) f = Literal.new(f) if f.kind_of? String b = Literal.new(b) if b.kind_of? String if b.kind_of? Array [f] + b else [f,b] end end def code(str, print=true) Code.new(str, print) end
}
space = " " | "\t" bs = " " | "\t" | "\n" - = bs+ eol = "\n" eof = !. rest = < (!eol .)* > (eol | eof) { text }
doctype = “!!!” space* rest:r { @doctype = r.empty? ? “html” : r }
name = < /[a-zA-Z0-9_\-:]+/ > { text } start = "<" name:n { n } pts = space+ { "" } | < eol bs* > { text } end = name:n ">" { n } slash = - "/>" marker = start | "<!" | "<|" | "<:" | (- end) chunk = < (!marker .)* > { text }
rclose = “:>”
ruby = "<:" bs+ < (!rclose .)* > rclose { code(text, false) }
pclose = “|>”
puby = "<|" < (!pclose .)* > pclose { code(text) } part = ruby | puby| filter | comment | tag | chunk body = part:p body:b { join(p,b) } | part key = name | "'" < /[^'\n]*/ > "'" { text } valsrt = "]" | "<|" valchk = < (!valsrt .)* > { text } valprt = puby | valchk valraw = valprt:p valraw:b { join(p,b) } | valprt val = "'" < /[^'\n]*/ > "'" { text } | valraw dattr = "[" key:k "=" val:v "]" { Pair.new("data-#{k}", v) } dattrs = dattr:a dattrs:l { [a] + l } | dattr:a { [a] } attr = "[data" dattrs:t "]" { t } | "[" key:k "=" val:v "]" { Pair.new(k, v) } | "[" key:k "]" { Pair.new(k,true) } attrs = attr:a attrs:l { [a] + l } | attr:a { [a] } cc_if = /[iI][fF]/ cc_end = /[eE][nN][dD][iI][fF]/
comment = “<!” space+ < “[” space* cc_if (!“]” .)* “]” > space+ “!>”
{ "<!--#{text}>" } | "<!" space+ < "[" space* cc_end (!"]" .)* "]" > space+ "!>" { "<!#{text}-->" } | "<!" < (!"!>" .)* > "!>" { "<!--#{text}-->" } simple = /[a-zA-Z0-9_\-]+/ select = "#" < simple > { Pair.new("id", text) } | "." < simple > { Pair.new("class", text) }
selects = select:s selects:t { [s] + t }
| select:s { [s] }
end_filter(n) = bs* < /[a-zA-Z]+/ > &{ n == text } “:>”
filter = “<:” name:n bs* < (!end_filter(n) .)* > end_filter(n)
{ Filter.new(n, text) } tag = start:l slash { "<#{l} />" } | start:l space+ end:r { "<#{l}></#{r}>" } | start:l attrs:a slash { joinm "<#{l} ", attrs(a), " />" } | start:l selects:t slash { joinm "<#{l} ", attrs(t), " />" } | start:l selects:t attrs:a slash { joinm "<#{l} ", attrs(t,a), " />" } | start:l attrs:a space+ end:r { joinm "<#{l} ", attrs(a), "></#{r}>" } | start:l selects:t space+ end:r { joinm "<#{l} ", attrs(t), "></#{r}>" } | start:l selects:t attrs:a space+ end:r { joinm "<#{l} ", attrs(t,a), "></#{r}>" } | start:l selects:t attrs:a pts body:b pts:es end:r { joinm "<#{l} ", attrs(a,t), ">",b,es,"</#{r}>" } | start:l attrs:a pts body:b pts:es end:r { joinm "<#{l} ", attrs(a), ">", b, es, "</#{r}>" } | start:l selects:t pts:s body:b pts:es end:r { joinm "<#{l} ", attrs(t), ">",s, b, es, "</#{r}>" } | start:l pts:s body:b pts:es end:r { joinm "<#{l}>", s, b, es, "</#{r}>" } root = doctype? body:b eof { @output = join(b,"") }