blob: 7a6a8f9c5f1c76036e8d7576975d99a501e0321f [file] [log] [blame]
Shinichiro Hamaji04932612015-03-31 23:46:56 +09001package main
2
3import (
Fumitoshi Ukai0b9e8132015-04-30 10:20:18 +09004 "bytes"
Fumitoshi Ukai953ce6f2015-04-04 00:38:53 +09005 "errors"
Shinichiro Hamaji04932612015-03-31 23:46:56 +09006 "strings"
7)
8
Fumitoshi Ukai935de962015-04-28 17:08:20 +09009type pattern struct {
10 prefix, suffix string
11}
12
13func (p pattern) String() string {
14 return p.prefix + "%" + p.suffix
15}
16
17func (p pattern) match(s string) bool {
18 return strings.HasPrefix(s, p.prefix) && strings.HasSuffix(s, p.suffix)
19}
20
21func (p pattern) subst(repl, str string) string {
22 in := str
23 trimed := str
24 if p.prefix != "" {
25 trimed = strings.TrimPrefix(in, p.prefix)
26 if trimed == in {
27 return str
28 }
29 }
30 in = trimed
31 if p.suffix != "" {
32 trimed = strings.TrimSuffix(in, p.suffix)
33 if trimed == in {
34 return str
35 }
36 }
37 rs := strings.SplitN(repl, "%", 2)
38 if len(rs) != 2 {
39 return repl
40 }
41 return rs[0] + trimed + rs[1]
42}
43
Shinichiro Hamaji04932612015-03-31 23:46:56 +090044type Rule struct {
Shinichiro Hamaji5c53b572015-04-02 05:36:42 +090045 outputs []string
46 inputs []string
47 orderOnlyInputs []string
Fumitoshi Ukai935de962015-04-28 17:08:20 +090048 outputPatterns []pattern
Shinichiro Hamaji5c53b572015-04-02 05:36:42 +090049 isDoubleColon bool
50 isSuffixRule bool
51 cmds []string
52 filename string
53 lineno int
54 cmdLineno int
Shinichiro Hamaji04932612015-03-31 23:46:56 +090055}
56
Fumitoshi Ukai0b9e8132015-04-30 10:20:18 +090057func isPatternRule(s []byte) (pattern, bool) {
58 i := bytes.IndexByte(s, '%')
Fumitoshi Ukai935de962015-04-28 17:08:20 +090059 if i < 0 {
60 return pattern{}, false
61 }
Fumitoshi Ukai0b9e8132015-04-30 10:20:18 +090062 return pattern{prefix: string(s[:i]), suffix: string(s[i+1:])}, true
Shinichiro Hamaji21a9b5f2015-04-01 02:42:59 +090063}
64
Fumitoshi Ukai0b9e8132015-04-30 10:20:18 +090065func (r *Rule) parseInputs(s []byte) {
66 inputs := splitSpacesBytes(s)
Shinichiro Hamaji5c53b572015-04-02 05:36:42 +090067 isOrderOnly := false
68 for _, input := range inputs {
Fumitoshi Ukai0b9e8132015-04-30 10:20:18 +090069 if len(input) == 1 && input[0] == '|' {
Shinichiro Hamaji5c53b572015-04-02 05:36:42 +090070 isOrderOnly = true
71 continue
72 }
73 if isOrderOnly {
Fumitoshi Ukai0b9e8132015-04-30 10:20:18 +090074 r.orderOnlyInputs = append(r.orderOnlyInputs, internBytes(input))
Shinichiro Hamaji5c53b572015-04-02 05:36:42 +090075 } else {
Fumitoshi Ukai0b9e8132015-04-30 10:20:18 +090076 r.inputs = append(r.inputs, internBytes(input))
Shinichiro Hamaji5c53b572015-04-02 05:36:42 +090077 }
78 }
79}
80
Fumitoshi Ukai0b9e8132015-04-30 10:20:18 +090081func (r *Rule) parseVar(s []byte) *AssignAST {
82 eq := bytes.IndexByte(s, '=')
Fumitoshi Ukai953ce6f2015-04-04 00:38:53 +090083 if eq <= 0 {
84 return nil
85 }
86 assign := &AssignAST{
Fumitoshi Ukai0b9e8132015-04-30 10:20:18 +090087 rhs: string(trimLeftSpaceBytes(s[eq+1:])),
Fumitoshi Ukai953ce6f2015-04-04 00:38:53 +090088 }
89 assign.filename = r.filename
90 assign.lineno = r.lineno
91 // TODO(ukai): support override, export.
Fumitoshi Ukai0b9e8132015-04-30 10:20:18 +090092 switch s[eq-1] { // s[eq] is '='
93 case ':':
94 assign.lhs = string(trimSpaceBytes(s[:eq-1]))
Fumitoshi Ukai953ce6f2015-04-04 00:38:53 +090095 assign.op = ":="
Fumitoshi Ukai0b9e8132015-04-30 10:20:18 +090096 case '+':
97 assign.lhs = string(trimSpaceBytes(s[:eq-1]))
Fumitoshi Ukai953ce6f2015-04-04 00:38:53 +090098 assign.op = "+="
Fumitoshi Ukai0b9e8132015-04-30 10:20:18 +090099 case '?':
100 assign.lhs = string(trimSpaceBytes(s[:eq-1]))
Fumitoshi Ukai953ce6f2015-04-04 00:38:53 +0900101 assign.op = "?="
102 default:
Fumitoshi Ukai0b9e8132015-04-30 10:20:18 +0900103 assign.lhs = string(trimSpaceBytes(s[:eq]))
Fumitoshi Ukai953ce6f2015-04-04 00:38:53 +0900104 assign.op = "="
105 }
106 return assign
107}
108
Fumitoshi Ukai0b9e8132015-04-30 10:20:18 +0900109func (r *Rule) parse(line []byte) (*AssignAST, error) {
110 index := bytes.IndexByte(line, ':')
Shinichiro Hamaji21a9b5f2015-04-01 02:42:59 +0900111 if index < 0 {
Fumitoshi Ukai953ce6f2015-04-04 00:38:53 +0900112 return nil, errors.New("*** missing separator.")
Shinichiro Hamaji04932612015-03-31 23:46:56 +0900113 }
114
Shinichiro Hamaji21a9b5f2015-04-01 02:42:59 +0900115 first := line[:index]
Fumitoshi Ukai0b9e8132015-04-30 10:20:18 +0900116 outputs := splitSpacesBytes(first)
Fumitoshi Ukai935de962015-04-28 17:08:20 +0900117 pat, isFirstPattern := isPatternRule(first)
Shinichiro Hamaji21a9b5f2015-04-01 02:42:59 +0900118 if isFirstPattern {
119 if len(outputs) > 1 {
Fumitoshi Ukai953ce6f2015-04-04 00:38:53 +0900120 return nil, errors.New("*** mixed implicit and normal rules: deprecated syntax")
Shinichiro Hamaji21a9b5f2015-04-01 02:42:59 +0900121 }
Fumitoshi Ukai935de962015-04-28 17:08:20 +0900122 r.outputPatterns = []pattern{pat}
Shinichiro Hamaji21a9b5f2015-04-01 02:42:59 +0900123 } else {
Fumitoshi Ukai0b9e8132015-04-30 10:20:18 +0900124 o := make([]string, len(outputs))
125 for i, output := range outputs {
126 o[i] = internBytes(output)
127 }
128 r.outputs = o
Shinichiro Hamaji21a9b5f2015-04-01 02:42:59 +0900129 }
130
131 index++
132 if index < len(line) && line[index] == ':' {
133 r.isDoubleColon = true
134 index++
135 }
136
137 rest := line[index:]
Fumitoshi Ukai953ce6f2015-04-04 00:38:53 +0900138 if assign := r.parseVar(rest); assign != nil {
139 return assign, nil
140 }
Fumitoshi Ukai0b9e8132015-04-30 10:20:18 +0900141 index = bytes.IndexByte(rest, ':')
Shinichiro Hamaji21a9b5f2015-04-01 02:42:59 +0900142 if index < 0 {
Shinichiro Hamaji5c53b572015-04-02 05:36:42 +0900143 r.parseInputs(rest)
Fumitoshi Ukai953ce6f2015-04-04 00:38:53 +0900144 return nil, nil
Shinichiro Hamaji21a9b5f2015-04-01 02:42:59 +0900145 }
146
147 // %.x: %.y: %.z
148 if isFirstPattern {
Fumitoshi Ukai953ce6f2015-04-04 00:38:53 +0900149 return nil, errors.New("*** mixed implicit and normal rules: deprecated syntax")
Shinichiro Hamaji21a9b5f2015-04-01 02:42:59 +0900150 }
151
152 second := rest[:index]
153 third := rest[index+1:]
154
Fumitoshi Ukai0b9e8132015-04-30 10:20:18 +0900155 // r.outputs is already set.
156 outputPatterns := splitSpacesBytes(second)
Fumitoshi Ukai935de962015-04-28 17:08:20 +0900157 if len(outputPatterns) == 0 {
Fumitoshi Ukai953ce6f2015-04-04 00:38:53 +0900158 return nil, errors.New("*** missing target pattern.")
Shinichiro Hamaji21a9b5f2015-04-01 02:42:59 +0900159 }
Fumitoshi Ukai935de962015-04-28 17:08:20 +0900160 if len(outputPatterns) > 1 {
Fumitoshi Ukai953ce6f2015-04-04 00:38:53 +0900161 return nil, errors.New("*** multiple target patterns.")
Shinichiro Hamaji21a9b5f2015-04-01 02:42:59 +0900162 }
Fumitoshi Ukai935de962015-04-28 17:08:20 +0900163 outpat, ok := isPatternRule(outputPatterns[0])
164 if !ok {
Fumitoshi Ukai953ce6f2015-04-04 00:38:53 +0900165 return nil, errors.New("*** target pattern contains no '%'.")
Shinichiro Hamaji21a9b5f2015-04-01 02:42:59 +0900166 }
Fumitoshi Ukai935de962015-04-28 17:08:20 +0900167 r.outputPatterns = []pattern{outpat}
Shinichiro Hamaji5c53b572015-04-02 05:36:42 +0900168 r.parseInputs(third)
Shinichiro Hamaji21a9b5f2015-04-01 02:42:59 +0900169
Fumitoshi Ukai953ce6f2015-04-04 00:38:53 +0900170 return nil, nil
Shinichiro Hamaji04932612015-03-31 23:46:56 +0900171}