181 lines
3.5 KiB
Go
181 lines
3.5 KiB
Go
package build
|
|
|
|
import (
|
|
"fmt"
|
|
"bufio"
|
|
"os"
|
|
"io"
|
|
"testing"
|
|
"strings"
|
|
"unicode"
|
|
)
|
|
|
|
type parseFunc func(key, values string) (Token, error)
|
|
|
|
var parseFuncs = map[string]parseFunc{
|
|
"rom": parseStrValue,
|
|
"fullaudio": parseStrValue,
|
|
"audiooffsets": parseAudioOffsets,
|
|
|
|
"page": parseNumValue,
|
|
"padding": parseNumValue,
|
|
"version": parseNumValue,
|
|
|
|
"delay": parseDelay,
|
|
|
|
"script": parseData,
|
|
"tiles": parseData,
|
|
"pattern": parseData,
|
|
}
|
|
|
|
func ParseFile(filename string) ([]Token, error) {
|
|
file, err := os.Open(filename)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer file.Close()
|
|
|
|
return Parse(file)
|
|
}
|
|
|
|
func Parse(r io.Reader) ([]Token, error) {
|
|
items := []Token{}
|
|
scanner := bufio.NewScanner(r)
|
|
prev := ""
|
|
|
|
for scanner.Scan() {
|
|
line := strings.TrimSpace(scanner.Text())
|
|
|
|
// blanks and comments
|
|
if line == "" || strings.HasPrefix(line, "#") {
|
|
continue
|
|
}
|
|
|
|
splitln := strings.SplitN(line, " ", 2)
|
|
if len(splitln) != 2 {
|
|
return nil, fmt.Errorf("invalid line: %q", line)
|
|
}
|
|
|
|
var itm Token
|
|
var err error
|
|
|
|
// TODO: Some of these will need to be unique in the file
|
|
// (rom, version, fullaudio), and probably exclusive/ordered.
|
|
// IE, rom, version, & fullaudio must come before the first
|
|
// page ##, and must only appear once. if a page has an
|
|
// audio line, all pages must have one and there can be no
|
|
// fullaudio.
|
|
|
|
if fn, ok := parseFuncs[splitln[0]]; ok {
|
|
itm, err = fn(splitln[0], splitln[1])
|
|
} else {
|
|
return nil, fmt.Errorf("unknown line: %s\n", splitln[0])
|
|
}
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if !itm.ValidAfter(prev) {
|
|
if prev == "" {
|
|
prev = "[empty]"
|
|
}
|
|
return nil, fmt.Errorf("%s not valid after %s", itm.Type(), prev)
|
|
}
|
|
prev = itm.Type()
|
|
|
|
items = append(items, itm)
|
|
}
|
|
|
|
return items, nil
|
|
}
|
|
|
|
var t *testing.T
|
|
|
|
func pKeyVals(values string) (map[string]string, error) {
|
|
m := map[string]string{}
|
|
|
|
start := 0
|
|
runes := []rune(values)
|
|
currentKey := ""
|
|
quote := false
|
|
tlog("[pKeyVals] start")
|
|
var i int
|
|
|
|
for i = 0; i < len(runes); i++ {
|
|
tlogf("[pKeyVals] rune: %c\n", runes[i])
|
|
if !quote && runes[i] == '"' {
|
|
quote = true
|
|
tlog("[pKeyVals] start quote")
|
|
continue
|
|
}
|
|
|
|
if !quote && unicode.IsSpace(runes[i]) {
|
|
tlog("[pKeyVals] !quote && IsSpace()")
|
|
if currentKey == "" {
|
|
tlog("[pKeyVals] currentKey empty")
|
|
start = i+1
|
|
} else {
|
|
tlog("[pKeyVals] currentKey not empty")
|
|
m[currentKey] = string(runes[start:i])
|
|
currentKey = ""
|
|
start = i+1
|
|
}
|
|
continue
|
|
}
|
|
|
|
if quote && runes[i] == '"' {
|
|
tlog("[pKeyVals] quote && rune == \"")
|
|
quote = false
|
|
|
|
if currentKey == "" {
|
|
currentKey = string(runes[start+1:i])
|
|
i++
|
|
start = i+1
|
|
tlogf("[pKeyVals] currentKey empty; set to %s\n", currentKey)
|
|
|
|
} else {
|
|
m[currentKey] = string(runes[start+1:i])
|
|
currentKey = ""
|
|
tlog("[pKeyVals] currentKey not empty")
|
|
}
|
|
continue
|
|
}
|
|
|
|
if runes[i] == ':' {
|
|
tlog("[pKeyVals] rune == :")
|
|
if i == start {
|
|
return nil, fmt.Errorf("missing key")
|
|
}
|
|
|
|
currentKey = string(runes[start:i])
|
|
start = i+1
|
|
continue
|
|
}
|
|
}
|
|
|
|
if quote {
|
|
return nil, fmt.Errorf("missmatched quote")
|
|
}
|
|
|
|
if currentKey != "" {
|
|
//return nil, fmt.Errorf("missing value for %q", currentKey)
|
|
tlogf("[pKeyVals] outside loop assign m[%s] = %s\n", currentKey, string(runes[start:i]))
|
|
m[currentKey] = string(runes[start:i])
|
|
}
|
|
|
|
return m, nil
|
|
}
|
|
|
|
func tlog(args ...any) {
|
|
if t != nil {
|
|
t.Log(args...)
|
|
}
|
|
}
|
|
|
|
func tlogf(format string, args ...any) {
|
|
if t != nil {
|
|
t.Logf(format, args...)
|
|
}
|
|
}
|