use crate::core::{ParseError, ParseResult, get_extension, load_file, validate_extension};
use crate::syntax::sysml::ast::{SysMLFile, parse_file};
use pest::Parser;
use std::path::{Path, PathBuf};
pub fn load_and_parse(path: &PathBuf) -> Result<SysMLFile, String> {
validate_extension(path)?;
let content = load_file(path)?;
parse_content(&content, path)
}
pub fn parse_content(content: &str, path: &Path) -> Result<SysMLFile, String> {
let mut pairs = crate::parser::SysMLParser::parse(crate::parser::sysml::Rule::file, content)
.map_err(|e| format!("Parse error in {}: {}", path.display(), e))?;
parse_file(&mut pairs).map_err(|e| format!("AST error in {}: {:?}", path.display(), e))
}
pub fn parse_with_result(content: &str, path: &Path) -> ParseResult<SysMLFile> {
if let Err(e) = get_extension(path) {
return ParseResult::with_errors(vec![e]);
}
match crate::parser::SysMLParser::parse(crate::parser::sysml::Rule::file, content) {
Ok(mut pairs) => match parse_file(&mut pairs) {
Ok(file) => ParseResult::success(file),
Err(e) => {
let error = ParseError::ast_error(format!("{e:?}"), 0, 0);
ParseResult::with_errors(vec![error])
}
},
Err(parse_error) => {
let (line, col) = match parse_error.line_col {
pest::error::LineColLocation::Pos((l, c)) => (l - 1, c - 1),
pest::error::LineColLocation::Span((l, c), _) => (l - 1, c - 1),
};
let error = ParseError::syntax_error(format!("{}", parse_error.variant), line, col);
match parse_valid_definitions(content) {
Some(partial_file) => ParseResult {
content: Some(partial_file),
errors: vec![error],
},
None => ParseResult::with_errors(vec![error]),
}
}
}
}
fn parse_valid_definitions(content: &str) -> Option<SysMLFile> {
use crate::syntax::sysml::ast::SysMLFile;
let mut elements = Vec::new();
for line in content.lines() {
let trimmed = line.trim();
if trimmed.is_empty() || trimmed.starts_with("//") || !trimmed.ends_with(';') {
continue;
}
if let Some(element) = try_parse_as_element(trimmed) {
elements.push(element);
}
}
if elements.is_empty() {
None
} else {
Some(SysMLFile {
namespace: None,
namespaces: Vec::new(),
elements,
})
}
}
fn try_parse_as_element(line: &str) -> Option<crate::syntax::sysml::ast::Element> {
use crate::parser::{SysMLParser, sysml::Rule};
use crate::syntax::sysml::ast::{Element, parse_definition, parse_usage};
if line.contains(" def ") {
for rule in [
Rule::part_definition,
Rule::attribute_definition,
Rule::action_definition,
Rule::item_definition,
Rule::port_definition,
Rule::connection_definition,
Rule::interface_definition,
Rule::allocation_definition,
Rule::state_definition,
Rule::requirement_definition,
Rule::enumeration_definition,
Rule::occurrence_definition,
Rule::calculation_definition,
] {
if let Ok(mut pairs) = SysMLParser::parse(rule, line)
&& let Some(pair) = pairs.next()
&& let Ok(def) = parse_definition(pair)
{
return Some(Element::Definition(def));
}
}
}
if !line.contains(" def ") {
for rule in [
Rule::part_usage,
Rule::attribute_usage,
Rule::item_usage,
Rule::port_usage,
Rule::action_usage,
] {
if let Ok(mut pairs) = SysMLParser::parse(rule, line)
&& let Some(pair) = pairs.next()
{
return Some(Element::Usage(parse_usage(pair)));
}
}
}
None
}
#[cfg(test)]
mod tests;