-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathrules.go
105 lines (91 loc) · 2.14 KB
/
rules.go
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
package denada
import "fmt"
import "strings"
type Cardinality int
const (
Zero = iota
Optional
ZeroOrMore
Singleton
OneOrMore
)
type RuleInfo struct {
ContextPath []string
Context RuleContext
Name string
Cardinality Cardinality
}
func (r RuleInfo) checkCount(count int) error {
switch r.Cardinality {
case Zero:
if count != 0 {
return fmt.Errorf("Expected zero of rule %s, found %d", r.Name, count)
}
case Optional:
if count > 1 {
return fmt.Errorf("Expected at most 1 of rule %s, found %d", r.Name, count)
}
case Singleton:
if count != 1 {
return fmt.Errorf("Expected at exactly 1 of rule %s, found %d", r.Name, count)
}
case OneOrMore:
if count == 0 {
return fmt.Errorf("Expected at least 1 of rule %s, found %d", r.Name, count)
}
}
return nil
}
func ParseRuleName(desc string) (rule RuleInfo, err error) {
return ParseRule(desc, NullContext())
}
func ParseRule(desc string, context RuleContext) (rule RuleInfo, err error) {
rule = RuleInfo{Cardinality: Zero}
// Note a rule is of the form "myrule>childrule". If no ">" is present,
// child rules are assumed to be indicated by the "contents" of the current
// rule.
parts := strings.Split(desc, ">")
str := desc
path := []string{"."}
if len(parts) == 0 {
err = fmt.Errorf("Empty rule string")
return
} else if len(parts) == 2 {
str = parts[0]
path = strings.Split(parts[1], "/")
} else if len(parts) > 2 {
err = fmt.Errorf("Rule contains multiple child rule indicators (>)")
return
}
// Shorthand notation
if str[0] == '^' {
path = []string{".."}
str = str[1:]
}
rctxt, ferr := context.Find(path...)
if ferr != nil {
err = fmt.Errorf("Error finding %v: %v", path, ferr)
return
}
rule.Context = rctxt
rule.ContextPath = path
l := len(str) - 1
lastchar := str[l]
if lastchar == '-' {
rule.Cardinality = Zero
str = str[0:l]
} else if lastchar == '+' {
rule.Cardinality = OneOrMore
str = str[0:l]
} else if lastchar == '*' {
rule.Cardinality = ZeroOrMore
str = str[0:l]
} else if lastchar == '?' {
rule.Cardinality = Optional
str = str[0:l]
} else {
rule.Cardinality = Singleton
}
rule.Name = str
return
}