use std::collections::BTreeMap;
use crate::{Error, Result, Symbol};
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct PrattOperator {
pub symbol: Symbol,
pub fixity: Fixity,
pub left_bp: u16,
pub right_bp: u16,
pub result: PrattResult,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum Fixity {
Prefix,
InfixLeft,
InfixRight,
Postfix,
Mixfix,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum PrattResult {
ExprInfix,
ExprPrefix,
ExprPostfix,
Call(Symbol),
Custom(Symbol),
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct PrattTable {
prefix: BTreeMap<String, PrattOperator>,
infix: BTreeMap<String, PrattOperator>,
postfix: BTreeMap<String, PrattOperator>,
}
impl PrattTable {
pub fn new() -> Self {
Self::default()
}
pub fn register(&mut self, operator: PrattOperator) {
let key = operator.symbol.to_string();
match operator.fixity {
Fixity::Prefix => {
self.prefix.insert(key, operator);
}
Fixity::InfixLeft | Fixity::InfixRight => {
self.infix.insert(key, operator);
}
Fixity::Postfix => {
self.postfix.insert(key, operator);
}
Fixity::Mixfix => {}
}
}
pub fn lookup_nud(&self, token: &Token) -> Option<PrattOperator> {
self.prefix.get(token.symbol_text()?).cloned()
}
pub fn lookup_led(&self, token: &Token) -> Option<PrattOperator> {
self.postfix
.get(token.symbol_text()?)
.cloned()
.or_else(|| self.infix.get(token.symbol_text()?).cloned())
}
pub fn require_infix(&self, symbol: &Symbol) -> Result<&PrattOperator> {
self.infix
.get(&symbol.to_string())
.ok_or_else(|| Error::Eval(format!("missing infix operator {}", symbol)))
}
pub fn require_prefix(&self, symbol: &Symbol) -> Result<&PrattOperator> {
self.prefix
.get(&symbol.to_string())
.ok_or_else(|| Error::Eval(format!("missing prefix operator {}", symbol)))
}
pub fn require_postfix(&self, symbol: &Symbol) -> Result<&PrattOperator> {
self.postfix
.get(&symbol.to_string())
.ok_or_else(|| Error::Eval(format!("missing postfix operator {}", symbol)))
}
pub fn operators(&self) -> Vec<PrattOperator> {
self.prefix
.values()
.chain(self.infix.values())
.chain(self.postfix.values())
.cloned()
.collect()
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Token {
Ident(String),
Number(String),
String(String),
OpenParen,
CloseParen,
Comma,
Operator(String),
}
impl Token {
pub fn symbol_text(&self) -> Option<&str> {
match self {
Self::Operator(text) | Self::Ident(text) => Some(text.as_str()),
_ => None,
}
}
}
pub fn parse_symbol(raw: &str) -> Symbol {
match raw.split_once('/').or_else(|| raw.split_once('.')) {
Some((namespace, name)) => Symbol::qualified(namespace.to_owned(), name.to_owned()),
None => Symbol::new(raw.to_owned()),
}
}