Skip to content

Commit db77d4d

Browse files
committed
Initial import of some basics of a Parser.
0 parents  commit db77d4d

File tree

5 files changed

+604
-0
lines changed

5 files changed

+604
-0
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
*swp

dependency/models.go

+69
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
/* Copyright (c) Paul R. Tagliamonte <paultag@debian.org>, 2015
2+
*
3+
* Permission is hereby granted, free of charge, to any person obtaining a copy
4+
* of this software and associated documentation files (the "Software"), to deal
5+
* in the Software without restriction, including without limitation the rights
6+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7+
* copies of the Software, and to permit persons to whom the Software is
8+
* furnished to do so, subject to the following conditions:
9+
*
10+
* The above copyright notice and this permission notice shall be included in
11+
* all copies or substantial portions of the Software.
12+
*
13+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19+
* THE SOFTWARE. */
20+
21+
package dependency
22+
23+
/*
24+
*/
25+
type Arch struct {
26+
Not bool
27+
Name string
28+
}
29+
30+
/*
31+
*/
32+
type VersionRelation struct {
33+
Number string
34+
Operator string
35+
}
36+
37+
/*
38+
*/
39+
type Stage struct {
40+
Not bool
41+
Name string
42+
}
43+
44+
/*
45+
*/
46+
type StageSet struct {
47+
Stages []*Stage
48+
}
49+
50+
/*
51+
*/
52+
type Possibility struct {
53+
Name string
54+
Arches []Arch
55+
Stages *StageSet
56+
Version *VersionRelation
57+
}
58+
59+
/*
60+
*/
61+
type Relation struct {
62+
Possibilities []*Possibility
63+
}
64+
65+
/*
66+
*/
67+
type Depedency struct {
68+
Relations []*Relation
69+
}

dependency/parser.go

+319
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,319 @@
1+
/* Copyright (c) Paul R. Tagliamonte <paultag@debian.org>, 2015
2+
*
3+
* Permission is hereby granted, free of charge, to any person obtaining a copy
4+
* of this software and associated documentation files (the "Software"), to deal
5+
* in the Software without restriction, including without limitation the rights
6+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7+
* copies of the Software, and to permit persons to whom the Software is
8+
* furnished to do so, subject to the following conditions:
9+
*
10+
* The above copyright notice and this permission notice shall be included in
11+
* all copies or substantial portions of the Software.
12+
*
13+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19+
* THE SOFTWARE. */
20+
21+
package dependency
22+
23+
import (
24+
"errors"
25+
"fmt"
26+
)
27+
28+
/*
29+
*/
30+
func Parse(in string) (*Depedency, error) {
31+
ibuf := Input{Index: 0, Data: in}
32+
dep := &Depedency{Relations: []*Relation{}}
33+
err := parseDependency(&ibuf, dep)
34+
if err != nil {
35+
return nil, err
36+
}
37+
return dep, nil
38+
}
39+
40+
/*
41+
*/
42+
type Input struct {
43+
Data string
44+
Index int
45+
}
46+
47+
/*
48+
*/
49+
func (i *Input) Peek() byte {
50+
if (i.Index) >= len(i.Data) {
51+
return 0
52+
}
53+
return i.Data[i.Index]
54+
}
55+
56+
/*
57+
*/
58+
func (i *Input) Next() byte {
59+
chr := i.Peek()
60+
i.Index++
61+
return chr
62+
}
63+
64+
/* Depedency (foo, bar | baz)
65+
* | Relation (bar | baz)
66+
* | Possibility (baz (>= 1.0))
67+
* | Archs
68+
* | Stages
69+
* | Stage
70+
* | Version
71+
*
72+
* Dependency State -> {Relation}
73+
* Relation State -> {Possi}
74+
* Possi State -> {Arch,Version,Stage}
75+
* Arch State -> {Possi}
76+
* Version State -> {Possi}
77+
* Stage State -> {Possi} */
78+
79+
/* */
80+
func eatWhitespace(input *Input) {
81+
for {
82+
peek := input.Peek()
83+
switch peek {
84+
case '\r', '\n', ' ', '\t':
85+
input.Next()
86+
continue
87+
}
88+
break
89+
}
90+
}
91+
92+
/* */
93+
func parseDependency(input *Input, ret *Depedency) error {
94+
eatWhitespace(input)
95+
96+
for {
97+
peek := input.Peek()
98+
switch peek {
99+
case 0: /* EOF, yay */
100+
return nil
101+
case ',': /* Next relation set */
102+
input.Next()
103+
eatWhitespace(input)
104+
continue
105+
}
106+
err := parseRelation(input, ret)
107+
if err != nil {
108+
return err
109+
}
110+
}
111+
}
112+
113+
/* */
114+
func parseRelation(input *Input, dependency *Depedency) error {
115+
eatWhitespace(input) /* Clean out leading whitespace */
116+
117+
ret := &Relation{Possibilities: []*Possibility{}}
118+
119+
for {
120+
peek := input.Peek()
121+
switch peek {
122+
case 0, ',': /* EOF, or done with this relation! yay */
123+
dependency.Relations = append(dependency.Relations, ret)
124+
return nil
125+
case '|': /* Next Possi */
126+
input.Next()
127+
eatWhitespace(input)
128+
continue
129+
}
130+
err := parsePossibility(input, ret)
131+
if err != nil {
132+
return err
133+
}
134+
}
135+
}
136+
137+
/* */
138+
func parsePossibility(input *Input, relation *Relation) error {
139+
eatWhitespace(input) /* Clean out leading whitespace */
140+
ret := &Possibility{
141+
Name: "",
142+
Version: nil,
143+
Arches: []Arch{},
144+
Stages: &StageSet{Stages: []*Stage{}},
145+
}
146+
147+
for {
148+
peek := input.Peek()
149+
switch peek {
150+
case ' ':
151+
err := parsePossibilityControllers(input, ret)
152+
if err != nil {
153+
return err
154+
}
155+
continue
156+
case ',', '|', 0: /* I'm out! */
157+
if ret.Name == "" {
158+
return errors.New("No package name in Possibility")
159+
}
160+
relation.Possibilities = append(relation.Possibilities, ret)
161+
return nil
162+
}
163+
/* Not a control, let's append */
164+
ret.Name += string(input.Next())
165+
}
166+
}
167+
168+
/* */
169+
func parsePossibilityControllers(input *Input, possi *Possibility) error {
170+
for {
171+
eatWhitespace(input) /* Clean out leading whitespace */
172+
peek := input.Peek()
173+
switch peek {
174+
case ',', '|', 0:
175+
return nil
176+
case '(':
177+
if possi.Version != nil {
178+
return errors.New(
179+
"Only one Version relation per Possibility, please!",
180+
)
181+
}
182+
err := parsePossibilityVersion(input, possi)
183+
if err != nil {
184+
return err
185+
}
186+
case '[':
187+
if len(possi.Arches) != 0 {
188+
return errors.New(
189+
"Only one Arch relation per Possibility, please!",
190+
)
191+
}
192+
err := parsePossibilityArchs(input, possi)
193+
if err != nil {
194+
return err
195+
}
196+
}
197+
}
198+
return nil
199+
}
200+
201+
/* */
202+
func parsePossibilityVersion(input *Input, possi *Possibility) error {
203+
eatWhitespace(input)
204+
input.Next() /* mandated to be ( */
205+
// assert ch == '('
206+
version := VersionRelation{}
207+
208+
err := parsePossibilityOperator(input, &version)
209+
if err != nil {
210+
return err
211+
}
212+
213+
err = parsePossibilityNumber(input, &version)
214+
if err != nil {
215+
return err
216+
}
217+
218+
input.Next() /* OK, let's tidy up */
219+
// assert ch == ')'
220+
221+
possi.Version = &version
222+
return nil
223+
}
224+
225+
/* */
226+
func parsePossibilityOperator(input *Input, version *VersionRelation) error {
227+
eatWhitespace(input)
228+
leader := input.Next() /* may be 0 */
229+
230+
if leader == '=' {
231+
/* Great, good enough. */
232+
version.Operator = "="
233+
return nil
234+
}
235+
236+
/* This is always one of:
237+
* >=, <=, <<, >> */
238+
secondary := input.Next()
239+
if leader == 0 || secondary == 0 {
240+
return errors.New("Oh no. Reached EOF before Operator finished")
241+
}
242+
243+
operator := string([]rune{rune(leader), rune(secondary)})
244+
245+
switch operator {
246+
case ">=", "<=", "<<", ">>":
247+
version.Operator = operator
248+
return nil
249+
}
250+
251+
return fmt.Errorf(
252+
"Unknown Operator in Possibility Version modifier: %s",
253+
operator,
254+
)
255+
256+
}
257+
258+
/* */
259+
func parsePossibilityNumber(input *Input, version *VersionRelation) error {
260+
eatWhitespace(input)
261+
for {
262+
peek := input.Peek()
263+
switch peek {
264+
case 0:
265+
return errors.New("Oh no. Reached EOF before Number finished")
266+
case ')':
267+
return nil
268+
}
269+
version.Number += string(input.Next())
270+
}
271+
}
272+
273+
/* */
274+
func parsePossibilityArchs(input *Input, possi *Possibility) error {
275+
eatWhitespace(input)
276+
input.Next() /* Assert ch == '[' */
277+
for {
278+
peek := input.Peek()
279+
switch peek {
280+
case 0:
281+
return errors.New("Oh no. Reached EOF before Arch list finished")
282+
case ']':
283+
input.Next()
284+
return nil
285+
}
286+
287+
err := parsePossibilityArch(input, possi)
288+
if err != nil {
289+
return err
290+
}
291+
}
292+
}
293+
294+
/* */
295+
func parsePossibilityArch(input *Input, possi *Possibility) error {
296+
eatWhitespace(input)
297+
arch := Arch{Name: "", Not: false}
298+
299+
/* So the first line of each guy can be a not (!), so let's check for
300+
* that with a Peek :) */
301+
peek := input.Peek()
302+
if peek == '!' {
303+
input.Next() /* Omnom */
304+
arch.Not = true
305+
}
306+
307+
for {
308+
peek := input.Peek()
309+
switch peek {
310+
case 0:
311+
return errors.New("Oh no. Reached EOF before Arch list finished")
312+
case ']', ' ': /* Let our parent deal with both of these */
313+
/* Validate name */
314+
possi.Arches = append(possi.Arches, arch)
315+
return nil
316+
}
317+
arch.Name += string(input.Next())
318+
}
319+
}

0 commit comments

Comments
 (0)