123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169 |
- package main
- import (
- "bufio"
- "encoding/json"
- "fmt"
- "os"
- "strings"
- "github.com/beanzilla/gonode"
- )
- // A Parser for handling Endless Sky save format
- type Parser struct {
- Debug bool // Do we print debug info (to logs)
- DebugJSON string // Do we json output a debug file (this is it's name, leave blank for disabled)
- }
- // Parses a given file returning a tree of Nodes
- func (p *Parser) Load(filename string) (Root *gonode.Node, err error) {
- Root = gonode.NewNode()
- Root.SetData(filename)
- 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)
- if p.DebugJSON != "" {
- pay, err := json.MarshalIndent(Root, "", " ")
- if err != nil {
- return Root, err
- }
- err = os.WriteFile(p.DebugJSON, pay, 0660)
- if err != nil {
- return Root, err
- }
- }
- 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 *gonode.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.SetData(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 *gonode.Node) error {
- if p.Debug {
- fmt.Printf("%s%v\n", strings.Repeat("\t", node.Depth()), node.Data())
- }
- _, err := file.WriteString(strings.Repeat("\t", node.Depth()) + fmt.Sprintf("%v\n", node.Data()))
- if err != nil {
- return err
- }
- if node.Len() != 0 {
- for kid := range node.Iter() {
- 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 *gonode.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.Iter() {
- 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
- }
|