mdq 0.10.0

Select and render specific elements in a Markdown document
Documentation
WHITESPACE = _{ " " | "\t" | "\r" | "\n" }

top = { SOI ~ "|"* ~ selector_chain? ~ EOI }

selector_chain = { selector ~ ("|"+ ~ selector?)* }

selector = {
    select_section
  | select_list_item
  | select_link
  | select_block_quote
  | select_code_block
  | select_front_matter
  | select_html
  | select_paragraph
  | select_table
}

selector_delim = _{ explicit_space | EOI }
explicit_space = !{ " " } // making this a rule lets us have nicer error messages if the user doesn't include it

select_section =  { section_start ~ PUSH_LITERAL("|") ~ #title = string }
section_start  = ${ "#" ~ section_opts? ~ selector_delim }
section_opts   = ${
    "{"
    ~ #level_min = section_num?
    ~ #level_comma = section_comma?
    ~ #level_max = section_num?
    ~ "}"
}
section_num = @{ ('0' .. '9')+ }
section_comma = @{ "," }

select_list_item  =  { list_start ~ list_task_options? ~ PUSH_LITERAL("|") ~ #contents = string }
list_start        = ${ (list_ordered | "-") ~ selector_delim }
list_ordered      = ${ "1." }
list_task_options = ${ "[" ~ (task_unchecked | task_checked | task_either) ~ task_end }
task_checked      = ${ "x" }
task_unchecked    = ${ " " }
task_either       = ${ "?" }
task_end          = ${ "]" }

select_link =  { link_start ~ PUSH_LITERAL("]") ~ #display_text = string ~ "](" ~ PUSH_LITERAL(")")~ #url_text = string ~ ")" }
link_start  = ${ image_start? ~ "[" }
image_start = @{ "!" }

select_block_quote       =  { select_block_quote_start ~ PUSH_LITERAL("|") ~ #text = string }
select_block_quote_start = @{ ">" ~ selector_delim }

select_code_block =  { code_block_start ~ PUSH_LITERAL("|") ~ #text = string }
code_block_start  = ${ "```" ~ PUSH_LITERAL(" ") ~ #language = string ~ selector_delim }

select_front_matter =  { front_matter_start ~ PUSH_LITERAL("|") ~ #text = string }
front_matter_start  = ${ "+++" ~ PUSH_LITERAL(" ") ~ #variant = string ~ selector_delim }

select_html =  { html_start ~ PUSH_LITERAL("|") ~ #text = string }
html_start  = @{ "</>" ~ selector_delim }

select_paragraph       =  { select_paragraph_start ~ PUSH_LITERAL("|") ~ #text = string }
select_paragraph_start = @{ "P:" ~ selector_delim }

select_table = { table_start ~ PUSH_LITERAL(":") ~ #column = string ~ ":-:" ~ PUSH_LITERAL("|") ~ #row = string }
table_start = ${":-:" ~ explicit_space }

// helper rule, just for unit tests
string_for_unit_tests__do_not_use_pipe = { PUSH_LITERAL("|") ~ string }
string_for_unit_tests__do_not_use_angle = { PUSH_LITERAL(">") ~ string }
string = {
  // end delimiter for unquoted string will have been PUSH_LITERAL'd by here
  (
        asterisk
      | regex
      | ( anchor_start? ~ ( quoted_string | unquoted_string ) ~ anchor_end? )
      | ( anchor_start ~ anchor_end )
  )?
  ~ DROP
}
asterisk = @{ "*" }
unquoted_string = @{ LETTER ~ (!(PEEK | "$") ~ ANY)* }

regex               = ${
  // Put these into a single rule, so that the error message just says "regex" for both the plain and replace variant.
    ("/" ~ regex_char* ~ "/")
  | ("!s/" ~ regex_char* ~ "/" ~ regex_replacement_segment? ~ "/")
}
regex_char          = ${
    (regex_escaped_slash | regex_normal_char)
}
regex_replacement_segment = ${ regex_char+ }
regex_escaped_slash = @{ "\\/" }
regex_normal_char   = @{ !("/") ~ ANY }

quoted_string = ${ PUSH("'" | "\"") ~ quoted_char* ~ POP }

quoted_char = ${
    quoted_plain_chars
  | ("\\" ~ (escaped_char | "u{" ~ unicode_seq ~ "}"))
}

anchor_start = @{ "^" }

anchor_end = @{ "$" }

quoted_plain_chars = @{ (!(PEEK | "\\") ~ ANY)+ }

escaped_char = @{ ("\"" | "'" | "`" | "\\" | "n" | "r" | "t") }

unicode_seq = @{ ASCII_HEX_DIGIT{1, 6} }