use crate::parser::ast::{
ElementNode, FunctionNode, IndexNode, Node, SelectorNode, SetOperationNode,
TextNode, Visitor, Visitable,
};
use crate::parser::error::ParseError;
#[derive(Debug, Clone)]
struct ValidationState {
has_text_selector: bool,
text_selector_pos: Option<(usize, usize)>,
current_pos: (usize, usize),
}
impl ValidationState {
fn new() -> Self {
ValidationState {
has_text_selector: false,
text_selector_pos: None,
current_pos: (0, 0),
}
}
fn set_position(&mut self, line: usize, column: usize) {
self.current_pos = (line, column);
}
fn mark_text_selector(&mut self) {
self.has_text_selector = true;
self.text_selector_pos = Some(self.current_pos);
}
fn can_add_element_selector(&self) -> bool {
!self.has_text_selector
}
}
pub struct SyntaxValidator {
state: ValidationState,
}
impl SyntaxValidator {
pub fn new() -> Self {
SyntaxValidator {
state: ValidationState::new(),
}
}
pub fn validate(&mut self, node: &Node) -> Result<(), ParseError> {
node.accept(self)
}
#[allow(dead_code)]
pub fn set_position(&mut self, line: usize, column: usize) {
self.state.set_position(line, column);
}
}
impl Visitor<Result<(), ParseError>> for SyntaxValidator {
fn visit_node(&mut self, node: &Node) -> Result<(), ParseError> {
match node {
Node::Selector(selector) => self.visit_selector(selector),
Node::Pipeline(left, right) => self.visit_pipeline(left, right),
Node::SetOperation(op) => self.visit_set_operation(op),
Node::IndexSelection(inner, idx) => {
self.visit_node(inner)?;
self.visit_index(idx)
}
Node::FunctionCall(inner, func) => {
self.visit_node(inner)?;
self.visit_function(func)
}
}
}
fn visit_selector(&mut self, node: &SelectorNode) -> Result<(), ParseError> {
match node {
SelectorNode::ElementSelector(elem) => self.visit_element(elem),
SelectorNode::TextSelector(text) => self.visit_text(text),
}
}
fn visit_element(&mut self, _node: &ElementNode) -> Result<(), ParseError> {
if !self.state.can_add_element_selector() {
let (line, column) = self.state.current_pos;
return Err(ParseError::element_after_text_selector(line, column));
}
Ok(())
}
fn visit_text(&mut self, _node: &TextNode) -> Result<(), ParseError> {
if self.state.has_text_selector {
let (line, column) = self.state.current_pos;
return Err(ParseError::multiple_text_selectors(line, column));
}
self.state.mark_text_selector();
Ok(())
}
fn visit_pipeline(&mut self, left: &Node, right: &Node) -> Result<(), ParseError> {
self.visit_node(left)?;
self.visit_node(right)
}
fn visit_set_operation(&mut self, node: &SetOperationNode) -> Result<(), ParseError> {
match node {
SetOperationNode::Union(left, right)
| SetOperationNode::Intersection(left, right)
| SetOperationNode::Difference(left, right) => {
let original_state = self.state.clone();
self.visit_node(left)?;
self.state = original_state;
self.visit_node(right)?;
self.state = ValidationState::new();
Ok(())
}
}
}
fn visit_index(&mut self, _node: &IndexNode) -> Result<(), ParseError> {
Ok(())
}
fn visit_function(&mut self, _node: &FunctionNode) -> Result<(), ParseError> {
Ok(())
}
}