use crate::semantic::symbol_table::Symbol;
use crate::semantic::types::TokenType;
use crate::syntax::sysml::ast::enums::DefinitionMember;
use crate::syntax::sysml::ast::{
Alias, Comment, Definition, Import, NamespaceDeclaration, Package, Usage,
};
use crate::syntax::sysml::visitor::AstVisitor;
use crate::semantic::adapters::SysmlAdapter;
fn extract_doc_from_definition(body: &[DefinitionMember]) -> Option<String> {
for member in body {
if let DefinitionMember::Comment(comment) = member {
let content = comment.content.trim();
if content.starts_with("doc") {
return Some(extract_doc_text(content));
}
}
}
None
}
fn extract_doc_from_usage(
body: &[crate::syntax::sysml::ast::enums::UsageMember],
) -> Option<String> {
for member in body {
if let crate::syntax::sysml::ast::enums::UsageMember::Comment(comment) = member {
let content = comment.content.trim();
if content.starts_with("doc") {
return Some(extract_doc_text(content));
}
}
}
None
}
fn extract_doc_text(doc: &str) -> String {
let rest = doc.strip_prefix("doc").unwrap_or(doc).trim();
if let Some(start) = rest.find("/*") {
if let Some(end) = rest.rfind("*/") {
let inner = &rest[start + 2..end];
return inner
.lines()
.map(|line| line.trim().trim_start_matches('*').trim())
.filter(|line| !line.is_empty())
.collect::<Vec<_>>()
.join(" ");
}
}
String::new()
}
impl<'a> AstVisitor for SysmlAdapter<'a> {
fn visit_namespace(&mut self, namespace: &NamespaceDeclaration) {
let qualified_name = self.qualified_name(&namespace.name);
let scope_id = self.symbol_table.current_scope_id();
let current_file = self.symbol_table.current_file().map(String::from);
let symbol = Symbol::Package {
name: namespace.name.clone(),
qualified_name,
scope_id,
source_file: current_file,
span: namespace.span,
documentation: None, };
self.insert_symbol(namespace.name.clone(), symbol);
self.enter_namespace(namespace.name.clone());
}
fn visit_package(&mut self, package: &Package) {
if let Some(name) = &package.name {
let qualified_name = self.qualified_name(name);
let scope_id = self.symbol_table.current_scope_id();
let source_file = self.symbol_table.current_file().map(String::from);
let symbol = Symbol::Package {
name: name.clone(),
qualified_name,
scope_id,
source_file,
span: package.span,
documentation: None, };
self.insert_symbol(name.clone(), symbol);
self.enter_namespace(name.clone());
}
}
fn visit_definition(&mut self, definition: &Definition) {
if let Some(name) = &definition.name {
let qualified_name = self.qualified_name(name);
let kind = Self::map_definition_kind(&definition.kind);
let semantic_role = Self::definition_kind_to_semantic_role(&definition.kind);
let scope_id = self.symbol_table.current_scope_id();
let documentation = extract_doc_from_definition(&definition.body);
let symbol = Symbol::Definition {
name: name.clone(),
qualified_name: qualified_name.clone(),
kind,
semantic_role: Some(semantic_role),
scope_id,
source_file: self.symbol_table.current_file().map(String::from),
span: definition.span,
documentation,
};
self.insert_symbol(name.clone(), symbol);
if let Some(ref short_name) = definition.short_name {
let short_qualified_name = self.qualified_name(short_name);
let alias_symbol = Symbol::Alias {
name: short_name.clone(),
qualified_name: short_qualified_name,
target: qualified_name.clone(),
target_span: definition.span,
scope_id,
source_file: self.symbol_table.current_file().map(String::from),
span: definition.short_name_span,
};
self.insert_symbol(short_name.clone(), alias_symbol);
}
for spec in &definition.relationships.specializes {
self.index_reference(&qualified_name, &spec.target, spec.span);
}
for redef in &definition.relationships.redefines {
self.index_reference(&qualified_name, &redef.target, redef.span);
}
for include in &definition.relationships.includes {
self.index_reference(&qualified_name, &include.target, include.span);
}
for member in &definition.body {
if let crate::syntax::sysml::ast::enums::DefinitionMember::Usage(usage) = member {
if let Some((target, span)) = usage.domain_target() {
self.index_reference(&qualified_name, target, span);
}
for satisfy in &usage.relationships.satisfies {
self.index_reference(&qualified_name, &satisfy.target, satisfy.span);
}
for perform in &usage.relationships.performs {
self.index_reference(&qualified_name, &perform.target, perform.span);
}
for exhibit in &usage.relationships.exhibits {
self.index_reference(&qualified_name, &exhibit.target, exhibit.span);
}
for include in &usage.relationships.includes {
self.index_reference(&qualified_name, &include.target, include.span);
}
}
}
self.enter_namespace(name.clone());
for member in &definition.body {
match member {
crate::syntax::sysml::ast::enums::DefinitionMember::Usage(usage) => {
self.visit_usage(usage);
}
crate::syntax::sysml::ast::enums::DefinitionMember::Comment(_) => {}
}
}
self.exit_namespace();
}
}
fn visit_usage(&mut self, usage: &Usage) {
let (name, name_span, is_anonymous) = if let Some(name) = &usage.name {
(name.clone(), usage.span, false)
} else if let Some(first_redef) = usage.relationships.redefines.first() {
(first_redef.target.clone(), first_redef.span, true)
} else if let Some(first_subset) = usage.relationships.subsets.first() {
(first_subset.target.clone(), first_subset.span, true)
} else {
return;
};
let qualified_name = self.qualified_name(&name);
for rel in &usage.relationships.redefines {
self.index_reference_with_type(
&qualified_name,
&rel.target,
rel.span,
Some(TokenType::Property),
);
}
for subset in &usage.relationships.subsets {
self.index_reference_with_type(
&qualified_name,
&subset.target,
subset.span,
Some(TokenType::Property),
);
}
if let Some(ref target) = usage.relationships.typed_by {
self.index_reference_with_type(
&qualified_name,
target,
usage.relationships.typed_by_span,
Some(TokenType::Type),
);
}
for reference in &usage.relationships.references {
self.index_reference_with_type(
&qualified_name,
&reference.target,
reference.span,
Some(TokenType::Property),
);
}
for cross in &usage.relationships.crosses {
self.index_reference_with_type(
&qualified_name,
&cross.target,
cross.span,
Some(TokenType::Property),
);
}
for meta in &usage.relationships.meta {
self.index_reference(&qualified_name, &meta.target, meta.span);
}
if is_anonymous
&& self
.symbol_table
.find_by_qualified_name(&qualified_name)
.is_some()
{
return;
}
let kind = Self::map_usage_kind(&usage.kind);
let semantic_role = Self::usage_kind_to_semantic_role(&usage.kind);
let scope_id = self.symbol_table.current_scope_id();
let documentation = extract_doc_from_usage(&usage.body);
let symbol = Symbol::Usage {
name: name.clone(),
qualified_name: qualified_name.clone(),
kind,
semantic_role: Some(semantic_role),
usage_type: usage.relationships.typed_by.clone(),
scope_id,
source_file: self.symbol_table.current_file().map(String::from),
span: name_span,
documentation,
};
self.insert_symbol(name.clone(), symbol);
if let Some(ref short_name) = usage.short_name {
let short_qualified_name = self.qualified_name(short_name);
let alias_symbol = Symbol::Alias {
name: short_name.clone(),
qualified_name: short_qualified_name,
target: qualified_name.clone(),
target_span: usage.span,
scope_id,
source_file: self.symbol_table.current_file().map(String::from),
span: usage.short_name_span,
};
self.insert_symbol(short_name.clone(), alias_symbol);
}
if !usage.body.is_empty() {
self.enter_namespace(name.clone());
for member in &usage.body {
match member {
crate::syntax::sysml::ast::enums::UsageMember::Usage(nested_usage) => {
self.visit_usage(nested_usage);
}
crate::syntax::sysml::ast::enums::UsageMember::Comment(_) => {}
}
}
self.exit_namespace();
}
}
fn visit_import(&mut self, import: &Import) {
let current_file = self.symbol_table.current_file().map(String::from);
self.symbol_table.add_import(
import.path.clone(),
import.is_recursive,
import.is_public,
import.span,
current_file.clone(),
);
let scope_id = self.symbol_table.current_scope_id();
let qualified_name = format!("import::{}::{}", scope_id, import.path);
let key = format!("import::{}", import.path);
let symbol = Symbol::Import {
path: import.path.clone(),
path_span: import.path_span,
qualified_name,
is_recursive: import.is_recursive,
scope_id,
source_file: current_file,
span: import.span,
};
self.insert_symbol(key, symbol);
}
fn visit_comment(&mut self, _comment: &Comment) {}
fn visit_alias(&mut self, alias: &Alias) {
if let Some(name) = &alias.name {
let qualified_name = self.qualified_name(name);
let scope_id = self.symbol_table.current_scope_id();
let symbol = Symbol::Alias {
name: name.clone(),
qualified_name,
target: alias.target.clone(),
target_span: alias.target_span,
scope_id,
source_file: self.symbol_table.current_file().map(String::from),
span: alias.span,
};
self.insert_symbol(name.clone(), symbol);
}
}
}