github.com/mfycheng/glide@v0.11.2-0.20160818232903-be8a502f4bc4/gom/parser.go (about) 1 package gom 2 3 // This is copied + slightly adapted from gom's `gomfile.go` file. 4 // 5 // gom's license is MIT-style. 6 7 import ( 8 "bufio" 9 "fmt" 10 "io" 11 "os" 12 "regexp" 13 "strings" 14 ) 15 16 var qx = `'[^']*'|"[^"]*"` 17 var kx = `:[a-z][a-z0-9_]*` 18 var ax = `(?:\s*` + kx + `\s*|,\s*` + kx + `\s*)` 19 var reGroup = regexp.MustCompile(`\s*group\s+((?:` + kx + `\s*|,\s*` + kx + `\s*)*)\s*do\s*$`) 20 var reEnd = regexp.MustCompile(`\s*end\s*$`) 21 var reGom = regexp.MustCompile(`^\s*gom\s+(` + qx + `)\s*((?:,\s*` + kx + `\s*=>\s*(?:` + qx + `|\s*\[\s*` + ax + `*\s*\]\s*))*)$`) 22 var reOptions = regexp.MustCompile(`(,\s*` + kx + `\s*=>\s*(?:` + qx + `|\s*\[\s*` + ax + `*\s*\]\s*)\s*)`) 23 24 func unquote(name string) string { 25 name = strings.TrimSpace(name) 26 if len(name) > 2 { 27 if (name[0] == '\'' && name[len(name)-1] == '\'') || (name[0] == '"' && name[len(name)-1] == '"') { 28 return name[1 : len(name)-1] 29 } 30 } 31 return name 32 } 33 34 func parseOptions(line string, options map[string]interface{}) { 35 ss := reOptions.FindAllStringSubmatch(line, -1) 36 reA := regexp.MustCompile(ax) 37 for _, s := range ss { 38 kvs := strings.SplitN(strings.TrimSpace(s[0])[1:], "=>", 2) 39 kvs[0], kvs[1] = strings.TrimSpace(kvs[0]), strings.TrimSpace(kvs[1]) 40 if kvs[1][0] == '[' { 41 as := reA.FindAllStringSubmatch(kvs[1][1:len(kvs[1])-1], -1) 42 a := []string{} 43 for i := range as { 44 it := strings.TrimSpace(as[i][0]) 45 if strings.HasPrefix(it, ",") { 46 it = strings.TrimSpace(it[1:]) 47 } 48 if strings.HasPrefix(it, ":") { 49 it = strings.TrimSpace(it[1:]) 50 } 51 a = append(a, it) 52 } 53 options[kvs[0][1:]] = a 54 } else { 55 options[kvs[0][1:]] = unquote(kvs[1]) 56 } 57 } 58 } 59 60 // Gom represents configuration from Gom. 61 type Gom struct { 62 name string 63 options map[string]interface{} 64 } 65 66 func parseGomfile(filename string) ([]Gom, error) { 67 f, err := os.Open(filename + ".lock") 68 if err != nil { 69 f, err = os.Open(filename) 70 if err != nil { 71 return nil, err 72 } 73 } 74 br := bufio.NewReader(f) 75 76 goms := make([]Gom, 0) 77 78 n := 0 79 skip := 0 80 valid := true 81 var envs []string 82 for { 83 n++ 84 lb, _, err := br.ReadLine() 85 if err != nil { 86 if err == io.EOF { 87 return goms, nil 88 } 89 return nil, err 90 } 91 line := strings.TrimSpace(string(lb)) 92 if line == "" || strings.HasPrefix(line, "#") { 93 continue 94 } 95 96 name := "" 97 options := make(map[string]interface{}) 98 var items []string 99 if reGroup.MatchString(line) { 100 envs = strings.Split(reGroup.FindStringSubmatch(line)[1], ",") 101 for i := range envs { 102 envs[i] = strings.TrimSpace(envs[i])[1:] 103 } 104 valid = true 105 continue 106 } else if reEnd.MatchString(line) { 107 if !valid { 108 skip-- 109 if skip < 0 { 110 return nil, fmt.Errorf("Syntax Error at line %d", n) 111 } 112 } 113 valid = false 114 envs = nil 115 continue 116 } else if skip > 0 { 117 continue 118 } else if reGom.MatchString(line) { 119 items = reGom.FindStringSubmatch(line)[1:] 120 name = unquote(items[0]) 121 parseOptions(items[1], options) 122 } else { 123 return nil, fmt.Errorf("Syntax Error at line %d", n) 124 } 125 if envs != nil { 126 options["group"] = envs 127 } 128 goms = append(goms, Gom{name, options}) 129 } 130 }