blob: 65fd6f7c022294f10a9117ddd12cef05da481dc8 [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:
225 ctx.write("((")
226 s.expr.emit(ctx)
227 ctx.write(`) ? "true" : "")`)
228 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) {
Sasha Smundak0554d762021-07-08 18:26:12 -0700286 emitSimple := func(expr starlarkExpr) {
Sasha Smundakb051c4e2020-11-05 20:45:07 -0800287 if eq.isEq {
288 gctx.write("not ")
289 }
Sasha Smundak0554d762021-07-08 18:26:12 -0700290 expr.emit(gctx)
Sasha Smundakb051c4e2020-11-05 20:45:07 -0800291 }
Sasha Smundak0554d762021-07-08 18:26:12 -0700292 // Are we checking that a variable is empty?
293 if isEmptyString(eq.left) {
294 emitSimple(eq.right)
295 return
296 } else if isEmptyString(eq.right) {
297 emitSimple(eq.left)
298 return
Sasha Smundakb051c4e2020-11-05 20:45:07 -0800299
Sasha Smundak0554d762021-07-08 18:26:12 -0700300 }
Cole Faustf8320212021-11-10 15:05:07 -0800301
302 if eq.left.typ() != eq.right.typ() {
303 eq.left = &toStringExpr{expr: eq.left}
304 eq.right = &toStringExpr{expr: eq.right}
305 }
306
Sasha Smundakb051c4e2020-11-05 20:45:07 -0800307 // General case
308 eq.left.emit(gctx)
309 if eq.isEq {
310 gctx.write(" == ")
311 } else {
312 gctx.write(" != ")
313 }
314 eq.right.emit(gctx)
315}
316
317func (_ *eqExpr) typ() starlarkType {
318 return starlarkTypeBool
319}
320
321func (eq *eqExpr) emitListVarCopy(gctx *generationContext) {
322 eq.emit(gctx)
323}
324
325// variableDefinedExpr corresponds to Make's ifdef VAR
326type variableDefinedExpr struct {
327 v variable
328}
329
330func (v *variableDefinedExpr) eval(_ map[string]starlarkExpr) (res starlarkExpr, same bool) {
331 res = v
332 same = true
333 return
334
335}
336
337func (v *variableDefinedExpr) emit(gctx *generationContext) {
338 if v.v != nil {
339 v.v.emitDefined(gctx)
340 return
341 }
342 gctx.writef("%s(%q)", cfnWarning, "TODO(VAR)")
343}
344
345func (_ *variableDefinedExpr) typ() starlarkType {
346 return starlarkTypeBool
347}
348
349func (v *variableDefinedExpr) emitListVarCopy(gctx *generationContext) {
350 v.emit(gctx)
351}
352
353type listExpr struct {
354 items []starlarkExpr
355}
356
357func (l *listExpr) eval(valueMap map[string]starlarkExpr) (res starlarkExpr, same bool) {
358 newItems := make([]starlarkExpr, len(l.items))
359 same = true
360 for i, item := range l.items {
361 var sameItem bool
362 newItems[i], sameItem = item.eval(valueMap)
363 same = same && sameItem
364 }
365 if same {
366 res = l
367 } else {
368 res = &listExpr{newItems}
369 }
370 return
371}
372
373func (l *listExpr) emit(gctx *generationContext) {
374 if !gctx.inAssignment || len(l.items) < 2 {
375 gctx.write("[")
376 sep := ""
377 for _, item := range l.items {
378 gctx.write(sep)
379 item.emit(gctx)
380 sep = ", "
381 }
382 gctx.write("]")
383 return
384 }
385
386 gctx.write("[")
387 gctx.indentLevel += 2
388
389 for _, item := range l.items {
390 gctx.newLine()
391 item.emit(gctx)
392 gctx.write(",")
393 }
394 gctx.indentLevel -= 2
395 gctx.newLine()
396 gctx.write("]")
397}
398
399func (_ *listExpr) typ() starlarkType {
400 return starlarkTypeList
401}
402
403func (l *listExpr) emitListVarCopy(gctx *generationContext) {
404 l.emit(gctx)
405}
406
407func newStringListExpr(items []string) *listExpr {
408 v := listExpr{}
409 for _, item := range items {
410 v.items = append(v.items, &stringLiteralExpr{item})
411 }
412 return &v
413}
414
Sasha Smundak422b6142021-11-11 18:31:59 -0800415// concatExpr generates expr1 + expr2 + ... + exprN in Starlark.
Sasha Smundakb051c4e2020-11-05 20:45:07 -0800416type concatExpr struct {
417 items []starlarkExpr
418}
419
420func (c *concatExpr) emit(gctx *generationContext) {
421 if len(c.items) == 1 {
422 c.items[0].emit(gctx)
423 return
424 }
425
426 if !gctx.inAssignment {
427 c.items[0].emit(gctx)
428 for _, item := range c.items[1:] {
429 gctx.write(" + ")
430 item.emit(gctx)
431 }
432 return
433 }
434 gctx.write("(")
435 c.items[0].emit(gctx)
436 gctx.indentLevel += 2
437 for _, item := range c.items[1:] {
438 gctx.write(" +")
439 gctx.newLine()
440 item.emit(gctx)
441 }
442 gctx.write(")")
443 gctx.indentLevel -= 2
444}
445
446func (c *concatExpr) eval(valueMap map[string]starlarkExpr) (res starlarkExpr, same bool) {
447 same = true
448 xConcat := &concatExpr{items: make([]starlarkExpr, len(c.items))}
449 for i, item := range c.items {
450 var sameItem bool
451 xConcat.items[i], sameItem = item.eval(valueMap)
452 same = same && sameItem
453 }
454 if same {
455 res = c
456 } else {
457 res = xConcat
458 }
459 return
460}
461
462func (_ *concatExpr) typ() starlarkType {
463 return starlarkTypeList
464}
465
466func (c *concatExpr) emitListVarCopy(gctx *generationContext) {
467 c.emit(gctx)
468}
469
470// inExpr generates <expr> [not] in <list>
471type inExpr struct {
472 expr starlarkExpr
473 list starlarkExpr
474 isNot bool
475}
476
477func (i *inExpr) eval(valueMap map[string]starlarkExpr) (res starlarkExpr, same bool) {
478 x := &inExpr{isNot: i.isNot}
479 var sameExpr, sameList bool
480 x.expr, sameExpr = i.expr.eval(valueMap)
481 x.list, sameList = i.list.eval(valueMap)
482 if same = sameExpr && sameList; same {
483 res = i
484 } else {
485 res = x
486 }
487 return
488}
489
490func (i *inExpr) emit(gctx *generationContext) {
491 i.expr.emit(gctx)
492 if i.isNot {
493 gctx.write(" not in ")
494 } else {
495 gctx.write(" in ")
496 }
497 i.list.emit(gctx)
498}
499
500func (_ *inExpr) typ() starlarkType {
501 return starlarkTypeBool
502}
503
504func (i *inExpr) emitListVarCopy(gctx *generationContext) {
505 i.emit(gctx)
506}
507
508type indexExpr struct {
509 array starlarkExpr
510 index starlarkExpr
511}
512
513func (ix indexExpr) emit(gctx *generationContext) {
514 ix.array.emit(gctx)
515 gctx.write("[")
516 ix.index.emit(gctx)
517 gctx.write("]")
518}
519
520func (ix indexExpr) typ() starlarkType {
521 return starlarkTypeString
522}
523
524func (ix indexExpr) eval(valueMap map[string]starlarkExpr) (res starlarkExpr, same bool) {
525 newArray, isSameArray := ix.array.eval(valueMap)
526 newIndex, isSameIndex := ix.index.eval(valueMap)
527 if same = isSameArray && isSameIndex; same {
528 res = ix
529 } else {
530 res = &indexExpr{newArray, newIndex}
531 }
532 return
533}
534
535func (ix indexExpr) emitListVarCopy(gctx *generationContext) {
536 ix.emit(gctx)
537}
538
539type callExpr struct {
540 object starlarkExpr // nil if static call
541 name string
542 args []starlarkExpr
543 returnType starlarkType
544}
545
546func (cx *callExpr) eval(valueMap map[string]starlarkExpr) (res starlarkExpr, same bool) {
547 newCallExpr := &callExpr{name: cx.name, args: make([]starlarkExpr, len(cx.args)),
548 returnType: cx.returnType}
549 if cx.object != nil {
550 newCallExpr.object, same = cx.object.eval(valueMap)
551 } else {
552 same = true
553 }
554 for i, args := range cx.args {
555 var s bool
556 newCallExpr.args[i], s = args.eval(valueMap)
557 same = same && s
558 }
559 if same {
560 res = cx
561 } else {
562 res = newCallExpr
563 }
564 return
565}
566
567func (cx *callExpr) emit(gctx *generationContext) {
Sasha Smundak3deb9682021-07-26 18:42:25 -0700568 sep := ""
Sasha Smundakb051c4e2020-11-05 20:45:07 -0800569 if cx.object != nil {
570 gctx.write("(")
571 cx.object.emit(gctx)
572 gctx.write(")")
573 gctx.write(".", cx.name, "(")
574 } else {
575 kf, found := knownFunctions[cx.name]
576 if !found {
577 panic(fmt.Errorf("callExpr with unknown function %q", cx.name))
578 }
579 if kf.runtimeName[0] == '!' {
580 panic(fmt.Errorf("callExpr for %q should not be there", cx.name))
581 }
582 gctx.write(kf.runtimeName, "(")
Sasha Smundak3deb9682021-07-26 18:42:25 -0700583 if kf.hiddenArg == hiddenArgGlobal {
584 gctx.write("g")
585 sep = ", "
586 } else if kf.hiddenArg == hiddenArgConfig {
587 gctx.write("cfg")
588 sep = ", "
589 }
Sasha Smundakb051c4e2020-11-05 20:45:07 -0800590 }
Sasha Smundakb051c4e2020-11-05 20:45:07 -0800591 for _, arg := range cx.args {
592 gctx.write(sep)
593 arg.emit(gctx)
594 sep = ", "
595 }
596 gctx.write(")")
597}
598
599func (cx *callExpr) typ() starlarkType {
600 return cx.returnType
601}
602
603func (cx *callExpr) emitListVarCopy(gctx *generationContext) {
604 cx.emit(gctx)
605}
606
607type badExpr struct {
Sasha Smundak422b6142021-11-11 18:31:59 -0800608 errorLocation ErrorLocation
609 message string
Sasha Smundakb051c4e2020-11-05 20:45:07 -0800610}
611
612func (b *badExpr) eval(_ map[string]starlarkExpr) (res starlarkExpr, same bool) {
613 res = b
614 same = true
615 return
616}
617
Sasha Smundak422b6142021-11-11 18:31:59 -0800618func (b *badExpr) emit(gctx *generationContext) {
619 gctx.emitConversionError(b.errorLocation, b.message)
Sasha Smundakb051c4e2020-11-05 20:45:07 -0800620}
621
622func (_ *badExpr) typ() starlarkType {
623 return starlarkTypeUnknown
624}
625
Sasha Smundak422b6142021-11-11 18:31:59 -0800626func (_ *badExpr) emitListVarCopy(_ *generationContext) {
Sasha Smundakb051c4e2020-11-05 20:45:07 -0800627 panic("implement me")
628}
629
630func maybeConvertToStringList(expr starlarkExpr) starlarkExpr {
631 if xString, ok := expr.(*stringLiteralExpr); ok {
632 return newStringListExpr(strings.Fields(xString.literal))
633 }
634 return expr
635}
Sasha Smundak0554d762021-07-08 18:26:12 -0700636
637func isEmptyString(expr starlarkExpr) bool {
638 x, ok := expr.(*stringLiteralExpr)
639 return ok && x.literal == ""
640}