use crate::ir::{BinaryOp, DType, UnaryOp};

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Span {
    pub start: usize,
    pub end: usize,
}

impl Span {
    pub const fn new(start: usize, end: usize) -> Self {
        Self { start, end }
    }
}

#[derive(Debug, Clone, PartialEq)]
pub struct Spanned<T> {
    pub node: T,
    pub span: Span,
}

impl<T> Spanned<T> {
    pub fn new(node: T, span: Span) -> Self {
        Self { node, span }
    }
}

#[derive(Debug, Clone, PartialEq)]
pub enum Expr {
    LitBool(bool),
    LitInt(i64, Option<DType>),
    LitFloat(f64, Option<DType>),
    LitComplex(f64, f64, Option<DType>),
    ConstE,
    ConstPi,
    Var(String),
    Unary(UnaryOp, Box<Spanned<Expr>>),
    Binary(BinaryOp, Box<Spanned<Expr>>, Box<Spanned<Expr>>),
    IfElse(Box<Spanned<Expr>>, Box<Spanned<Expr>>, Box<Spanned<Expr>>),
    Call(String, Vec<Spanned<Expr>>),
    Cast(DType, Box<Spanned<Expr>>),
    Tuple(Vec<Spanned<Expr>>),
    Let(String, Box<Spanned<Expr>>, Box<Spanned<Expr>>),
}

#[derive(Debug, Clone, PartialEq)]
pub struct FuncDef {
    pub params: Vec<(String, DType)>,
    pub body: Spanned<Expr>,
}
