Keep literal true/false distinct from variables named true/false

Found while testing crates/doc/src/proto_expr_parse.rs:462 (variable lookup before the true/false literals at :466) with crates/doc/src/proto_expr.rs:151/186 (display writes the literal as bare `true` and var_ref writes such a variable bare)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
NetterSilver
2026-10-22 08:56:12 +00:00
co-authored by Claude Opus 5.5
parent a86897dc91
commit 79e722d340
2 changed files with 30 additions and 5 deletions
+8 -5
View File
@@ -459,16 +459,19 @@ impl Parser<'_> {
if self.at_keyword("mode") && self.next_after(4) == Some('(') {
return self.mode_of();
}
if let Some((id, n)) = self.variable_here() {
self.i += n;
return self.in_mode(id);
}
let var = self.variable_here();
// A bare `true`/`false` is the literal, also when a variable has that name (it reads
// as `{true}`); longer names that start with it ("true value") stay variables.
for (word, v) in [("true", true), ("false", false)] {
if self.at_keyword(word) {
if self.at_keyword(word) && var.as_ref().is_none_or(|(_, n)| *n <= word.len()) {
self.i += word.len();
return Ok(Expr::Value(VariableValue::Boolean(v)));
}
}
if let Some((id, n)) = var {
self.i += n;
return self.in_mode(id);
}
let word: String = self.s.iter().skip(self.i).take_while(|c| ident(**c)).collect();
if word.is_empty() { Err(self.err(format!("Unexpected {c}"))) } else { Err(self.err(format!("No variable `{word}`"))) }
}
+22
View File
@@ -195,3 +195,25 @@ fn names_that_look_like_literals_read_back_as_variables() {
assert_eq!(parse("1"), n(1.0));
assert_eq!(parse("\"q\""), Expr::Value(VariableValue::String("q".into())));
}
#[test]
fn literal_true_false_survive_variables_named_true_false() {
use ExprOp::*;
let lit = |x: bool| Expr::Value(VariableValue::Boolean(x));
for name in ["true", "false", "TRUE"] {
let mut v = vars();
if let Some(var) = v.variables.iter_mut().find(|x| x.id == "b") {
var.name = name.into();
}
for e in [
Expr::op(Equals, vec![Expr::var("b"), lit(true)]),
Expr::op(Equals, vec![Expr::var("b"), lit(false)]),
Expr::op(Not, vec![lit(false)]),
Expr::var("b"),
] {
let shown = e.display(&v);
assert_eq!(Expr::parse(&shown, &v), Ok(e.clone()), "{name:?} shown as {shown:?}");
}
assert_eq!(Expr::parse(&format!("{{{name}}}"), &v), Ok(Expr::var("b")));
}
}