bonsai-ninja-lang-python 0.1.0

Python language adapter.
Documentation
use bonsai_db::AnalyzerDb;
use bonsai_lang_api::{AssignValueKind, FlowEvent, LanguageRegistry};
use bonsai_vfs::Vfs;
use std::sync::Arc;

#[test]
fn tuple_iterable_yield_refinement_reuses_the_generic_tuple_place() {
    let vfs = Arc::new(Vfs::new());
    vfs.write(
        "sample.py".to_string(),
        Arc::<str>::from(
            "def expand(parts):\n    for index, part in enumerate(parts):\n        consume(part)\n",
        ),
    );
    let registry = Arc::new(LanguageRegistry::new());
    registry.register(Arc::new(bonsai_lang_python::PythonAdapter::new()));
    let db = AnalyzerDb::new(vfs, registry);
    let global = db.global_index();
    let declaration = global
        .all_files()
        .flat_map(|file| global.decls_in(file))
        .find(|decl| decl.name == "expand")
        .expect("expand declaration");
    let part_bindings = declaration
        .flow_events
        .iter()
        .filter_map(|event| match event {
            FlowEvent::Assign {
                target,
                source_names,
                value_kind,
                ..
            } if target == "part" => Some((source_names.as_slice(), *value_kind)),
            _ => None,
        })
        .collect::<Vec<_>>();

    assert!(
        part_bindings.iter().any(|(sources, kind)| {
            *kind == Some(AssignValueKind::YieldResult)
                && sources.iter().any(|source| source == "__bonsai_tuple_result_1")
        }),
        "the yield alternative must target the same tuple projection as the generic binding: {part_bindings:?}"
    );
    assert!(
        !part_bindings
            .iter()
            .any(|(sources, kind)| *kind == Some(AssignValueKind::YieldResult) && sources.is_empty()),
        "a tuple yield refinement must not create a second clean scalar write: {part_bindings:?}"
    );
}