local html = require'html' local prose = require'prose' local function highlight(code) -- todo return html.T.pre(code) end local function mode_code(S) local lang = S:line():match("^```(%w+) ?(.*)") local text = "" for line in S:lines() do if line:match("^```") then break else text = text .. line end end S:emit(highlight(text)) end local function mode_list(S) local items = {} for line in S:lines() do if line:match("^%*") then local content = line:gsub("^%*%s*","") table.insert(items, html.T.li(prose(S,content))) else S:unline(line) break end end S:emit(html.T.ul(items)) end local function mode_heading(S) local level, line = S:line():match("^#*()%s*(.*)") level = level - 1 S:emit(html.tag('h'..level, prose(S,line))) end local mode_patterns = { { "^```", mode_code }, { "^*", mode_list }, { "^#", mode_heading }, } local function match_mode_pattern(S, line) for i, row in ipairs(mode_patterns) do local pattern, mode = row[1], row[2] if line:match(pattern) then return mode end end end local function mode_paragraphs(S) local para = "" local function finish() if para:match("%S") then S:emit(html.T.p(prose(S,para))) end para = "" end for line in S:lines() do local newmode = match_mode_pattern(S, line) if newmode then S:unline(line) finish() return newmode(S) elseif line:match("%S") then para = para .. line else finish() end end end local function split_lines(text) local lines = {} for x in text:gmatch("[^\n]*") do table.insert(lines, x.."\n") end return lines end local State = {} function State.new(input) return setmetatable({ the_lines = split_lines(input) }, {__index=State}) end function State.emit(self, x) print(html.html(x)) end function State.line(self) return table.remove(self.the_lines, 1) end function State.unline(self, line) table.insert(self.the_lines, 1, line) end function State.lines(self) return State.line, self end function State.remaining(self) return #self.the_lines > 0 end function State.toplevel(self) while self:remaining() do mode_paragraphs(self) end end State.new(io.read"a"):toplevel()