Convert firstword/lastword to starlark functions instead of index operators

firstword/lastword need to be able to give an empty string when
the input is empty, which the indexing operator can't do.

Bug: 226974242
Test: go test
Change-Id: I44162a258297b32ec27e7acfb1fa06223391dcc6
diff --git a/mk2rbc/mk2rbc.go b/mk2rbc/mk2rbc.go
index 02b3d08..87d2334 100644
--- a/mk2rbc/mk2rbc.go
+++ b/mk2rbc/mk2rbc.go
@@ -86,7 +86,7 @@
 	"find-copy-subdir-files":               &simpleCallParser{name: baseName + ".find_and_copy", returnType: starlarkTypeList},
 	"filter":                               &simpleCallParser{name: baseName + ".filter", returnType: starlarkTypeList},
 	"filter-out":                           &simpleCallParser{name: baseName + ".filter_out", returnType: starlarkTypeList},
-	"firstword":                            &firstOrLastwordCallParser{isLastWord: false},
+	"firstword":                            &simpleCallParser{name: baseName + ".first_word", returnType: starlarkTypeString},
 	"foreach":                              &foreachCallParser{},
 	"if":                                   &ifCallParser{},
 	"info":                                 &makeControlFuncParser{name: baseName + ".mkinfo"},
@@ -97,7 +97,7 @@
 	"is-product-in-list":                   &isProductInListCallParser{},
 	"is-vendor-board-platform":             &isVendorBoardPlatformCallParser{},
 	"is-vendor-board-qcom":                 &isVendorBoardQcomCallParser{},
-	"lastword":                             &firstOrLastwordCallParser{isLastWord: true},
+	"lastword":                             &simpleCallParser{name: baseName + ".last_word", returnType: starlarkTypeString},
 	"notdir":                               &simpleCallParser{name: baseName + ".notdir", returnType: starlarkTypeString},
 	"math_max":                             &mathMaxOrMinCallParser{function: "max"},
 	"math_min":                             &mathMaxOrMinCallParser{function: "min"},
@@ -459,6 +459,7 @@
 	predefined := []struct{ name, value string }{
 		{"SRC_TARGET_DIR", filepath.Join("build", "make", "target")},
 		{"LOCAL_PATH", filepath.Dir(ss.mkFile)},
+		{"MAKEFILE_LIST", ss.mkFile},
 		{"TOPDIR", ""}, // TOPDIR is just set to an empty string in cleanbuild.mk and core.mk
 		// TODO(asmundak): maybe read it from build/make/core/envsetup.mk?
 		{"TARGET_COPY_OUT_SYSTEM", "system"},
@@ -1662,28 +1663,6 @@
 	return &indexExpr{array, &intLiteralExpr{int(index - 1)}}
 }
 
-type firstOrLastwordCallParser struct {
-	isLastWord bool
-}
-
-func (p *firstOrLastwordCallParser) parse(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString) starlarkExpr {
-	arg := ctx.parseMakeString(node, args)
-	if bad, ok := arg.(*badExpr); ok {
-		return bad
-	}
-	index := &intLiteralExpr{0}
-	if p.isLastWord {
-		if v, ok := arg.(*variableRefExpr); ok && v.ref.name() == "MAKEFILE_LIST" {
-			return &stringLiteralExpr{ctx.script.mkFile}
-		}
-		index.literal = -1
-	}
-	if arg.typ() == starlarkTypeList {
-		return &indexExpr{arg, index}
-	}
-	return &indexExpr{&callExpr{object: arg, name: "split", returnType: starlarkTypeList}, index}
-}
-
 func parseIntegerArguments(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString, expectedArgs int) ([]starlarkExpr, error) {
 	parsedArgs := make([]starlarkExpr, 0)
 	for _, arg := range args.Split(",") {
diff --git a/mk2rbc/mk2rbc_test.go b/mk2rbc/mk2rbc_test.go
index 9485a42..ca7ee44 100644
--- a/mk2rbc/mk2rbc_test.go
+++ b/mk2rbc/mk2rbc_test.go
@@ -833,11 +833,11 @@
   cfg["PRODUCT_NAME"] = ((g.get("TARGET_BOARD_PLATFORM", "")).replace(".", " ")).split()[0]
   rblf.mkinfo("product.mk", rblf.mkpatsubst("%.pub", "%s%%" % cfg["PRODUCT_NAME"], g.get("PRODUCT_ADB_KEYS", "")))
   rblf.mkinfo("product.mk", "$(dir foo/bar): %s" % rblf.dir("foo/bar"))
-  rblf.mkinfo("product.mk", cfg["PRODUCT_COPY_FILES"][0])
-  rblf.mkinfo("product.mk", cfg["PRODUCT_COPY_FILES"][-1])
-  rblf.mkinfo("product.mk", rblf.dir("product.mk"))
-  rblf.mkinfo("product.mk", rblf.dir(cfg["PRODUCT_COPY_FILES"][-1]))
-  rblf.mkinfo("product.mk", rblf.dir((_foobar).split()[-1]))
+  rblf.mkinfo("product.mk", rblf.first_word(cfg["PRODUCT_COPY_FILES"]))
+  rblf.mkinfo("product.mk", rblf.last_word(cfg["PRODUCT_COPY_FILES"]))
+  rblf.mkinfo("product.mk", rblf.dir(rblf.last_word("product.mk")))
+  rblf.mkinfo("product.mk", rblf.dir(rblf.last_word(cfg["PRODUCT_COPY_FILES"])))
+  rblf.mkinfo("product.mk", rblf.dir(rblf.last_word(_foobar)))
   rblf.mkinfo("product.mk", rblf.abspath("foo/bar"))
   rblf.mkinfo("product.mk", rblf.notdir("foo/bar"))
   rblf.soong_config_namespace(g, "snsconfig")