blob: ec0b279b9cf9bc9cbf0b69962c608c400aac45b8 [file] [log] [blame]
Sasha Smundakb051c4e2020-11-05 20:45:07 -08001// Copyright 2021 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package mk2rbc
16
17import (
18 "fmt"
19 "strconv"
20 "strings"
Sasha Smundakb051c4e2020-11-05 20:45:07 -080021)
22
23// Represents an expression in the Starlark code. An expression has
24// a type, and it can be evaluated.
25type starlarkExpr interface {
26 starlarkNode
27 typ() starlarkType
28 // Try to substitute variable values. Return substitution result
29 // and whether it is the same as the original expression.
30 eval(valueMap map[string]starlarkExpr) (res starlarkExpr, same bool)
31 // Emit the code to copy the expression, otherwise we will end up
32 // with source and target pointing to the same list.
33 emitListVarCopy(gctx *generationContext)
34}
35
36func maybeString(expr starlarkExpr) (string, bool) {
37 if x, ok := expr.(*stringLiteralExpr); ok {
38 return x.literal, true
39 }
40 return "", false
41}
42
43type stringLiteralExpr struct {
44 literal string
45}
46
47func (s *stringLiteralExpr) eval(_ map[string]starlarkExpr) (res starlarkExpr, same bool) {
48 res = s
49 same = true
50 return
51}
52
53func (s *stringLiteralExpr) emit(gctx *generationContext) {
54 gctx.writef("%q", s.literal)
55}
56
57func (_ *stringLiteralExpr) typ() starlarkType {
58 return starlarkTypeString
59}
60
61func (s *stringLiteralExpr) emitListVarCopy(gctx *generationContext) {
62 s.emit(gctx)
63}
64
65// Integer literal
66type intLiteralExpr struct {
67 literal int
68}
69
70func (s *intLiteralExpr) eval(_ map[string]starlarkExpr) (res starlarkExpr, same bool) {
71 res = s
72 same = true
73 return
74}
75
76func (s *intLiteralExpr) emit(gctx *generationContext) {
77 gctx.writef("%d", s.literal)
78}
79
80func (_ *intLiteralExpr) typ() starlarkType {
81 return starlarkTypeInt
82}
83
84func (s *intLiteralExpr) emitListVarCopy(gctx *generationContext) {
85 s.emit(gctx)
86}
87
88// interpolateExpr represents Starlark's interpolation operator <string> % list
89// we break <string> into a list of chunks, i.e., "first%second%third" % (X, Y)
90// will have chunks = ["first", "second", "third"] and args = [X, Y]
91type interpolateExpr struct {
92 chunks []string // string chunks, separated by '%'
93 args []starlarkExpr
94}
95
96func (xi *interpolateExpr) emit(gctx *generationContext) {
97 if len(xi.chunks) != len(xi.args)+1 {
98 panic(fmt.Errorf("malformed interpolateExpr: #chunks(%d) != #args(%d)+1",
99 len(xi.chunks), len(xi.args)))
100 }
101 // Generate format as join of chunks, but first escape '%' in them
102 format := strings.ReplaceAll(xi.chunks[0], "%", "%%")
103 for _, chunk := range xi.chunks[1:] {
104 format += "%s" + strings.ReplaceAll(chunk, "%", "%%")
105 }
106 gctx.writef("%q %% ", format)
Sasha Smundak422b6142021-11-11 18:31:59 -0800107 emitArg := func(arg starlarkExpr) {
Sasha Smundakb051c4e2020-11-05 20:45:07 -0800108 if arg.typ() == starlarkTypeList {
109 gctx.write(`" ".join(`)
110 arg.emit(gctx)
111 gctx.write(`)`)
112 } else {
113 arg.emit(gctx)
114 }
115 }
116 if len(xi.args) == 1 {
Sasha Smundak422b6142021-11-11 18:31:59 -0800117 emitArg(xi.args[0])
Sasha Smundakb051c4e2020-11-05 20:45:07 -0800118 } else {
119 sep := "("
120 for _, arg := range xi.args {
121 gctx.write(sep)
Sasha Smundak422b6142021-11-11 18:31:59 -0800122 emitArg(arg)
Sasha Smundakb051c4e2020-11-05 20:45:07 -0800123 sep = ", "
124 }
125 gctx.write(")")
126 }
127}
128
129func (xi *interpolateExpr) eval(valueMap map[string]starlarkExpr) (res starlarkExpr, same bool) {
130 same = true
131 newChunks := []string{xi.chunks[0]}
132 var newArgs []starlarkExpr
133 for i, arg := range xi.args {
134 newArg, sameArg := arg.eval(valueMap)
135 same = same && sameArg
136 switch x := newArg.(type) {
137 case *stringLiteralExpr:
138 newChunks[len(newChunks)-1] += x.literal + xi.chunks[i+1]
139 same = false
140 continue
141 case *intLiteralExpr:
142 newChunks[len(newChunks)-1] += strconv.Itoa(x.literal) + xi.chunks[i+1]
143 same = false
144 continue
145 default:
146 newChunks = append(newChunks, xi.chunks[i+1])
147 newArgs = append(newArgs, newArg)
148 }
149 }
150 if same {
151 res = xi
152 } else if len(newChunks) == 1 {
153 res = &stringLiteralExpr{newChunks[0]}
154 } else {
155 res = &interpolateExpr{chunks: newChunks, args: newArgs}
156 }
157 return
158}
159
160func (_ *interpolateExpr) typ() starlarkType {
161 return starlarkTypeString
162}
163
164func (xi *interpolateExpr) emitListVarCopy(gctx *generationContext) {
165 xi.emit(gctx)
166}
167
168type variableRefExpr struct {
169 ref variable
170 isDefined bool
171}
172
173func (v *variableRefExpr) eval(map[string]starlarkExpr) (res starlarkExpr, same bool) {
174 predefined, ok := v.ref.(*predefinedVariable)
175 if same = !ok; same {
176 res = v
177 } else {
178 res = predefined.value
179 }
180 return
181}
182
183func (v *variableRefExpr) emit(gctx *generationContext) {
184 v.ref.emitGet(gctx, v.isDefined)
185}
186
187func (v *variableRefExpr) typ() starlarkType {
188 return v.ref.valueType()
189}
190
191func (v *variableRefExpr) emitListVarCopy(gctx *generationContext) {
192 v.emit(gctx)
193 if v.typ() == starlarkTypeList {
194 gctx.write("[:]") // this will copy the list
195 }
196}
197
Cole Faustf8320212021-11-10 15:05:07 -0800198type toStringExpr struct {
199 expr starlarkExpr
200}
201
202func (s *toStringExpr) eval(valueMap map[string]starlarkExpr) (res starlarkExpr, same bool) {
203 if x, same := s.expr.eval(valueMap); same {
204 res = s
205 } else {
206 res = &toStringExpr{expr: x}
207 }
208 return
209}
210
211func (s *toStringExpr) emit(ctx *generationContext) {
212 switch s.expr.typ() {
213 case starlarkTypeString, starlarkTypeUnknown:
214 // Assume unknown types are strings already.
215 s.expr.emit(ctx)
216 case starlarkTypeList:
217 ctx.write(`" ".join(`)
218 s.expr.emit(ctx)
219 ctx.write(")")
220 case starlarkTypeInt:
221 ctx.write(`("%d" % (`)
222 s.expr.emit(ctx)
223 ctx.write("))")
224 case starlarkTypeBool:
Cole Faustf1f44d32021-11-16 14:52:12 -0800225 ctx.write(`("true" if (`)
Cole Faustf8320212021-11-10 15:05:07 -0800226 s.expr.emit(ctx)
Cole Faustf1f44d32021-11-16 14:52:12 -0800227 ctx.write(`) else "")`)
Cole Faustf8320212021-11-10 15:05:07 -0800228 case starlarkTypeVoid:
229 ctx.write(`""`)
230 default:
231 panic("Unknown starlark type!")
232 }
233}
234
235func (s *toStringExpr) typ() starlarkType {
236 return starlarkTypeString
237}
238
239func (s *toStringExpr) emitListVarCopy(gctx *generationContext) {
240 s.emit(gctx)
241}
242
Sasha Smundakb051c4e2020-11-05 20:45:07 -0800243type notExpr struct {
244 expr starlarkExpr
245}
246
247func (n *notExpr) eval(valueMap map[string]starlarkExpr) (res starlarkExpr, same bool) {
248 if x, same := n.expr.eval(valueMap); same {
249 res = n
250 } else {
251 res = &notExpr{expr: x}
252 }
253 return
254}
255
256func (n *notExpr) emit(ctx *generationContext) {
257 ctx.write("not ")
258 n.expr.emit(ctx)
259}
260
261func (_ *notExpr) typ() starlarkType {
262 return starlarkTypeBool
263}
264
265func (n *notExpr) emitListVarCopy(gctx *generationContext) {
266 n.emit(gctx)
267}
268
269type eqExpr struct {
270 left, right starlarkExpr
271 isEq bool // if false, it's !=
272}
273
274func (eq *eqExpr) eval(valueMap map[string]starlarkExpr) (res starlarkExpr, same bool) {
275 xLeft, sameLeft := eq.left.eval(valueMap)
276 xRight, sameRight := eq.right.eval(valueMap)
277 if same = sameLeft && sameRight; same {
278 res = eq
279 } else {
280 res = &eqExpr{left: xLeft, right: xRight, isEq: eq.isEq}
281 }
282 return
283}
284
285func (eq *eqExpr) emit(gctx *generationContext) {
Cole Faustf1f44d32021-11-16 14:52:12 -0800286 var stringOperand string
287 var otherOperand starlarkExpr
288 if s, ok := maybeString(eq.left); ok {
289 stringOperand = s
290 otherOperand = eq.right
291 } else if s, ok := maybeString(eq.right); ok {
292 stringOperand = s
293 otherOperand = eq.left
Sasha Smundakb051c4e2020-11-05 20:45:07 -0800294 }
295
Cole Faustf1f44d32021-11-16 14:52:12 -0800296 // If we've identified one of the operands as being a string literal, check
297 // for some special cases we can do to simplify the resulting expression.
298 if otherOperand != nil {
299 if stringOperand == "" {
300 if eq.isEq {
301 gctx.write("not ")
302 }
303 otherOperand.emit(gctx)
304 return
305 }
306 if stringOperand == "true" && otherOperand.typ() == starlarkTypeBool {
307 if !eq.isEq {
308 gctx.write("not ")
309 }
310 otherOperand.emit(gctx)
311 return
312 }
Sasha Smundak0554d762021-07-08 18:26:12 -0700313 }
Cole Faustf8320212021-11-10 15:05:07 -0800314
315 if eq.left.typ() != eq.right.typ() {
316 eq.left = &toStringExpr{expr: eq.left}
317 eq.right = &toStringExpr{expr: eq.right}
318 }
319
Sasha Smundakb051c4e2020-11-05 20:45:07 -0800320 // General case
321 eq.left.emit(gctx)
322 if eq.isEq {
323 gctx.write(" == ")
324 } else {
325 gctx.write(" != ")
326 }
327 eq.right.emit(gctx)
328}
329
330func (_ *eqExpr) typ() starlarkType {
331 return starlarkTypeBool
332}
333
334func (eq *eqExpr) emitListVarCopy(gctx *generationContext) {
335 eq.emit(gctx)
336}
337
338// variableDefinedExpr corresponds to Make's ifdef VAR
339type variableDefinedExpr struct {
340 v variable
341}
342
343func (v *variableDefinedExpr) eval(_ map[string]starlarkExpr) (res starlarkExpr, same bool) {
344 res = v
345 same = true
346 return
347
348}
349
350func (v *variableDefinedExpr) emit(gctx *generationContext) {
351 if v.v != nil {
352 v.v.emitDefined(gctx)
353 return
354 }
355 gctx.writef("%s(%q)", cfnWarning, "TODO(VAR)")
356}
357
358func (_ *variableDefinedExpr) typ() starlarkType {
359 return starlarkTypeBool
360}
361
362func (v *variableDefinedExpr) emitListVarCopy(gctx *generationContext) {
363 v.emit(gctx)
364}
365
366type listExpr struct {
367 items []starlarkExpr
368}
369
370func (l *listExpr) eval(valueMap map[string]starlarkExpr) (res starlarkExpr, same bool) {
371 newItems := make([]starlarkExpr, len(l.items))
372 same = true
373 for i, item := range l.items {
374 var sameItem bool
375 newItems[i], sameItem = item.eval(valueMap)
376 same = same && sameItem
377 }
378 if same {
379 res = l
380 } else {
381 res = &listExpr{newItems}
382 }
383 return
384}
385
386func (l *listExpr) emit(gctx *generationContext) {
387 if !gctx.inAssignment || len(l.items) < 2 {
388 gctx.write("[")
389 sep := ""
390 for _, item := range l.items {
391 gctx.write(sep)
392 item.emit(gctx)
393 sep = ", "
394 }
395 gctx.write("]")
396 return
397 }
398
399 gctx.write("[")
400 gctx.indentLevel += 2
401
402 for _, item := range l.items {
403 gctx.newLine()
404 item.emit(gctx)
405 gctx.write(",")
406 }
407 gctx.indentLevel -= 2
408 gctx.newLine()
409 gctx.write("]")
410}
411
412func (_ *listExpr) typ() starlarkType {
413 return starlarkTypeList
414}
415
416func (l *listExpr) emitListVarCopy(gctx *generationContext) {
417 l.emit(gctx)
418}
419
420func newStringListExpr(items []string) *listExpr {
421 v := listExpr{}
422 for _, item := range items {
423 v.items = append(v.items, &stringLiteralExpr{item})
424 }
425 return &v
426}
427
Sasha Smundak422b6142021-11-11 18:31:59 -0800428// concatExpr generates expr1 + expr2 + ... + exprN in Starlark.
Sasha Smundakb051c4e2020-11-05 20:45:07 -0800429type concatExpr struct {
430 items []starlarkExpr
431}
432
433func (c *concatExpr) emit(gctx *generationContext) {
434 if len(c.items) == 1 {
435 c.items[0].emit(gctx)
436 return
437 }
438
439 if !gctx.inAssignment {
440 c.items[0].emit(gctx)
441 for _, item := range c.items[1:] {
442 gctx.write(" + ")
443 item.emit(gctx)
444 }
445 return
446 }
447 gctx.write("(")
448 c.items[0].emit(gctx)
449 gctx.indentLevel += 2
450 for _, item := range c.items[1:] {
451 gctx.write(" +")
452 gctx.newLine()
453 item.emit(gctx)
454 }
455 gctx.write(")")
456 gctx.indentLevel -= 2
457}
458
459func (c *concatExpr) eval(valueMap map[string]starlarkExpr) (res starlarkExpr, same bool) {
460 same = true
461 xConcat := &concatExpr{items: make([]starlarkExpr, len(c.items))}
462 for i, item := range c.items {
463 var sameItem bool
464 xConcat.items[i], sameItem = item.eval(valueMap)
465 same = same && sameItem
466 }
467 if same {
468 res = c
469 } else {
470 res = xConcat
471 }
472 return
473}
474
475func (_ *concatExpr) typ() starlarkType {
476 return starlarkTypeList
477}
478
479func (c *concatExpr) emitListVarCopy(gctx *generationContext) {
480 c.emit(gctx)
481}
482
483// inExpr generates <expr> [not] in <list>
484type inExpr struct {
485 expr starlarkExpr
486 list starlarkExpr
487 isNot bool
488}
489
490func (i *inExpr) eval(valueMap map[string]starlarkExpr) (res starlarkExpr, same bool) {
491 x := &inExpr{isNot: i.isNot}
492 var sameExpr, sameList bool
493 x.expr, sameExpr = i.expr.eval(valueMap)
494 x.list, sameList = i.list.eval(valueMap)
495 if same = sameExpr && sameList; same {
496 res = i
497 } else {
498 res = x
499 }
500 return
501}
502
503func (i *inExpr) emit(gctx *generationContext) {
504 i.expr.emit(gctx)
505 if i.isNot {
506 gctx.write(" not in ")
507 } else {
508 gctx.write(" in ")
509 }
510 i.list.emit(gctx)
511}
512
513func (_ *inExpr) typ() starlarkType {
514 return starlarkTypeBool
515}
516
517func (i *inExpr) emitListVarCopy(gctx *generationContext) {
518 i.emit(gctx)
519}
520
521type indexExpr struct {
522 array starlarkExpr
523 index starlarkExpr
524}
525
526func (ix indexExpr) emit(gctx *generationContext) {
527 ix.array.emit(gctx)
528 gctx.write("[")
529 ix.index.emit(gctx)
530 gctx.write("]")
531}
532
533func (ix indexExpr) typ() starlarkType {
534 return starlarkTypeString
535}
536
537func (ix indexExpr) eval(valueMap map[string]starlarkExpr) (res starlarkExpr, same bool) {
538 newArray, isSameArray := ix.array.eval(valueMap)
539 newIndex, isSameIndex := ix.index.eval(valueMap)
540 if same = isSameArray && isSameIndex; same {
541 res = ix
542 } else {
543 res = &indexExpr{newArray, newIndex}
544 }
545 return
546}
547
548func (ix indexExpr) emitListVarCopy(gctx *generationContext) {
549 ix.emit(gctx)
550}
551
552type callExpr struct {
553 object starlarkExpr // nil if static call
554 name string
555 args []starlarkExpr
556 returnType starlarkType
557}
558
559func (cx *callExpr) eval(valueMap map[string]starlarkExpr) (res starlarkExpr, same bool) {
560 newCallExpr := &callExpr{name: cx.name, args: make([]starlarkExpr, len(cx.args)),
561 returnType: cx.returnType}
562 if cx.object != nil {
563 newCallExpr.object, same = cx.object.eval(valueMap)
564 } else {
565 same = true
566 }
567 for i, args := range cx.args {
568 var s bool
569 newCallExpr.args[i], s = args.eval(valueMap)
570 same = same && s
571 }
572 if same {
573 res = cx
574 } else {
575 res = newCallExpr
576 }
577 return
578}
579
580func (cx *callExpr) emit(gctx *generationContext) {
Sasha Smundak3deb9682021-07-26 18:42:25 -0700581 sep := ""
Sasha Smundakb051c4e2020-11-05 20:45:07 -0800582 if cx.object != nil {
583 gctx.write("(")
584 cx.object.emit(gctx)
585 gctx.write(")")
586 gctx.write(".", cx.name, "(")
587 } else {
588 kf, found := knownFunctions[cx.name]
589 if !found {
590 panic(fmt.Errorf("callExpr with unknown function %q", cx.name))
591 }
592 if kf.runtimeName[0] == '!' {
593 panic(fmt.Errorf("callExpr for %q should not be there", cx.name))
594 }
595 gctx.write(kf.runtimeName, "(")
Sasha Smundak3deb9682021-07-26 18:42:25 -0700596 if kf.hiddenArg == hiddenArgGlobal {
597 gctx.write("g")
598 sep = ", "
599 } else if kf.hiddenArg == hiddenArgConfig {
600 gctx.write("cfg")
601 sep = ", "
602 }
Sasha Smundakb051c4e2020-11-05 20:45:07 -0800603 }
Sasha Smundakb051c4e2020-11-05 20:45:07 -0800604 for _, arg := range cx.args {
605 gctx.write(sep)
606 arg.emit(gctx)
607 sep = ", "
608 }
609 gctx.write(")")
610}
611
612func (cx *callExpr) typ() starlarkType {
613 return cx.returnType
614}
615
616func (cx *callExpr) emitListVarCopy(gctx *generationContext) {
617 cx.emit(gctx)
618}
619
620type badExpr struct {
Sasha Smundak422b6142021-11-11 18:31:59 -0800621 errorLocation ErrorLocation
622 message string
Sasha Smundakb051c4e2020-11-05 20:45:07 -0800623}
624
625func (b *badExpr) eval(_ map[string]starlarkExpr) (res starlarkExpr, same bool) {
626 res = b
627 same = true
628 return
629}
630
Sasha Smundak422b6142021-11-11 18:31:59 -0800631func (b *badExpr) emit(gctx *generationContext) {
632 gctx.emitConversionError(b.errorLocation, b.message)
Sasha Smundakb051c4e2020-11-05 20:45:07 -0800633}
634
635func (_ *badExpr) typ() starlarkType {
636 return starlarkTypeUnknown
637}
638
Sasha Smundak422b6142021-11-11 18:31:59 -0800639func (_ *badExpr) emitListVarCopy(_ *generationContext) {
Sasha Smundakb051c4e2020-11-05 20:45:07 -0800640 panic("implement me")
641}
642
643func maybeConvertToStringList(expr starlarkExpr) starlarkExpr {
644 if xString, ok := expr.(*stringLiteralExpr); ok {
645 return newStringListExpr(strings.Fields(xString.literal))
646 }
647 return expr
648}
Sasha Smundak0554d762021-07-08 18:26:12 -0700649
650func isEmptyString(expr starlarkExpr) bool {
651 x, ok := expr.(*stringLiteralExpr)
652 return ok && x.literal == ""
653}