Shinichiro Hamaji | 0493261 | 2015-03-31 23:46:56 +0900 | [diff] [blame] | 1 | package main |
| 2 | |
| 3 | import ( |
Fumitoshi Ukai | 0b9e813 | 2015-04-30 10:20:18 +0900 | [diff] [blame^] | 4 | "bytes" |
Fumitoshi Ukai | 953ce6f | 2015-04-04 00:38:53 +0900 | [diff] [blame] | 5 | "errors" |
Shinichiro Hamaji | 0493261 | 2015-03-31 23:46:56 +0900 | [diff] [blame] | 6 | "strings" |
| 7 | ) |
| 8 | |
Fumitoshi Ukai | 935de96 | 2015-04-28 17:08:20 +0900 | [diff] [blame] | 9 | type pattern struct { |
| 10 | prefix, suffix string |
| 11 | } |
| 12 | |
| 13 | func (p pattern) String() string { |
| 14 | return p.prefix + "%" + p.suffix |
| 15 | } |
| 16 | |
| 17 | func (p pattern) match(s string) bool { |
| 18 | return strings.HasPrefix(s, p.prefix) && strings.HasSuffix(s, p.suffix) |
| 19 | } |
| 20 | |
| 21 | func (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 Hamaji | 0493261 | 2015-03-31 23:46:56 +0900 | [diff] [blame] | 44 | type Rule struct { |
Shinichiro Hamaji | 5c53b57 | 2015-04-02 05:36:42 +0900 | [diff] [blame] | 45 | outputs []string |
| 46 | inputs []string |
| 47 | orderOnlyInputs []string |
Fumitoshi Ukai | 935de96 | 2015-04-28 17:08:20 +0900 | [diff] [blame] | 48 | outputPatterns []pattern |
Shinichiro Hamaji | 5c53b57 | 2015-04-02 05:36:42 +0900 | [diff] [blame] | 49 | isDoubleColon bool |
| 50 | isSuffixRule bool |
| 51 | cmds []string |
| 52 | filename string |
| 53 | lineno int |
| 54 | cmdLineno int |
Shinichiro Hamaji | 0493261 | 2015-03-31 23:46:56 +0900 | [diff] [blame] | 55 | } |
| 56 | |
Fumitoshi Ukai | 0b9e813 | 2015-04-30 10:20:18 +0900 | [diff] [blame^] | 57 | func isPatternRule(s []byte) (pattern, bool) { |
| 58 | i := bytes.IndexByte(s, '%') |
Fumitoshi Ukai | 935de96 | 2015-04-28 17:08:20 +0900 | [diff] [blame] | 59 | if i < 0 { |
| 60 | return pattern{}, false |
| 61 | } |
Fumitoshi Ukai | 0b9e813 | 2015-04-30 10:20:18 +0900 | [diff] [blame^] | 62 | return pattern{prefix: string(s[:i]), suffix: string(s[i+1:])}, true |
Shinichiro Hamaji | 21a9b5f | 2015-04-01 02:42:59 +0900 | [diff] [blame] | 63 | } |
| 64 | |
Fumitoshi Ukai | 0b9e813 | 2015-04-30 10:20:18 +0900 | [diff] [blame^] | 65 | func (r *Rule) parseInputs(s []byte) { |
| 66 | inputs := splitSpacesBytes(s) |
Shinichiro Hamaji | 5c53b57 | 2015-04-02 05:36:42 +0900 | [diff] [blame] | 67 | isOrderOnly := false |
| 68 | for _, input := range inputs { |
Fumitoshi Ukai | 0b9e813 | 2015-04-30 10:20:18 +0900 | [diff] [blame^] | 69 | if len(input) == 1 && input[0] == '|' { |
Shinichiro Hamaji | 5c53b57 | 2015-04-02 05:36:42 +0900 | [diff] [blame] | 70 | isOrderOnly = true |
| 71 | continue |
| 72 | } |
| 73 | if isOrderOnly { |
Fumitoshi Ukai | 0b9e813 | 2015-04-30 10:20:18 +0900 | [diff] [blame^] | 74 | r.orderOnlyInputs = append(r.orderOnlyInputs, internBytes(input)) |
Shinichiro Hamaji | 5c53b57 | 2015-04-02 05:36:42 +0900 | [diff] [blame] | 75 | } else { |
Fumitoshi Ukai | 0b9e813 | 2015-04-30 10:20:18 +0900 | [diff] [blame^] | 76 | r.inputs = append(r.inputs, internBytes(input)) |
Shinichiro Hamaji | 5c53b57 | 2015-04-02 05:36:42 +0900 | [diff] [blame] | 77 | } |
| 78 | } |
| 79 | } |
| 80 | |
Fumitoshi Ukai | 0b9e813 | 2015-04-30 10:20:18 +0900 | [diff] [blame^] | 81 | func (r *Rule) parseVar(s []byte) *AssignAST { |
| 82 | eq := bytes.IndexByte(s, '=') |
Fumitoshi Ukai | 953ce6f | 2015-04-04 00:38:53 +0900 | [diff] [blame] | 83 | if eq <= 0 { |
| 84 | return nil |
| 85 | } |
| 86 | assign := &AssignAST{ |
Fumitoshi Ukai | 0b9e813 | 2015-04-30 10:20:18 +0900 | [diff] [blame^] | 87 | rhs: string(trimLeftSpaceBytes(s[eq+1:])), |
Fumitoshi Ukai | 953ce6f | 2015-04-04 00:38:53 +0900 | [diff] [blame] | 88 | } |
| 89 | assign.filename = r.filename |
| 90 | assign.lineno = r.lineno |
| 91 | // TODO(ukai): support override, export. |
Fumitoshi Ukai | 0b9e813 | 2015-04-30 10:20:18 +0900 | [diff] [blame^] | 92 | switch s[eq-1] { // s[eq] is '=' |
| 93 | case ':': |
| 94 | assign.lhs = string(trimSpaceBytes(s[:eq-1])) |
Fumitoshi Ukai | 953ce6f | 2015-04-04 00:38:53 +0900 | [diff] [blame] | 95 | assign.op = ":=" |
Fumitoshi Ukai | 0b9e813 | 2015-04-30 10:20:18 +0900 | [diff] [blame^] | 96 | case '+': |
| 97 | assign.lhs = string(trimSpaceBytes(s[:eq-1])) |
Fumitoshi Ukai | 953ce6f | 2015-04-04 00:38:53 +0900 | [diff] [blame] | 98 | assign.op = "+=" |
Fumitoshi Ukai | 0b9e813 | 2015-04-30 10:20:18 +0900 | [diff] [blame^] | 99 | case '?': |
| 100 | assign.lhs = string(trimSpaceBytes(s[:eq-1])) |
Fumitoshi Ukai | 953ce6f | 2015-04-04 00:38:53 +0900 | [diff] [blame] | 101 | assign.op = "?=" |
| 102 | default: |
Fumitoshi Ukai | 0b9e813 | 2015-04-30 10:20:18 +0900 | [diff] [blame^] | 103 | assign.lhs = string(trimSpaceBytes(s[:eq])) |
Fumitoshi Ukai | 953ce6f | 2015-04-04 00:38:53 +0900 | [diff] [blame] | 104 | assign.op = "=" |
| 105 | } |
| 106 | return assign |
| 107 | } |
| 108 | |
Fumitoshi Ukai | 0b9e813 | 2015-04-30 10:20:18 +0900 | [diff] [blame^] | 109 | func (r *Rule) parse(line []byte) (*AssignAST, error) { |
| 110 | index := bytes.IndexByte(line, ':') |
Shinichiro Hamaji | 21a9b5f | 2015-04-01 02:42:59 +0900 | [diff] [blame] | 111 | if index < 0 { |
Fumitoshi Ukai | 953ce6f | 2015-04-04 00:38:53 +0900 | [diff] [blame] | 112 | return nil, errors.New("*** missing separator.") |
Shinichiro Hamaji | 0493261 | 2015-03-31 23:46:56 +0900 | [diff] [blame] | 113 | } |
| 114 | |
Shinichiro Hamaji | 21a9b5f | 2015-04-01 02:42:59 +0900 | [diff] [blame] | 115 | first := line[:index] |
Fumitoshi Ukai | 0b9e813 | 2015-04-30 10:20:18 +0900 | [diff] [blame^] | 116 | outputs := splitSpacesBytes(first) |
Fumitoshi Ukai | 935de96 | 2015-04-28 17:08:20 +0900 | [diff] [blame] | 117 | pat, isFirstPattern := isPatternRule(first) |
Shinichiro Hamaji | 21a9b5f | 2015-04-01 02:42:59 +0900 | [diff] [blame] | 118 | if isFirstPattern { |
| 119 | if len(outputs) > 1 { |
Fumitoshi Ukai | 953ce6f | 2015-04-04 00:38:53 +0900 | [diff] [blame] | 120 | return nil, errors.New("*** mixed implicit and normal rules: deprecated syntax") |
Shinichiro Hamaji | 21a9b5f | 2015-04-01 02:42:59 +0900 | [diff] [blame] | 121 | } |
Fumitoshi Ukai | 935de96 | 2015-04-28 17:08:20 +0900 | [diff] [blame] | 122 | r.outputPatterns = []pattern{pat} |
Shinichiro Hamaji | 21a9b5f | 2015-04-01 02:42:59 +0900 | [diff] [blame] | 123 | } else { |
Fumitoshi Ukai | 0b9e813 | 2015-04-30 10:20:18 +0900 | [diff] [blame^] | 124 | o := make([]string, len(outputs)) |
| 125 | for i, output := range outputs { |
| 126 | o[i] = internBytes(output) |
| 127 | } |
| 128 | r.outputs = o |
Shinichiro Hamaji | 21a9b5f | 2015-04-01 02:42:59 +0900 | [diff] [blame] | 129 | } |
| 130 | |
| 131 | index++ |
| 132 | if index < len(line) && line[index] == ':' { |
| 133 | r.isDoubleColon = true |
| 134 | index++ |
| 135 | } |
| 136 | |
| 137 | rest := line[index:] |
Fumitoshi Ukai | 953ce6f | 2015-04-04 00:38:53 +0900 | [diff] [blame] | 138 | if assign := r.parseVar(rest); assign != nil { |
| 139 | return assign, nil |
| 140 | } |
Fumitoshi Ukai | 0b9e813 | 2015-04-30 10:20:18 +0900 | [diff] [blame^] | 141 | index = bytes.IndexByte(rest, ':') |
Shinichiro Hamaji | 21a9b5f | 2015-04-01 02:42:59 +0900 | [diff] [blame] | 142 | if index < 0 { |
Shinichiro Hamaji | 5c53b57 | 2015-04-02 05:36:42 +0900 | [diff] [blame] | 143 | r.parseInputs(rest) |
Fumitoshi Ukai | 953ce6f | 2015-04-04 00:38:53 +0900 | [diff] [blame] | 144 | return nil, nil |
Shinichiro Hamaji | 21a9b5f | 2015-04-01 02:42:59 +0900 | [diff] [blame] | 145 | } |
| 146 | |
| 147 | // %.x: %.y: %.z |
| 148 | if isFirstPattern { |
Fumitoshi Ukai | 953ce6f | 2015-04-04 00:38:53 +0900 | [diff] [blame] | 149 | return nil, errors.New("*** mixed implicit and normal rules: deprecated syntax") |
Shinichiro Hamaji | 21a9b5f | 2015-04-01 02:42:59 +0900 | [diff] [blame] | 150 | } |
| 151 | |
| 152 | second := rest[:index] |
| 153 | third := rest[index+1:] |
| 154 | |
Fumitoshi Ukai | 0b9e813 | 2015-04-30 10:20:18 +0900 | [diff] [blame^] | 155 | // r.outputs is already set. |
| 156 | outputPatterns := splitSpacesBytes(second) |
Fumitoshi Ukai | 935de96 | 2015-04-28 17:08:20 +0900 | [diff] [blame] | 157 | if len(outputPatterns) == 0 { |
Fumitoshi Ukai | 953ce6f | 2015-04-04 00:38:53 +0900 | [diff] [blame] | 158 | return nil, errors.New("*** missing target pattern.") |
Shinichiro Hamaji | 21a9b5f | 2015-04-01 02:42:59 +0900 | [diff] [blame] | 159 | } |
Fumitoshi Ukai | 935de96 | 2015-04-28 17:08:20 +0900 | [diff] [blame] | 160 | if len(outputPatterns) > 1 { |
Fumitoshi Ukai | 953ce6f | 2015-04-04 00:38:53 +0900 | [diff] [blame] | 161 | return nil, errors.New("*** multiple target patterns.") |
Shinichiro Hamaji | 21a9b5f | 2015-04-01 02:42:59 +0900 | [diff] [blame] | 162 | } |
Fumitoshi Ukai | 935de96 | 2015-04-28 17:08:20 +0900 | [diff] [blame] | 163 | outpat, ok := isPatternRule(outputPatterns[0]) |
| 164 | if !ok { |
Fumitoshi Ukai | 953ce6f | 2015-04-04 00:38:53 +0900 | [diff] [blame] | 165 | return nil, errors.New("*** target pattern contains no '%'.") |
Shinichiro Hamaji | 21a9b5f | 2015-04-01 02:42:59 +0900 | [diff] [blame] | 166 | } |
Fumitoshi Ukai | 935de96 | 2015-04-28 17:08:20 +0900 | [diff] [blame] | 167 | r.outputPatterns = []pattern{outpat} |
Shinichiro Hamaji | 5c53b57 | 2015-04-02 05:36:42 +0900 | [diff] [blame] | 168 | r.parseInputs(third) |
Shinichiro Hamaji | 21a9b5f | 2015-04-01 02:42:59 +0900 | [diff] [blame] | 169 | |
Fumitoshi Ukai | 953ce6f | 2015-04-04 00:38:53 +0900 | [diff] [blame] | 170 | return nil, nil |
Shinichiro Hamaji | 0493261 | 2015-03-31 23:46:56 +0900 | [diff] [blame] | 171 | } |