// Newline separators // ignore_ws = _{ (" " | "\t")* } // Character types // // Characters that interrupt text special = { NEWLINE | "[" | "]" | "*" | "\\" | "_" | "`" | "$" } // Boring text charcters text_char = { !special ~ ANY } // Content - The meat of matthewdown // // Sequences to enable and disable bold and italic bold_toggle = { ("**" | "__") } italic_toggle = { ("*" | "_") } // Code code_str = { (!"`" ~ !NEWLINE ~ ANY)* } code = { "`" ~ code_str ~ "`" } // Escape sequence for writing special characters as normal characters escape_char = { ANY } escape_seq = { "\\" ~ escape_char } // Normal text sequence with no special handling text_str = @{ text_char+ } text = { text_str } line_break = @{ NEWLINE ~ ignore_ws } // Markdown-style links // // Terminate the link-ref on the first close bracket. link_ref = @{ (!")" ~ ANY)+ } link = { "[" ~ content ~ "]" ~ "(" ~ link_ref ~ ")" } // Inline Commands inline_command = { "$" ~ command_name ~ arg_list ~ ("[" ~ content ~ "]" | no_content) } // Single line content // // This is a line of content that breaks on the first newline. content_line = { ignore_ws ~ ( !NEWLINE ~ ( escape_seq | link | text | bold_toggle | italic_toggle | code | inline_command ) )+ } // Situations to break multi-line content // // This is where at the start of a new line, something new is introduced. break_paragraph = _{ NEWLINE ~ ignore_ws ~ ( EOI | // \nEOI NEWLINE | // \n\n New paragraph unordered_char | // * Unordered list list_number | // 1. Ordered list "#" | // ### Heading "$$" | // $$ Block command "```" // ``` Code block ) | EOI } // Multi-line content content = { ignore_ws ~ // Content can begin with a newline and then start after any initial // whitespace. ( !break_paragraph ~ NEWLINE ~ ignore_ws )? ~ // The inline elements of the content, each must not break the paragraph. ( !break_paragraph ~ ( escape_seq | link | text | bold_toggle | italic_toggle | code | inline_command | line_break ) )+ } no_content = { "" } // Types of blocks // block_indent = @{ (" " | "\t")* } // Code blocks code_block_start = _{ "```" ~ NEWLINE } code_block_end = _{ NEWLINE ~ "```" } code_multiline_str = { (!code_block_end ~ ANY)* } code_multiline = { code_multiline_str } code_block_content = { code_multiline ~ code_block_end } code_block = { block_indent ~ code_block_start ~ code_block_content } // Lists - both ordered and unordered unordered_char = _{ ("*" | "-" | "+") ~ (" " | "\t") } list_number = _{ ASCII_DIGIT+ ~ "." } unordered_item = { block_indent ~ unordered_char ~ content } ordered_item = { block_indent ~ list_number ~ content } paragraph = { block_indent ~ !NEWLINE ~ content } // Headings heading_level = { "#"+ } heading = { block_indent ~ heading_level ~ content_line } // Block commands command_name = { (ASCII_ALPHA | "-")+ } arg = { (!")" ~ !"," ~ ANY)+ } arg_list = { ( "(" ~ (arg ~ ",")* ~ arg ~ ")" )? } block_command = { block_indent ~ "$$" ~ command_name ~ arg_list ~ ignore_ws ~ (content | (no_content ~ &break_paragraph)) } block = { ordered_item | unordered_item | heading | block_command | code_block | paragraph } document = { SOI ~ (block | NEWLINE)* ~ ignore_ws ~ EOI }