organization.go 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250
  1. // Package parser provides Organization Chart parsing
  2. package parser
  3. import (
  4. "fmt"
  5. "strings"
  6. "mermaid-go/pkg/ast"
  7. "mermaid-go/pkg/lexer"
  8. )
  9. // OrganizationParser implements Organization Chart parsing
  10. type OrganizationParser struct {
  11. tokens []lexer.Token
  12. current int
  13. diagram *ast.OrganizationDiagram
  14. }
  15. // NewOrganizationParser creates a new Organization parser
  16. func NewOrganizationParser() *OrganizationParser {
  17. return &OrganizationParser{
  18. diagram: ast.NewOrganizationDiagram(),
  19. }
  20. }
  21. // Parse parses Organization Chart syntax
  22. func (p *OrganizationParser) Parse(input string) (*ast.OrganizationDiagram, error) {
  23. // Tokenize
  24. l := lexer.NewLexer(input)
  25. tokens, err := l.Tokenize()
  26. if err != nil {
  27. return nil, fmt.Errorf("lexical analysis failed: %w", err)
  28. }
  29. // Filter tokens
  30. p.tokens = lexer.FilterTokens(tokens)
  31. p.current = 0
  32. p.diagram = ast.NewOrganizationDiagram()
  33. // Parse document
  34. err = p.parseDocument()
  35. if err != nil {
  36. return nil, fmt.Errorf("syntax analysis failed: %w", err)
  37. }
  38. return p.diagram, nil
  39. }
  40. // parseDocument parses the Organization Chart document
  41. func (p *OrganizationParser) parseDocument() error {
  42. // Expect organization or orgChart
  43. if !p.check(lexer.TokenID) ||
  44. (p.peek().Value != "organization" && p.peek().Value != "orgChart") {
  45. return p.error("expected 'organization' or 'orgChart'")
  46. }
  47. p.advance()
  48. // Parse statements
  49. for !p.isAtEnd() {
  50. if err := p.parseStatement(); err != nil {
  51. return err
  52. }
  53. }
  54. // Build hierarchy after parsing all nodes
  55. p.buildHierarchy()
  56. return nil
  57. }
  58. // parseStatement parses individual Organization Chart statements
  59. func (p *OrganizationParser) parseStatement() error {
  60. if p.isAtEnd() {
  61. return nil
  62. }
  63. switch {
  64. case p.check(lexer.TokenNewline):
  65. p.advance() // Skip newlines
  66. return nil
  67. case p.checkKeyword("title"):
  68. return p.parseTitle()
  69. case p.check(lexer.TokenID):
  70. // Node definition
  71. return p.parseNode()
  72. default:
  73. token := p.peek()
  74. return p.error(fmt.Sprintf("unexpected token: %s", token.Value))
  75. }
  76. }
  77. // parseTitle parses title statements
  78. func (p *OrganizationParser) parseTitle() error {
  79. p.advance() // consume 'title'
  80. var titleParts []string
  81. for !p.check(lexer.TokenNewline) && !p.isAtEnd() {
  82. titleParts = append(titleParts, p.advance().Value)
  83. }
  84. if len(titleParts) > 0 {
  85. title := strings.TrimSpace(strings.Join(titleParts, " "))
  86. p.diagram.Title = &title
  87. }
  88. return nil
  89. }
  90. // parseNode parses node definitions
  91. func (p *OrganizationParser) parseNode() error {
  92. // Parse node ID
  93. nodeID := p.advance().Value
  94. node := &ast.OrganizationNode{
  95. ID: nodeID,
  96. Name: nodeID, // Default name is ID
  97. Level: 0, // Will be calculated later
  98. Children: make([]*ast.OrganizationNode, 0),
  99. CssClasses: make([]string, 0),
  100. Styles: make([]string, 0),
  101. }
  102. // Parse node properties
  103. for !p.check(lexer.TokenNewline) && !p.isAtEnd() {
  104. if p.check(lexer.TokenOpenBracket) {
  105. p.advance() // consume '['
  106. // Parse node title/name
  107. var titleParts []string
  108. for !p.check(lexer.TokenCloseBracket) && !p.isAtEnd() {
  109. titleParts = append(titleParts, p.advance().Value)
  110. }
  111. if len(titleParts) > 0 {
  112. title := strings.TrimSpace(strings.Join(titleParts, " "))
  113. node.Name = title
  114. }
  115. if p.check(lexer.TokenCloseBracket) {
  116. p.advance() // consume ']'
  117. }
  118. } else if p.check(lexer.TokenArrowSolid) || p.check(lexer.TokenMinus) {
  119. // Parse relationship
  120. p.advance() // consume arrow or minus
  121. // Skip additional arrow characters
  122. for p.check(lexer.TokenMinus) || p.check(lexer.TokenArrowSolid) {
  123. p.advance()
  124. }
  125. // Parse child node
  126. if p.check(lexer.TokenID) {
  127. childID := p.advance().Value
  128. // Create or find child node
  129. childNode := p.diagram.FindNode(childID)
  130. if childNode == nil {
  131. childNode = &ast.OrganizationNode{
  132. ID: childID,
  133. Name: childID,
  134. Level: node.Level + 1,
  135. Children: make([]*ast.OrganizationNode, 0),
  136. CssClasses: make([]string, 0),
  137. Styles: make([]string, 0),
  138. }
  139. p.diagram.AddNode(childNode)
  140. }
  141. // Establish parent-child relationship
  142. node.AddChild(childNode)
  143. }
  144. } else {
  145. p.advance() // consume unknown token
  146. }
  147. }
  148. p.diagram.AddNode(node)
  149. return nil
  150. }
  151. // buildHierarchy builds the hierarchical structure
  152. func (p *OrganizationParser) buildHierarchy() {
  153. // Find root nodes (nodes without parents)
  154. for _, node := range p.diagram.Nodes {
  155. if node.Parent == nil && p.diagram.Root == nil {
  156. p.diagram.Root = node
  157. break
  158. }
  159. }
  160. // Calculate levels
  161. if p.diagram.Root != nil {
  162. p.calculateLevels(p.diagram.Root, 0)
  163. }
  164. }
  165. // calculateLevels recursively calculates node levels
  166. func (p *OrganizationParser) calculateLevels(node *ast.OrganizationNode, level int) {
  167. node.Level = level
  168. for _, child := range node.Children {
  169. p.calculateLevels(child, level+1)
  170. }
  171. }
  172. // Helper methods
  173. func (p *OrganizationParser) check(tokenType lexer.TokenType) bool {
  174. if p.isAtEnd() {
  175. return false
  176. }
  177. return p.peek().Type == tokenType
  178. }
  179. func (p *OrganizationParser) checkKeyword(keyword string) bool {
  180. if p.isAtEnd() {
  181. return false
  182. }
  183. token := p.peek()
  184. return token.Type == lexer.TokenID && strings.ToLower(token.Value) == strings.ToLower(keyword)
  185. }
  186. func (p *OrganizationParser) advance() lexer.Token {
  187. if !p.isAtEnd() {
  188. p.current++
  189. }
  190. return p.previous()
  191. }
  192. func (p *OrganizationParser) isAtEnd() bool {
  193. return p.current >= len(p.tokens) || p.peek().Type == lexer.TokenEOF
  194. }
  195. func (p *OrganizationParser) peek() lexer.Token {
  196. if p.current >= len(p.tokens) {
  197. return lexer.Token{Type: lexer.TokenEOF}
  198. }
  199. return p.tokens[p.current]
  200. }
  201. func (p *OrganizationParser) previous() lexer.Token {
  202. if p.current <= 0 {
  203. return lexer.Token{Type: lexer.TokenEOF}
  204. }
  205. return p.tokens[p.current-1]
  206. }
  207. func (p *OrganizationParser) error(message string) error {
  208. token := p.peek()
  209. return fmt.Errorf("parse error at line %d, column %d: %s (got %s)",
  210. token.Line, token.Column, message, token.Type.String())
  211. }