// Boolean expression grammar for lalrpop
// Supports standard boolean operators with proper precedence:
// Parentheses () > NOT (~, !) > AND (*, &) > XOR (^) > OR (+, |)
//
// Each rule builds a reverse-Polish (postfix) `Vec<RpnToken<&'input str>>` bottom-up; the caller
// wraps the top-level program directly into an owned `BoolExpr`. The grammar keeps accepting
// both the `*`/`+`/`~` and the `&`/`|`/`!` spellings, but both lower to the single canonical token set.
// Variable operands stay borrowed `&'input str` here — the grammar is label-agnostic; the caller
// decides what label type to intern them into.
//
// `Token` is imported under the alias `RpnToken` to avoid colliding with lalrpop's own internal
// `Token` lexer type in the generated parser.

use crate::expression::rpn::Token as RpnToken;

grammar;

// Main entry point: the postfix program.
pub Expr: Vec<RpnToken<&'input str>> = {
    <ops:OrExpr> => ops,
};

// OR has lowest precedence (supports both + and |)
OrExpr: Vec<RpnToken<&'input str>> = {
    <mut l:OrExpr> "+" <r:XorExpr> => { l.extend(r); l.push(RpnToken::Or); l },
    <mut l:OrExpr> "|" <r:XorExpr> => { l.extend(r); l.push(RpnToken::Or); l },
    XorExpr,
};

// XOR sits between OR and AND
XorExpr: Vec<RpnToken<&'input str>> = {
    <mut l:XorExpr> "^" <r:AndExpr> => { l.extend(r); l.push(RpnToken::Xor); l },
    AndExpr,
};

// AND has higher precedence (supports both * and &)
AndExpr: Vec<RpnToken<&'input str>> = {
    <mut l:AndExpr> "*" <r:NotExpr> => { l.extend(r); l.push(RpnToken::And); l },
    <mut l:AndExpr> "&" <r:NotExpr> => { l.extend(r); l.push(RpnToken::And); l },
    NotExpr,
};

// NOT has highest precedence
NotExpr: Vec<RpnToken<&'input str>> = {
    "~" <mut e:NotExpr> => { e.push(RpnToken::Not); e },
    "!" <mut e:NotExpr> => { e.push(RpnToken::Not); e },
    Term,
};

// Base terms
Term: Vec<RpnToken<&'input str>> = {
    // Variables (identifiers)
    <v:Ident> => vec![RpnToken::Var(v)],

    // Constants
    "0" => vec![RpnToken::Const(false)],
    "1" => vec![RpnToken::Const(true)],
    "true" => vec![RpnToken::Const(true)],
    "false" => vec![RpnToken::Const(false)],

    // Parenthesized expressions (still a postfix program — only the top-level `Expr` realises one)
    "(" <e:OrExpr> ")" => e,
};

// Lexer rules

// Match identifiers (variable names)
// Standard Rust identifier: starts with letter or underscore,
// followed by letters, digits, or underscores
// Note: Operators must be listed explicitly before identifiers to avoid
// being treated as special regex characters
match {
    // Operators (must come before identifiers)
    "&",
    "|",
    "*",
    "+",
    "^",
    "~",
    "!",
    "(",
    ")",

    // Keywords/constants
    "true",
    "false",
    "0",
    "1",

    // Identifiers (matches variable names)
    r"[a-zA-Z_][a-zA-Z0-9_]*" => Ident,

    // Skip whitespace
    r"\s+" => { },
}
