// Package parser provides the main parser router for all diagram types package parser import ( "fmt" "strings" "mermaid-go/pkg/ast" ) // MermaidParser is the main parser that routes to specific diagram parsers type MermaidParser struct{} // NewMermaidParser creates a new main mermaid parser func NewMermaidParser() *MermaidParser { return &MermaidParser{} } // Parse parses a mermaid diagram string and returns the appropriate AST func (p *MermaidParser) Parse(input string) (ast.Diagram, error) { // Detect diagram type from the input diagramType := p.detectDiagramType(input) switch diagramType { case ast.DiagramTypeFlowchart: parser := NewFlowchartParser() return parser.Parse(input) case ast.DiagramTypeSequence: parser := NewSequenceParser() return parser.Parse(input) case ast.DiagramTypeClassDiagram: parser := NewClassParser() return parser.Parse(input) case ast.DiagramTypeStateDiagram: parser := NewStateParser() return parser.Parse(input) case ast.DiagramTypePie: parser := NewPieParser() return parser.Parse(input) case ast.DiagramTypeERDiagram: parser := NewERParser() return parser.Parse(input) case ast.DiagramTypeGantt: parser := NewGanttParser() return parser.Parse(input) case ast.DiagramTypeTimeline: parser := NewTimelineParser() return parser.Parse(input) case ast.DiagramTypeUserJourney: parser := NewJourneyParser() return parser.Parse(input) case ast.DiagramTypeArchitecture: parser := NewArchitectureParser() return parser.Parse(input) case ast.DiagramTypeOrganization: parser := NewOrganizationParser() return parser.Parse(input) case ast.DiagramTypeBPMN: parser := NewBPMNParser() return parser.Parse(input) default: return nil, fmt.Errorf("unsupported diagram type: %s", diagramType) } } // detectDiagramType analyzes the input to determine the diagram type func (p *MermaidParser) detectDiagramType(input string) ast.DiagramType { // Clean input and get first meaningful line lines := strings.Split(input, "\n") for _, line := range lines { line = strings.TrimSpace(line) if line == "" || strings.HasPrefix(line, "%%") { continue // Skip empty lines and comments } // Check for explicit diagram type declarations lowerLine := strings.ToLower(line) if strings.HasPrefix(lowerLine, "sequencediagram") { return ast.DiagramTypeSequence } if strings.HasPrefix(lowerLine, "classdiagram") { return ast.DiagramTypeClassDiagram } if strings.HasPrefix(lowerLine, "statediagram") { return ast.DiagramTypeStateDiagram } if strings.HasPrefix(lowerLine, "flowchart") || strings.HasPrefix(lowerLine, "graph") { return ast.DiagramTypeFlowchart } if strings.HasPrefix(lowerLine, "erdiagram") { return ast.DiagramTypeERDiagram } if strings.HasPrefix(lowerLine, "journey") { return ast.DiagramTypeUserJourney } if strings.HasPrefix(lowerLine, "timeline") { return ast.DiagramTypeTimeline } if strings.HasPrefix(lowerLine, "gantt") { return ast.DiagramTypeGantt } if strings.HasPrefix(lowerLine, "pie") { return ast.DiagramTypePie } if strings.HasPrefix(lowerLine, "quadrantchart") { return ast.DiagramTypeQuadrant } if strings.HasPrefix(lowerLine, "requirementdiagram") { return ast.DiagramTypeRequirement } if strings.HasPrefix(lowerLine, "block") { return ast.DiagramTypeBlock } if strings.HasPrefix(lowerLine, "architecture") { return ast.DiagramTypeArchitecture } if strings.HasPrefix(lowerLine, "organization") || strings.HasPrefix(lowerLine, "orgchart") { return ast.DiagramTypeOrganization } if strings.HasPrefix(lowerLine, "bpmn") { return ast.DiagramTypeBPMN } // If no explicit type found, try to infer from content // This is a fallback for diagrams without explicit type declarations // Look for sequence diagram patterns if strings.Contains(lowerLine, "participant") || strings.Contains(lowerLine, "actor") || strings.Contains(lowerLine, "-->") || strings.Contains(lowerLine, "->>") { return ast.DiagramTypeSequence } // Look for class diagram patterns if strings.Contains(lowerLine, "class ") || strings.Contains(lowerLine, "--|>") || strings.Contains(lowerLine, "--*") || strings.Contains(lowerLine, "--o") { return ast.DiagramTypeClassDiagram } // Look for state diagram patterns if strings.Contains(lowerLine, "[*]") || strings.Contains(lowerLine, "state ") { return ast.DiagramTypeStateDiagram } // Default to flowchart for backwards compatibility break } // Default fallback return ast.DiagramTypeFlowchart } // ParseWithType forces parsing with a specific diagram type func (p *MermaidParser) ParseWithType(input string, diagramType ast.DiagramType) (ast.Diagram, error) { switch diagramType { case ast.DiagramTypeFlowchart: parser := NewFlowchartParser() return parser.Parse(input) case ast.DiagramTypeSequence: parser := NewSequenceParser() return parser.Parse(input) case ast.DiagramTypeClassDiagram: parser := NewClassParser() return parser.Parse(input) case ast.DiagramTypeStateDiagram: parser := NewStateParser() return parser.Parse(input) case ast.DiagramTypePie: parser := NewPieParser() return parser.Parse(input) case ast.DiagramTypeERDiagram: parser := NewERParser() return parser.Parse(input) case ast.DiagramTypeGantt: parser := NewGanttParser() return parser.Parse(input) case ast.DiagramTypeTimeline: parser := NewTimelineParser() return parser.Parse(input) case ast.DiagramTypeUserJourney: parser := NewJourneyParser() return parser.Parse(input) case ast.DiagramTypeArchitecture: parser := NewArchitectureParser() return parser.Parse(input) case ast.DiagramTypeOrganization: parser := NewOrganizationParser() return parser.Parse(input) case ast.DiagramTypeBPMN: parser := NewBPMNParser() return parser.Parse(input) default: return nil, fmt.Errorf("unsupported diagram type: %s", diagramType) } }