|  | package gom | 
|  |  | 
|  | // This is copied + slightly adapted from gom's `gomfile.go` file. | 
|  | // | 
|  | // gom's license is MIT-style. | 
|  |  | 
|  | import ( | 
|  | "bufio" | 
|  | "fmt" | 
|  | "io" | 
|  | "os" | 
|  | "regexp" | 
|  | "strings" | 
|  | ) | 
|  |  | 
|  | var qx = `'[^']*'|"[^"]*"` | 
|  | var kx = `:[a-z][a-z0-9_]*` | 
|  | var ax = `(?:\s*` + kx + `\s*|,\s*` + kx + `\s*)` | 
|  | var reGroup = regexp.MustCompile(`\s*group\s+((?:` + kx + `\s*|,\s*` + kx + `\s*)*)\s*do\s*$`) | 
|  | var reEnd = regexp.MustCompile(`\s*end\s*$`) | 
|  | var reGom = regexp.MustCompile(`^\s*gom\s+(` + qx + `)\s*((?:,\s*` + kx + `\s*=>\s*(?:` + qx + `|\s*\[\s*` + ax + `*\s*\]\s*))*)$`) | 
|  | var reOptions = regexp.MustCompile(`(,\s*` + kx + `\s*=>\s*(?:` + qx + `|\s*\[\s*` + ax + `*\s*\]\s*)\s*)`) | 
|  |  | 
|  | func unquote(name string) string { | 
|  | name = strings.TrimSpace(name) | 
|  | if len(name) > 2 { | 
|  | if (name[0] == '\'' && name[len(name)-1] == '\'') || (name[0] == '"' && name[len(name)-1] == '"') { | 
|  | return name[1 : len(name)-1] | 
|  | } | 
|  | } | 
|  | return name | 
|  | } | 
|  |  | 
|  | func parseOptions(line string, options map[string]interface{}) { | 
|  | ss := reOptions.FindAllStringSubmatch(line, -1) | 
|  | reA := regexp.MustCompile(ax) | 
|  | for _, s := range ss { | 
|  | kvs := strings.SplitN(strings.TrimSpace(s[0])[1:], "=>", 2) | 
|  | kvs[0], kvs[1] = strings.TrimSpace(kvs[0]), strings.TrimSpace(kvs[1]) | 
|  | if kvs[1][0] == '[' { | 
|  | as := reA.FindAllStringSubmatch(kvs[1][1:len(kvs[1])-1], -1) | 
|  | a := []string{} | 
|  | for i := range as { | 
|  | it := strings.TrimSpace(as[i][0]) | 
|  | if strings.HasPrefix(it, ",") { | 
|  | it = strings.TrimSpace(it[1:]) | 
|  | } | 
|  | if strings.HasPrefix(it, ":") { | 
|  | it = strings.TrimSpace(it[1:]) | 
|  | } | 
|  | a = append(a, it) | 
|  | } | 
|  | options[kvs[0][1:]] = a | 
|  | } else { | 
|  | options[kvs[0][1:]] = unquote(kvs[1]) | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | // Gom represents configuration from Gom. | 
|  | type Gom struct { | 
|  | name    string | 
|  | options map[string]interface{} | 
|  | } | 
|  |  | 
|  | func parseGomfile(filename string) ([]Gom, error) { | 
|  | f, err := os.Open(filename + ".lock") | 
|  | if err != nil { | 
|  | f, err = os.Open(filename) | 
|  | if err != nil { | 
|  | return nil, err | 
|  | } | 
|  | } | 
|  | br := bufio.NewReader(f) | 
|  |  | 
|  | goms := make([]Gom, 0) | 
|  |  | 
|  | n := 0 | 
|  | skip := 0 | 
|  | valid := true | 
|  | var envs []string | 
|  | for { | 
|  | n++ | 
|  | lb, _, err := br.ReadLine() | 
|  | if err != nil { | 
|  | if err == io.EOF { | 
|  | return goms, nil | 
|  | } | 
|  | return nil, err | 
|  | } | 
|  | line := strings.TrimSpace(string(lb)) | 
|  | if line == "" || strings.HasPrefix(line, "#") { | 
|  | continue | 
|  | } | 
|  |  | 
|  | name := "" | 
|  | options := make(map[string]interface{}) | 
|  | var items []string | 
|  | if reGroup.MatchString(line) { | 
|  | envs = strings.Split(reGroup.FindStringSubmatch(line)[1], ",") | 
|  | for i := range envs { | 
|  | envs[i] = strings.TrimSpace(envs[i])[1:] | 
|  | } | 
|  | valid = true | 
|  | continue | 
|  | } else if reEnd.MatchString(line) { | 
|  | if !valid { | 
|  | skip-- | 
|  | if skip < 0 { | 
|  | return nil, fmt.Errorf("Syntax Error at line %d", n) | 
|  | } | 
|  | } | 
|  | valid = false | 
|  | envs = nil | 
|  | continue | 
|  | } else if skip > 0 { | 
|  | continue | 
|  | } else if reGom.MatchString(line) { | 
|  | items = reGom.FindStringSubmatch(line)[1:] | 
|  | name = unquote(items[0]) | 
|  | parseOptions(items[1], options) | 
|  | } else { | 
|  | return nil, fmt.Errorf("Syntax Error at line %d", n) | 
|  | } | 
|  | if envs != nil { | 
|  | options["group"] = envs | 
|  | } | 
|  | goms = append(goms, Gom{name, options}) | 
|  | } | 
|  | } |