9ebf6e44df
By having knownFunctions contain parsing interfaces instead of just metadata about the function, we can remove some hardcoded special cases. Bug: 201700692 Test: go test Change-Id: I1723c7f50b72d42b03380e999aa9601b455a969e
766 lines
17 KiB
Go
766 lines
17 KiB
Go
// Copyright 2021 Google LLC
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package mk2rbc
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
)
|
|
|
|
// Represents an expression in the Starlark code. An expression has a type.
|
|
type starlarkExpr interface {
|
|
starlarkNode
|
|
typ() starlarkType
|
|
// Emit the code to copy the expression, otherwise we will end up
|
|
// with source and target pointing to the same list.
|
|
emitListVarCopy(gctx *generationContext)
|
|
// Return the expression, calling the transformer func for
|
|
// every expression in the tree. If the transformer func returns non-nil,
|
|
// its result is used in place of the expression it was called with in the
|
|
// resulting expression. The resulting starlarkExpr will contain as many
|
|
// of the same objects from the original expression as possible.
|
|
transform(transformer func(expr starlarkExpr) starlarkExpr) starlarkExpr
|
|
}
|
|
|
|
func maybeString(expr starlarkExpr) (string, bool) {
|
|
if x, ok := expr.(*stringLiteralExpr); ok {
|
|
return x.literal, true
|
|
}
|
|
return "", false
|
|
}
|
|
|
|
type stringLiteralExpr struct {
|
|
literal string
|
|
}
|
|
|
|
func (s *stringLiteralExpr) emit(gctx *generationContext) {
|
|
gctx.writef("%q", s.literal)
|
|
}
|
|
|
|
func (_ *stringLiteralExpr) typ() starlarkType {
|
|
return starlarkTypeString
|
|
}
|
|
|
|
func (s *stringLiteralExpr) emitListVarCopy(gctx *generationContext) {
|
|
s.emit(gctx)
|
|
}
|
|
|
|
func (s *stringLiteralExpr) transform(transformer func(expr starlarkExpr) starlarkExpr) starlarkExpr {
|
|
if replacement := transformer(s); replacement != nil {
|
|
return replacement
|
|
} else {
|
|
return s
|
|
}
|
|
}
|
|
|
|
// Integer literal
|
|
type intLiteralExpr struct {
|
|
literal int
|
|
}
|
|
|
|
func (s *intLiteralExpr) emit(gctx *generationContext) {
|
|
gctx.writef("%d", s.literal)
|
|
}
|
|
|
|
func (_ *intLiteralExpr) typ() starlarkType {
|
|
return starlarkTypeInt
|
|
}
|
|
|
|
func (s *intLiteralExpr) emitListVarCopy(gctx *generationContext) {
|
|
s.emit(gctx)
|
|
}
|
|
|
|
func (s *intLiteralExpr) transform(transformer func(expr starlarkExpr) starlarkExpr) starlarkExpr {
|
|
if replacement := transformer(s); replacement != nil {
|
|
return replacement
|
|
} else {
|
|
return s
|
|
}
|
|
}
|
|
|
|
// Boolean literal
|
|
type boolLiteralExpr struct {
|
|
literal bool
|
|
}
|
|
|
|
func (b *boolLiteralExpr) emit(gctx *generationContext) {
|
|
if b.literal {
|
|
gctx.write("True")
|
|
} else {
|
|
gctx.write("False")
|
|
}
|
|
}
|
|
|
|
func (_ *boolLiteralExpr) typ() starlarkType {
|
|
return starlarkTypeBool
|
|
}
|
|
|
|
func (b *boolLiteralExpr) emitListVarCopy(gctx *generationContext) {
|
|
b.emit(gctx)
|
|
}
|
|
|
|
func (b *boolLiteralExpr) transform(transformer func(expr starlarkExpr) starlarkExpr) starlarkExpr {
|
|
if replacement := transformer(b); replacement != nil {
|
|
return replacement
|
|
} else {
|
|
return b
|
|
}
|
|
}
|
|
|
|
type globalsExpr struct {
|
|
}
|
|
|
|
func (g *globalsExpr) emit(gctx *generationContext) {
|
|
gctx.write("g")
|
|
}
|
|
|
|
func (g *globalsExpr) typ() starlarkType {
|
|
return starlarkTypeUnknown
|
|
}
|
|
|
|
func (g *globalsExpr) emitListVarCopy(gctx *generationContext) {
|
|
g.emit(gctx)
|
|
}
|
|
|
|
func (g *globalsExpr) transform(transformer func(expr starlarkExpr) starlarkExpr) starlarkExpr {
|
|
if replacement := transformer(g); replacement != nil {
|
|
return replacement
|
|
} else {
|
|
return g
|
|
}
|
|
}
|
|
|
|
// interpolateExpr represents Starlark's interpolation operator <string> % list
|
|
// we break <string> into a list of chunks, i.e., "first%second%third" % (X, Y)
|
|
// will have chunks = ["first", "second", "third"] and args = [X, Y]
|
|
type interpolateExpr struct {
|
|
chunks []string // string chunks, separated by '%'
|
|
args []starlarkExpr
|
|
}
|
|
|
|
func NewInterpolateExpr(parts []starlarkExpr) starlarkExpr {
|
|
result := &interpolateExpr{}
|
|
needString := true
|
|
for _, part := range parts {
|
|
if needString {
|
|
if strLit, ok := part.(*stringLiteralExpr); ok {
|
|
result.chunks = append(result.chunks, strLit.literal)
|
|
} else {
|
|
result.chunks = append(result.chunks, "")
|
|
}
|
|
needString = false
|
|
} else {
|
|
if strLit, ok := part.(*stringLiteralExpr); ok {
|
|
result.chunks[len(result.chunks)-1] += strLit.literal
|
|
} else {
|
|
result.args = append(result.args, part)
|
|
needString = true
|
|
}
|
|
}
|
|
}
|
|
if len(result.chunks) == len(result.args) {
|
|
result.chunks = append(result.chunks, "")
|
|
}
|
|
if len(result.args) == 0 {
|
|
return &stringLiteralExpr{literal: strings.Join(result.chunks, "")}
|
|
}
|
|
return result
|
|
}
|
|
|
|
func (xi *interpolateExpr) emit(gctx *generationContext) {
|
|
if len(xi.chunks) != len(xi.args)+1 {
|
|
panic(fmt.Errorf("malformed interpolateExpr: #chunks(%d) != #args(%d)+1",
|
|
len(xi.chunks), len(xi.args)))
|
|
}
|
|
// Generate format as join of chunks, but first escape '%' in them
|
|
format := strings.ReplaceAll(xi.chunks[0], "%", "%%")
|
|
for _, chunk := range xi.chunks[1:] {
|
|
format += "%s" + strings.ReplaceAll(chunk, "%", "%%")
|
|
}
|
|
gctx.writef("%q %% ", format)
|
|
emitArg := func(arg starlarkExpr) {
|
|
if arg.typ() == starlarkTypeList {
|
|
gctx.write(`" ".join(`)
|
|
arg.emit(gctx)
|
|
gctx.write(`)`)
|
|
} else {
|
|
arg.emit(gctx)
|
|
}
|
|
}
|
|
if len(xi.args) == 1 {
|
|
emitArg(xi.args[0])
|
|
} else {
|
|
sep := "("
|
|
for _, arg := range xi.args {
|
|
gctx.write(sep)
|
|
emitArg(arg)
|
|
sep = ", "
|
|
}
|
|
gctx.write(")")
|
|
}
|
|
}
|
|
|
|
func (_ *interpolateExpr) typ() starlarkType {
|
|
return starlarkTypeString
|
|
}
|
|
|
|
func (xi *interpolateExpr) emitListVarCopy(gctx *generationContext) {
|
|
xi.emit(gctx)
|
|
}
|
|
|
|
func (xi *interpolateExpr) transform(transformer func(expr starlarkExpr) starlarkExpr) starlarkExpr {
|
|
argsCopy := make([]starlarkExpr, len(xi.args))
|
|
for i, arg := range xi.args {
|
|
argsCopy[i] = arg.transform(transformer)
|
|
}
|
|
xi.args = argsCopy
|
|
if replacement := transformer(xi); replacement != nil {
|
|
return replacement
|
|
} else {
|
|
return xi
|
|
}
|
|
}
|
|
|
|
type variableRefExpr struct {
|
|
ref variable
|
|
isDefined bool
|
|
}
|
|
|
|
func NewVariableRefExpr(ref variable, isDefined bool) starlarkExpr {
|
|
if predefined, ok := ref.(*predefinedVariable); ok {
|
|
return predefined.value
|
|
}
|
|
return &variableRefExpr{ref, isDefined}
|
|
}
|
|
|
|
func (v *variableRefExpr) emit(gctx *generationContext) {
|
|
v.ref.emitGet(gctx, v.isDefined)
|
|
}
|
|
|
|
func (v *variableRefExpr) typ() starlarkType {
|
|
return v.ref.valueType()
|
|
}
|
|
|
|
func (v *variableRefExpr) emitListVarCopy(gctx *generationContext) {
|
|
v.emit(gctx)
|
|
if v.typ() == starlarkTypeList {
|
|
gctx.write("[:]") // this will copy the list
|
|
}
|
|
}
|
|
|
|
func (v *variableRefExpr) transform(transformer func(expr starlarkExpr) starlarkExpr) starlarkExpr {
|
|
if replacement := transformer(v); replacement != nil {
|
|
return replacement
|
|
} else {
|
|
return v
|
|
}
|
|
}
|
|
|
|
type toStringExpr struct {
|
|
expr starlarkExpr
|
|
}
|
|
|
|
func (s *toStringExpr) emit(ctx *generationContext) {
|
|
switch s.expr.typ() {
|
|
case starlarkTypeString, starlarkTypeUnknown:
|
|
// Assume unknown types are strings already.
|
|
s.expr.emit(ctx)
|
|
case starlarkTypeList:
|
|
ctx.write(`" ".join(`)
|
|
s.expr.emit(ctx)
|
|
ctx.write(")")
|
|
case starlarkTypeInt:
|
|
ctx.write(`("%d" % (`)
|
|
s.expr.emit(ctx)
|
|
ctx.write("))")
|
|
case starlarkTypeBool:
|
|
ctx.write(`("true" if (`)
|
|
s.expr.emit(ctx)
|
|
ctx.write(`) else "")`)
|
|
case starlarkTypeVoid:
|
|
ctx.write(`""`)
|
|
default:
|
|
panic("Unknown starlark type!")
|
|
}
|
|
}
|
|
|
|
func (s *toStringExpr) typ() starlarkType {
|
|
return starlarkTypeString
|
|
}
|
|
|
|
func (s *toStringExpr) emitListVarCopy(gctx *generationContext) {
|
|
s.emit(gctx)
|
|
}
|
|
|
|
func (s *toStringExpr) transform(transformer func(expr starlarkExpr) starlarkExpr) starlarkExpr {
|
|
s.expr = s.expr.transform(transformer)
|
|
if replacement := transformer(s); replacement != nil {
|
|
return replacement
|
|
} else {
|
|
return s
|
|
}
|
|
}
|
|
|
|
type notExpr struct {
|
|
expr starlarkExpr
|
|
}
|
|
|
|
func (n *notExpr) emit(ctx *generationContext) {
|
|
ctx.write("not ")
|
|
n.expr.emit(ctx)
|
|
}
|
|
|
|
func (_ *notExpr) typ() starlarkType {
|
|
return starlarkTypeBool
|
|
}
|
|
|
|
func (n *notExpr) emitListVarCopy(gctx *generationContext) {
|
|
n.emit(gctx)
|
|
}
|
|
|
|
func (n *notExpr) transform(transformer func(expr starlarkExpr) starlarkExpr) starlarkExpr {
|
|
n.expr = n.expr.transform(transformer)
|
|
if replacement := transformer(n); replacement != nil {
|
|
return replacement
|
|
} else {
|
|
return n
|
|
}
|
|
}
|
|
|
|
type eqExpr struct {
|
|
left, right starlarkExpr
|
|
isEq bool // if false, it's !=
|
|
}
|
|
|
|
func (eq *eqExpr) emit(gctx *generationContext) {
|
|
if eq.left.typ() != eq.right.typ() {
|
|
eq.left = &toStringExpr{expr: eq.left}
|
|
eq.right = &toStringExpr{expr: eq.right}
|
|
}
|
|
|
|
// General case
|
|
eq.left.emit(gctx)
|
|
if eq.isEq {
|
|
gctx.write(" == ")
|
|
} else {
|
|
gctx.write(" != ")
|
|
}
|
|
eq.right.emit(gctx)
|
|
}
|
|
|
|
func (_ *eqExpr) typ() starlarkType {
|
|
return starlarkTypeBool
|
|
}
|
|
|
|
func (eq *eqExpr) emitListVarCopy(gctx *generationContext) {
|
|
eq.emit(gctx)
|
|
}
|
|
|
|
func (eq *eqExpr) transform(transformer func(expr starlarkExpr) starlarkExpr) starlarkExpr {
|
|
eq.left = eq.left.transform(transformer)
|
|
eq.right = eq.right.transform(transformer)
|
|
if replacement := transformer(eq); replacement != nil {
|
|
return replacement
|
|
} else {
|
|
return eq
|
|
}
|
|
}
|
|
|
|
// variableDefinedExpr corresponds to Make's ifdef VAR
|
|
type variableDefinedExpr struct {
|
|
v variable
|
|
}
|
|
|
|
func (v *variableDefinedExpr) emit(gctx *generationContext) {
|
|
if v.v != nil {
|
|
v.v.emitDefined(gctx)
|
|
return
|
|
}
|
|
gctx.writef("%s(%q)", cfnWarning, "TODO(VAR)")
|
|
}
|
|
|
|
func (_ *variableDefinedExpr) typ() starlarkType {
|
|
return starlarkTypeBool
|
|
}
|
|
|
|
func (v *variableDefinedExpr) emitListVarCopy(gctx *generationContext) {
|
|
v.emit(gctx)
|
|
}
|
|
|
|
func (v *variableDefinedExpr) transform(transformer func(expr starlarkExpr) starlarkExpr) starlarkExpr {
|
|
// TODO: VariableDefinedExpr isn't really an expression?
|
|
return v
|
|
}
|
|
|
|
type listExpr struct {
|
|
items []starlarkExpr
|
|
}
|
|
|
|
func (l *listExpr) emit(gctx *generationContext) {
|
|
if !gctx.inAssignment || len(l.items) < 2 {
|
|
gctx.write("[")
|
|
sep := ""
|
|
for _, item := range l.items {
|
|
gctx.write(sep)
|
|
item.emit(gctx)
|
|
sep = ", "
|
|
}
|
|
gctx.write("]")
|
|
return
|
|
}
|
|
|
|
gctx.write("[")
|
|
gctx.indentLevel += 2
|
|
|
|
for _, item := range l.items {
|
|
gctx.newLine()
|
|
item.emit(gctx)
|
|
gctx.write(",")
|
|
}
|
|
gctx.indentLevel -= 2
|
|
gctx.newLine()
|
|
gctx.write("]")
|
|
}
|
|
|
|
func (_ *listExpr) typ() starlarkType {
|
|
return starlarkTypeList
|
|
}
|
|
|
|
func (l *listExpr) emitListVarCopy(gctx *generationContext) {
|
|
l.emit(gctx)
|
|
}
|
|
|
|
func (l *listExpr) transform(transformer func(expr starlarkExpr) starlarkExpr) starlarkExpr {
|
|
itemsCopy := make([]starlarkExpr, len(l.items))
|
|
for i, item := range l.items {
|
|
itemsCopy[i] = item.transform(transformer)
|
|
}
|
|
l.items = itemsCopy
|
|
if replacement := transformer(l); replacement != nil {
|
|
return replacement
|
|
} else {
|
|
return l
|
|
}
|
|
}
|
|
|
|
func newStringListExpr(items []string) *listExpr {
|
|
v := listExpr{}
|
|
for _, item := range items {
|
|
v.items = append(v.items, &stringLiteralExpr{item})
|
|
}
|
|
return &v
|
|
}
|
|
|
|
// concatExpr generates expr1 + expr2 + ... + exprN in Starlark.
|
|
type concatExpr struct {
|
|
items []starlarkExpr
|
|
}
|
|
|
|
func (c *concatExpr) emit(gctx *generationContext) {
|
|
if len(c.items) == 1 {
|
|
c.items[0].emit(gctx)
|
|
return
|
|
}
|
|
|
|
if !gctx.inAssignment {
|
|
c.items[0].emit(gctx)
|
|
for _, item := range c.items[1:] {
|
|
gctx.write(" + ")
|
|
item.emit(gctx)
|
|
}
|
|
return
|
|
}
|
|
gctx.write("(")
|
|
c.items[0].emit(gctx)
|
|
gctx.indentLevel += 2
|
|
for _, item := range c.items[1:] {
|
|
gctx.write(" +")
|
|
gctx.newLine()
|
|
item.emit(gctx)
|
|
}
|
|
gctx.write(")")
|
|
gctx.indentLevel -= 2
|
|
}
|
|
|
|
func (_ *concatExpr) typ() starlarkType {
|
|
return starlarkTypeList
|
|
}
|
|
|
|
func (c *concatExpr) emitListVarCopy(gctx *generationContext) {
|
|
c.emit(gctx)
|
|
}
|
|
|
|
func (c *concatExpr) transform(transformer func(expr starlarkExpr) starlarkExpr) starlarkExpr {
|
|
itemsCopy := make([]starlarkExpr, len(c.items))
|
|
for i, item := range c.items {
|
|
itemsCopy[i] = item.transform(transformer)
|
|
}
|
|
c.items = itemsCopy
|
|
if replacement := transformer(c); replacement != nil {
|
|
return replacement
|
|
} else {
|
|
return c
|
|
}
|
|
}
|
|
|
|
// inExpr generates <expr> [not] in <list>
|
|
type inExpr struct {
|
|
expr starlarkExpr
|
|
list starlarkExpr
|
|
isNot bool
|
|
}
|
|
|
|
func (i *inExpr) emit(gctx *generationContext) {
|
|
i.expr.emit(gctx)
|
|
if i.isNot {
|
|
gctx.write(" not in ")
|
|
} else {
|
|
gctx.write(" in ")
|
|
}
|
|
i.list.emit(gctx)
|
|
}
|
|
|
|
func (_ *inExpr) typ() starlarkType {
|
|
return starlarkTypeBool
|
|
}
|
|
|
|
func (i *inExpr) emitListVarCopy(gctx *generationContext) {
|
|
i.emit(gctx)
|
|
}
|
|
|
|
func (i *inExpr) transform(transformer func(expr starlarkExpr) starlarkExpr) starlarkExpr {
|
|
i.expr = i.expr.transform(transformer)
|
|
i.list = i.list.transform(transformer)
|
|
if replacement := transformer(i); replacement != nil {
|
|
return replacement
|
|
} else {
|
|
return i
|
|
}
|
|
}
|
|
|
|
type indexExpr struct {
|
|
array starlarkExpr
|
|
index starlarkExpr
|
|
}
|
|
|
|
func (ix *indexExpr) emit(gctx *generationContext) {
|
|
ix.array.emit(gctx)
|
|
gctx.write("[")
|
|
ix.index.emit(gctx)
|
|
gctx.write("]")
|
|
}
|
|
|
|
func (ix *indexExpr) typ() starlarkType {
|
|
return starlarkTypeString
|
|
}
|
|
|
|
func (ix *indexExpr) emitListVarCopy(gctx *generationContext) {
|
|
ix.emit(gctx)
|
|
}
|
|
|
|
func (ix *indexExpr) transform(transformer func(expr starlarkExpr) starlarkExpr) starlarkExpr {
|
|
ix.array = ix.array.transform(transformer)
|
|
ix.index = ix.index.transform(transformer)
|
|
if replacement := transformer(ix); replacement != nil {
|
|
return replacement
|
|
} else {
|
|
return ix
|
|
}
|
|
}
|
|
|
|
type callExpr struct {
|
|
object starlarkExpr // nil if static call
|
|
name string
|
|
args []starlarkExpr
|
|
returnType starlarkType
|
|
}
|
|
|
|
func (cx *callExpr) emit(gctx *generationContext) {
|
|
if cx.object != nil {
|
|
gctx.write("(")
|
|
cx.object.emit(gctx)
|
|
gctx.write(")")
|
|
gctx.write(".", cx.name, "(")
|
|
} else {
|
|
gctx.write(cx.name, "(")
|
|
}
|
|
sep := ""
|
|
for _, arg := range cx.args {
|
|
gctx.write(sep)
|
|
arg.emit(gctx)
|
|
sep = ", "
|
|
}
|
|
gctx.write(")")
|
|
}
|
|
|
|
func (cx *callExpr) typ() starlarkType {
|
|
return cx.returnType
|
|
}
|
|
|
|
func (cx *callExpr) emitListVarCopy(gctx *generationContext) {
|
|
cx.emit(gctx)
|
|
}
|
|
|
|
func (cx *callExpr) transform(transformer func(expr starlarkExpr) starlarkExpr) starlarkExpr {
|
|
if cx.object != nil {
|
|
cx.object = cx.object.transform(transformer)
|
|
}
|
|
argsCopy := make([]starlarkExpr, len(cx.args))
|
|
for i, arg := range cx.args {
|
|
argsCopy[i] = arg.transform(transformer)
|
|
}
|
|
if replacement := transformer(cx); replacement != nil {
|
|
return replacement
|
|
} else {
|
|
return cx
|
|
}
|
|
}
|
|
|
|
type ifExpr struct {
|
|
condition starlarkExpr
|
|
ifTrue starlarkExpr
|
|
ifFalse starlarkExpr
|
|
}
|
|
|
|
func (i *ifExpr) emit(gctx *generationContext) {
|
|
gctx.write("(")
|
|
i.ifTrue.emit(gctx)
|
|
gctx.write(" if ")
|
|
i.condition.emit(gctx)
|
|
gctx.write(" else ")
|
|
i.ifFalse.emit(gctx)
|
|
gctx.write(")")
|
|
}
|
|
|
|
func (i *ifExpr) typ() starlarkType {
|
|
tType := i.ifTrue.typ()
|
|
fType := i.ifFalse.typ()
|
|
if tType != fType && tType != starlarkTypeUnknown && fType != starlarkTypeUnknown {
|
|
panic("Conflicting types in if expression")
|
|
}
|
|
if tType != starlarkTypeUnknown {
|
|
return tType
|
|
} else {
|
|
return fType
|
|
}
|
|
}
|
|
|
|
func (i *ifExpr) emitListVarCopy(gctx *generationContext) {
|
|
i.emit(gctx)
|
|
}
|
|
|
|
func (i *ifExpr) transform(transformer func(expr starlarkExpr) starlarkExpr) starlarkExpr {
|
|
i.condition = i.condition.transform(transformer)
|
|
i.ifTrue = i.ifTrue.transform(transformer)
|
|
i.ifFalse = i.ifFalse.transform(transformer)
|
|
if replacement := transformer(i); replacement != nil {
|
|
return replacement
|
|
} else {
|
|
return i
|
|
}
|
|
}
|
|
|
|
type identifierExpr struct {
|
|
name string
|
|
}
|
|
|
|
func (i *identifierExpr) emit(gctx *generationContext) {
|
|
gctx.write(i.name)
|
|
}
|
|
|
|
func (i *identifierExpr) typ() starlarkType {
|
|
return starlarkTypeUnknown
|
|
}
|
|
|
|
func (i *identifierExpr) emitListVarCopy(gctx *generationContext) {
|
|
i.emit(gctx)
|
|
}
|
|
|
|
func (i *identifierExpr) transform(transformer func(expr starlarkExpr) starlarkExpr) starlarkExpr {
|
|
if replacement := transformer(i); replacement != nil {
|
|
return replacement
|
|
} else {
|
|
return i
|
|
}
|
|
}
|
|
|
|
type foreachExpr struct {
|
|
varName string
|
|
list starlarkExpr
|
|
action starlarkExpr
|
|
}
|
|
|
|
func (f *foreachExpr) emit(gctx *generationContext) {
|
|
gctx.write("[")
|
|
f.action.emit(gctx)
|
|
gctx.write(" for " + f.varName + " in ")
|
|
f.list.emit(gctx)
|
|
gctx.write("]")
|
|
}
|
|
|
|
func (f *foreachExpr) typ() starlarkType {
|
|
return starlarkTypeList
|
|
}
|
|
|
|
func (f *foreachExpr) emitListVarCopy(gctx *generationContext) {
|
|
f.emit(gctx)
|
|
}
|
|
|
|
func (f *foreachExpr) transform(transformer func(expr starlarkExpr) starlarkExpr) starlarkExpr {
|
|
f.list = f.list.transform(transformer)
|
|
f.action = f.action.transform(transformer)
|
|
if replacement := transformer(f); replacement != nil {
|
|
return replacement
|
|
} else {
|
|
return f
|
|
}
|
|
}
|
|
|
|
type badExpr struct {
|
|
errorLocation ErrorLocation
|
|
message string
|
|
}
|
|
|
|
func (b *badExpr) emit(gctx *generationContext) {
|
|
gctx.emitConversionError(b.errorLocation, b.message)
|
|
}
|
|
|
|
func (_ *badExpr) typ() starlarkType {
|
|
return starlarkTypeUnknown
|
|
}
|
|
|
|
func (_ *badExpr) emitListVarCopy(_ *generationContext) {
|
|
panic("implement me")
|
|
}
|
|
|
|
func (b *badExpr) transform(transformer func(expr starlarkExpr) starlarkExpr) starlarkExpr {
|
|
if replacement := transformer(b); replacement != nil {
|
|
return replacement
|
|
} else {
|
|
return b
|
|
}
|
|
}
|
|
|
|
func maybeConvertToStringList(expr starlarkExpr) starlarkExpr {
|
|
if xString, ok := expr.(*stringLiteralExpr); ok {
|
|
return newStringListExpr(strings.Fields(xString.literal))
|
|
}
|
|
return expr
|
|
}
|
|
|
|
func isEmptyString(expr starlarkExpr) bool {
|
|
x, ok := expr.(*stringLiteralExpr)
|
|
return ok && x.literal == ""
|
|
}
|