-- Adapted from Markdown LPeg lexer by Mitchell. -- See . local lexer = lexer local P, S, B = lpeg.P, lpeg.S, lpeg.B local lex = lexer.new(..., {no_user_word_lists = true}) local md = '.markdown' local ws = lex:get_rule('whitespace') local nl = P('\n') local bl = nl * nl local nws = lexer.any - lexer.space -- Heading. lex:add_rule('heading_delimiter', lex:tag('delimiter' .. md, lexer.starts_line(P('#')^-6 * P(' ')))) lex:add_rule('heading', lex:tag(lexer.HEADING .. md, ( B(bl * P('# ')) + B(bl * P('## ')) + B(bl * P('### ')) + B(bl * P('#### ')) + B(bl * P('##### ')) + B(bl * P('###### ')) ) * lexer.to_eol(nws) * #bl)) -- Blockquote. lex:add_rule('blockquote_delimiter', lex:tag('delimiter' .. md, lexer.starts_line(P('> ')^1))) -- Horizontal rule. lex:add_rule('hr', lex:tag('hr' .. md, B(bl) * S('*-_')^3 * #bl)) -- Native divs. lex:add_rule("native_div_delimiter", lex:tag('delimiter' .. md, lexer.starts_line(lexer.to_eol(':::')))) -- Code block. local code_line = lexer.starts_line( (B(' ') + B('\t')) * lexer.to_eol(), true) local code_block = lexer.range(lexer.starts_line('```', true), lexer.starts_line(P('```'))) + lexer.range(lexer.starts_line('~~~', true), lexer.starts_line(P('~~~'))) local code_inline = lpeg.Cmt( lpeg.C(P('`')^1), function(input, index, bt) local _, e = input:find('[^`]' .. bt .. '%f[^`]', index) return (e or #input) + 1 end) lex:add_rule('block_code', lex:tag(lexer.CODE .. md, code_line + code_block)) -- Escape. lex:add_rule('escape', lex:tag(lexer.DEFAULT, P('\\') * 1)) -- Bracket. lex:add_rule('brackets', lex:tag('delimiter' .. md, S('[]'))) -- Footnote. lex:add_rule('footnote_key', lex:tag(lexer.REFERENCE .. md, (P('^') * #P('[')) + (B('[') * P('^') * (lexer.any - lexer.space - S('^[]'))^1 * #P(']')))) lex:add_rule('cite_key', lex:tag(lexer.REFERENCE .. md, B(lexer.space + P('[')) * P('-')^-1 * P('@') * (lexer.alnum + P('_')) * (lexer.alnum + S(':.#$%&-+?<>~/'))^0 + B(lexer.space + P('[')) * P('-')^-1 * P('@') * lexer.range('{', '}'))) -- Link. lex:add_rule('link_text', lex:tag(lexer.LINK .. md, B('[') * (lexer.any - P(']'))^1 * #P(P(']') * lexer.range('(', ')')))) lex:add_rule('link_target', lex:tag('delimiter' .. md, B(']') * lexer.range('(', ')'))) lex:add_rule('link_ref', lex:tag('delimiter' .. md, B(']') * P(':') * #lexer.space)) -- Image lex:add_rule('image_bang', lex:tag(lexer.REFERENCE .. md, P('!') * #P('['))) -- Attribute. lex:add_rule('attribute', lex:tag('delimiter' .. md, B(S('])')) * lexer.range('{', '}'))) local punct_space = lexer.punct + lexer.space -- Handles flanking delimiters as described in -- https://github.github.com/gfm/#emphasis-and-strong-emphasis in the cases -- where simple delimited ranges are not sufficient. local function flanked_range(s, not_inword) local fl_char = lexer.any - s - lexer.space local left_fl = B(punct_space - s) * s * #fl_char + s * #(fl_char - lexer.punct) local right_fl = B(lexer.punct) * s * #(punct_space - s) + B(fl_char) * s return left_fl * (lexer.any - bl - (not_inword and s * #punct_space or s))^0 * right_fl end local asterisk_strong = flanked_range('**') local underscore_strong = (B(punct_space) + #lexer.starts_line('_')) * flanked_range('__', true) * #(punct_space + -1) lex:add_rule('strong', lex:tag(lexer.BOLD, asterisk_strong + underscore_strong)) local asterisk_em = flanked_range('*') local underscore_em = (B(punct_space) + #lexer.starts_line('_')) * flanked_range('_', true) * #(punct_space + -1) lex:add_rule('em', lex:tag(lexer.ITALIC, asterisk_em + underscore_em)) -- Embedded HTML. local html = lexer.load('html') local html_start_rule = lexer.starts_line(P(' ')^-3) * #P('<') * html:get_rule('tag') + html:get_rule('comment') local html_end_rule = #bl * ws lex:embed(html, html_start_rule, html_end_rule) -- Embedded YAML. local yaml = lexer.load('yaml') local yaml_start_rule = #lexer.starts_line(P('---'), false) * yaml:get_rule('doc_bounds') local yaml_end_rule = #lexer.starts_line(P('...'), false) * yaml:get_rule('doc_bounds') lex:embed(yaml, yaml_start_rule, yaml_end_rule) return lex