use std::collections::HashMap;
use std::fmt;
use crate::model::StarTerm;
use crate::{StarError, StarResult};
#[derive(Debug, Default)]
pub(super) struct TrigParserState {
pub(super) current_graph: Option<StarTerm>,
pub(super) brace_depth: usize,
pub(super) in_graph_block: bool,
pub(super) graph_name_buffer: String,
pub(super) parsing_graph_name: bool,
}
impl TrigParserState {
pub(super) fn new() -> Self {
Self::default()
}
pub(super) fn reset_graph_context(&mut self) {
self.current_graph = None;
self.in_graph_block = false;
self.brace_depth = 0;
self.graph_name_buffer.clear();
self.parsing_graph_name = false;
}
pub(super) fn enter_graph_block(&mut self, graph: Option<StarTerm>) {
self.current_graph = graph;
self.in_graph_block = true;
self.brace_depth += 1;
self.parsing_graph_name = false;
self.graph_name_buffer.clear();
}
pub(super) fn exit_graph_block(&mut self) -> bool {
if self.brace_depth > 0 {
self.brace_depth -= 1;
if self.brace_depth == 0 {
self.reset_graph_context();
return true;
}
}
false
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum ErrorSeverity {
Warning,
Error,
Fatal,
}
impl fmt::Display for ErrorSeverity {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ErrorSeverity::Warning => write!(f, "Warning"),
ErrorSeverity::Error => write!(f, "Error"),
ErrorSeverity::Fatal => write!(f, "Fatal"),
}
}
}
#[derive(Debug, Clone)]
pub struct ParseError {
pub message: String,
pub line: usize,
pub column: usize,
pub context: String,
pub severity: ErrorSeverity,
}
#[derive(Debug, Default)]
pub(super) struct ParseContext {
pub(super) prefixes: HashMap<String, String>,
pub(super) base_iri: Option<String>,
#[allow(dead_code)]
pub(super) current_graph: Option<StarTerm>,
pub(super) blank_node_counter: usize,
pub(super) line_number: usize,
pub(super) column_position: usize,
pub(super) strict_mode: bool,
pub(super) error_recovery: bool,
pub(super) parsing_errors: Vec<ParseError>,
}
impl ParseContext {
pub(super) fn new() -> Self {
Self::default()
}
pub(super) fn with_config(strict_mode: bool, error_recovery: bool) -> Self {
Self {
strict_mode,
error_recovery,
..Default::default()
}
}
pub(super) fn update_position(&mut self, line: usize, column: usize) {
self.line_number = line;
self.column_position = column;
}
pub(super) fn add_error(&mut self, message: String, context: String, severity: ErrorSeverity) {
let error = ParseError {
message,
line: self.line_number,
column: self.column_position,
context,
severity,
};
self.parsing_errors.push(error);
}
pub(super) fn has_fatal_errors(&self) -> bool {
self.parsing_errors
.iter()
.any(|e| e.severity == ErrorSeverity::Fatal)
}
pub(super) fn get_errors(&self) -> &[ParseError] {
&self.parsing_errors
}
#[allow(dead_code)]
pub(super) fn clear_errors(&mut self) {
self.parsing_errors.clear();
}
pub(super) fn next_blank_node(&mut self) -> String {
self.blank_node_counter += 1;
let counter = self.blank_node_counter;
format!("_:b{counter}")
}
pub(super) fn resolve_prefix(&mut self, prefixed: &str) -> StarResult<String> {
if let Some(colon_pos) = prefixed.find(':') {
let prefix = &prefixed[..colon_pos];
let local = &prefixed[colon_pos + 1..];
if let Some(namespace) = self.prefixes.get(prefix) {
Ok(format!("{namespace}{local}"))
} else {
let error_msg = format!("Unknown prefix: '{prefix}'");
let context = format!("in prefixed name '{prefixed}'");
if self.strict_mode {
self.add_error(error_msg.clone(), context, ErrorSeverity::Fatal);
Err(StarError::parse_error(error_msg))
} else {
self.add_error(error_msg.clone(), context, ErrorSeverity::Warning);
Ok(prefixed.to_string())
}
}
} else {
let error_msg = format!("Invalid prefixed name: '{prefixed}'");
self.add_error(
error_msg.clone(),
prefixed.to_string(),
ErrorSeverity::Error,
);
Err(StarError::parse_error(error_msg))
}
}
pub(super) fn resolve_relative(&self, iri: &str) -> String {
if let Some(ref base) = self.base_iri {
if iri.starts_with('#') {
format!("{base}{iri}")
} else {
iri.to_string()
}
} else {
iri.to_string()
}
}
pub(super) fn try_recover_from_error(&mut self, error_context: &str) -> bool {
if !self.error_recovery {
return false;
}
let recovery_msg = format!("Attempting error recovery from: {error_context}");
self.add_error(
recovery_msg,
error_context.to_string(),
ErrorSeverity::Warning,
);
true
}
}