// Boolean expression grammar for lalrpop
// Supports standard boolean operators with proper precedence:
// Parentheses () > NOT (~, !) > AND (*, &) > XOR (^) > OR (+, |)
//
// Each rule builds a postfix (reverse-Polish) `Vec<Op>` bottom-up; the top-level `Expr` realises it as a
// BoolExpr through a single `BoolExpr::build` (iterative, per-op manager locking — see `build_postfix`).

use crate::expression::builder::{build_postfix, Op};
use crate::expression::BoolExpr;

grammar;

// Main entry point: realise the postfix program into a BoolExpr.
pub Expr: BoolExpr = {
    <ops:OrExpr> => build_postfix(ops),
};

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

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

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

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

// Base terms
Term: Vec<Op> = {
    // Variables (identifiers)
    <v:Ident> => vec![Op::Var(v.to_string())],

    // Constants
    "0" => vec![Op::Const(false)],
    "1" => vec![Op::Const(true)],
    "true" => vec![Op::Const(true)],
    "false" => vec![Op::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+" => { },
}
