blob: 81b31c736feb82490b13756dc79fd2f7761d51d6 [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
Cole Faust4eadba72021-12-07 11:54:52 -080088// Boolean literal
89type boolLiteralExpr struct {
90 literal bool
91}
92
93func (b *boolLiteralExpr) eval(_ map[string]starlarkExpr) (res starlarkExpr, same bool) {
94 return b, true
95}
96
97func (b *boolLiteralExpr) emit(gctx *generationContext) {
98 if b.literal {
99 gctx.write("True")
100 } else {
101 gctx.write("False")
102 }
103}
104
105func (_ *boolLiteralExpr) typ() starlarkType {
106 return starlarkTypeBool
107}
108
109func (b *boolLiteralExpr) emitListVarCopy(gctx *generationContext) {
110 b.emit(gctx)
111}
112
Sasha Smundakb051c4e2020-11-05 20:45:07 -0800113// interpolateExpr represents Starlark's interpolation operator <string> % list
114// we break <string> into a list of chunks, i.e., "first%second%third" % (X, Y)
115// will have chunks = ["first", "second", "third"] and args = [X, Y]
116type interpolateExpr struct {
117 chunks []string // string chunks, separated by '%'
118 args []starlarkExpr
119}
120
121func (xi *interpolateExpr) emit(gctx *generationContext) {
122 if len(xi.chunks) != len(xi.args)+1 {
123 panic(fmt.Errorf("malformed interpolateExpr: #chunks(%d) != #args(%d)+1",
124 len(xi.chunks), len(xi.args)))
125 }
126 // Generate format as join of chunks, but first escape '%' in them
127 format := strings.ReplaceAll(xi.chunks[0], "%", "%%")
128 for _, chunk := range xi.chunks[1:] {
129 format += "%s" + strings.ReplaceAll(chunk, "%", "%%")
130 }
131 gctx.writef("%q %% ", format)
Sasha Smundak422b6142021-11-11 18:31:59 -0800132 emitArg := func(arg starlarkExpr) {
Sasha Smundakb051c4e2020-11-05 20:45:07 -0800133 if arg.typ() == starlarkTypeList {
134 gctx.write(`" ".join(`)
135 arg.emit(gctx)
136 gctx.write(`)`)
137 } else {
138 arg.emit(gctx)
139 }
140 }
141 if len(xi.args) == 1 {
Sasha Smundak422b6142021-11-11 18:31:59 -0800142 emitArg(xi.args[0])
Sasha Smundakb051c4e2020-11-05 20:45:07 -0800143 } else {
144 sep := "("
145 for _, arg := range xi.args {
146 gctx.write(sep)
Sasha Smundak422b6142021-11-11 18:31:59 -0800147 emitArg(arg)
Sasha Smundakb051c4e2020-11-05 20:45:07 -0800148 sep = ", "
149 }
150 gctx.write(")")
151 }
152}
153
154func (xi *interpolateExpr) eval(valueMap map[string]starlarkExpr) (res starlarkExpr, same bool) {
155 same = true
156 newChunks := []string{xi.chunks[0]}
157 var newArgs []starlarkExpr
158 for i, arg := range xi.args {
159 newArg, sameArg := arg.eval(valueMap)
160 same = same && sameArg
161 switch x := newArg.(type) {
162 case *stringLiteralExpr:
163 newChunks[len(newChunks)-1] += x.literal + xi.chunks[i+1]
164 same = false
165 continue
166 case *intLiteralExpr:
167 newChunks[len(newChunks)-1] += strconv.Itoa(x.literal) + xi.chunks[i+1]
168 same = false
169 continue
170 default:
171 newChunks = append(newChunks, xi.chunks[i+1])
172 newArgs = append(newArgs, newArg)
173 }
174 }
175 if same {
176 res = xi
177 } else if len(newChunks) == 1 {
178 res = &stringLiteralExpr{newChunks[0]}
179 } else {
180 res = &interpolateExpr{chunks: newChunks, args: newArgs}
181 }
182 return
183}
184
185func (_ *interpolateExpr) typ() starlarkType {
186 return starlarkTypeString
187}
188
189func (xi *interpolateExpr) emitListVarCopy(gctx *generationContext) {
190 xi.emit(gctx)
191}
192
193type variableRefExpr struct {
194 ref variable
195 isDefined bool
196}
197
198func (v *variableRefExpr) eval(map[string]starlarkExpr) (res starlarkExpr, same bool) {
199 predefined, ok := v.ref.(*predefinedVariable)
200 if same = !ok; same {
201 res = v
202 } else {
203 res = predefined.value
204 }
205 return
206}
207
208func (v *variableRefExpr) emit(gctx *generationContext) {
209 v.ref.emitGet(gctx, v.isDefined)
210}
211
212func (v *variableRefExpr) typ() starlarkType {
213 return v.ref.valueType()
214}
215
216func (v *variableRefExpr) emitListVarCopy(gctx *generationContext) {
217 v.emit(gctx)
218 if v.typ() == starlarkTypeList {
219 gctx.write("[:]") // this will copy the list
220 }
221}
222
Cole Faustf8320212021-11-10 15:05:07 -0800223type toStringExpr struct {
224 expr starlarkExpr
225}
226
227func (s *toStringExpr) eval(valueMap map[string]starlarkExpr) (res starlarkExpr, same bool) {
228 if x, same := s.expr.eval(valueMap); same {
229 res = s
230 } else {
231 res = &toStringExpr{expr: x}
232 }
233 return
234}
235
236func (s *toStringExpr) emit(ctx *generationContext) {
237 switch s.expr.typ() {
238 case starlarkTypeString, starlarkTypeUnknown:
239 // Assume unknown types are strings already.
240 s.expr.emit(ctx)
241 case starlarkTypeList:
242 ctx.write(`" ".join(`)
243 s.expr.emit(ctx)
244 ctx.write(")")
245 case starlarkTypeInt:
246 ctx.write(`("%d" % (`)
247 s.expr.emit(ctx)
248 ctx.write("))")
249 case starlarkTypeBool:
Cole Faustf1f44d32021-11-16 14:52:12 -0800250 ctx.write(`("true" if (`)
Cole Faustf8320212021-11-10 15:05:07 -0800251 s.expr.emit(ctx)
Cole Faustf1f44d32021-11-16 14:52:12 -0800252 ctx.write(`) else "")`)
Cole Faustf8320212021-11-10 15:05:07 -0800253 case starlarkTypeVoid:
254 ctx.write(`""`)
255 default:
256 panic("Unknown starlark type!")
257 }
258}
259
260func (s *toStringExpr) typ() starlarkType {
261 return starlarkTypeString
262}
263
264func (s *toStringExpr) emitListVarCopy(gctx *generationContext) {
265 s.emit(gctx)
266}
267
Sasha Smundakb051c4e2020-11-05 20:45:07 -0800268type notExpr struct {
269 expr starlarkExpr
270}
271
272func (n *notExpr) eval(valueMap map[string]starlarkExpr) (res starlarkExpr, same bool) {
273 if x, same := n.expr.eval(valueMap); same {
274 res = n
275 } else {
276 res = &notExpr{expr: x}
277 }
278 return
279}
280
281func (n *notExpr) emit(ctx *generationContext) {
282 ctx.write("not ")
283 n.expr.emit(ctx)
284}
285
286func (_ *notExpr) typ() starlarkType {
287 return starlarkTypeBool
288}
289
290func (n *notExpr) emitListVarCopy(gctx *generationContext) {
291 n.emit(gctx)
292}
293
294type eqExpr struct {
295 left, right starlarkExpr
296 isEq bool // if false, it's !=
297}
298
299func (eq *eqExpr) eval(valueMap map[string]starlarkExpr) (res starlarkExpr, same bool) {
300 xLeft, sameLeft := eq.left.eval(valueMap)
301 xRight, sameRight := eq.right.eval(valueMap)
302 if same = sameLeft && sameRight; same {
303 res = eq
304 } else {
305 res = &eqExpr{left: xLeft, right: xRight, isEq: eq.isEq}
306 }
307 return
308}
309
310func (eq *eqExpr) emit(gctx *generationContext) {
Cole Faustf1f44d32021-11-16 14:52:12 -0800311 var stringOperand string
312 var otherOperand starlarkExpr
313 if s, ok := maybeString(eq.left); ok {
314 stringOperand = s
315 otherOperand = eq.right
316 } else if s, ok := maybeString(eq.right); ok {
317 stringOperand = s
318 otherOperand = eq.left
Sasha Smundakb051c4e2020-11-05 20:45:07 -0800319 }
320
Cole Faustf1f44d32021-11-16 14:52:12 -0800321 // If we've identified one of the operands as being a string literal, check
322 // for some special cases we can do to simplify the resulting expression.
323 if otherOperand != nil {
324 if stringOperand == "" {
325 if eq.isEq {
326 gctx.write("not ")
327 }
328 otherOperand.emit(gctx)
329 return
330 }
331 if stringOperand == "true" && otherOperand.typ() == starlarkTypeBool {
332 if !eq.isEq {
333 gctx.write("not ")
334 }
335 otherOperand.emit(gctx)
336 return
337 }
Sasha Smundak0554d762021-07-08 18:26:12 -0700338 }
Cole Faustf8320212021-11-10 15:05:07 -0800339
340 if eq.left.typ() != eq.right.typ() {
341 eq.left = &toStringExpr{expr: eq.left}
342 eq.right = &toStringExpr{expr: eq.right}
343 }
344
Sasha Smundakb051c4e2020-11-05 20:45:07 -0800345 // General case
346 eq.left.emit(gctx)
347 if eq.isEq {
348 gctx.write(" == ")
349 } else {
350 gctx.write(" != ")
351 }
352 eq.right.emit(gctx)
353}
354
355func (_ *eqExpr) typ() starlarkType {
356 return starlarkTypeBool
357}
358
359func (eq *eqExpr) emitListVarCopy(gctx *generationContext) {
360 eq.emit(gctx)
361}
362
363// variableDefinedExpr corresponds to Make's ifdef VAR
364type variableDefinedExpr struct {
365 v variable
366}
367
368func (v *variableDefinedExpr) eval(_ map[string]starlarkExpr) (res starlarkExpr, same bool) {
369 res = v
370 same = true
371 return
372
373}
374
375func (v *variableDefinedExpr) emit(gctx *generationContext) {
376 if v.v != nil {
377 v.v.emitDefined(gctx)
378 return
379 }
380 gctx.writef("%s(%q)", cfnWarning, "TODO(VAR)")
381}
382
383func (_ *variableDefinedExpr) typ() starlarkType {
384 return starlarkTypeBool
385}
386
387func (v *variableDefinedExpr) emitListVarCopy(gctx *generationContext) {
388 v.emit(gctx)
389}
390
391type listExpr struct {
392 items []starlarkExpr
393}
394
395func (l *listExpr) eval(valueMap map[string]starlarkExpr) (res starlarkExpr, same bool) {
396 newItems := make([]starlarkExpr, len(l.items))
397 same = true
398 for i, item := range l.items {
399 var sameItem bool
400 newItems[i], sameItem = item.eval(valueMap)
401 same = same && sameItem
402 }
403 if same {
404 res = l
405 } else {
406 res = &listExpr{newItems}
407 }
408 return
409}
410
411func (l *listExpr) emit(gctx *generationContext) {
412 if !gctx.inAssignment || len(l.items) < 2 {
413 gctx.write("[")
414 sep := ""
415 for _, item := range l.items {
416 gctx.write(sep)
417 item.emit(gctx)
418 sep = ", "
419 }
420 gctx.write("]")
421 return
422 }
423
424 gctx.write("[")
425 gctx.indentLevel += 2
426
427 for _, item := range l.items {
428 gctx.newLine()
429 item.emit(gctx)
430 gctx.write(",")
431 }
432 gctx.indentLevel -= 2
433 gctx.newLine()
434 gctx.write("]")
435}
436
437func (_ *listExpr) typ() starlarkType {
438 return starlarkTypeList
439}
440
441func (l *listExpr) emitListVarCopy(gctx *generationContext) {
442 l.emit(gctx)
443}
444
445func newStringListExpr(items []string) *listExpr {
446 v := listExpr{}
447 for _, item := range items {
448 v.items = append(v.items, &stringLiteralExpr{item})
449 }
450 return &v
451}
452
Sasha Smundak422b6142021-11-11 18:31:59 -0800453// concatExpr generates expr1 + expr2 + ... + exprN in Starlark.
Sasha Smundakb051c4e2020-11-05 20:45:07 -0800454type concatExpr struct {
455 items []starlarkExpr
456}
457
458func (c *concatExpr) emit(gctx *generationContext) {
459 if len(c.items) == 1 {
460 c.items[0].emit(gctx)
461 return
462 }
463
464 if !gctx.inAssignment {
465 c.items[0].emit(gctx)
466 for _, item := range c.items[1:] {
467 gctx.write(" + ")
468 item.emit(gctx)
469 }
470 return
471 }
472 gctx.write("(")
473 c.items[0].emit(gctx)
474 gctx.indentLevel += 2
475 for _, item := range c.items[1:] {
476 gctx.write(" +")
477 gctx.newLine()
478 item.emit(gctx)
479 }
480 gctx.write(")")
481 gctx.indentLevel -= 2
482}
483
484func (c *concatExpr) eval(valueMap map[string]starlarkExpr) (res starlarkExpr, same bool) {
485 same = true
486 xConcat := &concatExpr{items: make([]starlarkExpr, len(c.items))}
487 for i, item := range c.items {
488 var sameItem bool
489 xConcat.items[i], sameItem = item.eval(valueMap)
490 same = same && sameItem
491 }
492 if same {
493 res = c
494 } else {
495 res = xConcat
496 }
497 return
498}
499
500func (_ *concatExpr) typ() starlarkType {
501 return starlarkTypeList
502}
503
504func (c *concatExpr) emitListVarCopy(gctx *generationContext) {
505 c.emit(gctx)
506}
507
508// inExpr generates <expr> [not] in <list>
509type inExpr struct {
510 expr starlarkExpr
511 list starlarkExpr
512 isNot bool
513}
514
515func (i *inExpr) eval(valueMap map[string]starlarkExpr) (res starlarkExpr, same bool) {
516 x := &inExpr{isNot: i.isNot}
517 var sameExpr, sameList bool
518 x.expr, sameExpr = i.expr.eval(valueMap)
519 x.list, sameList = i.list.eval(valueMap)
520 if same = sameExpr && sameList; same {
521 res = i
522 } else {
523 res = x
524 }
525 return
526}
527
528func (i *inExpr) emit(gctx *generationContext) {
529 i.expr.emit(gctx)
530 if i.isNot {
531 gctx.write(" not in ")
532 } else {
533 gctx.write(" in ")
534 }
535 i.list.emit(gctx)
536}
537
538func (_ *inExpr) typ() starlarkType {
539 return starlarkTypeBool
540}
541
542func (i *inExpr) emitListVarCopy(gctx *generationContext) {
543 i.emit(gctx)
544}
545
546type indexExpr struct {
547 array starlarkExpr
548 index starlarkExpr
549}
550
551func (ix indexExpr) emit(gctx *generationContext) {
552 ix.array.emit(gctx)
553 gctx.write("[")
554 ix.index.emit(gctx)
555 gctx.write("]")
556}
557
558func (ix indexExpr) typ() starlarkType {
559 return starlarkTypeString
560}
561
562func (ix indexExpr) eval(valueMap map[string]starlarkExpr) (res starlarkExpr, same bool) {
563 newArray, isSameArray := ix.array.eval(valueMap)
564 newIndex, isSameIndex := ix.index.eval(valueMap)
565 if same = isSameArray && isSameIndex; same {
566 res = ix
567 } else {
568 res = &indexExpr{newArray, newIndex}
569 }
570 return
571}
572
573func (ix indexExpr) emitListVarCopy(gctx *generationContext) {
574 ix.emit(gctx)
575}
576
577type callExpr struct {
578 object starlarkExpr // nil if static call
579 name string
580 args []starlarkExpr
581 returnType starlarkType
582}
583
584func (cx *callExpr) eval(valueMap map[string]starlarkExpr) (res starlarkExpr, same bool) {
585 newCallExpr := &callExpr{name: cx.name, args: make([]starlarkExpr, len(cx.args)),
586 returnType: cx.returnType}
587 if cx.object != nil {
588 newCallExpr.object, same = cx.object.eval(valueMap)
589 } else {
590 same = true
591 }
592 for i, args := range cx.args {
593 var s bool
594 newCallExpr.args[i], s = args.eval(valueMap)
595 same = same && s
596 }
597 if same {
598 res = cx
599 } else {
600 res = newCallExpr
601 }
602 return
603}
604
605func (cx *callExpr) emit(gctx *generationContext) {
Sasha Smundak3deb9682021-07-26 18:42:25 -0700606 sep := ""
Sasha Smundakb051c4e2020-11-05 20:45:07 -0800607 if cx.object != nil {
608 gctx.write("(")
609 cx.object.emit(gctx)
610 gctx.write(")")
611 gctx.write(".", cx.name, "(")
612 } else {
613 kf, found := knownFunctions[cx.name]
614 if !found {
615 panic(fmt.Errorf("callExpr with unknown function %q", cx.name))
616 }
617 if kf.runtimeName[0] == '!' {
618 panic(fmt.Errorf("callExpr for %q should not be there", cx.name))
619 }
620 gctx.write(kf.runtimeName, "(")
Sasha Smundak3deb9682021-07-26 18:42:25 -0700621 if kf.hiddenArg == hiddenArgGlobal {
622 gctx.write("g")
623 sep = ", "
624 } else if kf.hiddenArg == hiddenArgConfig {
625 gctx.write("cfg")
626 sep = ", "
627 }
Sasha Smundakb051c4e2020-11-05 20:45:07 -0800628 }
Sasha Smundakb051c4e2020-11-05 20:45:07 -0800629 for _, arg := range cx.args {
630 gctx.write(sep)
631 arg.emit(gctx)
632 sep = ", "
633 }
634 gctx.write(")")
635}
636
637func (cx *callExpr) typ() starlarkType {
638 return cx.returnType
639}
640
641func (cx *callExpr) emitListVarCopy(gctx *generationContext) {
642 cx.emit(gctx)
643}
644
Cole Faust4eadba72021-12-07 11:54:52 -0800645type ifExpr struct {
646 condition starlarkExpr
647 ifTrue starlarkExpr
648 ifFalse starlarkExpr
649}
650
651func (i *ifExpr) eval(valueMap map[string]starlarkExpr) (res starlarkExpr, same bool) {
652 cond, condSame := i.condition.eval(valueMap)
653 t, tSame := i.ifTrue.eval(valueMap)
654 f, fSame := i.ifFalse.eval(valueMap)
655 same = condSame && tSame && fSame
656 if same {
657 return i, same
658 } else {
659 return &ifExpr{
660 condition: cond,
661 ifTrue: t,
662 ifFalse: f,
663 }, same
664 }
665}
666
667func (i *ifExpr) emit(gctx *generationContext) {
668 gctx.write("(")
669 i.ifTrue.emit(gctx)
670 gctx.write(" if ")
671 i.condition.emit(gctx)
672 gctx.write(" else ")
673 i.ifFalse.emit(gctx)
674 gctx.write(")")
675}
676
677func (i *ifExpr) typ() starlarkType {
678 tType := i.ifTrue.typ()
679 fType := i.ifFalse.typ()
680 if tType != fType && tType != starlarkTypeUnknown && fType != starlarkTypeUnknown {
681 panic("Conflicting types in if expression")
682 }
683 if tType != starlarkTypeUnknown {
684 return tType
685 } else {
686 return fType
687 }
688}
689
690func (i *ifExpr) emitListVarCopy(gctx *generationContext) {
691 i.emit(gctx)
692}
693
Sasha Smundakb051c4e2020-11-05 20:45:07 -0800694type badExpr struct {
Sasha Smundak422b6142021-11-11 18:31:59 -0800695 errorLocation ErrorLocation
696 message string
Sasha Smundakb051c4e2020-11-05 20:45:07 -0800697}
698
699func (b *badExpr) eval(_ map[string]starlarkExpr) (res starlarkExpr, same bool) {
700 res = b
701 same = true
702 return
703}
704
Sasha Smundak422b6142021-11-11 18:31:59 -0800705func (b *badExpr) emit(gctx *generationContext) {
706 gctx.emitConversionError(b.errorLocation, b.message)
Sasha Smundakb051c4e2020-11-05 20:45:07 -0800707}
708
709func (_ *badExpr) typ() starlarkType {
710 return starlarkTypeUnknown
711}
712
Sasha Smundak422b6142021-11-11 18:31:59 -0800713func (_ *badExpr) emitListVarCopy(_ *generationContext) {
Sasha Smundakb051c4e2020-11-05 20:45:07 -0800714 panic("implement me")
715}
716
717func maybeConvertToStringList(expr starlarkExpr) starlarkExpr {
718 if xString, ok := expr.(*stringLiteralExpr); ok {
719 return newStringListExpr(strings.Fields(xString.literal))
720 }
721 return expr
722}
Sasha Smundak0554d762021-07-08 18:26:12 -0700723
724func isEmptyString(expr starlarkExpr) bool {
725 x, ok := expr.(*stringLiteralExpr)
726 return ok && x.literal == ""
727}