• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// 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	"strings"
20)
21
22// Represents an expression in the Starlark code. An expression has a type.
23type starlarkExpr interface {
24	starlarkNode
25	typ() starlarkType
26	// Emit the code to copy the expression, otherwise we will end up
27	// with source and target pointing to the same list.
28	emitListVarCopy(gctx *generationContext)
29	// Return the expression, calling the transformer func for
30	// every expression in the tree. If the transformer func returns non-nil,
31	// its result is used in place of the expression it was called with in the
32	// resulting expression. The resulting starlarkExpr will contain as many
33	// of the same objects from the original expression as possible.
34	transform(transformer func(expr starlarkExpr) starlarkExpr) starlarkExpr
35}
36
37func maybeString(expr starlarkExpr) (string, bool) {
38	if x, ok := expr.(*stringLiteralExpr); ok {
39		return x.literal, true
40	}
41	return "", false
42}
43
44type stringLiteralExpr struct {
45	literal string
46}
47
48func (s *stringLiteralExpr) emit(gctx *generationContext) {
49	gctx.writef("%q", s.literal)
50}
51
52func (_ *stringLiteralExpr) typ() starlarkType {
53	return starlarkTypeString
54}
55
56func (s *stringLiteralExpr) emitListVarCopy(gctx *generationContext) {
57	s.emit(gctx)
58}
59
60func (s *stringLiteralExpr) transform(transformer func(expr starlarkExpr) starlarkExpr) starlarkExpr {
61	if replacement := transformer(s); replacement != nil {
62		return replacement
63	} else {
64		return s
65	}
66}
67
68// Integer literal
69type intLiteralExpr struct {
70	literal int
71}
72
73func (s *intLiteralExpr) emit(gctx *generationContext) {
74	gctx.writef("%d", s.literal)
75}
76
77func (_ *intLiteralExpr) typ() starlarkType {
78	return starlarkTypeInt
79}
80
81func (s *intLiteralExpr) emitListVarCopy(gctx *generationContext) {
82	s.emit(gctx)
83}
84
85func (s *intLiteralExpr) transform(transformer func(expr starlarkExpr) starlarkExpr) starlarkExpr {
86	if replacement := transformer(s); replacement != nil {
87		return replacement
88	} else {
89		return s
90	}
91}
92
93// Boolean literal
94type boolLiteralExpr struct {
95	literal bool
96}
97
98func (b *boolLiteralExpr) emit(gctx *generationContext) {
99	if b.literal {
100		gctx.write("True")
101	} else {
102		gctx.write("False")
103	}
104}
105
106func (_ *boolLiteralExpr) typ() starlarkType {
107	return starlarkTypeBool
108}
109
110func (b *boolLiteralExpr) emitListVarCopy(gctx *generationContext) {
111	b.emit(gctx)
112}
113
114func (b *boolLiteralExpr) transform(transformer func(expr starlarkExpr) starlarkExpr) starlarkExpr {
115	if replacement := transformer(b); replacement != nil {
116		return replacement
117	} else {
118		return b
119	}
120}
121
122type globalsExpr struct {
123}
124
125func (g *globalsExpr) emit(gctx *generationContext) {
126	gctx.write("g")
127}
128
129func (g *globalsExpr) typ() starlarkType {
130	return starlarkTypeUnknown
131}
132
133func (g *globalsExpr) emitListVarCopy(gctx *generationContext) {
134	g.emit(gctx)
135}
136
137func (g *globalsExpr) transform(transformer func(expr starlarkExpr) starlarkExpr) starlarkExpr {
138	if replacement := transformer(g); replacement != nil {
139		return replacement
140	} else {
141		return g
142	}
143}
144
145// interpolateExpr represents Starlark's interpolation operator <string> % list
146// we break <string> into a list of chunks, i.e., "first%second%third" % (X, Y)
147// will have chunks = ["first", "second", "third"] and args = [X, Y]
148type interpolateExpr struct {
149	chunks []string // string chunks, separated by '%'
150	args   []starlarkExpr
151}
152
153func NewInterpolateExpr(parts []starlarkExpr) starlarkExpr {
154	result := &interpolateExpr{}
155	needString := true
156	for _, part := range parts {
157		if needString {
158			if strLit, ok := part.(*stringLiteralExpr); ok {
159				result.chunks = append(result.chunks, strLit.literal)
160			} else {
161				result.chunks = append(result.chunks, "")
162			}
163			needString = false
164		} else {
165			if strLit, ok := part.(*stringLiteralExpr); ok {
166				result.chunks[len(result.chunks)-1] += strLit.literal
167			} else {
168				result.args = append(result.args, part)
169				needString = true
170			}
171		}
172	}
173	if len(result.chunks) == len(result.args) {
174		result.chunks = append(result.chunks, "")
175	}
176	if len(result.args) == 0 {
177		return &stringLiteralExpr{literal: strings.Join(result.chunks, "")}
178	}
179	return result
180}
181
182func (xi *interpolateExpr) emit(gctx *generationContext) {
183	if len(xi.chunks) != len(xi.args)+1 {
184		panic(fmt.Errorf("malformed interpolateExpr: #chunks(%d) != #args(%d)+1",
185			len(xi.chunks), len(xi.args)))
186	}
187	// Generate format as join of chunks, but first escape '%' in them
188	format := strings.ReplaceAll(xi.chunks[0], "%", "%%")
189	for _, chunk := range xi.chunks[1:] {
190		format += "%s" + strings.ReplaceAll(chunk, "%", "%%")
191	}
192	gctx.writef("%q %% ", format)
193	emitArg := func(arg starlarkExpr) {
194		if arg.typ() == starlarkTypeList {
195			gctx.write(`" ".join(`)
196			arg.emit(gctx)
197			gctx.write(`)`)
198		} else {
199			arg.emit(gctx)
200		}
201	}
202	if len(xi.args) == 1 {
203		emitArg(xi.args[0])
204	} else {
205		sep := "("
206		for _, arg := range xi.args {
207			gctx.write(sep)
208			emitArg(arg)
209			sep = ", "
210		}
211		gctx.write(")")
212	}
213}
214
215func (_ *interpolateExpr) typ() starlarkType {
216	return starlarkTypeString
217}
218
219func (xi *interpolateExpr) emitListVarCopy(gctx *generationContext) {
220	xi.emit(gctx)
221}
222
223func (xi *interpolateExpr) transform(transformer func(expr starlarkExpr) starlarkExpr) starlarkExpr {
224	for i := range xi.args {
225		xi.args[i] = xi.args[i].transform(transformer)
226	}
227	if replacement := transformer(xi); replacement != nil {
228		return replacement
229	} else {
230		return xi
231	}
232}
233
234type variableRefExpr struct {
235	ref variable
236}
237
238func NewVariableRefExpr(ref variable) starlarkExpr {
239	if predefined, ok := ref.(*predefinedVariable); ok {
240		return predefined.value
241	}
242	return &variableRefExpr{ref}
243}
244
245func (v *variableRefExpr) emit(gctx *generationContext) {
246	v.ref.emitGet(gctx)
247}
248
249func (v *variableRefExpr) typ() starlarkType {
250	return v.ref.valueType()
251}
252
253func (v *variableRefExpr) emitListVarCopy(gctx *generationContext) {
254	v.emit(gctx)
255	if v.typ() == starlarkTypeList {
256		gctx.write("[:]") // this will copy the list
257	}
258}
259
260func (v *variableRefExpr) transform(transformer func(expr starlarkExpr) starlarkExpr) starlarkExpr {
261	if replacement := transformer(v); replacement != nil {
262		return replacement
263	} else {
264		return v
265	}
266}
267
268type toStringExpr struct {
269	expr starlarkExpr
270}
271
272func (s *toStringExpr) emit(ctx *generationContext) {
273	switch s.expr.typ() {
274	case starlarkTypeString, starlarkTypeUnknown:
275		// Assume unknown types are strings already.
276		s.expr.emit(ctx)
277	case starlarkTypeList:
278		ctx.write(`" ".join(`)
279		s.expr.emit(ctx)
280		ctx.write(")")
281	case starlarkTypeInt:
282		ctx.write(`("%d" % (`)
283		s.expr.emit(ctx)
284		ctx.write("))")
285	case starlarkTypeBool:
286		ctx.write(`("true" if (`)
287		s.expr.emit(ctx)
288		ctx.write(`) else "")`)
289	case starlarkTypeVoid:
290		ctx.write(`""`)
291	default:
292		panic("Unknown starlark type!")
293	}
294}
295
296func (s *toStringExpr) typ() starlarkType {
297	return starlarkTypeString
298}
299
300func (s *toStringExpr) emitListVarCopy(gctx *generationContext) {
301	s.emit(gctx)
302}
303
304func (s *toStringExpr) transform(transformer func(expr starlarkExpr) starlarkExpr) starlarkExpr {
305	s.expr = s.expr.transform(transformer)
306	if replacement := transformer(s); replacement != nil {
307		return replacement
308	} else {
309		return s
310	}
311}
312
313type notExpr struct {
314	expr starlarkExpr
315}
316
317func (n *notExpr) emit(ctx *generationContext) {
318	ctx.write("not ")
319	n.expr.emit(ctx)
320}
321
322func (_ *notExpr) typ() starlarkType {
323	return starlarkTypeBool
324}
325
326func (n *notExpr) emitListVarCopy(gctx *generationContext) {
327	n.emit(gctx)
328}
329
330func (n *notExpr) transform(transformer func(expr starlarkExpr) starlarkExpr) starlarkExpr {
331	n.expr = n.expr.transform(transformer)
332	if replacement := transformer(n); replacement != nil {
333		return replacement
334	} else {
335		return n
336	}
337}
338
339type eqExpr struct {
340	left, right starlarkExpr
341	isEq        bool // if false, it's !=
342}
343
344func (eq *eqExpr) emit(gctx *generationContext) {
345	if eq.left.typ() != eq.right.typ() {
346		eq.left = &toStringExpr{expr: eq.left}
347		eq.right = &toStringExpr{expr: eq.right}
348	}
349
350	// General case
351	eq.left.emit(gctx)
352	if eq.isEq {
353		gctx.write(" == ")
354	} else {
355		gctx.write(" != ")
356	}
357	eq.right.emit(gctx)
358}
359
360func (_ *eqExpr) typ() starlarkType {
361	return starlarkTypeBool
362}
363
364func (eq *eqExpr) emitListVarCopy(gctx *generationContext) {
365	eq.emit(gctx)
366}
367
368func (eq *eqExpr) transform(transformer func(expr starlarkExpr) starlarkExpr) starlarkExpr {
369	eq.left = eq.left.transform(transformer)
370	eq.right = eq.right.transform(transformer)
371	if replacement := transformer(eq); replacement != nil {
372		return replacement
373	} else {
374		return eq
375	}
376}
377
378type listExpr struct {
379	items []starlarkExpr
380}
381
382func (l *listExpr) emit(gctx *generationContext) {
383	if !gctx.inAssignment || len(l.items) < 2 {
384		gctx.write("[")
385		sep := ""
386		for _, item := range l.items {
387			gctx.write(sep)
388			item.emit(gctx)
389			sep = ", "
390		}
391		gctx.write("]")
392		return
393	}
394
395	gctx.write("[")
396	gctx.indentLevel += 2
397
398	for _, item := range l.items {
399		gctx.newLine()
400		item.emit(gctx)
401		gctx.write(",")
402	}
403	gctx.indentLevel -= 2
404	gctx.newLine()
405	gctx.write("]")
406}
407
408func (_ *listExpr) typ() starlarkType {
409	return starlarkTypeList
410}
411
412func (l *listExpr) emitListVarCopy(gctx *generationContext) {
413	l.emit(gctx)
414}
415
416func (l *listExpr) transform(transformer func(expr starlarkExpr) starlarkExpr) starlarkExpr {
417	itemsCopy := make([]starlarkExpr, len(l.items))
418	for i, item := range l.items {
419		itemsCopy[i] = item.transform(transformer)
420	}
421	l.items = itemsCopy
422	if replacement := transformer(l); replacement != nil {
423		return replacement
424	} else {
425		return l
426	}
427}
428
429func newStringListExpr(items []string) *listExpr {
430	v := listExpr{}
431	for _, item := range items {
432		v.items = append(v.items, &stringLiteralExpr{item})
433	}
434	return &v
435}
436
437// concatExpr generates expr1 + expr2 + ... + exprN in Starlark.
438type concatExpr struct {
439	items []starlarkExpr
440}
441
442func (c *concatExpr) emit(gctx *generationContext) {
443	if len(c.items) == 1 {
444		c.items[0].emit(gctx)
445		return
446	}
447
448	if !gctx.inAssignment {
449		c.items[0].emit(gctx)
450		for _, item := range c.items[1:] {
451			gctx.write(" + ")
452			item.emit(gctx)
453		}
454		return
455	}
456	gctx.write("(")
457	c.items[0].emit(gctx)
458	gctx.indentLevel += 2
459	for _, item := range c.items[1:] {
460		gctx.write(" +")
461		gctx.newLine()
462		item.emit(gctx)
463	}
464	gctx.write(")")
465	gctx.indentLevel -= 2
466}
467
468func (_ *concatExpr) typ() starlarkType {
469	return starlarkTypeList
470}
471
472func (c *concatExpr) emitListVarCopy(gctx *generationContext) {
473	c.emit(gctx)
474}
475
476func (c *concatExpr) transform(transformer func(expr starlarkExpr) starlarkExpr) starlarkExpr {
477	itemsCopy := make([]starlarkExpr, len(c.items))
478	for i, item := range c.items {
479		itemsCopy[i] = item.transform(transformer)
480	}
481	c.items = itemsCopy
482	if replacement := transformer(c); replacement != nil {
483		return replacement
484	} else {
485		return c
486	}
487}
488
489// inExpr generates <expr> [not] in <list>
490type inExpr struct {
491	expr  starlarkExpr
492	list  starlarkExpr
493	isNot bool
494}
495
496func (i *inExpr) emit(gctx *generationContext) {
497	i.expr.emit(gctx)
498	if i.isNot {
499		gctx.write(" not in ")
500	} else {
501		gctx.write(" in ")
502	}
503	i.list.emit(gctx)
504}
505
506func (_ *inExpr) typ() starlarkType {
507	return starlarkTypeBool
508}
509
510func (i *inExpr) emitListVarCopy(gctx *generationContext) {
511	i.emit(gctx)
512}
513
514func (i *inExpr) transform(transformer func(expr starlarkExpr) starlarkExpr) starlarkExpr {
515	i.expr = i.expr.transform(transformer)
516	i.list = i.list.transform(transformer)
517	if replacement := transformer(i); replacement != nil {
518		return replacement
519	} else {
520		return i
521	}
522}
523
524type indexExpr struct {
525	array starlarkExpr
526	index starlarkExpr
527}
528
529func (ix *indexExpr) emit(gctx *generationContext) {
530	ix.array.emit(gctx)
531	gctx.write("[")
532	ix.index.emit(gctx)
533	gctx.write("]")
534}
535
536func (ix *indexExpr) typ() starlarkType {
537	return starlarkTypeString
538}
539
540func (ix *indexExpr) emitListVarCopy(gctx *generationContext) {
541	ix.emit(gctx)
542}
543
544func (ix *indexExpr) transform(transformer func(expr starlarkExpr) starlarkExpr) starlarkExpr {
545	ix.array = ix.array.transform(transformer)
546	ix.index = ix.index.transform(transformer)
547	if replacement := transformer(ix); replacement != nil {
548		return replacement
549	} else {
550		return ix
551	}
552}
553
554type callExpr struct {
555	object     starlarkExpr // nil if static call
556	name       string
557	args       []starlarkExpr
558	returnType starlarkType
559}
560
561func (cx *callExpr) emit(gctx *generationContext) {
562	if cx.object != nil {
563		gctx.write("(")
564		cx.object.emit(gctx)
565		gctx.write(")")
566		gctx.write(".", cx.name, "(")
567	} else {
568		gctx.write(cx.name, "(")
569	}
570	sep := ""
571	for _, arg := range cx.args {
572		gctx.write(sep)
573		arg.emit(gctx)
574		sep = ", "
575	}
576	gctx.write(")")
577}
578
579func (cx *callExpr) typ() starlarkType {
580	return cx.returnType
581}
582
583func (cx *callExpr) emitListVarCopy(gctx *generationContext) {
584	cx.emit(gctx)
585}
586
587func (cx *callExpr) transform(transformer func(expr starlarkExpr) starlarkExpr) starlarkExpr {
588	if cx.object != nil {
589		cx.object = cx.object.transform(transformer)
590	}
591	for i := range cx.args {
592		cx.args[i] = cx.args[i].transform(transformer)
593	}
594	if replacement := transformer(cx); replacement != nil {
595		return replacement
596	} else {
597		return cx
598	}
599}
600
601type ifExpr struct {
602	condition starlarkExpr
603	ifTrue    starlarkExpr
604	ifFalse   starlarkExpr
605}
606
607func (i *ifExpr) emit(gctx *generationContext) {
608	gctx.write("(")
609	i.ifTrue.emit(gctx)
610	gctx.write(" if ")
611	i.condition.emit(gctx)
612	gctx.write(" else ")
613	i.ifFalse.emit(gctx)
614	gctx.write(")")
615}
616
617func (i *ifExpr) typ() starlarkType {
618	tType := i.ifTrue.typ()
619	fType := i.ifFalse.typ()
620	if tType != fType && tType != starlarkTypeUnknown && fType != starlarkTypeUnknown {
621		panic("Conflicting types in if expression")
622	}
623	if tType != starlarkTypeUnknown {
624		return tType
625	} else {
626		return fType
627	}
628}
629
630func (i *ifExpr) emitListVarCopy(gctx *generationContext) {
631	i.emit(gctx)
632}
633
634func (i *ifExpr) transform(transformer func(expr starlarkExpr) starlarkExpr) starlarkExpr {
635	i.condition = i.condition.transform(transformer)
636	i.ifTrue = i.ifTrue.transform(transformer)
637	i.ifFalse = i.ifFalse.transform(transformer)
638	if replacement := transformer(i); replacement != nil {
639		return replacement
640	} else {
641		return i
642	}
643}
644
645type identifierExpr struct {
646	name string
647}
648
649func (i *identifierExpr) emit(gctx *generationContext) {
650	gctx.write(i.name)
651}
652
653func (i *identifierExpr) typ() starlarkType {
654	return starlarkTypeUnknown
655}
656
657func (i *identifierExpr) emitListVarCopy(gctx *generationContext) {
658	i.emit(gctx)
659}
660
661func (i *identifierExpr) transform(transformer func(expr starlarkExpr) starlarkExpr) starlarkExpr {
662	if replacement := transformer(i); replacement != nil {
663		return replacement
664	} else {
665		return i
666	}
667}
668
669type foreachExpr struct {
670	varName string
671	list    starlarkExpr
672	action  starlarkExpr
673}
674
675func (f *foreachExpr) emit(gctx *generationContext) {
676	gctx.write("[")
677	f.action.emit(gctx)
678	gctx.write(" for " + f.varName + " in ")
679	f.list.emit(gctx)
680	gctx.write("]")
681}
682
683func (f *foreachExpr) typ() starlarkType {
684	return starlarkTypeList
685}
686
687func (f *foreachExpr) emitListVarCopy(gctx *generationContext) {
688	f.emit(gctx)
689}
690
691func (f *foreachExpr) transform(transformer func(expr starlarkExpr) starlarkExpr) starlarkExpr {
692	f.list = f.list.transform(transformer)
693	f.action = f.action.transform(transformer)
694	if replacement := transformer(f); replacement != nil {
695		return replacement
696	} else {
697		return f
698	}
699}
700
701type binaryOpExpr struct {
702	left, right starlarkExpr
703	op          string
704	returnType  starlarkType
705}
706
707func (b *binaryOpExpr) emit(gctx *generationContext) {
708	b.left.emit(gctx)
709	gctx.write(" " + b.op + " ")
710	b.right.emit(gctx)
711}
712
713func (b *binaryOpExpr) typ() starlarkType {
714	return b.returnType
715}
716
717func (b *binaryOpExpr) emitListVarCopy(gctx *generationContext) {
718	b.emit(gctx)
719}
720
721func (b *binaryOpExpr) transform(transformer func(expr starlarkExpr) starlarkExpr) starlarkExpr {
722	b.left = b.left.transform(transformer)
723	b.right = b.right.transform(transformer)
724	if replacement := transformer(b); replacement != nil {
725		return replacement
726	} else {
727		return b
728	}
729}
730
731type badExpr struct {
732	errorLocation ErrorLocation
733	message       string
734}
735
736func (b *badExpr) emit(gctx *generationContext) {
737	gctx.emitConversionError(b.errorLocation, b.message)
738}
739
740func (_ *badExpr) typ() starlarkType {
741	return starlarkTypeUnknown
742}
743
744func (b *badExpr) emitListVarCopy(gctx *generationContext) {
745	b.emit(gctx)
746}
747
748func (b *badExpr) transform(transformer func(expr starlarkExpr) starlarkExpr) starlarkExpr {
749	if replacement := transformer(b); replacement != nil {
750		return replacement
751	} else {
752		return b
753	}
754}
755
756func maybeConvertToStringList(expr starlarkExpr) starlarkExpr {
757	if xString, ok := expr.(*stringLiteralExpr); ok {
758		return newStringListExpr(strings.Fields(xString.literal))
759	}
760	return expr
761}
762
763func isEmptyString(expr starlarkExpr) bool {
764	x, ok := expr.(*stringLiteralExpr)
765	return ok && x.literal == ""
766}
767
768func negateExpr(expr starlarkExpr) starlarkExpr {
769	switch typedExpr := expr.(type) {
770	case *notExpr:
771		return typedExpr.expr
772	case *inExpr:
773		typedExpr.isNot = !typedExpr.isNot
774		return typedExpr
775	case *eqExpr:
776		typedExpr.isEq = !typedExpr.isEq
777		return typedExpr
778	case *binaryOpExpr:
779		switch typedExpr.op {
780		case ">":
781			typedExpr.op = "<="
782			return typedExpr
783		case "<":
784			typedExpr.op = ">="
785			return typedExpr
786		case ">=":
787			typedExpr.op = "<"
788			return typedExpr
789		case "<=":
790			typedExpr.op = ">"
791			return typedExpr
792		default:
793			return &notExpr{expr: expr}
794		}
795	default:
796		return &notExpr{expr: expr}
797	}
798}
799