refactor(ast): rename value types to Number/Decimal/Text/Boolean

Renamed AST value types for v0.3 naming convention:
- Value::Int -> Value::Number
- Value::Float -> Value::Decimal
- Value::String -> Value::Text
- Value::Bool -> Value::Boolean
- Expr::IntLit -> Expr::NumberLit
- Expr::FloatLit -> Expr::DecimalLit
- Expr::StringLit -> Expr::TextLit
- Expr::BoolLit -> Expr::BooleanLit

Updated all references across parser, resolver, validator, LSP,
query engine, tests, and editor.
This commit is contained in:
2026-02-14 14:03:21 +00:00
parent 8c3c380cfd
commit 8e4bdd3942
21 changed files with 188 additions and 188 deletions

View File

@@ -216,7 +216,7 @@ fn test_unknown_life_arc_state_error() {
on_enter: None,
transitions: vec![Transition {
to: "adult".to_string(), // 'adult' exists
condition: Expr::BoolLit(true),
condition: Expr::BooleanLit(true),
span: Span::new(0, 10),
}],
span: Span::new(0, 50),
@@ -226,7 +226,7 @@ fn test_unknown_life_arc_state_error() {
on_enter: None,
transitions: vec![Transition {
to: "senior".to_string(), // 'senior' doesn't exist!
condition: Expr::BoolLit(true),
condition: Expr::BooleanLit(true),
span: Span::new(50, 60),
}],
span: Span::new(50, 100),
@@ -252,7 +252,7 @@ fn test_unknown_life_arc_state_error() {
fn test_trait_out_of_range_error_bond() {
let fields = vec![Field {
name: "bond".to_string(),
value: Value::Float(1.5), // Out of range!
value: Value::Decimal(1.5), // Out of range!
span: Span::new(0, 10),
}];
@@ -276,7 +276,7 @@ fn test_trait_out_of_range_error_bond() {
fn test_trait_out_of_range_error_age() {
let fields = vec![Field {
name: "age".to_string(),
value: Value::Int(200), // Out of range!
value: Value::Number(200), // Out of range!
span: Span::new(0, 10),
}];
@@ -297,7 +297,7 @@ fn test_trait_out_of_range_error_age() {
fn test_trait_out_of_range_negative() {
let fields = vec![Field {
name: "trust".to_string(),
value: Value::Float(-0.2), // Negative!
value: Value::Decimal(-0.2), // Negative!
span: Span::new(0, 10),
}];
@@ -411,7 +411,7 @@ fn test_relationship_bond_out_of_range() {
participants: vec![],
fields: vec![Field {
name: "bond".to_string(),
value: Value::Float(2.5), // Way out of range!
value: Value::Decimal(2.5), // Way out of range!
span: Span::new(0, 10),
}],
span: Span::new(0, 50),
@@ -438,12 +438,12 @@ fn test_duplicate_field_in_convert() {
fields: vec![
Field {
name: "age".to_string(),
value: Value::Int(34),
value: Value::Number(34),
span: Span::new(0, 10),
},
Field {
name: "age".to_string(), // Duplicate!
value: Value::Int(35),
value: Value::Number(35),
span: Span::new(10, 20),
},
],

View File

@@ -2389,10 +2389,10 @@ fn get_default_value_for_type(field_type: &crate::syntax::ast::Value) -> String
}
},
| Value::List(_) => String::from("[]"),
| Value::String(_) => String::from("\"\""),
| Value::Int(_) => String::from("0"),
| Value::Float(_) => String::from("0.0"),
| Value::Bool(_) => String::from("false"),
| Value::Text(_) => String::from("\"\""),
| Value::Number(_) => String::from("0"),
| Value::Decimal(_) => String::from("0.0"),
| Value::Boolean(_) => String::from("false"),
| Value::Object(_) => String::from("{}"),
| Value::Range(_, _) => String::from("0..10"),
| Value::Time(_) => String::from("00:00"),
@@ -2408,10 +2408,10 @@ fn format_value(value: &crate::syntax::ast::Value) -> String {
use crate::syntax::ast::Value;
match value {
| Value::String(s) => format!("\"{}\"", s),
| Value::Int(n) => n.to_string(),
| Value::Float(f) => f.to_string(),
| Value::Bool(b) => b.to_string(),
| Value::Text(s) => format!("\"{}\"", s),
| Value::Number(n) => n.to_string(),
| Value::Decimal(f) => f.to_string(),
| Value::Boolean(b) => b.to_string(),
| Value::Identifier(path) => path.join("."),
| Value::List(items) => {
let formatted: Vec<String> = items.iter().map(format_value).collect();
@@ -2443,10 +2443,10 @@ fn infer_type_from_value(value: &crate::syntax::ast::Value) -> String {
use crate::syntax::ast::Value;
match value {
| Value::String(_) => String::from("String"),
| Value::Int(_) => String::from("Int"),
| Value::Float(_) => String::from("Float"),
| Value::Bool(_) => String::from("Bool"),
| Value::Text(_) => String::from("Text"),
| Value::Number(_) => String::from("Number"),
| Value::Decimal(_) => String::from("Decimal"),
| Value::Boolean(_) => String::from("Boolean"),
| Value::Identifier(path) => path.join("."), // Reference type
| Value::List(items) => {
if items.is_empty() {

View File

@@ -209,10 +209,10 @@ fn is_typing_declaration_name(text: &str, offset: usize) -> bool {
fn format_value_type(value: &Value) -> String {
match value {
| Value::Identifier(path) => path.join("."),
| Value::String(_) => "String".to_string(),
| Value::Int(_) => "Int".to_string(),
| Value::Float(_) => "Float".to_string(),
| Value::Bool(_) => "Bool".to_string(),
| Value::Text(_) => "Text".to_string(),
| Value::Number(_) => "Number".to_string(),
| Value::Decimal(_) => "Decimal".to_string(),
| Value::Boolean(_) => "Boolean".to_string(),
| Value::List(items) => {
if items.is_empty() {
"List".to_string()

View File

@@ -526,10 +526,10 @@ fn format_behavior_node_preview(node: &crate::syntax::ast::BehaviorNode, depth:
fn format_value_as_type(value: &Value) -> String {
match value {
| Value::Identifier(path) => path.join("."),
| Value::String(_) => "String".to_string(),
| Value::Int(_) => "Int".to_string(),
| Value::Float(_) => "Float".to_string(),
| Value::Bool(_) => "Bool".to_string(),
| Value::Text(_) => "Text".to_string(),
| Value::Number(_) => "Number".to_string(),
| Value::Decimal(_) => "Decimal".to_string(),
| Value::Boolean(_) => "Boolean".to_string(),
| Value::List(items) => {
if items.is_empty() {
"List".to_string()
@@ -557,10 +557,10 @@ fn format_value_as_type(value: &Value) -> String {
fn format_value_preview(value: &Value) -> String {
match value {
| Value::Identifier(path) => format!("`{}`", path.join(".")),
| Value::String(s) => format!("\"{}\"", truncate(s, 50)),
| Value::Int(n) => n.to_string(),
| Value::Float(f) => f.to_string(),
| Value::Bool(b) => b.to_string(),
| Value::Text(s) => format!("\"{}\"", truncate(s, 50)),
| Value::Number(n) => n.to_string(),
| Value::Decimal(f) => f.to_string(),
| Value::Boolean(b) => b.to_string(),
| Value::List(items) => {
if items.is_empty() {
"[]".to_string()

View File

@@ -145,7 +145,7 @@ fn add_type_hint(
// Only add hints for non-obvious types
// Skip if the type is clear from the literal (e.g., "string", 123, true)
let should_hint = match &field.value {
| Value::String(_) | Value::Int(_) | Value::Float(_) | Value::Bool(_) => false,
| Value::Text(_) | Value::Number(_) | Value::Decimal(_) | Value::Boolean(_) => false,
| Value::Identifier(_) => true, // Show type for identifier references
| Value::List(_) => true, // Show list element type
| Value::Object(_) => false, // Object structure is visible
@@ -183,10 +183,10 @@ fn add_type_hint(
fn infer_value_type(value: &Value) -> String {
match value {
| Value::Identifier(path) => path.join("."),
| Value::String(_) => "String".to_string(),
| Value::Int(_) => "Int".to_string(),
| Value::Float(_) => "Float".to_string(),
| Value::Bool(_) => "Bool".to_string(),
| Value::Text(_) => "Text".to_string(),
| Value::Number(_) => "Number".to_string(),
| Value::Decimal(_) => "Decimal".to_string(),
| Value::Boolean(_) => "Boolean".to_string(),
| Value::List(items) => {
if items.is_empty() {
"[]".to_string()

View File

@@ -411,14 +411,14 @@ fn highlight_field(builder: &mut SemanticTokensBuilder, field: &Field) {
/// Helper to highlight a value
fn highlight_value(builder: &mut SemanticTokensBuilder, value: &Value) {
match value {
| Value::String(_s) => {
// String literals are already highlighted by the grammar
| Value::Text(_s) => {
// Text literals are already highlighted by the grammar
// but we could add semantic highlighting here if needed
},
| Value::Int(_) | Value::Float(_) => {
| Value::Number(_) | Value::Decimal(_) => {
// Number literals are already highlighted by the grammar
},
| Value::Bool(_) => {
| Value::Boolean(_) => {
// Boolean literals are already highlighted by the grammar
},
| Value::Identifier(_path) => {

View File

@@ -50,7 +50,7 @@ where
max: i64,
) -> Box<dyn Iterator<Item = &'a ResolvedCharacter> + 'a> {
Box::new(self.filter(move |c| {
if let Some(Value::Int(age)) = c.fields.get("age") {
if let Some(Value::Number(age)) = c.fields.get("age") {
*age >= min && *age <= max
} else {
false
@@ -65,7 +65,7 @@ where
max: f64,
) -> Box<dyn Iterator<Item = &'a ResolvedCharacter> + 'a> {
Box::new(self.filter(move |c| {
if let Some(Value::Float(value)) = c.fields.get(trait_name) {
if let Some(Value::Decimal(value)) = c.fields.get(trait_name) {
*value >= min && *value <= max
} else {
false
@@ -121,7 +121,7 @@ where
max: f64,
) -> Box<dyn Iterator<Item = &'a ResolvedRelationship> + 'a> {
Box::new(self.filter(move |r| {
if let Some(Value::Float(bond)) = r.fields.get("bond") {
if let Some(Value::Decimal(bond)) = r.fields.get("bond") {
*bond >= min && *bond <= max
} else {
false
@@ -178,8 +178,8 @@ mod tests {
fn make_character(name: &str, age: i64, trust: f64) -> ResolvedCharacter {
let mut fields = HashMap::new();
fields.insert("age".to_string(), Value::Int(age));
fields.insert("trust".to_string(), Value::Float(trust));
fields.insert("age".to_string(), Value::Number(age));
fields.insert("trust".to_string(), Value::Decimal(trust));
ResolvedCharacter {
name: name.to_string(),
@@ -248,7 +248,7 @@ mod tests {
let mut char1 = make_character("Alice", 25, 0.8);
char1
.fields
.insert("job".to_string(), Value::String("baker".to_string()));
.insert("job".to_string(), Value::Text("baker".to_string()));
let char2 = make_character("Bob", 35, 0.6);
@@ -263,10 +263,10 @@ mod tests {
#[test]
fn test_relationship_with_bond_range() {
let mut fields1 = HashMap::new();
fields1.insert("bond".to_string(), Value::Float(0.9));
fields1.insert("bond".to_string(), Value::Decimal(0.9));
let mut fields2 = HashMap::new();
fields2.insert("bond".to_string(), Value::Float(0.5));
fields2.insert("bond".to_string(), Value::Decimal(0.5));
let relationships = [
ResolvedRelationship {

View File

@@ -384,12 +384,12 @@ mod tests {
fields: vec![
Field {
name: "age".to_string(),
value: Value::Int(34),
value: Value::Number(34),
span: Span::new(0, 10),
},
Field {
name: "health".to_string(),
value: Value::Float(0.8),
value: Value::Decimal(0.8),
span: Span::new(10, 20),
},
],
@@ -403,8 +403,8 @@ mod tests {
assert_eq!(resolved.name, "Martha");
assert_eq!(resolved.fields.len(), 2);
assert_eq!(resolved.fields.get("age"), Some(&Value::Int(34)));
assert_eq!(resolved.fields.get("health"), Some(&Value::Float(0.8)));
assert_eq!(resolved.fields.get("age"), Some(&Value::Number(34)));
assert_eq!(resolved.fields.get("health"), Some(&Value::Decimal(0.8)));
assert_eq!(resolved.prose_blocks.len(), 0);
}
@@ -422,7 +422,7 @@ mod tests {
fields: vec![
Field {
name: "age".to_string(),
value: Value::Int(34),
value: Value::Number(34),
span: Span::new(0, 10),
},
Field {
@@ -441,7 +441,7 @@ mod tests {
assert_eq!(resolved.name, "Martha");
assert_eq!(resolved.fields.len(), 1);
assert_eq!(resolved.fields.get("age"), Some(&Value::Int(34)));
assert_eq!(resolved.fields.get("age"), Some(&Value::Number(34)));
assert_eq!(resolved.prose_blocks.len(), 1);
assert_eq!(resolved.prose_blocks.get("backstory"), Some(&prose_block));
}
@@ -454,12 +454,12 @@ mod tests {
fields: vec![
Field {
name: "age".to_string(),
value: Value::Int(34),
value: Value::Number(34),
span: Span::new(0, 10),
},
Field {
name: "age".to_string(),
value: Value::Int(35),
value: Value::Number(35),
span: Span::new(10, 20),
},
],
@@ -481,7 +481,7 @@ mod tests {
species: None,
fields: vec![Field {
name: "age".to_string(),
value: Value::Int(34),
value: Value::Number(34),
span: Span::new(0, 10),
}],
template: None,
@@ -550,7 +550,7 @@ mod tests {
let fields = vec![
Field {
name: "age".to_string(),
value: Value::Int(30),
value: Value::Number(30),
span: Span::new(0, 10),
},
Field {
@@ -560,7 +560,7 @@ mod tests {
},
Field {
name: "active".to_string(),
value: Value::Bool(true),
value: Value::Boolean(true),
span: Span::new(30, 40),
},
];
@@ -568,8 +568,8 @@ mod tests {
let (field_map, prose_map) = extract_fields_and_prose(&fields).unwrap();
assert_eq!(field_map.len(), 2);
assert_eq!(field_map.get("age"), Some(&Value::Int(30)));
assert_eq!(field_map.get("active"), Some(&Value::Bool(true)));
assert_eq!(field_map.get("age"), Some(&Value::Number(30)));
assert_eq!(field_map.get("active"), Some(&Value::Boolean(true)));
assert_eq!(prose_map.len(), 1);
assert_eq!(prose_map.get("description"), Some(&prose_block));
@@ -585,7 +585,7 @@ mod tests {
name: "Person".to_string(),
fields: vec![Field {
name: "type".to_string(), // Changed from "species"
value: Value::String("human".to_string()),
value: Value::Text("human".to_string()),
span: Span::new(0, 10),
}],
strict: false,
@@ -600,7 +600,7 @@ mod tests {
species: None,
fields: vec![Field {
name: "age".to_string(),
value: Value::Int(34),
value: Value::Number(34),
span: Span::new(0, 10),
}],
template: Some(vec!["Person".to_string()]),
@@ -624,10 +624,10 @@ mod tests {
assert_eq!(resolved.name, "Martha");
assert_eq!(resolved.fields.len(), 2);
assert_eq!(resolved.fields.get("age"), Some(&Value::Int(34)));
assert_eq!(resolved.fields.get("age"), Some(&Value::Number(34)));
assert_eq!(
resolved.fields.get("type"),
Some(&Value::String("human".to_string()))
Some(&Value::Text("human".to_string()))
);
}
@@ -639,7 +639,7 @@ mod tests {
name: "Physical".to_string(),
fields: vec![Field {
name: "height".to_string(),
value: Value::Int(0),
value: Value::Number(0),
span: Span::new(0, 10),
}],
strict: false,
@@ -653,7 +653,7 @@ mod tests {
name: "Mental".to_string(),
fields: vec![Field {
name: "iq".to_string(),
value: Value::Int(0),
value: Value::Number(0),
span: Span::new(0, 10),
}],
strict: false,
@@ -669,12 +669,12 @@ mod tests {
fields: vec![
Field {
name: "height".to_string(),
value: Value::Int(165),
value: Value::Number(165),
span: Span::new(0, 10),
},
Field {
name: "iq".to_string(),
value: Value::Int(120),
value: Value::Number(120),
span: Span::new(10, 20),
},
],
@@ -700,8 +700,8 @@ mod tests {
assert_eq!(resolved.name, "Martha");
assert_eq!(resolved.fields.len(), 2);
assert_eq!(resolved.fields.get("height"), Some(&Value::Int(165)));
assert_eq!(resolved.fields.get("iq"), Some(&Value::Int(120)));
assert_eq!(resolved.fields.get("height"), Some(&Value::Number(165)));
assert_eq!(resolved.fields.get("iq"), Some(&Value::Number(120)));
}
#[test]
@@ -712,7 +712,7 @@ mod tests {
name: "Human".to_string(),
fields: vec![Field {
name: "type".to_string(), // Changed from "species"
value: Value::String("human".to_string()),
value: Value::Text("human".to_string()),
span: Span::new(0, 10),
}],
strict: false,
@@ -726,7 +726,7 @@ mod tests {
name: "Person".to_string(),
fields: vec![Field {
name: "age".to_string(),
value: Value::Int(0),
value: Value::Number(0),
span: Span::new(0, 10),
}],
strict: false,
@@ -751,10 +751,10 @@ mod tests {
assert_eq!(resolved.name, "Person");
assert_eq!(resolved.fields.len(), 2);
assert_eq!(resolved.fields.get("age"), Some(&Value::Int(0)));
assert_eq!(resolved.fields.get("age"), Some(&Value::Number(0)));
assert_eq!(
resolved.fields.get("type"),
Some(&Value::String("human".to_string()))
Some(&Value::Text("human".to_string()))
);
}
@@ -765,7 +765,7 @@ mod tests {
species: None,
fields: vec![Field {
name: "species".to_string(), // Reserved keyword!
value: Value::String("human".to_string()),
value: Value::Text("human".to_string()),
span: Span::new(0, 10),
}],
template: None,
@@ -793,7 +793,7 @@ mod tests {
name: "Person".to_string(),
fields: vec![Field {
name: "age".to_string(),
value: Value::Range(Box::new(Value::Int(18)), Box::new(Value::Int(65))),
value: Value::Range(Box::new(Value::Number(18)), Box::new(Value::Number(65))),
span: Span::new(0, 10),
}],
strict: true,

View File

@@ -32,8 +32,8 @@ fn test_simple_character_end_to_end() {
| ResolvedDeclaration::Character(c) => {
assert_eq!(c.name, "Martha");
assert_eq!(c.fields.len(), 2);
assert_eq!(c.fields.get("age"), Some(&Value::Int(34)));
assert_eq!(c.fields.get("health"), Some(&Value::Float(0.8)));
assert_eq!(c.fields.get("age"), Some(&Value::Number(34)));
assert_eq!(c.fields.get("health"), Some(&Value::Decimal(0.8)));
},
| _ => panic!("Expected Character"),
}
@@ -58,7 +58,7 @@ She loved baking from a young age.
| ResolvedDeclaration::Character(c) => {
assert_eq!(c.name, "Martha");
assert_eq!(c.fields.len(), 1);
assert_eq!(c.fields.get("age"), Some(&Value::Int(34)));
assert_eq!(c.fields.get("age"), Some(&Value::Number(34)));
assert_eq!(c.prose_blocks.len(), 1);
let backstory = c.prose_blocks.get("backstory").unwrap();
@@ -108,7 +108,7 @@ fn test_relationship_end_to_end() {
| ResolvedDeclaration::Relationship(r) => {
assert_eq!(r.name, "Spousal");
assert_eq!(r.participants.len(), 2);
assert_eq!(r.fields.get("bond"), Some(&Value::Float(0.9)));
assert_eq!(r.fields.get("bond"), Some(&Value::Decimal(0.9)));
},
| _ => panic!("Expected Relationship"),
}
@@ -208,8 +208,8 @@ fn test_institution_end_to_end() {
match &resolved[0] {
| ResolvedDeclaration::Institution(i) => {
assert_eq!(i.name, "Bakery");
assert_eq!(i.fields.get("employees"), Some(&Value::Int(5)));
assert_eq!(i.fields.get("revenue"), Some(&Value::Int(50000)));
assert_eq!(i.fields.get("employees"), Some(&Value::Number(5)));
assert_eq!(i.fields.get("revenue"), Some(&Value::Number(50000)));
},
| _ => panic!("Expected Institution"),
}
@@ -230,8 +230,8 @@ fn test_location_end_to_end() {
match &resolved[0] {
| ResolvedDeclaration::Location(l) => {
assert_eq!(l.name, "Bakery");
assert_eq!(l.fields.get("x"), Some(&Value::Int(100)));
assert_eq!(l.fields.get("y"), Some(&Value::Int(200)));
assert_eq!(l.fields.get("x"), Some(&Value::Number(100)));
assert_eq!(l.fields.get("y"), Some(&Value::Number(200)));
},
| _ => panic!("Expected Location"),
}
@@ -252,8 +252,8 @@ fn test_species_end_to_end() {
match &resolved[0] {
| ResolvedDeclaration::Species(s) => {
assert_eq!(s.name, "Human");
assert_eq!(s.fields.get("lifespan"), Some(&Value::Int(80)));
assert_eq!(s.fields.get("intelligence"), Some(&Value::Float(0.9)));
assert_eq!(s.fields.get("lifespan"), Some(&Value::Number(80)));
assert_eq!(s.fields.get("intelligence"), Some(&Value::Decimal(0.9)));
},
| _ => panic!("Expected Species"),
}
@@ -375,12 +375,12 @@ fn test_all_value_types_convert() {
let resolved = parse_and_convert(source).unwrap();
match &resolved[0] {
| ResolvedDeclaration::Character(c) => {
assert_eq!(c.fields.get("int_val"), Some(&Value::Int(42)));
assert_eq!(c.fields.get("float_val"), Some(&Value::Float(3.5)));
assert_eq!(c.fields.get("bool_val"), Some(&Value::Bool(true)));
assert_eq!(c.fields.get("int_val"), Some(&Value::Number(42)));
assert_eq!(c.fields.get("float_val"), Some(&Value::Decimal(3.5)));
assert_eq!(c.fields.get("bool_val"), Some(&Value::Boolean(true)));
assert_eq!(
c.fields.get("string_val"),
Some(&Value::String("hello".to_string()))
Some(&Value::Text("hello".to_string()))
);
},
| _ => panic!("Expected Character"),

View File

@@ -54,12 +54,12 @@ fn valid_ident() -> impl Strategy<Value = String> {
fn valid_value() -> impl Strategy<Value = Value> {
prop_oneof![
(-1000i64..1000).prop_map(Value::Int),
(-1000i64..1000).prop_map(Value::Number),
(-1000.0..1000.0)
.prop_filter("finite", |f: &f64| f.is_finite())
.prop_map(Value::Float),
any::<bool>().prop_map(Value::Bool),
"[a-zA-Z0-9 ]{0,30}".prop_map(Value::String),
.prop_map(Value::Decimal),
any::<bool>().prop_map(Value::Boolean),
"[a-zA-Z0-9 ]{0,30}".prop_map(Value::Text),
]
}
@@ -296,22 +296,22 @@ mod edge_cases {
fields: vec![
Field {
name: "int_field".to_string(),
value: Value::Int(int_val),
value: Value::Number(int_val),
span: Span::new(0, 10),
},
Field {
name: "float_field".to_string(),
value: Value::Float(float_val),
value: Value::Decimal(float_val),
span: Span::new(10, 20),
},
Field {
name: "bool_field".to_string(),
value: Value::Bool(bool_val),
value: Value::Boolean(bool_val),
span: Span::new(20, 30),
},
Field {
name: "string_field".to_string(),
value: Value::String(string_val.clone()),
value: Value::Text(string_val.clone()),
span: Span::new(30, 40),
},
],
@@ -322,10 +322,10 @@ mod edge_cases {
};
let resolved = convert_character(&character).unwrap();
assert_eq!(resolved.fields.get("int_field"), Some(&Value::Int(int_val)));
assert_eq!(resolved.fields.get("float_field"), Some(&Value::Float(float_val)));
assert_eq!(resolved.fields.get("bool_field"), Some(&Value::Bool(bool_val)));
assert_eq!(resolved.fields.get("string_field"), Some(&Value::String(string_val)));
assert_eq!(resolved.fields.get("int_field"), Some(&Value::Number(int_val)));
assert_eq!(resolved.fields.get("float_field"), Some(&Value::Decimal(float_val)));
assert_eq!(resolved.fields.get("bool_field"), Some(&Value::Boolean(bool_val)));
assert_eq!(resolved.fields.get("string_field"), Some(&Value::Text(string_val)));
}
#[test]
@@ -341,7 +341,7 @@ mod edge_cases {
species: None,
fields: vec![Field {
name: field_name.clone(),
value: Value::String(string_val.clone()),
value: Value::Text(string_val.clone()),
span: Span::new(0, 10),
}],
template: None,
@@ -354,7 +354,7 @@ mod edge_cases {
assert_eq!(resolved.name, name);
assert_eq!(
resolved.fields.get(&field_name),
Some(&Value::String(string_val))
Some(&Value::Text(string_val))
);
}
}

View File

@@ -194,7 +194,7 @@ mod tests {
fn make_field(name: &str, value: i64) -> Field {
Field {
name: name.to_string(),
value: Value::Int(value),
value: Value::Number(value),
span: Span::new(0, 10),
}
}

View File

@@ -48,7 +48,7 @@ fn valid_ident() -> impl Strategy<Value = String> {
fn valid_field() -> impl Strategy<Value = Field> {
(valid_ident(), 0i64..100).prop_map(|(name, value)| Field {
name,
value: Value::Int(value),
value: Value::Number(value),
span: Span::new(0, 10),
})
}

View File

@@ -430,7 +430,7 @@ mod tests {
fn make_field(name: &str, value: i64) -> Field {
Field {
name: name.to_string(),
value: Value::Int(value),
value: Value::Number(value),
span: Span::new(0, 10),
}
}
@@ -445,7 +445,7 @@ mod tests {
assert_eq!(result.len(), 2);
let age_field = result.iter().find(|f| f.name == "age").unwrap();
assert_eq!(age_field.value, Value::Int(30));
assert_eq!(age_field.value, Value::Number(30));
}
#[test]
@@ -526,7 +526,7 @@ mod tests {
assert_eq!(result.len(), 3);
let age = result.iter().find(|f| f.name == "age").unwrap();
assert_eq!(age.value, Value::Int(30));
assert_eq!(age.value, Value::Number(30));
assert!(!result.iter().any(|f| f.name == "energy"));
assert!(result.iter().any(|f| f.name == "strength"));
@@ -612,7 +612,7 @@ mod tests {
assert_eq!(result.len(), 1);
assert_eq!(result[0].name, "age");
assert_eq!(result[0].value, Value::Int(25));
assert_eq!(result[0].value, Value::Number(25));
}
#[test]
@@ -682,8 +682,8 @@ mod tests {
assert_eq!(result.len(), 1);
assert_eq!(result[0].name, "age");
assert_eq!(result[0].value, Value::Int(25)); // Should be overridden
// value
assert_eq!(result[0].value, Value::Number(25)); // Should be overridden
// value
}
#[test]
@@ -702,8 +702,8 @@ mod tests {
assert_eq!(result.len(), 1);
assert_eq!(result[0].name, "age");
assert_eq!(result[0].value, Value::Int(34)); // Character's value
// overrides template
assert_eq!(result[0].value, Value::Number(34)); // Character's value
// overrides template
}
#[test]
@@ -729,10 +729,10 @@ mod tests {
assert_eq!(result.len(), 2);
assert!(result
.iter()
.any(|f| f.name == "height" && f.value == Value::Int(165)));
.any(|f| f.name == "height" && f.value == Value::Number(165)));
assert!(result
.iter()
.any(|f| f.name == "iq" && f.value == Value::Int(120)));
.any(|f| f.name == "iq" && f.value == Value::Number(120)));
}
#[test]
@@ -758,10 +758,10 @@ mod tests {
assert_eq!(result.len(), 2);
assert!(result
.iter()
.any(|f| f.name == "age" && f.value == Value::Int(34)));
.any(|f| f.name == "age" && f.value == Value::Number(34)));
assert!(result
.iter()
.any(|f| f.name == "name" && f.value == Value::Int(1)));
.any(|f| f.name == "name" && f.value == Value::Number(1)));
}
#[test]
@@ -786,7 +786,7 @@ mod tests {
"Person",
vec![Field {
name: "age".to_string(),
value: Value::Range(Box::new(Value::Int(18)), Box::new(Value::Int(65))),
value: Value::Range(Box::new(Value::Number(18)), Box::new(Value::Number(65))),
span: Span::new(0, 10),
}],
vec![],
@@ -837,7 +837,7 @@ mod tests {
assert_eq!(result.len(), 2);
let age = result.iter().find(|f| f.name == "age").unwrap();
assert_eq!(age.value, Value::Int(30));
assert_eq!(age.value, Value::Number(30));
}
#[test]

View File

@@ -19,7 +19,7 @@ fn valid_ident() -> impl Strategy<Value = String> {
fn valid_field() -> impl Strategy<Value = Field> {
(valid_ident(), 0i64..1000).prop_map(|(name, value)| Field {
name,
value: Value::Int(value),
value: Value::Number(value),
span: Span::new(0, 10),
})
}
@@ -186,12 +186,12 @@ proptest! {
) {
let field1 = Field {
name: name.clone(),
value: Value::Int(val1),
value: Value::Number(val1),
span: Span::new(0, 10),
};
let field2 = Field {
name: name.clone(),
value: Value::Int(val2),
value: Value::Number(val2),
span: Span::new(0, 10),
};
@@ -251,12 +251,12 @@ proptest! {
) {
let field1 = Field {
name: name.clone(),
value: Value::Int(val1),
value: Value::Number(val1),
span: Span::new(0, 10),
};
let field2 = Field {
name: name.clone(),
value: Value::Int(val2),
value: Value::Number(val2),
span: Span::new(0, 10),
};
@@ -277,8 +277,8 @@ proptest! {
let value1 = result1.iter().find(|f| f.name == name).unwrap().value.clone();
let value2 = result2.iter().find(|f| f.name == name).unwrap().value.clone();
assert_eq!(value1, Value::Int(val2));
assert_eq!(value2, Value::Int(val1));
assert_eq!(value1, Value::Number(val2));
assert_eq!(value2, Value::Number(val1));
}
#[test]

View File

@@ -83,7 +83,7 @@ pub fn validate_no_reserved_keywords(fields: &[Field], collector: &mut ErrorColl
pub fn validate_trait_ranges(fields: &[Field], collector: &mut ErrorCollector) {
for field in fields {
match &field.value {
| Value::Float(f) => {
| Value::Decimal(f) => {
// Normalized trait values should be 0.0 .. 1.0
if (field.name.ends_with("_normalized") ||
field.name == "bond" ||
@@ -99,7 +99,7 @@ pub fn validate_trait_ranges(fields: &[Field], collector: &mut ErrorCollector) {
});
}
},
| Value::Int(i) => {
| Value::Number(i) => {
// Age should be reasonable
if field.name == "age" && (*i < 0 || *i > 150) {
collector.add(ResolveError::TraitOutOfRange {
@@ -120,7 +120,7 @@ pub fn validate_relationship_bonds(relationships: &[Relationship], collector: &m
for rel in relationships {
for field in &rel.fields {
if field.name == "bond" {
if let Value::Float(f) = field.value {
if let Value::Decimal(f) = field.value {
if !(0.0..=1.0).contains(&f) {
collector.add(ResolveError::TraitOutOfRange {
field: "bond".to_string(),
@@ -457,12 +457,12 @@ mod tests {
let fields = vec![
Field {
name: "bond".to_string(),
value: Value::Float(0.8),
value: Value::Decimal(0.8),
span: Span::new(0, 10),
},
Field {
name: "age".to_string(),
value: Value::Int(30),
value: Value::Number(30),
span: Span::new(0, 10),
},
];
@@ -476,7 +476,7 @@ mod tests {
fn test_invalid_bond_value_too_high() {
let fields = vec![Field {
name: "bond".to_string(),
value: Value::Float(1.5),
value: Value::Decimal(1.5),
span: Span::new(0, 10),
}];
@@ -489,7 +489,7 @@ mod tests {
fn test_invalid_bond_value_negative() {
let fields = vec![Field {
name: "bond".to_string(),
value: Value::Float(-0.1),
value: Value::Decimal(-0.1),
span: Span::new(0, 10),
}];
@@ -502,7 +502,7 @@ mod tests {
fn test_invalid_age_negative() {
let fields = vec![Field {
name: "age".to_string(),
value: Value::Int(-5),
value: Value::Number(-5),
span: Span::new(0, 10),
}];
@@ -515,7 +515,7 @@ mod tests {
fn test_invalid_age_too_high() {
let fields = vec![Field {
name: "age".to_string(),
value: Value::Int(200),
value: Value::Number(200),
span: Span::new(0, 10),
}];
@@ -531,7 +531,7 @@ mod tests {
participants: vec![],
fields: vec![Field {
name: "bond".to_string(),
value: Value::Float(0.9),
value: Value::Decimal(0.9),
span: Span::new(0, 10),
}],
span: Span::new(0, 100),
@@ -549,7 +549,7 @@ mod tests {
participants: vec![],
fields: vec![Field {
name: "bond".to_string(),
value: Value::Float(1.2),
value: Value::Decimal(1.2),
span: Span::new(0, 10),
}],
span: Span::new(0, 100),

View File

@@ -17,7 +17,7 @@ use crate::{
fn valid_bond_field() -> impl Strategy<Value = Field> {
(0.0..=1.0).prop_map(|f| Field {
name: "bond".to_string(),
value: Value::Float(f),
value: Value::Decimal(f),
span: Span::new(0, 10),
})
}
@@ -26,12 +26,12 @@ fn invalid_bond_field() -> impl Strategy<Value = Field> {
prop_oneof![
(-100.0..0.0).prop_map(|f| Field {
name: "bond".to_string(),
value: Value::Float(f),
value: Value::Decimal(f),
span: Span::new(0, 10),
}),
(1.0..100.0).prop_map(|f| Field {
name: "bond".to_string(),
value: Value::Float(f),
value: Value::Decimal(f),
span: Span::new(0, 10),
}),
]
@@ -40,7 +40,7 @@ fn invalid_bond_field() -> impl Strategy<Value = Field> {
fn valid_age_field() -> impl Strategy<Value = Field> {
(0i64..=150).prop_map(|age| Field {
name: "age".to_string(),
value: Value::Int(age),
value: Value::Number(age),
span: Span::new(0, 10),
})
}
@@ -49,12 +49,12 @@ fn invalid_age_field() -> impl Strategy<Value = Field> {
prop_oneof![
(-100i64..-1).prop_map(|age| Field {
name: "age".to_string(),
value: Value::Int(age),
value: Value::Number(age),
span: Span::new(0, 10),
}),
(151i64..300).prop_map(|age| Field {
name: "age".to_string(),
value: Value::Int(age),
value: Value::Number(age),
span: Span::new(0, 10),
}),
]
@@ -99,7 +99,7 @@ proptest! {
fn test_bond_exact_bounds(f in 0.0f64..=1.0) {
let field = Field {
name: "bond".to_string(),
value: Value::Float(f),
value: Value::Decimal(f),
span: Span::new(0, 10),
};
let mut collector = ErrorCollector::new();
@@ -116,7 +116,7 @@ proptest! {
participants: vec![],
fields: vec![Field {
name: "bond".to_string(),
value: Value::Float(bond_value),
value: Value::Decimal(bond_value),
span: Span::new(0, 10),
}],
span: Span::new(0, 100),
@@ -136,7 +136,7 @@ proptest! {
participants: vec![],
fields: vec![Field {
name: "bond".to_string(),
value: Value::Float(bond_value),
value: Value::Decimal(bond_value),
span: Span::new(0, 10),
}],
span: Span::new(0, 100),
@@ -164,7 +164,7 @@ proptest! {
on_enter: None,
transitions: vec![Transition {
to: state2_name.clone(),
condition: Expr::BoolLit(true),
condition: Expr::BooleanLit(true),
span: Span::new(0, 10),
}],
span: Span::new(0, 50),

View File

@@ -158,10 +158,10 @@ pub struct Field {
/// Field value types
#[derive(Debug, Clone, PartialEq)]
pub enum Value {
Int(i64),
Float(f64),
String(String),
Bool(bool),
Number(i64),
Decimal(f64),
Text(String),
Boolean(bool),
Range(Box<Value>, Box<Value>), // For templates: 20..40
Time(Time),
Duration(Duration),
@@ -466,10 +466,10 @@ pub enum Condition {
/// Expression AST for conditions and queries
#[derive(Debug, Clone, PartialEq)]
pub enum Expr {
IntLit(i64),
FloatLit(f64),
StringLit(String),
BoolLit(bool),
NumberLit(i64),
DecimalLit(f64),
TextLit(String),
BooleanLit(bool),
Identifier(Vec<String>),
FieldAccess(Box<Expr>, String),
Comparison(Box<Expr>, CompOp, Box<Expr>),

View File

@@ -244,18 +244,18 @@ Field: Field = {
};
Value: Value = {
<NumberLit> => Value::Int(<>),
<DecimalLit> => Value::Float(<>),
<TextLit> => Value::String(<>),
<BoolLit> => Value::Bool(<>),
<NumberLit> => Value::Number(<>),
<DecimalLit> => Value::Decimal(<>),
<TextLit> => Value::Text(<>),
<BoolLit> => Value::Boolean(<>),
"any" => Value::Any,
<lo:NumberLit> ".." <hi:NumberLit> => Value::Range(
Box::new(Value::Int(lo)),
Box::new(Value::Int(hi))
Box::new(Value::Number(lo)),
Box::new(Value::Number(hi))
),
<lo:DecimalLit> ".." <hi:DecimalLit> => Value::Range(
Box::new(Value::Float(lo)),
Box::new(Value::Float(hi))
Box::new(Value::Decimal(lo)),
Box::new(Value::Decimal(hi))
),
<t:Time> => Value::Time(t),
<d:Duration> => Value::Duration(d),
@@ -949,10 +949,10 @@ InequalityOp: CompOp = {
};
Literal: Expr = {
<NumberLit> => Expr::IntLit(<>),
<DecimalLit> => Expr::FloatLit(<>),
<TextLit> => Expr::StringLit(<>),
<BoolLit> => Expr::BoolLit(<>),
<NumberLit> => Expr::NumberLit(<>),
<DecimalLit> => Expr::DecimalLit(<>),
<TextLit> => Expr::TextLit(<>),
<BoolLit> => Expr::BooleanLit(<>),
};
// ===== Helpers =====

View File

@@ -1,5 +1,5 @@
// auto-generated: "lalrpop 0.21.0"
// sha3: 74aa329f2c008adb120b171834cf36dcc8e33b949ab1a2fe8bd5e54d42b398af
// sha3: 18bbe1925e11ceeec53b1eb8484775a415600ddf99220fba53d408b46198a3aa
use crate::syntax::{
ast::*,
lexer::Token,
@@ -13528,7 +13528,7 @@ fn __action46((_, pb, _): (usize, ProseBlock, usize)) -> Field {
clippy::just_underscores_and_digits
)]
fn __action47((_, __0, _): (usize, i64, usize)) -> Value {
Value::Int(__0)
Value::Number(__0)
}
#[allow(
@@ -13537,7 +13537,7 @@ fn __action47((_, __0, _): (usize, i64, usize)) -> Value {
clippy::just_underscores_and_digits
)]
fn __action48((_, __0, _): (usize, f64, usize)) -> Value {
Value::Float(__0)
Value::Decimal(__0)
}
#[allow(
@@ -13546,7 +13546,7 @@ fn __action48((_, __0, _): (usize, f64, usize)) -> Value {
clippy::just_underscores_and_digits
)]
fn __action49((_, __0, _): (usize, String, usize)) -> Value {
Value::String(__0)
Value::Text(__0)
}
#[allow(
@@ -13555,7 +13555,7 @@ fn __action49((_, __0, _): (usize, String, usize)) -> Value {
clippy::just_underscores_and_digits
)]
fn __action50((_, __0, _): (usize, bool, usize)) -> Value {
Value::Bool(__0)
Value::Boolean(__0)
}
#[allow(
@@ -13577,7 +13577,7 @@ fn __action52(
(_, _, _): (usize, Token, usize),
(_, hi, _): (usize, i64, usize),
) -> Value {
Value::Range(Box::new(Value::Int(lo)), Box::new(Value::Int(hi)))
Value::Range(Box::new(Value::Number(lo)), Box::new(Value::Number(hi)))
}
#[allow(
@@ -13590,7 +13590,7 @@ fn __action53(
(_, _, _): (usize, Token, usize),
(_, hi, _): (usize, f64, usize),
) -> Value {
Value::Range(Box::new(Value::Float(lo)), Box::new(Value::Float(hi)))
Value::Range(Box::new(Value::Decimal(lo)), Box::new(Value::Decimal(hi)))
}
#[allow(
@@ -15225,7 +15225,7 @@ fn __action161((_, __0, _): (usize, Token, usize)) -> CompOp {
clippy::just_underscores_and_digits
)]
fn __action162((_, __0, _): (usize, i64, usize)) -> Expr {
Expr::IntLit(__0)
Expr::NumberLit(__0)
}
#[allow(
@@ -15234,7 +15234,7 @@ fn __action162((_, __0, _): (usize, i64, usize)) -> Expr {
clippy::just_underscores_and_digits
)]
fn __action163((_, __0, _): (usize, f64, usize)) -> Expr {
Expr::FloatLit(__0)
Expr::DecimalLit(__0)
}
#[allow(
@@ -15243,7 +15243,7 @@ fn __action163((_, __0, _): (usize, f64, usize)) -> Expr {
clippy::just_underscores_and_digits
)]
fn __action164((_, __0, _): (usize, String, usize)) -> Expr {
Expr::StringLit(__0)
Expr::TextLit(__0)
}
#[allow(
@@ -15252,7 +15252,7 @@ fn __action164((_, __0, _): (usize, String, usize)) -> Expr {
clippy::just_underscores_and_digits
)]
fn __action165((_, __0, _): (usize, bool, usize)) -> Expr {
Expr::BoolLit(__0)
Expr::BooleanLit(__0)
}
#[allow(

View File

@@ -73,10 +73,10 @@ fn character_editor_view<'a>(editor: &'a Editor, char_name: &str) -> Element<'a,
// Show all fields with soft metadata styling
for (field_name, field_value) in &char.fields {
let value_text = match field_value {
| Value::Int(n) => format!("{}", n),
| Value::Float(n) => format!("{}", n),
| Value::String(s) => s.clone(),
| Value::Bool(b) => format!("{}", b),
| Value::Number(n) => format!("{}", n),
| Value::Decimal(n) => format!("{}", n),
| Value::Text(s) => s.clone(),
| Value::Boolean(b) => format!("{}", b),
| Value::List(items) => {
format!("[{} items]", items.len())
},

View File

@@ -106,15 +106,15 @@ fn test_baker_family_field_values() {
let martha = project.find_character("Martha").unwrap();
// Martha sets age: 34 (overriding Person template range 0..100)
if let Some(storybook::syntax::ast::Value::Int(age)) = martha.fields.get("age") {
if let Some(storybook::syntax::ast::Value::Number(age)) = martha.fields.get("age") {
assert_eq!(*age, 34, "Martha's age should be 34");
} else {
panic!("age should be an Int");
panic!("age should be a Number");
}
// Martha sets specialty: "sourdough" (overriding Baker template default
// "bread")
if let Some(storybook::syntax::ast::Value::String(specialty)) = martha.fields.get("specialty") {
if let Some(storybook::syntax::ast::Value::Text(specialty)) = martha.fields.get("specialty") {
assert_eq!(
specialty, "sourdough",
"Martha's specialty should be sourdough"