--[[ A parser that approximates 8chan's markup: Surround text with double single-quotes(') to make text italic Surround text with triple single-quotes to make text bold Surround text with underscores(_) to make it underlined Surround text with double asterisks(*) to make it spoilered Surround text with tildes(~) to make it strike through Begin a line with a greater-than followed by a a space to make it >greentext Begin a line with a less-than followed by a space to make it "] = ">", } local esctbl = {} for char,_ in pairs(escapes) do table.insert(esctbl,char) end local escapematch = string.format("([%s])",table.concat(esctbl)) local function sanitize_item(capture) return escapes[capture] or capture end local function sanitize(text) local ret,_ = string.gsub(text,escapematch,sanitize_item) return ret end --Grammar local space = S" \t\r"^0 local special = P{ P"**" + P"''" + P"'''" + P"__" + P"==" + P"~~" + P"\n>" + P"\n<" + P"\n" + P"[code]" + P"[spoiler]" } local word = Cs((1 - special)^1) * space / sanitize --Generates a pattern that formats text inside matching 'seq' tags with format --ex wrap("^^",[[%s]]) --will wrap text "5^^3^^" as "53" --The third argument is nessessary to stop exponential backtracking. This removes --a DOS vulnerability: If tags are nested really deep, the parser can lock up, --potentially locking up all processes. --[[ local function wrap(seq,format,V"sup") return P(seq) * Cs(((V"marked" + word + P"\n"))^1) * P(seq) / function(a) return string.format(format,a) end end ]] local function wrap(seq,format,s) return P(seq) * Cs(((s + word + P"\n"))^0) * P(seq) / function(a) return string.format(format,a) end end --Generates a pattern that formats text inside opening and closing "name" tags --with a format, BB forum style local function tag(name,format) local start_tag = P(string.format("[%s]",name)) local end_tag = P(string.format("[/%s]",name)) return start_tag * Cs(((1 - end_tag))^0) * end_tag / function(a) return string.format(format,sanitize(a)) end end --local grammar = P(require('pegdebug').trace({ local grammar = P{ "chunk"; --regular heading = wrap("==",[[

%s

]], V"underline" + V"strike" + V"italic"), bold = wrap("'''",[[%s]], V"italic" + V"underline" + V"strike"), italic = wrap("''",[[%s]], V"underline" + V"strike"), underline = wrap("__",[[%s]], V"strike"), strike = wrap("~~",[[%s]], P("blah")), spoiler = wrap("**",[[%s]],V"spoiler2" + V"bold" + V"italic" + V"underline" + V"strike"), spoiler2 = tag("spoiler",[[%s]],V"spoiler" + V"bold" + V"italic" + V"underline" + V"strike"), code = tag("code",[[
%s
]]), greentext = P">" * (B"\n>" + B">") * Cs((V"marked" + word)^0) / function(a) return string.format([[>%s]],a) end, pinktext = P"<" * (B"\n<" + B"<") * Cs((V"marked" + word)^0) / function(a) return string.format([[<%s]],a) end, marked = V"spoiler" + V"bold" + V"italic" + V"underline" + V"heading" + V"strike" + V"spoiler2" + V"code", plainline = (V"marked" + word)^0, line = Cs(V"greentext" + V"pinktext" + V"plainline" + P"") * P"\n" / function(a) if a == "\r" then return [[

]] else return string.format("

%s

",a) end end, ending = C(P(1)^0) / function(a) return sanitize(a) end, chunk = V"line"^0 * V"plainline" * V"ending" } return function(text) return table.concat({grammar:match(text .. "\n")}," ") end