use std::fmt::Debug; use crate::func::prelude::*; use super::decoration::Decoration; use super::expr::{Expr, Ident, NamedTuple, Object, Pair, Tuple}; use super::parsing::{FuncArg, FuncArgs, FuncHeader}; use super::span::Spanned; use super::tokens::Token; use super::tree::{DynamicNode, SyntaxNode, SyntaxTree}; pub fn check(src: &str, exp: T, found: T, cmp_spans: bool) where T: Debug + PartialEq + SpanlessEq, { let cmp = if cmp_spans { PartialEq::eq } else { SpanlessEq::spanless_eq }; if !cmp(&exp, &found) { println!("source: {:?}", src); println!("expected: {:#?}", exp); println!("found: {:#?}", found); panic!("test failed"); } } /// Create a vector of optionally spanned expressions from a list description. /// /// # Examples /// ``` /// // With spans /// spanned![(0:0, 0:5, "hello"), (0:5, 0:3, "world")] /// /// // Without spans: Implicit zero spans. /// spanned!["hello", "world"] /// ``` macro_rules! span_vec { ($(($sl:tt:$sc:tt, $el:tt:$ec:tt, $v:expr)),* $(,)?) => { (vec![$(span_item!(($sl:$sc, $el:$ec, $v))),*], true) }; ($($v:expr),* $(,)?) => { (vec![$(span_item!($v)),*], false) }; } macro_rules! span_item { (($sl:tt:$sc:tt, $el:tt:$ec:tt, $v:expr)) => {{ use $crate::syntax::span::{Pos, Span, Spanned}; Spanned { span: Span::new( Pos::new($sl, $sc), Pos::new($el, $ec) ), v: $v } }}; ($v:expr) => { $crate::syntax::span::Spanned::zero($v) }; } pub fn debug_func(call: FuncCall, state: &ParseState) -> Pass { let mut f = Feedback::new(); let node = DebugNode { header: call.header, body: parse_body_maybe(call.body, state, &mut f), }; Pass::node(node, f) } #[derive(Debug, Clone, PartialEq)] pub struct DebugNode { pub header: FuncHeader, pub body: Option, } #[async_trait(?Send)] impl Layout for DebugNode { async fn layout<'a>(&'a self, _: LayoutContext<'_>) -> Pass> { unimplemented!() } } /// Compares elements by only looking at values and ignoring spans. pub trait SpanlessEq { fn spanless_eq(&self, other: &Rhs) -> bool; } impl SpanlessEq for SyntaxNode { fn spanless_eq(&self, other: &Self) -> bool { fn downcast<'a>(func: &'a (dyn DynamicNode + 'static)) -> &'a DebugNode { func.downcast::().expect("not a debug node") } match (self, other) { (Self::Dyn(a), Self::Dyn(b)) => { downcast(a.as_ref()).spanless_eq(downcast(b.as_ref())) } (Self::Par(a), Self::Par(b)) => a.spanless_eq(b), (a, b) => a == b, } } } impl SpanlessEq for DebugNode { fn spanless_eq(&self, other: &Self) -> bool { self.header.spanless_eq(&other.header) && self.body.spanless_eq(&other.body) } } impl SpanlessEq for FuncHeader { fn spanless_eq(&self, other: &Self) -> bool { self.name.spanless_eq(&other.name) && self.args.spanless_eq(&other.args) } } impl SpanlessEq for FuncArgs { fn spanless_eq(&self, other: &Self) -> bool { self.key.spanless_eq(&other.key) && self.pos.spanless_eq(&other.pos) } } impl SpanlessEq for FuncArg { fn spanless_eq(&self, other: &Self) -> bool { match (self, other) { (FuncArg::Pos(a), FuncArg::Pos(b)) => a.spanless_eq(b), (FuncArg::Key(a), FuncArg::Key(b)) => a.spanless_eq(b), _ => false, } } } impl SpanlessEq for Expr { fn spanless_eq(&self, other: &Self) -> bool { match (self, other) { (Expr::Tuple(a), Expr::Tuple(b)) => a.spanless_eq(b), (Expr::NamedTuple(a), Expr::NamedTuple(b)) => a.spanless_eq(b), (Expr::Object(a), Expr::Object(b)) => a.spanless_eq(b), (Expr::Neg(a), Expr::Neg(b)) => a.spanless_eq(&b), (Expr::Add(a1, a2), Expr::Add(b1, b2)) => a1.spanless_eq(&b1) && a2.spanless_eq(&b2), (Expr::Sub(a1, a2), Expr::Sub(b1, b2)) => a1.spanless_eq(&b1) && a2.spanless_eq(&b2), (Expr::Mul(a1, a2), Expr::Mul(b1, b2)) => a1.spanless_eq(&b1) && a2.spanless_eq(&b2), (Expr::Div(a1, a2), Expr::Div(b1, b2)) => a1.spanless_eq(&b1) && a2.spanless_eq(&b2), (a, b) => a == b, } } } impl SpanlessEq for Tuple { fn spanless_eq(&self, other: &Self) -> bool { self.0.spanless_eq(&other.0) } } impl SpanlessEq for NamedTuple { fn spanless_eq(&self, other: &Self) -> bool { self.name.v == other.name.v && self.tuple.v.spanless_eq(&other.tuple.v) } } impl SpanlessEq for Object { fn spanless_eq(&self, other: &Self) -> bool { self.0.spanless_eq(&other.0) } } impl SpanlessEq for Pair { fn spanless_eq(&self, other: &Self) -> bool { self.key.spanless_eq(&other.key) && self.value.spanless_eq(&other.value) } } impl SpanlessEq for Vec { fn spanless_eq(&self, other: &Self) -> bool { self.len() == other.len() && self.iter().zip(other).all(|(x, y)| x.spanless_eq(&y)) } } impl SpanlessEq for Spanned { fn spanless_eq(&self, other: &Self) -> bool { self.v.spanless_eq(&other.v) } } impl SpanlessEq for Box { fn spanless_eq(&self, other: &Self) -> bool { (&**self).spanless_eq(&**other) } } impl SpanlessEq for Option { fn spanless_eq(&self, other: &Self) -> bool { match (self, other) { (Some(a), Some(b)) => a.spanless_eq(b), (None, None) => true, _ => false, } } } macro_rules! impl_through_partial_eq { ($type:ty) => { impl SpanlessEq for $type { fn spanless_eq(&self, other: &$type) -> bool { self == other } } }; } impl_through_partial_eq!(Token<'_>); impl_through_partial_eq!(Ident); // Implement for string and decoration to be able to compare feedback. impl_through_partial_eq!(String); impl_through_partial_eq!(Decoration);