-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathnode_string.go
More file actions
137 lines (109 loc) · 2.97 KB
/
Copy pathnode_string.go
File metadata and controls
137 lines (109 loc) · 2.97 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
package libparser
import (
"fmt"
"strings"
)
type NodeString struct {
Segments SegmentedString
NodeContext
}
func (node *NodeString) Context() NodeContext {
return node.NodeContext
}
func (node *NodeString) String() string {
value := node.Segments.String()
if ShouldStringBeQuoted(value) {
return fmt.Sprintf("\"%s\"", node.Segments.String())
}
return value
}
func (node *NodeString) Eval(locals Locals) (string, error) {
var builder strings.Builder
for _, segment := range node.Segments {
part, err := segment.Eval(locals)
if err != nil {
return "", err
}
builder.WriteString(part)
}
return builder.String(), nil
}
func NewSimpleNodeString(contents string) *NodeString {
return &NodeString{
Segments: SegmentedString{
&LiteralStringSegment{
Contents: contents,
},
},
}
}
// ————————————————————————————————
type StringSegment interface {
Segment() string
Eval(Locals) (string, error)
}
// ————————————————————————————————
type SegmentedString []StringSegment
func (segments SegmentedString) String() string {
var builder strings.Builder
for _, segment := range segments {
builder.WriteString(segment.Segment())
}
return builder.String()
}
// ————————————————————————————————
type LiteralStringSegment struct {
Contents string
}
func (segment *LiteralStringSegment) Segment() string {
return segment.Contents
}
func (segment *LiteralStringSegment) Eval(_ Locals) (string, error) {
return segment.Contents, nil
}
// ————————————————————————————————
type VariableStringSegment struct {
Name string
Modifiers []StringModifier
IsOptional bool
}
func (segment *VariableStringSegment) Segment() string {
if len(segment.Modifiers) == 0 {
if segment.IsOptional {
return fmt.Sprintf("${%s?}", segment.Name)
}
return fmt.Sprintf("$%s", segment.Name)
}
name := segment.Name
if segment.IsOptional {
name += "?"
}
var builder strings.Builder
for _, modifier := range segment.Modifiers {
builder.WriteString(":" + modifier.String())
}
return fmt.Sprintf("${%s%s}", segment.Name, builder.String())
}
func (segment *VariableStringSegment) Eval(locals Locals) (string, error) {
value, exists := locals[segment.Name]
if !exists {
if segment.IsOptional {
return "", nil
}
return segment.Name, fmt.Errorf(
"variable %q is not defined in the current scope",
segment.Name,
)
}
for _, modifier := range segment.Modifiers {
value = modifier.Call(locals, value)
}
return value, nil
}
// ————————————————————————————————
func ShouldStringBeQuoted(value string) bool {
if strings.HasPrefix(value, "${") && strings.HasSuffix(value, "}") {
return false
}
return strings.ContainsAny(value, " \"'`[]{}")
}