hara-native 0.1.21

HAL-free native host runtime and package launcher for Hara
Documentation
use super::super::graph::{Effect, UnitAnalysis, UnitKind};
use super::super::plan::BuildPlan;
use super::super::source::{Diagnostic, SourceLocation};
use super::dynamic::{canonical_symbol, collect_resolved_symbols, scan_dynamic_access};
use super::program::{classify_effect, scan_program};
use super::provides::{list_head, provided_vars, unit_kind, without_metadata};
use crate::core;
use crate::kernel::{Form, GeneratedNamespaceConfig};
use crate::vm::Program;
use crate::Runtime;
use std::collections::BTreeSet;
use std::rc::Rc;

pub struct UnitSeed {
    pub id: String,
    pub module: String,
    pub index: usize,
    pub form: Form,
    pub source_form: Option<Form>,
    pub compile_time_edges: BTreeSet<String>,
    pub location: SourceLocation,
}

pub struct CompiledUnit {
    pub analysis: UnitAnalysis,
    pub program: Option<Rc<Program>>,
}

pub fn expand_top_level(
    runtime: &Runtime,
    config: &GeneratedNamespaceConfig,
    module: &str,
    index: usize,
    form: &Form,
    location: SourceLocation,
) -> Result<Vec<UnitSeed>, String> {
    let mut compile_time_edges = BTreeSet::new();
    let configured = config.rewrite_for_macroexpand(form.clone());
    let expanded = expand_form(runtime, module, &configured, &mut compile_time_edges)?;
    let mut forms = Vec::new();
    flatten_top_level(expanded, &mut forms);
    Ok(forms
        .into_iter()
        .enumerate()
        .map(|(subindex, form)| UnitSeed {
            id: format!("{module}:{index:05}:{subindex:03}"),
            module: module.into(),
            index: index * 1000 + subindex,
            form,
            source_form: (subindex == 0).then(|| configured.clone()),
            compile_time_edges: compile_time_edges.clone(),
            location: location.clone(),
        })
        .collect())
}

pub fn analyze_unit(runtime: &Runtime, seed: UnitSeed, plan: &BuildPlan) -> CompiledUnit {
    let form_source = seed.form.to_string();
    let kind = unit_kind(&seed.form);
    let provides = provided_vars(&seed.form, &seed.module);
    let mut analysis = UnitAnalysis {
        id: seed.id,
        module: seed.module.clone(),
        index: seed.index,
        form_source: form_source.clone(),
        kind,
        effect: Effect::Unknown,
        location: seed.location.clone(),
        provides,
        runtime_edges: BTreeSet::new(),
        compile_time_edges: seed.compile_time_edges,
        namespace_edges: BTreeSet::new(),
        native_roots: Default::default(),
        native_primitives: BTreeSet::new(),
        native_types: BTreeSet::new(),
        native_protocols: BTreeSet::new(),
        diagnostics: Vec::new(),
    };
    if let Some(source_form) = &seed.source_form {
        scan_dynamic_access(runtime, &seed.module, source_form, plan, &mut analysis);
    }
    if seed.source_form.as_ref() != Some(&seed.form) {
        scan_dynamic_access(runtime, &seed.module, &seed.form, plan, &mut analysis);
    }
    if kind == UnitKind::Registration {
        collect_resolved_symbols(
            runtime,
            &seed.module,
            &seed.form,
            &mut analysis.runtime_edges,
        );
    }
    let program = match runtime.compile_bytecode(&form_source) {
        Ok(program) => {
            scan_program(&program, &mut analysis);
            analysis.effect = classify_effect(&program, kind);
            Some(program)
        }
        Err(error) => {
            analysis.effect = Effect::Unknown;
            analysis.diagnostics.push(Diagnostic {
                code: "production/unit-compile-failed".into(),
                operation: "compile".into(),
                module: seed.module,
                location: seed.location,
                message: error,
            });
            None
        }
    };
    CompiledUnit { analysis, program }
}

pub fn execute_compile_time_unit(
    runtime: &mut Runtime,
    compiled: &CompiledUnit,
) -> Result<(), String> {
    let eligible = match compiled.analysis.kind {
        UnitKind::Macro | UnitKind::Registration => compiled.analysis.effect != Effect::Effectful,
        UnitKind::Definition => compiled.analysis.effect == Effect::Pure,
        UnitKind::Initializer => false,
    };
    if !eligible {
        return Ok(());
    }
    let Some(program) = &compiled.program else {
        return Ok(());
    };
    runtime
        .execute_compiled_bytecode_registry_value(program.clone())
        .map(|_| ())
}

fn expand_form(
    runtime: &Runtime,
    module: &str,
    form: &Form,
    compile_time_edges: &mut BTreeSet<String>,
) -> Result<Form, String> {
    let stripped = without_metadata(form);
    if matches!(
        stripped,
        Form::List(values)
            if matches!(values.first(), Some(Form::Symbol(head)) if head == "quote" || head == "syntax-quote")
    ) {
        return Ok(form.clone());
    }
    if matches!(stripped, Form::List(_)) {
        let expanded = macroexpand(runtime, form)?;
        if expanded != *form {
            if let Some(head) = list_head(stripped) {
                compile_time_edges.insert(canonical_symbol(runtime, module, head));
            }
            return expand_form(runtime, module, &expanded, compile_time_edges);
        }
    }
    Ok(match form {
        Form::Metadata(metadata, value) => Form::Metadata(
            metadata.clone(),
            Box::new(expand_form(runtime, module, value, compile_time_edges)?),
        ),
        Form::Tagged(tag, value) => Form::Tagged(
            tag.clone(),
            Box::new(expand_form(runtime, module, value, compile_time_edges)?),
        ),
        Form::List(values) => Form::List(
            values
                .iter()
                .map(|value| expand_form(runtime, module, value, compile_time_edges))
                .collect::<Result<Vec<_>, _>>()?,
        ),
        Form::Vector(values) => Form::Vector(
            values
                .iter()
                .map(|value| expand_form(runtime, module, value, compile_time_edges))
                .collect::<Result<Vec<_>, _>>()?,
        ),
        Form::Set(values) => Form::Set(
            values
                .iter()
                .map(|value| expand_form(runtime, module, value, compile_time_edges))
                .collect::<Result<Vec<_>, _>>()?,
        ),
        Form::Map(entries) => Form::Map(
            entries
                .iter()
                .map(|(key, value)| {
                    Ok((
                        expand_form(runtime, module, key, compile_time_edges)?,
                        expand_form(runtime, module, value, compile_time_edges)?,
                    ))
                })
                .collect::<Result<Vec<_>, String>>()?,
        ),
        value => value.clone(),
    })
}

fn macroexpand(runtime: &Runtime, form: &Form) -> Result<Form, String> {
    core::with_macros(runtime.macros.clone(), || {
        core::with_namespace_registry(&runtime.namespace_registry, || {
            core::with_protocols(&runtime.protocols, || core::vm_macroexpand(form))
        })
    })
}

fn flatten_top_level(form: Form, output: &mut Vec<Form>) {
    let stripped = without_metadata(&form);
    if let Form::List(values) = stripped {
        if matches!(values.first(), Some(Form::Symbol(head)) if head == "do") {
            for value in values.iter().skip(1) {
                flatten_top_level(value.clone(), output);
            }
            return;
        }
    }
    output.push(form);
}