smol lang
Diffstat (limited to 'src/parser/expression.rs')
-rw-r--r--src/parser/expression.rs195
1 files changed, 195 insertions, 0 deletions
diff --git a/src/parser/expression.rs b/src/parser/expression.rs
new file mode 100644
index 0000000..3973f3c
--- /dev/null
+++ b/src/parser/expression.rs
@@ -0,0 +1,195 @@
+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<H: std::hash::Hasher>(&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<Proto<'s>, 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:#?}");
+}