feat(resolver): implement cross-file template resolution
Enable characters to inherit from templates defined in different files across the project structure. - Add file_index field to NameEntry to track declaration source files - Update NameTable::from_files() to set file indices when merging tables - Change conversion pipeline to pass &[ast::File] instead of flat arrays - Update merge functions to use two-level indexing: all_files[entry.file_index].declarations[entry.decl_index] - Update all affected tests to use new signatures
This commit is contained in:
@@ -42,7 +42,7 @@ use crate::{
|
||||
/// Returns the fully merged fields for this template
|
||||
pub fn resolve_template_includes(
|
||||
template: &Template,
|
||||
declarations: &[Declaration],
|
||||
all_files: &[crate::syntax::ast::File],
|
||||
name_table: &NameTable,
|
||||
visited: &mut HashSet<String>,
|
||||
) -> Result<Vec<Field>> {
|
||||
@@ -69,8 +69,8 @@ pub fn resolve_template_includes(
|
||||
suggestion: name_table.find_suggestion(include_name),
|
||||
})?;
|
||||
|
||||
// Get the template declaration
|
||||
let included_template = match &declarations[entry.decl_index] {
|
||||
// Get the template declaration using two-level indexing
|
||||
let included_template = match &all_files[entry.file_index].declarations[entry.decl_index] {
|
||||
| Declaration::Template(t) => t,
|
||||
| _ => {
|
||||
return Err(ResolveError::ValidationError {
|
||||
@@ -88,7 +88,7 @@ pub fn resolve_template_includes(
|
||||
|
||||
// Recursively resolve the included template
|
||||
let included_fields =
|
||||
resolve_template_includes(included_template, declarations, name_table, visited)?;
|
||||
resolve_template_includes(included_template, all_files, name_table, visited)?;
|
||||
|
||||
// Merge included fields (replacing any existing fields with same name)
|
||||
merged_fields = merge_field_lists(merged_fields, included_fields);
|
||||
@@ -114,7 +114,7 @@ pub fn resolve_template_includes(
|
||||
/// Returns the fully merged fields for this character
|
||||
pub fn merge_character_templates(
|
||||
character: &Character,
|
||||
declarations: &[Declaration],
|
||||
all_files: &[crate::syntax::ast::File],
|
||||
name_table: &NameTable,
|
||||
) -> Result<Vec<Field>> {
|
||||
let mut merged_fields = Vec::new();
|
||||
@@ -131,8 +131,8 @@ pub fn merge_character_templates(
|
||||
suggestion: name_table.find_suggestion(template_name),
|
||||
})?;
|
||||
|
||||
// Get the template declaration
|
||||
let template = match &declarations[entry.decl_index] {
|
||||
// Get the template declaration using two-level indexing
|
||||
let template = match &all_files[entry.file_index].declarations[entry.decl_index] {
|
||||
| Declaration::Template(t) => t,
|
||||
| _ => {
|
||||
return Err(ResolveError::ValidationError {
|
||||
@@ -156,7 +156,7 @@ pub fn merge_character_templates(
|
||||
// Resolve template (which handles includes recursively)
|
||||
let mut visited = HashSet::new();
|
||||
let template_fields =
|
||||
resolve_template_includes(template, declarations, name_table, &mut visited)?;
|
||||
resolve_template_includes(template, all_files, name_table, &mut visited)?;
|
||||
|
||||
// Merge template fields into accumulated fields
|
||||
merged_fields = merge_field_lists(merged_fields, template_fields);
|
||||
@@ -506,6 +506,7 @@ mod tests {
|
||||
fn make_character(name: &str, fields: Vec<Field>, templates: Vec<&str>) -> Character {
|
||||
Character {
|
||||
name: name.to_string(),
|
||||
species: None,
|
||||
fields,
|
||||
template: if templates.is_empty() {
|
||||
None
|
||||
@@ -523,8 +524,9 @@ mod tests {
|
||||
let name_table = NameTable::from_file(&make_file(declarations.clone())).unwrap();
|
||||
let mut visited = HashSet::new();
|
||||
|
||||
let files = vec![make_file(declarations.clone())];
|
||||
let result =
|
||||
resolve_template_includes(&template, &declarations, &name_table, &mut visited).unwrap();
|
||||
resolve_template_includes(&template, &files, &name_table, &mut visited).unwrap();
|
||||
|
||||
assert_eq!(result.len(), 1);
|
||||
assert_eq!(result[0].name, "age");
|
||||
@@ -543,8 +545,9 @@ mod tests {
|
||||
let name_table = NameTable::from_file(&make_file(declarations.clone())).unwrap();
|
||||
let mut visited = HashSet::new();
|
||||
|
||||
let files = vec![make_file(declarations.clone())];
|
||||
let result =
|
||||
resolve_template_includes(&derived, &declarations, &name_table, &mut visited).unwrap();
|
||||
resolve_template_includes(&derived, &files, &name_table, &mut visited).unwrap();
|
||||
|
||||
assert_eq!(result.len(), 2);
|
||||
assert!(result.iter().any(|f| f.name == "age"));
|
||||
@@ -565,8 +568,8 @@ mod tests {
|
||||
let name_table = NameTable::from_file(&make_file(declarations.clone())).unwrap();
|
||||
let mut visited = HashSet::new();
|
||||
|
||||
let result =
|
||||
resolve_template_includes(&top, &declarations, &name_table, &mut visited).unwrap();
|
||||
let files = vec![make_file(declarations.clone())];
|
||||
let result = resolve_template_includes(&top, &files, &name_table, &mut visited).unwrap();
|
||||
|
||||
assert_eq!(result.len(), 3);
|
||||
assert!(result.iter().any(|f| f.name == "alive"));
|
||||
@@ -591,8 +594,9 @@ mod tests {
|
||||
let name_table = NameTable::from_file(&make_file(declarations.clone())).unwrap();
|
||||
let mut visited = HashSet::new();
|
||||
|
||||
let files = vec![make_file(declarations.clone())];
|
||||
let result =
|
||||
resolve_template_includes(&derived, &declarations, &name_table, &mut visited).unwrap();
|
||||
resolve_template_includes(&derived, &files, &name_table, &mut visited).unwrap();
|
||||
|
||||
assert_eq!(result.len(), 1);
|
||||
assert_eq!(result[0].name, "age");
|
||||
@@ -611,7 +615,8 @@ mod tests {
|
||||
];
|
||||
let name_table = NameTable::from_file(&make_file(declarations.clone())).unwrap();
|
||||
|
||||
let result = merge_character_templates(&character, &declarations, &name_table).unwrap();
|
||||
let files = vec![make_file(declarations.clone())];
|
||||
let result = merge_character_templates(&character, &files, &name_table).unwrap();
|
||||
|
||||
assert_eq!(result.len(), 1);
|
||||
assert_eq!(result[0].name, "age");
|
||||
@@ -636,7 +641,8 @@ mod tests {
|
||||
];
|
||||
let name_table = NameTable::from_file(&make_file(declarations.clone())).unwrap();
|
||||
|
||||
let result = merge_character_templates(&character, &declarations, &name_table).unwrap();
|
||||
let files = vec![make_file(declarations.clone())];
|
||||
let result = merge_character_templates(&character, &files, &name_table).unwrap();
|
||||
|
||||
assert_eq!(result.len(), 2);
|
||||
assert!(result
|
||||
@@ -664,7 +670,8 @@ mod tests {
|
||||
];
|
||||
let name_table = NameTable::from_file(&make_file(declarations.clone())).unwrap();
|
||||
|
||||
let result = merge_character_templates(&character, &declarations, &name_table).unwrap();
|
||||
let files = vec![make_file(declarations.clone())];
|
||||
let result = merge_character_templates(&character, &files, &name_table).unwrap();
|
||||
|
||||
assert_eq!(result.len(), 2);
|
||||
assert!(result
|
||||
@@ -686,7 +693,8 @@ mod tests {
|
||||
];
|
||||
let name_table = NameTable::from_file(&make_file(declarations.clone())).unwrap();
|
||||
|
||||
let result = merge_character_templates(&character, &declarations, &name_table);
|
||||
let files = vec![make_file(declarations.clone())];
|
||||
let result = merge_character_templates(&character, &files, &name_table);
|
||||
assert!(result.is_ok());
|
||||
}
|
||||
|
||||
@@ -710,7 +718,8 @@ mod tests {
|
||||
];
|
||||
let name_table = NameTable::from_file(&make_file(declarations.clone())).unwrap();
|
||||
|
||||
let result = merge_character_templates(&character, &declarations, &name_table);
|
||||
let files = vec![make_file(declarations.clone())];
|
||||
let result = merge_character_templates(&character, &files, &name_table);
|
||||
assert!(result.is_err());
|
||||
if let Err(ResolveError::ValidationError { message, .. }) = result {
|
||||
assert!(message.contains("strict template"));
|
||||
@@ -727,7 +736,8 @@ mod tests {
|
||||
let name_table = NameTable::from_file(&make_file(declarations.clone())).unwrap();
|
||||
let mut visited = HashSet::new();
|
||||
|
||||
let result = resolve_template_includes(&a, &declarations, &name_table, &mut visited);
|
||||
let files = vec![make_file(declarations.clone())];
|
||||
let result = resolve_template_includes(&a, &files, &name_table, &mut visited);
|
||||
assert!(result.is_err());
|
||||
if let Err(ResolveError::CircularDependency { .. }) = result {
|
||||
// Expected
|
||||
|
||||
Reference in New Issue
Block a user