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 ¬Expr{expr: expr} 794 } 795 default: 796 return ¬Expr{expr: expr} 797 } 798} 799