//! Parseo de expresiones aritméticas (`COMPUTE`) y de condiciones //! (`IF`, `PERFORM UNTIL`). use charka_parser::TokenKind; use crate::ast::{BinOp, CmpOp, Cond, Expr, Operand}; use crate::cursor::{parse_operand, Cursor}; use crate::kw::is_boundary; // ── Expresiones ─────────────────────────────────────────────────── /// Parsea una expresión aritmética con precedencia y paréntesis. pub(crate) fn parse_expr(c: &mut Cursor) -> Expr { parse_bin(c, 0) } /// Trepa por precedencia: `min_prec` es la mínima precedencia que este /// nivel acepta seguir consumiendo. fn parse_bin(c: &mut Cursor, min_prec: u8) -> Expr { let mut lhs = parse_unary(c); while let Some((op, prec, right_assoc)) = peek_binop(c) { if prec < min_prec { break; } c.bump(); let next_min = if right_assoc { prec } else { prec + 1 }; let rhs = parse_bin(c, next_min); lhs = Expr::Binary { op, lhs: Box::new(lhs), rhs: Box::new(rhs), }; } lhs } /// Negación o signo unario delante de un primario. fn parse_unary(c: &mut Cursor) -> Expr { if c.eat_sym("-") { return Expr::Neg(Box::new(parse_unary(c))); } if c.eat_sym("+") { return parse_unary(c); } parse_primary(c) } /// Un primario: un paréntesis o un operando hoja. fn parse_primary(c: &mut Cursor) -> Expr { if c.eat_sym("(") { let e = parse_bin(c, 0); c.eat_sym(")"); return e; } // No consumir un verbo o conector: la expresión terminó. if c.done() || c.peek_word().map(|w| is_boundary(&w)).unwrap_or(false) { return Expr::Operand(Operand::Num("0".into())); } Expr::Operand(parse_operand(c)) } /// El operador binario en el token actual, con su precedencia y si es /// asociativo a derecha. `**` es la única potencia, asociativa a der. fn peek_binop(c: &Cursor) -> Option<(BinOp, u8, bool)> { let t = c.peek()?; if t.kind != TokenKind::Symbol { return None; } match t.text.as_str() { "+" => Some((BinOp::Add, 1, false)), "-" => Some((BinOp::Sub, 1, false)), "*" => Some((BinOp::Mul, 2, false)), "/" => Some((BinOp::Div, 2, false)), "**" => Some((BinOp::Pow, 3, true)), _ => None, } } // ── Condiciones ─────────────────────────────────────────────────── /// Parsea una condición: comparaciones unidas por `AND`/`OR`/`NOT`. pub(crate) fn parse_cond(c: &mut Cursor) -> Cond { parse_or(c) } fn parse_or(c: &mut Cursor) -> Cond { let mut lhs = parse_and(c); while c.eat_word("OR") { let rhs = parse_and(c); lhs = Cond::Or(Box::new(lhs), Box::new(rhs)); } lhs } fn parse_and(c: &mut Cursor) -> Cond { let mut lhs = parse_not(c); while c.eat_word("AND") { let rhs = parse_not(c); lhs = Cond::And(Box::new(lhs), Box::new(rhs)); } lhs } fn parse_not(c: &mut Cursor) -> Cond { if c.eat_word("NOT") { return Cond::Not(Box::new(parse_not(c))); } parse_cond_primary(c) } /// Un primario de condición: un paréntesis, una comparación, o un dato /// suelto (un nombre de condición de nivel 88). fn parse_cond_primary(c: &mut Cursor) -> Cond { if c.eat_sym("(") { let inner = parse_or(c); c.eat_sym(")"); return inner; } if c.done() || c.peek_word().map(|w| is_boundary(&w)).unwrap_or(false) { return Cond::Named(String::new()); } let lhs = parse_operand(c); match parse_cmp_op(c) { Some(op) => { let rhs = parse_operand(c); Cond::Compare { lhs, op, rhs } } None => match lhs { Operand::Data(n) => Cond::Named(n), // Un literal solo como condición es raro; se degrada a "≠ 0". other => Cond::Compare { lhs: other, op: CmpOp::Ne, rhs: Operand::Num("0".into()), }, }, } } /// Lee un operador relacional (forma símbolo o forma palabra). Si no /// hay ninguno, rebobina el cursor y devuelve `None`. fn parse_cmp_op(c: &mut Cursor) -> Option { let save = c.pos; c.eat_word("IS"); let negated = c.eat_word("NOT"); if let Some(op) = cmp_core(c) { return Some(if negated { negate(op) } else { op }); } c.pos = save; None } /// El núcleo del comparador, sin el `IS`/`NOT` opcionales. fn cmp_core(c: &mut Cursor) -> Option { if c.eat_sym("<>") { return Some(CmpOp::Ne); } if c.eat_sym("<=") { return Some(CmpOp::Le); } if c.eat_sym(">=") { return Some(CmpOp::Ge); } if c.eat_sym("=") { return Some(CmpOp::Eq); } if c.eat_sym("<") { return Some(CmpOp::Lt); } if c.eat_sym(">") { return Some(CmpOp::Gt); } if c.eat_word("EQUAL") || c.eat_word("EQUALS") { c.eat_word("TO"); return Some(CmpOp::Eq); } if c.eat_word("GREATER") { c.eat_word("THAN"); if c.eat_word("OR") { c.eat_word("EQUAL"); c.eat_word("TO"); return Some(CmpOp::Ge); } return Some(CmpOp::Gt); } if c.eat_word("LESS") { c.eat_word("THAN"); if c.eat_word("OR") { c.eat_word("EQUAL"); c.eat_word("TO"); return Some(CmpOp::Le); } return Some(CmpOp::Lt); } None } /// El comparador opuesto — para resolver `NOT` delante de un relacional. fn negate(op: CmpOp) -> CmpOp { match op { CmpOp::Eq => CmpOp::Ne, CmpOp::Ne => CmpOp::Eq, CmpOp::Lt => CmpOp::Ge, CmpOp::Ge => CmpOp::Lt, CmpOp::Gt => CmpOp::Le, CmpOp::Le => CmpOp::Gt, } }