use std::collections::HashMap;
use codespan_reporting::diagnostic::Diagnostic;
use arret_syntax::datum::Datum;
use arret_syntax::span::{FileId, Span};
use crate::ty;
use crate::ty::purity;
use crate::CompileCtx;
use crate::context::ModuleImports;
use crate::hir::destruc;
use crate::hir::error::{Error, ErrorKind, ExpectedSym, Result};
use crate::hir::exports::Exports;
use crate::hir::import;
use crate::hir::macros::{expand_macro, lower_macro_rules};
use crate::hir::ns::{Ident, NsDataIter, NsDatum};
use crate::hir::prim::Prim;
use crate::hir::records::lower_record;
use crate::hir::scope::{Binding, Scope};
use crate::hir::types::{lower_poly, lower_polymorphic_var_set, try_lower_purity};
use crate::hir::util::{expect_one_arg, expect_spanned_ns_ident, try_take_rest_arg};
use crate::hir::var_id::{ExportId, LocalIdAlloc};
use crate::hir::Lowered;
use crate::hir::{
    App, Cond, DeclPurity, DeclTy, Def, Expr, ExprKind, FieldAccessor, Fun, Let, LocalId, Recur,
};
#[cfg(test)]
use crate::source::EMPTY_SPAN;
pub struct LoweredModule {
    
    pub defs: Vec<Def<Lowered>>,
    pub exports: Exports,
    pub main_local_id: Option<LocalId>,
}
struct DeferredDef {
    span: Span,
    macro_invocation_span: Option<Span>,
    destruc: destruc::Destruc<Lowered>,
    value_datum: NsDatum,
}
struct DeferredExport {
    span: Span,
    ident: Ident,
}
enum DeferredModulePrim {
    Def(DeferredDef),
    Exports(Vec<DeferredExport>),
}
impl DeferredModulePrim {
    fn with_macro_invocation_span(self, span: Span) -> DeferredModulePrim {
        match self {
            DeferredModulePrim::Def(deferred_def) => DeferredModulePrim::Def(DeferredDef {
                macro_invocation_span: Some(span),
                ..deferred_def
            }),
            other => other,
        }
    }
}
pub(crate) enum LoweredReplDatum {
    
    Import(ModuleImports),
    
    EvaluableDef(Def<Lowered>),
    
    NonEvaluableDef,
    
    Expr(Expr<Lowered>),
}
fn lower_user_compile_error(span: Span, arg_iter: NsDataIter) -> Error {
    match expect_one_arg(span, arg_iter) {
        Ok(NsDatum::Str(_, user_message)) => Error::new(span, ErrorKind::UserError(user_message)),
        Ok(other) => Error::new(
            other.span(),
            ErrorKind::ExpectedCompileErrorString(other.description()),
        ),
        Err(error) => error,
    }
}
fn lower_macro(
    scope: &mut Scope<'_>,
    self_datum: NsDatum,
    transformer_spec: NsDatum,
) -> Result<()> {
    let (self_span, self_ident) = expect_spanned_ns_ident(self_datum, "new macro name")?;
    let macro_rules_data = if let NsDatum::List(span, vs) = transformer_spec {
        let mut transformer_data = vs.into_vec();
        let macro_type_datum = if let Some(macro_type_datum) = transformer_data.first() {
            macro_type_datum
        } else {
            return Err(Error::new(span, ErrorKind::NoMacroType));
        };
        if let Some(Binding::Prim(Prim::MacroRules)) = scope.get_datum(macro_type_datum) {
        } else {
            return Err(Error::new(macro_type_datum.span(), ErrorKind::BadMacroType));
        }
        transformer_data.remove(0);
        transformer_data
    } else {
        return Err(Error::new(
            transformer_spec.span(),
            ErrorKind::ExpectedMacroSpecList(transformer_spec.description()),
        ));
    };
    let mac = lower_macro_rules(scope, &self_ident, macro_rules_data)?;
    scope.insert_binding(self_span, self_ident, Binding::Macro(None, mac))?;
    Ok(())
}
fn lower_defmacro(scope: &mut Scope<'_>, span: Span, mut arg_iter: NsDataIter) -> Result<()> {
    if arg_iter.len() != 2 {
        return Err(Error::new(
            span,
            ErrorKind::WrongDefLikeArgCount("defmacro"),
        ));
    }
    let self_datum = arg_iter.next().unwrap();
    let transformer_spec = arg_iter.next().unwrap();
    lower_macro(scope, self_datum, transformer_spec)
}
fn lower_letmacro(
    lia: &LocalIdAlloc,
    scope: &Scope<'_>,
    span: Span,
    arg_iter: NsDataIter,
) -> Result<Expr<Lowered>> {
    lower_let_like(lia, scope, span, arg_iter, lower_macro, |expr, _| expr)
}
fn lower_type(scope: &mut Scope<'_>, self_datum: NsDatum, ty_datum: NsDatum) -> Result<()> {
    let (span, ident) = expect_spanned_ns_ident(self_datum, "new type name")?;
    let ty = lower_poly(scope, ty_datum)?;
    scope.insert_binding(span, ident, Binding::Ty(ty))?;
    Ok(())
}
fn lower_deftype(scope: &mut Scope<'_>, span: Span, mut arg_iter: NsDataIter) -> Result<()> {
    if arg_iter.len() != 2 {
        return Err(Error::new(span, ErrorKind::WrongDefLikeArgCount("deftype")));
    }
    let self_datum = arg_iter.next().unwrap();
    let ty_datum = arg_iter.next().unwrap();
    lower_type(scope, self_datum, ty_datum)
}
fn lower_lettype(
    lia: &LocalIdAlloc,
    scope: &Scope<'_>,
    span: Span,
    arg_iter: NsDataIter,
) -> Result<Expr<Lowered>> {
    lower_let_like(lia, scope, span, arg_iter, lower_type, |expr, _| expr)
}
fn lower_defrecord(scope: &mut Scope<'_>, span: Span, mut arg_iter: NsDataIter) -> Result<()> {
    if arg_iter.len() != 2 {
        return Err(Error::new(span, ErrorKind::WrongDefRecordArgCount));
    }
    let ty_cons_datum = arg_iter.next().unwrap();
    let value_cons_datum = arg_iter.next().unwrap();
    lower_record(scope, ty_cons_datum, value_cons_datum)
}
fn lower_letrecord(
    lia: &LocalIdAlloc,
    scope: &Scope<'_>,
    span: Span,
    arg_iter: NsDataIter,
) -> Result<Expr<Lowered>> {
    lower_let_like(lia, scope, span, arg_iter, lower_record, |expr, _| expr)
}
fn lower_ident_destruc(
    lia: &LocalIdAlloc,
    scope: &mut Scope<'_>,
    span: Span,
    ident: Ident,
    decl_ty: DeclTy,
) -> Result<destruc::Scalar<Lowered>> {
    if ident.is_underscore() {
        Ok(destruc::Scalar::new(None, ident.into_name(), decl_ty))
    } else {
        let local_id = lia.alloc();
        let source_name = ident.name().clone();
        scope.insert_local(span, ident, local_id)?;
        Ok(destruc::Scalar::new(Some(local_id), source_name, decl_ty))
    }
}
fn lower_scalar_destruc(
    lia: &LocalIdAlloc,
    scope: &mut Scope<'_>,
    destruc_datum: NsDatum,
) -> Result<destruc::Scalar<Lowered>> {
    match destruc_datum {
        NsDatum::Ident(span, ident) => lower_ident_destruc(lia, scope, span, ident, DeclTy::Free),
        NsDatum::Vector(span, vs) => {
            let mut data = vs.into_vec();
            if data.len() != 2 {
                return Err(Error::new(span, ErrorKind::NoVecDestruc));
            }
            let ty = lower_poly(scope, data.pop().unwrap())?;
            let (span, ident) = expect_spanned_ns_ident(data.pop().unwrap(), "new variable name")?;
            lower_ident_destruc(lia, scope, span, ident, ty.into())
        }
        _ => Err(Error::new(destruc_datum.span(), ErrorKind::BadRestDestruc)),
    }
}
fn lower_list_destruc(
    lia: &LocalIdAlloc,
    scope: &mut Scope<'_>,
    mut data_iter: NsDataIter,
) -> Result<destruc::List<Lowered>> {
    let rest = try_take_rest_arg(&mut data_iter);
    let fixed_destrucs = data_iter
        .map(|v| lower_destruc(lia, scope, v))
        .collect::<Result<Vec<destruc::Destruc<Lowered>>>>()?;
    let rest_destruc = match rest {
        Some(rest) => Some(Box::new(lower_scalar_destruc(lia, scope, rest)?)),
        None => None,
    };
    Ok(destruc::List::new(fixed_destrucs, rest_destruc))
}
fn lower_destruc(
    lia: &LocalIdAlloc,
    scope: &mut Scope<'_>,
    destruc_datum: NsDatum,
) -> Result<destruc::Destruc<Lowered>> {
    match destruc_datum {
        NsDatum::Ident(span, _) | NsDatum::Vector(span, _) => {
            lower_scalar_destruc(lia, scope, destruc_datum)
                .map(|scalar| destruc::Destruc::Scalar(span, scalar))
        }
        NsDatum::List(span, vs) => lower_list_destruc(lia, scope, vs.into_vec().into_iter())
            .map(|list_destruc| destruc::Destruc::List(span, list_destruc)),
        NsDatum::Keyword(span, _) => Err(Error::new(
            span,
            ErrorKind::ExpectedSym(
                ExpectedSym {
                    found: "keyword",
                    usage: "new variable name",
                }
                .into(),
            ),
        )),
        _ => Err(Error::new(destruc_datum.span(), ErrorKind::BadListDestruc)),
    }
}
fn lower_let_like<B, C, O>(
    lia: &LocalIdAlloc,
    outer_scope: &Scope<'_>,
    span: Span,
    mut arg_iter: NsDataIter,
    binder: B,
    fold_output: C,
) -> Result<Expr<Lowered>>
where
    B: Fn(&mut Scope<'_>, NsDatum, NsDatum) -> Result<O>,
    C: Fn(Expr<Lowered>, O) -> Expr<Lowered>,
{
    let bindings_datum = arg_iter
        .next()
        .ok_or_else(|| Error::new(span, ErrorKind::NoBindingVec))?;
    let bindings_data = if let NsDatum::Vector(_, vs) = bindings_datum {
        vs.into_vec()
    } else {
        return Err(Error::new(
            bindings_datum.span(),
            ErrorKind::BindingsNotVec(bindings_datum.description()),
        ));
    };
    let mut scope = outer_scope.child();
    let mut outputs = Vec::<O>::with_capacity(bindings_data.len() / 2);
    let mut bindings_iter = bindings_data.into_iter();
    while let Some(target_datum) = bindings_iter.next() {
        let value_datum = bindings_iter
            .next()
            .ok_or_else(|| Error::new(target_datum.span(), ErrorKind::UnevenBindingVec))?;
        outputs.push(binder(&mut scope, target_datum, value_datum)?);
    }
    let body_expr = lower_body(lia, &scope, arg_iter)?;
    
    Ok(outputs.into_iter().rfold(body_expr, fold_output))
}
fn lower_body(
    lia: &LocalIdAlloc,
    scope: &Scope<'_>,
    body_data: NsDataIter,
) -> Result<Expr<Lowered>> {
    let mut flattened_exprs = vec![];
    for body_datum in body_data {
        match lower_expr(lia, scope, body_datum)? {
            Expr {
                kind: ExprKind::Do(mut exprs),
                ..
            } => {
                flattened_exprs.append(&mut exprs);
            }
            other => {
                flattened_exprs.push(other);
            }
        }
    }
    if flattened_exprs.len() == 1 {
        Ok(flattened_exprs.pop().unwrap())
    } else {
        Ok(ExprKind::Do(flattened_exprs).into())
    }
}
fn lower_let(
    lia: &LocalIdAlloc,
    scope: &Scope<'_>,
    span: Span,
    arg_iter: NsDataIter,
) -> Result<Expr<Lowered>> {
    lower_let_like(
        lia,
        scope,
        span,
        arg_iter,
        |scope, target_datum, value_datum| {
            let value_expr = lower_expr(lia, scope, value_datum)?;
            let destruc = lower_destruc(lia, scope, target_datum)?;
            Ok((destruc, value_expr))
        },
        |body_expr, (destruc, value_expr)| {
            ExprKind::Let(Box::new(Let {
                span,
                destruc,
                value_expr,
                body_expr,
            }))
            .into()
        },
    )
}
fn lower_fun(
    lia: &LocalIdAlloc,
    outer_scope: &Scope<'_>,
    span: Span,
    mut arg_iter: NsDataIter,
) -> Result<Expr<Lowered>> {
    let mut fun_scope = outer_scope.child();
    let mut next_datum = arg_iter
        .next()
        .ok_or_else(|| Error::new(span, ErrorKind::NoParamDecl))?;
    
    let (pvars, tvars) = if let NsDatum::Set(_, vs) = next_datum {
        next_datum = arg_iter
            .next()
            .ok_or_else(|| Error::new(span, ErrorKind::NoParamDecl))?;
        lower_polymorphic_var_set(outer_scope, &mut fun_scope, vs.into_vec().into_iter())?
    } else {
        (purity::PVars::new(), ty::TVars::new())
    };
    
    let params = match next_datum {
        NsDatum::List(_, vs) => lower_list_destruc(lia, &mut fun_scope, vs.into_vec().into_iter())?,
        other => {
            return Err(Error::new(
                other.span(),
                ErrorKind::ExpectedParamList(other.description()),
            ));
        }
    };
    
    let mut purity = DeclPurity::Free;
    let mut ret_ty = DeclTy::Free;
    let mut ret_ty_span = None;
    if arg_iter.len() >= 2 {
        if let Some(poly_purity) = try_lower_purity(&fun_scope, &arg_iter.as_slice()[0]) {
            arg_iter.next();
            purity = poly_purity.into();
            match arg_iter.next().unwrap() {
                NsDatum::Ident(_, ref ident) if ident.is_underscore() => {}
                ret_datum => {
                    ret_ty_span = Some(ret_datum.span());
                    ret_ty = lower_poly(&fun_scope, ret_datum)?.into();
                }
            }
        }
    }
    
    let body_expr = lower_body(lia, &fun_scope, arg_iter)?;
    Ok(ExprKind::Fun(Box::new(Fun {
        span,
        pvars,
        tvars,
        purity,
        params,
        ret_ty,
        ret_ty_span,
        body_expr,
    }))
    .into())
}
fn lower_expr_prim_apply(
    lia: &LocalIdAlloc,
    scope: &Scope<'_>,
    span: Span,
    prim: Prim,
    mut arg_iter: NsDataIter,
) -> Result<Expr<Lowered>> {
    match prim {
        Prim::Def | Prim::DefMacro | Prim::DefType | Prim::ImportPlaceholder | Prim::DefRecord => {
            Err(Error::new(span, ErrorKind::DefOutsideBody))
        }
        Prim::Let => lower_let(lia, scope, span, arg_iter),
        Prim::LetMacro => lower_letmacro(lia, scope, span, arg_iter),
        Prim::LetType => lower_lettype(lia, scope, span, arg_iter),
        Prim::LetRecord => lower_letrecord(lia, scope, span, arg_iter),
        Prim::Export => Err(Error::new(span, ErrorKind::ExportOutsideModule)),
        Prim::Quote => {
            let literal_datum = expect_one_arg(span, arg_iter)?;
            Ok(literal_datum.into_syntax_datum().into())
        }
        Prim::Fun => lower_fun(lia, scope, span, arg_iter),
        Prim::If => {
            if arg_iter.len() != 3 {
                return Err(Error::new(span, ErrorKind::WrongCondArgCount));
            }
            Ok(ExprKind::Cond(Box::new(Cond {
                span,
                test_expr: lower_expr(lia, scope, arg_iter.next().unwrap())?,
                true_expr: lower_expr(lia, scope, arg_iter.next().unwrap())?,
                false_expr: lower_expr(lia, scope, arg_iter.next().unwrap())?,
            }))
            .into())
        }
        Prim::Do => lower_body(lia, scope, arg_iter),
        Prim::Recur => lower_recur(lia, scope, span, arg_iter),
        Prim::CompileError => Err(lower_user_compile_error(span, arg_iter)),
        Prim::MacroRules | Prim::All => {
            Err(Error::new(span, ErrorKind::ExpectedValue("primitive")))
        }
    }
}
fn lower_expr_apply(
    lia: &LocalIdAlloc,
    scope: &Scope<'_>,
    span: Span,
    fun_expr: Expr<Lowered>,
    mut arg_iter: NsDataIter,
) -> Result<Expr<Lowered>> {
    let rest_arg_datum = try_take_rest_arg(&mut arg_iter);
    let fixed_arg_exprs = arg_iter
        .map(|arg_datum| lower_expr(lia, scope, arg_datum))
        .collect::<Result<Vec<Expr<Lowered>>>>()?;
    let rest_arg_expr = match rest_arg_datum {
        Some(rest_arg_datum) => Some(lower_expr(lia, scope, rest_arg_datum)?),
        None => None,
    };
    Ok(ExprKind::App(Box::new(App {
        span,
        fun_expr,
        ty_args: (),
        fixed_arg_exprs,
        rest_arg_expr,
    }))
    .into())
}
fn lower_recur(
    lia: &LocalIdAlloc,
    scope: &Scope<'_>,
    span: Span,
    mut arg_iter: NsDataIter,
) -> Result<Expr<Lowered>> {
    let rest_arg_datum = try_take_rest_arg(&mut arg_iter);
    let fixed_arg_exprs = arg_iter
        .map(|arg_datum| lower_expr(lia, scope, arg_datum))
        .collect::<Result<Vec<Expr<Lowered>>>>()?;
    let rest_arg_expr = match rest_arg_datum {
        Some(rest_arg_datum) => Some(lower_expr(lia, scope, rest_arg_datum)?),
        None => None,
    };
    Ok(ExprKind::Recur(Box::new(Recur {
        span,
        fixed_arg_exprs,
        rest_arg_expr,
    }))
    .into())
}
fn lower_expr(lia: &LocalIdAlloc, scope: &Scope<'_>, datum: NsDatum) -> Result<Expr<Lowered>> {
    match datum {
        NsDatum::Ident(span, ident) => match scope.get_or_err(span, &ident)? {
            Binding::Var(Some(module_id), local_id) => {
                Ok(ExprKind::ExportRef(span, ExportId::new(*module_id, *local_id)).into())
            }
            Binding::Var(None, local_id) => Ok(ExprKind::LocalRef(span, *local_id).into()),
            Binding::TyPred(test_ty) => Ok(ExprKind::TyPred(span, test_ty.clone()).into()),
            Binding::EqPred => Ok(ExprKind::EqPred(span).into()),
            Binding::RecordValueCons(record_cons) => {
                Ok(ExprKind::RecordCons(span, record_cons.clone()).into())
            }
            Binding::FieldAccessor(record_cons, field_index) => {
                Ok(ExprKind::FieldAccessor(Box::new(FieldAccessor {
                    span,
                    record_cons: record_cons.clone(),
                    field_index: *field_index,
                }))
                .into())
            }
            other => Err(Error::new(
                span,
                ErrorKind::ExpectedValue(other.description()),
            )),
        },
        NsDatum::List(span, vs) => {
            let mut data_iter = vs.into_vec().into_iter();
            let fn_datum = if let Some(fn_datum) = data_iter.next() {
                fn_datum
            } else {
                return Ok(Datum::List(span, Box::new([])).into());
            };
            if let NsDatum::Ident(fn_span, ref ident) = fn_datum {
                match scope.get_or_err(fn_span, ident)? {
                    Binding::Prim(prim) => {
                        return lower_expr_prim_apply(lia, scope, span, *prim, data_iter);
                    }
                    Binding::Macro(module_id, mac) => {
                        let mut macro_scope = scope.child();
                        let expanded_datum = expand_macro(
                            &mut macro_scope,
                            span,
                            *module_id,
                            mac,
                            data_iter.as_slice(),
                        )?;
                        return lower_expr(lia, ¯o_scope, expanded_datum)
                            .map(|expr| ExprKind::MacroExpand(span, Box::new(expr)).into())
                            .map_err(|e| e.with_macro_invocation_span(span));
                    }
                    _ => {}
                }
            }
            let fn_expr = lower_expr(lia, scope, fn_datum)?;
            lower_expr_apply(lia, scope, span, fn_expr, data_iter)
        }
        other => Ok(other.into_syntax_datum().into()),
    }
}
fn lower_module_prim_apply(
    lia: &LocalIdAlloc,
    scope: &mut Scope<'_>,
    span: Span,
    prim: Prim,
    mut arg_iter: NsDataIter,
) -> Result<Option<DeferredModulePrim>, Vec<Error>> {
    match prim {
        Prim::Export => {
            let deferred_exports = arg_iter
                .map(|datum| {
                    let (span, ident) = expect_spanned_ns_ident(datum, "identifier to export")?;
                    Ok(DeferredExport { span, ident })
                })
                .collect::<Result<Vec<DeferredExport>>>()?;
            Ok(Some(DeferredModulePrim::Exports(deferred_exports)))
        }
        Prim::Def => {
            if arg_iter.len() != 2 {
                return Err(vec![Error::new(
                    span,
                    ErrorKind::WrongDefLikeArgCount("def"),
                )]);
            }
            let destruc_datum = arg_iter.next().unwrap();
            let destruc = lower_destruc(lia, scope, destruc_datum)?;
            let value_datum = arg_iter.next().unwrap();
            let deferred_def = DeferredDef {
                span,
                macro_invocation_span: None,
                destruc,
                value_datum,
            };
            Ok(Some(DeferredModulePrim::Def(deferred_def)))
        }
        Prim::DefMacro => Ok(lower_defmacro(scope, span, arg_iter).map(|_| None)?),
        Prim::DefType => Ok(lower_deftype(scope, span, arg_iter).map(|_| None)?),
        Prim::DefRecord => Ok(lower_defrecord(scope, span, arg_iter).map(|_| None)?),
        Prim::CompileError => Err(vec![lower_user_compile_error(span, arg_iter)]),
        _ => Err(vec![Error::new(span, ErrorKind::NonDefInsideModule)]),
    }
}
fn lower_module_def(
    lia: &LocalIdAlloc,
    scope: &mut Scope<'_>,
    datum: NsDatum,
) -> Result<Option<DeferredModulePrim>, Vec<Error>> {
    let span = datum.span();
    if let NsDatum::List(span, vs) = datum {
        let mut data_iter = vs.into_vec().into_iter();
        if let Some(NsDatum::Ident(fn_span, ref ident)) = data_iter.next() {
            match scope.get_or_err(fn_span, ident)? {
                Binding::Prim(prim) => {
                    let prim = *prim;
                    return lower_module_prim_apply(lia, scope, span, prim, data_iter);
                }
                Binding::Macro(module_id, mac) => {
                    let module_id = *module_id;
                    let mac = &mac.clone();
                    let expanded_datum =
                        expand_macro(scope, span, module_id, mac, data_iter.as_slice())?;
                    return lower_module_def(lia, scope, expanded_datum)
                        .map(|def| def.map(|def| def.with_macro_invocation_span(span)))
                        .map_err(|errs| {
                            errs.into_iter()
                                .map(|e| e.with_macro_invocation_span(span))
                                .collect()
                        });
                }
                _ => {
                    
                }
            }
        }
    }
    Err(vec![Error::new(span, ErrorKind::NonDefInsideModule)])
}
fn insert_import_bindings(
    imports: &ModuleImports,
    scope: &mut Scope<'_>,
    arg_data: &[Datum],
) -> Result<(), Vec<Error>> {
    for arg_datum in arg_data {
        let span = arg_datum.span();
        let parsed_import = import::parse_import_set(arg_datum)?;
        let import_module = &imports[parsed_import.module_name()];
        let exports = import::filter_imported_exports(parsed_import, &import_module.exports)?;
        scope.insert_bindings(
            span,
            exports.into_iter().map(|(name, binding)| {
                (
                    Ident::new(Scope::root_ns_id(), name),
                    binding.import_from(import_module.module_id),
                )
            }),
        )?;
    }
    Ok(())
}
pub(crate) fn lower_data(
    imports: &ModuleImports,
    data: &[Datum],
) -> Result<LoweredModule, Vec<Error>> {
    let lia = LocalIdAlloc::new();
    let mut scope = Scope::root();
    
    let mut errors: Vec<Error> = vec![];
    
    
    
    
    
    
    let mut deferred_exports = Vec::<DeferredExport>::new();
    let mut deferred_defs = Vec::<DeferredDef>::new();
    for input_datum in data {
        if let Some(arg_data) = import::try_extract_import_set(input_datum) {
            if let Err(mut new_errors) = insert_import_bindings(imports, &mut scope, arg_data) {
                errors.append(&mut new_errors);
            }
            continue;
        }
        let ns_datum = NsDatum::from_syntax_datum(input_datum);
        match lower_module_def(&lia, &mut scope, ns_datum) {
            Ok(Some(DeferredModulePrim::Exports(mut exports))) => {
                deferred_exports.append(&mut exports);
            }
            Ok(Some(DeferredModulePrim::Def(deferred_def))) => {
                deferred_defs.push(deferred_def);
            }
            Ok(None) => {}
            Err(mut new_errors) => {
                errors.append(&mut new_errors);
            }
        };
    }
    
    let mut exports = HashMap::with_capacity(deferred_exports.len());
    for deferred_export in deferred_exports {
        let DeferredExport { span, ident } = deferred_export;
        match scope.get_or_err(span, &ident) {
            Ok(binding) => {
                exports.insert(ident.into_name(), binding.clone());
            }
            Err(err) => {
                errors.push(err);
            }
        };
    }
    
    let mut defs = Vec::with_capacity(deferred_defs.len());
    for deferred_def in deferred_defs {
        match resolve_deferred_def(&lia, &scope, deferred_def) {
            Ok(def) => {
                defs.push(def);
            }
            Err(error) => {
                errors.push(error);
            }
        }
    }
    
    let main_ident = Ident::new(Scope::root_ns_id(), "main!".into());
    let main_local_id = if let Some(Binding::Var(None, local_id)) = scope.get(&main_ident) {
        Some(*local_id)
    } else {
        None
    };
    if errors.is_empty() {
        Ok(LoweredModule {
            defs,
            exports,
            main_local_id,
        })
    } else {
        Err(errors)
    }
}
fn resolve_deferred_def(
    lia: &LocalIdAlloc,
    scope: &Scope<'_>,
    deferred_def: DeferredDef,
) -> Result<Def<Lowered>> {
    let DeferredDef {
        span,
        macro_invocation_span,
        destruc,
        value_datum,
    } = deferred_def;
    lower_expr(lia, scope, value_datum).map(|value_expr| Def {
        span,
        macro_invocation_span,
        destruc,
        value_expr,
    })
}
pub(crate) fn lower_repl_datum(
    ccx: &CompileCtx,
    scope: &mut Scope<'_>,
    datum: &Datum,
) -> Result<LoweredReplDatum, Vec<Diagnostic<FileId>>> {
    use crate::reporting::errors_to_diagnostics;
    
    let lia = LocalIdAlloc::new();
    if let Some(arg_data) = import::try_extract_import_set(datum) {
        let imports = ccx.imports_for_data(std::iter::once(datum))?;
        insert_import_bindings(&imports, scope, arg_data).map_err(errors_to_diagnostics)?;
        return Ok(LoweredReplDatum::Import(imports));
    }
    
    let ns_datum = NsDatum::from_syntax_datum(datum);
    match lower_module_def(&lia, scope, ns_datum.clone()) {
        Ok(Some(DeferredModulePrim::Def(deferred_def))) => {
            let def =
                resolve_deferred_def(&lia, scope, deferred_def).map_err(|err| vec![err.into()])?;
            Ok(LoweredReplDatum::EvaluableDef(def))
        }
        Ok(Some(DeferredModulePrim::Exports(_))) => {
            Err(vec![
                Error::new(datum.span(), ErrorKind::ExportInsideRepl).into()
            ])
        }
        Ok(None) => Ok(LoweredReplDatum::NonEvaluableDef),
        Err(mut errs) => {
            
            errs.retain(|err| err.kind() != &ErrorKind::NonDefInsideModule);
            if errs.is_empty() {
                
                let expr = lower_expr(&lia, scope, ns_datum).map_err(|err| vec![err.into()])?;
                Ok(LoweredReplDatum::Expr(expr))
            } else {
                Err(errors_to_diagnostics(errs))
            }
        }
    }
}
#[cfg(test)]
fn import_statement_for_module(names: &[&'static str]) -> Datum {
    Datum::List(
        EMPTY_SPAN,
        Box::new([
            Datum::Sym(EMPTY_SPAN, "import".into()),
            Datum::Vector(
                EMPTY_SPAN,
                names
                    .iter()
                    .map(|&n| Datum::Sym(EMPTY_SPAN, n.into()))
                    .collect(),
            ),
        ]),
    )
}
#[cfg(test)]
fn module_for_str(data_str: &str) -> Result<LoweredModule> {
    use std::iter;
    use std::sync::Arc;
    use arret_syntax::parser::data_from_str;
    use crate::context;
    use crate::hir::exports;
    use crate::hir::loader::ModuleName;
    let mut program_data = vec![];
    let mut imports: ModuleImports = HashMap::new();
    for (terminal_name, exports) in iter::once(("primitives", exports::prims_exports()))
        .chain(iter::once(("types", exports::tys_exports())))
    {
        program_data.push(import_statement_for_module(&[
            "arret",
            "internal",
            terminal_name,
        ]));
        imports.insert(
            ModuleName::new(
                "arret".into(),
                vec!["internal".into()],
                terminal_name.into(),
            ),
            Arc::new(context::prims_to_module(exports)),
        );
    }
    let mut test_data = data_from_str(None, data_str).unwrap();
    program_data.append(&mut test_data);
    imports.insert(
        ModuleName::new("arret".into(), vec!["internal".into()], "types".into()),
        Arc::new(context::prims_to_module(exports::tys_exports())),
    );
    lower_data(&imports, &program_data).map_err(|mut errors| errors.remove(0))
}
#[cfg(test)]
pub fn expr_for_str(data_str: &str) -> Expr<Lowered> {
    use arret_syntax::parser::datum_from_str;
    let lia = LocalIdAlloc::new();
    let scope = Scope::new_with_primitives();
    let test_datum = datum_from_str(None, data_str).unwrap();
    let test_nsdatum = NsDatum::from_syntax_datum(&test_datum);
    lower_expr(&lia, &scope, test_nsdatum).unwrap()
}
#[allow(clippy::many_single_char_names)]
#[cfg(test)]
mod test {
    use super::*;
    use arret_syntax::span::t2s;
    use crate::ty::purity::Purity;
    use crate::ty::Ty;
    #[test]
    fn self_quoting_bool() {
        let j = "false";
        let t = "^^^^^";
        let expected: Expr<_> = Datum::Bool(t2s(t), false).into();
        assert_eq!(expected, expr_for_str(j));
    }
    #[test]
    fn self_quoting_empty_list() {
        let j = "()";
        let t = "^^";
        let expected: Expr<_> = Datum::List(t2s(t), Box::new([])).into();
        assert_eq!(expected, expr_for_str(j));
    }
    #[test]
    fn quoted_datum_shorthand() {
        let j = "'foo";
        let t = " ^^^";
        let expected: Expr<_> = Datum::Sym(t2s(t), "foo".into()).into();
        assert_eq!(expected, expr_for_str(j));
    }
    #[test]
    fn quoted_datum_explicit() {
        let j = "(quote foo)";
        let t = "       ^^^ ";
        let expected: Expr<_> = Datum::Sym(t2s(t), "foo".into()).into();
        assert_eq!(expected, expr_for_str(j));
    }
    #[test]
    fn self_evaluating_keyword() {
        let j = ":foo";
        let t = "^^^^";
        let expected: Expr<_> = Datum::Sym(t2s(t), ":foo".into()).into();
        assert_eq!(expected, expr_for_str(j));
    }
    #[test]
    fn wildcard_let() {
        let j = "(let [_ 1])";
        let t = "      ^    ";
        let u = "^^^^^^^^^^^";
        let v = "        ^  ";
        let destruc =
            destruc::Destruc::Scalar(t2s(t), destruc::Scalar::new(None, "_".into(), DeclTy::Free));
        let expected: Expr<_> = ExprKind::Let(Box::new(Let {
            span: t2s(u),
            destruc,
            value_expr: Datum::Int(t2s(v), 1).into(),
            body_expr: ExprKind::Do(vec![]).into(),
        }))
        .into();
        assert_eq!(expected, expr_for_str(j));
    }
    #[test]
    fn empty_fn() {
        let j = "(fn ())";
        let t = "^^^^^^^";
        let expected: Expr<_> = ExprKind::Fun(Box::new(Fun {
            span: t2s(t),
            pvars: purity::PVars::new(),
            tvars: ty::TVars::new(),
            purity: DeclPurity::Free,
            params: destruc::List::new(vec![], None),
            ret_ty: DeclTy::Free,
            ret_ty_span: None,
            body_expr: ExprKind::Do(vec![]).into(),
        }))
        .into();
        assert_eq!(expected, expr_for_str(j));
    }
    #[test]
    fn empty_fn_with_purity() {
        let j = "(fn () -> _ 1)";
        let t = "^^^^^^^^^^^^^^";
        let u = "            ^ ";
        let expected: Expr<_> = ExprKind::Fun(Box::new(Fun {
            span: t2s(t),
            pvars: purity::PVars::new(),
            tvars: ty::TVars::new(),
            purity: Purity::Pure.into(),
            params: destruc::List::new(vec![], None),
            ret_ty: DeclTy::Free,
            ret_ty_span: None,
            body_expr: Datum::Int(t2s(u), 1).into(),
        }))
        .into();
        assert_eq!(expected, expr_for_str(j));
    }
    #[test]
    fn empty_fn_with_ret_ty() {
        let j = "(fn () -> Int 1)";
        let t = "^^^^^^^^^^^^^^^^";
        let u = "          ^^^   ";
        let v = "              ^ ";
        let expected: Expr<_> = ExprKind::Fun(Box::new(Fun {
            span: t2s(t),
            pvars: purity::PVars::new(),
            tvars: ty::TVars::new(),
            purity: Purity::Pure.into(),
            params: destruc::List::new(vec![], None),
            ret_ty: Ty::Int.into(),
            ret_ty_span: Some(t2s(u)),
            body_expr: Datum::Int(t2s(v), 1).into(),
        }))
        .into();
        assert_eq!(expected, expr_for_str(j));
    }
    #[test]
    fn fixed_expr_apply() {
        let j = "(1 2 3)";
        let t = "^^^^^^^";
        let u = " ^     ";
        let v = "   ^   ";
        let w = "     ^ ";
        let expected: Expr<_> = ExprKind::App(Box::new(App {
            span: t2s(t),
            fun_expr: Datum::Int(t2s(u), 1).into(),
            ty_args: (),
            fixed_arg_exprs: vec![Datum::Int(t2s(v), 2).into(), Datum::Int(t2s(w), 3).into()],
            rest_arg_expr: None,
        }))
        .into();
        assert_eq!(expected, expr_for_str(j));
    }
    #[test]
    fn rest_expr_apply() {
        let j = "(1 2 & 3)";
        let t = "^^^^^^^^^";
        let u = " ^       ";
        let v = "   ^     ";
        let w = "       ^ ";
        let expected: Expr<_> = ExprKind::App(Box::new(App {
            span: t2s(t),
            fun_expr: Datum::Int(t2s(u), 1).into(),
            ty_args: (),
            fixed_arg_exprs: vec![Datum::Int(t2s(v), 2).into()],
            rest_arg_expr: Some(Datum::Int(t2s(w), 3).into()),
        }))
        .into();
        assert_eq!(expected, expr_for_str(j));
    }
    #[test]
    fn recur_expr() {
        let j = "(recur 1 2 3)";
        let t = "^^^^^^^^^^^^^";
        let u = "       ^     ";
        let v = "         ^   ";
        let w = "           ^ ";
        let expected: Expr<_> = ExprKind::Recur(Box::new(Recur {
            span: t2s(t),
            fixed_arg_exprs: vec![
                Datum::Int(t2s(u), 1).into(),
                Datum::Int(t2s(v), 2).into(),
                Datum::Int(t2s(w), 3).into(),
            ],
            rest_arg_expr: None,
        }))
        .into();
        assert_eq!(expected, expr_for_str(j));
    }
    #[test]
    fn if_expr() {
        let j = "(if true 1 2)";
        let t = "^^^^^^^^^^^^^";
        let u = "    ^^^^     ";
        let v = "         ^   ";
        let w = "           ^ ";
        let expected: Expr<_> = ExprKind::Cond(Box::new(Cond {
            span: t2s(t),
            test_expr: ExprKind::Lit(Datum::Bool(t2s(u), true)).into(),
            true_expr: ExprKind::Lit(Datum::Int(t2s(v), 1)).into(),
            false_expr: ExprKind::Lit(Datum::Int(t2s(w), 2)).into(),
        }))
        .into();
        assert_eq!(expected, expr_for_str(j));
    }
    #[test]
    fn expand_trivial_macro() {
        let j = "(letmacro [one (macro-rules [() 1])] (one))";
        let t = "                                     ^^^^^ ";
        let u = "                                ^          ";
        let expected: Expr<_> =
            ExprKind::MacroExpand(t2s(t), Box::new(Datum::Int(t2s(u), 1).into())).into();
        assert_eq!(expected, expr_for_str(j));
    }
    #[test]
    fn mutual_module_def() {
        let j1 = "(export x y)";
        let j2 = "(def x y)";
        let j3 = "(def y x)";
        let j = &[j1, j2, j3].join("");
        let module = module_for_str(j).unwrap();
        assert_eq!(2, module.exports.len());
    }
    #[test]
    fn type_predicate() {
        let j = "bool?";
        let t = "^^^^^";
        let expected: Expr<_> = ExprKind::TyPred(t2s(t), ty::pred::TestTy::Bool).into();
        assert_eq!(expected, expr_for_str(j));
    }
    #[test]
    fn equality_predicate() {
        let j = "=";
        let t = "^";
        let expected: Expr<_> = ExprKind::EqPred(t2s(t)).into();
        assert_eq!(expected, expr_for_str(j));
    }
}