rule/value.go
func ExprFromValue(val interface{}) bzl.Expr {
	if e, ok := val.(bzl.Expr); ok {
		return e
	}

	rv := reflect.ValueOf(val)
	switch rv.Kind() {
	case reflect.Bool:
		tok := "False"
		if rv.Bool() {
			tok = "True"
		}
		return &bzl.LiteralExpr{Token: tok}

	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
		reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
		return &bzl.LiteralExpr{Token: fmt.Sprintf("%d", val)}

	case reflect.Float32, reflect.Float64:
		return &bzl.LiteralExpr{Token: fmt.Sprintf("%f", val)}

	case reflect.String:
		return &bzl.StringExpr{Value: val.(string)}

	case reflect.Slice, reflect.Array:
		var list []bzl.Expr
		for i := 0; i < rv.Len(); i++ {
			elem := ExprFromValue(rv.Index(i).Interface())
			list = append(list, elem)
		}
		return &bzl.ListExpr{List: list}

	case reflect.Map:
		rkeys := rv.MapKeys()
		sort.Sort(byString(rkeys))
		args := make([]bzl.Expr, len(rkeys))
		for i, rk := range rkeys {
			label := fmt.Sprintf("@io_bazel_rules_go//go/platform:%s", mapKeyString(rk))
			k := &bzl.StringExpr{Value: label}
			v := ExprFromValue(rv.MapIndex(rk).Interface())
			if l, ok := v.(*bzl.ListExpr); ok {
				l.ForceMultiLine = true
			}
			args[i] = &bzl.KeyValueExpr{Key: k, Value: v}
		}
		args = append(args, &bzl.KeyValueExpr{
			Key:   &bzl.StringExpr{Value: "//conditions:default"},
			Value: &bzl.ListExpr{},
		})
		sel := &bzl.CallExpr{
			X:    &bzl.Ident{Name: "select"},
			List: []bzl.Expr{&bzl.DictExpr{List: args, ForceMultiLine: true}},
		}
		return sel

	case reflect.Struct:
		switch val := val.(type) {
		case GlobValue:
			patternsValue := ExprFromValue(val.Patterns)
			globArgs := []bzl.Expr{patternsValue}
			if len(val.Excludes) > 0 {
				excludesValue := ExprFromValue(val.Excludes)
				globArgs = append(globArgs, &bzl.KeyValueExpr{
					Key:   &bzl.StringExpr{Value: "excludes"},
					Value: excludesValue,
				})
			}
			return &bzl.CallExpr{
				X:    &bzl.LiteralExpr{Token: "glob"},
				List: globArgs,
			}

		case PlatformStrings:
			var pieces []bzl.Expr
			if len(val.Generic) > 0 {
				pieces = append(pieces, ExprFromValue(val.Generic))
			}
			if len(val.OS) > 0 {
				pieces = append(pieces, ExprFromValue(val.OS))
			}
			if len(val.Arch) > 0 {
				pieces = append(pieces, ExprFromValue(val.Arch))
			}
			if len(val.Platform) > 0 {
				pieces = append(pieces, ExprFromValue(val.Platform))
			}
			if len(pieces) == 0 {
				return &bzl.ListExpr{}
			} else if len(pieces) == 1 {
				return pieces[0]
			} else {
				e := pieces[0]
				if list, ok := e.(*bzl.ListExpr); ok {
					list.ForceMultiLine = true
				}
				for _, piece := range pieces[1:] {
					e = &bzl.BinaryExpr{X: e, Y: piece, Op: "+"}
				}
				return e
			}
		}
	}

	log.Panicf("type not supported: %T", val)
	return nil
}
func ExprFromValue(val interface{}) bzl.Expr {
	if e, ok := val.(bzl.Expr); ok {
		return e
	}

	rv := reflect.ValueOf(val)
	switch rv.Kind() {
	case reflect.Bool:
		tok := "False"
		if rv.Bool() {
			tok = "True"
		}
		return &bzl.LiteralExpr{Token: tok}

	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
		reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
		return &bzl.LiteralExpr{Token: fmt.Sprintf("%d", val)}

	case reflect.Float32, reflect.Float64:
		return &bzl.LiteralExpr{Token: fmt.Sprintf("%f", val)}

	case reflect.String:
		return &bzl.StringExpr{Value: val.(string)}

	case reflect.Slice, reflect.Array:
		var list []bzl.Expr
		for i := 0; i < rv.Len(); i++ {
			elem := ExprFromValue(rv.Index(i).Interface())
			list = append(list, elem)
		}
		return &bzl.ListExpr{List: list}

	case reflect.Map:
		rkeys := rv.MapKeys()
		sort.Sort(byString(rkeys))
		args := make([]bzl.Expr, len(rkeys))
		for i, rk := range rkeys {
			label := fmt.Sprintf("@io_bazel_rules_go//go/platform:%s", mapKeyString(rk))
			k := &bzl.StringExpr{Value: label}
			v := ExprFromValue(rv.MapIndex(rk).Interface())
			if l, ok := v.(*bzl.ListExpr); ok {
				l.ForceMultiLine = true
			}
			args[i] = &bzl.KeyValueExpr{Key: k, Value: v}
		}
		args = append(args, &bzl.KeyValueExpr{
			Key:   &bzl.StringExpr{Value: "//conditions:default"},
			Value: &bzl.ListExpr{},
		})
		sel := &bzl.CallExpr{
			X:    &bzl.Ident{Name: "select"},
			List: []bzl.Expr{&bzl.DictExpr{List: args, ForceMultiLine: true}},
		}
		return sel

	case reflect.Struct:
		switch val := val.(type) {
		case GlobValue:
			patternsValue := ExprFromValue(val.Patterns)
			globArgs := []bzl.Expr{patternsValue}
			if len(val.Excludes) > 0 {
				excludesValue := ExprFromValue(val.Excludes)
				globArgs = append(globArgs, &bzl.KeyValueExpr{
					Key:   &bzl.StringExpr{Value: "excludes"},
					Value: excludesValue,
				})
			}
			return &bzl.CallExpr{
				X:    &bzl.LiteralExpr{Token: "glob"},
				List: globArgs,
			}

		case PlatformStrings:
			var pieces []bzl.Expr
			if len(val.Generic) > 0 {
				pieces = append(pieces, ExprFromValue(val.Generic))
			}
			if len(val.OS) > 0 {
				pieces = append(pieces, ExprFromValue(val.OS))
			}
			if len(val.Arch) > 0 {
				pieces = append(pieces, ExprFromValue(val.Arch))
			}
			if len(val.Platform) > 0 {
				pieces = append(pieces, ExprFromValue(val.Platform))
			}
			if len(pieces) == 0 {
				return &bzl.ListExpr{}
			} else if len(pieces) == 1 {
				return pieces[0]
			} else {
				e := pieces[0]
				if list, ok := e.(*bzl.ListExpr); ok {
					list.ForceMultiLine = true
				}
				for _, piece := range pieces[1:] {
					e = &bzl.BinaryExpr{X: e, Y: piece, Op: "+"}
				}
				return e
			}
		}
	}

	log.Panicf("type not supported: %T", val)
	return nil
}
// ExprFromValue converts a value into an expression that can be written into
// a Bazel build file. The following types of values can be converted:
//
//   * bools, integers, floats, strings.
//   * slices, arrays (converted to lists).
//   * maps (converted to select expressions; keys must be rules in
//     @io_bazel_rules_go//go/platform).
//   * GlobValue (converted to glob expressions).
//   * PlatformStrings (converted to a concatenation of a list and selects).
//
// Converting unsupported types will cause a panic.