use crate::error::Error;
use crate::point::{MAX_POINT_NODES, PointType};
const RESERVED_PARAMS: [&str; 8] = [
"approx",
"err",
"exact",
"is_explicit",
"new",
"new_unchecked",
"evaluate",
"write_exact",
];
const RESERVED_TYPES: [&str; 21] = [
"AnyPoint",
"PointError",
"Point",
"Scratch",
"ScratchExhausted",
"ExactCoords",
"ExactError",
"Weight",
"Mark",
"Span",
"Dyadic",
"UNIT",
"TINY",
"SUM_OPERAND_LIMIT",
"Result",
"Ok",
"Err",
"Option",
"Some",
"None",
"From",
];
pub(crate) fn check_names(point: &PointType, file: &str) -> Result<(), Error> {
if !point.name.starts_with(|c: char| c.is_ascii_uppercase()) {
return Err(Error::at(
file,
point.position,
format!(
"`{}` cannot name a point type: the name of a point type must start with an \
uppercase letter, so that it cannot be a primitive type, a module or a \
function that the generated code uses",
point.name
),
));
}
if RESERVED_TYPES.contains(&point.name.as_str()) {
return Err(Error::at(
file,
point.position,
format!(
"`{}` cannot name a point type: the generated code uses that name",
point.name
),
));
}
for param in &point.params {
if !param.name.starts_with(|c: char| c.is_ascii_lowercase()) {
return Err(Error::at(
file,
param.position,
format!(
"`{}` cannot name a parameter: the name of a parameter must start with a \
lowercase letter, so that it cannot be a type, a constant or a variant \
that the generated code uses",
param.name
),
));
}
if !is_snake_case(¶m.name) {
return Err(Error::at(
file,
param.position,
format!(
"`{}` cannot name a parameter: the name of a parameter must be in snake \
case, lowercase ASCII letters, digits and underscores without two \
underscores in a row before the end, so that the generated code compiles \
without `non_snake_case` warnings",
param.name
),
));
}
if RESERVED_PARAMS.contains(¶m.name.as_str()) {
return Err(Error::at(
file,
param.position,
format!(
"`{}` cannot name a parameter: the generated type uses that name",
param.name
),
));
}
}
Ok(())
}
fn is_snake_case(name: &str) -> bool {
name.chars()
.all(|c| c.is_ascii_lowercase() || c.is_ascii_digit() || c == '_')
&& !name.trim_end_matches('_').contains("__")
}
pub(crate) fn check_size(point: &PointType, file: &str) -> Result<(), Error> {
let nodes = point.graph.nodes.len();
if nodes > MAX_POINT_NODES {
return Err(Error::at(
file,
point.position,
format!(
"the coordinates of `{}` take {nodes} distinct operations and inputs, more \
than the {MAX_POINT_NODES} that the generated code supports",
point.name
),
));
}
Ok(())
}