| 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}) | 
 | 	} | 
 | } |