// MMIXAL Grammar for Pest Parser
// Based on TAOCP Vol 1 Fascicle 1 - MMIX Assembly Language
WHITESPACE = _{ " " | "\t" | "\r" }
COMMENT = _{ "%" ~ (!"\n" ~ ANY)* }
// A keyword ends where an identifier could not continue: the complement of
// an identifier's continuation set (ASCII_ALPHANUMERIC or '_'). Every
// mnemonic and directive rule closes with it, which keeps the two
// definitions in step.
word_end = _{ !(ASCII_ALPHANUMERIC | "_") }
// Identifier that may carry a leading ':' marking a global (linkage-visible)
// symbol, and interior ':'s qualifying it (`Foo:Bar`). A colon counts as
// part of the name only when an identifier character follows it; a colon
// with whitespace or end of line after it is `label_def`'s own trailing
// separator, never absorbed here -- so `Main: SET $1,0` still names `Main`
// while `Main:SET $1,0` reads `Main:SET` as one symbol.
global_id_part = @{ (ASCII_ALPHA | "_") ~ (ASCII_ALPHANUMERIC | "_")* }
global_id = @{ ":"? ~ global_id_part ~ (":" ~ global_id_part)* }
// A decimal digit followed by `H` names a local label; `B`/`F` reference the
// nearest one behind/ahead in source order. Upper case only -- `2h`, `2b`
// and `2f` are not local symbols. `word_end` keeps a longer identifier from
// ever being mistaken for one of these.
local_label_def = @{ ASCII_DIGIT ~ "H" ~ word_end }
local_ref_back = @{ ASCII_DIGIT ~ "B" ~ word_end }
local_ref_fwd = @{ ASCII_DIGIT ~ "F" ~ word_end }
// Numeric literals: decimal (a leading '0' is an ordinary digit -- the
// MMIXAL reference has no octal spelling) and hex (#... or 0x...).
// @ represents current location
at_symbol = @{ "@" }
hex_literal = @{ ("#" | "0x" | "0X") ~ ASCII_HEX_DIGIT+ }
dec_literal = @{ ASCII_DIGIT+ }
char_literal = @{ "'" ~ (!NEWLINE ~ ANY) ~ "'" }
constant = { hex_literal | dec_literal | char_literal }
// MMIXAL expressions: two left-associative precedence levels over unary
// operators, parentheses and `@`. `expr` is compound-atomic end to end,
// groups included: no implicit WHITESPACE/COMMENT ever splices into it,
// which is what keeps `%` an operator here and a comment opener everywhere
// else, and compound- (not plain) atomicity is what keeps every level's
// inner pairs available to the evaluator. A bare expression therefore holds
// no whitespace of its own and ends at the first character that fails to
// extend it. A parenthesized group is the one place whitespace is allowed;
// `group_ws` is its own explicit space rule, never pest's implicit one, so
// entering a group never reopens a comment.
unary_op = { "+" | "-" | "~" | "$" | "&" }
strong_op = { "//" | "*" | "/" | "%" | "<<" | ">>" | "&" }
weak_op = { "+" | "-" | "|" | "^" }
expr = ${ term ~ (weak_op ~ term)* }
term = { primary ~ (strong_op ~ primary)* }
primary = {
unary_op ~ primary | group | at_symbol | local_ref_back | local_ref_fwd | constant | global_id
}
group_ws = _{ " " | "\t" }
group = { "(" ~ group_ws* ~ group_expr ~ group_ws* ~ ")" }
group_expr = { group_term ~ (group_ws* ~ weak_op ~ group_ws* ~ group_term)* }
group_term = { group_primary ~ (group_ws* ~ strong_op ~ group_ws* ~ group_primary)* }
group_primary = {
unary_op ~ group_ws* ~ group_primary | group | at_symbol
| local_ref_back | local_ref_fwd | constant | global_id
}
// Identifiers (kept for backward compatibility in some contexts)
identifier = @{ (ASCII_ALPHA | "_") ~ (ASCII_ALPHANUMERIC | "_")* }
// Label definition: identifier (optionally global, with leading ':')
// optionally followed by a trailing colon (legacy MMIXAL syntax).
label_def = { global_id ~ ":"? }
// String literal for a data directive's value list
string_literal = @{ "\"" ~ (!"\"" ~ ANY)* ~ "\"" }
// Data directives - must be complete words
directive_byte = @{ "BYTE" ~ word_end }
directive_wyde = @{ "WYDE" ~ word_end }
directive_tetra = @{ "TETRA" ~ word_end }
directive_octa = @{ "OCTA" ~ word_end }
// Control directives - must be complete words, not prefixes
directive_loc = @{ "LOC" ~ word_end }
directive_is = @{ "IS" ~ word_end }
directive_greg = @{ "GREG" ~ word_end }
directive_prefix = @{ "PREFIX" ~ word_end }
directive_local = @{ "LOCAL" ~ word_end }
directive_bspec = @{ "BSPEC" ~ word_end }
directive_espec = @{ "ESPEC" ~ word_end }
// PREFIX text is prepended verbatim, so PREFIX foo: makes bar define
// foo:bar (PREFIX foo would give foobar); PREFIX : resets to the root
// namespace.
prefix_arg = @{ (ASCII_ALPHA | "_" | ":") ~ (ASCII_ALPHANUMERIC | "_" | ":")* }
// A data directive's list mirrors `expr`/`term`/`primary` and `group*`,
// with one addition: a string may stand wherever a `data_primary` stands,
// combining with a unary operator directly on it and with a strong or weak
// operator on either side. A string is not an expression operand elsewhere
// -- the reference defines it as shorthand for a list of character
// constants, not as a value arithmetic can touch, so `data_expr` serves
// data lists only and `expr` keeps serving instruction operands.
data_expr = ${ data_term ~ (weak_op ~ data_term)* }
// The trailing `string_literal` alternative never matches: `data_primary`
// already covers a bare string, so the first alternative always succeeds
// where this one would. It keeps a string-free failure's diagnostic naming
// `data_term`, not `data_primary`. The evaluators assume a `string_literal`
// never appears directly in `data_term` position.
data_term = { (data_primary ~ (strong_op ~ data_primary)*) | string_literal }
data_primary = {
unary_op ~ data_primary | data_group | at_symbol | local_ref_back | local_ref_fwd | constant | global_id
| string_literal
}
data_group = { "(" ~ group_ws* ~ data_group_expr ~ group_ws* ~ ")" }
data_group_expr = { data_group_term ~ (group_ws* ~ weak_op ~ group_ws* ~ data_group_term)* }
data_group_term = { data_group_primary ~ (group_ws* ~ strong_op ~ group_ws* ~ data_group_primary)* }
data_group_primary = {
unary_op ~ group_ws* ~ data_group_primary | data_group | at_symbol
| local_ref_back | local_ref_fwd | constant | global_id | string_literal
}
data_value = { data_expr }
data_values = { data_value ~ ("," ~ data_value)* }
data_directive = {
directive_byte ~ data_values |
directive_wyde ~ data_values |
directive_tetra ~ data_values |
directive_octa ~ data_values
}
greg_directive = { directive_greg ~ expr? }
loc_directive = { directive_loc ~ expr }
is_directive = { (local_label_def | global_id) ~ directive_is ~ expr }
prefix_directive = { directive_prefix ~ prefix_arg }
local_directive = { directive_local ~ expr }
bspec_directive = { directive_bspec ~ expr }
espec_directive = { directive_espec }
// Group all directives under a single rule
// Note: debug directives are handled by preprocessor, not grammar
directive = {
prefix_directive
| is_directive
| loc_directive
| greg_directive
| local_directive
| bspec_directive
| espec_directive
| data_directive
}
// A line can have: [label] [instruction|directive]
// Labels may optionally end with colon
// IS directive is special - identifier IS value (no label separator)
// Order matters: try directive first (which includes is_directive), then label+content
statement = {
directive
| instruction
| local_label_def ~ (instruction | directive)?
| label_def ~ (instruction | directive)?
}
mnemonic_setmh = @{ "SETMH" ~ word_end }
mnemonic_setml = @{ "SETML" ~ word_end }
mnemonic_setl = @{ "SETL" ~ word_end }
mnemonic_seth = @{ "SETH" ~ word_end }
mnemonic_seti = @{ "SETI" ~ word_end }
mnemonic_set = @{ "SET" ~ word_end }
mnemonic_incl = @{ "INCL" ~ word_end }
mnemonic_inch = @{ "INCH" ~ word_end }
mnemonic_incmh = @{ "INCMH" ~ word_end }
mnemonic_incml = @{ "INCML" ~ word_end }
mnemonic_orh = @{ "ORH" ~ word_end }
mnemonic_ormh = @{ "ORMH" ~ word_end }
mnemonic_orml = @{ "ORML" ~ word_end }
mnemonic_orl = @{ "ORL" ~ word_end }
mnemonic_andnh = @{ "ANDNH" ~ word_end }
mnemonic_andnmh = @{ "ANDNMH" ~ word_end }
mnemonic_andnml = @{ "ANDNML" ~ word_end }
mnemonic_andnl = @{ "ANDNL" ~ word_end }
// Load/Store instructions - ordered longest first
mnemonic_ldbui = @{ "LDBUI" ~ word_end }
mnemonic_ldbu = @{ "LDBU" ~ word_end }
mnemonic_ldbi = @{ "LDBI" ~ word_end }
mnemonic_ldb = @{ "LDB" ~ word_end }
mnemonic_ldwui = @{ "LDWUI" ~ word_end }
mnemonic_ldwu = @{ "LDWU" ~ word_end }
mnemonic_ldwi = @{ "LDWI" ~ word_end }
mnemonic_ldw = @{ "LDW" ~ word_end }
mnemonic_ldtui = @{ "LDTUI" ~ word_end }
mnemonic_ldtu = @{ "LDTU" ~ word_end }
mnemonic_ldti = @{ "LDTI" ~ word_end }
mnemonic_ldt = @{ "LDT" ~ word_end }
mnemonic_ldoui = @{ "LDOUI" ~ word_end }
mnemonic_ldou = @{ "LDOU" ~ word_end }
mnemonic_ldoi = @{ "LDOI" ~ word_end }
mnemonic_ldo = @{ "LDO" ~ word_end }
mnemonic_ldai = @{ "LDAI" ~ word_end }
mnemonic_lda = @{ "LDA" ~ word_end }
mnemonic_stbui = @{ "STBUI" ~ word_end }
mnemonic_stbu = @{ "STBU" ~ word_end }
mnemonic_stbi = @{ "STBI" ~ word_end }
mnemonic_stb = @{ "STB" ~ word_end }
mnemonic_stwui = @{ "STWUI" ~ word_end }
mnemonic_stwu = @{ "STWU" ~ word_end }
mnemonic_stwi = @{ "STWI" ~ word_end }
mnemonic_stw = @{ "STW" ~ word_end }
mnemonic_sttui = @{ "STTUI" ~ word_end }
mnemonic_sttu = @{ "STTU" ~ word_end }
mnemonic_stti = @{ "STTI" ~ word_end }
mnemonic_stt = @{ "STT" ~ word_end }
mnemonic_stoui = @{ "STOUI" ~ word_end }
mnemonic_stou = @{ "STOU" ~ word_end }
mnemonic_stoi = @{ "STOI" ~ word_end }
mnemonic_sto = @{ "STO" ~ word_end }
// Arithmetic instructions - ordered longest first
mnemonic_16addui = @{ "16ADDUI" ~ word_end }
mnemonic_16addu = @{ "16ADDU" ~ word_end }
mnemonic_8addui = @{ "8ADDUI" ~ word_end }
mnemonic_8addu = @{ "8ADDU" ~ word_end }
mnemonic_4addui = @{ "4ADDUI" ~ word_end }
mnemonic_4addu = @{ "4ADDU" ~ word_end }
mnemonic_2addui = @{ "2ADDUI" ~ word_end }
mnemonic_2addu = @{ "2ADDU" ~ word_end }
mnemonic_addui = @{ "ADDUI" ~ word_end }
mnemonic_addu = @{ "ADDU" ~ word_end }
mnemonic_addi = @{ "ADDI" ~ word_end }
mnemonic_add = @{ "ADD" ~ word_end }
mnemonic_subui = @{ "SUBUI" ~ word_end }
mnemonic_subu = @{ "SUBU" ~ word_end }
mnemonic_subi = @{ "SUBI" ~ word_end }
mnemonic_sub = @{ "SUB" ~ word_end }
mnemonic_negui = @{ "NEGUI" ~ word_end }
mnemonic_negu = @{ "NEGU" ~ word_end }
mnemonic_negi = @{ "NEGI" ~ word_end }
mnemonic_neg = @{ "NEG" ~ word_end }
mnemonic_mului = @{ "MULUI" ~ word_end }
mnemonic_mulu = @{ "MULU" ~ word_end }
mnemonic_muli = @{ "MULI" ~ word_end }
mnemonic_mul = @{ "MUL" ~ word_end }
mnemonic_divui = @{ "DIVUI" ~ word_end }
mnemonic_divu = @{ "DIVU" ~ word_end }
mnemonic_divi = @{ "DIVI" ~ word_end }
mnemonic_div = @{ "DIV" ~ word_end }
// Comparison instructions - ordered longest first
mnemonic_cmpui = @{ "CMPUI" ~ word_end }
mnemonic_cmpu = @{ "CMPU" ~ word_end }
mnemonic_cmpi = @{ "CMPI" ~ word_end }
mnemonic_cmp = @{ "CMP" ~ word_end }
// Floating point instructions - ordered longest first
mnemonic_sflotui = @{ "SFLOTUI" ~ word_end }
mnemonic_sflotu = @{ "SFLOTU" ~ word_end }
mnemonic_sfloti = @{ "SFLOTI" ~ word_end }
mnemonic_sflot = @{ "SFLOT" ~ word_end }
mnemonic_flotui = @{ "FLOTUI" ~ word_end }
mnemonic_flotu = @{ "FLOTU" ~ word_end }
mnemonic_floti = @{ "FLOTI" ~ word_end }
mnemonic_flot = @{ "FLOT" ~ word_end }
mnemonic_fixu = @{ "FIXU" ~ word_end }
mnemonic_fix = @{ "FIX" ~ word_end }
mnemonic_fsqrt = @{ "FSQRT" ~ word_end }
mnemonic_fint = @{ "FINT" ~ word_end }
mnemonic_frem = @{ "FREM" ~ word_end }
mnemonic_fmul = @{ "FMUL" ~ word_end }
mnemonic_fdiv = @{ "FDIV" ~ word_end }
mnemonic_fadd = @{ "FADD" ~ word_end }
mnemonic_fsub = @{ "FSUB" ~ word_end }
mnemonic_fcmpe = @{ "FCMPE" ~ word_end }
mnemonic_fcmp = @{ "FCMP" ~ word_end }
mnemonic_feqle = @{ "FEQLE" ~ word_end }
mnemonic_feql = @{ "FEQL" ~ word_end }
mnemonic_fune = @{ "FUNE" ~ word_end }
mnemonic_fun = @{ "FUN" ~ word_end }
// Bitwise operations - ordered longest first for exact matching
mnemonic_andni = @{ "ANDNI" ~ word_end }
mnemonic_andn = @{ "ANDN" ~ word_end }
mnemonic_andi = @{ "ANDI" ~ word_end }
mnemonic_and = @{ "AND" ~ word_end }
mnemonic_orni = @{ "ORNI" ~ word_end }
mnemonic_orn = @{ "ORN" ~ word_end }
mnemonic_ori = @{ "ORI" ~ word_end }
mnemonic_or = @{ "OR" ~ word_end }
mnemonic_xori = @{ "XORI" ~ word_end }
mnemonic_xor = @{ "XOR" ~ word_end }
mnemonic_nandi = @{ "NANDI" ~ word_end }
mnemonic_nand = @{ "NAND" ~ word_end }
mnemonic_nxori = @{ "NXORI" ~ word_end }
mnemonic_nxor = @{ "NXOR" ~ word_end }
mnemonic_nori = @{ "NORI" ~ word_end }
mnemonic_nor = @{ "NOR" ~ word_end }
mnemonic_muxi = @{ "MUXI" ~ word_end }
mnemonic_mux = @{ "MUX" ~ word_end }
// Conditional set instructions - ordered longest first
mnemonic_csnni = @{ "CSNNI" ~ word_end }
mnemonic_csnzi = @{ "CSNZI" ~ word_end }
mnemonic_csnpi = @{ "CSNPI" ~ word_end }
mnemonic_csodi = @{ "CSODI" ~ word_end }
mnemonic_csevi = @{ "CSEVI" ~ word_end }
mnemonic_csni = @{ "CSNI" ~ word_end }
mnemonic_cszi = @{ "CSZI" ~ word_end }
mnemonic_cspi = @{ "CSPI" ~ word_end }
mnemonic_csnn = @{ "CSNN" ~ word_end }
mnemonic_csnz = @{ "CSNZ" ~ word_end }
mnemonic_csnp = @{ "CSNP" ~ word_end }
mnemonic_csod = @{ "CSOD" ~ word_end }
mnemonic_csev = @{ "CSEV" ~ word_end }
mnemonic_csn = @{ "CSN" ~ word_end }
mnemonic_csz = @{ "CSZ" ~ word_end }
mnemonic_csp = @{ "CSP" ~ word_end }
// Zero or set instructions - ordered longest first
mnemonic_zsnni = @{ "ZSNNI" ~ word_end }
mnemonic_zsnzi = @{ "ZSNZI" ~ word_end }
mnemonic_zsnpi = @{ "ZSNPI" ~ word_end }
mnemonic_zsodi = @{ "ZSODI" ~ word_end }
mnemonic_zsevi = @{ "ZSEVI" ~ word_end }
mnemonic_zsni = @{ "ZSNI" ~ word_end }
mnemonic_zszi = @{ "ZSZI" ~ word_end }
mnemonic_zspi = @{ "ZSPI" ~ word_end }
mnemonic_zsnn = @{ "ZSNN" ~ word_end }
mnemonic_zsnz = @{ "ZSNZ" ~ word_end }
mnemonic_zsnp = @{ "ZSNP" ~ word_end }
mnemonic_zsod = @{ "ZSOD" ~ word_end }
mnemonic_zsev = @{ "ZSEV" ~ word_end }
mnemonic_zsn = @{ "ZSN" ~ word_end }
mnemonic_zsz = @{ "ZSZ" ~ word_end }
mnemonic_zsp = @{ "ZSP" ~ word_end }
// Bit fiddling operations - ordered longest first
mnemonic_bdifi = @{ "BDIFI" ~ word_end }
mnemonic_bdif = @{ "BDIF" ~ word_end }
mnemonic_wdifi = @{ "WDIFI" ~ word_end }
mnemonic_wdif = @{ "WDIF" ~ word_end }
mnemonic_tdifi = @{ "TDIFI" ~ word_end }
mnemonic_tdif = @{ "TDIF" ~ word_end }
mnemonic_odifi = @{ "ODIFI" ~ word_end }
mnemonic_odif = @{ "ODIF" ~ word_end }
mnemonic_saddi = @{ "SADDI" ~ word_end }
mnemonic_sadd = @{ "SADD" ~ word_end }
mnemonic_mxori = @{ "MXORI" ~ word_end }
mnemonic_mxor = @{ "MXOR" ~ word_end }
mnemonic_mori = @{ "MORI" ~ word_end }
mnemonic_mor = @{ "MOR" ~ word_end }
// Shift instructions - ordered longest first
mnemonic_slui = @{ "SLUI" ~ word_end }
mnemonic_slu = @{ "SLU" ~ word_end }
mnemonic_sli = @{ "SLI" ~ word_end }
mnemonic_sl = @{ "SL" ~ word_end }
mnemonic_srui = @{ "SRUI" ~ word_end }
mnemonic_sru = @{ "SRU" ~ word_end }
mnemonic_sri = @{ "SRI" ~ word_end }
mnemonic_sr = @{ "SR" ~ word_end }
// Branch/Jump instructions - ordered longest first
mnemonic_pbodb = @{ "PBODB" ~ word_end }
mnemonic_pbnnb = @{ "PBNNB" ~ word_end }
mnemonic_pbnzb = @{ "PBNZB" ~ word_end }
mnemonic_pbnpb = @{ "PBNPB" ~ word_end }
mnemonic_pbevb = @{ "PBEVB" ~ word_end }
mnemonic_pbnb = @{ "PBNB" ~ word_end }
mnemonic_pbzb = @{ "PBZB" ~ word_end }
mnemonic_pbpb = @{ "PBPB" ~ word_end }
mnemonic_pbod = @{ "PBOD" ~ word_end }
mnemonic_pbnn = @{ "PBNN" ~ word_end }
mnemonic_pbnz = @{ "PBNZ" ~ word_end }
mnemonic_pbnp = @{ "PBNP" ~ word_end }
mnemonic_pbev = @{ "PBEV" ~ word_end }
mnemonic_pbn = @{ "PBN" ~ word_end }
mnemonic_pbz = @{ "PBZ" ~ word_end }
mnemonic_pbp = @{ "PBP" ~ word_end }
mnemonic_bodb = @{ "BODB" ~ word_end }
mnemonic_bnnb = @{ "BNNB" ~ word_end }
mnemonic_bnzb = @{ "BNZB" ~ word_end }
mnemonic_bnpb = @{ "BNPB" ~ word_end }
mnemonic_bevb = @{ "BEVB" ~ word_end }
mnemonic_bnb = @{ "BNB" ~ word_end }
mnemonic_bzb = @{ "BZB" ~ word_end }
mnemonic_bpb = @{ "BPB" ~ word_end }
mnemonic_bod = @{ "BOD" ~ word_end }
mnemonic_bnn = @{ "BNN" ~ word_end }
mnemonic_bnz = @{ "BNZ" ~ word_end }
mnemonic_bnp = @{ "BNP" ~ word_end }
mnemonic_bev = @{ "BEV" ~ word_end }
mnemonic_bn = @{ "BN" ~ word_end }
mnemonic_bz = @{ "BZ" ~ word_end }
mnemonic_bp = @{ "BP" ~ word_end }
mnemonic_jmpb = @{ "JMPB" ~ word_end }
mnemonic_jmp = @{ "JMP" ~ word_end }
// Subroutine and control flow instructions
mnemonic_pushj = @{ "PUSHJ" ~ word_end }
mnemonic_pushjb = @{ "PUSHJB" ~ word_end }
mnemonic_pushgo = @{ "PUSHGO" ~ word_end }
mnemonic_pushgoi = @{ "PUSHGOI" ~ word_end }
mnemonic_pop = @{ "POP" ~ word_end }
mnemonic_go = @{ "GO" ~ word_end }
mnemonic_goi = @{ "GOI" ~ word_end }
mnemonic_save = @{ "SAVE" ~ word_end }
mnemonic_unsave = @{ "UNSAVE" ~ word_end }
mnemonic_resume = @{ "RESUME" ~ word_end }
mnemonic_trip = @{ "TRIP" ~ word_end }
mnemonic_swym = @{ "SWYM" ~ word_end }
mnemonic_sync = @{ "SYNC" ~ word_end }
// Special register access
mnemonic_get = @{ "GET" ~ word_end }
mnemonic_put = @{ "PUT" ~ word_end }
mnemonic_puti = @{ "PUTI" ~ word_end }
// Prefetch and cache instructions
mnemonic_prego = @{ "PREGO" ~ word_end }
mnemonic_pregoi = @{ "PREGOI" ~ word_end }
mnemonic_preld = @{ "PRELD" ~ word_end }
mnemonic_preldi = @{ "PRELDI" ~ word_end }
mnemonic_prest = @{ "PREST" ~ word_end }
mnemonic_presti = @{ "PRESTI" ~ word_end }
mnemonic_syncd = @{ "SYNCD" ~ word_end }
mnemonic_syncdi = @{ "SYNCDI" ~ word_end }
mnemonic_syncid = @{ "SYNCID" ~ word_end }
mnemonic_syncidi = @{ "SYNCIDI" ~ word_end }
// Additional load/store instructions
mnemonic_ldunci = @{ "LDUNCI" ~ word_end }
mnemonic_ldunc = @{ "LDUNC" ~ word_end }
mnemonic_stunci = @{ "STUNCI" ~ word_end }
mnemonic_stunc = @{ "STUNC" ~ word_end }
mnemonic_ldhti = @{ "LDHTI" ~ word_end }
mnemonic_ldht = @{ "LDHT" ~ word_end }
mnemonic_sthti = @{ "STHTI" ~ word_end }
mnemonic_stht = @{ "STHT" ~ word_end }
mnemonic_ldsfi = @{ "LDSFI" ~ word_end }
mnemonic_ldsf = @{ "LDSF" ~ word_end }
mnemonic_stsfi = @{ "STSFI" ~ word_end }
mnemonic_stsf = @{ "STSF" ~ word_end }
mnemonic_ldvtsi = @{ "LDVTSI" ~ word_end }
mnemonic_ldvts = @{ "LDVTS" ~ word_end }
mnemonic_cswapi = @{ "CSWAPI" ~ word_end }
mnemonic_cswap = @{ "CSWAP" ~ word_end }
mnemonic_stcoi = @{ "STCOI" ~ word_end }
mnemonic_stco = @{ "STCO" ~ word_end }
// Other instructions
mnemonic_geta = @{ "GETA" ~ word_end }
mnemonic_getab = @{ "GETAB" ~ word_end }
mnemonic_trap = @{ "TRAP" ~ word_end }
mnemonic_halt = @{ "HALT" ~ word_end }
// Instruction operands
comma = _{ "," }
// Every operand is one expression; whether a site demands a register or a
// pure value, and whether a register-or-immediate site auto-selects between
// them, is the evaluator's call, not the grammar's. One rule per operand
// count, reused at every arity the reference gives that many operands.
operand_list_one = { expr }
operand_list_two = { expr ~ comma ~ expr }
operand_list_three = { expr ~ comma ~ expr ~ comma ~ expr }
// Instructions with different operand patterns
inst_set = { mnemonic_set ~ operand_list_two }
inst_seti = { mnemonic_seti ~ operand_list_two }
inst_setl_ri = { mnemonic_setl ~ operand_list_two }
inst_seth_ri = { mnemonic_seth ~ operand_list_two }
inst_setmh_ri = { mnemonic_setmh ~ operand_list_two }
inst_setml_ri = { mnemonic_setml ~ operand_list_two }
inst_incl_ri = { mnemonic_incl ~ operand_list_two }
inst_inch_ri = { mnemonic_inch ~ operand_list_two }
inst_incmh_ri = { mnemonic_incmh ~ operand_list_two }
inst_incml_ri = { mnemonic_incml ~ operand_list_two }
inst_orh_ri = { mnemonic_orh ~ operand_list_two }
inst_ormh_ri = { mnemonic_ormh ~ operand_list_two }
inst_orml_ri = { mnemonic_orml ~ operand_list_two }
inst_orl_ri = { mnemonic_orl ~ operand_list_two }
inst_andnh_ri = { mnemonic_andnh ~ operand_list_two }
inst_andnmh_ri = { mnemonic_andnmh ~ operand_list_two }
inst_andnml_ri = { mnemonic_andnml ~ operand_list_two }
inst_andnl_ri = { mnemonic_andnl ~ operand_list_two }
// Load/Store base mnemonics auto-select RRR/RRI by Z.
// Ordered longest first so prefixes like LDBU win over LDB.
inst_load_store_auto = {
(mnemonic_ldbu | mnemonic_ldb |
mnemonic_ldwu | mnemonic_ldw |
mnemonic_ldtu | mnemonic_ldt |
mnemonic_ldou | mnemonic_ldo |
mnemonic_ldunc | mnemonic_ldvts |
mnemonic_ldht | mnemonic_ldsf |
mnemonic_stbu | mnemonic_stb |
mnemonic_stwu | mnemonic_stw |
mnemonic_sttu | mnemonic_stt |
mnemonic_stou | mnemonic_sto |
mnemonic_stunc | mnemonic_stht |
mnemonic_stsf | mnemonic_cswap) ~
(operand_list_three | operand_list_two)
}
inst_load_store_rri = {
(mnemonic_ldbui | mnemonic_ldbi | mnemonic_ldwui | mnemonic_ldwi |
mnemonic_ldtui | mnemonic_ldti | mnemonic_ldoui | mnemonic_ldoi |
mnemonic_stbui | mnemonic_stbi | mnemonic_stwui | mnemonic_stwi |
mnemonic_sttui | mnemonic_stti | mnemonic_stoui | mnemonic_stoi) ~
operand_list_three
}
// LDA aliases ADDU, so its Z auto-selects the register or immediate opcode.
// The two-operand form LDA $X,addr resolves addr against a preceding GREG
// base, like the memory forms.
inst_lda_auto = { mnemonic_lda ~ operand_list_three }
inst_lda_rri = { mnemonic_ldai ~ operand_list_three }
inst_lda_ri = { (mnemonic_ldai | mnemonic_lda) ~ operand_list_two }
// Arithmetic (3 operands) - base mnemonics auto-select RRR/RRI by Z.
// Ordered longest first so prefixes like ADDU win over ADD.
inst_arith_auto = {
(mnemonic_16addu | mnemonic_8addu | mnemonic_4addu | mnemonic_2addu |
mnemonic_addu | mnemonic_subu |
mnemonic_mulu | mnemonic_divu |
mnemonic_cmpu | mnemonic_add | mnemonic_sub | mnemonic_mul | mnemonic_div | mnemonic_cmp) ~
operand_list_three
}
inst_arith_rri = {
(mnemonic_16addui | mnemonic_8addui | mnemonic_4addui | mnemonic_2addui |
mnemonic_addui | mnemonic_subui | mnemonic_mului | mnemonic_divui | mnemonic_cmpui |
mnemonic_addi | mnemonic_subi | mnemonic_muli | mnemonic_divi | mnemonic_cmpi) ~
operand_list_three
}
// NEG is special: Y is an immediate byte, and Z auto-selects RRR/RRI.
// NEG/NEGU's 2-operand form omits Y (implicitly 0); the 3-operand form
// is tried first so `NEG $1,5,$2` is unaffected.
inst_neg_auto = {
(mnemonic_negu | mnemonic_neg) ~
(operand_list_three | operand_list_two)
}
inst_neg_rri = {
(mnemonic_negui | mnemonic_negi) ~
operand_list_three
}
// Floating point instructions (3 operands) - ordered longest first.
// FCMPE/FEQLE/FUNE's middle operand is rE compared as a real register, and
// FADD/FSUB/FMUL/FDIV/FREM's is a genuine operand — unlike the twelve
// mnemonics below, none of these take a Y rounding-mode override.
inst_float_rrr = {
(mnemonic_fcmpe | mnemonic_feqle | mnemonic_fune |
mnemonic_fcmp | mnemonic_feql | mnemonic_fun |
mnemonic_fadd | mnemonic_fsub | mnemonic_fmul | mnemonic_fdiv |
mnemonic_frem) ~
operand_list_three
}
// FIX/FIXU/FSQRT/FINT take a single register operand plus an optional Y
// rounding-mode override (a pure value, never a register — Y is a mode
// number, not $Y). The 3-operand form is tried first: PEG commits to
// the first alternative that matches, and the shorter 2-operand form would
// otherwise swallow the register-looking Y symbol and strand the comma.
inst_float_round_rrz = {
(mnemonic_fixu | mnemonic_fix | mnemonic_fsqrt | mnemonic_fint) ~
operand_list_three
}
inst_float_round_rr = {
(mnemonic_fixu | mnemonic_fix | mnemonic_fsqrt | mnemonic_fint) ~
operand_list_two
}
// Fixed-to-float conversions: Y is a rounding-mode value, Z auto-selects
// register or immediate. Precedent: inst_neg_auto. 3-operand form first,
// same PEG-commit reasoning as inst_float_round_rrz above.
inst_flot_round = {
(mnemonic_sflotu | mnemonic_sflot |
mnemonic_flotu | mnemonic_flot) ~
operand_list_three
}
inst_flot_auto = {
(mnemonic_sflotu | mnemonic_sflot |
mnemonic_flotu | mnemonic_flot) ~
operand_list_two
}
// FLOTI/FLOTUI/SFLOTI/SFLOTUI: Y is a rounding-mode value; Z stays
// immediate-only. Precedent: inst_neg_rri. 3-operand form first.
inst_float_round_rri = {
(mnemonic_sflotui | mnemonic_sfloti |
mnemonic_flotui | mnemonic_floti) ~
operand_list_three
}
inst_float_rri = {
(mnemonic_sflotui | mnemonic_sfloti |
mnemonic_flotui | mnemonic_floti) ~
operand_list_two
}
// Bitwise operations (3 operands) - base mnemonics auto-select RRR/RRI by Z.
inst_bitwise_auto = {
(mnemonic_andn | mnemonic_nand |
mnemonic_nxor | mnemonic_and |
mnemonic_orn | mnemonic_nor |
mnemonic_or | mnemonic_xor |
mnemonic_mux) ~
operand_list_three
}
inst_bitwise_rri = {
(mnemonic_andni | mnemonic_nandi | mnemonic_nxori | mnemonic_andi |
mnemonic_orni | mnemonic_nori | mnemonic_ori |
mnemonic_xori | mnemonic_muxi) ~
operand_list_three
}
// Bit fiddling operations - base mnemonics auto-select RRR/RRI by Z.
inst_bitfiddle_auto = {
(mnemonic_bdif | mnemonic_wdif | mnemonic_tdif | mnemonic_odif | mnemonic_sadd | mnemonic_mxor | mnemonic_mor) ~
operand_list_three
}
inst_bitfiddle_rri = {
(mnemonic_bdifi | mnemonic_wdifi | mnemonic_tdifi | mnemonic_odifi | mnemonic_saddi | mnemonic_mxori | mnemonic_mori) ~
operand_list_three
}
// Shift instructions - base mnemonics auto-select RRR/RRI by Z.
inst_shift_auto = {
(mnemonic_slu | mnemonic_sru |
mnemonic_sl | mnemonic_sr) ~
operand_list_three
}
inst_shift_rri = {
(mnemonic_slui | mnemonic_srui | mnemonic_sli | mnemonic_sri) ~
operand_list_three
}
// Conditional set instructions - base mnemonics auto-select RRR/RRI by Z.
inst_conditional_set_auto = {
(mnemonic_csnn | mnemonic_csnz | mnemonic_csnp | mnemonic_csod | mnemonic_csev |
mnemonic_csn | mnemonic_csz | mnemonic_csp) ~
operand_list_three
}
inst_conditional_set_rri = {
(mnemonic_csnni | mnemonic_csnzi | mnemonic_csnpi | mnemonic_csodi | mnemonic_csevi |
mnemonic_csni | mnemonic_cszi | mnemonic_cspi) ~
operand_list_three
}
// Zero or set instructions - base mnemonics auto-select RRR/RRI by Z.
inst_zero_or_set_auto = {
(mnemonic_zsnn | mnemonic_zsnz | mnemonic_zsnp | mnemonic_zsod | mnemonic_zsev |
mnemonic_zsn | mnemonic_zsz | mnemonic_zsp) ~
operand_list_three
}
inst_zero_or_set_rri = {
(mnemonic_zsnni | mnemonic_zsnzi | mnemonic_zsnpi | mnemonic_zsodi | mnemonic_zsevi |
mnemonic_zsni | mnemonic_zszi | mnemonic_zspi) ~
operand_list_three
}
// Branch instructions - ordered longest first
inst_branch = {
(mnemonic_bnnb | mnemonic_bnzb | mnemonic_bnpb | mnemonic_bevb |
mnemonic_bnn | mnemonic_bnz | mnemonic_bnp | mnemonic_bev |
mnemonic_bnb | mnemonic_bzb | mnemonic_bpb | mnemonic_bodb |
mnemonic_bn | mnemonic_bz | mnemonic_bp | mnemonic_bod) ~
operand_list_two
}
inst_pbranch = {
(mnemonic_pbnnb | mnemonic_pbnzb | mnemonic_pbnpb | mnemonic_pbevb |
mnemonic_pbodb | mnemonic_pbnb | mnemonic_pbzb | mnemonic_pbpb |
mnemonic_pbod | mnemonic_pbnn | mnemonic_pbnz | mnemonic_pbnp |
mnemonic_pbev | mnemonic_pbn | mnemonic_pbz | mnemonic_pbp) ~
operand_list_two
}
inst_jmp = { (mnemonic_jmpb | mnemonic_jmp) ~ operand_list_one }
// TRAP instruction: TRAP X, Y, Z (where X, Y, Z can be numbers or identifiers)
inst_trap = { mnemonic_trap ~ (operand_list_three | operand_list_two | operand_list_one)? }
// GETA instructions: GETA $X, addr
inst_geta = { mnemonic_geta ~ operand_list_two }
inst_getab = { mnemonic_getab ~ operand_list_two }
// Subroutine instructions
inst_pushj = { mnemonic_pushj ~ operand_list_two }
inst_pushjb = { mnemonic_pushjb ~ operand_list_two }
inst_go_auto = { (mnemonic_pushgo | mnemonic_go) ~ (operand_list_three | operand_list_two) }
inst_pushgo_rri = { mnemonic_pushgoi ~ operand_list_three }
inst_pop = { mnemonic_pop ~ (operand_list_two | operand_list_one)? }
inst_go_rri = { mnemonic_goi ~ operand_list_three }
// Special register access
inst_get = { mnemonic_get ~ operand_list_two }
inst_put_auto = { mnemonic_put ~ operand_list_two }
inst_puti = { mnemonic_puti ~ operand_list_two }
// Context management
inst_save = { mnemonic_save ~ operand_list_two }
// UNSAVE $Z is the one-operand spelling of UNSAVE 0,$Z.
inst_unsave = { mnemonic_unsave ~ (operand_list_two | operand_list_one) }
// Prefetch and cache control
inst_cache_auto = {
(mnemonic_preld | mnemonic_prego | mnemonic_prest |
mnemonic_syncid | mnemonic_syncd) ~
(operand_list_three | operand_list_two)
}
inst_preld_rri = { mnemonic_preldi ~ operand_list_three }
inst_prego_rri = { mnemonic_pregoi ~ operand_list_three }
inst_prest_rri = { mnemonic_presti ~ operand_list_three }
inst_syncd_rri = { mnemonic_syncdi ~ operand_list_three }
inst_syncid_rri = { mnemonic_syncidi ~ operand_list_three }
// Additional load/store
inst_ldunc_rri = { mnemonic_ldunci ~ operand_list_three }
inst_stunc_rri = { mnemonic_stunci ~ operand_list_three }
inst_ldht_rri = { mnemonic_ldhti ~ operand_list_three }
inst_stht_rri = { mnemonic_sthti ~ operand_list_three }
inst_ldsf_rri = { mnemonic_ldsfi ~ operand_list_three }
inst_stsf_rri = { mnemonic_stsfi ~ operand_list_three }
inst_ldvts_rri = { mnemonic_ldvtsi ~ operand_list_three }
inst_cswap_rri = { mnemonic_cswapi ~ operand_list_three }
// STCO's X may be a pure byte or a register, whose number fills the byte;
// only Z auto-selects between the register and immediate opcode.
inst_stco_auto = { mnemonic_stco ~ (operand_list_three | operand_list_two) }
inst_stco_rri = { mnemonic_stcoi ~ operand_list_three }
// System instructions
inst_resume = { mnemonic_resume ~ operand_list_one? }
inst_trip = { mnemonic_trip ~ (operand_list_three | operand_list_two | operand_list_one)? }
inst_swym = { mnemonic_swym ~ (operand_list_three | operand_list_two | operand_list_one)? }
inst_sync = { mnemonic_sync ~ operand_list_one? }
// Simple instructions
inst_halt = { mnemonic_halt }
// Combined instruction rule
// PEG requires longest match first: rrr before rri
instruction = {
inst_seti | inst_set | inst_setl_ri | inst_seth_ri | inst_setmh_ri | inst_setml_ri | inst_incl_ri |
inst_inch_ri | inst_incmh_ri | inst_incml_ri |
inst_orh_ri | inst_ormh_ri | inst_orml_ri | inst_orl_ri |
inst_andnh_ri | inst_andnmh_ri | inst_andnml_ri | inst_andnl_ri |
inst_load_store_auto | inst_load_store_rri |
inst_ldunc_rri | inst_stunc_rri |
inst_ldht_rri | inst_stht_rri |
inst_ldsf_rri | inst_stsf_rri |
inst_ldvts_rri | inst_cswap_rri |
inst_stco_auto | inst_stco_rri |
inst_lda_auto | inst_lda_rri | inst_lda_ri |
inst_arith_auto | inst_arith_rri |
inst_flot_round | inst_flot_auto |
inst_float_round_rrz | inst_float_round_rr |
inst_float_round_rri | inst_float_rri |
inst_float_rrr |
inst_neg_auto | inst_neg_rri |
inst_bitwise_auto | inst_bitwise_rri |
inst_bitfiddle_auto | inst_bitfiddle_rri |
inst_shift_auto | inst_shift_rri |
inst_conditional_set_auto | inst_conditional_set_rri |
inst_zero_or_set_auto | inst_zero_or_set_rri |
inst_pbranch | inst_branch | inst_jmp |
inst_pushj | inst_pushjb | inst_pop |
inst_go_auto | inst_pushgo_rri | inst_go_rri |
inst_cache_auto |
inst_preld_rri | inst_prego_rri | inst_prest_rri |
inst_syncd_rri | inst_syncid_rri |
inst_get | inst_put_auto | inst_puti |
inst_save | inst_unsave |
inst_geta | inst_getab |
inst_resume | inst_trip | inst_swym | inst_sync |
inst_trap | inst_halt
}
// A candidate remark: text past a statement's operand field, up to the next
// statement separator or end of line. Never itself parsed: `parse_two_pass`
// inspects its span against the raw source to tell permitted commentary
// from text mistakable for part of the statement, a distinction a bare rule
// match can't express.
remark = @{ (!(NEWLINE | ";") ~ ANY)* }
// One logical line: `;` separates statements, each followed by whatever
// candidate remark trails its operand field up to the next `;` or newline.
line = { statement? ~ remark ~ (";" ~ statement? ~ remark)* }
// Program is zero or more lines separated by newlines
// This allows blank lines, comment-only lines, and empty files
program = {
SOI ~
NEWLINE* ~ // allow leading blank lines / comment lines
line ~ (NEWLINE+ ~ line)* // lines separated by 1+ newlines
~ NEWLINE* ~ // allow trailing blank lines
EOI
}
NEWLINE = _{ "\n" }