use super::syntax_kind::SyntaxKind;
use super::{SyntaxNode, SyntaxToken};
pub trait AstNode: Sized {
fn can_cast(kind: SyntaxKind) -> bool;
fn cast(node: SyntaxNode) -> Option<Self>;
fn syntax(&self) -> &SyntaxNode;
fn descendants<T: AstNode>(&self) -> impl Iterator<Item = T> {
self.syntax().descendants().filter_map(T::cast)
}
fn doc_comment(&self) -> Option<String> {
extract_doc_comment(self.syntax())
}
}
pub fn extract_doc_comment(node: &SyntaxNode) -> Option<String> {
let mut comments = Vec::new();
let mut current = node.prev_sibling_or_token();
while let Some(node_or_token) = current {
match node_or_token {
rowan::NodeOrToken::Token(ref t) => {
match t.kind() {
SyntaxKind::WHITESPACE => {
current = t.prev_sibling_or_token();
}
SyntaxKind::BLOCK_COMMENT => {
let text = t.text();
let content = text
.strip_prefix("/*")
.and_then(|s| s.strip_suffix("*/"))
.map(clean_doc_comment)
.unwrap_or_default();
if !content.is_empty() {
comments.push(content);
}
break;
}
SyntaxKind::LINE_COMMENT => {
let text = t.text();
let content = text.strip_prefix("//").unwrap_or(text).trim();
if !content.is_empty() {
comments.push(content.to_string());
}
current = t.prev_sibling_or_token();
}
_ => break, }
}
rowan::NodeOrToken::Node(ref n) => {
if n.kind() == SyntaxKind::COMMENT_ELEMENT {
for child in n.children_with_tokens() {
if let rowan::NodeOrToken::Token(t) = child {
if t.kind() == SyntaxKind::BLOCK_COMMENT {
let text = t.text();
let content = text
.strip_prefix("/*")
.and_then(|s| s.strip_suffix("*/"))
.map(clean_doc_comment)
.unwrap_or_default();
if !content.is_empty() {
comments.push(content);
}
break;
}
}
}
break;
} else {
break;
}
}
}
}
if comments.is_empty() {
return None;
}
comments.reverse();
Some(comments.join("\n"))
}
fn clean_doc_comment(s: &str) -> String {
s.lines()
.map(|line| {
let trimmed = line.trim();
if let Some(rest) = trimmed.strip_prefix('*') {
rest.trim_start().to_string()
} else {
trimmed.to_string()
}
})
.filter(|line| !line.is_empty())
.collect::<Vec<_>>()
.join("\n")
}
pub trait AstToken: Sized {
fn can_cast(kind: SyntaxKind) -> bool;
fn cast(token: SyntaxToken) -> Option<Self>;
fn syntax(&self) -> &SyntaxToken;
fn text(&self) -> &str {
self.syntax().text()
}
}
macro_rules! ast_node {
($name:ident, $kind:ident) => {
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct $name(SyntaxNode);
impl AstNode for $name {
fn can_cast(kind: SyntaxKind) -> bool {
kind == SyntaxKind::$kind
}
fn cast(node: SyntaxNode) -> Option<Self> {
if Self::can_cast(node.kind()) {
Some(Self(node))
} else {
None
}
}
fn syntax(&self) -> &SyntaxNode {
&self.0
}
}
};
}
ast_node!(SourceFile, SOURCE_FILE);
impl SourceFile {
pub fn members(&self) -> impl Iterator<Item = NamespaceMember> + '_ {
self.0.children().filter_map(NamespaceMember::cast)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum NamespaceMember {
Package(Package),
LibraryPackage(LibraryPackage),
Import(Import),
Alias(Alias),
Dependency(Dependency),
Definition(Definition),
Usage(Usage),
Filter(ElementFilter),
Metadata(MetadataUsage),
Comment(Comment),
Bind(BindingConnector),
Succession(Succession),
Transition(TransitionUsage),
Connector(Connector),
ConnectUsage(ConnectUsage),
SendAction(SendActionUsage),
AcceptAction(AcceptActionUsage),
StateSubaction(StateSubaction),
ControlNode(ControlNode),
ForLoop(ForLoopActionUsage),
IfAction(IfActionUsage),
WhileLoop(WhileLoopActionUsage),
}
impl AstNode for NamespaceMember {
fn can_cast(kind: SyntaxKind) -> bool {
matches!(
kind,
SyntaxKind::PACKAGE
| SyntaxKind::LIBRARY_PACKAGE
| SyntaxKind::IMPORT
| SyntaxKind::ALIAS_MEMBER
| SyntaxKind::DEPENDENCY
| SyntaxKind::DEFINITION
| SyntaxKind::USAGE
| SyntaxKind::SUBJECT_USAGE
| SyntaxKind::ACTOR_USAGE
| SyntaxKind::STAKEHOLDER_USAGE
| SyntaxKind::OBJECTIVE_USAGE
| SyntaxKind::ELEMENT_FILTER_MEMBER
| SyntaxKind::METADATA_USAGE
| SyntaxKind::COMMENT_ELEMENT
| SyntaxKind::BINDING_CONNECTOR
| SyntaxKind::SUCCESSION
| SyntaxKind::TRANSITION_USAGE
| SyntaxKind::CONNECTOR
| SyntaxKind::CONNECT_USAGE
| SyntaxKind::SEND_ACTION_USAGE
| SyntaxKind::ACCEPT_ACTION_USAGE
| SyntaxKind::STATE_SUBACTION
| SyntaxKind::CONTROL_NODE
| SyntaxKind::FOR_LOOP_ACTION_USAGE
| SyntaxKind::IF_ACTION_USAGE
| SyntaxKind::WHILE_LOOP_ACTION_USAGE
)
}
fn cast(node: SyntaxNode) -> Option<Self> {
match node.kind() {
SyntaxKind::PACKAGE => Some(Self::Package(Package(node))),
SyntaxKind::LIBRARY_PACKAGE => Some(Self::LibraryPackage(LibraryPackage(node))),
SyntaxKind::IMPORT => Some(Self::Import(Import(node))),
SyntaxKind::ALIAS_MEMBER => Some(Self::Alias(Alias(node))),
SyntaxKind::DEPENDENCY => Some(Self::Dependency(Dependency(node))),
SyntaxKind::DEFINITION => Some(Self::Definition(Definition(node))),
SyntaxKind::USAGE
| SyntaxKind::SUBJECT_USAGE
| SyntaxKind::ACTOR_USAGE
| SyntaxKind::STAKEHOLDER_USAGE
| SyntaxKind::OBJECTIVE_USAGE => Some(Self::Usage(Usage(node))),
SyntaxKind::ELEMENT_FILTER_MEMBER => Some(Self::Filter(ElementFilter(node))),
SyntaxKind::METADATA_USAGE => Some(Self::Metadata(MetadataUsage(node))),
SyntaxKind::COMMENT_ELEMENT => Some(Self::Comment(Comment(node))),
SyntaxKind::BINDING_CONNECTOR => Some(Self::Bind(BindingConnector(node))),
SyntaxKind::SUCCESSION => Some(Self::Succession(Succession(node))),
SyntaxKind::TRANSITION_USAGE => Some(Self::Transition(TransitionUsage(node))),
SyntaxKind::CONNECTOR => Some(Self::Connector(Connector(node))),
SyntaxKind::CONNECT_USAGE => Some(Self::ConnectUsage(ConnectUsage(node))),
SyntaxKind::SEND_ACTION_USAGE => Some(Self::SendAction(SendActionUsage(node))),
SyntaxKind::ACCEPT_ACTION_USAGE => Some(Self::AcceptAction(AcceptActionUsage(node))),
SyntaxKind::STATE_SUBACTION => Some(Self::StateSubaction(StateSubaction(node))),
SyntaxKind::CONTROL_NODE => Some(Self::ControlNode(ControlNode(node))),
SyntaxKind::FOR_LOOP_ACTION_USAGE => Some(Self::ForLoop(ForLoopActionUsage(node))),
SyntaxKind::IF_ACTION_USAGE => Some(Self::IfAction(IfActionUsage(node))),
SyntaxKind::WHILE_LOOP_ACTION_USAGE => {
Some(Self::WhileLoop(WhileLoopActionUsage(node)))
}
_ => None,
}
}
fn syntax(&self) -> &SyntaxNode {
match self {
Self::Package(n) => n.syntax(),
Self::LibraryPackage(n) => n.syntax(),
Self::Import(n) => n.syntax(),
Self::Alias(n) => n.syntax(),
Self::Dependency(n) => n.syntax(),
Self::Definition(n) => n.syntax(),
Self::Usage(n) => n.syntax(),
Self::Filter(n) => n.syntax(),
Self::Metadata(n) => n.syntax(),
Self::Comment(n) => n.syntax(),
Self::Bind(n) => n.syntax(),
Self::Succession(n) => n.syntax(),
Self::Transition(n) => n.syntax(),
Self::Connector(n) => n.syntax(),
Self::ConnectUsage(n) => n.syntax(),
Self::SendAction(n) => n.syntax(),
Self::AcceptAction(n) => n.syntax(),
Self::StateSubaction(n) => n.syntax(),
Self::ControlNode(n) => n.syntax(),
Self::ForLoop(n) => n.syntax(),
Self::IfAction(n) => n.syntax(),
Self::WhileLoop(n) => n.syntax(),
}
}
}
ast_node!(Package, PACKAGE);
impl Package {
pub fn name(&self) -> Option<Name> {
self.0.children().find_map(Name::cast)
}
pub fn body(&self) -> Option<NamespaceBody> {
self.0.children().find_map(NamespaceBody::cast)
}
pub fn members(&self) -> impl Iterator<Item = NamespaceMember> + '_ {
self.body()
.into_iter()
.flat_map(|body| body.members().collect::<Vec<_>>())
}
}
ast_node!(LibraryPackage, LIBRARY_PACKAGE);
impl LibraryPackage {
pub fn is_standard(&self) -> bool {
self.0
.children_with_tokens()
.filter_map(|e| e.into_token())
.any(|t| t.kind() == SyntaxKind::STANDARD_KW)
}
pub fn name(&self) -> Option<Name> {
self.0.children().find_map(Name::cast)
}
pub fn body(&self) -> Option<NamespaceBody> {
self.0.children().find_map(NamespaceBody::cast)
}
}
ast_node!(NamespaceBody, NAMESPACE_BODY);
impl NamespaceBody {
pub fn members(&self) -> impl Iterator<Item = NamespaceMember> + '_ {
self.0.children().flat_map(|child| {
if child.kind() == SyntaxKind::STATE_SUBACTION {
let nested: Vec<NamespaceMember> =
child.children().filter_map(NamespaceMember::cast).collect();
if nested.is_empty() {
StateSubaction::cast(child)
.map(NamespaceMember::StateSubaction)
.into_iter()
.collect::<Vec<_>>()
} else {
nested
}
} else {
NamespaceMember::cast(child).into_iter().collect()
}
})
}
}
ast_node!(Import, IMPORT);
impl Import {
pub fn is_all(&self) -> bool {
self.0
.children_with_tokens()
.filter_map(|e| e.into_token())
.any(|t| t.kind() == SyntaxKind::ALL_KW)
}
pub fn target(&self) -> Option<QualifiedName> {
self.0.children().find_map(QualifiedName::cast)
}
pub fn is_wildcard(&self) -> bool {
self.0
.children_with_tokens()
.filter_map(|e| e.into_token())
.any(|t| t.kind() == SyntaxKind::STAR)
}
pub fn is_recursive(&self) -> bool {
let has_star_star = self
.0
.descendants_with_tokens()
.filter_map(|e| match e {
rowan::NodeOrToken::Token(t) => Some(t),
_ => None,
})
.any(|t| t.kind() == SyntaxKind::STAR_STAR);
if has_star_star {
return true;
}
let stars: Vec<_> = self
.0
.children_with_tokens()
.filter_map(|e| e.into_token())
.filter(|t| t.kind() == SyntaxKind::STAR)
.collect();
stars.len() >= 2
}
pub fn is_public(&self) -> bool {
let has_inside = self
.0
.children_with_tokens()
.filter_map(|e| e.into_token())
.any(|t| t.kind() == SyntaxKind::PUBLIC_KW);
if has_inside {
return true;
}
if let Some(prev) = self.0.prev_sibling_or_token() {
let mut current = Some(prev);
while let Some(node_or_token) = current {
match node_or_token {
rowan::NodeOrToken::Token(t) if t.kind() == SyntaxKind::PUBLIC_KW => {
return true;
}
rowan::NodeOrToken::Token(t) if t.kind() == SyntaxKind::WHITESPACE => {
current = t.prev_sibling_or_token();
}
_ => break,
}
}
}
false
}
pub fn filter(&self) -> Option<FilterPackage> {
self.0.children().find_map(FilterPackage::cast)
}
}
ast_node!(FilterPackage, FILTER_PACKAGE);
impl FilterPackage {
pub fn target(&self) -> Option<QualifiedName> {
self.0.children().find_map(QualifiedName::cast)
}
pub fn targets(&self) -> Vec<QualifiedName> {
self.0.children().filter_map(QualifiedName::cast).collect()
}
}
ast_node!(Alias, ALIAS_MEMBER);
impl Alias {
pub fn name(&self) -> Option<Name> {
self.0.children().find_map(Name::cast)
}
pub fn target(&self) -> Option<QualifiedName> {
self.0.children().find_map(QualifiedName::cast)
}
}
ast_node!(Dependency, DEPENDENCY);
impl Dependency {
pub fn qualified_names(&self) -> impl Iterator<Item = QualifiedName> + '_ {
self.0.children().filter_map(QualifiedName::cast)
}
pub fn sources(&self) -> Vec<QualifiedName> {
let mut sources = Vec::new();
let mut found_to = false;
for elem in self.0.children_with_tokens() {
if let Some(token) = elem.as_token() {
if token.kind() == SyntaxKind::TO_KW {
found_to = true;
}
} else if let Some(node) = elem.as_node() {
if !found_to {
if let Some(qn) = QualifiedName::cast(node.clone()) {
sources.push(qn);
}
}
}
}
sources
}
pub fn target(&self) -> Option<QualifiedName> {
let mut found_to = false;
for elem in self.0.children_with_tokens() {
if let Some(token) = elem.as_token() {
if token.kind() == SyntaxKind::TO_KW {
found_to = true;
}
} else if let Some(node) = elem.as_node() {
if found_to {
if let Some(qn) = QualifiedName::cast(node.clone()) {
return Some(qn);
}
}
}
}
None
}
pub fn prefix_metadata(&self) -> Vec<PrefixMetadata> {
let mut result = Vec::new();
let mut current = self.0.prev_sibling();
while let Some(sibling) = current {
if sibling.kind() == SyntaxKind::PREFIX_METADATA {
if let Some(pm) = PrefixMetadata::cast(sibling.clone()) {
result.push(pm);
}
current = sibling.prev_sibling();
} else {
break;
}
}
result.reverse();
result
}
}
ast_node!(ElementFilter, ELEMENT_FILTER_MEMBER);
impl ElementFilter {
pub fn expression(&self) -> Option<Expression> {
self.0.children().find_map(Expression::cast)
}
pub fn metadata_refs(&self) -> Vec<String> {
let mut refs = Vec::new();
if let Some(expr) = self.expression() {
let mut at_seen = false;
for child in expr.syntax().children_with_tokens() {
match child {
rowan::NodeOrToken::Token(t) if t.kind() == SyntaxKind::AT => {
at_seen = true;
}
rowan::NodeOrToken::Node(n)
if at_seen && n.kind() == SyntaxKind::QUALIFIED_NAME =>
{
if let Some(qn) = QualifiedName::cast(n) {
refs.push(qn.to_string());
}
at_seen = false;
}
_ => {}
}
}
}
refs
}
pub fn all_qualified_refs(&self) -> Vec<(String, rowan::TextRange)> {
let mut refs = Vec::new();
if let Some(expr) = self.expression() {
for node in expr.syntax().descendants() {
if node.kind() == SyntaxKind::QUALIFIED_NAME {
if let Some(qn) = QualifiedName::cast(node.clone()) {
refs.push((qn.to_string(), node.text_range()));
}
}
}
}
refs
}
}
ast_node!(Comment, COMMENT_ELEMENT);
impl Comment {
pub fn name(&self) -> Option<Name> {
self.0.children().find_map(Name::cast)
}
pub fn about_targets(&self) -> impl Iterator<Item = QualifiedName> + '_ {
self.0.children().filter_map(QualifiedName::cast)
}
pub fn has_about(&self) -> bool {
self.0
.children_with_tokens()
.filter_map(|e| e.into_token())
.any(|t| t.kind() == SyntaxKind::ABOUT_KW)
}
}
ast_node!(MetadataUsage, METADATA_USAGE);
impl MetadataUsage {
pub fn target(&self) -> Option<QualifiedName> {
self.0.children().find_map(QualifiedName::cast)
}
pub fn about_targets(&self) -> impl Iterator<Item = QualifiedName> + '_ {
self.0.children().filter_map(QualifiedName::cast).skip(1)
}
pub fn has_about(&self) -> bool {
self.0
.children_with_tokens()
.filter_map(|e| e.into_token())
.any(|t| t.kind() == SyntaxKind::ABOUT_KW)
}
pub fn body(&self) -> Option<NamespaceBody> {
self.0.children().find_map(NamespaceBody::cast)
}
}
ast_node!(PrefixMetadata, PREFIX_METADATA);
impl PrefixMetadata {
pub fn name(&self) -> Option<String> {
self.0
.children_with_tokens()
.filter_map(|e| e.into_token())
.find(|t| t.kind() == SyntaxKind::IDENT)
.map(|t| t.text().to_string())
}
pub fn name_range(&self) -> Option<rowan::TextRange> {
self.0
.children_with_tokens()
.filter_map(|e| e.into_token())
.find(|t| t.kind() == SyntaxKind::IDENT)
.map(|t| t.text_range())
}
}
ast_node!(Definition, DEFINITION);
impl Definition {
pub fn is_abstract(&self) -> bool {
self.0
.children_with_tokens()
.filter_map(|e| e.into_token())
.any(|t| t.kind() == SyntaxKind::ABSTRACT_KW)
}
pub fn is_variation(&self) -> bool {
self.0
.children_with_tokens()
.filter_map(|e| e.into_token())
.any(|t| t.kind() == SyntaxKind::VARIATION_KW)
}
pub fn is_individual(&self) -> bool {
self.0
.children_with_tokens()
.filter_map(|e| e.into_token())
.any(|t| t.kind() == SyntaxKind::INDIVIDUAL_KW)
}
pub fn definition_kind(&self) -> Option<DefinitionKind> {
for token in self.0.children_with_tokens().filter_map(|e| e.into_token()) {
match token.kind() {
SyntaxKind::PART_KW => return Some(DefinitionKind::Part),
SyntaxKind::ATTRIBUTE_KW => return Some(DefinitionKind::Attribute),
SyntaxKind::PORT_KW => return Some(DefinitionKind::Port),
SyntaxKind::ITEM_KW => return Some(DefinitionKind::Item),
SyntaxKind::ACTION_KW => return Some(DefinitionKind::Action),
SyntaxKind::STATE_KW => return Some(DefinitionKind::State),
SyntaxKind::CONSTRAINT_KW => return Some(DefinitionKind::Constraint),
SyntaxKind::REQUIREMENT_KW => return Some(DefinitionKind::Requirement),
SyntaxKind::CASE_KW => return Some(DefinitionKind::Case),
SyntaxKind::CALC_KW => return Some(DefinitionKind::Calc),
SyntaxKind::CONNECTION_KW => return Some(DefinitionKind::Connection),
SyntaxKind::INTERFACE_KW => return Some(DefinitionKind::Interface),
SyntaxKind::ALLOCATION_KW => return Some(DefinitionKind::Allocation),
SyntaxKind::FLOW_KW => return Some(DefinitionKind::Flow),
SyntaxKind::VIEW_KW => return Some(DefinitionKind::View),
SyntaxKind::VIEWPOINT_KW => return Some(DefinitionKind::Viewpoint),
SyntaxKind::RENDERING_KW => return Some(DefinitionKind::Rendering),
SyntaxKind::METADATA_KW => return Some(DefinitionKind::Metadata),
SyntaxKind::OCCURRENCE_KW => return Some(DefinitionKind::Occurrence),
SyntaxKind::ENUM_KW => return Some(DefinitionKind::Enum),
SyntaxKind::ANALYSIS_KW => return Some(DefinitionKind::Analysis),
SyntaxKind::VERIFICATION_KW => return Some(DefinitionKind::Verification),
SyntaxKind::USE_KW => return Some(DefinitionKind::UseCase),
SyntaxKind::CONCERN_KW => return Some(DefinitionKind::Concern),
SyntaxKind::CLASS_KW => return Some(DefinitionKind::Class),
SyntaxKind::STRUCT_KW => return Some(DefinitionKind::Struct),
SyntaxKind::ASSOC_KW => return Some(DefinitionKind::Assoc),
SyntaxKind::BEHAVIOR_KW => return Some(DefinitionKind::Behavior),
SyntaxKind::FUNCTION_KW => return Some(DefinitionKind::Function),
SyntaxKind::PREDICATE_KW => return Some(DefinitionKind::Predicate),
SyntaxKind::INTERACTION_KW => return Some(DefinitionKind::Interaction),
SyntaxKind::DATATYPE_KW => return Some(DefinitionKind::Datatype),
SyntaxKind::CLASSIFIER_KW => return Some(DefinitionKind::Classifier),
SyntaxKind::TYPE_KW => return Some(DefinitionKind::Type),
SyntaxKind::METACLASS_KW => return Some(DefinitionKind::Metaclass),
_ => {}
}
}
None
}
pub fn name(&self) -> Option<Name> {
self.0.children().find_map(Name::cast)
}
pub fn specializations(&self) -> impl Iterator<Item = Specialization> + '_ {
self.0.children().filter_map(Specialization::cast)
}
pub fn body(&self) -> Option<NamespaceBody> {
self.0.children().find_map(NamespaceBody::cast)
}
pub fn constraint_body(&self) -> Option<ConstraintBody> {
self.0.children().find_map(ConstraintBody::cast)
}
pub fn members(&self) -> impl Iterator<Item = NamespaceMember> + '_ {
self.body()
.into_iter()
.flat_map(|body| body.members().collect::<Vec<_>>())
}
pub fn prefix_metadata(&self) -> Vec<PrefixMetadata> {
let mut result = Vec::new();
let mut current = self.0.prev_sibling();
while let Some(sibling) = current {
if sibling.kind() == SyntaxKind::PREFIX_METADATA {
if let Some(pm) = PrefixMetadata::cast(sibling.clone()) {
result.push(pm);
}
current = sibling.prev_sibling();
} else {
break;
}
}
result.reverse();
result
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DefinitionKind {
Part,
Attribute,
Port,
Item,
Action,
State,
Constraint,
Requirement,
Case,
Calc,
Connection,
Interface,
Allocation,
Flow,
View,
Viewpoint,
Rendering,
Metadata,
Occurrence,
Enum,
Analysis,
Verification,
UseCase,
Concern,
Class,
Struct,
Assoc,
Behavior,
Function,
Predicate,
Interaction,
Datatype,
Classifier,
Type,
Metaclass,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Usage(SyntaxNode);
impl AstNode for Usage {
fn can_cast(kind: SyntaxKind) -> bool {
matches!(
kind,
SyntaxKind::USAGE
| SyntaxKind::SUBJECT_USAGE
| SyntaxKind::ACTOR_USAGE
| SyntaxKind::STAKEHOLDER_USAGE
| SyntaxKind::OBJECTIVE_USAGE
)
}
fn cast(node: SyntaxNode) -> Option<Self> {
if Self::can_cast(node.kind()) {
Some(Self(node))
} else {
None
}
}
fn syntax(&self) -> &SyntaxNode {
&self.0
}
}
impl Usage {
pub fn is_ref(&self) -> bool {
self.0
.children_with_tokens()
.filter_map(|e| e.into_token())
.any(|t| t.kind() == SyntaxKind::REF_KW)
}
pub fn is_readonly(&self) -> bool {
self.0
.children_with_tokens()
.filter_map(|e| e.into_token())
.any(|t| t.kind() == SyntaxKind::READONLY_KW)
}
pub fn is_derived(&self) -> bool {
self.0
.children_with_tokens()
.filter_map(|e| e.into_token())
.any(|t| t.kind() == SyntaxKind::DERIVED_KW)
}
pub fn is_abstract(&self) -> bool {
self.0
.children_with_tokens()
.filter_map(|e| e.into_token())
.any(|t| t.kind() == SyntaxKind::ABSTRACT_KW)
}
pub fn is_variation(&self) -> bool {
self.0
.children_with_tokens()
.filter_map(|e| e.into_token())
.any(|t| t.kind() == SyntaxKind::VARIATION_KW)
}
pub fn is_var(&self) -> bool {
self.0
.children_with_tokens()
.filter_map(|e| e.into_token())
.any(|t| t.kind() == SyntaxKind::VAR_KW)
}
pub fn is_all(&self) -> bool {
self.0
.children_with_tokens()
.filter_map(|e| e.into_token())
.any(|t| t.kind() == SyntaxKind::ALL_KW)
}
pub fn is_parallel(&self) -> bool {
self.0
.children_with_tokens()
.filter_map(|e| e.into_token())
.any(|t| t.kind() == SyntaxKind::PARALLEL_KW)
}
pub fn is_individual(&self) -> bool {
self.0
.children_with_tokens()
.filter_map(|e| e.into_token())
.any(|t| t.kind() == SyntaxKind::INDIVIDUAL_KW)
}
pub fn is_end(&self) -> bool {
self.0
.children_with_tokens()
.filter_map(|e| e.into_token())
.any(|t| t.kind() == SyntaxKind::END_KW)
}
pub fn is_default(&self) -> bool {
self.0
.children_with_tokens()
.filter_map(|e| e.into_token())
.any(|t| t.kind() == SyntaxKind::DEFAULT_KW)
}
pub fn is_ordered(&self) -> bool {
self.0
.children_with_tokens()
.filter_map(|e| e.into_token())
.any(|t| t.kind() == SyntaxKind::ORDERED_KW)
}
pub fn is_nonunique(&self) -> bool {
self.0
.children_with_tokens()
.filter_map(|e| e.into_token())
.any(|t| t.kind() == SyntaxKind::NONUNIQUE_KW)
}
pub fn is_portion(&self) -> bool {
self.0
.children_with_tokens()
.filter_map(|e| e.into_token())
.any(|t| t.kind() == SyntaxKind::PORTION_KW)
}
pub fn direction(&self) -> Option<Direction> {
for token in self.0.children_with_tokens().filter_map(|e| e.into_token()) {
match token.kind() {
SyntaxKind::IN_KW => return Some(Direction::In),
SyntaxKind::OUT_KW => return Some(Direction::Out),
SyntaxKind::INOUT_KW => return Some(Direction::InOut),
_ => {}
}
}
None
}
pub fn multiplicity(&self) -> Option<(Option<u64>, Option<u64>)> {
if let Some(mult_node) = self
.0
.children()
.find(|n| n.kind() == SyntaxKind::MULTIPLICITY)
{
return Self::parse_multiplicity_node(&mult_node);
}
for child in self.0.children() {
match child.kind() {
SyntaxKind::TYPING | SyntaxKind::SPECIALIZATION => {
if let Some(mult_node) = child
.children()
.find(|n| n.kind() == SyntaxKind::MULTIPLICITY)
{
return Self::parse_multiplicity_node(&mult_node);
}
}
_ => {}
}
}
None
}
fn parse_multiplicity_node(mult_node: &SyntaxNode) -> Option<(Option<u64>, Option<u64>)> {
let mut lower: Option<u64> = None;
let mut upper: Option<u64> = None;
let mut found_dot_dot = false;
fn find_bounds(
node: &SyntaxNode,
lower: &mut Option<u64>,
upper: &mut Option<u64>,
found_dot_dot: &mut bool,
) {
for child in node.children_with_tokens() {
match child.kind() {
SyntaxKind::INTEGER => {
if let Some(token) = child.into_token() {
let text = token.text();
if let Ok(val) = text.parse::<u64>() {
if *found_dot_dot {
*upper = Some(val);
} else {
*lower = Some(val);
}
}
}
}
SyntaxKind::STAR => {
if *found_dot_dot {
*upper = None;
} else {
*lower = None;
}
}
SyntaxKind::DOT_DOT => {
*found_dot_dot = true;
}
_ => {
if let Some(node) = child.into_node() {
find_bounds(&node, lower, upper, found_dot_dot);
}
}
}
}
}
find_bounds(mult_node, &mut lower, &mut upper, &mut found_dot_dot);
if !found_dot_dot && lower.is_some() {
upper = lower;
}
if lower.is_some() || upper.is_some() || found_dot_dot {
Some((lower, upper))
} else {
Some((lower, upper))
}
}
pub fn prefix_metadata(&self) -> Vec<PrefixMetadata> {
let mut result = Vec::new();
let mut current = self.0.prev_sibling();
while let Some(sibling) = current {
if sibling.kind() == SyntaxKind::PREFIX_METADATA {
if let Some(pm) = PrefixMetadata::cast(sibling.clone()) {
result.push(pm);
}
current = sibling.prev_sibling();
} else {
break;
}
}
result.reverse();
for child in self.0.children() {
if child.kind() == SyntaxKind::PREFIX_METADATA {
if let Some(pm) = PrefixMetadata::cast(child) {
result.push(pm);
}
}
}
result
}
pub fn name(&self) -> Option<Name> {
self.0.children().find_map(Name::cast)
}
pub fn names(&self) -> Vec<Name> {
self.0.children().filter_map(Name::cast).collect()
}
pub fn typing(&self) -> Option<Typing> {
self.0.children().find_map(Typing::cast)
}
pub fn of_type(&self) -> Option<QualifiedName> {
let mut found_of = false;
for elem in self.0.children_with_tokens() {
if let Some(token) = elem.as_token() {
if token.kind() == SyntaxKind::OF_KW {
found_of = true;
}
} else if let Some(node) = elem.as_node() {
if found_of && node.kind() == SyntaxKind::QUALIFIED_NAME {
return QualifiedName::cast(node.clone());
}
}
}
None
}
pub fn specializations(&self) -> impl Iterator<Item = Specialization> + '_ {
self.0.children().filter_map(Specialization::cast)
}
pub fn body(&self) -> Option<NamespaceBody> {
self.0.children().find_map(NamespaceBody::cast)
}
pub fn value_expression(&self) -> Option<Expression> {
self.0.children().find_map(Expression::cast)
}
pub fn from_to_clause(&self) -> Option<FromToClause> {
self.0.children().find_map(FromToClause::cast)
}
pub fn transition_usage(&self) -> Option<TransitionUsage> {
self.0.children().find_map(TransitionUsage::cast)
}
pub fn succession(&self) -> Option<Succession> {
self.0.children().find_map(Succession::cast)
}
pub fn perform_action_usage(&self) -> Option<PerformActionUsage> {
self.0.children().find_map(PerformActionUsage::cast)
}
pub fn accept_action_usage(&self) -> Option<AcceptActionUsage> {
self.0.children().find_map(AcceptActionUsage::cast)
}
pub fn send_action_usage(&self) -> Option<SendActionUsage> {
self.0.children().find_map(SendActionUsage::cast)
}
pub fn requirement_verification(&self) -> Option<RequirementVerification> {
self.0.children().find_map(RequirementVerification::cast)
}
pub fn connect_usage(&self) -> Option<ConnectUsage> {
self.0.children().find_map(ConnectUsage::cast)
}
pub fn connector_part(&self) -> Option<ConnectorPart> {
self.0.children().find_map(ConnectorPart::cast)
}
pub fn binding_connector(&self) -> Option<BindingConnector> {
self.0.children().find_map(BindingConnector::cast)
}
pub fn constraint_body(&self) -> Option<ConstraintBody> {
self.0.children().find_map(ConstraintBody::cast)
}
pub fn is_exhibit(&self) -> bool {
self.0
.children_with_tokens()
.filter_map(|e| e.into_token())
.any(|t| t.kind() == SyntaxKind::EXHIBIT_KW)
}
pub fn is_include(&self) -> bool {
self.0
.children_with_tokens()
.filter_map(|e| e.into_token())
.any(|t| t.kind() == SyntaxKind::INCLUDE_KW)
}
pub fn is_allocate(&self) -> bool {
self.0
.children_with_tokens()
.filter_map(|e| e.into_token())
.any(|t| t.kind() == SyntaxKind::ALLOCATE_KW)
}
pub fn is_flow(&self) -> bool {
self.0
.children_with_tokens()
.filter_map(|e| e.into_token())
.any(|t| t.kind() == SyntaxKind::FLOW_KW)
}
pub fn direct_flow_endpoints(&self) -> (Option<QualifiedName>, Option<QualifiedName>) {
if !self.is_flow() {
return (None, None);
}
if self.from_to_clause().is_some() {
return (None, None);
}
let mut found_flow = false;
let mut found_to = false;
let mut source: Option<QualifiedName> = None;
let mut target: Option<QualifiedName> = None;
for elem in self.0.children_with_tokens() {
if let Some(token) = elem.as_token() {
if token.kind() == SyntaxKind::FLOW_KW {
found_flow = true;
} else if token.kind() == SyntaxKind::TO_KW && found_flow {
found_to = true;
}
} else if let Some(node) = elem.as_node() {
if found_flow && node.kind() == SyntaxKind::QUALIFIED_NAME {
if !found_to && source.is_none() {
source = QualifiedName::cast(node.clone());
} else if found_to && target.is_none() {
target = QualifiedName::cast(node.clone());
}
}
}
}
(source, target)
}
pub fn is_assert(&self) -> bool {
self.0
.children_with_tokens()
.filter_map(|e| e.into_token())
.any(|t| t.kind() == SyntaxKind::ASSERT_KW)
}
pub fn is_assume(&self) -> bool {
self.0
.children_with_tokens()
.filter_map(|e| e.into_token())
.any(|t| t.kind() == SyntaxKind::ASSUME_KW)
}
pub fn is_require(&self) -> bool {
self.0
.children_with_tokens()
.filter_map(|e| e.into_token())
.any(|t| t.kind() == SyntaxKind::REQUIRE_KW)
}
pub fn usage_kind(&self) -> Option<UsageKind> {
for token in self.0.children_with_tokens().filter_map(|e| e.into_token()) {
match token.kind() {
SyntaxKind::PART_KW => return Some(UsageKind::Part),
SyntaxKind::ATTRIBUTE_KW => return Some(UsageKind::Attribute),
SyntaxKind::PORT_KW => return Some(UsageKind::Port),
SyntaxKind::ITEM_KW => return Some(UsageKind::Item),
SyntaxKind::ACTION_KW => return Some(UsageKind::Action),
SyntaxKind::STATE_KW => return Some(UsageKind::State),
SyntaxKind::CONSTRAINT_KW => return Some(UsageKind::Constraint),
SyntaxKind::REQUIREMENT_KW => return Some(UsageKind::Requirement),
SyntaxKind::CASE_KW => return Some(UsageKind::Case),
SyntaxKind::CALC_KW => return Some(UsageKind::Calc),
SyntaxKind::CONNECTION_KW => return Some(UsageKind::Connection),
SyntaxKind::INTERFACE_KW => return Some(UsageKind::Interface),
SyntaxKind::ALLOCATION_KW => return Some(UsageKind::Allocation),
SyntaxKind::FLOW_KW | SyntaxKind::MESSAGE_KW => return Some(UsageKind::Flow),
SyntaxKind::OCCURRENCE_KW => return Some(UsageKind::Occurrence),
SyntaxKind::REF_KW => return Some(UsageKind::Ref),
SyntaxKind::FEATURE_KW => return Some(UsageKind::Feature),
SyntaxKind::STEP_KW => return Some(UsageKind::Step),
SyntaxKind::EXPR_KW => return Some(UsageKind::Expr),
SyntaxKind::CONNECTOR_KW => return Some(UsageKind::Connector),
_ => {}
}
}
None
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum UsageKind {
Part,
Attribute,
Port,
Item,
Action,
State,
Constraint,
Requirement,
Case,
Calc,
Connection,
Interface,
Allocation,
Flow,
Occurrence,
Ref,
Feature,
Step,
Expr,
Connector,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Direction {
In,
Out,
InOut,
}
ast_node!(Name, NAME);
impl Name {
pub fn short_name(&self) -> Option<ShortName> {
self.0.children().find_map(ShortName::cast)
}
pub fn text(&self) -> Option<String> {
self.0
.children_with_tokens()
.filter_map(|e| e.into_token())
.find(|t| {
matches!(
t.kind(),
SyntaxKind::IDENT
| SyntaxKind::START_KW
| SyntaxKind::END_KW
| SyntaxKind::DONE_KW
| SyntaxKind::THIS_KW
)
})
.map(|t| t.text().to_string())
}
}
ast_node!(ShortName, SHORT_NAME);
impl ShortName {
pub fn text(&self) -> Option<String> {
self.0
.children_with_tokens()
.filter_map(|e| e.into_token())
.find(|t| {
matches!(
t.kind(),
SyntaxKind::IDENT
| SyntaxKind::START_KW
| SyntaxKind::END_KW
| SyntaxKind::DONE_KW
)
})
.map(|t| {
let text = t.text();
if text.starts_with('\'') && text.ends_with('\'') && text.len() > 1 {
text[1..text.len() - 1].to_string()
} else {
text.to_string()
}
})
}
}
ast_node!(QualifiedName, QUALIFIED_NAME);
impl QualifiedName {
pub fn segments(&self) -> Vec<String> {
self.0
.children_with_tokens()
.filter_map(|e| e.into_token())
.filter(|t| {
matches!(
t.kind(),
SyntaxKind::IDENT
| SyntaxKind::START_KW
| SyntaxKind::END_KW
| SyntaxKind::DONE_KW
)
})
.map(|t| {
let text = t.text();
if text.starts_with('\'') && text.ends_with('\'') && text.len() > 1 {
text[1..text.len() - 1].to_string()
} else {
text.to_string()
}
})
.collect()
}
pub fn segments_with_ranges(&self) -> Vec<(String, rowan::TextRange)> {
self.0
.children_with_tokens()
.filter_map(|e| e.into_token())
.filter(|t| {
matches!(
t.kind(),
SyntaxKind::IDENT
| SyntaxKind::START_KW
| SyntaxKind::END_KW
| SyntaxKind::DONE_KW
)
})
.map(|t| {
let text = t.text();
let stripped = if text.starts_with('\'') && text.ends_with('\'') && text.len() > 1 {
text[1..text.len() - 1].to_string()
} else {
text.to_string()
};
(stripped, t.text_range())
})
.collect()
}
fn to_string_inner(&self) -> String {
let has_dot = self
.0
.children_with_tokens()
.filter_map(|e| e.into_token())
.any(|t| t.kind() == SyntaxKind::DOT);
let separator = if has_dot { "." } else { "::" };
self.segments().join(separator)
}
}
impl std::fmt::Display for QualifiedName {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.to_string_inner())
}
}
ast_node!(Typing, TYPING);
impl Typing {
pub fn target(&self) -> Option<QualifiedName> {
self.0.children().find_map(QualifiedName::cast)
}
}
ast_node!(Specialization, SPECIALIZATION);
impl Specialization {
pub fn kind(&self) -> Option<SpecializationKind> {
for token in self.0.children_with_tokens().filter_map(|e| e.into_token()) {
match token.kind() {
SyntaxKind::COLON_GT => return Some(SpecializationKind::Specializes),
SyntaxKind::COLON_GT_GT => return Some(SpecializationKind::Redefines),
SyntaxKind::COLON_COLON_GT => return Some(SpecializationKind::FeatureChain),
SyntaxKind::SPECIALIZES_KW => return Some(SpecializationKind::Specializes),
SyntaxKind::SUBSETS_KW => return Some(SpecializationKind::Subsets),
SyntaxKind::REDEFINES_KW => return Some(SpecializationKind::Redefines),
SyntaxKind::REFERENCES_KW => return Some(SpecializationKind::References),
SyntaxKind::TILDE => return Some(SpecializationKind::Conjugates),
SyntaxKind::FROM_KW => return Some(SpecializationKind::FeatureChain),
SyntaxKind::TO_KW => return Some(SpecializationKind::FeatureChain),
SyntaxKind::CHAINS_KW => return Some(SpecializationKind::FeatureChain),
_ => {}
}
}
None
}
pub fn is_shorthand_redefines(&self) -> bool {
self.0
.children_with_tokens()
.filter_map(|e| e.into_token())
.any(|t| t.kind() == SyntaxKind::COLON_GT_GT)
}
pub fn target(&self) -> Option<QualifiedName> {
self.0.children().find_map(QualifiedName::cast)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SpecializationKind {
Specializes,
Subsets,
Redefines,
References,
Conjugates,
FeatureChain,
}
ast_node!(FromToClause, FROM_TO_CLAUSE);
impl FromToClause {
pub fn source(&self) -> Option<FromToSource> {
self.0.children().find_map(FromToSource::cast)
}
pub fn target(&self) -> Option<FromToTarget> {
self.0.children().find_map(FromToTarget::cast)
}
}
ast_node!(FromToSource, FROM_TO_SOURCE);
impl FromToSource {
pub fn target(&self) -> Option<QualifiedName> {
self.0.children().find_map(QualifiedName::cast)
}
}
ast_node!(FromToTarget, FROM_TO_TARGET);
impl FromToTarget {
pub fn target(&self) -> Option<QualifiedName> {
self.0.children().find_map(QualifiedName::cast)
}
}
ast_node!(TransitionUsage, TRANSITION_USAGE);
impl TransitionUsage {
pub fn name(&self) -> Option<Name> {
use crate::parser::SyntaxKind;
for child in self.0.children_with_tokens() {
match &child {
rowan::NodeOrToken::Token(t) => {
match t.kind() {
SyntaxKind::FIRST_KW
| SyntaxKind::ACCEPT_KW
| SyntaxKind::THEN_KW
| SyntaxKind::DO_KW
| SyntaxKind::IF_KW
| SyntaxKind::VIA_KW => return None,
_ => {}
}
}
rowan::NodeOrToken::Node(n) => {
if let Some(name) = Name::cast(n.clone()) {
return Some(name);
}
}
}
}
None
}
pub fn specializations(&self) -> impl Iterator<Item = Specialization> + '_ {
self.0.children().filter_map(Specialization::cast)
}
pub fn source(&self) -> Option<Specialization> {
self.specializations().next()
}
pub fn target(&self) -> Option<Specialization> {
self.specializations().nth(1)
}
pub fn accept_payload_name(&self) -> Option<Name> {
use crate::parser::SyntaxKind;
let mut found_accept = false;
for child in self.0.children_with_tokens() {
match &child {
rowan::NodeOrToken::Token(t) if t.kind() == SyntaxKind::ACCEPT_KW => {
found_accept = true;
}
rowan::NodeOrToken::Node(n) if found_accept => {
if let Some(name) = Name::cast(n.clone()) {
return Some(name);
}
}
_ => {}
}
}
None
}
pub fn accept_typing(&self) -> Option<Typing> {
self.0.children().find_map(Typing::cast)
}
pub fn accept_via(&self) -> Option<QualifiedName> {
use crate::parser::SyntaxKind;
let mut seen_via = false;
for child in self.0.children_with_tokens() {
match child {
rowan::NodeOrToken::Token(t) if t.kind() == SyntaxKind::VIA_KW => {
seen_via = true;
}
rowan::NodeOrToken::Node(n) if seen_via => {
if let Some(qn) = QualifiedName::cast(n) {
return Some(qn);
}
}
_ => {}
}
}
None
}
}
ast_node!(PerformActionUsage, PERFORM_ACTION_USAGE);
impl PerformActionUsage {
pub fn name(&self) -> Option<Name> {
self.0.children().find_map(Name::cast)
}
pub fn typing(&self) -> Option<Typing> {
self.0.children().find_map(Typing::cast)
}
pub fn specializations(&self) -> impl Iterator<Item = Specialization> + '_ {
self.0.children().filter_map(Specialization::cast)
}
pub fn performed(&self) -> Option<Specialization> {
self.specializations().next()
}
pub fn body(&self) -> Option<NamespaceBody> {
self.0.children().find_map(NamespaceBody::cast)
}
}
ast_node!(AcceptActionUsage, ACCEPT_ACTION_USAGE);
impl AcceptActionUsage {
pub fn name(&self) -> Option<Name> {
self.0.children().find_map(Name::cast)
}
pub fn trigger(&self) -> Option<Expression> {
self.0.children().find_map(Expression::cast)
}
pub fn accepted(&self) -> Option<QualifiedName> {
self.0.children().find_map(QualifiedName::cast)
}
pub fn via(&self) -> Option<QualifiedName> {
let mut seen_via = false;
for child in self.0.children_with_tokens() {
match child {
rowan::NodeOrToken::Token(t) if t.kind() == SyntaxKind::VIA_KW => {
seen_via = true;
}
rowan::NodeOrToken::Node(n) if seen_via => {
if let Some(qn) = QualifiedName::cast(n) {
return Some(qn);
}
}
_ => {}
}
}
None
}
}
ast_node!(SendActionUsage, SEND_ACTION_USAGE);
impl SendActionUsage {
pub fn payload(&self) -> Option<Expression> {
self.0.children().find_map(Expression::cast)
}
pub fn qualified_names(&self) -> impl Iterator<Item = QualifiedName> + '_ {
self.0.children().filter_map(QualifiedName::cast)
}
}
ast_node!(ForLoopActionUsage, FOR_LOOP_ACTION_USAGE);
impl ForLoopActionUsage {
pub fn variable_name(&self) -> Option<Name> {
self.0.children().find_map(Name::cast)
}
pub fn typing(&self) -> Option<Typing> {
self.0.children().find_map(Typing::cast)
}
pub fn body(&self) -> Option<NamespaceBody> {
self.0.children().find_map(NamespaceBody::cast)
}
pub fn members(&self) -> impl Iterator<Item = NamespaceMember> + '_ {
self.body()
.into_iter()
.flat_map(|b| b.members().collect::<Vec<_>>())
}
}
ast_node!(IfActionUsage, IF_ACTION_USAGE);
impl IfActionUsage {
pub fn expressions(&self) -> impl Iterator<Item = Expression> + '_ {
self.0.descendants().filter_map(Expression::cast)
}
pub fn qualified_names(&self) -> impl Iterator<Item = QualifiedName> + '_ {
self.0.children().filter_map(QualifiedName::cast)
}
pub fn body(&self) -> Option<NamespaceBody> {
self.0.children().find_map(NamespaceBody::cast)
}
}
ast_node!(WhileLoopActionUsage, WHILE_LOOP_ACTION_USAGE);
impl WhileLoopActionUsage {
pub fn expressions(&self) -> impl Iterator<Item = Expression> + '_ {
self.0.descendants().filter_map(Expression::cast)
}
pub fn body(&self) -> Option<NamespaceBody> {
self.0.children().find_map(NamespaceBody::cast)
}
pub fn members(&self) -> impl Iterator<Item = NamespaceMember> + '_ {
self.body()
.into_iter()
.flat_map(|b| b.members().collect::<Vec<_>>())
}
}
ast_node!(StateSubaction, STATE_SUBACTION);
impl StateSubaction {
pub fn kind(&self) -> Option<SyntaxKind> {
self.0
.children_with_tokens()
.filter_map(|e| e.into_token())
.find(|t| {
matches!(
t.kind(),
SyntaxKind::ENTRY_KW | SyntaxKind::DO_KW | SyntaxKind::EXIT_KW
)
})
.map(|t| t.kind())
}
pub fn name(&self) -> Option<Name> {
self.0.children().find_map(Name::cast)
}
pub fn body(&self) -> Option<NamespaceBody> {
self.0.children().find_map(NamespaceBody::cast)
}
pub fn is_entry(&self) -> bool {
self.kind() == Some(SyntaxKind::ENTRY_KW)
}
pub fn is_do(&self) -> bool {
self.kind() == Some(SyntaxKind::DO_KW)
}
pub fn is_exit(&self) -> bool {
self.kind() == Some(SyntaxKind::EXIT_KW)
}
}
ast_node!(ControlNode, CONTROL_NODE);
impl ControlNode {
pub fn kind(&self) -> Option<SyntaxKind> {
self.0
.children_with_tokens()
.filter_map(|e| e.into_token())
.find(|t| {
matches!(
t.kind(),
SyntaxKind::FORK_KW
| SyntaxKind::JOIN_KW
| SyntaxKind::MERGE_KW
| SyntaxKind::DECIDE_KW
)
})
.map(|t| t.kind())
}
pub fn name(&self) -> Option<Name> {
self.0.children().find_map(Name::cast)
}
pub fn body(&self) -> Option<NamespaceBody> {
self.0.children().find_map(NamespaceBody::cast)
}
pub fn is_fork(&self) -> bool {
self.kind() == Some(SyntaxKind::FORK_KW)
}
pub fn is_join(&self) -> bool {
self.kind() == Some(SyntaxKind::JOIN_KW)
}
pub fn is_merge(&self) -> bool {
self.kind() == Some(SyntaxKind::MERGE_KW)
}
pub fn is_decide(&self) -> bool {
self.kind() == Some(SyntaxKind::DECIDE_KW)
}
}
ast_node!(RequirementVerification, REQUIREMENT_VERIFICATION);
impl RequirementVerification {
pub fn is_satisfy(&self) -> bool {
self.0
.children_with_tokens()
.filter_map(|e| e.into_token())
.any(|t| t.kind() == SyntaxKind::SATISFY_KW)
}
pub fn is_verify(&self) -> bool {
self.0
.children_with_tokens()
.filter_map(|e| e.into_token())
.any(|t| t.kind() == SyntaxKind::VERIFY_KW)
}
pub fn is_negated(&self) -> bool {
self.0
.children_with_tokens()
.filter_map(|e| e.into_token())
.any(|t| t.kind() == SyntaxKind::NOT_KW)
}
pub fn is_asserted(&self) -> bool {
self.0
.children_with_tokens()
.filter_map(|e| e.into_token())
.any(|t| t.kind() == SyntaxKind::ASSERT_KW)
}
pub fn requirement(&self) -> Option<QualifiedName> {
self.0.children().find_map(QualifiedName::cast)
}
pub fn by_target(&self) -> Option<QualifiedName> {
let mut seen_by = false;
for child in self.0.children_with_tokens() {
match child {
rowan::NodeOrToken::Token(t) if t.kind() == SyntaxKind::BY_KW => {
seen_by = true;
}
rowan::NodeOrToken::Node(n) if seen_by => {
if let Some(qn) = QualifiedName::cast(n) {
return Some(qn);
}
}
_ => {}
}
}
None
}
pub fn typing(&self) -> Option<Typing> {
self.0.children().find_map(Typing::cast)
}
}
ast_node!(Connector, CONNECTOR);
impl Connector {
pub fn name(&self) -> Option<Name> {
self.0.children().find_map(Name::cast)
}
pub fn typing(&self) -> Option<Typing> {
self.0.children().find_map(Typing::cast)
}
pub fn connector_part(&self) -> Option<ConnectorPart> {
self.0.children().find_map(ConnectorPart::cast)
}
pub fn ends(&self) -> impl Iterator<Item = ConnectorEnd> + '_ {
let from_part: Vec<_> = self
.connector_part()
.into_iter()
.flat_map(|cp| cp.ends().collect::<Vec<_>>())
.collect();
let direct: Vec<_> = if from_part.is_empty() {
self.0.children().filter_map(ConnectorEnd::cast).collect()
} else {
Vec::new()
};
from_part.into_iter().chain(direct)
}
pub fn body(&self) -> Option<NamespaceBody> {
self.0.children().find_map(NamespaceBody::cast)
}
}
ast_node!(ConnectUsage, CONNECT_USAGE);
impl ConnectUsage {
pub fn connector_part(&self) -> Option<ConnectorPart> {
self.0.children().find_map(ConnectorPart::cast)
}
}
ast_node!(ConnectorPart, CONNECTOR_PART);
impl ConnectorPart {
pub fn ends(&self) -> impl Iterator<Item = ConnectorEnd> + '_ {
self.0.children().filter_map(ConnectorEnd::cast)
}
pub fn source(&self) -> Option<ConnectorEnd> {
self.ends().next()
}
pub fn target(&self) -> Option<ConnectorEnd> {
self.ends().nth(1)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct ConnectorEnd(SyntaxNode);
impl AstNode for ConnectorEnd {
fn can_cast(kind: SyntaxKind) -> bool {
kind == SyntaxKind::CONNECTION_END || kind == SyntaxKind::CONNECTOR_END
}
fn cast(node: SyntaxNode) -> Option<Self> {
if Self::can_cast(node.kind()) {
Some(Self(node))
} else {
None
}
}
fn syntax(&self) -> &SyntaxNode {
&self.0
}
}
impl ConnectorEnd {
pub fn target(&self) -> Option<QualifiedName> {
if let Some(ref_node) = self
.0
.children()
.find(|n| n.kind() == SyntaxKind::CONNECTOR_END_REFERENCE)
{
let qns: Vec<_> = ref_node
.children()
.filter_map(QualifiedName::cast)
.collect();
let has_references = ref_node.children_with_tokens().any(|n| {
n.kind() == SyntaxKind::COLON_COLON_GT || n.kind() == SyntaxKind::REFERENCES_KW
});
if has_references && qns.len() > 1 {
return Some(qns[1].clone());
} else {
return qns.into_iter().next();
}
}
self.0.children().find_map(QualifiedName::cast)
}
pub fn endpoint_name(&self) -> Option<QualifiedName> {
if let Some(ref_node) = self
.0
.children()
.find(|n| n.kind() == SyntaxKind::CONNECTOR_END_REFERENCE)
{
let has_references = ref_node.children_with_tokens().any(|n| {
n.kind() == SyntaxKind::COLON_COLON_GT || n.kind() == SyntaxKind::REFERENCES_KW
});
if has_references {
return ref_node.children().filter_map(QualifiedName::cast).next();
}
}
None
}
}
ast_node!(BindingConnector, BINDING_CONNECTOR);
impl BindingConnector {
pub fn qualified_names(&self) -> impl Iterator<Item = QualifiedName> + '_ {
self.0.children().filter_map(QualifiedName::cast)
}
pub fn source(&self) -> Option<QualifiedName> {
self.qualified_names().next()
}
pub fn target(&self) -> Option<QualifiedName> {
self.qualified_names().nth(1)
}
}
ast_node!(Succession, SUCCESSION);
impl Succession {
pub fn items(&self) -> impl Iterator<Item = SuccessionItem> + '_ {
self.0.children().filter_map(SuccessionItem::cast)
}
pub fn source(&self) -> Option<SuccessionItem> {
self.items().next()
}
pub fn target(&self) -> Option<SuccessionItem> {
self.items().nth(1)
}
pub fn inline_usages(&self) -> impl Iterator<Item = Usage> + '_ {
self.0.children().filter_map(Usage::cast)
}
}
ast_node!(SuccessionItem, SUCCESSION_ITEM);
impl SuccessionItem {
pub fn target(&self) -> Option<QualifiedName> {
self.0.children().find_map(QualifiedName::cast)
}
pub fn usage(&self) -> Option<Usage> {
self.0.children().find_map(Usage::cast)
}
}
ast_node!(ConstraintBody, CONSTRAINT_BODY);
impl ConstraintBody {
pub fn expression(&self) -> Option<Expression> {
self.0.children().find_map(Expression::cast)
}
pub fn members(&self) -> impl Iterator<Item = NamespaceMember> + '_ {
self.0.children().filter_map(NamespaceMember::cast)
}
}
ast_node!(Expression, EXPRESSION);
#[derive(Debug, Clone)]
pub struct FeatureChainRef {
pub parts: Vec<(String, rowan::TextRange)>,
pub full_range: rowan::TextRange,
}
impl Expression {
pub fn references(&self) -> Vec<(String, rowan::TextRange)> {
let mut refs = Vec::new();
self.collect_references(&self.0, &mut refs);
refs
}
pub fn feature_chains(&self) -> Vec<FeatureChainRef> {
let mut chains = Vec::new();
self.collect_feature_chains(&self.0, &mut chains);
chains
}
pub fn named_constructor_args(&self) -> Vec<(String, String, rowan::TextRange)> {
let mut results = Vec::new();
self.collect_named_constructor_args(&self.0, &mut results);
results
}
fn collect_named_constructor_args(
&self,
node: &SyntaxNode,
results: &mut Vec<(String, String, rowan::TextRange)>,
) {
let children: Vec<_> = node.children_with_tokens().collect();
let mut i = 0;
while i < children.len() {
if let Some(token) = children[i].as_token() {
if token.kind() == SyntaxKind::NEW_KW {
let mut type_name = None;
for child in children.iter().skip(i + 1) {
if let Some(qn_node) = child.as_node() {
if qn_node.kind() == SyntaxKind::QUALIFIED_NAME {
type_name = Some(qn_node.text().to_string());
break;
}
}
}
if let Some(type_name) = type_name {
for child in children.iter().skip(i + 1) {
if let Some(arg_list_node) = child.as_node() {
if arg_list_node.kind() == SyntaxKind::ARGUMENT_LIST {
self.extract_named_args_from_list(
arg_list_node,
&type_name,
results,
);
}
}
}
}
}
}
i += 1;
}
for child in node.children() {
self.collect_named_constructor_args(&child, results);
}
}
fn extract_named_args_from_list(
&self,
arg_list: &SyntaxNode,
type_name: &str,
results: &mut Vec<(String, String, rowan::TextRange)>,
) {
for child in arg_list.children() {
if child.kind() == SyntaxKind::ARGUMENT_LIST {
let tokens: Vec<_> = child.children_with_tokens().collect();
for (idx, elem) in tokens.iter().enumerate() {
if let Some(token) = elem.as_token() {
if token.kind() == SyntaxKind::IDENT {
for next_elem in tokens.iter().skip(idx + 1) {
if let Some(next_token) = next_elem.as_token() {
if next_token.kind() == SyntaxKind::WHITESPACE {
continue;
}
if next_token.kind() == SyntaxKind::EQ {
results.push((
type_name.to_string(),
token.text().to_string(),
token.text_range(),
));
}
break;
}
}
}
}
}
self.extract_named_args_from_list(&child, type_name, results);
}
}
}
fn collect_feature_chains(&self, node: &SyntaxNode, chains: &mut Vec<FeatureChainRef>) {
if node.kind() == SyntaxKind::QUALIFIED_NAME {
let mut parts = Vec::new();
for child in node.children_with_tokens() {
if let rowan::NodeOrToken::Token(token) = child {
if token.kind() == SyntaxKind::IDENT {
parts.push((token.text().to_string(), token.text_range()));
}
}
}
if !parts.is_empty() {
let full_range = node.text_range();
chains.push(FeatureChainRef { parts, full_range });
}
return; }
for child in node.children_with_tokens() {
match &child {
rowan::NodeOrToken::Token(token) if token.kind() == SyntaxKind::IDENT => {
let parts = vec![(token.text().to_string(), token.text_range())];
chains.push(FeatureChainRef {
parts,
full_range: token.text_range(),
});
}
rowan::NodeOrToken::Node(child_node) => {
self.collect_feature_chains(child_node, chains);
}
_ => {}
}
}
}
fn collect_references(&self, node: &SyntaxNode, refs: &mut Vec<(String, rowan::TextRange)>) {
for child in node.children_with_tokens() {
match child {
rowan::NodeOrToken::Token(token) => {
if token.kind() == SyntaxKind::IDENT {
refs.push((token.text().to_string(), token.text_range()));
}
}
rowan::NodeOrToken::Node(child_node) => {
self.collect_references(&child_node, refs);
}
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::parser::parse_sysml;
#[test]
fn test_ast_package() {
let parsed = parse_sysml("package Test;");
let root = SourceFile::cast(parsed.syntax()).unwrap();
let members: Vec<_> = root.members().collect();
assert_eq!(members.len(), 1);
if let NamespaceMember::Package(pkg) = &members[0] {
let name = pkg.name().unwrap();
assert_eq!(name.text(), Some("Test".to_string()));
} else {
panic!("expected Package");
}
}
#[test]
fn test_ast_import() {
let parsed = parse_sysml("import ISQ::*;");
let root = SourceFile::cast(parsed.syntax()).unwrap();
let members: Vec<_> = root.members().collect();
assert_eq!(members.len(), 1);
if let NamespaceMember::Import(imp) = &members[0] {
assert!(!imp.is_all());
assert!(imp.is_wildcard());
assert!(!imp.is_recursive());
let target = imp.target().unwrap();
assert_eq!(target.segments(), vec!["ISQ"]);
} else {
panic!("expected Import");
}
}
#[test]
fn test_ast_import_recursive() {
let parsed = parse_sysml("import all Library::**;");
assert!(parsed.ok(), "errors: {:?}", parsed.errors);
let root = SourceFile::cast(parsed.syntax()).unwrap();
let members: Vec<_> = root.members().collect();
if let NamespaceMember::Import(imp) = &members[0] {
assert!(imp.is_all());
assert!(imp.is_recursive());
} else {
panic!("expected Import");
}
}
#[test]
fn test_ast_definition() {
let parsed = parse_sysml("abstract part def Vehicle :> Base;");
let root = SourceFile::cast(parsed.syntax()).unwrap();
let members: Vec<_> = root.members().collect();
if let NamespaceMember::Definition(def) = &members[0] {
assert!(def.is_abstract());
assert_eq!(def.definition_kind(), Some(DefinitionKind::Part));
let name = def.name().unwrap();
assert_eq!(name.text(), Some("Vehicle".to_string()));
let specializations: Vec<_> = def.specializations().collect();
assert_eq!(specializations.len(), 1);
assert_eq!(
specializations[0].kind(),
Some(SpecializationKind::Specializes)
);
} else {
panic!("expected Definition");
}
}
#[test]
fn test_ast_usage() {
let parsed = parse_sysml("ref part engine : Engine;");
let root = SourceFile::cast(parsed.syntax()).unwrap();
let members: Vec<_> = root.members().collect();
if let NamespaceMember::Usage(usage) = &members[0] {
assert!(usage.is_ref());
let name = usage.name().unwrap();
assert_eq!(name.text(), Some("engine".to_string()));
let typing = usage.typing().unwrap();
let target = typing.target().unwrap();
assert_eq!(target.segments(), vec!["Engine"]);
} else {
panic!("expected Usage");
}
}
#[test]
fn test_message_usage_name() {
let parsed = parse_sysml("part p { message of ignitionCmd : IgnitionCmd; }");
let root = SourceFile::cast(parsed.syntax()).unwrap();
let members: Vec<_> = root.members().collect();
if let NamespaceMember::Usage(part_usage) = &members[0] {
if let Some(body) = part_usage.body() {
let inner_members: Vec<_> = body.members().collect();
if let NamespaceMember::Usage(usage) = &inner_members[0] {
let name = usage.name();
assert!(name.is_some(), "message usage should have a name");
assert_eq!(name.unwrap().text(), Some("ignitionCmd".to_string()));
return;
}
}
}
panic!("expected Usage for part p with message inside");
}
}