-
Notifications
You must be signed in to change notification settings - Fork 27
/
Copy pathrules.go
149 lines (126 loc) · 3.2 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
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
// Copyright (c) 2019, The Emergent Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package esg
import (
"fmt"
)
// Rules is a collection of rules
type Rules struct { //git:add
// name of this rule collection
Name string
// description of this rule collection
Desc string
// if true, will print out a trace during generation
Trace bool
// top-level rule -- this is where to start generating
Top *Rule
// map of each rule
Map map[string]*Rule
// map of names of all the rules that have fired
Fired map[string]bool
// array of output strings -- appended as the rules generate output
Output []string
// user-defined state map optionally created during generation
States State
// errors from parsing
ParseErrs []error
// current line number during parsing
ParseLn int
}
// Gen generates one expression according to the rules.
// returns the token output, which is also avail as rls.Output
func (rls *Rules) Gen() []string {
rls.Fired = make(map[string]bool)
rls.States = make(State)
rls.Output = nil
if rls.Trace {
fmt.Printf("\n#########################\nRules: %v starting Gen\n", rls.Name)
}
rls.Top.Gen(rls)
return rls.Output
}
// String generates string representation of all rules
func (rls *Rules) String() string {
str := "Rules: " + rls.Name
if rls.Desc != "" {
str += ": " + rls.Desc
}
str += "\n"
for _, rl := range rls.Map {
str += rl.String()
}
return str
}
// Validate checks for config errors
func (rls *Rules) Validate() []error {
if len(rls.Map) == 0 {
return []error{fmt.Errorf("Rules: %v has no Rules", rls.Name)}
}
var errs []error
if rls.Top == nil {
errs = append(errs, fmt.Errorf("Rules: %v Top is nil", rls.Name))
}
for _, rl := range rls.Map {
ers := rl.Validate(rls)
if len(ers) > 0 {
errs = append(errs, ers...)
}
}
if len(errs) > 0 {
fmt.Printf("\nValidation errors:\n")
for _, err := range errs {
fmt.Println(err)
}
}
return errs
}
// Init initializes rule order state
func (rls *Rules) Init() {
rls.Top.Init()
for _, rl := range rls.Map {
rl.Init()
}
}
// Rule returns rule of given name, and error if not found
func (rls *Rules) Rule(name string) (*Rule, error) {
rl, ok := rls.Map[name]
if ok {
return rl, nil
}
return nil, fmt.Errorf("Rule: %v not found in Rules: %v", name, rls.Name)
}
// HasFired returns true if rule of given name has fired
func (rls *Rules) HasFired(name string) bool {
_, has := rls.Fired[name]
return has
}
// HasOutput returns true if given token is in the output string
// strips ' ' delimiters if present in out string
func (rls *Rules) HasOutput(out string) bool {
if out[0] == '\'' {
out = out[1 : len(out)-1]
}
for _, o := range rls.Output {
if o == out {
return true
}
}
return false
}
// SetFired adds given rule name to map of those that fired this round
func (rls *Rules) SetFired(name string) {
rls.Fired[name] = true
}
// AddOutput adds given string to Output array
func (rls *Rules) AddOutput(out string) {
rls.Output = append(rls.Output, out)
}
// Adds given rule
func (rls *Rules) Add(rl *Rule) {
if rls.Map == nil {
rls.Map = make(map[string]*Rule)
rls.Top = rl
}
rls.Map[rl.Name] = rl
}