Merge branch 'pr-491' into batch/t5

This commit is contained in:
bflatastic
2026-10-16 01:35:23 +07:00
2 changed files with 32 additions and 1 deletions
+13 -1
View File
@@ -143,7 +143,7 @@ impl Expr {
return "…".into();
}
match self {
Expr::Value(VariableValue::Alias(id)) => vars.get(id).map_or_else(|| "(missing variable)".into(), |v| v.name.clone()),
Expr::Value(VariableValue::Alias(id)) => vars.get(id).map_or_else(|| "(missing variable)".into(), |v| var_ref(vars, v)),
Expr::Value(VariableValue::String(s)) => quote(s),
// Exact numbers and `#` colours, so the text reads back as the same expression.
Expr::Value(VariableValue::Number(n)) => format!("{n}"),
@@ -178,6 +178,18 @@ impl Expr {
}
}
/// A variable as written in an expression: its bare name when that reads back as this variable,
/// otherwise `{name}` (or `{id}` when even the braced name wouldn't), so names like `1`, `2x` or
/// `"q"` don't read back as literals or errors.
fn var_ref(vars: &Variables, v: &crate::Variable) -> String {
let this = Expr::var(v.id.clone());
if Expr::parse(&v.name, vars).is_ok_and(|e| e == this) {
return v.name.clone();
}
let braced = !v.name.contains('}') && v.name.trim() == v.name && vars.get(&v.name).is_some_and(|x| x.id == v.id);
if braced { format!("{{{}}}", v.name) } else { format!("{{{}}}", v.id) }
}
/// A collection or mode name as written inside `mode(…)` or `[…]`: as it is, or quoted when it has
/// spaces at its ends, quotes or brackets.
fn name_arg(name: &str) -> String {
+19
View File
@@ -176,3 +176,22 @@ fn hostile_text_is_bounded() {
}
}
}
#[test]
fn names_that_look_like_literals_read_back_as_variables() {
for name in ["count", "count total", "2x", "1", "-3", ".5", "(x)", "#ff", "\"q\"", "not", "true"] {
let mut v = vars();
if let Some(var) = v.variables.iter_mut().find(|x| x.id == "n") {
var.name = name.into();
}
for e in [Expr::var("n"), Expr::op(ExprOp::Add, vec![Expr::var("n"), n(1.0)]), Expr::InMode { variable: "n".into(), mode: "dark".into() }] {
let shown = e.display(&v);
assert_eq!(Expr::parse(&shown, &v), Ok(e.clone()), "{name:?} shown as {shown:?}");
}
}
// Plain names stay bare, and literals still read as literals.
let v = vars();
assert_eq!(Expr::var("n").display(&v), "n-name");
assert_eq!(parse("1"), n(1.0));
assert_eq!(parse("\"q\""), Expr::Value(VariableValue::String("q".into())));
}