1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
|
package main
import (
"errors"
"strings"
)
type Rule struct {
outputs []string
inputs []string
orderOnlyInputs []string
outputPatterns []string
isDoubleColon bool
isSuffixRule bool
vars *VarTab
cmds []string
filename string
lineno int
cmdLineno int
}
func isPatternRule(s string) bool {
return strings.IndexByte(s, '%') >= 0
}
func (r *Rule) parseInputs(s string) {
inputs := splitSpaces(s)
isOrderOnly := false
for _, input := range inputs {
if input == "|" {
isOrderOnly = true
continue
}
if isOrderOnly {
r.orderOnlyInputs = append(r.orderOnlyInputs, input)
} else {
r.inputs = append(r.inputs, input)
}
}
}
func (r *Rule) parseVar(s string) *AssignAST {
eq := strings.IndexByte(s, '=')
if eq <= 0 {
return nil
}
assign := &AssignAST{
rhs: strings.TrimLeft(s[eq+1:], " \t"),
}
assign.filename = r.filename
assign.lineno = r.lineno
// TODO(ukai): support override, export.
switch s[eq-1 : eq] {
case ":=":
assign.lhs = strings.TrimSpace(s[:eq-1])
assign.op = ":="
case "+=":
assign.lhs = strings.TrimSpace(s[:eq-1])
assign.op = "+="
case "?=":
assign.lhs = strings.TrimSpace(s[:eq-1])
assign.op = "?="
default:
assign.lhs = strings.TrimSpace(s[:eq])
assign.op = "="
}
return assign
}
func (r *Rule) parse(line string) (*AssignAST, error) {
index := strings.IndexByte(line, ':')
if index < 0 {
return nil, errors.New("*** missing separator.")
}
first := line[:index]
outputs := splitSpaces(first)
isFirstPattern := isPatternRule(first)
if isFirstPattern {
if len(outputs) > 1 {
return nil, errors.New("*** mixed implicit and normal rules: deprecated syntax")
}
r.outputPatterns = outputs
} else {
r.outputs = outputs
}
index++
if index < len(line) && line[index] == ':' {
r.isDoubleColon = true
index++
}
rest := line[index:]
if assign := r.parseVar(rest); assign != nil {
return assign, nil
}
index = strings.IndexByte(rest, ':')
if index < 0 {
r.parseInputs(rest)
return nil, nil
}
// %.x: %.y: %.z
if isFirstPattern {
return nil, errors.New("*** mixed implicit and normal rules: deprecated syntax")
}
second := rest[:index]
third := rest[index+1:]
r.outputs = outputs
r.outputPatterns = splitSpaces(second)
if len(r.outputPatterns) == 0 {
return nil, errors.New("*** missing target pattern.")
}
if len(r.outputPatterns) > 1 {
return nil, errors.New("*** multiple target patterns.")
}
if !isPatternRule(r.outputPatterns[0]) {
return nil, errors.New("*** target pattern contains no '%'.")
}
r.parseInputs(third)
return nil, nil
}
|