// src/core/prompt.pest
WHITESPACE = _{ " " | "\t" | "\r" | "\n" }
// --- Values ---
string = @{ "\"" ~ (!"\"" ~ ANY)* ~ "\"" }
number = @{ "-"? ~ ("0" | ASCII_NONZERO_DIGIT ~ ASCII_DIGIT*) ~ ("." ~ ASCII_DIGIT*)? }
boolean = { "true" | "false" }
identifier = @{ ASCII_ALPHA ~ (ASCII_ALPHANUMERIC | "_")* }
// --- JSON-like Recursive Structures ---
// fm_value is the type allowed for metadata values
fm_value = _{ json_object | json_array | string | number | boolean }
json_pair = { (string | identifier) ~ ":" ~ fm_value }
json_object = { "{" ~ (json_pair ~ ("," ~ json_pair)*)? ~ ","? ~ "}" }
json_array = { "[" ~ (fm_value ~ ("," ~ fm_value)*)? ~ ","? ~ "]" }
// --- Metadata Object (The content under inputs:) ---
meta_key_names = _{ "slug" | "name" | "type" | "description" | "inputs" | "is_public" }
// obj_pair represents a key-value entry in the inputs list
obj_pair = { !(meta_key_names ~ ":") ~ !separator ~ identifier ~ ":" ~ fm_value }
metadata_obj = {
json_object |
json_array |
obj_pair*
}
// --- Frontmatter Fields ---
key_slug = { "slug" ~ ":" ~ string }
key_name = { "name" ~ ":" ~ string }
key_type = { "type" ~ ":" ~ string }
key_desc = { "description" ~ ":" ~ string }
key_inputs = { "inputs" ~ ":" ~ metadata_obj }
key_public = { "is_public" ~ ":" ~ boolean }
meta_entry = _{ (key_slug | key_name | key_type | key_desc | key_inputs | key_public) }
separator = _{ "---" }
frontmatter = { separator ~ meta_entry+ ~ separator }
// --- Template Logic ---
nl = _{ "\r\n" | "\n" }
if_tag = ${ "{%" ~ inner_ws* ~ "if" ~ inner_ws+ ~ expression ~ inner_ws* ~ "%}" ~ nl? }
elif_tag = ${ "{%" ~ inner_ws* ~ "elif" ~ inner_ws+ ~ expression ~ inner_ws* ~ "%}" ~ nl? }
else_tag = ${ "{%" ~ inner_ws* ~ "else" ~ inner_ws* ~ "%}" ~ nl? }
endif_tag = ${ "{%" ~ inner_ws* ~ "endif" ~ inner_ws* ~ "%}" ~ nl? }
for_tag = ${ "{%" ~ inner_ws* ~ "for" ~ inner_ws+ ~ identifier ~ inner_ws+ ~ "in" ~ inner_ws+ ~ expression ~ inner_ws* ~ "%}" ~ nl? }
endfor_tag = ${ "{%" ~ inner_ws* ~ "endfor" ~ inner_ws* ~ "%}" ~ nl? }
interpolation = { "{{" ~ inner_ws* ~ expression ~ inner_ws* ~ "}}" }
body = ${ node* }
node = _{ for_block | if_block | tag_block | tag_self_close | interpolation | raw_text }
inner_ws = _{ " " | "\t" | "\r" | "\n" }
expression = @{ (!"}}" ~ !"%}" ~ ANY)+ }
raw_text = @{ (!"{{" ~ !"{%" ~ ANY)+ }
elif_branch = { elif_tag ~ body }
if_block = { if_tag ~ body ~ elif_branch* ~ (else_tag ~ body)? ~ endif_tag }
for_block = { for_tag ~ body ~ (else_tag ~ body)? ~ endfor_tag }
// --- Custom Tags (Markdoc-style) ---
reserved_keyword = @{ ("if" | "elif" | "else" | "endif" | "for" | "endfor") ~ !ASCII_ALPHANUMERIC }
tag_attr_value = { string | tag_attr_expr }
tag_attr_expr = @{ (!inner_ws ~ !"%}" ~ !"/%}" ~ ANY)+ }
tag_attr = ${ inner_ws+ ~ identifier ~ inner_ws* ~ "=" ~ inner_ws* ~ tag_attr_value }
tag_attrs = ${ tag_attr* }
tag_open = ${ "{%" ~ inner_ws+ ~ !reserved_keyword ~ identifier ~ tag_attrs ~ inner_ws* ~ "%}" ~ nl? }
tag_close = ${ "{%" ~ inner_ws* ~ "/" ~ inner_ws* ~ identifier ~ inner_ws* ~ "%}" ~ nl? }
tag_self_close = ${ "{%" ~ inner_ws+ ~ !reserved_keyword ~ identifier ~ tag_attrs ~ inner_ws* ~ "/%}" ~ nl? }
tag_block = { tag_open ~ body ~ tag_close }
prompt_file = { SOI ~ frontmatter ~ body ~ EOI }