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
|
package ast
import (
"fmt"
"strconv"
"go.neonxp.ru/conf/internal/parser"
"go.neonxp.ru/conf/model"
)
func ToDoc(config *Node) (*model.Doc, error) {
if len(config.Children) < 1 {
return nil, fmt.Errorf("invalid ast tree")
}
doc := config.Children[0]
return processDoc(doc), nil
}
func processDoc(docNode *Node) *model.Doc {
doc := model.New(len(docNode.Children))
for _, stmt := range docNode.Children {
processStmt(doc, stmt)
}
return doc
}
func processStmt(doc *model.Doc, stmt *Node) {
ident := extractIdent(stmt.Children[0])
nodeBody := stmt.Children[1]
switch nodeBody.Symbol {
case parser.Command:
doc.AppendCommand(processCommand(ident, nodeBody))
case parser.Assignment:
doc.AppendAssignment(processAssignment(ident, nodeBody))
}
}
func processCommand(ident string, command *Node) *model.Command {
result := &model.Command{
Name: ident,
}
for _, child := range command.Children {
// Can be arguments OR body OR both
switch child.Symbol {
case parser.Values:
result.Arguments = extractValues(child)
case parser.Body:
// Children[0] = '{', Children[1] = Body, Children[2] = '}'
result.Body = processDoc(child.Children[1])
}
}
return result
}
func processAssignment(ident string, assignment *Node) *model.Assignment {
result := &model.Assignment{
Key: ident,
Value: extractValues(assignment.Children[1]), // Children[0] = '=', Children[1] = Values, Children[2] = ';'
}
return result
}
func extractIdent(word *Node) string {
return word.Children[0].Source
}
func extractValues(args *Node) []model.Value {
result := make([]model.Value, len(args.Children))
for i, child := range args.Children {
v, err := extractValue(child)
if err != nil {
result[i] = err
continue
}
result[i] = v
}
return result
}
func extractValue(value *Node) (any, error) {
v := value.Children[0]
s := v.Children[0].Source
switch v.Symbol {
case parser.Word:
return model.Word(s), nil
case parser.String:
return unquote(s), nil
case parser.Number:
d, err := strconv.Atoi(s)
if err == nil {
return d, nil
}
fl, err := strconv.ParseFloat(s, 32)
if err == nil {
return fl, nil
}
return nil, fmt.Errorf("invalid number: %s (%s)", v.Source, s)
case parser.Boolean:
return s == "true", nil
default:
return nil, fmt.Errorf("unknown value type: %s (%s)", v.Symbol, s)
}
}
func unquote(str string) string {
if len(str) == 0 {
return ""
}
if str[0:1] == `"` || str[0:1] == "'" || str[0:1] == "`" {
return str[1 : len(str)-1]
}
return str
}
|