use super::*; use chumsky::prelude::*; use match_deref::match_deref; use std::collections::{hash_map::Entry, HashMap}; #[derive(Eq, Clone, Copy, Debug)] pub struct Proto<'s> { name: &'s str, prec: Precedence, } impl<'s> Proto<'s> { pub fn new(name: &'s str) -> Proto<'s> { Proto { name, prec: Precedence::Ambigous, } } pub fn then(self, prec: Precedence) -> Proto<'s> { Self { prec, ..self } } } impl std::hash::Hash for Proto<'_> { fn hash(&self, state: &mut H) { self.name.hash(state); } } impl PartialEq for Proto<'_> { fn eq(&self, other: &Self) -> bool { self.name == other.name } } #[derive(Copy, Clone, Debug, Eq, PartialEq)] enum Precedence { Ambigous, Prefix(u16), Postfix(u16), Infix(u16), } #[derive(Debug, Clone)] pub struct Context<'s> { precedence: HashMap, Vec<&'s str>>, } #[derive(thiserror::Error, Debug, Clone)] pub enum Err<'s> { #[error("unable to find function {0}. are you sure it exists?")] CannotFind(Spanned<&'s str>), #[error("problem.")] Mismatch(Spanned<(Precedence, Fix)>), #[error("{0} != 2")] InfixWithNot2(u8), #[error("you cant bind higher than top")] TtTop(Span), #[error("you cant bind lower than bottom")] LtBot(Span), } /// precedence of "function" defined as (infix) tt bottom (postfix) tt bottom (prefix) lt top impl<'s> Context<'s> { fn add(&mut self, dat: FixMeta, proto: Proto<'s>) -> Result<(), Err> { use Fix::*; use Precedence::*; if dat.fix() == In && proto.argc != 2 { return Err(Err::InfixWithNot2(*Spanned::from((proto.argc, dat.span())))); } if let Some(n) = match_deref! { match &dat { FixMeta::Data(Deref @ FixMetaData { looser_than: Some(Deref @ &"top"), .. }) =>Some( 0xfffd), FixMeta::Default(Deref @ Pre) =>Some( 0xfffe), _ => None, } } { let value = match dat.fix() { In => Infix(n), Pre => Prefix(n), Post => Postfix(n), }; match self.precedence.entry(proto) { Entry::Occupied(x) => { x.into_mut().push(proto.then(value)); } Entry::Vacant(x) => { x.insert(vec![]); } }; return Ok(()); } if let Some(x) = tt && *x == "top" { return Err(Err::TtTop(s)); } // TODO graphs if let Some(lt) = lt { let &position = self .precedence .get(&Proto { name: &*lt, argc: 0, }) .ok_or(Err::CannotFind(lt))?; let value = match (position, fix) { (Infix(x), In) => Infix(x - 1), (Prefix(x), Pre) => Prefix(x - 1), (Postfix(x), Post) => Postfix(x - 1), (x, y) => return Err(Err::Mismatch(lt.copys((x, y)))), }; self.precedence.insert(proto, value); } if let Some(lt) = tt { let &position = self .precedence .get(&Proto { name: &*lt, argc: 0, }) .ok_or(Err::CannotFind(lt))?; let value = match (position, fix) { (Infix(x), In) => Infix(x - 1), (Prefix(x), Pre) => Prefix(x - 1), (Postfix(x), Post) => Postfix(x - 1), (x, y) => return Err(Err::Mismatch(lt.copys((x, y)))), }; self.precedence.insert(proto, value); } if let Some(gt) = tt { let &position = self .precedence .get(&Proto { name: &*gt, argc: 0, }) .ok_or(Err::CannotFind(gt))?; let value = match (position, fix) { (Infix(x), In) => Infix(x + 1), (Prefix(x), Pre) => Prefix(x + 1), (Postfix(x), Post) => Postfix(x + 1), (x, y) => return Err(Err::Mismatch(gt.copys((x, y)))), }; self.precedence.insert(proto, value); } Ok(()) } } #[test] fn hmm() { let mut hm = std::collections::HashMap::new(); hm.insert(Proto { name: "*", argc: 2 }, 32768 - 1); hm.insert(Proto { name: "/", argc: 2 }, 32768 - 1); hm.insert(Proto { name: "+", argc: 2 }, 32768 - 2); hm.insert(Proto { name: "-", argc: 2 }, 32768 - 2); let mut c = Context { precedence: HashMap::default(), }; c.add( Proto { name: "*", argc: 2 }, None, Some(Spanned::dummy("top")), Fix::In, (0..0).into(), ) .unwrap(); c.add( Proto { name: "/", argc: 2 }, None, Some(Spanned::dummy("top")), Fix::In, (0..0).into(), ) .unwrap(); println!("{c:#?}"); c.add( Proto { name: "+", argc: 2 }, None, Some(Spanned::dummy("*")), Fix::In, (0..0).into(), ) .unwrap(); c.add( Proto { name: "-", argc: 2 }, None, Some(Spanned::dummy("*")), Fix::In, (0..0).into(), ) .unwrap(); panic!("{c:#?}"); }