feat(ast): add species base to template declarations
Added optional species_base field to Template struct enabling
template-species inheritance syntax: `template Name: Species { ... }`.
Updated LALRPOP grammar and all Template construction sites.
This commit is contained in:
@@ -583,6 +583,7 @@ mod tests {
|
|||||||
|
|
||||||
let template = ast::Template {
|
let template = ast::Template {
|
||||||
name: "Person".to_string(),
|
name: "Person".to_string(),
|
||||||
|
species_base: None,
|
||||||
fields: vec![Field {
|
fields: vec![Field {
|
||||||
name: "type".to_string(), // Changed from "species"
|
name: "type".to_string(), // Changed from "species"
|
||||||
value: Value::Text("human".to_string()),
|
value: Value::Text("human".to_string()),
|
||||||
@@ -637,6 +638,7 @@ mod tests {
|
|||||||
|
|
||||||
let physical = ast::Template {
|
let physical = ast::Template {
|
||||||
name: "Physical".to_string(),
|
name: "Physical".to_string(),
|
||||||
|
species_base: None,
|
||||||
fields: vec![Field {
|
fields: vec![Field {
|
||||||
name: "height".to_string(),
|
name: "height".to_string(),
|
||||||
value: Value::Number(0),
|
value: Value::Number(0),
|
||||||
@@ -651,6 +653,7 @@ mod tests {
|
|||||||
|
|
||||||
let mental = ast::Template {
|
let mental = ast::Template {
|
||||||
name: "Mental".to_string(),
|
name: "Mental".to_string(),
|
||||||
|
species_base: None,
|
||||||
fields: vec![Field {
|
fields: vec![Field {
|
||||||
name: "iq".to_string(),
|
name: "iq".to_string(),
|
||||||
value: Value::Number(0),
|
value: Value::Number(0),
|
||||||
@@ -710,6 +713,7 @@ mod tests {
|
|||||||
|
|
||||||
let base = ast::Template {
|
let base = ast::Template {
|
||||||
name: "Human".to_string(),
|
name: "Human".to_string(),
|
||||||
|
species_base: None,
|
||||||
fields: vec![Field {
|
fields: vec![Field {
|
||||||
name: "type".to_string(), // Changed from "species"
|
name: "type".to_string(), // Changed from "species"
|
||||||
value: Value::Text("human".to_string()),
|
value: Value::Text("human".to_string()),
|
||||||
@@ -724,6 +728,7 @@ mod tests {
|
|||||||
|
|
||||||
let derived = ast::Template {
|
let derived = ast::Template {
|
||||||
name: "Person".to_string(),
|
name: "Person".to_string(),
|
||||||
|
species_base: None,
|
||||||
fields: vec![Field {
|
fields: vec![Field {
|
||||||
name: "age".to_string(),
|
name: "age".to_string(),
|
||||||
value: Value::Number(0),
|
value: Value::Number(0),
|
||||||
@@ -791,6 +796,7 @@ mod tests {
|
|||||||
|
|
||||||
let template = ast::Template {
|
let template = ast::Template {
|
||||||
name: "Person".to_string(),
|
name: "Person".to_string(),
|
||||||
|
species_base: None,
|
||||||
fields: vec![Field {
|
fields: vec![Field {
|
||||||
name: "age".to_string(),
|
name: "age".to_string(),
|
||||||
value: Value::Range(Box::new(Value::Number(18)), Box::new(Value::Number(65))),
|
value: Value::Range(Box::new(Value::Number(18)), Box::new(Value::Number(65))),
|
||||||
|
|||||||
@@ -446,6 +446,7 @@ proptest! {
|
|||||||
})),
|
})),
|
||||||
valid_ident().prop_map(|name| Declaration::Template(Template {
|
valid_ident().prop_map(|name| Declaration::Template(Template {
|
||||||
name,
|
name,
|
||||||
|
species_base: None,
|
||||||
fields: vec![],
|
fields: vec![],
|
||||||
strict: false,
|
strict: false,
|
||||||
includes: vec![],
|
includes: vec![],
|
||||||
|
|||||||
@@ -574,6 +574,7 @@ mod tests {
|
|||||||
) -> Template {
|
) -> Template {
|
||||||
Template {
|
Template {
|
||||||
name: name.to_string(),
|
name: name.to_string(),
|
||||||
|
species_base: None,
|
||||||
fields,
|
fields,
|
||||||
includes: includes.iter().map(|s| s.to_string()).collect(),
|
includes: includes.iter().map(|s| s.to_string()).collect(),
|
||||||
strict,
|
strict,
|
||||||
|
|||||||
@@ -331,6 +331,7 @@ mod tests {
|
|||||||
}),
|
}),
|
||||||
Declaration::Template(Template {
|
Declaration::Template(Template {
|
||||||
name: "Person".to_string(),
|
name: "Person".to_string(),
|
||||||
|
species_base: None,
|
||||||
fields: vec![],
|
fields: vec![],
|
||||||
strict: false,
|
strict: false,
|
||||||
includes: vec![],
|
includes: vec![],
|
||||||
|
|||||||
@@ -68,6 +68,7 @@ fn valid_template_decl() -> impl Strategy<Value = (String, Declaration)> {
|
|||||||
valid_ident().prop_map(|name| {
|
valid_ident().prop_map(|name| {
|
||||||
let decl = Declaration::Template(Template {
|
let decl = Declaration::Template(Template {
|
||||||
name: name.clone(),
|
name: name.clone(),
|
||||||
|
species_base: None,
|
||||||
fields: vec![],
|
fields: vec![],
|
||||||
strict: false,
|
strict: false,
|
||||||
includes: vec![],
|
includes: vec![],
|
||||||
|
|||||||
@@ -138,6 +138,7 @@ pub struct Character {
|
|||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub struct Template {
|
pub struct Template {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
pub species_base: Option<String>, // `: Species` - type constraint from species
|
||||||
pub fields: Vec<Field>,
|
pub fields: Vec<Field>,
|
||||||
pub strict: bool,
|
pub strict: bool,
|
||||||
pub includes: Vec<String>,
|
pub includes: Vec<String>,
|
||||||
|
|||||||
@@ -169,7 +169,7 @@ UsesScheduleClause: Vec<String> = {
|
|||||||
// ===== Template =====
|
// ===== Template =====
|
||||||
|
|
||||||
Template: Template = {
|
Template: Template = {
|
||||||
"template" <name:Ident> <strict:"strict"?> "{" <body:TemplateBodyItem*> "}" => {
|
"template" <name:Ident> <species_base:(":" <Ident>)?> <strict:"strict"?> "{" <body:TemplateBodyItem*> "}" => {
|
||||||
let mut fields = Vec::new();
|
let mut fields = Vec::new();
|
||||||
let mut includes = Vec::new();
|
let mut includes = Vec::new();
|
||||||
let mut uses_behaviors = None;
|
let mut uses_behaviors = None;
|
||||||
@@ -186,6 +186,7 @@ Template: Template = {
|
|||||||
|
|
||||||
Template {
|
Template {
|
||||||
name,
|
name,
|
||||||
|
species_base,
|
||||||
fields,
|
fields,
|
||||||
strict: strict.is_some(),
|
strict: strict.is_some(),
|
||||||
includes,
|
includes,
|
||||||
|
|||||||
3298
src/syntax/parser.rs
3298
src/syntax/parser.rs
File diff suppressed because it is too large
Load Diff
@@ -182,3 +182,91 @@ sub_concept Cup.Material { Glass, Ceramic, Metal }
|
|||||||
| _ => panic!("Expected SubConcept"),
|
| _ => panic!("Expected SubConcept"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ===== Template species base tests =====
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_template_with_species_base() {
|
||||||
|
let input = r#"
|
||||||
|
template Person: Human {
|
||||||
|
age: 30
|
||||||
|
name: "default"
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
let file = parse(input);
|
||||||
|
|
||||||
|
assert_eq!(file.declarations.len(), 1);
|
||||||
|
match &file.declarations[0] {
|
||||||
|
| Declaration::Template(t) => {
|
||||||
|
assert_eq!(t.name, "Person");
|
||||||
|
assert_eq!(t.species_base, Some("Human".to_string()));
|
||||||
|
assert_eq!(t.fields.len(), 2);
|
||||||
|
},
|
||||||
|
| _ => panic!("Expected Template declaration"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_template_without_species_base() {
|
||||||
|
let input = r#"
|
||||||
|
template Person {
|
||||||
|
age: 30
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
let file = parse(input);
|
||||||
|
|
||||||
|
assert_eq!(file.declarations.len(), 1);
|
||||||
|
match &file.declarations[0] {
|
||||||
|
| Declaration::Template(t) => {
|
||||||
|
assert_eq!(t.name, "Person");
|
||||||
|
assert_eq!(t.species_base, None);
|
||||||
|
assert_eq!(t.fields.len(), 1);
|
||||||
|
},
|
||||||
|
| _ => panic!("Expected Template declaration"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_template_strict_with_species_base() {
|
||||||
|
let input = r#"
|
||||||
|
template Warrior: Human strict {
|
||||||
|
strength: 50..100
|
||||||
|
weapon: "sword"
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
let file = parse(input);
|
||||||
|
|
||||||
|
assert_eq!(file.declarations.len(), 1);
|
||||||
|
match &file.declarations[0] {
|
||||||
|
| Declaration::Template(t) => {
|
||||||
|
assert_eq!(t.name, "Warrior");
|
||||||
|
assert_eq!(t.species_base, Some("Human".to_string()));
|
||||||
|
assert!(t.strict);
|
||||||
|
assert_eq!(t.fields.len(), 2);
|
||||||
|
},
|
||||||
|
| _ => panic!("Expected Template declaration"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_template_species_base_with_includes() {
|
||||||
|
let input = r#"
|
||||||
|
template Knight: Human {
|
||||||
|
include Warrior
|
||||||
|
honor: 80
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
let file = parse(input);
|
||||||
|
|
||||||
|
assert_eq!(file.declarations.len(), 1);
|
||||||
|
match &file.declarations[0] {
|
||||||
|
| Declaration::Template(t) => {
|
||||||
|
assert_eq!(t.name, "Knight");
|
||||||
|
assert_eq!(t.species_base, Some("Human".to_string()));
|
||||||
|
assert_eq!(t.includes, vec!["Warrior".to_string()]);
|
||||||
|
assert_eq!(t.fields.len(), 1);
|
||||||
|
assert_eq!(t.fields[0].name, "honor");
|
||||||
|
},
|
||||||
|
| _ => panic!("Expected Template declaration"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user