parser.go 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. package main
  2. import (
  3. "bufio"
  4. "fmt"
  5. "os"
  6. "strings"
  7. )
  8. // A Parser for handling Endless Sky save format
  9. type Parser struct {
  10. Debug bool // Do we print debug info (to logs)
  11. }
  12. // Parses a given file returning a tree of Nodes
  13. func (p *Parser) Load(filename string) (Root *Node, err error) {
  14. Root = &Node{
  15. Line: filename,
  16. }
  17. Root.AddTag("root")
  18. file, err := os.Open(filename)
  19. if err != nil {
  20. return
  21. }
  22. defer file.Close()
  23. radar := bufio.NewScanner(file)
  24. content := []string{}
  25. for radar.Scan() {
  26. line := radar.Text()
  27. line = strings.ReplaceAll(line, "\n", "")
  28. content = append(content, line)
  29. }
  30. p.parse(&content, 0, Root)
  31. return
  32. }
  33. // Assistant func
  34. //
  35. // Processes the current line and next line for making the node tree/structure
  36. func (p *Parser) parse(file *[]string, linenum int, node *Node) {
  37. if linenum+1 > len(*file) {
  38. if p.Debug {
  39. fmt.Println("EOF")
  40. }
  41. return
  42. }
  43. line := (*file)[linenum]
  44. var next_line string
  45. if linenum+2 <= len(*file) {
  46. next_line = (*file)[linenum+1]
  47. } else {
  48. next_line = ""
  49. }
  50. next_depth := FindDepth(next_line)
  51. if p.Debug {
  52. fmt.Printf("%4d | '%s'\nNXTL | '%s'\n", linenum, line, next_line)
  53. }
  54. kid := node.NewChild()
  55. depth := FindDepth(line)
  56. kid.Line = CleanLine(line)
  57. if next_depth == depth {
  58. if p.Debug {
  59. fmt.Printf("Same Depth (%d)\n", depth)
  60. }
  61. p.parse(file, linenum+1, node)
  62. } else if next_depth > depth {
  63. if p.Debug {
  64. fmt.Printf("Deeper (%d->%d)\n", depth, next_depth)
  65. }
  66. p.parse(file, linenum+1, kid)
  67. } else if next_depth < depth {
  68. diff := depth - next_depth
  69. at := node
  70. if p.Debug {
  71. fmt.Printf("Surface (%d<-%d) diff=%d\n", depth, next_depth, diff)
  72. }
  73. for up := 0; up < diff; up++ {
  74. if at.Parent != nil {
  75. at = at.Parent
  76. }
  77. }
  78. p.parse(file, linenum+1, at)
  79. }
  80. }
  81. // Assistant func
  82. //
  83. // # Supports writing the current node's line, and the node's children too
  84. //
  85. // This uses recursive calls to write out the children
  86. func (p *Parser) saveassist(file *os.File, node *Node) error {
  87. _, err := file.WriteString(node.String() + "\n")
  88. if err != nil {
  89. return err
  90. }
  91. if node.Len() != 0 {
  92. for _, kid := range node.Children {
  93. err := p.saveassist(file, kid)
  94. if err != nil {
  95. return err
  96. }
  97. }
  98. }
  99. return nil
  100. }
  101. // Saves the node and it's children to the given filename
  102. func (p *Parser) Save(filename string, Root *Node) error {
  103. file, err := os.OpenFile(filename, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0660)
  104. if err != nil {
  105. return err
  106. }
  107. defer file.Close()
  108. for _, kid := range Root.Children {
  109. err = p.saveassist(file, kid)
  110. if err != nil {
  111. return err
  112. }
  113. }
  114. return nil
  115. }
  116. // TODO: Move these Util funcs to utils.go (update tests)
  117. // Util func
  118. //
  119. // Finds how many tabs deep the line is
  120. func FindDepth(line string) int {
  121. if strings.HasPrefix(line, "\t") {
  122. depth := 0
  123. for _, c := range line {
  124. if c != '\t' {
  125. break
  126. }
  127. depth += 1
  128. }
  129. return depth
  130. }
  131. return 0
  132. }
  133. // Util func
  134. //
  135. // # Cleans the starting tabs from the line
  136. //
  137. // Utilizes FindDepth to chop off that many characters in the beginning of the line
  138. func CleanLine(line string) string {
  139. if strings.HasPrefix(line, "\t") {
  140. depth := FindDepth(line)
  141. return line[depth:]
  142. }
  143. return line
  144. }