blob: b06ed90e3b8da50a395235f837b9eafb8b218f05 [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"
21
22 mkparser "android/soong/androidmk/parser"
23)
24
25// Represents an expression in the Starlark code. An expression has
26// a type, and it can be evaluated.
27type starlarkExpr interface {
28 starlarkNode
29 typ() starlarkType
30 // Try to substitute variable values. Return substitution result
31 // and whether it is the same as the original expression.
32 eval(valueMap map[string]starlarkExpr) (res starlarkExpr, same bool)
33 // Emit the code to copy the expression, otherwise we will end up
34 // with source and target pointing to the same list.
35 emitListVarCopy(gctx *generationContext)
36}
37
38func maybeString(expr starlarkExpr) (string, bool) {
39 if x, ok := expr.(*stringLiteralExpr); ok {
40 return x.literal, true
41 }
42 return "", false
43}
44
45type stringLiteralExpr struct {
46 literal string
47}
48
49func (s *stringLiteralExpr) eval(_ map[string]starlarkExpr) (res starlarkExpr, same bool) {
50 res = s
51 same = true
52 return
53}
54
55func (s *stringLiteralExpr) emit(gctx *generationContext) {
56 gctx.writef("%q", s.literal)
57}
58
59func (_ *stringLiteralExpr) typ() starlarkType {
60 return starlarkTypeString
61}
62
63func (s *stringLiteralExpr) emitListVarCopy(gctx *generationContext) {
64 s.emit(gctx)
65}
66
67// Integer literal
68type intLiteralExpr struct {
69 literal int
70}
71
72func (s *intLiteralExpr) eval(_ map[string]starlarkExpr) (res starlarkExpr, same bool) {
73 res = s
74 same = true
75 return
76}
77
78func (s *intLiteralExpr) emit(gctx *generationContext) {
79 gctx.writef("%d", s.literal)
80}
81
82func (_ *intLiteralExpr) typ() starlarkType {
83 return starlarkTypeInt
84}
85
86func (s *intLiteralExpr) emitListVarCopy(gctx *generationContext) {
87 s.emit(gctx)
88}
89
90// interpolateExpr represents Starlark's interpolation operator <string> % list
91// we break <string> into a list of chunks, i.e., "first%second%third" % (X, Y)
92// will have chunks = ["first", "second", "third"] and args = [X, Y]
93type interpolateExpr struct {
94 chunks []string // string chunks, separated by '%'
95 args []starlarkExpr
96}
97
98func (xi *interpolateExpr) emit(gctx *generationContext) {
99 if len(xi.chunks) != len(xi.args)+1 {
100 panic(fmt.Errorf("malformed interpolateExpr: #chunks(%d) != #args(%d)+1",
101 len(xi.chunks), len(xi.args)))
102 }
103 // Generate format as join of chunks, but first escape '%' in them
104 format := strings.ReplaceAll(xi.chunks[0], "%", "%%")
105 for _, chunk := range xi.chunks[1:] {
106 format += "%s" + strings.ReplaceAll(chunk, "%", "%%")
107 }
108 gctx.writef("%q %% ", format)
109 emitarg := func(arg starlarkExpr) {
110 if arg.typ() == starlarkTypeList {
111 gctx.write(`" ".join(`)
112 arg.emit(gctx)
113 gctx.write(`)`)
114 } else {
115 arg.emit(gctx)
116 }
117 }
118 if len(xi.args) == 1 {
119 emitarg(xi.args[0])
120 } else {
121 sep := "("
122 for _, arg := range xi.args {
123 gctx.write(sep)
124 emitarg(arg)
125 sep = ", "
126 }
127 gctx.write(")")
128 }
129}
130
131func (xi *interpolateExpr) eval(valueMap map[string]starlarkExpr) (res starlarkExpr, same bool) {
132 same = true
133 newChunks := []string{xi.chunks[0]}
134 var newArgs []starlarkExpr
135 for i, arg := range xi.args {
136 newArg, sameArg := arg.eval(valueMap)
137 same = same && sameArg
138 switch x := newArg.(type) {
139 case *stringLiteralExpr:
140 newChunks[len(newChunks)-1] += x.literal + xi.chunks[i+1]
141 same = false
142 continue
143 case *intLiteralExpr:
144 newChunks[len(newChunks)-1] += strconv.Itoa(x.literal) + xi.chunks[i+1]
145 same = false
146 continue
147 default:
148 newChunks = append(newChunks, xi.chunks[i+1])
149 newArgs = append(newArgs, newArg)
150 }
151 }
152 if same {
153 res = xi
154 } else if len(newChunks) == 1 {
155 res = &stringLiteralExpr{newChunks[0]}
156 } else {
157 res = &interpolateExpr{chunks: newChunks, args: newArgs}
158 }
159 return
160}
161
162func (_ *interpolateExpr) typ() starlarkType {
163 return starlarkTypeString
164}
165
166func (xi *interpolateExpr) emitListVarCopy(gctx *generationContext) {
167 xi.emit(gctx)
168}
169
170type variableRefExpr struct {
171 ref variable
172 isDefined bool
173}
174
175func (v *variableRefExpr) eval(map[string]starlarkExpr) (res starlarkExpr, same bool) {
176 predefined, ok := v.ref.(*predefinedVariable)
177 if same = !ok; same {
178 res = v
179 } else {
180 res = predefined.value
181 }
182 return
183}
184
185func (v *variableRefExpr) emit(gctx *generationContext) {
186 v.ref.emitGet(gctx, v.isDefined)
187}
188
189func (v *variableRefExpr) typ() starlarkType {
190 return v.ref.valueType()
191}
192
193func (v *variableRefExpr) emitListVarCopy(gctx *generationContext) {
194 v.emit(gctx)
195 if v.typ() == starlarkTypeList {
196 gctx.write("[:]") // this will copy the list
197 }
198}
199
200type notExpr struct {
201 expr starlarkExpr
202}
203
204func (n *notExpr) eval(valueMap map[string]starlarkExpr) (res starlarkExpr, same bool) {
205 if x, same := n.expr.eval(valueMap); same {
206 res = n
207 } else {
208 res = &notExpr{expr: x}
209 }
210 return
211}
212
213func (n *notExpr) emit(ctx *generationContext) {
214 ctx.write("not ")
215 n.expr.emit(ctx)
216}
217
218func (_ *notExpr) typ() starlarkType {
219 return starlarkTypeBool
220}
221
222func (n *notExpr) emitListVarCopy(gctx *generationContext) {
223 n.emit(gctx)
224}
225
226type eqExpr struct {
227 left, right starlarkExpr
228 isEq bool // if false, it's !=
229}
230
231func (eq *eqExpr) eval(valueMap map[string]starlarkExpr) (res starlarkExpr, same bool) {
232 xLeft, sameLeft := eq.left.eval(valueMap)
233 xRight, sameRight := eq.right.eval(valueMap)
234 if same = sameLeft && sameRight; same {
235 res = eq
236 } else {
237 res = &eqExpr{left: xLeft, right: xRight, isEq: eq.isEq}
238 }
239 return
240}
241
242func (eq *eqExpr) emit(gctx *generationContext) {
243 // Are we checking that a variable is empty?
244 var varRef *variableRefExpr
245 if s, ok := maybeString(eq.left); ok && s == "" {
246 varRef, ok = eq.right.(*variableRefExpr)
247 } else if s, ok := maybeString(eq.right); ok && s == "" {
248 varRef, ok = eq.left.(*variableRefExpr)
249 }
250 if varRef != nil {
251 // Yes.
252 if eq.isEq {
253 gctx.write("not ")
254 }
255 varRef.emit(gctx)
256 return
257 }
258
259 // General case
260 eq.left.emit(gctx)
261 if eq.isEq {
262 gctx.write(" == ")
263 } else {
264 gctx.write(" != ")
265 }
266 eq.right.emit(gctx)
267}
268
269func (_ *eqExpr) typ() starlarkType {
270 return starlarkTypeBool
271}
272
273func (eq *eqExpr) emitListVarCopy(gctx *generationContext) {
274 eq.emit(gctx)
275}
276
277// variableDefinedExpr corresponds to Make's ifdef VAR
278type variableDefinedExpr struct {
279 v variable
280}
281
282func (v *variableDefinedExpr) eval(_ map[string]starlarkExpr) (res starlarkExpr, same bool) {
283 res = v
284 same = true
285 return
286
287}
288
289func (v *variableDefinedExpr) emit(gctx *generationContext) {
290 if v.v != nil {
291 v.v.emitDefined(gctx)
292 return
293 }
294 gctx.writef("%s(%q)", cfnWarning, "TODO(VAR)")
295}
296
297func (_ *variableDefinedExpr) typ() starlarkType {
298 return starlarkTypeBool
299}
300
301func (v *variableDefinedExpr) emitListVarCopy(gctx *generationContext) {
302 v.emit(gctx)
303}
304
305type listExpr struct {
306 items []starlarkExpr
307}
308
309func (l *listExpr) eval(valueMap map[string]starlarkExpr) (res starlarkExpr, same bool) {
310 newItems := make([]starlarkExpr, len(l.items))
311 same = true
312 for i, item := range l.items {
313 var sameItem bool
314 newItems[i], sameItem = item.eval(valueMap)
315 same = same && sameItem
316 }
317 if same {
318 res = l
319 } else {
320 res = &listExpr{newItems}
321 }
322 return
323}
324
325func (l *listExpr) emit(gctx *generationContext) {
326 if !gctx.inAssignment || len(l.items) < 2 {
327 gctx.write("[")
328 sep := ""
329 for _, item := range l.items {
330 gctx.write(sep)
331 item.emit(gctx)
332 sep = ", "
333 }
334 gctx.write("]")
335 return
336 }
337
338 gctx.write("[")
339 gctx.indentLevel += 2
340
341 for _, item := range l.items {
342 gctx.newLine()
343 item.emit(gctx)
344 gctx.write(",")
345 }
346 gctx.indentLevel -= 2
347 gctx.newLine()
348 gctx.write("]")
349}
350
351func (_ *listExpr) typ() starlarkType {
352 return starlarkTypeList
353}
354
355func (l *listExpr) emitListVarCopy(gctx *generationContext) {
356 l.emit(gctx)
357}
358
359func newStringListExpr(items []string) *listExpr {
360 v := listExpr{}
361 for _, item := range items {
362 v.items = append(v.items, &stringLiteralExpr{item})
363 }
364 return &v
365}
366
367// concatExpr generates epxr1 + expr2 + ... + exprN in Starlark.
368type concatExpr struct {
369 items []starlarkExpr
370}
371
372func (c *concatExpr) emit(gctx *generationContext) {
373 if len(c.items) == 1 {
374 c.items[0].emit(gctx)
375 return
376 }
377
378 if !gctx.inAssignment {
379 c.items[0].emit(gctx)
380 for _, item := range c.items[1:] {
381 gctx.write(" + ")
382 item.emit(gctx)
383 }
384 return
385 }
386 gctx.write("(")
387 c.items[0].emit(gctx)
388 gctx.indentLevel += 2
389 for _, item := range c.items[1:] {
390 gctx.write(" +")
391 gctx.newLine()
392 item.emit(gctx)
393 }
394 gctx.write(")")
395 gctx.indentLevel -= 2
396}
397
398func (c *concatExpr) eval(valueMap map[string]starlarkExpr) (res starlarkExpr, same bool) {
399 same = true
400 xConcat := &concatExpr{items: make([]starlarkExpr, len(c.items))}
401 for i, item := range c.items {
402 var sameItem bool
403 xConcat.items[i], sameItem = item.eval(valueMap)
404 same = same && sameItem
405 }
406 if same {
407 res = c
408 } else {
409 res = xConcat
410 }
411 return
412}
413
414func (_ *concatExpr) typ() starlarkType {
415 return starlarkTypeList
416}
417
418func (c *concatExpr) emitListVarCopy(gctx *generationContext) {
419 c.emit(gctx)
420}
421
422// inExpr generates <expr> [not] in <list>
423type inExpr struct {
424 expr starlarkExpr
425 list starlarkExpr
426 isNot bool
427}
428
429func (i *inExpr) eval(valueMap map[string]starlarkExpr) (res starlarkExpr, same bool) {
430 x := &inExpr{isNot: i.isNot}
431 var sameExpr, sameList bool
432 x.expr, sameExpr = i.expr.eval(valueMap)
433 x.list, sameList = i.list.eval(valueMap)
434 if same = sameExpr && sameList; same {
435 res = i
436 } else {
437 res = x
438 }
439 return
440}
441
442func (i *inExpr) emit(gctx *generationContext) {
443 i.expr.emit(gctx)
444 if i.isNot {
445 gctx.write(" not in ")
446 } else {
447 gctx.write(" in ")
448 }
449 i.list.emit(gctx)
450}
451
452func (_ *inExpr) typ() starlarkType {
453 return starlarkTypeBool
454}
455
456func (i *inExpr) emitListVarCopy(gctx *generationContext) {
457 i.emit(gctx)
458}
459
460type indexExpr struct {
461 array starlarkExpr
462 index starlarkExpr
463}
464
465func (ix indexExpr) emit(gctx *generationContext) {
466 ix.array.emit(gctx)
467 gctx.write("[")
468 ix.index.emit(gctx)
469 gctx.write("]")
470}
471
472func (ix indexExpr) typ() starlarkType {
473 return starlarkTypeString
474}
475
476func (ix indexExpr) eval(valueMap map[string]starlarkExpr) (res starlarkExpr, same bool) {
477 newArray, isSameArray := ix.array.eval(valueMap)
478 newIndex, isSameIndex := ix.index.eval(valueMap)
479 if same = isSameArray && isSameIndex; same {
480 res = ix
481 } else {
482 res = &indexExpr{newArray, newIndex}
483 }
484 return
485}
486
487func (ix indexExpr) emitListVarCopy(gctx *generationContext) {
488 ix.emit(gctx)
489}
490
491type callExpr struct {
492 object starlarkExpr // nil if static call
493 name string
494 args []starlarkExpr
495 returnType starlarkType
496}
497
498func (cx *callExpr) eval(valueMap map[string]starlarkExpr) (res starlarkExpr, same bool) {
499 newCallExpr := &callExpr{name: cx.name, args: make([]starlarkExpr, len(cx.args)),
500 returnType: cx.returnType}
501 if cx.object != nil {
502 newCallExpr.object, same = cx.object.eval(valueMap)
503 } else {
504 same = true
505 }
506 for i, args := range cx.args {
507 var s bool
508 newCallExpr.args[i], s = args.eval(valueMap)
509 same = same && s
510 }
511 if same {
512 res = cx
513 } else {
514 res = newCallExpr
515 }
516 return
517}
518
519func (cx *callExpr) emit(gctx *generationContext) {
520 if cx.object != nil {
521 gctx.write("(")
522 cx.object.emit(gctx)
523 gctx.write(")")
524 gctx.write(".", cx.name, "(")
525 } else {
526 kf, found := knownFunctions[cx.name]
527 if !found {
528 panic(fmt.Errorf("callExpr with unknown function %q", cx.name))
529 }
530 if kf.runtimeName[0] == '!' {
531 panic(fmt.Errorf("callExpr for %q should not be there", cx.name))
532 }
533 gctx.write(kf.runtimeName, "(")
534 }
535 sep := ""
536 for _, arg := range cx.args {
537 gctx.write(sep)
538 arg.emit(gctx)
539 sep = ", "
540 }
541 gctx.write(")")
542}
543
544func (cx *callExpr) typ() starlarkType {
545 return cx.returnType
546}
547
548func (cx *callExpr) emitListVarCopy(gctx *generationContext) {
549 cx.emit(gctx)
550}
551
552type badExpr struct {
553 node mkparser.Node
554 message string
555}
556
557func (b *badExpr) eval(_ map[string]starlarkExpr) (res starlarkExpr, same bool) {
558 res = b
559 same = true
560 return
561}
562
563func (b *badExpr) emit(_ *generationContext) {
564 panic("implement me")
565}
566
567func (_ *badExpr) typ() starlarkType {
568 return starlarkTypeUnknown
569}
570
571func (b *badExpr) emitListVarCopy(gctx *generationContext) {
572 panic("implement me")
573}
574
575func maybeConvertToStringList(expr starlarkExpr) starlarkExpr {
576 if xString, ok := expr.(*stringLiteralExpr); ok {
577 return newStringListExpr(strings.Fields(xString.literal))
578 }
579 return expr
580}