WHITESPACE         = _{ " " | "\t" | "\r" | "\n" }
COMMENT            = _{ ";" ~ (!("\n") ~ ANY)* ~ "\n" }
identifier         = @{ identifier_initial ~ identifier_rest* }
identifier_initial = @{ ASCII_ALPHA | "_" | "-" }
identifier_rest    = @{ ASCII_ALPHANUMERIC | "_" | "-" }
symbol_name        = @{ !(reserved ~ !(identifier_rest | ".")) ~ (identifier ~ ("." ~ identifier)*) }
link_name          = @{ "%" ~ (ASCII_ALPHANUMERIC | "_" | "-")* }

literal = { literal_string | literal_float | literal_nat | literal_bytes }

reserved = @{
    "mod"
  | "hugr"
  | "bytes"
  | "tuple"
  | "list"
  | "meta"
  | "signature"
  | "public"
  | "private"
  | "dfg"
  | "cfg"
  | "block"
  | "define-func"
  | "declare-func"
  | "declare-operation"
  | "declare-ctr"
  | "declare-alias"
  | "tail-loop"
  | "cond"
  | "import"
  | "..."
}

literal_string         = ${ "\"" ~ (literal_string_raw | literal_string_escape | literal_string_unicode)* ~ "\"" }
literal_string_raw     = @{ (!("\\" | "\"") ~ ANY)+ }
literal_string_escape  = @{ "\\" ~ ("\"" | "\\" | "n" | "r" | "t") }
literal_string_unicode = @{ "\\u" ~ "{" ~ ASCII_HEX_DIGIT+ ~ "}" }

literal_nat   = @{ (ASCII_DIGIT)+ }
literal_bytes =  { "(" ~ "bytes" ~ base64_string ~ ")" }
literal_float = @{ ("+" | "-")? ~ ASCII_DIGIT+ ~ "." ~ ASCII_DIGIT+ ~ ("e" ~ ("+" | "-")? ~ ASCII_DIGIT+)? }

base64_string = { "\"" ~ (ASCII_ALPHANUMERIC | "+" | "/")* ~ "="* ~ "\"" }

package = { "(" ~ "hugr" ~ "0" ~ ")" ~ module* ~ EOI }
module  = { "(" ~ "mod" ~ ")" ~ meta* ~ node* }

meta = { "(" ~ "meta" ~ term ~ ")" }

port_list  =  { "[" ~ link_name* ~ "]" }
port_lists = _{ port_list ~ port_list }

node = {
    node_dfg
  | node_cfg
  | node_block
  | node_define_func
  | node_declare_func
  | node_define_alias
  | node_declare_alias
  | node_declare_ctr
  | node_declare_operation
  | node_tail_loop
  | node_cond
  | node_import
  | node_custom
}

node_dfg               = { "(" ~ "dfg" ~ port_lists? ~ signature? ~ meta* ~ region* ~ ")" }
node_cfg               = { "(" ~ "cfg" ~ port_lists? ~ signature? ~ meta* ~ region* ~ ")" }
node_block             = { "(" ~ "block" ~ port_lists? ~ signature? ~ meta* ~ region* ~ ")" }
node_define_func       = { "(" ~ "define-func" ~ symbol ~ meta* ~ region* ~ ")" }
node_declare_func      = { "(" ~ "declare-func" ~ symbol ~ meta* ~ ")" }
node_define_alias      = { "(" ~ "define-alias" ~ symbol ~ term ~ meta* ~ ")" }
node_declare_alias     = { "(" ~ "declare-alias" ~ symbol ~ meta* ~ ")" }
node_declare_ctr       = { "(" ~ "declare-ctr" ~ symbol ~ meta* ~ ")" }
node_declare_operation = { "(" ~ "declare-operation" ~ symbol ~ meta* ~ ")" }
node_tail_loop         = { "(" ~ "tail-loop" ~ port_lists? ~ signature? ~ meta* ~ region* ~ ")" }
node_cond              = { "(" ~ "cond" ~ port_lists? ~ signature? ~ meta* ~ region* ~ ")" }
node_import            = { "(" ~ "import" ~ symbol_name ~ meta* ~ ")" }
node_custom            = { "(" ~ term ~ port_lists? ~ signature? ~ meta* ~ region* ~ ")" }

visibility = { "public" | "private" }

symbol = { visibility? ~ symbol_name ~ param* ~ where_clause* ~ term }

signature    = { "(" ~ "signature" ~ term ~ ")" }
param        = { "(" ~ "param" ~ term_var ~ term ~ ")" }
where_clause = { "(" ~ "where" ~ term ~ ")" }

region      = { "(" ~ region_kind ~ port_lists? ~ signature? ~ meta* ~ node* ~ ")" }
region_kind = { "dfg" | "cfg" | "mod" }

term = {
    term_wildcard
  | term_var
  | term_list
  | literal
  | term_tuple
  | term_const_func
  | term_apply
}

term_wildcard   = @{ "_" ~ !(ASCII_ALPHANUMERIC | "_" | "-") }
term_var        =  { "?" ~ identifier }
term_apply      =  { symbol_name | ("(" ~ symbol_name ~ term* ~ ")") }
term_list       =  { "[" ~ part* ~ "]" }
term_tuple      =  { "(" ~ "tuple" ~ part* ~ ")" }
term_const_func =  { "(" ~ "fn" ~ term ~ ")" }

part         = { spliced_term | term }
spliced_term = { term ~ "..." }
