@@ -21,8 +21,6 @@ type Constraint interface {
2121
2222// ParseConstraint converts a string into a Constraint. The resulting Constraint
2323// 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
2624func ParseConstraint (in string ) (Constraint , error ) {
2725 in = strings .TrimSpace (in )
2826 curr := 0
@@ -37,14 +35,19 @@ func ParseConstraint(in string) (Constraint, error) {
3735 }
3836 return 0
3937 }
38+ skipSpace := func () {
39+ for curr < l && in [curr ] == ' ' {
40+ curr ++
41+ }
42+ }
4043 peek := func () byte {
4144 if curr < l {
4245 return in [curr ]
4346 }
4447 return 0
4548 }
4649
47- ver := func () (* Version , error ) {
50+ version := func () (* Version , error ) {
4851 start := curr
4952 for {
5053 n := peek ()
@@ -58,56 +61,134 @@ func ParseConstraint(in string) (Constraint, error) {
5861 }
5962 }
6063
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 ()
6369 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
6486 case '=' :
65- if v , err := ver (); err == nil {
66- stack = append (stack , & Equals {v })
67- } else {
87+ v , err := version ()
88+ if err != nil {
6889 return nil , err
6990 }
91+ return & Equals {v }, nil
7092 case '>' :
7193 if peek () == '=' {
7294 next ()
73- if v , err := ver (); err == nil {
74- stack = append (stack , & GreaterThanOrEqual {v })
75- } else {
95+ v , err := version ()
96+ if err != nil {
7697 return nil , err
7798 }
99+ return & GreaterThanOrEqual {v }, nil
78100 } else {
79- if v , err := ver (); err == nil {
80- stack = append (stack , & GreaterThan {v })
81- } else {
101+ v , err := version ()
102+ if err != nil {
82103 return nil , err
83104 }
105+ return & GreaterThan {v }, nil
84106 }
85107 case '<' :
86108 if peek () == '=' {
87109 next ()
88- if v , err := ver (); err == nil {
89- stack = append (stack , & LessThanOrEqual {v })
90- } else {
110+ v , err := version ()
111+ if err != nil {
91112 return nil , err
92113 }
114+ return & LessThanOrEqual {v }, nil
93115 } else {
94- if v , err := ver (); err == nil {
95- stack = append (stack , & LessThan {v })
96- } else {
116+ v , err := version ()
117+ if err != nil {
97118 return nil , err
98119 }
120+ return & LessThan {v }, nil
99121 }
100- case ' ' :
101- // ignore
102122 default :
103123 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
107151 }
108- return stack [ 0 ], nil
152+ stack = append ( stack , t2 )
109153 }
110154 }
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 ()
111192}
112193
113194// True is the empty constraint
@@ -259,7 +340,7 @@ func (not *Not) Match(v *Version) bool {
259340
260341func (not * Not ) String () string {
261342 op := not .Operand .String ()
262- if op [0 ] != '(' {
343+ if op == "" || op [0 ] != '(' {
263344 return "!(" + op + ")"
264345 }
265346 return "!" + op
0 commit comments