@@ -21,8 +21,6 @@ type Constraint interface {
21
21
22
22
// ParseConstraint converts a string into a Constraint. The resulting Constraint
23
23
// may be converted back to string using the String() method.
24
- // WIP: only simple constraint (like ""=1.2.0" or ">=2.0.0) are parsed for now
25
- // a full parser will be deployed in the future
26
24
func ParseConstraint (in string ) (Constraint , error ) {
27
25
in = strings .TrimSpace (in )
28
26
curr := 0
@@ -37,14 +35,19 @@ func ParseConstraint(in string) (Constraint, error) {
37
35
}
38
36
return 0
39
37
}
38
+ skipSpace := func () {
39
+ for curr < l && in [curr ] == ' ' {
40
+ curr ++
41
+ }
42
+ }
40
43
peek := func () byte {
41
44
if curr < l {
42
45
return in [curr ]
43
46
}
44
47
return 0
45
48
}
46
49
47
- ver := func () (* Version , error ) {
50
+ version := func () (* Version , error ) {
48
51
start := curr
49
52
for {
50
53
n := peek ()
@@ -58,56 +61,134 @@ func ParseConstraint(in string) (Constraint, error) {
58
61
}
59
62
}
60
63
61
- stack := []Constraint {}
62
- for {
64
+ var terminal func () (Constraint , error )
65
+ var constraint func () (Constraint , error )
66
+
67
+ terminal = func () (Constraint , error ) {
68
+ skipSpace ()
63
69
switch next () {
70
+ case '!' :
71
+ expr , err := terminal ()
72
+ if err != nil {
73
+ return nil , err
74
+ }
75
+ return & Not {expr }, nil
76
+ case '(' :
77
+ expr , err := constraint ()
78
+ if err != nil {
79
+ return nil , err
80
+ }
81
+ skipSpace ()
82
+ if c := next (); c != ')' {
83
+ return nil , fmt .Errorf ("unexpected char at: %s" , in [curr - 1 :])
84
+ }
85
+ return expr , nil
64
86
case '=' :
65
- if v , err := ver (); err == nil {
66
- stack = append (stack , & Equals {v })
67
- } else {
87
+ v , err := version ()
88
+ if err != nil {
68
89
return nil , err
69
90
}
91
+ return & Equals {v }, nil
70
92
case '>' :
71
93
if peek () == '=' {
72
94
next ()
73
- if v , err := ver (); err == nil {
74
- stack = append (stack , & GreaterThanOrEqual {v })
75
- } else {
95
+ v , err := version ()
96
+ if err != nil {
76
97
return nil , err
77
98
}
99
+ return & GreaterThanOrEqual {v }, nil
78
100
} else {
79
- if v , err := ver (); err == nil {
80
- stack = append (stack , & GreaterThan {v })
81
- } else {
101
+ v , err := version ()
102
+ if err != nil {
82
103
return nil , err
83
104
}
105
+ return & GreaterThan {v }, nil
84
106
}
85
107
case '<' :
86
108
if peek () == '=' {
87
109
next ()
88
- if v , err := ver (); err == nil {
89
- stack = append (stack , & LessThanOrEqual {v })
90
- } else {
110
+ v , err := version ()
111
+ if err != nil {
91
112
return nil , err
92
113
}
114
+ return & LessThanOrEqual {v }, nil
93
115
} else {
94
- if v , err := ver (); err == nil {
95
- stack = append (stack , & LessThan {v })
96
- } else {
116
+ v , err := version ()
117
+ if err != nil {
97
118
return nil , err
98
119
}
120
+ return & LessThan {v }, nil
99
121
}
100
- case ' ' :
101
- // ignore
102
122
default :
103
123
return nil , fmt .Errorf ("unexpected char at: %s" , in [curr - 1 :])
104
- case 0 :
105
- if len (stack ) != 1 {
106
- return nil , fmt .Errorf ("invalid constraint: %s" , in )
124
+ }
125
+ }
126
+
127
+ andExpr := func () (Constraint , error ) {
128
+ t1 , err := terminal ()
129
+ if err != nil {
130
+ return nil , err
131
+ }
132
+ stack := []Constraint {t1 }
133
+
134
+ for {
135
+ skipSpace ()
136
+ if peek () != '&' {
137
+ if len (stack ) == 1 {
138
+ return stack [0 ], nil
139
+ }
140
+ return & And {stack }, nil
141
+ }
142
+ next ()
143
+ if peek () != '&' {
144
+ return nil , fmt .Errorf ("unexpected char at: %s" , in [curr - 1 :])
145
+ }
146
+ next ()
147
+
148
+ t2 , err := terminal ()
149
+ if err != nil {
150
+ return nil , err
107
151
}
108
- return stack [ 0 ], nil
152
+ stack = append ( stack , t2 )
109
153
}
110
154
}
155
+
156
+ constraint = func () (Constraint , error ) {
157
+ t1 , err := andExpr ()
158
+ if err != nil {
159
+ return nil , err
160
+ }
161
+ stack := []Constraint {t1 }
162
+
163
+ for {
164
+ skipSpace ()
165
+ switch peek () {
166
+ case '|' :
167
+ next ()
168
+ if peek () != '|' {
169
+ return nil , fmt .Errorf ("unexpected char at: %s" , in [curr - 1 :])
170
+ }
171
+ next ()
172
+
173
+ t2 , err := andExpr ()
174
+ if err != nil {
175
+ return nil , err
176
+ }
177
+ stack = append (stack , t2 )
178
+
179
+ case 0 , ')' :
180
+ if len (stack ) == 1 {
181
+ return stack [0 ], nil
182
+ }
183
+ return & Or {stack }, nil
184
+
185
+ default :
186
+ return nil , fmt .Errorf ("unexpected char at: %s" , in [curr - 1 :])
187
+ }
188
+ }
189
+ }
190
+
191
+ return constraint ()
111
192
}
112
193
113
194
// True is the empty constraint
@@ -259,7 +340,7 @@ func (not *Not) Match(v *Version) bool {
259
340
260
341
func (not * Not ) String () string {
261
342
op := not .Operand .String ()
262
- if op [0 ] != '(' {
343
+ if op == "" || op [0 ] != '(' {
263
344
return "!(" + op + ")"
264
345
}
265
346
return "!" + op
0 commit comments