#[derive(Clone, Copy)]
pub enum BuiltinShape {
Fallible,
Infallible,
Range,
Prompt,
}
pub struct BuiltinFn {
pub name: &'static str,
pub runtime_fn: &'static str,
pub arities: &'static [usize],
pub shape: BuiltinShape,
pub hint: &'static str,
}
impl BuiltinFn {
pub fn accepts(&self, argc: usize) -> bool {
self.arities.contains(&argc)
}
pub fn arity_phrase(&self) -> String {
let counts = self
.arities
.iter()
.map(|n| n.to_string())
.collect::<Vec<_>>()
.join(" or ");
let noun = if self.arities == [1] {
"argument"
} else {
"arguments"
};
format!("{counts} {noun}")
}
}
pub const BUILTINS: &[BuiltinFn] = &[
BuiltinFn {
name: "len",
runtime_fn: "len",
arities: &[1],
shape: BuiltinShape::Fallible,
hint: "len(thing)",
},
BuiltinFn {
name: "str",
runtime_fn: "to_str",
arities: &[1],
shape: BuiltinShape::Infallible,
hint: "str(thing)",
},
BuiltinFn {
name: "int",
runtime_fn: "to_int",
arities: &[1],
shape: BuiltinShape::Fallible,
hint: "int(thing)",
},
BuiltinFn {
name: "float",
runtime_fn: "to_float",
arities: &[1],
shape: BuiltinShape::Fallible,
hint: "float(thing)",
},
BuiltinFn {
name: "range",
runtime_fn: "range",
arities: &[1, 2],
shape: BuiltinShape::Range,
hint: "range(n) or range(a, b)",
},
BuiltinFn {
name: "gib",
runtime_fn: "gib",
arities: &[0, 1],
shape: BuiltinShape::Prompt,
hint: "gib() or gib(\"prompt\")",
},
];
pub fn builtin(name: &str) -> Option<&'static BuiltinFn> {
BUILTINS.iter().find(|b| b.name == name)
}
pub fn is_builtin(name: &str) -> bool {
builtin(name).is_some()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn known_names_resolve_and_unknown_do_not() {
assert!(is_builtin("len"));
assert!(is_builtin("range"));
assert!(!is_builtin("nope"));
assert!(builtin("str").is_some());
}
#[test]
fn arity_phrase_matches_diagnostic_wording() {
assert_eq!(builtin("len").unwrap().arity_phrase(), "1 argument");
assert_eq!(builtin("range").unwrap().arity_phrase(), "1 or 2 arguments");
}
#[test]
fn accepts_reflects_declared_arities() {
let range = builtin("range").unwrap();
assert!(range.accepts(1));
assert!(range.accepts(2));
assert!(!range.accepts(3));
assert!(!range.accepts(0));
let len = builtin("len").unwrap();
assert!(len.accepts(1));
assert!(!len.accepts(2));
}
}