inauguration 0.2.0

.in language and general compiler CLI (Core IR, hybrid SIL, staging, plugins)
Documentation
use crate::language_gates::{self, evaluate_language_gates, polyglot_sample_for};
use crate::language_support::LanguageSupport;
use serde::Serialize;

pub const BOUNDARY_GATE_LADDER: &[(&str, u8)] = &[
    ("reported", 0),
    ("core-ir-decls", 1),
    ("core-ir-bodies", 2),
    ("textual-sil", 2),
    ("boundary-ir-verify", 3),
    ("boundary-ir-attach", 3),
    ("boundary-extract", 4),
    ("abi-layout-hash", 4),
    ("abi-emit", 5),
    ("bytecode-vm", 5),
    ("owned-runtime", 5),
];

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
pub struct BoundaryCapability {
    pub boundary_level: u8,
    pub effective_level: u8,
    pub evaluated_level: u8,
}

#[derive(Debug, Clone, Serialize)]
pub struct LanguageSupportJson<'a> {
    pub language: &'a str,
    pub parser_id: Option<&'a str>,
    pub extensions: &'a [&'a str],
    pub level: u8,
    pub level_label: &'a str,
    pub front: &'a str,
    pub runtime_boundary: &'a str,
    pub example: &'a str,
    pub next_step: &'a str,
    pub boundary_level: u8,
    pub effective_level: u8,
    pub evaluated_level: u8,
    pub passed_gates: Vec<&'static str>,
    pub blocking_gates: Vec<&'static str>,
    pub boundary_gates: Vec<&'static str>,
}

fn declared_boundary_level_for(entry: &LanguageSupport) -> u8 {
    match entry.parser_id {
        Some("in") => 5,
        Some("icore") => 3,
        Some("nim" | "odin" | "hare" | "d" | "crystal" | "clojure" | "vb") => 3,
        Some("zig" | "rust") => 4,
        Some("javascript" | "typescript") => 5,
        None if entry.language == "Swift" => 2,
        Some(_) => infer_boundary_level_from_runtime(entry),
        None => infer_boundary_level_from_runtime(entry),
    }
}

fn infer_boundary_level_from_runtime(entry: &LanguageSupport) -> u8 {
    if entry.runtime_boundary.contains("not compiled directly") {
        0
    } else if entry.runtime_boundary.contains("declarations only") {
        1
    } else if entry.runtime_boundary.contains("Core IR") {
        2
    } else {
        1
    }
}

fn gate_report_for(entry: &LanguageSupport) -> language_gates::LanguageGateReport {
    evaluate_language_gates(entry, &language_gates::repo_root())
}

fn gate_evaluated_level(entry: &LanguageSupport) -> Option<u8> {
    let sample_rel = polyglot_sample_for(entry)?;
    let sample = language_gates::repo_root().join(sample_rel);
    if !sample.is_file() {
        return None;
    }
    Some(gate_report_for(entry).evaluated_level)
}

#[must_use]
pub fn boundary_level_for(entry: &LanguageSupport) -> u8 {
    let declared = declared_boundary_level_for(entry);
    gate_evaluated_level(entry)
        .map(|evaluated| declared.min(evaluated))
        .unwrap_or(declared)
}

#[must_use]
pub fn evaluated_level_for(entry: &LanguageSupport) -> u8 {
    gate_evaluated_level(entry).unwrap_or(0)
}

#[must_use]
pub fn effective_level_for(entry: &LanguageSupport) -> u8 {
    boundary_level_for(entry).min(entry.level)
}

#[must_use]
pub fn boundary_capability_for(entry: &LanguageSupport) -> BoundaryCapability {
    let boundary_level = boundary_level_for(entry);
    let evaluated_level = evaluated_level_for(entry);
    BoundaryCapability {
        boundary_level,
        effective_level: boundary_level.min(entry.level),
        evaluated_level,
    }
}

#[must_use]
pub fn boundary_gates_for(boundary_level: u8) -> Vec<&'static str> {
    let mut gates = Vec::new();
    let mut seen = std::collections::BTreeSet::new();
    for (gate, min_level) in BOUNDARY_GATE_LADDER {
        if boundary_level >= *min_level && seen.insert(*gate) {
            gates.push(*gate);
        }
    }
    gates
}

#[must_use]
pub fn language_support_json(entry: &LanguageSupport) -> LanguageSupportJson<'_> {
    let capability = boundary_capability_for(entry);
    let report = gate_report_for(entry);
    LanguageSupportJson {
        language: entry.language,
        parser_id: entry.parser_id,
        extensions: entry.extensions,
        level: entry.level,
        level_label: entry.level_label,
        front: entry.front,
        runtime_boundary: entry.runtime_boundary,
        example: entry.example,
        next_step: entry.next_step,
        boundary_level: capability.boundary_level,
        effective_level: capability.effective_level,
        evaluated_level: capability.evaluated_level,
        passed_gates: report.passed_gates,
        blocking_gates: report.blocking_gates,
        boundary_gates: boundary_gates_for(capability.boundary_level),
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::language_support::{all_language_support, language_support_for_parser};
    use crate::parser_registry::ParserId;

    #[test]
    fn in_reports_gate_capped_boundary_level() {
        let entry = language_support_for_parser(ParserId::In.as_str()).expect("in");
        let capability = boundary_capability_for(entry);
        assert!(capability.boundary_level >= 3);
        assert!(capability.boundary_level <= 5);
        assert_eq!(
            capability.effective_level,
            entry.level.min(capability.boundary_level)
        );
    }

    #[test]
    fn icore_reports_boundary_ir_interchange_level() {
        let entry = language_support_for_parser(ParserId::Icore.as_str()).expect("icore");
        assert_eq!(boundary_level_for(entry), 3);
        assert_eq!(
            effective_level_for(entry),
            entry.level.min(boundary_level_for(entry))
        );
        let gates = boundary_gates_for(3);
        assert!(gates.contains(&"boundary-ir-verify"));
        assert!(gates.contains(&"boundary-ir-attach"));
        assert!(!gates.contains(&"boundary-extract"));
    }

    #[test]
    fn dedicated_boundary_fronts_report_semantic_level() {
        for parser_id in [
            ParserId::Clojure,
            ParserId::D,
            ParserId::Crystal,
            ParserId::Hare,
            ParserId::VbNet,
        ] {
            let entry = language_support_for_parser(parser_id.as_str())
                .unwrap_or_else(|| panic!("{}", parser_id.as_str()));
            assert!(boundary_level_for(entry) >= 2, "{}", entry.language);
            assert_eq!(
                effective_level_for(entry),
                entry.level.min(boundary_level_for(entry))
            );
        }
    }

    #[test]
    fn rust_reports_family_typecheck_level() {
        let entry = language_support_for_parser(ParserId::Rust.as_str()).expect("rust");
        assert!(boundary_level_for(entry) >= 3);
        assert_eq!(
            effective_level_for(entry),
            entry.level.min(boundary_level_for(entry))
        );
    }

    #[test]
    fn nim_odin_report_boundary_ir_attach_level() {
        for parser_id in [ParserId::Nim, ParserId::Odin] {
            let entry = language_support_for_parser(parser_id.as_str())
                .unwrap_or_else(|| panic!("{}", parser_id.as_str()));
            assert_eq!(boundary_level_for(entry), 3, "{}", entry.language);
            assert_eq!(
                effective_level_for(entry),
                entry.level.min(boundary_level_for(entry)),
                "{}",
                entry.language
            );
        }
    }

    #[test]
    fn php_reports_family_typecheck_level() {
        let entry = language_support_for_parser(ParserId::Php.as_str()).expect("php");
        assert!(boundary_level_for(entry) >= 2);
        assert_eq!(
            effective_level_for(entry),
            entry.level.min(boundary_level_for(entry))
        );
    }

    #[test]
    fn language_support_json_includes_boundary_fields() {
        let entry = all_language_support()
            .iter()
            .find(|entry| entry.language == "in")
            .expect("in");
        let json = language_support_json(entry);
        assert!(json.boundary_level >= 3);
        assert_eq!(json.effective_level, entry.level.min(json.boundary_level));
        assert!(!json.passed_gates.is_empty());
    }

    #[test]
    fn declared_level_never_exceeds_evaluated_when_sample_exists() {
        for entry in all_language_support() {
            if polyglot_sample_for(entry).is_none() {
                continue;
            }
            let sample = language_gates::repo_root().join(polyglot_sample_for(entry).unwrap());
            if !sample.is_file() {
                continue;
            }
            let capability = boundary_capability_for(entry);
            assert!(
                entry.level >= capability.evaluated_level
                    || capability.evaluated_level >= entry.level.saturating_sub(1),
                "{} declared {} evaluated {}",
                entry.language,
                entry.level,
                capability.evaluated_level
            );
        }
    }

    #[test]
    fn every_language_support_entry_has_boundary_capability() {
        for entry in all_language_support() {
            let capability = boundary_capability_for(entry);
            assert!(capability.boundary_level <= 5);
            assert!(capability.effective_level <= entry.level);
            assert!(capability.effective_level <= capability.boundary_level);
            assert!(!boundary_gates_for(capability.boundary_level).is_empty());
        }
    }
}