use super::*;
use ast::Ident;
use std::collections::BTreeSet;
use std::collections::BTreeMap;
use once_cell::sync::OnceCell;
use std::sync::Arc;
use std::sync::Mutex;
pub struct Namespace {
    items: BTreeMap<String, Item>,
    idents: Mutex<Vec<Ident>>,
}
pub fn resolve(package: &ast::Package) -> Result<Namespace, Vec<BitsyError>> {
    let mut namespace = Namespace::new();
    for item in order_items(package)? {
        let item = namespace.resolve_item(item)?;
        namespace.add_item(item.name(), item.clone());
    }
    Ok(namespace)
}
impl Namespace {
    fn new() -> Namespace {
        Namespace {
            items: BTreeMap::new(),
            idents: Mutex::new(Vec::new()),
        }
    }
    pub fn items(&self) -> Vec<(String, Item)> {
        self.items.clone().into_iter().collect()
    }
    pub fn idents(&self) -> Vec<Ident> {
        let idents = self.idents.lock().unwrap();
        idents.to_vec()
    }
    fn add_item(&mut self, name: &str, item: Item) {
        let prev = self.items.insert(name.to_string(), item);
        assert!(prev.is_none());
    }
    fn add_ident(&self, ident: &Ident) {
        let mut idents = self.idents.lock().unwrap();
        idents.push(ident.clone());
    }
    fn item(&self, name: &str) -> Option<Item> {
        self.items.get(name).cloned()
    }
    fn moddef(&self, name: &str) -> Option<Arc<Component>> {
        let item = self.item(name);
        if let Some(Item::ModDef(component)) = &item {
            Some(component.clone())
        } else if let Some(Item::ExtDef(component)) = &item {
            Some(component.clone())
        } else {
            None
        }
    }
    fn fndef(&self, name: &str) -> Option<Arc<FnDef>> {
        let item = self.item(name);
        if let Some(Item::FnDef(fndef)) = &item {
            Some(fndef.clone())
        } else {
            None
        }
    }
    fn resolve_item(&self, item: &ast::Item) -> Result<Item, Vec<BitsyError>> {
        Ok(match item {
            ast::Item::ModDef(moddef) => Item::ModDef(self.resolve_moddef(moddef)?),
            ast::Item::ExtDef(moddef) => Item::ExtDef(self.resolve_extmoddef(moddef)?),
            ast::Item::EnumTypeDef(typedef) => Item::EnumTypeDef(self.resolve_enum_typedef(typedef)?),
            ast::Item::StructTypeDef(typedef) => Item::StructTypeDef(self.resolve_struct_typedef(typedef)?),
            ast::Item::AltTypeDef(typedef) => Item::AltTypeDef(self.resolve_alt_typedef(typedef)?),
            ast::Item::FnDef(fndef) => Item::FnDef(self.resolve_fndef(fndef)?),
            ast::Item::TbDef(fndef) => Item::TbDef(self.resolve_tbdef(fndef)?),
        })
    }
    fn resolve_moddef(&self, moddef: &ast::ModDef) -> Result<Arc<Component>, Vec<BitsyError>> {
        let ast::ModDef(span, name, decls) = moddef;
        let decls_slice: &[&ast::Decl] = &decls.iter().collect::<Vec<_>>();
        let (children, wires, whens) = self.resolve_decls(decls_slice)?;
        Ok(Arc::new(Component::Mod(span.clone(), name.to_string(), children, wires, whens)))
    }
    fn resolve_extmoddef(&self, moddef: &ast::ModDef) -> Result<Arc<Component>, Vec<BitsyError>> {
        let ast::ModDef(span, name, decls) = moddef;
        let decls_slice: &[&ast::Decl] = &decls.iter().collect::<Vec<_>>();
        let (children, wires, whens) = self.resolve_decls(decls_slice)?;
        assert!(wires.is_empty());
        assert!(whens.is_empty());
        Ok(Arc::new(Component::Ext(span.clone(), name.to_string(), children)))
    }
    fn resolve_enum_typedef(&self, typedef: &ast::EnumTypeDef) -> Result<Arc<EnumTypeDef>, Vec<BitsyError>> {
        let package_typedef = Arc::new(EnumTypeDef {
            name: typedef.name.to_string(),
            values: typedef.values.iter().map(|(name, val)| (name.to_string(), val.clone())).collect(),
            span: typedef.span.clone(),
        });
        Ok(package_typedef)
    }
    fn resolve_struct_typedef(&self, typedef: &ast::StructTypeDef) -> Result<Arc<StructTypeDef>, Vec<BitsyError>> {
        let mut fields: BTreeMap<String, Type> = BTreeMap::new();
        for (name, typ) in &typedef.fields {
            fields.insert(name.to_string(), self.resolve_type(typ)?);
        }
        let package_typedef = Arc::new(StructTypeDef {
            name: typedef.name.to_string(),
            fields: fields.into_iter().collect(),
            span: typedef.span.clone(),
        });
        Ok(package_typedef)
    }
    fn resolve_alt_typedef(&self, typedef: &ast::AltTypeDef) -> Result<Arc<AltTypeDef>, Vec<BitsyError>> {
        let mut alts: BTreeMap<String, Vec<Type>> = BTreeMap::new();
        for (name, typs) in &typedef.alts {
            let mut alt_types = vec![];
            for typ in typs {
                alt_types.push(self.resolve_type(typ)?);
            }
            alts.insert(name.to_string(), alt_types);
        }
        let package_typedef = Arc::new(AltTypeDef {
            name: typedef.name.to_string(),
            alts: alts.into_iter().collect(),
            span: typedef.span.clone(),
        });
        Ok(package_typedef)
    }
    fn resolve_fndef(&self, fndef: &ast::FnDef) -> Result<Arc<FnDef>, Vec<BitsyError>> {
        let mut type_args: Vec<(String, Kind)> = vec![];
        for (name, kind) in &fndef.type_args {
            type_args.push((name.to_string(), kind.clone()));
        }
        let mut args: Vec<(String, Type)> = vec![];
        for (name, typ) in &fndef.args {
            args.push((name.to_string(), self.resolve_type(typ)?));
        }
        let package_typedef = Arc::new(FnDef {
            span: Span::unknown(),
            name: fndef.name.to_string(),
            type_args: type_args.into_iter().collect(),
            args,
            ret: self.resolve_type(&fndef.ret)?,
            body: self.resolve_expr(&fndef.body, Context::empty())?,
        });
        Ok(package_typedef)
    }
    fn resolve_tbdef(&self, tbdef: &ast::TbDef) -> Result<Arc<TbDef>, Vec<BitsyError>> {
        let mut statements = vec![];
        for statement in &tbdef.statements {
            statements.push(self.resolve_tbstatement(statement)?);
        }
        Ok(Arc::new(TbDef {
            span: tbdef.span.clone(),
            name: tbdef.name.to_string(),
            statements,
        }))
    }
    fn resolve_tbstatement(&self, statement: &ast::TbStatement) -> Result<TbStatement, Vec<BitsyError>> {
        Ok(match statement {
            ast::TbStatement::Debug => TbStatement::Debug,
            ast::TbStatement::Reset => TbStatement::Reset,
            ast::TbStatement::Clock => TbStatement::Clock,
            ast::TbStatement::ModInst(name, moddef_name) => {
                let moddef = self.moddef(moddef_name.as_str()).unwrap();
                TbStatement::ModInst(name.to_string(), moddef)
            }
        })
    }
    fn resolve_type(&self, typ: &ast::Type) -> Result<Type, Vec<BitsyError>> {
        Ok(match typ {
            ast::Type::Word(n) => Type::word(*n),
            ast::Type::Vec(t, n) => Type::vec(self.resolve_type(t)?, *n),
            ast::Type::Valid(t) => Type::valid(self.resolve_type(t)?),
            ast::Type::TypeRef(r, params) => {
                self.add_ident(r);
                let mut resolved_params = vec![];
                for param in params {
                    resolved_params.push(self.resolve_type_param(param)?);
                }
                match self.item(r.as_str()) {
                    Some(Item::EnumTypeDef(typedef)) => Type::Enum(typedef.clone()),
                    Some(Item::StructTypeDef(typedef)) => Type::Struct(typedef.clone()),
                    Some(Item::AltTypeDef(typedef)) => Type::Alt(typedef.clone(), resolved_params),
                    Some(_) => return Err(vec![BitsyError::Unknown(None, format!("Not a type definition: {r}"))]),
                    None => return Err(vec![BitsyError::Unknown(None, format!("Type definition not found: {r}"))]),
                }
            },
        })
    }
    fn resolve_type_param(&self, typ: &ast::TypeParam) -> Result<TypeParam, Vec<BitsyError>> {
        Ok(match typ {
            ast::TypeParam::Nat(n) => TypeParam::Nat(*n),
            ast::TypeParam::Type(typ) => TypeParam::Type(self.resolve_type(typ)?),
        })
    }
    fn resolve_decls(&self, decls: &[&ast::Decl]) -> Result<(Vec<Arc<Component>>, Vec<Wire>, Vec<When>), Vec<BitsyError>> {
        let mut children = vec![];
        let mut wires = vec![];
        let mut whens = vec![];
        for decl in decls {
            match decl {
                ast::Decl::Mod(span, name, decls) => {
                    let (inner_children, wires, whens) = self.resolve_decls(&decls.iter().collect::<Vec<_>>())?;
                    let child = Component::Mod(span.clone(), name.to_string(), inner_children, wires, whens);
                    children.push(Arc::new(child));
                },
                ast::Decl::ModInst(span, name, moddef_name) => {
                    self.add_ident(moddef_name);
                    let moddef = self.moddef(moddef_name.as_str()).unwrap();
                    let child = Component::ModInst(span.clone(), name.to_string(), moddef);
                    children.push(Arc::new(child));
                },
                ast::Decl::Dom(span, name) => {
                    let child = Component::Dom(span.clone(), name.to_string());
                    children.push(Arc::new(child));
                },
                ast::Decl::Incoming(span, name, typ) => {
                    let child = Component::Incoming(span.clone(), name.to_string(), self.resolve_type(typ)?);
                    children.push(Arc::new(child));
                },
                ast::Decl::Outgoing(span, name, typ) => {
                    let child = Component::Outgoing(span.clone(), name.to_string(), self.resolve_type(typ)?);
                    children.push(Arc::new(child));
                },
                ast::Decl::Node(span, name, typ) => {
                    let child = Component::Node(span.clone(), name.to_string(), self.resolve_type(typ)?);
                    children.push(Arc::new(child));
                },
                ast::Decl::Reg(span, name, typ, reset) => {
                    let reset_e = if let Some(e) = reset {
                        Some(self.resolve_expr(&e, Context::empty())?)
                    } else {
                        None
                    };
                    let child = Component::Reg(
                        span.clone(),
                        name.to_string(),
                        self.resolve_type(typ)?,
                        reset_e,
                    );
                    children.push(Arc::new(child));
                },
                ast::Decl::Wire(span, ast::Wire(_loc, target, expr, wire_type)) => {
                    let wire = Wire(
                        span.clone(),
                        target_to_path(target),
                        self.resolve_expr(expr, Context::empty())?,
                        wire_type.clone(),
                    );
                    wires.push(wire);
                },
                ast::Decl::When(_loc, ast::When(_span, cond, wires)) => {
                    let mut package_wires = vec![];
                    let package_cond = self.resolve_expr(&*cond, Context::empty())?;
                    for ast::Wire(span, target, expr, wire_type) in wires {
                        let package_wire = Wire(
                            span.clone(),
                            target_to_path(target),
                            self.resolve_expr(expr, Context::empty())?,
                            wire_type.clone(),
                        );
                        package_wires.push(package_wire);
                    }
                    let package_when = When(package_cond, package_wires);
                    whens.push(package_when);
                },
            }
        }
        Ok((children, wires, whens))
    }
    fn resolve_expr(&self, expr: &ast::Expr, ctx: Context<String, Type>) -> Result<Arc<Expr>, Vec<BitsyError>> {
        Ok(Arc::new(match expr {
            ast::Expr::Ident(span, id) => {
                self.add_ident(id);
                Expr::Reference(span.clone(), OnceCell::new(), id.to_string().into())
            },
            ast::Expr::Dot(span, e, x) => {
                if let ast::Expr::Ident(_loc, id) = &**e {
                    self.add_ident(id);
                    Expr::Reference(span.clone(), OnceCell::new(), format!("{id}.{x}").into())
                } else {
                    panic!()
                }
            },
            ast::Expr::Word(span, w, n) => Expr::Word(span.clone(), OnceCell::new(), *w, *n),
            ast::Expr::Enum(span, typ, value) => {
                Expr::Enum(span.clone(), OnceCell::new(), self.resolve_type(typ)?, value.clone())
            },
            ast::Expr::Struct(span, fields) => {
                let mut package_fields: Vec<(String, Arc<Expr>)> = vec![];
                for (name, expr) in fields {
                    package_fields.push((name.to_string(), self.resolve_expr(expr, ctx.clone())?));
                }
                Expr::Struct(span.clone(), OnceCell::new(), package_fields)
            },
            ast::Expr::Vec(span, es) => {
                let mut package_es: Vec<Arc<Expr>> = vec![];
                for expr in es {
                    package_es.push(self.resolve_expr(expr, ctx.clone())?);
                }
                Expr::Vec(span.clone(), OnceCell::new(), package_es)
            },
            ast::Expr::Call(span, func, type_params, es) => {
                let mut package_es: Vec<Arc<Expr>> = vec![];
                for expr in es {
                    package_es.push(self.resolve_expr(expr, ctx.clone())?);
                }
                match func.as_str() {
                    "cat" => Expr::Cat(span.clone(), OnceCell::new(), package_es),
                    "mux" => Expr::Mux(
                        span.clone(),
                        OnceCell::new(),
                        package_es[0].clone(),
                        package_es[1].clone(),
                        package_es[2].clone(),
                    ),
                    "sext" => Expr::Sext(span.clone(), OnceCell::new(), package_es[0].clone()),
                    "zext" => Expr::Zext(span.clone(), OnceCell::new(), package_es[0].clone()),
                    "trycast" => Expr::TryCast(span.clone(), OnceCell::new(), package_es[0].clone()),
                    "word" => Expr::ToWord(span.clone(), OnceCell::new(), package_es[0].clone()),
                    "@Valid" => Expr::Ctor(span.clone(), OnceCell::new(), "Valid".to_string(), package_es),
                    "@Invalid" => Expr::Ctor(span.clone(), OnceCell::new(), "Invalid".to_string(), vec![]),
                    fnname => {
                        let func = fnname.to_string();
                        if fnname.starts_with("@") {
                            let mut package_es: Vec<Arc<Expr>> = vec![];
                            for expr in es {
                                package_es.push(self.resolve_expr(expr, ctx.clone())?);
                            }
                            Expr::Ctor(span.clone(), OnceCell::new(), fnname[1..].to_string(), package_es)
                        } else if let Some(fndef) = self.fndef(&func) {
                            let mut package_es: Vec<Arc<Expr>> = vec![];
                            for expr in es {
                                package_es.push(self.resolve_expr(expr, ctx.clone())?);
                            }
                            Expr::Call(span.clone(), OnceCell::new(), fndef, package_es)
                        } else {
                            panic!("Unknown call: {func}")
                        }
                    },
                }
            },
            ast::Expr::Let(span, x, type_ascription, e, b) => {
                let package_e = self.resolve_expr(e, ctx.clone())?;
                let package_b = self.resolve_expr(b, ctx)?;
                let package_ascription = if let Some(typ) = type_ascription {
                    Some(self.resolve_type(&typ)?)
                } else {
                    None
                };
                Expr::Let(span.clone(), OnceCell::new(), x.to_string(), package_ascription, package_e, package_b)
            },
            ast::Expr::UnOp(span, op, e1) => {
                Expr::UnOp(span.clone(), OnceCell::new(), *op, self.resolve_expr(&e1, ctx)?)
            },
            ast::Expr::BinOp(span, op, e1, e2) => Expr::BinOp(
                span.clone(),
                OnceCell::new(),
                *op,
                self.resolve_expr(&e1, ctx.clone())?,
                self.resolve_expr(&e2, ctx)?,
            ),
            ast::Expr::If(span, c, e1, e2) => {
                let package_c = self.resolve_expr(c, ctx.clone())?;
                let package_e1 = self.resolve_expr(e1, ctx.clone())?;
                let package_e2 = self.resolve_expr(e2, ctx)?;
                Expr::If(span.clone(), OnceCell::new(), package_c, package_e1, package_e2)
            },
            ast::Expr::Match(span, e, arms) => {
                let package_e = self.resolve_expr(e, ctx.clone())?;
                let mut package_arms: Vec<MatchArm> = vec![];
                for ast::MatchArm(pat, expr) in arms {
                    let package_expr = self.resolve_expr(expr, ctx.clone())?;
                    package_arms.push(MatchArm(pat.clone(), package_expr))
                }
                Expr::Match(span.clone(), OnceCell::new(), package_e, package_arms)
            },
            ast::Expr::IdxField(span, e, field) => Expr::IdxField(
                span.clone(),
                OnceCell::new(),
                self.resolve_expr(&e, ctx)?,
                field.to_string(),
            ),
            ast::Expr::Idx(span, e, i) => {
                Expr::Idx(span.clone(), OnceCell::new(), self.resolve_expr(&e, ctx)?, *i)
            },
            ast::Expr::IdxRange(span, e, j, i) => {
                Expr::IdxRange(span.clone(), OnceCell::new(), self.resolve_expr(&e, ctx)?, *j, *i)
            },
            ast::Expr::Hole(span, name) => {
                Expr::Hole(span.clone(), OnceCell::new(), name.clone().map(|name| name.to_string()))
            },
        }))
    }
}
fn item_dependencies(item: &ast::Item) -> Result<Vec<ast::Ident>, Vec<BitsyError>> {
    match item {
        ast::Item::ModDef(moddef) => moddef_dependencies(moddef),
        ast::Item::ExtDef(moddef) => moddef_dependencies(moddef),
        ast::Item::EnumTypeDef(_typedef) => Ok(Vec::new()),
        ast::Item::StructTypeDef(typedef) => structtypedef_dependencies(typedef),
        ast::Item::AltTypeDef(typedef) => altypedef_dependencies(typedef),
        ast::Item::FnDef(typedef) => fndef_dependencies(typedef),
        ast::Item::TbDef(typedef) => tbdef_dependencies(typedef),
    }
}
fn moddef_dependencies(moddef: &ast::ModDef) -> Result<Vec<ast::Ident>, Vec<BitsyError>> {
    let mut results = vec![];
    let component_names = moddef_component_names(moddef)?;
    let ast::ModDef(_loc, _name, decls) = moddef;
    for decl in decls {
        results.extend(decl_dependencies(decl, &component_names)?.into_iter());
    }
    Ok(results)
}
fn moddef_component_names(moddef: &ast::ModDef) -> Result<BTreeSet<String>, Vec<BitsyError>> {
    let mut result = BTreeSet::new();
    let ast::ModDef(_loc, _name, decls) = moddef;
    for decl in decls {
        match decl {
            ast::Decl::Mod(_loc, name, _decls) => {
                result.insert(name.to_string());
            },
            ast::Decl::ModInst(_loc, name, _moddef_name) => {
                result.insert(name.to_string());
            },
            ast::Decl::Dom(_loc, name) => {
                result.insert(name.to_string());
            },
            ast::Decl::Incoming(_loc, name, _typ) => {
                result.insert(name.to_string());
            },
            ast::Decl::Outgoing(_loc, name, _typ) => {
                result.insert(name.to_string());
            },
            ast::Decl::Node(_loc, name, _typ) => {
                result.insert(name.to_string());
            },
            ast::Decl::Reg(_loc, name, _typ, _reset) => {
                result.insert(name.to_string());
            },
            ast::Decl::Wire(_loc, _wire) => (),
            ast::Decl::When(_loc, _when) => (),
        }
    }
    Ok(result)
}
fn moddef_component_names_anonymous(decls: &[ast::Decl]) -> Result<BTreeSet<String>, Vec<BitsyError>> {
    let mut result = BTreeSet::new();
    for decl in decls {
        match decl {
            ast::Decl::Mod(_loc, name, _decls) => {
                result.insert(name.to_string());
            },
            ast::Decl::ModInst(_loc, name, _moddef_name) => {
                result.insert(name.to_string());
            },
            ast::Decl::Dom(_loc, name) => {
                result.insert(name.to_string());
            },
            ast::Decl::Incoming(_loc, name, _typ) => {
                result.insert(name.to_string());
            },
            ast::Decl::Outgoing(_loc, name, _typ) => {
                result.insert(name.to_string());
            },
            ast::Decl::Node(_loc, name, _typ) => {
                result.insert(name.to_string());
            },
            ast::Decl::Reg(_loc, name, _typ, _reset) => {
                result.insert(name.to_string());
            },
            ast::Decl::Wire(_loc, _wire) => (),
            ast::Decl::When(_loc, _when) => (),
        }
    }
    Ok(result)
}
fn decl_dependencies(decl: &ast::Decl, component_names: &BTreeSet<String>) -> Result<Vec<ast::Ident>, Vec<BitsyError>> {
    let mut results = vec![];
    match decl {
        ast::Decl::Mod(_loc, _name, decls) => {
            let component_names = moddef_component_names_anonymous(&*decls)?;
            for decl in decls {
                results.extend(decl_dependencies(decl, &component_names)?.into_iter());
            }
        },
        ast::Decl::ModInst(_loc, _name, moddef_name) => results.push(moddef_name.clone()),
        ast::Decl::Dom(_loc, _name) => (),
        ast::Decl::Incoming(_loc, _name, typ) => results.extend(type_dependencies(typ)?),
        ast::Decl::Outgoing(_loc, _name, typ) => results.extend(type_dependencies(typ)?),
        ast::Decl::Node(_loc, _name, typ) => results.extend(type_dependencies(typ)?),
        ast::Decl::Reg(_loc, _name, typ, reset) => {
            results.extend(type_dependencies(typ)?.into_iter());
            if let Some(expr) = reset {
                results.extend(expr_dependencies(expr, component_names)?.into_iter());
            }
        },
        ast::Decl::Wire(_loc, wire) => {
            let ast::Wire(_loc2, _target, expr, _wire_type) = wire;
            results.extend(expr_dependencies(expr, component_names)?.into_iter())
        },
        ast::Decl::When(_loc, ast::When(_span, cond, wires)) => {
            results.extend(expr_dependencies(cond, component_names)?.into_iter());
            for ast::Wire(_loc2, _target, expr, _wire_type) in wires {
                results.extend(expr_dependencies(expr, component_names)?.into_iter())
            }
        },
    }
    Ok(results.into_iter().collect())
}
fn structtypedef_dependencies(typedef: &ast::StructTypeDef) -> Result<Vec<ast::Ident>, Vec<BitsyError>> {
    let mut results = Vec::new();
    for (_name, typ) in &typedef.fields {
        results.extend(type_dependencies(typ)?.into_iter());
    }
    Ok(results)
}
fn altypedef_dependencies(typedef: &ast::AltTypeDef) -> Result<Vec<ast::Ident>, Vec<BitsyError>> {
    let mut deps = vec![];
    for (_name, typs) in &typedef.alts {
        for typ in typs {
            deps.extend(type_dependencies(typ)?.into_iter())
        }
    }
    Ok(deps.into_iter().collect())
}
fn fndef_dependencies(typedef: &ast::FnDef) -> Result<Vec<ast::Ident>, Vec<BitsyError>> {
    let mut result = type_dependencies(&typedef.ret)?;
    let mut arguments = BTreeSet::new();
    for (name, typ) in &typedef.args {
        arguments.insert(name.to_string());
        result.extend(type_dependencies(typ)?.into_iter());
    }
    result.extend(expr_dependencies(&typedef.body, &arguments)?.into_iter());
    Ok(result)
}
fn tbdef_dependencies(tbdef: &ast::TbDef) -> Result<Vec<ast::Ident>, Vec<BitsyError>> {
    let mut results = vec![];
    for statement in &tbdef.statements {
        match statement {
            ast::TbStatement::ModInst(_name, moddef_name) => results.push(moddef_name.clone()),
            _ => (),
        }
    }
    Ok(results)
}
fn type_dependencies(typ: &ast::Type) -> Result<Vec<ast::Ident>, Vec<BitsyError>> {
    match typ {
        ast::Type::Word(_n) => Ok(Vec::new()),
        ast::Type::Vec(t, _n) => type_dependencies(t),
        ast::Type::Valid(t) => type_dependencies(t),
        ast::Type::TypeRef(r, _params) => Ok(vec![r.clone()]),
    }
}
fn expr_dependencies(expr: &ast::Expr, shadowed: &BTreeSet<String>) -> Result<Vec<ast::Ident>, Vec<BitsyError>> {
    Ok(match expr {
        ast::Expr::Ident(_loc, ident) => {
            if shadowed.contains(&ident.name) {
                Vec::new()
            } else {
                vec![ident.clone()]
            }
        },
        ast::Expr::Dot(_loc, e, _x) => expr_dependencies(e, shadowed)?,
        ast::Expr::Word(_loc, _w, _v) => Vec::new(),
        ast::Expr::Enum(_loc, typ, _value) => type_dependencies(typ)?,
        ast::Expr::Struct(_loc, _fields) => Vec::new(),
        ast::Expr::Vec(_loc, es) => {
            let mut results = Vec::new();
            for e in es {
                results.extend(expr_dependencies(e, shadowed)?.into_iter());
            }
            results
        },
        ast::Expr::Call(_loc, func, type_params, es) => {
            let mut results = Vec::new();
            #[rustfmt::skip]
            const SPECIALS: &[&str] = &[
                "cat",
                "mux",
                "sext",
                "zext",
                "trycast",
                "word",
                "@Valid",
                "@Invalid",
            ];
            if !SPECIALS.contains(&func.as_str()) && !func.as_str().starts_with("@") {
                results.push(func.clone());
            }
            for e in es {
                results.extend(expr_dependencies(e, shadowed)?.into_iter());
            }
            results
        },
        ast::Expr::Let(_loc, x, type_ascription, e, b) => {
            let mut new_shadowed = shadowed.clone();
            new_shadowed.insert(x.to_string());
            let mut results = Vec::new();
            if let Some(typ) = type_ascription {
                results.extend(type_dependencies(typ)?.into_iter());
            }
            results.extend(expr_dependencies(e, shadowed)?.into_iter());
            results.extend(expr_dependencies(b, &new_shadowed)?.into_iter());
            results
        },
        ast::Expr::UnOp(_loc, _op, e1) => expr_dependencies(e1, shadowed)?,
        ast::Expr::BinOp(_loc, _op, e1, e2) => {
            let mut results = Vec::new();
            for e in &[e1, e2] {
                results.extend(expr_dependencies(e, shadowed)?.into_iter());
            }
            results
        },
        ast::Expr::If(_loc, c, e1, e2) => {
            let mut results = Vec::new();
            for e in &[c, e1, e2] {
                results.extend(expr_dependencies(e, shadowed)?.into_iter());
            }
            results
        },
        ast::Expr::Match(_loc, e, arms) => {
            let mut results = Vec::new();
            results.extend(expr_dependencies(e, shadowed)?.into_iter());
            for ast::MatchArm(pat, e) in arms {
                let mut new_shadowed = shadowed.clone();
                new_shadowed.extend(pat.bound_vars().into_iter());
                results.extend(expr_dependencies(e, &new_shadowed)?.into_iter());
            }
            results
        },
        ast::Expr::IdxField(_loc, e, _field) => expr_dependencies(e, shadowed)?,
        ast::Expr::Idx(_loc, e, _i) => expr_dependencies(e, shadowed)?,
        ast::Expr::IdxRange(_loc, e, _j, _i) => expr_dependencies(e, shadowed)?,
        ast::Expr::Hole(_loc, _name) => Vec::new(),
    })
}
fn target_to_path(target: &ast::Target) -> Path {
    match target {
        ast::Target::Local(id) => id.as_str().into(),
        ast::Target::Nonlocal(id1, id2) => format!("{}.{}", id1.as_str(), id2.as_str()).into(),
    }
}
fn order_items(package: &ast::Package) -> Result<Vec<&ast::Item>, Vec<BitsyError>> {
    use petgraph::graph::{DiGraph, NodeIndex};
    use petgraph::algo::toposort;
    let mut items: BTreeMap<String, (NodeIndex, &ast::Item)> = BTreeMap::new();
    let mut name_by_node: BTreeMap<NodeIndex, String> = BTreeMap::new();
    let mut graph = DiGraph::new();
    for item in &package.items {
        let node = graph.add_node(item.name().to_string());
        items.insert(item.name().to_string(), (node, item));
        name_by_node.insert(node, item.name().to_string());
    }
    let mut errors = vec![];
    for item in &package.items {
        let (node, _item) = items[item.name()];
        items.insert(item.name().to_string(), (node, item));
        for item_dependency in item_dependencies(item)? {
            if let Some((dependency, _item)) = items.get(&item_dependency.name) {
                graph.add_edge(node, *dependency, ());
            } else {
                errors.push(BitsyError::Unknown(Some(item_dependency.span.clone()), format!("{item_dependency} not found")));
            }
        }
    }
    if !errors.is_empty() {
        return Err(errors);
    }
    let mut sorted: Vec<NodeIndex> = toposort(&graph, None).unwrap();
    sorted.reverse();
    let mut results = vec![];
    for node in sorted {
        let name = &name_by_node[&node];
        let (_node, item) = items[name];
        results.push(item);
    }
    Ok(results)
}