tm 3 ay önce
ebeveyn
işleme
d78ab47682

+ 29 - 0
debug_tokens.go

@@ -0,0 +1,29 @@
+package main
+
+import (
+	"fmt"
+	"log"
+	"mermaid-go/pkg/lexer"
+)
+
+func main() {
+	fmt.Println("Debug tokenization of annotations:")
+
+	input := `classDiagram
+    class Shape {
+        <<interface>>
+        +area() double
+    }`
+
+	l := lexer.NewLexer(input)
+	tokens, err := l.Tokenize()
+	if err != nil {
+		log.Printf("Failed to tokenize: %v", err)
+		return
+	}
+
+	fmt.Printf("Tokens:\n")
+	for i, token := range tokens {
+		fmt.Printf("%d: %s\n", i, token.String())
+	}
+}

+ 28 - 0
pkg/ast/class.go

@@ -6,6 +6,7 @@ type ClassDiagram struct {
 	Classes   map[string]*ClassNode `json:"classes"`
 	Relations []*ClassRelation      `json:"relations"`
 	ClassDefs map[string]*ClassDef  `json:"classDefs"`
+	Notes     []*ClassNote          `json:"notes,omitempty"`
 	Direction string                `json:"direction,omitempty"`
 	Title     *string               `json:"title,omitempty"`
 	Config    map[string]any        `json:"config,omitempty"`
@@ -36,6 +37,7 @@ const (
 type ClassMember struct {
 	Name       string           `json:"name"`
 	Type       string           `json:"type"`
+	Generics   []*Generic       `json:"generics,omitempty"`
 	Visibility MemberVisibility `json:"visibility"`
 	Classifier *string          `json:"classifier,omitempty"` // static, abstract
 }
@@ -43,6 +45,7 @@ type ClassMember struct {
 type ClassMethod struct {
 	Name       string           `json:"name"`
 	Type       string           `json:"type"`
+	Generics   []*Generic       `json:"generics,omitempty"`
 	Parameters []string         `json:"parameters,omitempty"`
 	Visibility MemberVisibility `json:"visibility"`
 	Classifier *string          `json:"classifier,omitempty"` // static, abstract
@@ -81,6 +84,30 @@ type ClassCardinality struct {
 	To   string `json:"to,omitempty"`
 }
 
+// ClassNote represents a note annotation
+type ClassNote struct {
+	ID       string    `json:"id,omitempty"`
+	ForClass *string   `json:"forClass,omitempty"`
+	Text     string    `json:"text"`
+	Position NotePlace `json:"position,omitempty"`
+}
+
+// Generic represents a generic type parameter
+type Generic struct {
+	Name       string     `json:"name"`
+	Constraint *string    `json:"constraint,omitempty"`
+	Arguments  []*Generic `json:"arguments,omitempty"`
+}
+
+// ClickEvent represents a click interaction
+type ClickEvent struct {
+	NodeID   string  `json:"nodeId"`
+	Callback *string `json:"callback,omitempty"`
+	Link     *string `json:"link,omitempty"`
+	Target   *string `json:"target,omitempty"`
+	Tooltip  *string `json:"tooltip,omitempty"`
+}
+
 type ClassDef struct {
 	ID     string   `json:"id"`
 	Styles []string `json:"styles"`
@@ -111,6 +138,7 @@ func NewClassDiagram() *ClassDiagram {
 		Classes:   make(map[string]*ClassNode),
 		Relations: make([]*ClassRelation, 0),
 		ClassDefs: make(map[string]*ClassDef),
+		Notes:     make([]*ClassNote, 0),
 		Config:    make(map[string]any),
 	}
 }

+ 20 - 0
pkg/ast/flowchart.go

@@ -47,6 +47,7 @@ const (
 	VertexTypeInvTrapezoid FlowVertexTypeParam = "inv_trapezoid"
 	VertexTypeLeanRight    FlowVertexTypeParam = "lean_right"
 	VertexTypeLeanLeft     FlowVertexTypeParam = "lean_left"
+	VertexTypeFlag         FlowVertexTypeParam = "flag"
 )
 
 // FlowVertex represents a node in a flowchart
@@ -73,6 +74,8 @@ type FlowVertex struct {
 	DefaultWidth     *int                 `json:"defaultWidth,omitempty"`
 	ImageAspectRatio *float64             `json:"imageAspectRatio,omitempty"`
 	Constraint       *string              `json:"constraint,omitempty"` // "on" or "off"
+	Tooltip          *string              `json:"tooltip,omitempty"`
+	OnClick          *ClickEvent          `json:"onClick,omitempty"`
 }
 
 // FlowText represents text content in diagrams
@@ -108,6 +111,8 @@ type FlowEdge struct {
 	Classes         []string        `json:"classes"`
 	Animation       *string         `json:"animation,omitempty"` // "fast" or "slow"
 	Animate         *bool           `json:"animate,omitempty"`
+	Pattern         *string         `json:"pattern,omitempty"`
+	Tooltip         *string         `json:"tooltip,omitempty"`
 }
 
 // FlowClass represents CSS class definitions
@@ -181,6 +186,21 @@ func NewValidationError(message string) *ValidationError {
 	return &ValidationError{Message: message}
 }
 
+// EdgeAnimation represents animation properties for edges
+type EdgeAnimation struct {
+	Duration string `json:"duration,omitempty"`
+	Delay    string `json:"delay,omitempty"`
+	Easing   string `json:"easing,omitempty"`
+}
+
+// MarkdownText represents markdown-formatted text
+type MarkdownText struct {
+	Content string `json:"content"`
+	Bold    bool   `json:"bold,omitempty"`
+	Italic  bool   `json:"italic,omitempty"`
+	Strike  bool   `json:"strike,omitempty"`
+}
+
 // NewFlowchart creates a new flowchart with default values
 func NewFlowchart() *Flowchart {
 	return &Flowchart{

+ 30 - 0
pkg/lexer/lexer.go

@@ -85,6 +85,13 @@ const (
 	TokenEquals      // =
 	TokenDot         // .
 	TokenExclamation // !
+	TokenSlash       // /
+	TokenBackslash   // \
+	TokenHash        // #
+	TokenAt          // @
+	TokenPercent     // %
+	TokenTilde       // ~
+	TokenQuestion    // ?
 
 	// Error token
 	TokenError
@@ -158,9 +165,18 @@ var tokenTypeNames = map[TokenType]string{
 	TokenComma:            "COMMA",
 	TokenAmpersand:        "AMPERSAND",
 	TokenMult:             "MULT",
+	TokenPlus:             "PLUS",
 	TokenMinus:            "MINUS",
 	TokenEquals:           "EQUALS",
 	TokenDot:              "DOT",
+	TokenExclamation:      "EXCLAMATION",
+	TokenSlash:            "SLASH",
+	TokenBackslash:        "BACKSLASH",
+	TokenHash:             "HASH",
+	TokenAt:               "AT",
+	TokenPercent:          "PERCENT",
+	TokenTilde:            "TILDE",
+	TokenQuestion:         "QUESTION",
 	TokenError:            "ERROR",
 }
 
@@ -507,6 +523,20 @@ func (l *Lexer) consumeSingleChar() error {
 		tokenType = TokenDot
 	case '!':
 		tokenType = TokenExclamation
+	case '/':
+		tokenType = TokenSlash
+	case '\\':
+		tokenType = TokenBackslash
+	case '#':
+		tokenType = TokenHash
+	case '@':
+		tokenType = TokenAt
+	case '%':
+		tokenType = TokenPercent
+	case '~':
+		tokenType = TokenTilde
+	case '?':
+		tokenType = TokenQuestion
 	default:
 		return fmt.Errorf("unexpected character '%c' at line %d, column %d", ch, l.line, l.column)
 	}

+ 251 - 45
pkg/parser/class.go

@@ -74,6 +74,8 @@ func (p *ClassParser) parseStatement() error {
 	case p.check(lexer.TokenNewline):
 		p.advance() // Skip newlines
 		return nil
+	case p.checkComment():
+		return p.parseComment()
 	case p.check(lexer.TokenClass) || p.checkKeyword("class"):
 		return p.parseClass()
 	case p.checkKeyword("direction"):
@@ -161,55 +163,62 @@ func (p *ClassParser) parseClassBody(class *ast.ClassNode) error {
 			p.advance()
 		}
 
+		// Check for annotations like <<interface>>, <<abstract>>
+		if p.check(lexer.TokenOpenAngle) {
+			// Look ahead to see if it's << (double angle)
+			if p.checkNext(lexer.TokenOpenAngle) {
+				p.advance() // consume first <
+				p.advance() // consume second <
+
+				annotation := ""
+				for !p.check(lexer.TokenCloseAngle) && !p.isAtEnd() {
+					if p.check(lexer.TokenID) {
+						annotation += p.advance().Value
+					} else {
+						annotation += p.advance().Value
+					}
+				}
+
+				// Consume closing >>
+				if p.check(lexer.TokenCloseAngle) {
+					p.advance() // consume first >
+					if p.check(lexer.TokenCloseAngle) {
+						p.advance() // consume second >
+						class.Annotations = append(class.Annotations, annotation)
+
+						// Update class type based on annotation
+						switch strings.ToLower(annotation) {
+						case "interface":
+							class.Type = ast.ClassTypeInterface
+						case "abstract":
+							class.Type = ast.ClassTypeAbstract
+						case "enumeration", "enum":
+							class.Type = ast.ClassTypeEnum
+						}
+					}
+				}
+				continue
+			}
+		}
+
 		if !p.check(lexer.TokenID) {
 			return p.error("expected member or method name")
 		}
 
 		name := p.advance().Value
-		var memberType string
-
-		// Check for type annotation
-		if p.check(lexer.TokenColon) {
-			p.advance() // consume ':'
-			if p.check(lexer.TokenID) {
-				memberType = p.advance().Value
-			}
-		}
 
 		// Check if it's a method (has parentheses)
 		if p.check(lexer.TokenOpenParen) {
-			p.advance() // consume '('
-
-			method := &ast.ClassMethod{
-				Name:       name,
-				Type:       memberType,
-				Parameters: make([]string, 0),
-				Visibility: visibility,
-			}
-
-			// Parse parameters
-			for !p.check(lexer.TokenCloseParen) && !p.isAtEnd() {
-				if p.check(lexer.TokenID) {
-					param := p.advance().Value
-					method.Parameters = append(method.Parameters, param)
-				}
-				if p.check(lexer.TokenComma) {
-					p.advance()
-				}
-			}
-
-			if !p.check(lexer.TokenCloseParen) {
-				return p.error("expected ')'")
+			method, err := p.parseMethod(name, visibility)
+			if err != nil {
+				return err
 			}
-			p.advance() // consume ')'
-
 			class.Methods = append(class.Methods, method)
 		} else {
 			// It's a member
-			member := &ast.ClassMember{
-				Name:       name,
-				Type:       memberType,
-				Visibility: visibility,
+			member, err := p.parseMember(name, visibility)
+			if err != nil {
+				return err
 			}
 			class.Members = append(class.Members, member)
 		}
@@ -218,6 +227,70 @@ func (p *ClassParser) parseClassBody(class *ast.ClassNode) error {
 	return nil
 }
 
+// parseMethod parses a method with parameters and return type
+func (p *ClassParser) parseMethod(name string, visibility ast.MemberVisibility) (*ast.ClassMethod, error) {
+	method := &ast.ClassMethod{
+		Name:       name,
+		Parameters: make([]string, 0),
+		Visibility: visibility,
+	}
+
+	// Parse opening parenthesis
+	if !p.check(lexer.TokenOpenParen) {
+		return nil, p.error("expected '('")
+	}
+	p.advance() // consume '('
+
+	// Parse parameters
+	for !p.check(lexer.TokenCloseParen) && !p.isAtEnd() {
+		if p.check(lexer.TokenID) {
+			param := p.advance().Value
+
+			// Check for parameter type
+			if p.check(lexer.TokenID) {
+				paramType := p.advance().Value
+				method.Parameters = append(method.Parameters, param+" "+paramType)
+			} else {
+				method.Parameters = append(method.Parameters, param)
+			}
+		}
+
+		// Skip commas
+		if p.check(lexer.TokenComma) {
+			p.advance()
+		}
+	}
+
+	if !p.check(lexer.TokenCloseParen) {
+		return nil, p.error("expected ')'")
+	}
+	p.advance() // consume ')'
+
+	// Check for return type
+	if p.check(lexer.TokenID) {
+		returnType := p.advance().Value
+		method.Type = returnType
+	}
+
+	return method, nil
+}
+
+// parseMember parses a class member/field
+func (p *ClassParser) parseMember(name string, visibility ast.MemberVisibility) (*ast.ClassMember, error) {
+	member := &ast.ClassMember{
+		Name:       name,
+		Visibility: visibility,
+	}
+
+	// Check for type annotation
+	if p.check(lexer.TokenID) {
+		memberType := p.advance().Value
+		member.Type = memberType
+	}
+
+	return member, nil
+}
+
 // parseClassOrRelation parses either a class definition or relationship
 func (p *ClassParser) parseClassOrRelation() error {
 	className := p.advance().Value
@@ -350,11 +423,17 @@ func (p *ClassParser) parseRelationType() ast.ClassRelationType {
 func (p *ClassParser) parseDirection() error {
 	p.advance() // consume 'direction'
 
-	if !p.check(lexer.TokenID) {
-		return p.error("expected direction value")
+	// Check for direction tokens or ID
+	var direction string
+	if p.check(lexer.TokenTD) || p.check(lexer.TokenTB) || p.check(lexer.TokenBT) ||
+		p.check(lexer.TokenRL) || p.check(lexer.TokenLR) {
+		direction = p.advance().Value
+	} else if p.check(lexer.TokenID) {
+		direction = p.advance().Value
+	} else {
+		return p.error("expected direction value (TD, TB, BT, RL, LR)")
 	}
 
-	direction := p.advance().Value
 	p.diagram.Direction = direction
 	return nil
 }
@@ -368,10 +447,6 @@ func (p *ClassParser) parseClick() error {
 	return p.skipToNextStatement()
 }
 
-func (p *ClassParser) parseNote() error {
-	return p.skipToNextStatement()
-}
-
 func (p *ClassParser) parseClassDef() error {
 	return p.skipToNextStatement()
 }
@@ -434,7 +509,9 @@ func (p *ClassParser) checkVisibility() bool {
 		return false
 	}
 	token := p.peek()
-	return token.Value == "+" || token.Value == "-" || token.Value == "#" || token.Value == "~"
+	return (token.Value == "+" || token.Value == "-" || token.Value == "#" || token.Value == "~") ||
+		(token.Type == lexer.TokenPlus || token.Type == lexer.TokenMinus ||
+		 token.Type == lexer.TokenHash || token.Type == lexer.TokenTilde)
 }
 
 func (p *ClassParser) checkRelation() bool {
@@ -485,3 +562,132 @@ func (p *ClassParser) skipToNextStatement() error {
 	}
 	return nil
 }
+
+// checkComment checks if current token sequence is a comment (%%)
+func (p *ClassParser) checkComment() bool {
+	return p.check(lexer.TokenPercent) && p.checkNext(lexer.TokenPercent)
+}
+
+// parseComment parses comment statements
+func (p *ClassParser) parseComment() error {
+	p.advance() // consume first %
+	p.advance() // consume second %
+
+	// Skip everything until newline
+	for !p.check(lexer.TokenNewline) && !p.isAtEnd() {
+		p.advance()
+	}
+	if p.check(lexer.TokenNewline) {
+		p.advance()
+	}
+	return nil
+}
+
+// Enhanced parseNote to support class-specific notes
+func (p *ClassParser) parseNote() error {
+	p.advance() // consume 'note'
+
+	var note *ast.ClassNote
+
+	// Check if it's "note for ClassName"
+	if p.checkKeyword("for") {
+		p.advance() // consume 'for'
+		if !p.check(lexer.TokenID) {
+			return p.error("expected class name after 'note for'")
+		}
+		className := p.advance().Value
+
+		// Parse note text
+		text := ""
+		for !p.check(lexer.TokenNewline) && !p.isAtEnd() {
+			token := p.advance()
+			if token.Type == lexer.TokenString {
+				// Remove quotes from string tokens
+				val := token.Value
+				text += val[1 : len(val)-1] // Remove surrounding quotes
+			} else {
+				text += token.Value + " "
+			}
+		}
+
+		note = &ast.ClassNote{
+			ForClass: &className,
+			Text:     strings.TrimSpace(text),
+		}
+	} else {
+		// General note for the whole diagram
+		text := ""
+		for !p.check(lexer.TokenNewline) && !p.isAtEnd() {
+			token := p.advance()
+			if token.Type == lexer.TokenString {
+				// Remove quotes from string tokens
+				val := token.Value
+				text += val[1 : len(val)-1] // Remove surrounding quotes
+			} else {
+				text += token.Value + " "
+			}
+		}
+
+		note = &ast.ClassNote{
+			Text: strings.TrimSpace(text),
+		}
+	}
+
+	p.diagram.Notes = append(p.diagram.Notes, note)
+	return nil
+}
+
+// parseGeneric parses generic type parameters with ~Type~
+func (p *ClassParser) parseGeneric() (*ast.Generic, error) {
+	if !p.check(lexer.TokenTilde) {
+		return nil, p.error("expected '~' for generic type")
+	}
+	p.advance() // consume ~
+
+	if !p.check(lexer.TokenID) {
+		return nil, p.error("expected generic type name")
+	}
+
+	typeName := p.advance().Value
+	generic := &ast.Generic{
+		Name:      typeName,
+		Arguments: make([]*ast.Generic, 0),
+	}
+
+	// Check for nested generics like List<String>
+	if p.check(lexer.TokenOpenAngle) {
+		p.advance() // consume <
+
+		for !p.check(lexer.TokenCloseAngle) && !p.isAtEnd() {
+			if p.check(lexer.TokenTilde) {
+				nestedGeneric, err := p.parseGeneric()
+				if err != nil {
+					return nil, err
+				}
+				generic.Arguments = append(generic.Arguments, nestedGeneric)
+			} else if p.check(lexer.TokenID) {
+				// Simple type argument
+				argType := p.advance().Value
+				generic.Arguments = append(generic.Arguments, &ast.Generic{
+					Name: argType,
+				})
+			}
+
+			if p.check(lexer.TokenComma) {
+				p.advance() // consume comma
+			}
+		}
+
+		if !p.check(lexer.TokenCloseAngle) {
+			return nil, p.error("expected '>' to close generic")
+		}
+		p.advance() // consume >
+	}
+
+	if !p.check(lexer.TokenTilde) {
+		return nil, p.error("expected closing '~' for generic type")
+	}
+	p.advance() // consume closing ~
+
+	return generic, nil
+}

+ 290 - 30
pkg/parser/flowchart.go

@@ -4,8 +4,7 @@ package parser
 
 import (
 	"fmt"
-	_ "strconv"
-	_ "strings"
+	"strings"
 
 	"mermaid-go/pkg/ast"
 	"mermaid-go/pkg/lexer"
@@ -157,15 +156,93 @@ func (p *Parser) parseSubgraphStatement() error {
 	}
 	p.advance()
 
-	// TODO: Implement subgraph parsing based on flow.jison
-	// For now, skip to end
+	// Parse subgraph ID (optional)
+	var subgraphID string
+	var title string
+
+	if p.check(lexer.TokenID) {
+		subgraphID = p.advance().Value
+	} else if p.check(lexer.TokenString) {
+		// Quoted title becomes both ID and title
+		titleToken := p.advance().Value
+		title = titleToken[1 : len(titleToken)-1] // Remove quotes
+		subgraphID = title
+	}
+
+	// Check for explicit title in square brackets
+	if p.check(lexer.TokenOpenBracket) {
+		p.advance() // consume [
+		titleParts := make([]string, 0)
+		for !p.check(lexer.TokenCloseBracket) && !p.isAtEnd() {
+			titleParts = append(titleParts, p.advance().Value)
+		}
+		if p.check(lexer.TokenCloseBracket) {
+			p.advance() // consume ]
+			title = strings.Join(titleParts, "")
+		}
+	}
+
+	// Create subgraph
+	subgraph := &ast.FlowSubGraph{
+		ID:        subgraphID,
+		Title:     title,
+		LabelType: "text",
+		Classes:   make([]string, 0),
+		Nodes:     make([]string, 0),
+	}
+
+	// Store current parsing state
+	oldCurrent := p.current
+
+	// Parse subgraph content until 'end'
 	for !p.check(lexer.TokenEnd) && !p.isAtEnd() {
-		p.advance()
+		if p.check(lexer.TokenNewline) {
+			p.advance()
+			continue
+		}
+
+		// Save state before parsing statement
+		beforeStatement := p.current
+
+		// Try to parse as edge statement (this will add vertices and edges)
+		err := p.parseEdgeStatement()
+		if err != nil {
+			// If edge parsing failed, skip to next statement
+			p.current = beforeStatement
+			p.skipToNextStatement()
+			continue
+		}
+
+		// Collect all vertices referenced in the statements parsed within this subgraph
+		for i := oldCurrent; i < p.current; i++ {
+			token := p.tokens[i]
+			if token.Type == lexer.TokenID {
+				// Check if this ID is a vertex
+				if vertex, exists := p.flowDB.vertices[token.Value]; exists {
+					// Add to subgraph nodes if not already present
+					found := false
+					for _, nodeID := range subgraph.Nodes {
+						if nodeID == vertex.ID {
+							found = true
+							break
+						}
+					}
+					if !found {
+						subgraph.Nodes = append(subgraph.Nodes, vertex.ID)
+					}
+				}
+			}
+		}
 	}
+
 	if p.check(lexer.TokenEnd) {
 		p.advance()
 	}
 
+	// Add subgraph to flowDB
+	p.flowDB.subGraphs = append(p.flowDB.subGraphs, subgraph)
+	p.flowDB.subGraphLookup[subgraphID] = subgraph
+
 	return nil
 }
 
@@ -176,8 +253,38 @@ func (p *Parser) parseClassStatement() error {
 	}
 	p.advance()
 
-	// Skip implementation for now
-	return p.skipToNextStatement()
+	// Parse node list (comma separated)
+	nodeIDs := make([]string, 0)
+	for {
+		if !p.check(lexer.TokenID) {
+			break
+		}
+		nodeIDs = append(nodeIDs, p.advance().Value)
+
+		if p.check(lexer.TokenComma) {
+			p.advance() // consume comma
+		} else {
+			break
+		}
+	}
+
+	// Parse class name
+	if !p.check(lexer.TokenID) {
+		return p.error("expected class name")
+	}
+	className := p.advance().Value
+
+	// Apply class to nodes
+	for _, nodeID := range nodeIDs {
+		// Ensure vertex exists
+		if _, exists := p.flowDB.vertices[nodeID]; !exists {
+			p.addVertex(nodeID, "", "")
+		}
+		vertex := p.flowDB.vertices[nodeID]
+		vertex.Classes = append(vertex.Classes, className)
+	}
+
+	return nil
 }
 
 // parseClassDefStatement handles class definitions
@@ -187,8 +294,29 @@ func (p *Parser) parseClassDefStatement() error {
 	}
 	p.advance()
 
-	// Skip implementation for now
-	return p.skipToNextStatement()
+	// Parse class name
+	if !p.check(lexer.TokenID) {
+		return p.error("expected class name")
+	}
+	className := p.advance().Value
+
+	// Parse style definitions (everything until newline)
+	styles := make([]string, 0)
+	for !p.check(lexer.TokenNewline) && !p.isAtEnd() {
+		token := p.advance()
+		styles = append(styles, token.Value)
+	}
+
+	// Create class definition
+	class := &ast.FlowClass{
+		ID:         className,
+		Styles:     styles,
+		TextStyles: make([]string, 0),
+	}
+
+	p.flowDB.classes[className] = class
+
+	return nil
 }
 
 // parseStyleStatement handles style definitions
@@ -198,8 +326,29 @@ func (p *Parser) parseStyleStatement() error {
 	}
 	p.advance()
 
-	// Skip implementation for now
-	return p.skipToNextStatement()
+	// Parse node ID
+	if !p.check(lexer.TokenID) {
+		return p.error("expected node ID")
+	}
+	nodeID := p.advance().Value
+
+	// Parse style definitions (everything until newline)
+	styles := make([]string, 0)
+	for !p.check(lexer.TokenNewline) && !p.isAtEnd() {
+		token := p.advance()
+		styles = append(styles, token.Value)
+	}
+
+	// Ensure vertex exists
+	if _, exists := p.flowDB.vertices[nodeID]; !exists {
+		p.addVertex(nodeID, "", "")
+	}
+
+	// Apply styles to vertex
+	vertex := p.flowDB.vertices[nodeID]
+	vertex.Styles = append(vertex.Styles, styles...)
+
+	return nil
 }
 
 // parseLinkStyleStatement handles link style definitions
@@ -218,10 +367,52 @@ func (p *Parser) parseClickStatement() error {
 	if !p.check(lexer.TokenClick) {
 		return p.error("expected 'click'")
 	}
-	p.advance()
+	p.advance() // consume 'click'
 
-	// Skip implementation for now
-	return p.skipToNextStatement()
+	// Parse node ID
+	if !p.check(lexer.TokenID) {
+		return p.error("expected node ID after 'click'")
+	}
+	nodeID := p.advance().Value
+
+	// Parse click action (callback or href)
+	clickEvent := &ast.ClickEvent{
+		NodeID: nodeID,
+	}
+
+	if p.check(lexer.TokenID) || p.check(lexer.TokenString) {
+		action := p.advance().Value
+
+		// Remove quotes if it's a string
+		if strings.HasPrefix(action, "\"") && strings.HasSuffix(action, "\"") {
+			action = action[1 : len(action)-1]
+		}
+
+		// Check if it's a callback (function call) or URL
+		if strings.Contains(action, "http") || strings.Contains(action, "www.") {
+			clickEvent.Link = &action
+		} else {
+			clickEvent.Callback = &action
+		}
+	}
+
+	// Parse optional target for links
+	if p.check(lexer.TokenString) {
+		target := p.advance().Value
+		target = target[1 : len(target)-1] // Remove quotes
+		clickEvent.Target = &target
+	}
+
+	// Apply click event to vertex
+	if vertex, exists := p.flowDB.vertices[nodeID]; exists {
+		vertex.OnClick = clickEvent
+	} else {
+		// Ensure vertex exists
+		p.addVertex(nodeID, "", "")
+		p.flowDB.vertices[nodeID].OnClick = clickEvent
+	}
+
+	return nil
 }
 
 // parseEdgeStatement parses edge definitions
@@ -298,8 +489,22 @@ func (p *Parser) parseShape() (ast.FlowVertexTypeParam, string, error) {
 
 	switch startToken.Type {
 	case lexer.TokenOpenBracket:
-		shape = ast.VertexTypeRect
-		endToken = lexer.TokenCloseBracket
+		// Check for special bracket shapes
+		if p.checkSequence([]lexer.TokenType{lexer.TokenSlash}) {
+			shape = ast.VertexTypeLeanRight
+			p.advance() // consume [
+			p.advance() // consume /
+			endToken = lexer.TokenSlash
+		} else if p.checkSequence([]lexer.TokenType{lexer.TokenBackslash}) {
+			shape = ast.VertexTypeLeanLeft
+			p.advance() // consume [
+			p.advance() // consume \
+			endToken = lexer.TokenBackslash
+		} else {
+			shape = ast.VertexTypeRect
+			endToken = lexer.TokenCloseBracket
+			p.advance() // consume [
+		}
 	case lexer.TokenOpenParen:
 		if p.checkNext(lexer.TokenOpenParen) { // ((text))
 			shape = ast.VertexTypeCircle
@@ -308,25 +513,36 @@ func (p *Parser) parseShape() (ast.FlowVertexTypeParam, string, error) {
 			endToken = lexer.TokenCloseParen
 		} else { // (text)
 			shape = ast.VertexTypeRound
+			p.advance() // consume (
 			endToken = lexer.TokenCloseParen
 		}
 	case lexer.TokenOpenBrace:
 		shape = ast.VertexTypeDiamond
+		p.advance() // consume {
 		endToken = lexer.TokenCloseBrace
 	case lexer.TokenOpenDoubleParen:
 		shape = ast.VertexTypeCircle
+		p.advance() // consume ((
 		endToken = lexer.TokenCloseDoubleParen
+	case lexer.TokenCloseAngle:
+		// Check for flag shape >text]
+		shape = ast.VertexTypeFlag
+		p.advance() // consume >
+		endToken = lexer.TokenCloseBracket
 	default:
 		return "", "", p.error("expected shape delimiter")
 	}
 
-	if shape != ast.VertexTypeCircle || startToken.Type != lexer.TokenOpenDoubleParen {
-		p.advance() // consume opening delimiter
-	}
-
 	// Parse text content
 	text := ""
 	for !p.check(endToken) && !p.isAtEnd() {
+		if endToken == lexer.TokenSlash && p.check(lexer.TokenSlash) {
+			break
+		}
+		if endToken == lexer.TokenBackslash && p.check(lexer.TokenBackslash) {
+			break
+		}
+
 		if p.check(lexer.TokenString) {
 			// Remove quotes from string
 			val := p.advance().Value
@@ -336,17 +552,47 @@ func (p *Parser) parseShape() (ast.FlowVertexTypeParam, string, error) {
 		}
 	}
 
-	if !p.check(endToken) {
-		return "", "", p.error(fmt.Sprintf("expected closing delimiter"))
-	}
-	p.advance() // consume closing delimiter
+	// Consume closing delimiter(s)
+	switch endToken {
+	case lexer.TokenSlash:
+		if !p.check(lexer.TokenSlash) {
+			return "", "", p.error("expected closing /")
+		}
+		p.advance() // consume /
+		if !p.check(lexer.TokenCloseBracket) {
+			return "", "", p.error("expected closing ]")
+		}
+		p.advance() // consume ]
 
-	// Handle double paren closing
-	if shape == ast.VertexTypeCircle && endToken == lexer.TokenCloseParen {
-		if !p.check(lexer.TokenCloseParen) {
-			return "", "", p.error("expected second closing parenthesis")
+	case lexer.TokenBackslash:
+		if !p.check(lexer.TokenBackslash) {
+			return "", "", p.error("expected closing \\")
 		}
-		p.advance()
+		p.advance() // consume \
+		if !p.check(lexer.TokenCloseBracket) {
+			return "", "", p.error("expected closing ]")
+		}
+		p.advance() // consume ]
+
+	case lexer.TokenCloseParen:
+		if !p.check(endToken) {
+			return "", "", p.error("expected closing delimiter")
+		}
+		p.advance() // consume closing delimiter
+
+		// Handle double paren closing
+		if shape == ast.VertexTypeCircle && startToken.Type == lexer.TokenOpenParen {
+			if !p.check(lexer.TokenCloseParen) {
+				return "", "", p.error("expected second closing parenthesis")
+			}
+			p.advance()
+		}
+
+	default:
+		if !p.check(endToken) {
+			return "", "", p.error("expected closing delimiter")
+		}
+		p.advance() // consume closing delimiter
 	}
 
 	return shape, text, nil
@@ -496,6 +742,19 @@ func (p *Parser) checkNext(tokenType lexer.TokenType) bool {
 	return p.tokens[p.current+1].Type == tokenType
 }
 
+// checkSequence checks if the current position plus offset matches a sequence of token types
+func (p *Parser) checkSequence(types []lexer.TokenType) bool {
+	for i, tokenType := range types {
+		if p.current+1+i >= len(p.tokens) {
+			return false
+		}
+		if p.tokens[p.current+1+i].Type != tokenType {
+			return false
+		}
+	}
+	return true
+}
+
 // checkDirection returns true if current token is a direction
 func (p *Parser) checkDirection() bool {
 	if p.isAtEnd() {
@@ -514,7 +773,8 @@ func (p *Parser) checkShapeStart() bool {
 	}
 	tokenType := p.peek().Type
 	return tokenType == lexer.TokenOpenBracket || tokenType == lexer.TokenOpenParen ||
-		tokenType == lexer.TokenOpenBrace || tokenType == lexer.TokenOpenDoubleParen
+		tokenType == lexer.TokenOpenBrace || tokenType == lexer.TokenOpenDoubleParen ||
+		tokenType == lexer.TokenCloseAngle // for flag shape >text]
 }
 
 // checkArrow returns true if current token is an arrow

+ 59 - 18
pkg/renderer/class.go

@@ -33,23 +33,35 @@ func (r *ClassRenderer) Render(diagram *ast.ClassDiagram) (string, error) {
 		builder.WriteString(fmt.Sprintf("    direction %s\n", diagram.Direction))
 	}
 
-	// Render classes
+		// Render classes
 	for _, class := range diagram.Classes {
 		builder.WriteString("    class ")
 		builder.WriteString(class.ID)
 
 		// Render class body if it has members or methods
-		if len(class.Members) > 0 || len(class.Methods) > 0 {
+		if len(class.Members) > 0 || len(class.Methods) > 0 || len(class.Annotations) > 0 {
 			builder.WriteString(" {\n")
 
+			// Render annotations first
+			for _, annotation := range class.Annotations {
+				builder.WriteString("        <<")
+				builder.WriteString(annotation)
+				builder.WriteString(">>\n")
+			}
+
 			// Render members
 			for _, member := range class.Members {
 				builder.WriteString("        ")
 				builder.WriteString(string(member.Visibility))
 				builder.WriteString(member.Name)
 				if member.Type != "" {
-					builder.WriteString(" : ")
-					builder.WriteString(member.Type)
+					builder.WriteString(" ")
+					// Handle generics in member type
+					if len(member.Generics) > 0 {
+						builder.WriteString(r.renderGenericType(member.Type, member.Generics))
+					} else {
+						builder.WriteString(member.Type)
+					}
 				}
 				if member.Classifier != nil {
 					builder.WriteString(" ")
@@ -74,8 +86,13 @@ func (r *ClassRenderer) Render(diagram *ast.ClassDiagram) (string, error) {
 
 				builder.WriteString(")")
 				if method.Type != "" {
-					builder.WriteString(" : ")
-					builder.WriteString(method.Type)
+					builder.WriteString(" ")
+					// Handle generics in return type
+					if len(method.Generics) > 0 {
+						builder.WriteString(r.renderGenericType(method.Type, method.Generics))
+					} else {
+						builder.WriteString(method.Type)
+					}
 				}
 				if method.Classifier != nil {
 					builder.WriteString(" ")
@@ -84,19 +101,9 @@ func (r *ClassRenderer) Render(diagram *ast.ClassDiagram) (string, error) {
 				builder.WriteString("\n")
 			}
 
-			builder.WriteString("    }\n")
-		} else {
-			builder.WriteString("\n")
-		}
-
-		// Render annotations
-		for _, annotation := range class.Annotations {
-			builder.WriteString("    ")
-			builder.WriteString(class.ID)
-			builder.WriteString(" : ")
-			builder.WriteString(annotation)
-			builder.WriteString("\n")
+			builder.WriteString("    }")
 		}
+		builder.WriteString("\n")
 
 		// Render links
 		if class.Link != nil {
@@ -185,5 +192,39 @@ func (r *ClassRenderer) Render(diagram *ast.ClassDiagram) (string, error) {
 		builder.WriteString("\n")
 	}
 
+	// Render notes
+	for _, note := range diagram.Notes {
+		if note.ForClass != nil {
+			builder.WriteString(fmt.Sprintf("    note for %s \"%s\"\n", *note.ForClass, note.Text))
+		} else {
+			builder.WriteString(fmt.Sprintf("    note \"%s\"\n", note.Text))
+		}
+	}
+
 	return builder.String(), nil
 }
+
+// renderGenericType renders a type with its generic parameters
+func (r *ClassRenderer) renderGenericType(baseType string, generics []*ast.Generic) string {
+	if len(generics) == 0 {
+		return baseType
+	}
+
+	result := "~" + baseType
+	if len(generics) > 0 {
+		result += "<"
+		for i, generic := range generics {
+			if i > 0 {
+				result += ", "
+			}
+			if len(generic.Arguments) > 0 {
+				result += r.renderGenericType(generic.Name, generic.Arguments)
+			} else {
+				result += generic.Name
+			}
+		}
+		result += ">"
+	}
+	result += "~"
+	return result
+}

+ 55 - 1
pkg/renderer/flowchart.go

@@ -71,6 +71,54 @@ func (r *FlowchartRenderer) Render(flowchart *ast.Flowchart) (string, error) {
 		r.renderClassDef(&builder, class)
 	}
 
+	// Render class assignments
+	classAssignments := make(map[string][]string)
+	for _, vertex := range flowchart.Vertices {
+		for _, className := range vertex.Classes {
+			if classAssignments[className] == nil {
+				classAssignments[className] = make([]string, 0)
+			}
+			classAssignments[className] = append(classAssignments[className], vertex.ID)
+		}
+	}
+
+	for className, nodeIDs := range classAssignments {
+		if len(nodeIDs) > 0 {
+			builder.WriteString(fmt.Sprintf("    class %s %s\n", strings.Join(nodeIDs, ","), className))
+		}
+	}
+
+	// Render individual node styles
+	for _, vertex := range flowchart.Vertices {
+		if len(vertex.Styles) > 0 {
+			styles := strings.Join(vertex.Styles, " ")
+			builder.WriteString(fmt.Sprintf("    style %s %s\n", vertex.ID, styles))
+		}
+	}
+
+	// Render click events
+	for _, vertex := range flowchart.Vertices {
+		if vertex.OnClick != nil {
+			if vertex.OnClick.Link != nil {
+				if vertex.OnClick.Target != nil {
+					builder.WriteString(fmt.Sprintf("    click %s \"%s\" \"%s\"\n",
+						vertex.ID, *vertex.OnClick.Link, *vertex.OnClick.Target))
+				} else {
+					builder.WriteString(fmt.Sprintf("    click %s \"%s\"\n",
+						vertex.ID, *vertex.OnClick.Link))
+				}
+			} else if vertex.OnClick.Callback != nil {
+				builder.WriteString(fmt.Sprintf("    click %s %s\n",
+					vertex.ID, *vertex.OnClick.Callback))
+			}
+		}
+	}
+
+	// Render tooltips
+	for nodeID, tooltip := range flowchart.Tooltips {
+		builder.WriteString(fmt.Sprintf("    %s --> tooltip[\"%s\"]\n", nodeID, tooltip))
+	}
+
 	return builder.String(), nil
 }
 
@@ -115,6 +163,12 @@ func (r *FlowchartRenderer) renderVertexWithShape(vertex *ast.FlowVertex) string
 		return fmt.Sprintf("%s((%s))", vertex.ID, text)
 	case ast.VertexTypeDiamond:
 		return fmt.Sprintf("%s{%s}", vertex.ID, text)
+	case ast.VertexTypeFlag:
+		return fmt.Sprintf("%s>%s]", vertex.ID, text)
+	case ast.VertexTypeLeanRight:
+		return fmt.Sprintf("%s[/%s/]", vertex.ID, text)
+	case ast.VertexTypeLeanLeft:
+		return fmt.Sprintf("%s[\\%s\\]", vertex.ID, text)
 	case ast.VertexTypeStadium:
 		return fmt.Sprintf("%s([%s])", vertex.ID, text)
 	case ast.VertexTypeCylinder:
@@ -258,7 +312,7 @@ func (r *FlowchartRenderer) renderSubGraph(builder *strings.Builder, subGraph *a
 // renderClassDef renders a class definition
 func (r *FlowchartRenderer) renderClassDef(builder *strings.Builder, class *ast.FlowClass) {
 	if len(class.Styles) > 0 {
-		styles := strings.Join(class.Styles, ",")
+		styles := strings.Join(class.Styles, " ")
 		builder.WriteString(fmt.Sprintf("    classDef %s %s\n", class.ID, styles))
 	}
 }

+ 35 - 0
simple_annotation.go

@@ -0,0 +1,35 @@
+package main
+
+import (
+	"fmt"
+	"log"
+	"mermaid-go/pkg/parser"
+	"mermaid-go/pkg/renderer"
+)
+
+func main() {
+	fmt.Println("Simple annotation test:")
+
+	input := `classDiagram
+    class Shape {
+        <<interface>>
+        +area() double
+    }`
+
+	mermaidParser := parser.NewMermaidParser()
+	diagram, err := mermaidParser.Parse(input)
+	if err != nil {
+		log.Printf("Failed to parse class diagram: %v", err)
+		return
+	}
+
+	mermaidRenderer := renderer.NewMermaidRenderer()
+	output, err := mermaidRenderer.Render(diagram)
+	if err != nil {
+		log.Printf("Failed to render class diagram: %v", err)
+		return
+	}
+
+	fmt.Printf("Input:\n%s\n", input)
+	fmt.Printf("Parsed and rendered:\n%s", output)
+}

+ 115 - 0
test_class.go

@@ -0,0 +1,115 @@
+package main
+
+import (
+	"fmt"
+	"log"
+	"mermaid-go/pkg/parser"
+	"mermaid-go/pkg/renderer"
+)
+
+func main() {
+	fmt.Println("Testing Enhanced Class Diagram Features:")
+
+	// Test 1: Interface types
+	testInterface()
+	fmt.Println()
+
+	// Test 2: Abstract classes
+	testAbstractClass()
+	fmt.Println()
+
+	// Test 3: Method parameters and return types
+	testMethodsAndParameters()
+	fmt.Println()
+}
+
+func testInterface() {
+	fmt.Println("--- Testing Interface ---")
+
+	input := `classDiagram
+    class Shape {
+        <<interface>>
+        +area() double
+        +perimeter() double
+    }
+
+    Shape <|.. Circle
+    Shape <|.. Rectangle`
+
+	mermaidParser := parser.NewMermaidParser()
+	diagram, err := mermaidParser.Parse(input)
+	if err != nil {
+		log.Printf("Failed to parse class diagram: %v", err)
+		return
+	}
+
+	mermaidRenderer := renderer.NewMermaidRenderer()
+	output, err := mermaidRenderer.Render(diagram)
+	if err != nil {
+		log.Printf("Failed to render class diagram: %v", err)
+		return
+	}
+
+	fmt.Printf("Input:\n%s\n", input)
+	fmt.Printf("Parsed and rendered:\n%s", output)
+}
+
+func testAbstractClass() {
+	fmt.Println("--- Testing Abstract Class ---")
+
+	input := `classDiagram
+    class Animal {
+        <<abstract>>
+        +name String
+        +age int
+        +makeSound()* void
+        +move() void
+    }`
+
+	mermaidParser := parser.NewMermaidParser()
+	diagram, err := mermaidParser.Parse(input)
+	if err != nil {
+		log.Printf("Failed to parse class diagram: %v", err)
+		return
+	}
+
+	mermaidRenderer := renderer.NewMermaidRenderer()
+	output, err := mermaidRenderer.Render(diagram)
+	if err != nil {
+		log.Printf("Failed to render class diagram: %v", err)
+		return
+	}
+
+	fmt.Printf("Input:\n%s\n", input)
+	fmt.Printf("Parsed and rendered:\n%s", output)
+}
+
+func testMethodsAndParameters() {
+	fmt.Println("--- Testing Methods with Parameters ---")
+
+	input := `classDiagram
+    class Calculator {
+        +add(a int, b int) int
+        +subtract(a int, b int) int
+        +multiply(a int, b int) int
+        +divide(a int, b int) float
+        -validateInput(value int) bool
+    }`
+
+	mermaidParser := parser.NewMermaidParser()
+	diagram, err := mermaidParser.Parse(input)
+	if err != nil {
+		log.Printf("Failed to parse class diagram: %v", err)
+		return
+	}
+
+	mermaidRenderer := renderer.NewMermaidRenderer()
+	output, err := mermaidRenderer.Render(diagram)
+	if err != nil {
+		log.Printf("Failed to render class diagram: %v", err)
+		return
+	}
+
+	fmt.Printf("Input:\n%s\n", input)
+	fmt.Printf("Parsed and rendered:\n%s", output)
+}

+ 116 - 0
test_class_advanced.go

@@ -0,0 +1,116 @@
+package main
+
+import (
+	"fmt"
+	"log"
+	"mermaid-go/pkg/parser"
+	"mermaid-go/pkg/renderer"
+)
+
+func main() {
+	fmt.Println("Testing Advanced Class Diagram Features:")
+
+	// Test 1: Comments and Notes
+	testCommentsAndNotes()
+	fmt.Println()
+
+	// Test 2: Enhanced Annotations
+	testEnhancedAnnotations()
+	fmt.Println()
+
+	// Test 3: Direction control
+	testDirection()
+	fmt.Println()
+}
+
+func testCommentsAndNotes() {
+	fmt.Println("--- Testing Comments and Notes ---")
+
+	input := `classDiagram
+    %% This is a comment
+    class Animal {
+        +name String
+        +age int
+    }
+
+    note "This is a general note"
+    note for Animal "This is a note for Animal class"`
+
+	mermaidParser := parser.NewMermaidParser()
+	diagram, err := mermaidParser.Parse(input)
+	if err != nil {
+		log.Printf("Failed to parse class diagram: %v", err)
+		return
+	}
+
+	mermaidRenderer := renderer.NewMermaidRenderer()
+	output, err := mermaidRenderer.Render(diagram)
+	if err != nil {
+		log.Printf("Failed to render class diagram: %v", err)
+		return
+	}
+
+	fmt.Printf("Input:\n%s\n", input)
+	fmt.Printf("Parsed and rendered:\n%s", output)
+}
+
+func testEnhancedAnnotations() {
+	fmt.Println("--- Testing Enhanced Annotations ---")
+
+	input := `classDiagram
+    class Shape {
+        <<interface>>
+        +area() double
+        +perimeter() double
+    }
+
+    class Animal {
+        <<abstract>>
+        +name String
+        +makeSound()* void
+    }`
+
+	mermaidParser := parser.NewMermaidParser()
+	diagram, err := mermaidParser.Parse(input)
+	if err != nil {
+		log.Printf("Failed to parse class diagram: %v", err)
+		return
+	}
+
+	mermaidRenderer := renderer.NewMermaidRenderer()
+	output, err := mermaidRenderer.Render(diagram)
+	if err != nil {
+		log.Printf("Failed to render class diagram: %v", err)
+		return
+	}
+
+	fmt.Printf("Input:\n%s\n", input)
+	fmt.Printf("Parsed and rendered:\n%s", output)
+}
+
+func testDirection() {
+	fmt.Println("--- Testing Direction Control ---")
+
+	input := `classDiagram
+    direction TB
+    class A
+    class B
+    A --> B`
+
+	mermaidParser := parser.NewMermaidParser()
+	diagram, err := mermaidParser.Parse(input)
+	if err != nil {
+		log.Printf("Failed to parse class diagram: %v", err)
+		return
+	}
+
+	mermaidRenderer := renderer.NewMermaidRenderer()
+	output, err := mermaidRenderer.Render(diagram)
+	if err != nil {
+		log.Printf("Failed to render class diagram: %v", err)
+		return
+	}
+
+	fmt.Printf("Input:\n%s\n", input)
+	fmt.Printf("Parsed and rendered:\n%s", output)
+}

+ 117 - 0
test_flowchart_advanced.go

@@ -0,0 +1,117 @@
+package main
+
+import (
+	"fmt"
+	"log"
+	"mermaid-go/pkg/parser"
+	"mermaid-go/pkg/renderer"
+)
+
+func main() {
+	fmt.Println("Testing Advanced Flowchart Features:")
+
+	// Test 1: Click events
+	testClickEvents()
+	fmt.Println()
+
+	// Test 2: Advanced subgraphs with styling
+	testAdvancedSubgraphs()
+	fmt.Println()
+
+	// Test 3: Complex node shapes
+	testComplexShapes()
+	fmt.Println()
+}
+
+func testClickEvents() {
+	fmt.Println("--- Testing Click Events ---")
+
+	input := `flowchart TD
+    A[Start] --> B[Process]
+    B --> C[End]
+
+    click A "https://example.com" "_blank"
+    click B callback
+    click C "https://mermaid.js.org"`
+
+	mermaidParser := parser.NewMermaidParser()
+	diagram, err := mermaidParser.Parse(input)
+	if err != nil {
+		log.Printf("Failed to parse flowchart: %v", err)
+		return
+	}
+
+	mermaidRenderer := renderer.NewMermaidRenderer()
+	output, err := mermaidRenderer.Render(diagram)
+	if err != nil {
+		log.Printf("Failed to render flowchart: %v", err)
+		return
+	}
+
+	fmt.Printf("Input:\n%s\n", input)
+	fmt.Printf("Parsed and rendered:\n%s", output)
+}
+
+func testAdvancedSubgraphs() {
+	fmt.Println("--- Testing Advanced Subgraphs ---")
+
+	input := `flowchart TB
+    subgraph TOP["Top Level"]
+        direction TB
+        A[Input] --> B[Process]
+    end
+
+    subgraph BOTTOM[Bottom Level]
+        direction LR
+        C[Review] --> D[Output]
+    end
+
+    TOP --> BOTTOM
+    classDef processClass fill:#f9f,stroke:#333,stroke-width:2px`
+
+	mermaidParser := parser.NewMermaidParser()
+	diagram, err := mermaidParser.Parse(input)
+	if err != nil {
+		log.Printf("Failed to parse flowchart: %v", err)
+		return
+	}
+
+	mermaidRenderer := renderer.NewMermaidRenderer()
+	output, err := mermaidRenderer.Render(diagram)
+	if err != nil {
+		log.Printf("Failed to render flowchart: %v", err)
+		return
+	}
+
+	fmt.Printf("Input:\n%s\n", input)
+	fmt.Printf("Parsed and rendered:\n%s", output)
+}
+
+func testComplexShapes() {
+	fmt.Println("--- Testing Complex Shapes ---")
+
+	input := `flowchart LR
+    A([Start]) --> B{Decision}
+    B --> C[/Process/]
+    B --> D[\Alternative\]
+    C --> E>Flag Shape]
+    D --> E
+    E --> F((End))`
+
+	mermaidParser := parser.NewMermaidParser()
+	diagram, err := mermaidParser.Parse(input)
+	if err != nil {
+		log.Printf("Failed to parse flowchart: %v", err)
+		return
+	}
+
+	mermaidRenderer := renderer.NewMermaidRenderer()
+	output, err := mermaidRenderer.Render(diagram)
+	if err != nil {
+		log.Printf("Failed to render flowchart: %v", err)
+		return
+	}
+
+	fmt.Printf("Input:\n%s\n", input)
+	fmt.Printf("Parsed and rendered:\n%s", output)
+}