package main import ( "bufio" "fmt" "os" "strings" ) // A Parser for handling Endless Sky save format type Parser struct { Debug bool // Do we print debug info (to logs) } // Parses a given file returning a tree of Nodes func (p *Parser) Load(filename string) (Root *Node, err error) { Root = &Node{ Line: filename, } Root.AddTag("root") file, err := os.Open(filename) if err != nil { return } defer file.Close() radar := bufio.NewScanner(file) content := []string{} for radar.Scan() { line := radar.Text() line = strings.ReplaceAll(line, "\n", "") content = append(content, line) } p.parse(&content, 0, Root) return } // Assistant func // // Processes the current line and next line for making the node tree/structure func (p *Parser) parse(file *[]string, linenum int, node *Node) { if linenum+1 > len(*file) { if p.Debug { fmt.Println("EOF") } return } line := (*file)[linenum] var next_line string if linenum+2 <= len(*file) { next_line = (*file)[linenum+1] } else { next_line = "" } next_depth := FindDepth(next_line) if p.Debug { fmt.Printf("%4d | '%s'\nNXTL | '%s'\n", linenum, line, next_line) } kid := node.NewChild() depth := FindDepth(line) kid.Line = CleanLine(line) if next_depth == depth { if p.Debug { fmt.Printf("Same Depth (%d)\n", depth) } p.parse(file, linenum+1, node) } else if next_depth > depth { if p.Debug { fmt.Printf("Deeper (%d->%d)\n", depth, next_depth) } p.parse(file, linenum+1, kid) } else if next_depth < depth { diff := depth - next_depth at := node if p.Debug { fmt.Printf("Surface (%d<-%d) diff=%d\n", depth, next_depth, diff) } for up := 0; up < diff; up++ { if at.Parent != nil { at = at.Parent } } p.parse(file, linenum+1, at) } } // Assistant func // // # Supports writing the current node's line, and the node's children too // // This uses recursive calls to write out the children func (p *Parser) saveassist(file *os.File, node *Node) error { _, err := file.WriteString(node.String() + "\n") if err != nil { return err } if node.Len() != 0 { for _, kid := range node.Children { err := p.saveassist(file, kid) if err != nil { return err } } } return nil } // Saves the node and it's children to the given filename func (p *Parser) Save(filename string, Root *Node) error { file, err := os.OpenFile(filename, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0660) if err != nil { return err } defer file.Close() for _, kid := range Root.Children { err = p.saveassist(file, kid) if err != nil { return err } } return nil } // TODO: Move these Util funcs to utils.go (update tests) // Util func // // Finds how many tabs deep the line is func FindDepth(line string) int { if strings.HasPrefix(line, "\t") { depth := 0 for _, c := range line { if c != '\t' { break } depth += 1 } return depth } return 0 } // Util func // // # Cleans the starting tabs from the line // // Utilizes FindDepth to chop off that many characters in the beginning of the line func CleanLine(line string) string { if strings.HasPrefix(line, "\t") { depth := FindDepth(line) return line[depth:] } return line }