render_regex 0.0.0

SVG visualization of regex DFAs.
Documentation
//! Convert an AST into a VisualGraph.

use rusty_regex::ast::Ast;
use crate::graph::{VisualGraph, VisualNode, VisualEdge, NodeType, ShapeHint, EdgeStyle};

pub fn ast_to_graph(ast: &Ast) -> VisualGraph {
    let mut graph = VisualGraph::new();
    let mut next_id = 0;
    
    fn walk(node: &Ast, graph: &mut VisualGraph, next_id: &mut usize, parent: Option<usize>) -> usize {
        let id = *next_id;
        *next_id += 1;

        let label = match node {
            Ast::Literal(c) => format!("'{}'", c),
            Ast::Dot => ".".to_string(),
            Ast::CharacterClass { .. } => "[class]".to_string(),
            Ast::PredefinedClass(p) => format!("{:?}", p),
            Ast::Quantifier { .. } => "Quant".to_string(),
            Ast::Group { capturing, .. } => {
                if *capturing { "Group".to_string() } else { "NonCap".to_string() }
            },
            Ast::Alternation(_, _) => "Alt".to_string(),
            Ast::Sequence(_) => "Seq".to_string(),
            Ast::Scoped { case_i, .. } => format!("(?i:{})", case_i),
            Ast::AnchorStart => "^".to_string(),
            Ast::AnchorEnd => "$".to_string(),
            Ast::WordBoundary => "\\b".to_string(),
            Ast::LookAhead { positive, .. } => if *positive { "Look+".to_string() } else { "Look-".to_string() },
        };

        graph.nodes.push(VisualNode {
            id,
            label: label.clone(),
            shape: ShapeHint::Box,
            is_accept: false,
            node_type: NodeType::Ast,
        });

        if let Some(p) = parent {
                graph.edges.push(VisualEdge {
	        from: p,
	        to: id,
	        label,
	        style: EdgeStyle::Solid,
	        bend_offset: 0.0,
	    });

        }

        match node {
            Ast::Quantifier { node, .. } => {
                walk(node, graph, next_id, Some(id));
            }
            Ast::Group { ast: inner, .. } => {
                walk(inner, graph, next_id, Some(id));
            }
            Ast::Alternation(a, b) => {
                walk(a, graph, next_id, Some(id));
                walk(b, graph, next_id, Some(id));
            }
            Ast::Sequence(seq) => {
                for child in seq {
                    walk(child, graph, next_id, Some(id));
                }
            }
            Ast::Scoped { inner, .. } => {
                walk(inner, graph, next_id, Some(id));
            }
            Ast::LookAhead { ast: inner, .. } => {
                walk(inner, graph, next_id, Some(id));
            }
            _ => {}
        }

        id
    }

    walk(ast, &mut graph, &mut next_id, None);
    graph
}