Update androidmk to match blueprint changes

blueprint/parser.Value is now an Expression interface, update androidmk
to match.

Change-Id: I01e0b2a83cf430c1981b450d35c8ab0aab6975f1
diff --git a/androidmk/cmd/androidmk/android.go b/androidmk/cmd/androidmk/android.go
index b95df9a..2d9a5ce 100644
--- a/androidmk/cmd/androidmk/android.go
+++ b/androidmk/cmd/androidmk/android.go
@@ -14,65 +14,65 @@
 
 var standardProperties = map[string]struct {
 	string
-	bpparser.ValueType
+	bpparser.Type
 }{
 	// String properties
-	"LOCAL_MODULE":               {"name", bpparser.String},
-	"LOCAL_MODULE_CLASS":         {"class", bpparser.String},
-	"LOCAL_CXX_STL":              {"stl", bpparser.String},
-	"LOCAL_STRIP_MODULE":         {"strip", bpparser.String},
-	"LOCAL_MULTILIB":             {"compile_multilib", bpparser.String},
-	"LOCAL_ARM_MODE_HACK":        {"instruction_set", bpparser.String},
-	"LOCAL_SDK_VERSION":          {"sdk_version", bpparser.String},
-	"LOCAL_NDK_STL_VARIANT":      {"stl", bpparser.String},
-	"LOCAL_JAR_MANIFEST":         {"manifest", bpparser.String},
-	"LOCAL_JARJAR_RULES":         {"jarjar_rules", bpparser.String},
-	"LOCAL_CERTIFICATE":          {"certificate", bpparser.String},
-	"LOCAL_PACKAGE_NAME":         {"name", bpparser.String},
-	"LOCAL_MODULE_RELATIVE_PATH": {"relative_install_path", bpparser.String},
+	"LOCAL_MODULE":               {"name", bpparser.StringType},
+	"LOCAL_MODULE_CLASS":         {"class", bpparser.StringType},
+	"LOCAL_CXX_STL":              {"stl", bpparser.StringType},
+	"LOCAL_STRIP_MODULE":         {"strip", bpparser.StringType},
+	"LOCAL_MULTILIB":             {"compile_multilib", bpparser.StringType},
+	"LOCAL_ARM_MODE_HACK":        {"instruction_set", bpparser.StringType},
+	"LOCAL_SDK_VERSION":          {"sdk_version", bpparser.StringType},
+	"LOCAL_NDK_STL_VARIANT":      {"stl", bpparser.StringType},
+	"LOCAL_JAR_MANIFEST":         {"manifest", bpparser.StringType},
+	"LOCAL_JARJAR_RULES":         {"jarjar_rules", bpparser.StringType},
+	"LOCAL_CERTIFICATE":          {"certificate", bpparser.StringType},
+	"LOCAL_PACKAGE_NAME":         {"name", bpparser.StringType},
+	"LOCAL_MODULE_RELATIVE_PATH": {"relative_install_path", bpparser.StringType},
 
 	// List properties
-	"LOCAL_SRC_FILES_EXCLUDE":             {"exclude_srcs", bpparser.List},
-	"LOCAL_SHARED_LIBRARIES":              {"shared_libs", bpparser.List},
-	"LOCAL_STATIC_LIBRARIES":              {"static_libs", bpparser.List},
-	"LOCAL_WHOLE_STATIC_LIBRARIES":        {"whole_static_libs", bpparser.List},
-	"LOCAL_SYSTEM_SHARED_LIBRARIES":       {"system_shared_libs", bpparser.List},
-	"LOCAL_ASFLAGS":                       {"asflags", bpparser.List},
-	"LOCAL_CLANG_ASFLAGS":                 {"clang_asflags", bpparser.List},
-	"LOCAL_CFLAGS":                        {"cflags", bpparser.List},
-	"LOCAL_CONLYFLAGS":                    {"conlyflags", bpparser.List},
-	"LOCAL_CPPFLAGS":                      {"cppflags", bpparser.List},
-	"LOCAL_REQUIRED_MODULES":              {"required", bpparser.List},
-	"LOCAL_MODULE_TAGS":                   {"tags", bpparser.List},
-	"LOCAL_LDLIBS":                        {"host_ldlibs", bpparser.List},
-	"LOCAL_CLANG_CFLAGS":                  {"clang_cflags", bpparser.List},
-	"LOCAL_YACCFLAGS":                     {"yaccflags", bpparser.List},
-	"LOCAL_SANITIZE_RECOVER":              {"sanitize.recover", bpparser.List},
-	"LOCAL_LOGTAGS_FILES":                 {"logtags", bpparser.List},
-	"LOCAL_EXPORT_SHARED_LIBRARY_HEADERS": {"export_shared_lib_headers", bpparser.List},
-	"LOCAL_EXPORT_STATIC_LIBRARY_HEADERS": {"export_static_lib_headers", bpparser.List},
+	"LOCAL_SRC_FILES_EXCLUDE":             {"exclude_srcs", bpparser.ListType},
+	"LOCAL_SHARED_LIBRARIES":              {"shared_libs", bpparser.ListType},
+	"LOCAL_STATIC_LIBRARIES":              {"static_libs", bpparser.ListType},
+	"LOCAL_WHOLE_STATIC_LIBRARIES":        {"whole_static_libs", bpparser.ListType},
+	"LOCAL_SYSTEM_SHARED_LIBRARIES":       {"system_shared_libs", bpparser.ListType},
+	"LOCAL_ASFLAGS":                       {"asflags", bpparser.ListType},
+	"LOCAL_CLANG_ASFLAGS":                 {"clang_asflags", bpparser.ListType},
+	"LOCAL_CFLAGS":                        {"cflags", bpparser.ListType},
+	"LOCAL_CONLYFLAGS":                    {"conlyflags", bpparser.ListType},
+	"LOCAL_CPPFLAGS":                      {"cppflags", bpparser.ListType},
+	"LOCAL_REQUIRED_MODULES":              {"required", bpparser.ListType},
+	"LOCAL_MODULE_TAGS":                   {"tags", bpparser.ListType},
+	"LOCAL_LDLIBS":                        {"host_ldlibs", bpparser.ListType},
+	"LOCAL_CLANG_CFLAGS":                  {"clang_cflags", bpparser.ListType},
+	"LOCAL_YACCFLAGS":                     {"yaccflags", bpparser.ListType},
+	"LOCAL_SANITIZE_RECOVER":              {"sanitize.recover", bpparser.ListType},
+	"LOCAL_LOGTAGS_FILES":                 {"logtags", bpparser.ListType},
+	"LOCAL_EXPORT_SHARED_LIBRARY_HEADERS": {"export_shared_lib_headers", bpparser.ListType},
+	"LOCAL_EXPORT_STATIC_LIBRARY_HEADERS": {"export_static_lib_headers", bpparser.ListType},
 
-	"LOCAL_JAVA_RESOURCE_DIRS":    {"java_resource_dirs", bpparser.List},
-	"LOCAL_JAVACFLAGS":            {"javacflags", bpparser.List},
-	"LOCAL_DX_FLAGS":              {"dxflags", bpparser.List},
-	"LOCAL_JAVA_LIBRARIES":        {"java_libs", bpparser.List},
-	"LOCAL_STATIC_JAVA_LIBRARIES": {"java_static_libs", bpparser.List},
-	"LOCAL_AIDL_INCLUDES":         {"aidl_includes", bpparser.List},
-	"LOCAL_AAPT_FLAGS":            {"aaptflags", bpparser.List},
-	"LOCAL_PACKAGE_SPLITS":        {"package_splits", bpparser.List},
+	"LOCAL_JAVA_RESOURCE_DIRS":    {"java_resource_dirs", bpparser.ListType},
+	"LOCAL_JAVACFLAGS":            {"javacflags", bpparser.ListType},
+	"LOCAL_DX_FLAGS":              {"dxflags", bpparser.ListType},
+	"LOCAL_JAVA_LIBRARIES":        {"java_libs", bpparser.ListType},
+	"LOCAL_STATIC_JAVA_LIBRARIES": {"java_static_libs", bpparser.ListType},
+	"LOCAL_AIDL_INCLUDES":         {"aidl_includes", bpparser.ListType},
+	"LOCAL_AAPT_FLAGS":            {"aaptflags", bpparser.ListType},
+	"LOCAL_PACKAGE_SPLITS":        {"package_splits", bpparser.ListType},
 
 	// Bool properties
-	"LOCAL_IS_HOST_MODULE":          {"host", bpparser.Bool},
-	"LOCAL_CLANG":                   {"clang", bpparser.Bool},
-	"LOCAL_FORCE_STATIC_EXECUTABLE": {"static_executable", bpparser.Bool},
-	"LOCAL_NATIVE_COVERAGE":         {"native_coverage", bpparser.Bool},
-	"LOCAL_NO_CRT":                  {"nocrt", bpparser.Bool},
-	"LOCAL_ALLOW_UNDEFINED_SYMBOLS": {"allow_undefined_symbols", bpparser.Bool},
-	"LOCAL_RTTI_FLAG":               {"rtti", bpparser.Bool},
+	"LOCAL_IS_HOST_MODULE":          {"host", bpparser.BoolType},
+	"LOCAL_CLANG":                   {"clang", bpparser.BoolType},
+	"LOCAL_FORCE_STATIC_EXECUTABLE": {"static_executable", bpparser.BoolType},
+	"LOCAL_NATIVE_COVERAGE":         {"native_coverage", bpparser.BoolType},
+	"LOCAL_NO_CRT":                  {"nocrt", bpparser.BoolType},
+	"LOCAL_ALLOW_UNDEFINED_SYMBOLS": {"allow_undefined_symbols", bpparser.BoolType},
+	"LOCAL_RTTI_FLAG":               {"rtti", bpparser.BoolType},
 
-	"LOCAL_NO_STANDARD_LIBRARIES": {"no_standard_libraries", bpparser.Bool},
+	"LOCAL_NO_STANDARD_LIBRARIES": {"no_standard_libraries", bpparser.BoolType},
 
-	"LOCAL_EXPORT_PACKAGE_RESOURCES": {"export_package_resources", bpparser.Bool},
+	"LOCAL_EXPORT_PACKAGE_RESOURCES": {"export_package_resources", bpparser.BoolType},
 }
 
 var rewriteProperties = map[string]struct {
@@ -87,23 +87,26 @@
 	"LOCAL_LDFLAGS":               {ldflags},
 }
 
-type listSplitFunc func(bpparser.Value) (string, *bpparser.Value, error)
+type listSplitFunc func(bpparser.Expression) (string, bpparser.Expression, error)
 
-func emptyList(value *bpparser.Value) bool {
-	return value.Type == bpparser.List && value.Expression == nil && value.Variable == "" &&
-		len(value.ListValue) == 0
+func emptyList(value bpparser.Expression) bool {
+	if list, ok := value.(*bpparser.List); ok {
+		return len(list.Values) == 0
+	}
+	return false
 }
 
-func splitBpList(val *bpparser.Value, keyFunc listSplitFunc) (lists map[string]*bpparser.Value, err error) {
-	lists = make(map[string]*bpparser.Value)
+func splitBpList(val bpparser.Expression, keyFunc listSplitFunc) (lists map[string]bpparser.Expression, err error) {
+	lists = make(map[string]bpparser.Expression)
 
-	if val.Expression != nil {
-		listsA, err := splitBpList(&val.Expression.Args[0], keyFunc)
+	switch val := val.(type) {
+	case *bpparser.Operator:
+		listsA, err := splitBpList(val.Args[0], keyFunc)
 		if err != nil {
 			return nil, err
 		}
 
-		listsB, err := splitBpList(&val.Expression.Args[1], keyFunc)
+		listsB, err := splitBpList(val.Args[1], keyFunc)
 		if err != nil {
 			return nil, err
 		}
@@ -120,94 +123,96 @@
 			}
 
 			if vA, ok := lists[k]; ok {
-				expression := *val.Expression
-				lists[k] = &bpparser.Value{
-					Type:       bpparser.List,
-					Expression: &expression,
-				}
-				lists[k].Expression.Args = [2]bpparser.Value{*vA, *vB}
+				expression := val.Copy().(*bpparser.Operator)
+				expression.Args = [2]bpparser.Expression{vA, vB}
+				lists[k] = expression
 			} else {
 				lists[k] = vB
 			}
 		}
-	} else if val.Variable != "" {
-		key, value, err := keyFunc(*val)
+	case *bpparser.Variable:
+		key, value, err := keyFunc(val)
 		if err != nil {
 			return nil, err
 		}
-		if value.Type == bpparser.List {
+		if value.Type() == bpparser.ListType {
 			lists[key] = value
 		} else {
-			lists[key] = &bpparser.Value{
-				Type:      bpparser.List,
-				ListValue: []bpparser.Value{*value},
+			lists[key] = &bpparser.List{
+				Values: []bpparser.Expression{value},
 			}
 		}
-	} else {
-		for _, v := range val.ListValue {
+	case *bpparser.List:
+		for _, v := range val.Values {
 			key, value, err := keyFunc(v)
 			if err != nil {
 				return nil, err
 			}
-			if _, ok := lists[key]; !ok {
-				lists[key] = &bpparser.Value{
-					Type: bpparser.List,
-				}
+			l := lists[key]
+			if l == nil {
+				l = &bpparser.List{}
 			}
-			lists[key].ListValue = append(lists[key].ListValue, *value)
+			l.(*bpparser.List).Values = append(l.(*bpparser.List).Values, value)
+			lists[key] = l
 		}
+	default:
+		panic(fmt.Errorf("unexpected type %t", val))
 	}
 
 	return lists, nil
 }
 
-func splitLocalGlobalPath(value bpparser.Value) (string, *bpparser.Value, error) {
-	if value.Variable == "LOCAL_PATH" {
-		return "local", &bpparser.Value{
-			Type:        bpparser.String,
-			StringValue: ".",
-		}, nil
-	} else if value.Variable != "" {
-		// TODO: Should we split variables?
-		return "global", &value, nil
-	}
-
-	if value.Type != bpparser.String {
-		return "", nil, fmt.Errorf("splitLocalGlobalPath expected a string, got %s", value.Type)
-	}
-
-	if value.Expression == nil {
-		return "global", &value, nil
-	}
-
-	if value.Expression.Operator != '+' {
-		return "global", &value, nil
-	}
-
-	firstOperand := value.Expression.Args[0]
-	secondOperand := value.Expression.Args[1]
-	if firstOperand.Type != bpparser.String {
-		return "global", &value, nil
-	}
-
-	if firstOperand.Expression != nil {
-		return "global", &value, nil
-	}
-
-	if firstOperand.Variable != "LOCAL_PATH" {
-		return "global", &value, nil
-	}
-
-	if secondOperand.Expression == nil && secondOperand.Variable == "" {
-		if strings.HasPrefix(secondOperand.StringValue, "/") {
-			secondOperand.StringValue = secondOperand.StringValue[1:]
+func splitLocalGlobalPath(value bpparser.Expression) (string, bpparser.Expression, error) {
+	switch v := value.(type) {
+	case *bpparser.Variable:
+		if v.Name == "LOCAL_PATH" {
+			return "local", &bpparser.String{
+				Value: ".",
+			}, nil
+		} else {
+			// TODO: Should we split variables?
+			return "global", value, nil
 		}
+	case *bpparser.Operator:
+		if v.Type() != bpparser.StringType {
+			return "", nil, fmt.Errorf("splitLocalGlobalPath expected a string, got %s", value.Type)
+		}
+
+		if v.Operator != '+' {
+			return "global", value, nil
+		}
+
+		firstOperand := v.Args[0]
+		secondOperand := v.Args[1]
+		if firstOperand.Type() != bpparser.StringType {
+			return "global", value, nil
+		}
+
+		if _, ok := firstOperand.(*bpparser.Operator); ok {
+			return "global", value, nil
+		}
+
+		if variable, ok := firstOperand.(*bpparser.Variable); !ok || variable.Name != "LOCAL_PATH" {
+			return "global", value, nil
+		}
+
+		local := secondOperand
+		if s, ok := secondOperand.(*bpparser.String); ok {
+			if strings.HasPrefix(s.Value, "/") {
+				s.Value = s.Value[1:]
+			}
+		}
+		return "local", local, nil
+	case *bpparser.String:
+		return "global", value, nil
+	default:
+		return "", nil, fmt.Errorf("splitLocalGlobalPath expected a string, got %s", value.Type)
+
 	}
-	return "local", &secondOperand, nil
 }
 
 func localIncludeDirs(file *bpFile, prefix string, value *mkparser.MakeString, appendVariable bool) error {
-	val, err := makeVariableToBlueprint(file, value, bpparser.List)
+	val, err := makeVariableToBlueprint(file, value, bpparser.ListType)
 	if err != nil {
 		return err
 	}
@@ -235,7 +240,7 @@
 }
 
 func exportIncludeDirs(file *bpFile, prefix string, value *mkparser.MakeString, appendVariable bool) error {
-	val, err := makeVariableToBlueprint(file, value, bpparser.List)
+	val, err := makeVariableToBlueprint(file, value, bpparser.ListType)
 	if err != nil {
 		return err
 	}
@@ -266,44 +271,43 @@
 }
 
 func stem(file *bpFile, prefix string, value *mkparser.MakeString, appendVariable bool) error {
-	val, err := makeVariableToBlueprint(file, value, bpparser.String)
+	val, err := makeVariableToBlueprint(file, value, bpparser.StringType)
 	if err != nil {
 		return err
 	}
 	varName := "stem"
 
-	if val.Expression != nil && val.Expression.Operator == '+' &&
-		val.Expression.Args[0].Variable == "LOCAL_MODULE" {
-		varName = "suffix"
-		val = &val.Expression.Args[1]
+	if exp, ok := val.(*bpparser.Operator); ok && exp.Operator == '+' {
+		if variable, ok := exp.Args[0].(*bpparser.Variable); ok && variable.Name == "LOCAL_MODULE" {
+			varName = "suffix"
+			val = exp.Args[1]
+		}
 	}
 
 	return setVariable(file, appendVariable, prefix, varName, val, true)
 }
 
 func hostOs(file *bpFile, prefix string, value *mkparser.MakeString, appendVariable bool) error {
-	val, err := makeVariableToBlueprint(file, value, bpparser.List)
+	val, err := makeVariableToBlueprint(file, value, bpparser.ListType)
 	if err != nil {
 		return err
 	}
 
 	inList := func(s string) bool {
-		for _, v := range val.ListValue {
-			if v.StringValue == s {
+		for _, v := range val.(*bpparser.List).Values {
+			if v.(*bpparser.String).Value == s {
 				return true
 			}
 		}
 		return false
 	}
 
-	falseValue := &bpparser.Value{
-		Type:      bpparser.Bool,
-		BoolValue: false,
+	falseValue := &bpparser.Bool{
+		Value: false,
 	}
 
-	trueValue := &bpparser.Value{
-		Type:      bpparser.Bool,
-		BoolValue: true,
+	trueValue := &bpparser.Bool{
+		Value: true,
 	}
 
 	if inList("windows") {
@@ -321,30 +325,27 @@
 	return err
 }
 
-func splitSrcsLogtags(value bpparser.Value) (string, *bpparser.Value, error) {
-	if value.Variable != "" {
+func splitSrcsLogtags(value bpparser.Expression) (string, bpparser.Expression, error) {
+	switch v := value.(type) {
+	case *bpparser.Variable:
 		// TODO: attempt to split variables?
-		return "srcs", &value, nil
-	}
-
-	if value.Type != bpparser.String {
-		return "", nil, fmt.Errorf("splitSrcsLogtags expected a string, got %s", value.Type)
-	}
-
-	if value.Expression != nil {
+		return "srcs", value, nil
+	case *bpparser.Operator:
 		// TODO: attempt to handle expressions?
-		return "srcs", &value, nil
+		return "srcs", value, nil
+	case *bpparser.String:
+		if strings.HasSuffix(v.Value, ".logtags") {
+			return "logtags", value, nil
+		}
+		return "srcs", value, nil
+	default:
+		return "", nil, fmt.Errorf("splitSrcsLogtags expected a string, got %s", value.Type())
 	}
 
-	if strings.HasSuffix(value.StringValue, ".logtags") {
-		return "logtags", &value, nil
-	}
-
-	return "srcs", &value, nil
 }
 
 func srcFiles(file *bpFile, prefix string, value *mkparser.MakeString, appendVariable bool) error {
-	val, err := makeVariableToBlueprint(file, value, bpparser.List)
+	val, err := makeVariableToBlueprint(file, value, bpparser.ListType)
 	if err != nil {
 		return err
 	}
@@ -369,31 +370,31 @@
 }
 
 func sanitize(file *bpFile, prefix string, mkvalue *mkparser.MakeString, appendVariable bool) error {
-	val, err := makeVariableToBlueprint(file, mkvalue, bpparser.List)
+	val, err := makeVariableToBlueprint(file, mkvalue, bpparser.ListType)
 	if err != nil {
 		return err
 	}
 
-	lists, err := splitBpList(val, func(value bpparser.Value) (string, *bpparser.Value, error) {
-		if value.Variable != "" {
-			return "vars", &value, nil
-		}
-
-		if value.Expression != nil {
+	lists, err := splitBpList(val, func(value bpparser.Expression) (string, bpparser.Expression, error) {
+		switch v := value.(type) {
+		case *bpparser.Variable:
+			return "vars", value, nil
+		case *bpparser.Operator:
 			file.errorf(mkvalue, "unknown sanitize expression")
-			return "unknown", &value, nil
-		}
-
-		switch value.StringValue {
-		case "never", "address", "coverage", "integer", "thread", "undefined":
-			bpTrue := bpparser.Value{
-				Type:      bpparser.Bool,
-				BoolValue: true,
+			return "unknown", value, nil
+		case *bpparser.String:
+			switch v.Value {
+			case "never", "address", "coverage", "integer", "thread", "undefined":
+				bpTrue := &bpparser.Bool{
+					Value: true,
+				}
+				return v.Value, bpTrue, nil
+			default:
+				file.errorf(mkvalue, "unknown sanitize argument: %s", v.Value)
+				return "unknown", value, nil
 			}
-			return value.StringValue, &bpTrue, nil
 		default:
-			file.errorf(mkvalue, "unknown sanitize argument: %s", value.StringValue)
-			return "unknown", &value, nil
+			return "", nil, fmt.Errorf("sanitize expected a string, got %s", value.Type())
 		}
 	})
 	if err != nil {
@@ -407,7 +408,7 @@
 
 		switch k {
 		case "never", "address", "coverage", "integer", "thread", "undefined":
-			err = setVariable(file, false, prefix, "sanitize."+k, &lists[k].ListValue[0], true)
+			err = setVariable(file, false, prefix, "sanitize."+k, lists[k].(*bpparser.List).Values[0], true)
 		case "unknown":
 			// Nothing, we already added the error above
 		case "vars":
@@ -425,30 +426,41 @@
 }
 
 func ldflags(file *bpFile, prefix string, mkvalue *mkparser.MakeString, appendVariable bool) error {
-	val, err := makeVariableToBlueprint(file, mkvalue, bpparser.List)
+	val, err := makeVariableToBlueprint(file, mkvalue, bpparser.ListType)
 	if err != nil {
 		return err
 	}
 
-	lists, err := splitBpList(val, func(value bpparser.Value) (string, *bpparser.Value, error) {
+	lists, err := splitBpList(val, func(value bpparser.Expression) (string, bpparser.Expression, error) {
 		// Anything other than "-Wl,--version_script," + LOCAL_PATH + "<path>" matches ldflags
-		if value.Variable != "" || value.Expression == nil {
-			return "ldflags", &value, nil
+		exp1, ok := value.(*bpparser.Operator)
+		if !ok {
+			return "ldflags", value, nil
 		}
 
-		exp := value.Expression.Args[0]
-		if exp.Variable != "" || exp.Expression == nil || exp.Expression.Args[0].StringValue != "-Wl,--version-script," {
-			return "ldflags", &value, nil
+		exp2, ok := exp1.Args[0].(*bpparser.Operator)
+		if !ok {
+			return "ldflags", value, nil
 		}
 
-		if exp.Expression.Args[1].Variable != "LOCAL_PATH" {
+		if s, ok := exp2.Args[0].(*bpparser.String); !ok || s.Value != "-Wl,--version-script," {
+			return "ldflags", value, nil
+		}
+
+		if v, ok := exp2.Args[1].(*bpparser.Variable); !ok || v.Name != "LOCAL_PATH" {
 			file.errorf(mkvalue, "Unrecognized version-script")
-			return "ldflags", &value, nil
+			return "ldflags", value, nil
 		}
 
-		value.Expression.Args[1].StringValue = strings.TrimPrefix(value.Expression.Args[1].StringValue, "/")
+		s, ok := exp1.Args[1].(*bpparser.String)
+		if !ok {
+			file.errorf(mkvalue, "Unrecognized version-script")
+			return "ldflags", value, nil
+		}
 
-		return "version", &value.Expression.Args[1], nil
+		s.Value = strings.TrimPrefix(s.Value, "/")
+
+		return "version", s, nil
 	})
 	if err != nil {
 		return err
@@ -462,10 +474,10 @@
 	}
 
 	if version_script, ok := lists["version"]; ok && !emptyList(version_script) {
-		if len(version_script.ListValue) > 1 {
+		if len(version_script.(*bpparser.List).Values) > 1 {
 			file.errorf(mkvalue, "multiple version scripts found?")
 		}
-		err = setVariable(file, false, prefix, "version_script", &version_script.ListValue[0], true)
+		err = setVariable(file, false, prefix, "version_script", version_script.(*bpparser.List).Values[0], true)
 		if err != nil {
 			return err
 		}
diff --git a/androidmk/cmd/androidmk/androidmk.go b/androidmk/cmd/androidmk/androidmk.go
index e5407b6..67d1101 100644
--- a/androidmk/cmd/androidmk/androidmk.go
+++ b/androidmk/cmd/androidmk/androidmk.go
@@ -19,7 +19,7 @@
 	comments          []bpparser.Comment
 	defs              []bpparser.Definition
 	localAssignments  map[string]*bpparser.Property
-	globalAssignments map[string]*bpparser.Value
+	globalAssignments map[string]*bpparser.Expression
 	scope             mkparser.Scope
 	module            *bpparser.Module
 
@@ -34,7 +34,7 @@
 	s = fmt.Sprintf(s, args...)
 	c := bpparser.Comment{
 		Comment: []string{fmt.Sprintf("// ANDROIDMK TRANSLATION ERROR: %s", s)},
-		Pos:     f.bpPos,
+		Slash:   f.bpPos,
 	}
 
 	lines := strings.Split(orig, "\n")
@@ -93,7 +93,7 @@
 	file := &bpFile{
 		scope:             androidScope(),
 		localAssignments:  make(map[string]*bpparser.Property),
-		globalAssignments: make(map[string]*bpparser.Value),
+		globalAssignments: make(map[string]*bpparser.Expression),
 	}
 
 	var conds []*conditional
@@ -105,7 +105,7 @@
 		switch x := node.(type) {
 		case *mkparser.Comment:
 			file.comments = append(file.comments, bpparser.Comment{
-				Pos:     file.bpPos,
+				Slash:   file.bpPos,
 				Comment: []string{"//" + x.Comment},
 			})
 		case *mkparser.Assignment:
@@ -231,8 +231,8 @@
 
 	var err error
 	if prop, ok := standardProperties[name]; ok {
-		var val *bpparser.Value
-		val, err = makeVariableToBlueprint(file, assignment.Value, prop.ValueType)
+		var val bpparser.Expression
+		val, err = makeVariableToBlueprint(file, assignment.Value, prop.Type)
 		if err == nil {
 			err = setVariable(file, appendVariable, prefix, prop.string, val, true)
 		}
@@ -256,8 +256,8 @@
 			file.errorf(assignment, "unsupported assignment to %s", name)
 			return
 		default:
-			var val *bpparser.Value
-			val, err = makeVariableToBlueprint(file, assignment.Value, bpparser.List)
+			var val bpparser.Expression
+			val, err = makeVariableToBlueprint(file, assignment.Value, bpparser.ListType)
 			err = setVariable(file, appendVariable, prefix, name, val, false)
 		}
 	}
@@ -279,7 +279,7 @@
 		disabledPrefix := conditionalTranslations[c.cond][!c.eq]
 
 		// Create a fake assignment with enabled = false
-		val, err := makeVariableToBlueprint(file, mkparser.SimpleMakeString("false", mkparser.NoPos), bpparser.Bool)
+		val, err := makeVariableToBlueprint(file, mkparser.SimpleMakeString("false", mkparser.NoPos), bpparser.BoolType)
 		if err == nil {
 			err = setVariable(file, false, disabledPrefix, "enabled", val, true)
 		}
@@ -292,31 +292,31 @@
 func makeModule(file *bpFile, t string) {
 	file.module.Type = bpparser.Ident{
 		Name: t,
-		Pos:  file.module.LbracePos,
+		Pos:  file.module.LBracePos,
 	}
-	file.module.RbracePos = file.bpPos
+	file.module.RBracePos = file.bpPos
 	file.defs = append(file.defs, file.module)
 	file.inModule = false
 }
 
 func resetModule(file *bpFile) {
 	file.module = &bpparser.Module{}
-	file.module.LbracePos = file.bpPos
+	file.module.LBracePos = file.bpPos
 	file.localAssignments = make(map[string]*bpparser.Property)
 	file.inModule = true
 }
 
 func makeVariableToBlueprint(file *bpFile, val *mkparser.MakeString,
-	typ bpparser.ValueType) (*bpparser.Value, error) {
+	typ bpparser.Type) (bpparser.Expression, error) {
 
-	var exp *bpparser.Value
+	var exp bpparser.Expression
 	var err error
 	switch typ {
-	case bpparser.List:
+	case bpparser.ListType:
 		exp, err = makeToListExpression(val, file.scope)
-	case bpparser.String:
+	case bpparser.StringType:
 		exp, err = makeToStringExpression(val, file.scope)
-	case bpparser.Bool:
+	case bpparser.BoolType:
 		exp, err = makeToBoolExpression(val)
 	default:
 		panic("unknown type")
@@ -329,7 +329,7 @@
 	return exp, nil
 }
 
-func setVariable(file *bpFile, plusequals bool, prefix, name string, value *bpparser.Value, local bool) error {
+func setVariable(file *bpFile, plusequals bool, prefix, name string, value bpparser.Expression, local bool) error {
 
 	if prefix != "" {
 		name = prefix + "." + name
@@ -337,7 +337,7 @@
 
 	pos := file.bpPos
 
-	var oldValue *bpparser.Value
+	var oldValue *bpparser.Expression
 	if local {
 		oldProp := file.localAssignments[name]
 		if oldProp != nil {
@@ -349,12 +349,12 @@
 
 	if local {
 		if oldValue != nil && plusequals {
-			val, err := addValues(oldValue, value)
+			val, err := addValues(*oldValue, value)
 			if err != nil {
 				return fmt.Errorf("unsupported addition: %s", err.Error())
 			}
-			val.Expression.Pos = pos
-			*oldValue = *val
+			val.(*bpparser.Operator).OperatorPos = pos
+			*oldValue = val
 		} else {
 			names := strings.Split(name, ".")
 			container := &file.module.Properties
@@ -366,21 +366,20 @@
 					prop = &bpparser.Property{
 						Name: bpparser.Ident{Name: n, Pos: pos},
 						Pos:  pos,
-						Value: bpparser.Value{
-							Type:     bpparser.Map,
-							MapValue: []*bpparser.Property{},
+						Value: &bpparser.Map{
+							Properties: []*bpparser.Property{},
 						},
 					}
 					file.localAssignments[fqn] = prop
 					*container = append(*container, prop)
 				}
-				container = &prop.Value.MapValue
+				container = &prop.Value.(*bpparser.Map).Properties
 			}
 
 			prop := &bpparser.Property{
 				Name:  bpparser.Ident{Name: names[len(names)-1], Pos: pos},
 				Pos:   pos,
-				Value: *value,
+				Value: value,
 			}
 			file.localAssignments[name] = prop
 			*container = append(*container, prop)
@@ -392,8 +391,8 @@
 					Name: name,
 					Pos:  pos,
 				},
-				Value:     *value,
-				OrigValue: *value,
+				Value:     value,
+				OrigValue: value,
 				Pos:       pos,
 				Assigner:  "+=",
 			}
@@ -404,8 +403,8 @@
 					Name: name,
 					Pos:  pos,
 				},
-				Value:     *value,
-				OrigValue: *value,
+				Value:     value,
+				OrigValue: value,
 				Pos:       pos,
 				Assigner:  "=",
 			}
diff --git a/androidmk/cmd/androidmk/androidmk_test.go b/androidmk/cmd/androidmk/androidmk_test.go
index 077c35f..2f4daf7 100644
--- a/androidmk/cmd/androidmk/androidmk_test.go
+++ b/androidmk/cmd/androidmk/androidmk_test.go
@@ -41,9 +41,9 @@
 # Name Comment
 LOCAL_MODULE := test
 # Source comment
-LOCAL_SRC_FILES := a.c
+LOCAL_SRC_FILES_EXCLUDE := a.c
 # Second source comment
-LOCAL_SRC_FILES += b.c
+LOCAL_SRC_FILES_EXCLUDE += b.c
 include $(BUILD_SHARED_LIBRARY)`,
 		expected: `
 //
@@ -55,7 +55,7 @@
     // Name Comment
     name: "test",
     // Source comment
-    srcs: ["a.c"] + ["b.c"], // Second source comment
+    exclude_srcs: ["a.c"] + ["b.c"], // Second source comment
 
 }`,
 	},
@@ -107,8 +107,7 @@
 input = ["testing/include"]
 cc_library_shared {
     // Comment 1
-    include_dirs: ["system/core/include"] + // Comment 2
-    input + ["system/core/include"],
+    include_dirs: ["system/core/include"] + input + ["system/core/include"], // Comment 2
     local_include_dirs: ["."] + ["include"] + ["test/include"],
     // Comment 3
 }`,
@@ -387,9 +386,6 @@
 			continue
 		}
 
-		// TODO(dwillemsen): remove once bpfmt and androidmk agree
-		got = reformatBlueprint(got)
-
 		if got != expected {
 			t.Errorf("failed testcase '%s'\ninput:\n%s\n\nexpected:\n%s\ngot:\n%s\n", test.desc, strings.TrimSpace(test.in), expected, got)
 		}
diff --git a/androidmk/cmd/androidmk/values.go b/androidmk/cmd/androidmk/values.go
index c4fb204..0074bb9 100644
--- a/androidmk/cmd/androidmk/values.go
+++ b/androidmk/cmd/androidmk/values.go
@@ -9,43 +9,38 @@
 	bpparser "github.com/google/blueprint/parser"
 )
 
-func stringToStringValue(s string) *bpparser.Value {
-	return &bpparser.Value{
-		Type:        bpparser.String,
-		StringValue: s,
+func stringToStringValue(s string) bpparser.Expression {
+	return &bpparser.String{
+		Value: s,
 	}
 }
 
-func addValues(val1, val2 *bpparser.Value) (*bpparser.Value, error) {
+func addValues(val1, val2 bpparser.Expression) (bpparser.Expression, error) {
 	if val1 == nil {
 		return val2, nil
 	}
 
-	if val1.Type == bpparser.String && val2.Type == bpparser.List {
-		val1 = &bpparser.Value{
-			Type:      bpparser.List,
-			ListValue: []bpparser.Value{*val1},
+	if val1.Type() == bpparser.StringType && val2.Type() == bpparser.ListType {
+		val1 = &bpparser.List{
+			Values: []bpparser.Expression{val1},
 		}
-	} else if val2.Type == bpparser.String && val1.Type == bpparser.List {
-		val2 = &bpparser.Value{
-			Type:      bpparser.List,
-			ListValue: []bpparser.Value{*val1},
+	} else if val2.Type() == bpparser.StringType && val1.Type() == bpparser.ListType {
+		val2 = &bpparser.List{
+			Values: []bpparser.Expression{val1},
 		}
-	} else if val1.Type != val2.Type {
+	} else if val1.Type() != val2.Type() {
 		return nil, fmt.Errorf("cannot add mismatched types")
 	}
 
-	return &bpparser.Value{
-		Type: val1.Type,
-		Expression: &bpparser.Expression{
-			Operator: '+',
-			Args:     [2]bpparser.Value{*val1, *val2},
-		},
+	return &bpparser.Operator{
+		Operator: '+',
+		Args:     [2]bpparser.Expression{val1, val2},
 	}, nil
 }
 
-func makeToStringExpression(ms *mkparser.MakeString, scope mkparser.Scope) (*bpparser.Value, error) {
-	var val *bpparser.Value
+func makeToStringExpression(ms *mkparser.MakeString, scope mkparser.Scope) (bpparser.Expression, error) {
+
+	var val bpparser.Expression
 	var err error
 
 	if ms.Strings[0] != "" {
@@ -60,12 +55,12 @@
 			if !name.Const() {
 				return nil, fmt.Errorf("Unsupported non-const variable name %s", name.Dump())
 			}
-			tmp := &bpparser.Value{
-				Type:     bpparser.String,
-				Variable: name.Value(nil),
+			tmp := &bpparser.Variable{
+				Name:  name.Value(nil),
+				Value: &bpparser.String{},
 			}
 
-			if tmp.Variable == "TOP" {
+			if tmp.Name == "TOP" {
 				if s[0] == '/' {
 					s = s[1:]
 				} else {
@@ -91,37 +86,33 @@
 	return val, nil
 }
 
-func stringToListValue(s string) *bpparser.Value {
+func stringToListValue(s string) bpparser.Expression {
 	list := strings.Fields(s)
-	valList := make([]bpparser.Value, len(list))
+	valList := make([]bpparser.Expression, len(list))
 	for i, l := range list {
-		valList[i] = bpparser.Value{
-			Type:        bpparser.String,
-			StringValue: l,
+		valList[i] = &bpparser.String{
+			Value: l,
 		}
 	}
-	return &bpparser.Value{
-		Type:      bpparser.List,
-		ListValue: valList,
+	return &bpparser.List{
+		Values: valList,
 	}
 
 }
 
-func makeToListExpression(ms *mkparser.MakeString, scope mkparser.Scope) (*bpparser.Value, error) {
+func makeToListExpression(ms *mkparser.MakeString, scope mkparser.Scope) (bpparser.Expression, error) {
+
 	fields := ms.Split(" \t")
 
-	var listOfListValues []*bpparser.Value
+	var listOfListValues []bpparser.Expression
 
-	listValue := &bpparser.Value{
-		Type: bpparser.List,
-	}
+	listValue := &bpparser.List{}
 
 	for _, f := range fields {
 		if len(f.Variables) == 1 && f.Strings[0] == "" && f.Strings[1] == "" {
 			if ret, ok := f.Variables[0].EvalFunction(scope); ok {
-				listValue.ListValue = append(listValue.ListValue, bpparser.Value{
-					Type:        bpparser.String,
-					StringValue: ret,
+				listValue.Values = append(listValue.Values, &bpparser.String{
+					Value: ret,
 				})
 			} else {
 				// Variable by itself, variable is probably a list
@@ -129,21 +120,18 @@
 					return nil, fmt.Errorf("unsupported non-const variable name")
 				}
 				if f.Variables[0].Name.Value(nil) == "TOP" {
-					listValue.ListValue = append(listValue.ListValue, bpparser.Value{
-						Type:        bpparser.String,
-						StringValue: ".",
+					listValue.Values = append(listValue.Values, &bpparser.String{
+						Value: ".",
 					})
 				} else {
-					if len(listValue.ListValue) > 0 {
+					if len(listValue.Values) > 0 {
 						listOfListValues = append(listOfListValues, listValue)
 					}
-					listOfListValues = append(listOfListValues, &bpparser.Value{
-						Type:     bpparser.List,
-						Variable: f.Variables[0].Name.Value(nil),
+					listOfListValues = append(listOfListValues, &bpparser.Variable{
+						Name:  f.Variables[0].Name.Value(nil),
+						Value: &bpparser.List{},
 					})
-					listValue = &bpparser.Value{
-						Type: bpparser.List,
-					}
+					listValue = &bpparser.List{}
 				}
 			}
 		} else {
@@ -155,11 +143,11 @@
 				continue
 			}
 
-			listValue.ListValue = append(listValue.ListValue, *s)
+			listValue.Values = append(listValue.Values, s)
 		}
 	}
 
-	if len(listValue.ListValue) > 0 {
+	if len(listValue.Values) > 0 {
 		listOfListValues = append(listOfListValues, listValue)
 	}
 
@@ -179,7 +167,7 @@
 	return val, nil
 }
 
-func stringToBoolValue(s string) (*bpparser.Value, error) {
+func stringToBoolValue(s string) (bpparser.Expression, error) {
 	var b bool
 	s = strings.TrimSpace(s)
 	switch s {
@@ -192,22 +180,21 @@
 	default:
 		return nil, fmt.Errorf("unexpected bool value %s", s)
 	}
-	return &bpparser.Value{
-		Type:      bpparser.Bool,
-		BoolValue: b,
+	return &bpparser.Bool{
+		Value: b,
 	}, nil
 }
 
-func makeToBoolExpression(ms *mkparser.MakeString) (*bpparser.Value, error) {
+func makeToBoolExpression(ms *mkparser.MakeString) (bpparser.Expression, error) {
 	if !ms.Const() {
 		if len(ms.Variables) == 1 && ms.Strings[0] == "" && ms.Strings[1] == "" {
 			name := ms.Variables[0].Name
 			if !name.Const() {
 				return nil, fmt.Errorf("unsupported non-const variable name")
 			}
-			return &bpparser.Value{
-				Type:     bpparser.Bool,
-				Variable: name.Value(nil),
+			return &bpparser.Variable{
+				Name:  name.Value(nil),
+				Value: &bpparser.Bool{},
 			}, nil
 		} else {
 			return nil, fmt.Errorf("non-const bool expression %s", ms.Dump())
diff --git a/build.ninja.in b/build.ninja.in
index 7be4b86..3371581 100644
--- a/build.ninja.in
+++ b/build.ninja.in
@@ -158,7 +158,7 @@
 # Variant:
 # Type:    bootstrap_go_package
 # Factory: github.com/google/blueprint/bootstrap.newGoPackageModuleFactory.func1
-# Defined: build/blueprint/Blueprints:80:1
+# Defined: build/blueprint/Blueprints:81:1
 
 build $
         ${g.bootstrap.buildDir}/.bootstrap/blueprint-bootstrap/pkg/github.com/google/blueprint/bootstrap.a $
@@ -186,7 +186,7 @@
 # Variant:
 # Type:    bootstrap_go_package
 # Factory: github.com/google/blueprint/bootstrap.newGoPackageModuleFactory.func1
-# Defined: build/blueprint/Blueprints:99:1
+# Defined: build/blueprint/Blueprints:100:1
 
 build $
         ${g.bootstrap.buildDir}/.bootstrap/blueprint-bootstrap-bpdoc/pkg/github.com/google/blueprint/bootstrap/bpdoc.a $
@@ -207,7 +207,7 @@
 # Variant:
 # Type:    bootstrap_go_package
 # Factory: github.com/google/blueprint/bootstrap.newGoPackageModuleFactory.func1
-# Defined: build/blueprint/Blueprints:46:1
+# Defined: build/blueprint/Blueprints:47:1
 
 build $
         ${g.bootstrap.buildDir}/.bootstrap/blueprint-deptools/pkg/github.com/google/blueprint/deptools.a $
@@ -228,6 +228,7 @@
 build $
         ${g.bootstrap.buildDir}/.bootstrap/blueprint-parser/test/github.com/google/blueprint/parser.a $
         : g.bootstrap.compile $
+        ${g.bootstrap.srcDir}/build/blueprint/parser/ast.go $
         ${g.bootstrap.srcDir}/build/blueprint/parser/modify.go $
         ${g.bootstrap.srcDir}/build/blueprint/parser/parser.go $
         ${g.bootstrap.srcDir}/build/blueprint/parser/printer.go $
@@ -274,6 +275,7 @@
 build $
         ${g.bootstrap.buildDir}/.bootstrap/blueprint-parser/pkg/github.com/google/blueprint/parser.a $
         : g.bootstrap.compile $
+        ${g.bootstrap.srcDir}/build/blueprint/parser/ast.go $
         ${g.bootstrap.srcDir}/build/blueprint/parser/modify.go $
         ${g.bootstrap.srcDir}/build/blueprint/parser/parser.go $
         ${g.bootstrap.srcDir}/build/blueprint/parser/printer.go $
@@ -289,7 +291,7 @@
 # Variant:
 # Type:    bootstrap_go_package
 # Factory: github.com/google/blueprint/bootstrap.newGoPackageModuleFactory.func1
-# Defined: build/blueprint/Blueprints:52:1
+# Defined: build/blueprint/Blueprints:53:1
 
 build $
         ${g.bootstrap.buildDir}/.bootstrap/blueprint-pathtools/test/github.com/google/blueprint/pathtools.a $
@@ -350,7 +352,7 @@
 # Variant:
 # Type:    bootstrap_go_package
 # Factory: github.com/google/blueprint/bootstrap.newGoPackageModuleFactory.func1
-# Defined: build/blueprint/Blueprints:64:1
+# Defined: build/blueprint/Blueprints:65:1
 
 build $
         ${g.bootstrap.buildDir}/.bootstrap/blueprint-proptools/test/github.com/google/blueprint/proptools.a $
@@ -419,7 +421,7 @@
 # Variant:
 # Type:    bootstrap_core_go_binary
 # Factory: github.com/google/blueprint/bootstrap.newGoBinaryModuleFactory.func1
-# Defined: build/blueprint/Blueprints:142:1
+# Defined: build/blueprint/Blueprints:143:1
 
 build ${g.bootstrap.buildDir}/.bootstrap/choosestage/obj/choosestage.a: $
         g.bootstrap.compile $
@@ -443,7 +445,7 @@
 # Variant:
 # Type:    bootstrap_core_go_binary
 # Factory: github.com/google/blueprint/bootstrap.newGoBinaryModuleFactory.func1
-# Defined: build/blueprint/Blueprints:132:1
+# Defined: build/blueprint/Blueprints:133:1
 
 build ${g.bootstrap.buildDir}/.bootstrap/gotestmain/obj/gotestmain.a: $
         g.bootstrap.compile $
@@ -467,7 +469,7 @@
 # Variant:
 # Type:    bootstrap_core_go_binary
 # Factory: github.com/google/blueprint/bootstrap.newGoBinaryModuleFactory.func1
-# Defined: build/blueprint/Blueprints:137:1
+# Defined: build/blueprint/Blueprints:138:1
 
 build ${g.bootstrap.buildDir}/.bootstrap/gotestrunner/obj/gotestrunner.a: $
         g.bootstrap.compile $
@@ -491,7 +493,7 @@
 # Variant:
 # Type:    bootstrap_core_go_binary
 # Factory: github.com/google/blueprint/bootstrap.newGoBinaryModuleFactory.func1
-# Defined: build/blueprint/Blueprints:111:1
+# Defined: build/blueprint/Blueprints:112:1
 
 build ${g.bootstrap.buildDir}/.bootstrap/minibp/obj/minibp.a: $
         g.bootstrap.compile $