SPACE = _{ " " | "\t" }
SPACES = _{ SPACE* }
// Tags
TAG_OPEN = _{ "<<" }
TAG_CLOSE = _{ ">>" }
// Keywords
SOLUTION = _{ "solution" }
PLACEHOLDER = _{ "placeholder" }
// Comment and markup definitions
COMMENT_SYMBOLS = _{ "#" | "//" }
COMMENT_DEF = _{ COMMENT_SYMBOLS }
MARKUP = _{ COMMENT_DEF ~ "|" }
REGULAR_COMMENT = _{ COMMENT_DEF ~ !"|" }
// Captures any single line string
ident = { (ASCII_ALPHANUMERIC | "_")+ }
str = { (!NEWLINE ~ ANY)* }
str_anon = _{ (!NEWLINE ~ ANY)* }
// Source code elements
comment_space = { SPACES }
source_code = { (!(NEWLINE | MARKUP) ~ ANY)* }
source_code_block = @{ (source_code ~ NEWLINE)+ }
comment = @{ str ~ NEWLINE }
source_comment = ${ comment_space ~ REGULAR_COMMENT ~ SPACES ~ comment }
source_comment_block = { (source_comment)+ }
// Code block definitions
code_block = { SPACES ~ code_block_def ~ NEWLINE ~ solution ~ (code_block_placeholder ~ NEWLINE ~ placeholder)? ~ code_block_end }
code_block_def = _{ MARKUP ~ SPACES ~ SOLUTION ~ SPACES ~ TAG_OPEN ~ SPACES }
code_block_end = _{ SPACES ~ MARKUP ~ SPACES ~ TAG_CLOSE ~ SPACES }
code_block_placeholder = _{ SPACES ~ MARKUP ~ SPACES ~ PLACEHOLDER ~ SPACES }
// Solution and placeholder definitions
solution = { (source_code_block)* }
placeholder = { (source_comment_block)* }
meta = { SPACES ~ MARKUP ~ SPACES ~ meta_inner ~ SPACES }
meta_inner = _{ custom }
custom = { ident ~ ":" ~ SPACES ~ value }
key = { str }
value = { str_anon }
// Top level glue
top_level = _{ SPACES ~ (code_block ~ NEWLINE | meta ~ NEWLINE | source_code_block) }
src = { top_level+ }
doc = _{ src ~ EOI }