hawdb-sql-syntax 0.16.0

An embedded Rust graph database for local data-plane workloads
Documentation
// Copyright 2026 Nowledge
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use super::{Expression, Grammar};
use crate::TokenKind;

impl<T> Grammar<T> {
    pub(super) fn validate(&self) -> Result<(), String> {
        for (_, expression) in &self.rules {
            self.check_references(expression)?;
        }
        let mut nullable = vec![false; self.rules.len()];
        loop {
            let mut changed = false;
            for (index, (_, expression)) in self.rules.iter().enumerate() {
                if !nullable[index] && self.nullable(expression, &nullable) {
                    nullable[index] = true;
                    changed = true;
                }
            }
            if !changed {
                break;
            }
        }
        let mut edges = Vec::new();
        for (_, expression) in &self.rules {
            self.check_repetition(expression, &nullable)?;
            let mut next = Vec::new();
            self.leading_rules(expression, &nullable, &mut next);
            edges.push(next);
        }
        let mut visiting = vec![false; self.rules.len()];
        let mut visited = vec![false; self.rules.len()];
        for root in 0..self.rules.len() {
            if cycle(root, &edges, &mut visiting, &mut visited) {
                return Err(format!(
                    "left recursion in PEG rule: {}",
                    self.rules[root].0
                ));
            }
        }
        Ok(())
    }

    fn check_references(&self, expression: &Expression<T>) -> Result<(), String> {
        match expression {
            Expression::Rule(name) if !self.names.contains_key(name) => {
                Err(format!("unknown PEG rule: {name}"))
            }
            Expression::Choice(items) | Expression::Sequence(items) => {
                for item in items {
                    self.check_references(item)?;
                }
                Ok(())
            }
            Expression::Optional(item)
            | Expression::Repeat(item)
            | Expression::Ahead(item, _)
            | Expression::Not(item, _) => self.check_references(item),
            _ => Ok(()),
        }
    }

    fn nullable(&self, expression: &Expression<T>, nullable: &[bool]) -> bool {
        match expression {
            Expression::Rule(name) => nullable[self.names[name]],
            Expression::Sequence(items) => items.iter().all(|item| self.nullable(item, nullable)),
            Expression::Choice(items) => items.iter().any(|item| self.nullable(item, nullable)),
            Expression::Optional(_)
            | Expression::Repeat(_)
            | Expression::Ahead(_, _)
            | Expression::Not(_, _) => true,
            Expression::Native { nullable, .. } => *nullable,
            Expression::Token(TokenKind::End, _) => true,
            _ => false,
        }
    }

    fn check_repetition(
        &self,
        expression: &Expression<T>,
        nullable: &[bool],
    ) -> Result<(), String> {
        match expression {
            Expression::Repeat(item) if self.nullable(item, nullable) => {
                Err("nullable PEG repetition".into())
            }
            Expression::Choice(items) | Expression::Sequence(items) => {
                for item in items {
                    self.check_repetition(item, nullable)?;
                }
                Ok(())
            }
            Expression::Optional(item)
            | Expression::Repeat(item)
            | Expression::Ahead(item, _)
            | Expression::Not(item, _) => self.check_repetition(item, nullable),
            _ => Ok(()),
        }
    }

    fn leading_rules(&self, expression: &Expression<T>, nullable: &[bool], edges: &mut Vec<usize>) {
        match expression {
            Expression::Rule(name) => edges.push(self.names[name]),
            Expression::Sequence(items) => {
                for item in items {
                    self.leading_rules(item, nullable, edges);
                    if !self.nullable(item, nullable) {
                        break;
                    }
                }
            }
            Expression::Choice(items) => {
                for item in items {
                    self.leading_rules(item, nullable, edges);
                }
            }
            Expression::Optional(item)
            | Expression::Repeat(item)
            | Expression::Ahead(item, _)
            | Expression::Not(item, _) => {
                self.leading_rules(item, nullable, edges);
            }
            _ => (),
        }
    }
}

fn cycle(node: usize, edges: &[Vec<usize>], visiting: &mut [bool], visited: &mut [bool]) -> bool {
    if visiting[node] {
        return true;
    }
    if visited[node] {
        return false;
    }
    visiting[node] = true;
    for child in &edges[node] {
        if cycle(*child, edges, visiting, visited) {
            return true;
        }
    }
    visiting[node] = false;
    visited[node] = true;
    false
}