-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathparser.go
More file actions
172 lines (149 loc) · 3.83 KB
/
parser.go
File metadata and controls
172 lines (149 loc) · 3.83 KB
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
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
// Parser includes a simple parsing function to translate signature syntax into
// raw signatures. NOTE: it does NOT
package dice
import (
"io"
"slices"
"strings"
"github.com/alecthomas/participle/v2"
"github.com/dice/pkg/ast"
"github.com/pkg/errors"
)
type Parser interface {
// Parse signature syntax into a raw signature. NOTE: does not
// validate the signature, does not load modules, and does not
// check for validity. It is a simple translating function.
// Use a dice.SignaturesAdapter instead.
Parse(string, io.Reader) (*Signature, error)
}
type parser struct {
parser *participle.Parser[ast.Signature]
}
func NewParser() *parser {
p := participle.MustBuild[ast.Signature](
participle.Unquote("String"),
participle.Union[ast.Value](ast.String{}, ast.Number{}, ast.List{}),
)
return &parser{parser: p}
}
func (p *parser) Parse(fname string, r io.Reader) (*Signature, error) {
s, err := p.parser.Parse(fname, r)
if err != nil {
return nil, err
}
sig := Signature{
Name: strings.TrimSuffix(fname, ".dice"),
Component: "classifier",
}
if err := p.bind(s, &sig); err != nil {
return nil, errors.Wrapf(err, "failed to bind signature '%s'", fname)
}
return &sig, nil
}
// Converts the AST signature to an actual Signature object
func (p *parser) bind(s *ast.Signature, sig *Signature) error {
// get the type of the signature. ignore the rest
for _, prop := range s.Properties {
switch prop.Key {
case "component":
if v, ok := prop.Value.(ast.String); ok {
sig.Component = v.String
}
}
}
// bind the nodes
nodes, err := p.collectNodes(s.Nodes)
if err != nil {
return err
}
sig.Nodes = nodes
return nil
}
// Convert an ast.Node to a regular Node
// NOTE: this process does NOT validate the nodes!
func (p *parser) collectNodes(nodes []*ast.Node) ([]*Node, error) {
nList := []*Node{}
for _, n := range nodes {
var node Node
if err := p.bindNode(n, &node, nList); err != nil {
return nil, err
}
nList = append(nList, &node)
}
return nList, nil
}
func (p *parser) bindNode(n *ast.Node, node *Node, reg []*Node) error {
node.name = n.Name
switch n.Type {
case "mod":
node.Type = MODULE_NODE
case "sig":
node.Type = SIGNATURE_NODE
default:
return errors.Errorf("failed to bind node type '%s'", node.Type)
}
if err := p.bindNodeAttributes(n.Attributes, node, reg); err != nil {
return errors.Wrapf(err, "failed to bind node '%s' attributes", n.Name)
}
return nil
}
func (p *parser) bindNodeAttributes(attrs []*ast.Attribute, node *Node, nodes []*Node) error {
for _, attr := range attrs {
if val, ok := attr.Value.(ast.List); ok {
// ignore the rest for now
if !isNodeType(attr.Key) {
continue
}
t := attrToNodeType(attr.Key)
if err := p.bindNodeParents(t, val.List, node, nodes); err != nil {
return err
}
}
}
return nil
}
func (p *parser) bindNodeParents(t NodeType, names []string, n *Node, reg []*Node) error {
slices.Sort(names)
filtNodes := Filter(reg, func(n *Node) bool { return n.Type == t })
slices.SortFunc(filtNodes, func(a *Node, b *Node) int {
if a.name < b.name {
return -1
}
if a.name > b.name {
return 1
}
return 0
})
lastIndex := 0
for _, name := range names {
var parent *Node
for j := lastIndex; lastIndex < len(filtNodes); j++ {
node := filtNodes[j]
if node.name != name {
continue
}
parent = node
lastIndex = j
break
}
if parent != nil {
parent.Children = append(parent.Children, n)
continue
}
return errors.Errorf("failed to bind node to parent. Parent not found: '%s'", name)
}
return nil
}
func isNodeType(k string) bool {
return slices.Contains([]string{"mod", "sig"}, k)
}
func attrToNodeType(k string) NodeType {
switch k {
case "mod":
return MODULE_NODE
case "sig":
return SIGNATURE_NODE
default:
panic(errors.Errorf("attribute %s is not a type of node", k))
}
}