]>
Commit | Line | Data |
---|---|---|
15c0b25d AP |
1 | package hclsyntax |
2 | ||
3 | import ( | |
4 | "fmt" | |
5 | ||
6 | "github.com/hashicorp/hcl2/hcl" | |
7 | "github.com/zclconf/go-cty/cty" | |
8 | "github.com/zclconf/go-cty/cty/convert" | |
9 | "github.com/zclconf/go-cty/cty/function" | |
10 | "github.com/zclconf/go-cty/cty/function/stdlib" | |
11 | ) | |
12 | ||
13 | type Operation struct { | |
14 | Impl function.Function | |
15 | Type cty.Type | |
16 | } | |
17 | ||
18 | var ( | |
19 | OpLogicalOr = &Operation{ | |
20 | Impl: stdlib.OrFunc, | |
21 | Type: cty.Bool, | |
22 | } | |
23 | OpLogicalAnd = &Operation{ | |
24 | Impl: stdlib.AndFunc, | |
25 | Type: cty.Bool, | |
26 | } | |
27 | OpLogicalNot = &Operation{ | |
28 | Impl: stdlib.NotFunc, | |
29 | Type: cty.Bool, | |
30 | } | |
31 | ||
32 | OpEqual = &Operation{ | |
33 | Impl: stdlib.EqualFunc, | |
34 | Type: cty.Bool, | |
35 | } | |
36 | OpNotEqual = &Operation{ | |
37 | Impl: stdlib.NotEqualFunc, | |
38 | Type: cty.Bool, | |
39 | } | |
40 | ||
41 | OpGreaterThan = &Operation{ | |
42 | Impl: stdlib.GreaterThanFunc, | |
43 | Type: cty.Bool, | |
44 | } | |
45 | OpGreaterThanOrEqual = &Operation{ | |
46 | Impl: stdlib.GreaterThanOrEqualToFunc, | |
47 | Type: cty.Bool, | |
48 | } | |
49 | OpLessThan = &Operation{ | |
50 | Impl: stdlib.LessThanFunc, | |
51 | Type: cty.Bool, | |
52 | } | |
53 | OpLessThanOrEqual = &Operation{ | |
54 | Impl: stdlib.LessThanOrEqualToFunc, | |
55 | Type: cty.Bool, | |
56 | } | |
57 | ||
58 | OpAdd = &Operation{ | |
59 | Impl: stdlib.AddFunc, | |
60 | Type: cty.Number, | |
61 | } | |
62 | OpSubtract = &Operation{ | |
63 | Impl: stdlib.SubtractFunc, | |
64 | Type: cty.Number, | |
65 | } | |
66 | OpMultiply = &Operation{ | |
67 | Impl: stdlib.MultiplyFunc, | |
68 | Type: cty.Number, | |
69 | } | |
70 | OpDivide = &Operation{ | |
71 | Impl: stdlib.DivideFunc, | |
72 | Type: cty.Number, | |
73 | } | |
74 | OpModulo = &Operation{ | |
75 | Impl: stdlib.ModuloFunc, | |
76 | Type: cty.Number, | |
77 | } | |
78 | OpNegate = &Operation{ | |
79 | Impl: stdlib.NegateFunc, | |
80 | Type: cty.Number, | |
81 | } | |
82 | ) | |
83 | ||
84 | var binaryOps []map[TokenType]*Operation | |
85 | ||
86 | func init() { | |
87 | // This operation table maps from the operator's token type | |
88 | // to the AST operation type. All expressions produced from | |
89 | // binary operators are BinaryOp nodes. | |
90 | // | |
91 | // Binary operator groups are listed in order of precedence, with | |
92 | // the *lowest* precedence first. Operators within the same group | |
93 | // have left-to-right associativity. | |
94 | binaryOps = []map[TokenType]*Operation{ | |
95 | { | |
96 | TokenOr: OpLogicalOr, | |
97 | }, | |
98 | { | |
99 | TokenAnd: OpLogicalAnd, | |
100 | }, | |
101 | { | |
102 | TokenEqualOp: OpEqual, | |
103 | TokenNotEqual: OpNotEqual, | |
104 | }, | |
105 | { | |
106 | TokenGreaterThan: OpGreaterThan, | |
107 | TokenGreaterThanEq: OpGreaterThanOrEqual, | |
108 | TokenLessThan: OpLessThan, | |
109 | TokenLessThanEq: OpLessThanOrEqual, | |
110 | }, | |
111 | { | |
112 | TokenPlus: OpAdd, | |
113 | TokenMinus: OpSubtract, | |
114 | }, | |
115 | { | |
116 | TokenStar: OpMultiply, | |
117 | TokenSlash: OpDivide, | |
118 | TokenPercent: OpModulo, | |
119 | }, | |
120 | } | |
121 | } | |
122 | ||
123 | type BinaryOpExpr struct { | |
124 | LHS Expression | |
125 | Op *Operation | |
126 | RHS Expression | |
127 | ||
128 | SrcRange hcl.Range | |
129 | } | |
130 | ||
131 | func (e *BinaryOpExpr) walkChildNodes(w internalWalkFunc) { | |
107c1cdb ND |
132 | w(e.LHS) |
133 | w(e.RHS) | |
15c0b25d AP |
134 | } |
135 | ||
136 | func (e *BinaryOpExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) { | |
137 | impl := e.Op.Impl // assumed to be a function taking exactly two arguments | |
138 | params := impl.Params() | |
139 | lhsParam := params[0] | |
140 | rhsParam := params[1] | |
141 | ||
142 | var diags hcl.Diagnostics | |
143 | ||
144 | givenLHSVal, lhsDiags := e.LHS.Value(ctx) | |
145 | givenRHSVal, rhsDiags := e.RHS.Value(ctx) | |
146 | diags = append(diags, lhsDiags...) | |
147 | diags = append(diags, rhsDiags...) | |
148 | ||
149 | lhsVal, err := convert.Convert(givenLHSVal, lhsParam.Type) | |
150 | if err != nil { | |
151 | diags = append(diags, &hcl.Diagnostic{ | |
107c1cdb ND |
152 | Severity: hcl.DiagError, |
153 | Summary: "Invalid operand", | |
154 | Detail: fmt.Sprintf("Unsuitable value for left operand: %s.", err), | |
155 | Subject: e.LHS.Range().Ptr(), | |
156 | Context: &e.SrcRange, | |
157 | Expression: e.LHS, | |
158 | EvalContext: ctx, | |
15c0b25d AP |
159 | }) |
160 | } | |
161 | rhsVal, err := convert.Convert(givenRHSVal, rhsParam.Type) | |
162 | if err != nil { | |
163 | diags = append(diags, &hcl.Diagnostic{ | |
107c1cdb ND |
164 | Severity: hcl.DiagError, |
165 | Summary: "Invalid operand", | |
166 | Detail: fmt.Sprintf("Unsuitable value for right operand: %s.", err), | |
167 | Subject: e.RHS.Range().Ptr(), | |
168 | Context: &e.SrcRange, | |
169 | Expression: e.RHS, | |
170 | EvalContext: ctx, | |
15c0b25d AP |
171 | }) |
172 | } | |
173 | ||
174 | if diags.HasErrors() { | |
175 | // Don't actually try the call if we have errors already, since the | |
176 | // this will probably just produce a confusing duplicative diagnostic. | |
177 | return cty.UnknownVal(e.Op.Type), diags | |
178 | } | |
179 | ||
180 | args := []cty.Value{lhsVal, rhsVal} | |
181 | result, err := impl.Call(args) | |
182 | if err != nil { | |
183 | diags = append(diags, &hcl.Diagnostic{ | |
184 | // FIXME: This diagnostic is useless. | |
107c1cdb ND |
185 | Severity: hcl.DiagError, |
186 | Summary: "Operation failed", | |
187 | Detail: fmt.Sprintf("Error during operation: %s.", err), | |
188 | Subject: &e.SrcRange, | |
189 | Expression: e, | |
190 | EvalContext: ctx, | |
15c0b25d AP |
191 | }) |
192 | return cty.UnknownVal(e.Op.Type), diags | |
193 | } | |
194 | ||
195 | return result, diags | |
196 | } | |
197 | ||
198 | func (e *BinaryOpExpr) Range() hcl.Range { | |
199 | return e.SrcRange | |
200 | } | |
201 | ||
202 | func (e *BinaryOpExpr) StartRange() hcl.Range { | |
203 | return e.LHS.StartRange() | |
204 | } | |
205 | ||
206 | type UnaryOpExpr struct { | |
207 | Op *Operation | |
208 | Val Expression | |
209 | ||
210 | SrcRange hcl.Range | |
211 | SymbolRange hcl.Range | |
212 | } | |
213 | ||
214 | func (e *UnaryOpExpr) walkChildNodes(w internalWalkFunc) { | |
107c1cdb | 215 | w(e.Val) |
15c0b25d AP |
216 | } |
217 | ||
218 | func (e *UnaryOpExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) { | |
219 | impl := e.Op.Impl // assumed to be a function taking exactly one argument | |
220 | params := impl.Params() | |
221 | param := params[0] | |
222 | ||
223 | givenVal, diags := e.Val.Value(ctx) | |
224 | ||
225 | val, err := convert.Convert(givenVal, param.Type) | |
226 | if err != nil { | |
227 | diags = append(diags, &hcl.Diagnostic{ | |
107c1cdb ND |
228 | Severity: hcl.DiagError, |
229 | Summary: "Invalid operand", | |
230 | Detail: fmt.Sprintf("Unsuitable value for unary operand: %s.", err), | |
231 | Subject: e.Val.Range().Ptr(), | |
232 | Context: &e.SrcRange, | |
233 | Expression: e.Val, | |
234 | EvalContext: ctx, | |
15c0b25d AP |
235 | }) |
236 | } | |
237 | ||
238 | if diags.HasErrors() { | |
239 | // Don't actually try the call if we have errors already, since the | |
240 | // this will probably just produce a confusing duplicative diagnostic. | |
241 | return cty.UnknownVal(e.Op.Type), diags | |
242 | } | |
243 | ||
244 | args := []cty.Value{val} | |
245 | result, err := impl.Call(args) | |
246 | if err != nil { | |
247 | diags = append(diags, &hcl.Diagnostic{ | |
248 | // FIXME: This diagnostic is useless. | |
107c1cdb ND |
249 | Severity: hcl.DiagError, |
250 | Summary: "Operation failed", | |
251 | Detail: fmt.Sprintf("Error during operation: %s.", err), | |
252 | Subject: &e.SrcRange, | |
253 | Expression: e, | |
254 | EvalContext: ctx, | |
15c0b25d AP |
255 | }) |
256 | return cty.UnknownVal(e.Op.Type), diags | |
257 | } | |
258 | ||
259 | return result, diags | |
260 | } | |
261 | ||
262 | func (e *UnaryOpExpr) Range() hcl.Range { | |
263 | return e.SrcRange | |
264 | } | |
265 | ||
266 | func (e *UnaryOpExpr) StartRange() hcl.Range { | |
267 | return e.SymbolRange | |
268 | } |