use blue_lang_check::names::Target;
use blue_lang_check::{Code, Namespace};
use blue_lang_runtime::inputs::Inputs;
use blue_lang_runtime::pipeline::{check_entry, run_in_surface, Checking};
use blue_lang_runtime::uses::{Entry, Loader};
use tatara_lisp_eval::Value;
struct Mem(&'static [(&'static str, &'static str)]);
impl Loader for Mem {
fn load(&self, name: &str) -> Result<Vec<(String, String)>, String> {
self.0
.iter()
.find(|(n, _)| *n == name)
.map(|(n, s)| vec![(format!("{n}.b"), (*s).to_string())])
.ok_or_else(|| format!("no bidama named \"{name}\""))
}
}
const DIST: &[(&str, &str)] = &[
(
"retsu",
"def first(xs)\n :retsu_first\nend\n\ndef size(xs)\n length(xs)\nend\n",
),
("kazu", "def abs(n)\n n\nend\n"),
];
fn codes(src: &str) -> Vec<(Code, String)> {
let checked =
check_entry(Entry::anonymous(src), &Mem(DIST), None, Checking::Program).expect("check");
checked
.outcome
.diagnostics
.iter()
.map(|d| (d.code, d.message.clone()))
.collect()
}
#[test]
fn a_qualifier_that_does_not_own_the_name_is_an_error_naming_the_owner() {
let src = "use(\"kazu\")\nuse(\"retsu\")\n\nkazu::first([1])\n";
let checked =
check_entry(Entry::anonymous(src), &Mem(DIST), None, Checking::Program).expect("check");
let d = &checked.outcome.diagnostics;
assert_eq!(d.len(), 1, "{d:?}");
assert_eq!(d[0].code, Code::B0011);
assert_eq!(d[0].message, "bidama `kazu` defines no `first`");
assert_eq!(
d[0].help.as_deref(),
Some("`first` is defined in bidama `retsu`")
);
assert_eq!(d[0].fixes[0].edits[0].replacement, "retsu::first");
}
#[test]
fn a_qualified_name_runs_the_definition_it_names() {
let src = "use(\"retsu\")\n\nretsu::first([1])\n";
assert!(codes(src).is_empty(), "{:?}", codes(src));
let v = run_in_surface(Entry::anonymous(src), Inputs::new(), &Mem(DIST), None)
.expect("run")
.value;
assert!(matches!(&v, Value::Keyword(k) if &**k == "retsu_first"));
}
#[test]
fn a_qualifier_the_file_does_not_use_is_an_error() {
let c = codes("use(\"kazu\")\n\nretsu::first([1])\n");
assert_eq!(c.len(), 1, "{c:?}");
assert_eq!(c[0].0, Code::B0010);
}
#[test]
fn blue_qualifies_only_builtins() {
assert_eq!(codes("blue::length([1])\n")[0].0, Code::B0018);
let c = codes("blue::lenght([1])\n");
assert_eq!(
c,
vec![(Code::B0011, "`blue::lenght` names no builtin".into())]
);
}
#[test]
fn a_listed_name_must_be_the_packages() {
let c = codes("use(\"retsu\", [:frist])\n");
assert_eq!(c.len(), 1, "{c:?}");
assert_eq!(
c[0],
(Code::B0011, "bidama `retsu` defines no `frist`".into())
);
}
#[test]
fn a_listed_name_is_what_the_namespaced_rule_binds() {
let target = |src: &str| {
let checked =
check_entry(Entry::anonymous(src), &Mem(DIST), None, Checking::Program).expect("check");
let r = checked.resolve();
let first = r
.references
.iter()
.find(|r| r.written == "first")
.expect("a reference to first")
.clone();
(first.flat, first.ns)
};
let retsu = Target::Def(Namespace::Bidama("retsu".into()), "first".into());
assert_eq!(
target("use(\"retsu\", [:first])\n\nfirst([1])\n"),
(retsu.clone(), retsu.clone())
);
assert_eq!(
target("use(\"retsu\")\n\nfirst([1])\n"),
(retsu, Target::Builtin("first".into()))
);
}
#[test]
fn blue_first_is_the_builtin_whatever_the_file_lists() {
let src = "use(\"retsu\", [:first])\n\n[first([1]), blue::first([1])]\n";
assert!(codes(src).is_empty(), "{:?}", codes(src));
let v = run_in_surface(Entry::anonymous(src), Inputs::new(), &Mem(DIST), None)
.expect("run")
.value;
assert_eq!(format!("{v:?}"), "[Keyword(:retsu_first), Int(1)]");
}
#[test]
fn a_facade_makes_its_members_qualifiers_reachable() {
const WITH_FACADE: &[(&str, &str)] = &[
("retsu", "def first(xs)\n :retsu_first\nend\n"),
("fz", "use(\"retsu\")\n"),
];
let checked = check_entry(
Entry::anonymous("use(\"fz\")\n\nretsu::first([1])\n"),
&Mem(WITH_FACADE),
None,
Checking::WithTests,
)
.expect("check");
let d: Vec<_> = checked
.outcome
.diagnostics
.iter()
.map(|d| (d.code, d.message.clone()))
.collect();
assert!(d.is_empty(), "{d:?}");
}
struct Versioned(&'static [(&'static str, &'static str)]);
impl Loader for Versioned {
fn load(&self, name: &str) -> Result<Vec<(String, String)>, String> {
Mem(self.0).load(name)
}
fn version(&self, _: &str, _: Option<&std::path::Path>) -> Option<String> {
Some("0.1.1".into())
}
}
#[test]
fn a_kept_prefix_gives_one_definition_two_names() {
use blue_lang_check::names::Target;
const KK: &[(&str, &str)] = &[(
"kk",
"legacy_names(\"0.1.1\", \"kk\")\n\n# waive B0013: count is the list builtin, which kk also uses\ndef kk_count(xs)\n blue::count(xs) + 100\nend\n",
)];
let src = "use(\"kk\")\n\n[kk::count([1]), kk::kk_count([1])]\n";
let checked = check_entry(
Entry::anonymous(src),
&Versioned(KK),
None,
Checking::Program,
)
.expect("check");
assert!(
checked.outcome.diagnostics.is_empty(),
"{:?}",
checked.outcome.diagnostics
);
let r = checked.resolve();
let targets: Vec<_> = r
.references
.iter()
.filter(|r| r.written.starts_with("kk/"))
.map(|r| r.ns.clone())
.collect();
let one = Target::Def(Namespace::Bidama("kk".into()), "kk_count".into());
assert_eq!(targets, vec![one.clone(), one]);
let v = run_in_surface(Entry::anonymous(src), Inputs::new(), &Versioned(KK), None)
.expect("run")
.value;
assert_eq!(format!("{v:?}"), "[Int(101), Int(101)]");
const TWICE: &[(&str, &str)] = &[(
"kk",
"legacy_names(\"0.1.1\", \"kk\")\n\n# waive B0013: count is the list builtin\ndef kk_count(xs)\n 1\nend\n\ndef count(xs)\n 2\nend\n",
)];
let checked = check_entry(
Entry::anonymous("use(\"kk\")\n\nkk::count([1])\n"),
&Versioned(TWICE),
None,
Checking::Program,
)
.expect("check");
let codes: Vec<Code> = checked.outcome.diagnostics.iter().map(|d| d.code).collect();
assert!(codes.contains(&Code::B0022), "{codes:?}");
}