use crate::pure_rust_parser::AstNode;
use crate::pure_rust_parser::Rule;
use pest::iterators::Pair;
use std::collections::HashMap;
use std::sync::Arc;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct Precedence(pub u8);
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Associativity {
Left,
Right,
None,
}
pub struct OpInfo {
pub precedence: Precedence,
pub associativity: Associativity,
}
pub struct PrattParser {
operators: HashMap<&'static str, OpInfo>,
}
impl Default for PrattParser {
fn default() -> Self {
Self::new()
}
}
impl PrattParser {
pub fn new() -> Self {
let mut operators = HashMap::new();
operators
.insert(",", OpInfo { precedence: Precedence(1), associativity: Associativity::Left });
operators
.insert("=>", OpInfo { precedence: Precedence(1), associativity: Associativity::Left });
operators
.insert("=", OpInfo { precedence: Precedence(3), associativity: Associativity::Right });
operators.insert(
"+=",
OpInfo { precedence: Precedence(3), associativity: Associativity::Right },
);
operators.insert(
"-=",
OpInfo { precedence: Precedence(3), associativity: Associativity::Right },
);
operators.insert(
"*=",
OpInfo { precedence: Precedence(3), associativity: Associativity::Right },
);
operators.insert(
"/=",
OpInfo { precedence: Precedence(3), associativity: Associativity::Right },
);
operators.insert(
"%=",
OpInfo { precedence: Precedence(3), associativity: Associativity::Right },
);
operators.insert(
"**=",
OpInfo { precedence: Precedence(3), associativity: Associativity::Right },
);
operators.insert(
"&=",
OpInfo { precedence: Precedence(3), associativity: Associativity::Right },
);
operators.insert(
"|=",
OpInfo { precedence: Precedence(3), associativity: Associativity::Right },
);
operators.insert(
"^=",
OpInfo { precedence: Precedence(3), associativity: Associativity::Right },
);
operators.insert(
"&.=",
OpInfo { precedence: Precedence(3), associativity: Associativity::Right },
);
operators.insert(
"|.=",
OpInfo { precedence: Precedence(3), associativity: Associativity::Right },
);
operators.insert(
"^.=",
OpInfo { precedence: Precedence(3), associativity: Associativity::Right },
);
operators.insert(
"<<=",
OpInfo { precedence: Precedence(3), associativity: Associativity::Right },
);
operators.insert(
">>=",
OpInfo { precedence: Precedence(3), associativity: Associativity::Right },
);
operators.insert(
".=",
OpInfo { precedence: Precedence(3), associativity: Associativity::Right },
);
operators.insert(
"//=",
OpInfo { precedence: Precedence(3), associativity: Associativity::Right },
);
operators.insert(
"&&=",
OpInfo { precedence: Precedence(3), associativity: Associativity::Right },
);
operators.insert(
"||=",
OpInfo { precedence: Precedence(3), associativity: Associativity::Right },
);
operators
.insert("?", OpInfo { precedence: Precedence(4), associativity: Associativity::Right });
operators
.insert(":", OpInfo { precedence: Precedence(4), associativity: Associativity::Right });
operators
.insert("..", OpInfo { precedence: Precedence(5), associativity: Associativity::None });
operators.insert(
"...",
OpInfo { precedence: Precedence(5), associativity: Associativity::None },
);
operators
.insert("||", OpInfo { precedence: Precedence(6), associativity: Associativity::Left });
operators
.insert("//", OpInfo { precedence: Precedence(7), associativity: Associativity::Left });
operators
.insert("&&", OpInfo { precedence: Precedence(8), associativity: Associativity::Left });
operators
.insert("or", OpInfo { precedence: Precedence(9), associativity: Associativity::Left });
operators.insert(
"xor",
OpInfo { precedence: Precedence(9), associativity: Associativity::Left },
);
operators.insert(
"and",
OpInfo { precedence: Precedence(10), associativity: Associativity::Left },
);
operators.insert(
"not",
OpInfo { precedence: Precedence(11), associativity: Associativity::Right },
);
operators
.insert("<", OpInfo { precedence: Precedence(14), associativity: Associativity::None });
operators
.insert(">", OpInfo { precedence: Precedence(14), associativity: Associativity::None });
operators.insert(
"<=",
OpInfo { precedence: Precedence(14), associativity: Associativity::None },
);
operators.insert(
">=",
OpInfo { precedence: Precedence(14), associativity: Associativity::None },
);
operators.insert(
"lt",
OpInfo { precedence: Precedence(14), associativity: Associativity::None },
);
operators.insert(
"gt",
OpInfo { precedence: Precedence(14), associativity: Associativity::None },
);
operators.insert(
"le",
OpInfo { precedence: Precedence(14), associativity: Associativity::None },
);
operators.insert(
"ge",
OpInfo { precedence: Precedence(14), associativity: Associativity::None },
);
operators.insert(
"==",
OpInfo { precedence: Precedence(15), associativity: Associativity::None },
);
operators.insert(
"!=",
OpInfo { precedence: Precedence(15), associativity: Associativity::None },
);
operators.insert(
"<=>",
OpInfo { precedence: Precedence(15), associativity: Associativity::None },
);
operators.insert(
"eq",
OpInfo { precedence: Precedence(15), associativity: Associativity::None },
);
operators.insert(
"ne",
OpInfo { precedence: Precedence(15), associativity: Associativity::None },
);
operators.insert(
"cmp",
OpInfo { precedence: Precedence(15), associativity: Associativity::None },
);
operators.insert(
"~~",
OpInfo { precedence: Precedence(15), associativity: Associativity::None },
);
operators.insert(
"isa",
OpInfo { precedence: Precedence(16), associativity: Associativity::None },
);
operators
.insert("&", OpInfo { precedence: Precedence(17), associativity: Associativity::Left });
operators.insert(
"&.",
OpInfo { precedence: Precedence(17), associativity: Associativity::Left },
);
operators
.insert("|", OpInfo { precedence: Precedence(18), associativity: Associativity::Left });
operators
.insert("^", OpInfo { precedence: Precedence(18), associativity: Associativity::Left });
operators.insert(
"|.",
OpInfo { precedence: Precedence(18), associativity: Associativity::Left },
);
operators.insert(
"^.",
OpInfo { precedence: Precedence(18), associativity: Associativity::Left },
);
operators
.insert("+", OpInfo { precedence: Precedence(22), associativity: Associativity::Left });
operators
.insert("-", OpInfo { precedence: Precedence(22), associativity: Associativity::Left });
operators
.insert(".", OpInfo { precedence: Precedence(22), associativity: Associativity::Left });
operators
.insert("*", OpInfo { precedence: Precedence(23), associativity: Associativity::Left });
operators
.insert("/", OpInfo { precedence: Precedence(23), associativity: Associativity::Left });
operators
.insert("%", OpInfo { precedence: Precedence(23), associativity: Associativity::Left });
operators
.insert("x", OpInfo { precedence: Precedence(23), associativity: Associativity::Left });
operators.insert(
"<<",
OpInfo { precedence: Precedence(24), associativity: Associativity::Left },
);
operators.insert(
">>",
OpInfo { precedence: Precedence(24), associativity: Associativity::Left },
);
operators.insert(
"~",
OpInfo { precedence: Precedence(26), associativity: Associativity::Right },
);
operators.insert(
"~.",
OpInfo { precedence: Precedence(26), associativity: Associativity::Right },
);
operators.insert(
"**",
OpInfo { precedence: Precedence(28), associativity: Associativity::Right },
);
operators.insert(
"=~",
OpInfo { precedence: Precedence(29), associativity: Associativity::Left },
);
operators.insert(
"!~",
OpInfo { precedence: Precedence(29), associativity: Associativity::Left },
);
PrattParser { operators }
}
pub fn get_operator_info(&self, op: &str) -> Option<&OpInfo> {
self.operators.get(op)
}
pub fn is_prefix_operator(op: &str) -> bool {
matches!(
op,
"!" | "not"
| "~"
| "~."
| "+"
| "-"
| "++"
| "--"
| "\\"
| "defined"
| "undef"
| "scalar"
| "my"
| "our"
| "local"
| "state"
)
}
pub fn is_postfix_operator(op: &str) -> bool {
matches!(op, "++" | "--")
}
pub fn parse_expression_from_pairs<'a>(
&self,
pairs: Vec<Pair<'a, Rule>>,
parser: &mut crate::pure_rust_parser::PureRustPerlParser,
) -> Result<AstNode, Box<dyn std::error::Error>> {
if pairs.is_empty() {
return Err("Empty expression".into());
}
if pairs.len() == 1 {
parser
.build_node(pairs.into_iter().next().ok_or(crate::error::ParseError::ParseFailed)?)?
.ok_or_else(|| "Failed to parse".into())
} else if pairs.len() >= 3 {
self.parse_binary_expr(pairs, 0, parser)
} else {
parser
.build_node(pairs.into_iter().next().ok_or(crate::error::ParseError::ParseFailed)?)?
.ok_or_else(|| "Failed to parse".into())
}
}
fn parse_binary_expr(
&self,
pairs: Vec<Pair<'_, Rule>>,
index: usize,
parser: &mut crate::pure_rust_parser::PureRustPerlParser,
) -> Result<AstNode, Box<dyn std::error::Error>> {
if index >= pairs.len() {
return Err("Invalid expression".into());
}
let mut left =
parser.build_node(pairs[index].clone())?.ok_or("Failed to parse left operand")?;
let mut i = index + 1;
while i + 1 < pairs.len() {
let op = pairs[i].as_str();
if let Some(_op_info) = self.get_operator_info(op) {
let right = parser
.build_node(pairs[i + 1].clone())?
.ok_or("Failed to parse right operand")?;
left = AstNode::BinaryOp {
op: Arc::from(op),
left: Box::new(left),
right: Box::new(right),
};
i += 2;
} else {
break;
}
}
Ok(left)
}
}