mod languages;
mod tasks;
mod templates_core;
mod templates_extended;
mod types;
pub use languages::PROGRAM_LANGUAGES;
pub use tasks::PROGRAM_TASKS;
pub use types::{
ExecutionStatus, ProgramExecution, ProgramLanguage, ProgramSpec, ProgramTask, ProgramTemplate,
};
pub const WRITE_PROGRAM_INTENT: &str = "write_program";
const TEMPLATE_GROUPS: &[&[ProgramTemplate]] = &[
templates_core::TEMPLATES_CORE,
templates_extended::TEMPLATES_EXTENDED,
];
pub fn program_templates() -> impl Iterator<Item = &'static ProgramTemplate> {
TEMPLATE_GROUPS.iter().copied().flatten()
}
#[must_use]
pub fn program_template_count() -> usize {
TEMPLATE_GROUPS.iter().map(|group| group.len()).sum()
}
#[must_use]
pub fn program_language_by_slug(slug: &str) -> Option<&'static ProgramLanguage> {
PROGRAM_LANGUAGES
.iter()
.find(|language| language.slug == slug)
}
#[must_use]
pub fn program_task_by_slug(slug: &str) -> Option<&'static ProgramTask> {
PROGRAM_TASKS.iter().find(|task| task.slug == slug)
}
#[must_use]
pub fn program_template(task_slug: &str, language_slug: &str) -> Option<&'static ProgramTemplate> {
program_templates()
.find(|template| template.task_slug == task_slug && template.language_slug == language_slug)
}
#[must_use]
pub fn program_spec(task_slug: &str, language_slug: &str) -> Option<ProgramSpec> {
Some(ProgramSpec {
task: program_task_by_slug(task_slug)?,
language: program_language_by_slug(language_slug)?,
template: program_template(task_slug, language_slug)?,
})
}
fn alias_surfaces(prefix: &str, slug: &str) -> impl Iterator<Item = &'static str> {
crate::seed::lexicon()
.meaning(&format!("{prefix}_{slug}"))
.into_iter()
.flat_map(crate::seed::Meaning::words)
}
#[must_use]
pub fn program_language_by_alias(normalized: &str) -> Option<&'static ProgramLanguage> {
PROGRAM_LANGUAGES.iter().find(|language| {
alias_surfaces("program_language", language.slug)
.any(|alias| contains_token(normalized, alias))
})
}
#[must_use]
pub fn program_task_by_alias(normalized: &str) -> Option<&'static ProgramTask> {
PROGRAM_TASKS.iter().find(|task| {
alias_surfaces("program_task", task.slug).any(|alias| contains_phrase(normalized, alias))
})
}
#[must_use]
pub fn supported_program_languages() -> String {
PROGRAM_LANGUAGES
.iter()
.map(|language| language.slug)
.collect::<Vec<_>>()
.join(", ")
}
#[must_use]
pub fn supported_program_tasks() -> String {
PROGRAM_TASKS
.iter()
.map(|task| task.slug)
.collect::<Vec<_>>()
.join(", ")
}
pub fn contains_cjk(text: &str) -> bool {
text.chars().any(|ch| {
let cp = ch as u32;
(0x3400..=0x4DBF).contains(&cp)
|| (0x4E00..=0x9FFF).contains(&cp)
|| (0xF900..=0xFAFF).contains(&cp)
|| (0x3040..=0x30FF).contains(&cp)
|| (0x3100..=0x312F).contains(&cp)
})
}
pub fn contains_devanagari(text: &str) -> bool {
text.chars()
.any(|ch| (0x0900..=0x097F).contains(&(ch as u32)))
}
fn contains_token(normalized: &str, expected: &str) -> bool {
if contains_cjk(expected) {
return normalized.contains(expected);
}
normalized.split_whitespace().any(|token| token == expected)
}
fn contains_phrase(normalized: &str, expected: &str) -> bool {
if contains_cjk(expected) {
return normalized.contains(expected);
}
normalized == expected
|| normalized.starts_with(&format!("{expected} "))
|| normalized.ends_with(&format!(" {expected}"))
|| normalized.contains(&format!(" {expected} "))
}
#[cfg(test)]
mod lino_parity {
use super::*;
fn escape_for_lino(code: &str) -> String {
code.replace('\\', "\\\\")
.replace('\'', "\\x27")
.replace('\n', "\\n")
}
#[test]
fn lino_seed_tasks_line_lists_every_catalog_task() {
let lino = crate::seed::HELLO_WORLD_PROGRAMS_LINO;
let tasks_line = lino
.lines()
.find(|l| l.trim_start().starts_with("tasks \""))
.expect("lino seed declares a tasks line");
let listed: Vec<&str> = tasks_line
.trim()
.trim_start_matches("tasks \"")
.trim_end_matches('"')
.split('|')
.collect();
for task in PROGRAM_TASKS {
assert!(
listed.contains(&task.slug),
"lino tasks line is missing `{}` (has {listed:?})",
task.slug
);
}
}
#[test]
fn lino_seed_mirrors_every_catalog_template() {
let lino = crate::seed::HELLO_WORLD_PROGRAMS_LINO;
for template in program_templates() {
let needle = format!("code '{}'", escape_for_lino(template.code));
assert!(
lino.contains(&needle),
"lino seed is missing the {}/{} template (escaped code not found)",
template.task_slug,
template.language_slug
);
}
}
#[test]
fn lino_seed_has_no_extra_templates() {
let lino = crate::seed::HELLO_WORLD_PROGRAMS_LINO;
let seed_templates = lino.matches("\n code '").count();
assert_eq!(
seed_templates,
program_template_count(),
"lino seed template count ({seed_templates}) must equal the catalog count ({})",
program_template_count()
);
}
}
#[cfg(test)]
mod seed_alias_coverage {
use super::*;
use crate::seed::{lexicon, ROLE_PROGRAM_LANGUAGE_ALIAS, ROLE_PROGRAM_TASK_ALIAS};
#[test]
fn every_language_slug_owns_an_alias_meaning() {
for language in PROGRAM_LANGUAGES {
let slug = format!("program_language_{}", language.slug);
let meaning = lexicon()
.meaning(&slug)
.unwrap_or_else(|| panic!("language `{}` has no `{slug}` meaning", language.slug));
assert!(
meaning.has_role(ROLE_PROGRAM_LANGUAGE_ALIAS),
"`{slug}` must carry the `{ROLE_PROGRAM_LANGUAGE_ALIAS}` role"
);
assert!(
meaning.words().next().is_some(),
"`{slug}` must lexicalise at least one alias surface"
);
}
}
#[test]
fn every_task_slug_owns_an_alias_meaning() {
for task in PROGRAM_TASKS {
let slug = format!("program_task_{}", task.slug);
let meaning = lexicon()
.meaning(&slug)
.unwrap_or_else(|| panic!("task `{}` has no `{slug}` meaning", task.slug));
assert!(
meaning.has_role(ROLE_PROGRAM_TASK_ALIAS),
"`{slug}` must carry the `{ROLE_PROGRAM_TASK_ALIAS}` role"
);
assert!(
meaning.words().next().is_some(),
"`{slug}` must lexicalise at least one alias surface"
);
}
}
#[test]
fn every_language_alias_meaning_names_a_catalog_slug() {
for meaning in lexicon().meanings_with_role(ROLE_PROGRAM_LANGUAGE_ALIAS) {
let slug = meaning.slug.strip_prefix("program_language_").unwrap_or_else(|| {
panic!("`{}` carries the language-alias role but is not a `program_language_<slug>` meaning", meaning.slug)
});
assert!(
program_language_by_slug(slug).is_some(),
"alias meaning `{}` names language slug `{slug}`, absent from PROGRAM_LANGUAGES",
meaning.slug
);
}
}
#[test]
fn every_task_alias_meaning_names_a_catalog_slug() {
for meaning in lexicon().meanings_with_role(ROLE_PROGRAM_TASK_ALIAS) {
let slug = meaning.slug.strip_prefix("program_task_").unwrap_or_else(|| {
panic!("`{}` carries the task-alias role but is not a `program_task_<slug>` meaning", meaning.slug)
});
assert!(
program_task_by_slug(slug).is_some(),
"alias meaning `{}` names task slug `{slug}`, absent from PROGRAM_TASKS",
meaning.slug
);
}
}
#[test]
fn every_language_resolves_from_its_seed_surfaces() {
for language in PROGRAM_LANGUAGES {
assert_eq!(
program_language_by_alias(language.slug).map(|l| l.slug),
Some(language.slug),
"`{}` must resolve to itself through its seed surfaces",
language.slug
);
}
}
#[test]
fn every_task_resolves_through_the_seed() {
for task in PROGRAM_TASKS {
let slug = format!("program_task_{}", task.slug);
let first = lexicon()
.meaning(&slug)
.and_then(|m| m.words().next())
.unwrap_or_else(|| panic!("`{slug}` must lexicalise at least one surface"));
assert!(
program_task_by_alias(first).is_some(),
"task surface `{first}` (from `{slug}`) must resolve to a catalog task"
);
}
}
}