Compare commits
4 commits
b7e4d26faf
...
0a002f5985
Author | SHA1 | Date | |
---|---|---|---|
|
0a002f5985 | ||
|
c472e38ec1 | ||
|
738bb54ded | ||
|
1e62c68bfe |
18 changed files with 1697 additions and 1380 deletions
|
@ -133,6 +133,7 @@ bootstrap_go_package {
|
||||||
],
|
],
|
||||||
testSrcs: [
|
testSrcs: [
|
||||||
"proptools/clone_test.go",
|
"proptools/clone_test.go",
|
||||||
|
"proptools/configurable_test.go",
|
||||||
"proptools/escape_test.go",
|
"proptools/escape_test.go",
|
||||||
"proptools/extend_test.go",
|
"proptools/extend_test.go",
|
||||||
"proptools/filter_test.go",
|
"proptools/filter_test.go",
|
||||||
|
|
|
@ -66,7 +66,7 @@ func processReader(filename string, in io.Reader, out io.Writer) error {
|
||||||
|
|
||||||
r := bytes.NewBuffer(src)
|
r := bytes.NewBuffer(src)
|
||||||
|
|
||||||
file, errs := parser.Parse(filename, r, parser.NewScope(nil))
|
file, errs := parser.Parse(filename, r)
|
||||||
if len(errs) > 0 {
|
if len(errs) > 0 {
|
||||||
for _, err := range errs {
|
for _, err := range errs {
|
||||||
fmt.Fprintln(os.Stderr, err)
|
fmt.Fprintln(os.Stderr, err)
|
||||||
|
|
|
@ -85,7 +85,7 @@ func processFile(filename string, in io.Reader, out io.Writer) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
r := bytes.NewBuffer(src)
|
r := bytes.NewBuffer(src)
|
||||||
file, errs := parser.Parse(filename, r, parser.NewScope(nil))
|
file, errs := parser.Parse(filename, r)
|
||||||
if len(errs) > 0 {
|
if len(errs) > 0 {
|
||||||
for _, err := range errs {
|
for _, err := range errs {
|
||||||
fmt.Fprintln(os.Stderr, err)
|
fmt.Fprintln(os.Stderr, err)
|
||||||
|
@ -131,11 +131,13 @@ func findModules(file *parser.File) (modified bool, errs []error) {
|
||||||
for _, def := range file.Defs {
|
for _, def := range file.Defs {
|
||||||
if module, ok := def.(*parser.Module); ok {
|
if module, ok := def.(*parser.Module); ok {
|
||||||
for _, prop := range module.Properties {
|
for _, prop := range module.Properties {
|
||||||
if prop.Name == "name" && prop.Value.Type() == parser.StringType && targetedModule(prop.Value.Eval().(*parser.String).Value) {
|
if prop.Name == "name" {
|
||||||
for _, p := range targetedProperties.properties {
|
if stringValue, ok := prop.Value.(*parser.String); ok && targetedModule(stringValue.Value) {
|
||||||
m, newErrs := processModuleProperty(module, prop.Name, file, p)
|
for _, p := range targetedProperties.properties {
|
||||||
errs = append(errs, newErrs...)
|
m, newErrs := processModuleProperty(module, prop.Name, file, p)
|
||||||
modified = modified || m
|
errs = append(errs, newErrs...)
|
||||||
|
modified = modified || m
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -194,7 +196,7 @@ func getOrCreateRecursiveProperty(module *parser.Module, name string, prefixes [
|
||||||
m := &module.Map
|
m := &module.Map
|
||||||
for i, prefix := range prefixes {
|
for i, prefix := range prefixes {
|
||||||
if prop, found := m.GetProperty(prefix); found {
|
if prop, found := m.GetProperty(prefix); found {
|
||||||
if mm, ok := prop.Value.Eval().(*parser.Map); ok {
|
if mm, ok := prop.Value.(*parser.Map); ok {
|
||||||
m = mm
|
m = mm
|
||||||
} else {
|
} else {
|
||||||
// We've found a property in the AST and such property is not of type
|
// We've found a property in the AST and such property is not of type
|
||||||
|
@ -236,9 +238,9 @@ func processParameter(value parser.Expression, paramName, moduleName string,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (*replaceProperty).size() != 0 {
|
if (*replaceProperty).size() != 0 {
|
||||||
if list, ok := value.Eval().(*parser.List); ok {
|
if list, ok := value.(*parser.List); ok {
|
||||||
return parser.ReplaceStringsInList(list, (*replaceProperty).oldNameToNewName), nil
|
return parser.ReplaceStringsInList(list, (*replaceProperty).oldNameToNewName), nil
|
||||||
} else if str, ok := value.Eval().(*parser.String); ok {
|
} else if str, ok := value.(*parser.String); ok {
|
||||||
oldVal := str.Value
|
oldVal := str.Value
|
||||||
replacementValue := (*replaceProperty).oldNameToNewName[oldVal]
|
replacementValue := (*replaceProperty).oldNameToNewName[oldVal]
|
||||||
if replacementValue != "" {
|
if replacementValue != "" {
|
||||||
|
|
28
context.go
28
context.go
|
@ -1221,9 +1221,9 @@ func (c *Context) parseOne(rootDir, filename string, reader io.Reader,
|
||||||
return nil, nil, []error{err}
|
return nil, nil, []error{err}
|
||||||
}
|
}
|
||||||
|
|
||||||
scope.Remove("subdirs")
|
scope.DontInherit("subdirs")
|
||||||
scope.Remove("optional_subdirs")
|
scope.DontInherit("optional_subdirs")
|
||||||
scope.Remove("build")
|
scope.DontInherit("build")
|
||||||
file, errs = parser.ParseAndEval(filename, reader, scope)
|
file, errs = parser.ParseAndEval(filename, reader, scope)
|
||||||
if len(errs) > 0 {
|
if len(errs) > 0 {
|
||||||
for i, err := range errs {
|
for i, err := range errs {
|
||||||
|
@ -1357,10 +1357,10 @@ func (c *Context) findSubdirBlueprints(dir string, subdirs []string, subdirsPos
|
||||||
}
|
}
|
||||||
|
|
||||||
func getLocalStringListFromScope(scope *parser.Scope, v string) ([]string, scanner.Position, error) {
|
func getLocalStringListFromScope(scope *parser.Scope, v string) ([]string, scanner.Position, error) {
|
||||||
if assignment, local := scope.Get(v); assignment == nil || !local {
|
if assignment := scope.GetLocal(v); assignment == nil {
|
||||||
return nil, scanner.Position{}, nil
|
return nil, scanner.Position{}, nil
|
||||||
} else {
|
} else {
|
||||||
switch value := assignment.Value.Eval().(type) {
|
switch value := assignment.Value.(type) {
|
||||||
case *parser.List:
|
case *parser.List:
|
||||||
ret := make([]string, 0, len(value.Values))
|
ret := make([]string, 0, len(value.Values))
|
||||||
|
|
||||||
|
@ -1386,24 +1386,6 @@ func getLocalStringListFromScope(scope *parser.Scope, v string) ([]string, scann
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func getStringFromScope(scope *parser.Scope, v string) (string, scanner.Position, error) {
|
|
||||||
if assignment, _ := scope.Get(v); assignment == nil {
|
|
||||||
return "", scanner.Position{}, nil
|
|
||||||
} else {
|
|
||||||
switch value := assignment.Value.Eval().(type) {
|
|
||||||
case *parser.String:
|
|
||||||
return value.Value, assignment.EqualsPos, nil
|
|
||||||
case *parser.Bool, *parser.List:
|
|
||||||
return "", scanner.Position{}, &BlueprintError{
|
|
||||||
Err: fmt.Errorf("%q must be a string", v),
|
|
||||||
Pos: assignment.EqualsPos,
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
panic(fmt.Errorf("unknown value type: %d", assignment.Value.Type()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clones a build logic module by calling the factory method for its module type, and then cloning
|
// Clones a build logic module by calling the factory method for its module type, and then cloning
|
||||||
// property values. Any values stored in the module object that are not stored in properties
|
// property values. Any values stored in the module object that are not stored in properties
|
||||||
// structs will be lost.
|
// structs will be lost.
|
||||||
|
|
|
@ -1392,8 +1392,7 @@ func runAndRemoveLoadHooks(ctx *Context, config interface{}, module *moduleInfo,
|
||||||
//
|
//
|
||||||
// The filename is only used for reporting errors.
|
// The filename is only used for reporting errors.
|
||||||
func CheckBlueprintSyntax(moduleFactories map[string]ModuleFactory, filename string, contents string) []error {
|
func CheckBlueprintSyntax(moduleFactories map[string]ModuleFactory, filename string, contents string) []error {
|
||||||
scope := parser.NewScope(nil)
|
file, errs := parser.Parse(filename, strings.NewReader(contents))
|
||||||
file, errs := parser.Parse(filename, strings.NewReader(contents), scope)
|
|
||||||
if len(errs) != 0 {
|
if len(errs) != 0 {
|
||||||
return errs
|
return errs
|
||||||
}
|
}
|
||||||
|
|
416
parser/ast.go
416
parser/ast.go
|
@ -16,6 +16,7 @@ package parser
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"text/scanner"
|
"text/scanner"
|
||||||
)
|
)
|
||||||
|
@ -40,14 +41,13 @@ type Assignment struct {
|
||||||
Name string
|
Name string
|
||||||
NamePos scanner.Position
|
NamePos scanner.Position
|
||||||
Value Expression
|
Value Expression
|
||||||
OrigValue Expression
|
|
||||||
EqualsPos scanner.Position
|
EqualsPos scanner.Position
|
||||||
Assigner string
|
Assigner string
|
||||||
Referenced bool
|
Referenced bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Assignment) String() string {
|
func (a *Assignment) String() string {
|
||||||
return fmt.Sprintf("%s@%s %s %s (%s) %t", a.Name, a.EqualsPos, a.Assigner, a.Value, a.OrigValue, a.Referenced)
|
return fmt.Sprintf("%s@%s %s %s %t", a.Name, a.EqualsPos, a.Assigner, a.Value, a.Referenced)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Assignment) Pos() scanner.Position { return a.NamePos }
|
func (a *Assignment) Pos() scanner.Position { return a.NamePos }
|
||||||
|
@ -131,6 +131,10 @@ func (p *Property) String() string {
|
||||||
func (p *Property) Pos() scanner.Position { return p.NamePos }
|
func (p *Property) Pos() scanner.Position { return p.NamePos }
|
||||||
func (p *Property) End() scanner.Position { return p.Value.End() }
|
func (p *Property) End() scanner.Position { return p.Value.End() }
|
||||||
|
|
||||||
|
func (p *Property) MarkReferencedVariables(scope *Scope) {
|
||||||
|
p.Value.MarkReferencedVariables(scope)
|
||||||
|
}
|
||||||
|
|
||||||
// An Expression is a Value in a Property or Assignment. It can be a literal (String or Bool), a
|
// An Expression is a Value in a Property or Assignment. It can be a literal (String or Bool), a
|
||||||
// Map, a List, an Operator that combines two expressions of the same type, or a Variable that
|
// Map, a List, an Operator that combines two expressions of the same type, or a Variable that
|
||||||
// references and Assignment.
|
// references and Assignment.
|
||||||
|
@ -139,11 +143,24 @@ type Expression interface {
|
||||||
// Copy returns a copy of the Expression that will not affect the original if mutated
|
// Copy returns a copy of the Expression that will not affect the original if mutated
|
||||||
Copy() Expression
|
Copy() Expression
|
||||||
String() string
|
String() string
|
||||||
// Type returns the underlying Type enum of the Expression if it were to be evaluated
|
// Type returns the underlying Type enum of the Expression if it were to be evaluated, if it's known.
|
||||||
|
// It's possible that the type isn't known, such as when a select statement with a late-bound variable
|
||||||
|
// is used. For that reason, Type() is mostly for use in error messages, not to make logic decisions
|
||||||
|
// off of.
|
||||||
Type() Type
|
Type() Type
|
||||||
// Eval returns an expression that is fully evaluated to a simple type (List, Map, String, or
|
// Eval returns an expression that is fully evaluated to a simple type (List, Map, String,
|
||||||
// Bool). It will return the same object for every call to Eval().
|
// Bool, or Select). It will return the origional expression if possible, or allocate a
|
||||||
Eval() Expression
|
// new one if modifications were necessary.
|
||||||
|
Eval(scope *Scope) (Expression, error)
|
||||||
|
// PrintfInto will substitute any %s's in string literals in the AST with the provided
|
||||||
|
// value. It will modify the AST in-place. This is used to implement soong config value
|
||||||
|
// variables, but should be removed when those have switched to selects.
|
||||||
|
PrintfInto(value string) error
|
||||||
|
// MarkReferencedVariables marks the variables in the given scope referenced if there
|
||||||
|
// is a matching variable reference in this expression. This happens naturally during
|
||||||
|
// Eval as well, but for selects, we need to mark variables as referenced without
|
||||||
|
// actually evaluating the expression yet.
|
||||||
|
MarkReferencedVariables(scope *Scope)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExpressionsAreSame tells whether the two values are the same Expression.
|
// ExpressionsAreSame tells whether the two values are the same Expression.
|
||||||
|
@ -157,9 +174,6 @@ func ExpressionsAreSame(a Expression, b Expression) (equal bool, err error) {
|
||||||
// TODO(jeffrygaston) once positions are removed from Expression structs,
|
// TODO(jeffrygaston) once positions are removed from Expression structs,
|
||||||
// remove this function and have callers use reflect.DeepEqual(a, b)
|
// remove this function and have callers use reflect.DeepEqual(a, b)
|
||||||
func hackyExpressionsAreSame(a Expression, b Expression) (equal bool, err error) {
|
func hackyExpressionsAreSame(a Expression, b Expression) (equal bool, err error) {
|
||||||
if a.Type() != b.Type() {
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
left, err := hackyFingerprint(a)
|
left, err := hackyFingerprint(a)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, nil
|
return false, nil
|
||||||
|
@ -173,7 +187,7 @@ func hackyExpressionsAreSame(a Expression, b Expression) (equal bool, err error)
|
||||||
}
|
}
|
||||||
|
|
||||||
func hackyFingerprint(expression Expression) (fingerprint []byte, err error) {
|
func hackyFingerprint(expression Expression) (fingerprint []byte, err error) {
|
||||||
assignment := &Assignment{"a", noPos, expression, expression, noPos, "=", false}
|
assignment := &Assignment{"a", noPos, expression, noPos, "=", false}
|
||||||
module := &File{}
|
module := &File{}
|
||||||
module.Defs = append(module.Defs, assignment)
|
module.Defs = append(module.Defs, assignment)
|
||||||
p := newPrinter(module)
|
p := newPrinter(module)
|
||||||
|
@ -183,17 +197,19 @@ func hackyFingerprint(expression Expression) (fingerprint []byte, err error) {
|
||||||
type Type int
|
type Type int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
BoolType Type = iota + 1
|
UnknownType Type = iota
|
||||||
|
BoolType
|
||||||
StringType
|
StringType
|
||||||
Int64Type
|
Int64Type
|
||||||
ListType
|
ListType
|
||||||
MapType
|
MapType
|
||||||
NotEvaluatedType
|
|
||||||
UnsetType
|
UnsetType
|
||||||
)
|
)
|
||||||
|
|
||||||
func (t Type) String() string {
|
func (t Type) String() string {
|
||||||
switch t {
|
switch t {
|
||||||
|
case UnknownType:
|
||||||
|
return "unknown"
|
||||||
case BoolType:
|
case BoolType:
|
||||||
return "bool"
|
return "bool"
|
||||||
case StringType:
|
case StringType:
|
||||||
|
@ -204,12 +220,10 @@ func (t Type) String() string {
|
||||||
return "list"
|
return "list"
|
||||||
case MapType:
|
case MapType:
|
||||||
return "map"
|
return "map"
|
||||||
case NotEvaluatedType:
|
|
||||||
return "notevaluated"
|
|
||||||
case UnsetType:
|
case UnsetType:
|
||||||
return "unset"
|
return "unset"
|
||||||
default:
|
default:
|
||||||
panic(fmt.Errorf("Unknown type %d", t))
|
panic(fmt.Sprintf("Unknown type %d", t))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -217,7 +231,6 @@ type Operator struct {
|
||||||
Args [2]Expression
|
Args [2]Expression
|
||||||
Operator rune
|
Operator rune
|
||||||
OperatorPos scanner.Position
|
OperatorPos scanner.Position
|
||||||
Value Expression
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *Operator) Copy() Expression {
|
func (x *Operator) Copy() Expression {
|
||||||
|
@ -227,26 +240,142 @@ func (x *Operator) Copy() Expression {
|
||||||
return &ret
|
return &ret
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *Operator) Eval() Expression {
|
func (x *Operator) Type() Type {
|
||||||
return x.Value.Eval()
|
t1 := x.Args[0].Type()
|
||||||
|
t2 := x.Args[1].Type()
|
||||||
|
if t1 == UnknownType {
|
||||||
|
return t2
|
||||||
|
}
|
||||||
|
if t2 == UnknownType {
|
||||||
|
return t1
|
||||||
|
}
|
||||||
|
if t1 != t2 {
|
||||||
|
return UnknownType
|
||||||
|
}
|
||||||
|
return t1
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *Operator) Type() Type {
|
func (x *Operator) Eval(scope *Scope) (Expression, error) {
|
||||||
return x.Args[0].Type()
|
return evaluateOperator(scope, x.Operator, x.Args[0], x.Args[1])
|
||||||
|
}
|
||||||
|
|
||||||
|
func evaluateOperator(scope *Scope, operator rune, left, right Expression) (Expression, error) {
|
||||||
|
if operator != '+' {
|
||||||
|
return nil, fmt.Errorf("unknown operator %c", operator)
|
||||||
|
}
|
||||||
|
l, err := left.Eval(scope)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
r, err := right.Eval(scope)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := l.(*Select); !ok {
|
||||||
|
if _, ok := r.(*Select); ok {
|
||||||
|
// Promote l to a select so we can add r to it
|
||||||
|
l = &Select{
|
||||||
|
Cases: []*SelectCase{{
|
||||||
|
Value: l,
|
||||||
|
}},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
l = l.Copy()
|
||||||
|
|
||||||
|
switch v := l.(type) {
|
||||||
|
case *String:
|
||||||
|
if _, ok := r.(*String); !ok {
|
||||||
|
fmt.Fprintf(os.Stderr, "not ok")
|
||||||
|
}
|
||||||
|
v.Value += r.(*String).Value
|
||||||
|
case *Int64:
|
||||||
|
v.Value += r.(*Int64).Value
|
||||||
|
v.Token = ""
|
||||||
|
case *List:
|
||||||
|
v.Values = append(v.Values, r.(*List).Values...)
|
||||||
|
case *Map:
|
||||||
|
var err error
|
||||||
|
v.Properties, err = addMaps(scope, v.Properties, r.(*Map).Properties)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
case *Select:
|
||||||
|
v.Append = r
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("operator %c not supported on %v", operator, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
return l, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func addMaps(scope *Scope, map1, map2 []*Property) ([]*Property, error) {
|
||||||
|
ret := make([]*Property, 0, len(map1))
|
||||||
|
|
||||||
|
inMap1 := make(map[string]*Property)
|
||||||
|
inMap2 := make(map[string]*Property)
|
||||||
|
inBoth := make(map[string]*Property)
|
||||||
|
|
||||||
|
for _, prop1 := range map1 {
|
||||||
|
inMap1[prop1.Name] = prop1
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, prop2 := range map2 {
|
||||||
|
inMap2[prop2.Name] = prop2
|
||||||
|
if _, ok := inMap1[prop2.Name]; ok {
|
||||||
|
inBoth[prop2.Name] = prop2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, prop1 := range map1 {
|
||||||
|
if prop2, ok := inBoth[prop1.Name]; ok {
|
||||||
|
var err error
|
||||||
|
newProp := *prop1
|
||||||
|
newProp.Value, err = evaluateOperator(scope, '+', prop1.Value, prop2.Value)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ret = append(ret, &newProp)
|
||||||
|
} else {
|
||||||
|
ret = append(ret, prop1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, prop2 := range map2 {
|
||||||
|
if _, ok := inBoth[prop2.Name]; !ok {
|
||||||
|
ret = append(ret, prop2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Operator) PrintfInto(value string) error {
|
||||||
|
if err := x.Args[0].PrintfInto(value); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return x.Args[1].PrintfInto(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Operator) MarkReferencedVariables(scope *Scope) {
|
||||||
|
x.Args[0].MarkReferencedVariables(scope)
|
||||||
|
x.Args[1].MarkReferencedVariables(scope)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *Operator) Pos() scanner.Position { return x.Args[0].Pos() }
|
func (x *Operator) Pos() scanner.Position { return x.Args[0].Pos() }
|
||||||
func (x *Operator) End() scanner.Position { return x.Args[1].End() }
|
func (x *Operator) End() scanner.Position { return x.Args[1].End() }
|
||||||
|
|
||||||
func (x *Operator) String() string {
|
func (x *Operator) String() string {
|
||||||
return fmt.Sprintf("(%s %c %s = %s)@%s", x.Args[0].String(), x.Operator, x.Args[1].String(),
|
return fmt.Sprintf("(%s %c %s)@%s", x.Args[0].String(), x.Operator, x.Args[1].String(),
|
||||||
x.Value, x.OperatorPos)
|
x.OperatorPos)
|
||||||
}
|
}
|
||||||
|
|
||||||
type Variable struct {
|
type Variable struct {
|
||||||
Name string
|
Name string
|
||||||
NamePos scanner.Position
|
NamePos scanner.Position
|
||||||
Value Expression
|
Type_ Type
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *Variable) Pos() scanner.Position { return x.NamePos }
|
func (x *Variable) Pos() scanner.Position { return x.NamePos }
|
||||||
|
@ -257,15 +386,33 @@ func (x *Variable) Copy() Expression {
|
||||||
return &ret
|
return &ret
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *Variable) Eval() Expression {
|
func (x *Variable) Eval(scope *Scope) (Expression, error) {
|
||||||
return x.Value.Eval()
|
if assignment := scope.Get(x.Name); assignment != nil {
|
||||||
|
assignment.Referenced = true
|
||||||
|
return assignment.Value, nil
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("undefined variable %s", x.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Variable) PrintfInto(value string) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Variable) MarkReferencedVariables(scope *Scope) {
|
||||||
|
if assignment := scope.Get(x.Name); assignment != nil {
|
||||||
|
assignment.Referenced = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *Variable) String() string {
|
func (x *Variable) String() string {
|
||||||
return x.Name + " = " + x.Value.String()
|
return x.Name
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *Variable) Type() Type { return x.Value.Type() }
|
func (x *Variable) Type() Type {
|
||||||
|
// Variables do not normally have a type associated with them, this is only
|
||||||
|
// filled out in the androidmk tool
|
||||||
|
return x.Type_
|
||||||
|
}
|
||||||
|
|
||||||
type Map struct {
|
type Map struct {
|
||||||
LBracePos scanner.Position
|
LBracePos scanner.Position
|
||||||
|
@ -285,8 +432,36 @@ func (x *Map) Copy() Expression {
|
||||||
return &ret
|
return &ret
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *Map) Eval() Expression {
|
func (x *Map) Eval(scope *Scope) (Expression, error) {
|
||||||
return x
|
newProps := make([]*Property, len(x.Properties))
|
||||||
|
for i, prop := range x.Properties {
|
||||||
|
newVal, err := prop.Value.Eval(scope)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
newProps[i] = &Property{
|
||||||
|
Name: prop.Name,
|
||||||
|
NamePos: prop.NamePos,
|
||||||
|
ColonPos: prop.ColonPos,
|
||||||
|
Value: newVal,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &Map{
|
||||||
|
LBracePos: x.LBracePos,
|
||||||
|
RBracePos: x.RBracePos,
|
||||||
|
Properties: newProps,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Map) PrintfInto(value string) error {
|
||||||
|
// We should never reach this because selects cannot hold maps
|
||||||
|
panic("printfinto() is unsupported on maps")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Map) MarkReferencedVariables(scope *Scope) {
|
||||||
|
for _, prop := range x.Properties {
|
||||||
|
prop.MarkReferencedVariables(scope)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *Map) String() string {
|
func (x *Map) String() string {
|
||||||
|
@ -379,8 +554,35 @@ func (x *List) Copy() Expression {
|
||||||
return &ret
|
return &ret
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *List) Eval() Expression {
|
func (x *List) Eval(scope *Scope) (Expression, error) {
|
||||||
return x
|
newValues := make([]Expression, len(x.Values))
|
||||||
|
for i, val := range x.Values {
|
||||||
|
newVal, err := val.Eval(scope)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
newValues[i] = newVal
|
||||||
|
}
|
||||||
|
return &List{
|
||||||
|
LBracePos: x.LBracePos,
|
||||||
|
RBracePos: x.RBracePos,
|
||||||
|
Values: newValues,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *List) PrintfInto(value string) error {
|
||||||
|
for _, val := range x.Values {
|
||||||
|
if err := val.PrintfInto(value); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *List) MarkReferencedVariables(scope *Scope) {
|
||||||
|
for _, val := range x.Values {
|
||||||
|
val.MarkReferencedVariables(scope)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *List) String() string {
|
func (x *List) String() string {
|
||||||
|
@ -407,8 +609,29 @@ func (x *String) Copy() Expression {
|
||||||
return &ret
|
return &ret
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *String) Eval() Expression {
|
func (x *String) Eval(scope *Scope) (Expression, error) {
|
||||||
return x
|
return x, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *String) PrintfInto(value string) error {
|
||||||
|
count := strings.Count(x.Value, "%")
|
||||||
|
if count == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if count > 1 {
|
||||||
|
return fmt.Errorf("list/value variable properties only support a single '%%'")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !strings.Contains(x.Value, "%s") {
|
||||||
|
return fmt.Errorf("unsupported %% in value variable property")
|
||||||
|
}
|
||||||
|
|
||||||
|
x.Value = fmt.Sprintf(x.Value, value)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *String) MarkReferencedVariables(scope *Scope) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *String) String() string {
|
func (x *String) String() string {
|
||||||
|
@ -433,8 +656,15 @@ func (x *Int64) Copy() Expression {
|
||||||
return &ret
|
return &ret
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *Int64) Eval() Expression {
|
func (x *Int64) Eval(scope *Scope) (Expression, error) {
|
||||||
return x
|
return x, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Int64) PrintfInto(value string) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Int64) MarkReferencedVariables(scope *Scope) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *Int64) String() string {
|
func (x *Int64) String() string {
|
||||||
|
@ -459,8 +689,15 @@ func (x *Bool) Copy() Expression {
|
||||||
return &ret
|
return &ret
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *Bool) Eval() Expression {
|
func (x *Bool) Eval(scope *Scope) (Expression, error) {
|
||||||
return x
|
return x, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Bool) PrintfInto(value string) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Bool) MarkReferencedVariables(scope *Scope) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *Bool) String() string {
|
func (x *Bool) String() string {
|
||||||
|
@ -542,29 +779,6 @@ func (c Comment) Text() string {
|
||||||
return string(buf)
|
return string(buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
type NotEvaluated struct {
|
|
||||||
Position scanner.Position
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n NotEvaluated) Copy() Expression {
|
|
||||||
return NotEvaluated{Position: n.Position}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n NotEvaluated) String() string {
|
|
||||||
return "Not Evaluated"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n NotEvaluated) Type() Type {
|
|
||||||
return NotEvaluatedType
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n NotEvaluated) Eval() Expression {
|
|
||||||
return NotEvaluated{Position: n.Position}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n NotEvaluated) Pos() scanner.Position { return n.Position }
|
|
||||||
func (n NotEvaluated) End() scanner.Position { return n.Position }
|
|
||||||
|
|
||||||
func endPos(pos scanner.Position, n int) scanner.Position {
|
func endPos(pos scanner.Position, n int) scanner.Position {
|
||||||
pos.Offset += n
|
pos.Offset += n
|
||||||
pos.Column += n
|
pos.Column += n
|
||||||
|
@ -609,13 +823,13 @@ func (c *ConfigurableCondition) String() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
type Select struct {
|
type Select struct {
|
||||||
KeywordPos scanner.Position // the keyword "select"
|
Scope *Scope // scope used to evaluate the body of the select later on
|
||||||
Conditions []ConfigurableCondition
|
KeywordPos scanner.Position // the keyword "select"
|
||||||
LBracePos scanner.Position
|
Conditions []ConfigurableCondition
|
||||||
RBracePos scanner.Position
|
LBracePos scanner.Position
|
||||||
Cases []*SelectCase // the case statements
|
RBracePos scanner.Position
|
||||||
Append Expression
|
Cases []*SelectCase // the case statements
|
||||||
ExpressionType Type
|
Append Expression
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Select) Pos() scanner.Position { return s.KeywordPos }
|
func (s *Select) Pos() scanner.Position { return s.KeywordPos }
|
||||||
|
@ -633,8 +847,24 @@ func (s *Select) Copy() Expression {
|
||||||
return &ret
|
return &ret
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Select) Eval() Expression {
|
func (s *Select) Eval(scope *Scope) (Expression, error) {
|
||||||
return s
|
s.Scope = scope
|
||||||
|
s.MarkReferencedVariables(scope)
|
||||||
|
return s, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Select) PrintfInto(value string) error {
|
||||||
|
// PrintfInto will be handled at the Configurable object level
|
||||||
|
panic("Cannot call PrintfInto on a select expression")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Select) MarkReferencedVariables(scope *Scope) {
|
||||||
|
for _, c := range x.Cases {
|
||||||
|
c.MarkReferencedVariables(scope)
|
||||||
|
}
|
||||||
|
if x.Append != nil {
|
||||||
|
x.Append.MarkReferencedVariables(scope)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Select) String() string {
|
func (s *Select) String() string {
|
||||||
|
@ -642,18 +872,35 @@ func (s *Select) String() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Select) Type() Type {
|
func (s *Select) Type() Type {
|
||||||
if s.ExpressionType == UnsetType && s.Append != nil {
|
if len(s.Cases) == 0 {
|
||||||
return s.Append.Type()
|
return UnsetType
|
||||||
}
|
}
|
||||||
return s.ExpressionType
|
return UnknownType
|
||||||
|
}
|
||||||
|
|
||||||
|
type SelectPattern struct {
|
||||||
|
Value Expression
|
||||||
|
Binding Variable
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *SelectPattern) Pos() scanner.Position { return c.Value.Pos() }
|
||||||
|
func (c *SelectPattern) End() scanner.Position {
|
||||||
|
if c.Binding.NamePos.IsValid() {
|
||||||
|
return c.Binding.End()
|
||||||
|
}
|
||||||
|
return c.Value.End()
|
||||||
}
|
}
|
||||||
|
|
||||||
type SelectCase struct {
|
type SelectCase struct {
|
||||||
Patterns []Expression
|
Patterns []SelectPattern
|
||||||
ColonPos scanner.Position
|
ColonPos scanner.Position
|
||||||
Value Expression
|
Value Expression
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *SelectCase) MarkReferencedVariables(scope *Scope) {
|
||||||
|
x.Value.MarkReferencedVariables(scope)
|
||||||
|
}
|
||||||
|
|
||||||
func (c *SelectCase) Copy() *SelectCase {
|
func (c *SelectCase) Copy() *SelectCase {
|
||||||
ret := *c
|
ret := *c
|
||||||
ret.Value = c.Value.Copy()
|
ret.Value = c.Value.Copy()
|
||||||
|
@ -681,21 +928,28 @@ type UnsetProperty struct {
|
||||||
Position scanner.Position
|
Position scanner.Position
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n UnsetProperty) Copy() Expression {
|
func (n *UnsetProperty) Copy() Expression {
|
||||||
return UnsetProperty{Position: n.Position}
|
return &UnsetProperty{Position: n.Position}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n UnsetProperty) String() string {
|
func (n *UnsetProperty) String() string {
|
||||||
return "unset"
|
return "unset"
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n UnsetProperty) Type() Type {
|
func (n *UnsetProperty) Type() Type {
|
||||||
return UnsetType
|
return UnsetType
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n UnsetProperty) Eval() Expression {
|
func (n *UnsetProperty) Eval(scope *Scope) (Expression, error) {
|
||||||
return UnsetProperty{Position: n.Position}
|
return n, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n UnsetProperty) Pos() scanner.Position { return n.Position }
|
func (x *UnsetProperty) PrintfInto(value string) error {
|
||||||
func (n UnsetProperty) End() scanner.Position { return n.Position }
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *UnsetProperty) MarkReferencedVariables(scope *Scope) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *UnsetProperty) Pos() scanner.Position { return n.Position }
|
||||||
|
func (n *UnsetProperty) End() scanner.Position { return n.Position }
|
||||||
|
|
|
@ -23,13 +23,11 @@ import (
|
||||||
|
|
||||||
func AddStringToList(list *List, s string) (modified bool) {
|
func AddStringToList(list *List, s string) (modified bool) {
|
||||||
for _, v := range list.Values {
|
for _, v := range list.Values {
|
||||||
if v.Type() != StringType {
|
|
||||||
panic(fmt.Errorf("expected string in list, got %s", v.Type()))
|
|
||||||
}
|
|
||||||
|
|
||||||
if sv, ok := v.(*String); ok && sv.Value == s {
|
if sv, ok := v.(*String); ok && sv.Value == s {
|
||||||
// string already exists
|
// string already exists
|
||||||
return false
|
return false
|
||||||
|
} else if !ok {
|
||||||
|
panic(fmt.Errorf("expected string in list, got %s", v.Type()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,13 +41,11 @@ func AddStringToList(list *List, s string) (modified bool) {
|
||||||
|
|
||||||
func RemoveStringFromList(list *List, s string) (modified bool) {
|
func RemoveStringFromList(list *List, s string) (modified bool) {
|
||||||
for i, v := range list.Values {
|
for i, v := range list.Values {
|
||||||
if v.Type() != StringType {
|
|
||||||
panic(fmt.Errorf("expected string in list, got %s", v.Type()))
|
|
||||||
}
|
|
||||||
|
|
||||||
if sv, ok := v.(*String); ok && sv.Value == s {
|
if sv, ok := v.(*String); ok && sv.Value == s {
|
||||||
list.Values = append(list.Values[:i], list.Values[i+1:]...)
|
list.Values = append(list.Values[:i], list.Values[i+1:]...)
|
||||||
return true
|
return true
|
||||||
|
} else if !ok {
|
||||||
|
panic(fmt.Errorf("expected string in list, got %s", v.Type()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,9 +55,6 @@ func RemoveStringFromList(list *List, s string) (modified bool) {
|
||||||
func ReplaceStringsInList(list *List, replacements map[string]string) (replaced bool) {
|
func ReplaceStringsInList(list *List, replacements map[string]string) (replaced bool) {
|
||||||
modified := false
|
modified := false
|
||||||
for i, v := range list.Values {
|
for i, v := range list.Values {
|
||||||
if v.Type() != StringType {
|
|
||||||
panic(fmt.Errorf("expected string in list, got %s", v.Type()))
|
|
||||||
}
|
|
||||||
if sv, ok := v.(*String); ok && replacements[sv.Value] != "" {
|
if sv, ok := v.(*String); ok && replacements[sv.Value] != "" {
|
||||||
pos := list.Values[i].Pos()
|
pos := list.Values[i].Pos()
|
||||||
list.Values[i] = &String{
|
list.Values[i] = &String{
|
||||||
|
@ -69,6 +62,8 @@ func ReplaceStringsInList(list *List, replacements map[string]string) (replaced
|
||||||
Value: replacements[sv.Value],
|
Value: replacements[sv.Value],
|
||||||
}
|
}
|
||||||
modified = true
|
modified = true
|
||||||
|
} else if !ok {
|
||||||
|
panic(fmt.Errorf("expected string in list, got %s", v.Type()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return modified
|
return modified
|
||||||
|
|
465
parser/parser.go
465
parser/parser.go
|
@ -29,6 +29,7 @@ var errTooManyErrors = errors.New("too many errors")
|
||||||
const maxErrors = 1
|
const maxErrors = 1
|
||||||
|
|
||||||
const default_select_branch_name = "__soong_conditions_default__"
|
const default_select_branch_name = "__soong_conditions_default__"
|
||||||
|
const any_select_branch_name = "__soong_conditions_any__"
|
||||||
|
|
||||||
type ParseError struct {
|
type ParseError struct {
|
||||||
Err error
|
Err error
|
||||||
|
@ -45,22 +46,6 @@ type File struct {
|
||||||
Comments []*CommentGroup
|
Comments []*CommentGroup
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *File) Pos() scanner.Position {
|
|
||||||
return scanner.Position{
|
|
||||||
Filename: f.Name,
|
|
||||||
Line: 1,
|
|
||||||
Column: 1,
|
|
||||||
Offset: 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *File) End() scanner.Position {
|
|
||||||
if len(f.Defs) > 0 {
|
|
||||||
return f.Defs[len(f.Defs)-1].End()
|
|
||||||
}
|
|
||||||
return noPos
|
|
||||||
}
|
|
||||||
|
|
||||||
func parse(p *parser) (file *File, errs []error) {
|
func parse(p *parser) (file *File, errs []error) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if r := recover(); r != nil {
|
if r := recover(); r != nil {
|
||||||
|
@ -87,22 +72,54 @@ func parse(p *parser) (file *File, errs []error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func ParseAndEval(filename string, r io.Reader, scope *Scope) (file *File, errs []error) {
|
func ParseAndEval(filename string, r io.Reader, scope *Scope) (file *File, errs []error) {
|
||||||
p := newParser(r, scope)
|
file, errs = Parse(filename, r)
|
||||||
p.eval = true
|
if len(errs) > 0 {
|
||||||
p.scanner.Filename = filename
|
return nil, errs
|
||||||
|
}
|
||||||
|
|
||||||
return parse(p)
|
// evaluate all module properties
|
||||||
|
var newDefs []Definition
|
||||||
|
for _, def := range file.Defs {
|
||||||
|
switch d := def.(type) {
|
||||||
|
case *Module:
|
||||||
|
for _, prop := range d.Map.Properties {
|
||||||
|
newval, err := prop.Value.Eval(scope)
|
||||||
|
if err != nil {
|
||||||
|
return nil, []error{err}
|
||||||
|
}
|
||||||
|
switch newval.(type) {
|
||||||
|
case *String, *Bool, *Int64, *Select, *Map, *List:
|
||||||
|
// ok
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("Evaled but got %#v\n", newval))
|
||||||
|
}
|
||||||
|
prop.Value = newval
|
||||||
|
}
|
||||||
|
newDefs = append(newDefs, d)
|
||||||
|
case *Assignment:
|
||||||
|
if err := scope.HandleAssignment(d); err != nil {
|
||||||
|
return nil, []error{err}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is not strictly necessary, but removing the assignments from
|
||||||
|
// the result makes it clearer that this is an evaluated file.
|
||||||
|
// We could also consider adding a "EvaluatedFile" type to return.
|
||||||
|
file.Defs = newDefs
|
||||||
|
|
||||||
|
return file, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func Parse(filename string, r io.Reader, scope *Scope) (file *File, errs []error) {
|
func Parse(filename string, r io.Reader) (file *File, errs []error) {
|
||||||
p := newParser(r, scope)
|
p := newParser(r)
|
||||||
p.scanner.Filename = filename
|
p.scanner.Filename = filename
|
||||||
|
|
||||||
return parse(p)
|
return parse(p)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ParseExpression(r io.Reader) (value Expression, errs []error) {
|
func ParseExpression(r io.Reader) (value Expression, errs []error) {
|
||||||
p := newParser(r, NewScope(nil))
|
p := newParser(r)
|
||||||
p.next()
|
p.next()
|
||||||
value = p.parseExpression()
|
value = p.parseExpression()
|
||||||
p.accept(scanner.EOF)
|
p.accept(scanner.EOF)
|
||||||
|
@ -114,14 +131,11 @@ type parser struct {
|
||||||
scanner scanner.Scanner
|
scanner scanner.Scanner
|
||||||
tok rune
|
tok rune
|
||||||
errors []error
|
errors []error
|
||||||
scope *Scope
|
|
||||||
comments []*CommentGroup
|
comments []*CommentGroup
|
||||||
eval bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func newParser(r io.Reader, scope *Scope) *parser {
|
func newParser(r io.Reader) *parser {
|
||||||
p := &parser{}
|
p := &parser{}
|
||||||
p.scope = scope
|
|
||||||
p.scanner.Init(r)
|
p.scanner.Init(r)
|
||||||
p.scanner.Error = func(sc *scanner.Scanner, msg string) {
|
p.scanner.Error = func(sc *scanner.Scanner, msg string) {
|
||||||
p.errorf(msg)
|
p.errorf(msg)
|
||||||
|
@ -234,34 +248,9 @@ func (p *parser) parseAssignment(name string, namePos scanner.Position,
|
||||||
assignment.Name = name
|
assignment.Name = name
|
||||||
assignment.NamePos = namePos
|
assignment.NamePos = namePos
|
||||||
assignment.Value = value
|
assignment.Value = value
|
||||||
assignment.OrigValue = value
|
|
||||||
assignment.EqualsPos = pos
|
assignment.EqualsPos = pos
|
||||||
assignment.Assigner = assigner
|
assignment.Assigner = assigner
|
||||||
|
|
||||||
if p.scope != nil {
|
|
||||||
if assigner == "+=" {
|
|
||||||
if old, local := p.scope.Get(assignment.Name); old == nil {
|
|
||||||
p.errorf("modified non-existent variable %q with +=", assignment.Name)
|
|
||||||
} else if !local {
|
|
||||||
p.errorf("modified non-local variable %q with +=", assignment.Name)
|
|
||||||
} else if old.Referenced {
|
|
||||||
p.errorf("modified variable %q with += after referencing", assignment.Name)
|
|
||||||
} else {
|
|
||||||
val, err := p.evaluateOperator(old.Value, assignment.Value, '+', assignment.EqualsPos)
|
|
||||||
if err != nil {
|
|
||||||
p.error(err)
|
|
||||||
} else {
|
|
||||||
old.Value = val
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
err := p.scope.Add(assignment)
|
|
||||||
if err != nil {
|
|
||||||
p.error(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -297,13 +286,7 @@ func (p *parser) parseModule(typ string, typPos scanner.Position) *Module {
|
||||||
|
|
||||||
func (p *parser) parsePropertyList(isModule, compat bool) (properties []*Property) {
|
func (p *parser) parsePropertyList(isModule, compat bool) (properties []*Property) {
|
||||||
for p.tok == scanner.Ident {
|
for p.tok == scanner.Ident {
|
||||||
property := p.parseProperty(isModule, compat)
|
properties = append(properties, p.parseProperty(isModule, compat))
|
||||||
|
|
||||||
// If a property is set to an empty select or a select where all branches are "unset",
|
|
||||||
// skip emitting the property entirely.
|
|
||||||
if property.Value.Type() != UnsetType {
|
|
||||||
properties = append(properties, property)
|
|
||||||
}
|
|
||||||
|
|
||||||
if p.tok != ',' {
|
if p.tok != ',' {
|
||||||
// There was no comma, so the list is done.
|
// There was no comma, so the list is done.
|
||||||
|
@ -363,115 +346,6 @@ func (p *parser) parseExpression() (value Expression) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *parser) evaluateOperator(value1, value2 Expression, operator rune,
|
|
||||||
pos scanner.Position) (Expression, error) {
|
|
||||||
|
|
||||||
if value1.Type() == UnsetType {
|
|
||||||
return value2, nil
|
|
||||||
}
|
|
||||||
if value2.Type() == UnsetType {
|
|
||||||
return value1, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
value := value1
|
|
||||||
|
|
||||||
if p.eval {
|
|
||||||
e1 := value1.Eval()
|
|
||||||
e2 := value2.Eval()
|
|
||||||
if e1.Type() != e2.Type() {
|
|
||||||
return nil, fmt.Errorf("mismatched type in operator %c: %s != %s", operator,
|
|
||||||
e1.Type(), e2.Type())
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, ok := e1.(*Select); !ok {
|
|
||||||
if _, ok := e2.(*Select); ok {
|
|
||||||
// Promote e1 to a select so we can add e2 to it
|
|
||||||
e1 = &Select{
|
|
||||||
Cases: []*SelectCase{{
|
|
||||||
Value: e1,
|
|
||||||
}},
|
|
||||||
ExpressionType: e1.Type(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
value = e1.Copy()
|
|
||||||
|
|
||||||
switch operator {
|
|
||||||
case '+':
|
|
||||||
switch v := value.(type) {
|
|
||||||
case *String:
|
|
||||||
v.Value += e2.(*String).Value
|
|
||||||
case *Int64:
|
|
||||||
v.Value += e2.(*Int64).Value
|
|
||||||
v.Token = ""
|
|
||||||
case *List:
|
|
||||||
v.Values = append(v.Values, e2.(*List).Values...)
|
|
||||||
case *Map:
|
|
||||||
var err error
|
|
||||||
v.Properties, err = p.addMaps(v.Properties, e2.(*Map).Properties, pos)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
case *Select:
|
|
||||||
v.Append = e2
|
|
||||||
default:
|
|
||||||
return nil, fmt.Errorf("operator %c not supported on type %s", operator, v.Type())
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
panic("unknown operator " + string(operator))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return &Operator{
|
|
||||||
Args: [2]Expression{value1, value2},
|
|
||||||
Operator: operator,
|
|
||||||
OperatorPos: pos,
|
|
||||||
Value: value,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *parser) addMaps(map1, map2 []*Property, pos scanner.Position) ([]*Property, error) {
|
|
||||||
ret := make([]*Property, 0, len(map1))
|
|
||||||
|
|
||||||
inMap1 := make(map[string]*Property)
|
|
||||||
inMap2 := make(map[string]*Property)
|
|
||||||
inBoth := make(map[string]*Property)
|
|
||||||
|
|
||||||
for _, prop1 := range map1 {
|
|
||||||
inMap1[prop1.Name] = prop1
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, prop2 := range map2 {
|
|
||||||
inMap2[prop2.Name] = prop2
|
|
||||||
if _, ok := inMap1[prop2.Name]; ok {
|
|
||||||
inBoth[prop2.Name] = prop2
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, prop1 := range map1 {
|
|
||||||
if prop2, ok := inBoth[prop1.Name]; ok {
|
|
||||||
var err error
|
|
||||||
newProp := *prop1
|
|
||||||
newProp.Value, err = p.evaluateOperator(prop1.Value, prop2.Value, '+', pos)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
ret = append(ret, &newProp)
|
|
||||||
} else {
|
|
||||||
ret = append(ret, prop1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, prop2 := range map2 {
|
|
||||||
if _, ok := inBoth[prop2.Name]; !ok {
|
|
||||||
ret = append(ret, prop2)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *parser) parseOperator(value1 Expression) Expression {
|
func (p *parser) parseOperator(value1 Expression) Expression {
|
||||||
operator := p.tok
|
operator := p.tok
|
||||||
pos := p.scanner.Position
|
pos := p.scanner.Position
|
||||||
|
@ -479,14 +353,11 @@ func (p *parser) parseOperator(value1 Expression) Expression {
|
||||||
|
|
||||||
value2 := p.parseExpression()
|
value2 := p.parseExpression()
|
||||||
|
|
||||||
value, err := p.evaluateOperator(value1, value2, operator, pos)
|
return &Operator{
|
||||||
if err != nil {
|
Args: [2]Expression{value1, value2},
|
||||||
p.error(err)
|
Operator: operator,
|
||||||
return nil
|
OperatorPos: pos,
|
||||||
}
|
}
|
||||||
|
|
||||||
return value
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *parser) parseValue() (value Expression) {
|
func (p *parser) parseValue() (value Expression) {
|
||||||
|
@ -535,22 +406,9 @@ func (p *parser) parseVariable() Expression {
|
||||||
var value Expression
|
var value Expression
|
||||||
|
|
||||||
text := p.scanner.TokenText()
|
text := p.scanner.TokenText()
|
||||||
if p.eval {
|
|
||||||
if assignment, local := p.scope.Get(text); assignment == nil {
|
|
||||||
p.errorf("variable %q is not set", text)
|
|
||||||
} else {
|
|
||||||
if local {
|
|
||||||
assignment.Referenced = true
|
|
||||||
}
|
|
||||||
value = assignment.Value
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
value = &NotEvaluated{}
|
|
||||||
}
|
|
||||||
value = &Variable{
|
value = &Variable{
|
||||||
Name: text,
|
Name: text,
|
||||||
NamePos: p.scanner.Position,
|
NamePos: p.scanner.Position,
|
||||||
Value: value,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
p.accept(scanner.Ident)
|
p.accept(scanner.Ident)
|
||||||
|
@ -645,44 +503,72 @@ func (p *parser) parseSelect() Expression {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
parseOnePattern := func() Expression {
|
maybeParseBinding := func() (Variable, bool) {
|
||||||
|
if p.scanner.TokenText() != "@" {
|
||||||
|
return Variable{}, false
|
||||||
|
}
|
||||||
|
p.next()
|
||||||
|
value := Variable{
|
||||||
|
Name: p.scanner.TokenText(),
|
||||||
|
NamePos: p.scanner.Position,
|
||||||
|
}
|
||||||
|
p.accept(scanner.Ident)
|
||||||
|
return value, true
|
||||||
|
}
|
||||||
|
|
||||||
|
parseOnePattern := func() SelectPattern {
|
||||||
|
var result SelectPattern
|
||||||
switch p.tok {
|
switch p.tok {
|
||||||
case scanner.Ident:
|
case scanner.Ident:
|
||||||
switch p.scanner.TokenText() {
|
switch p.scanner.TokenText() {
|
||||||
case "default":
|
case "any":
|
||||||
|
result.Value = &String{
|
||||||
|
LiteralPos: p.scanner.Position,
|
||||||
|
Value: any_select_branch_name,
|
||||||
|
}
|
||||||
p.next()
|
p.next()
|
||||||
return &String{
|
if binding, exists := maybeParseBinding(); exists {
|
||||||
|
result.Binding = binding
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
case "default":
|
||||||
|
result.Value = &String{
|
||||||
LiteralPos: p.scanner.Position,
|
LiteralPos: p.scanner.Position,
|
||||||
Value: default_select_branch_name,
|
Value: default_select_branch_name,
|
||||||
}
|
}
|
||||||
case "true":
|
|
||||||
p.next()
|
p.next()
|
||||||
return &Bool{
|
return result
|
||||||
|
case "true":
|
||||||
|
result.Value = &Bool{
|
||||||
LiteralPos: p.scanner.Position,
|
LiteralPos: p.scanner.Position,
|
||||||
Value: true,
|
Value: true,
|
||||||
}
|
}
|
||||||
case "false":
|
|
||||||
p.next()
|
p.next()
|
||||||
return &Bool{
|
return result
|
||||||
|
case "false":
|
||||||
|
result.Value = &Bool{
|
||||||
LiteralPos: p.scanner.Position,
|
LiteralPos: p.scanner.Position,
|
||||||
Value: false,
|
Value: false,
|
||||||
}
|
}
|
||||||
|
p.next()
|
||||||
|
return result
|
||||||
default:
|
default:
|
||||||
p.errorf("Expted a string, true, false, or default, got %s", p.scanner.TokenText())
|
p.errorf("Expected a string, true, false, or default, got %s", p.scanner.TokenText())
|
||||||
}
|
}
|
||||||
case scanner.String:
|
case scanner.String:
|
||||||
if s := p.parseStringValue(); s != nil {
|
if s := p.parseStringValue(); s != nil {
|
||||||
if strings.HasPrefix(s.Value, "__soong") {
|
if strings.HasPrefix(s.Value, "__soong") {
|
||||||
p.errorf("select branch conditions starting with __soong are reserved for internal use")
|
p.errorf("select branch patterns starting with __soong are reserved for internal use")
|
||||||
return nil
|
return result
|
||||||
}
|
}
|
||||||
return s
|
result.Value = s
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
fallthrough
|
fallthrough
|
||||||
default:
|
default:
|
||||||
p.errorf("Expted a string, true, false, or default, got %s", p.scanner.TokenText())
|
p.errorf("Expected a string, true, false, or default, got %s", p.scanner.TokenText())
|
||||||
}
|
}
|
||||||
return nil
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
hasNonUnsetValue := false
|
hasNonUnsetValue := false
|
||||||
|
@ -694,11 +580,7 @@ func (p *parser) parseSelect() Expression {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
for i := 0; i < len(conditions); i++ {
|
for i := 0; i < len(conditions); i++ {
|
||||||
if p := parseOnePattern(); p != nil {
|
c.Patterns = append(c.Patterns, parseOnePattern())
|
||||||
c.Patterns = append(c.Patterns, p)
|
|
||||||
} else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if i < len(conditions)-1 {
|
if i < len(conditions)-1 {
|
||||||
if !p.accept(',') {
|
if !p.accept(',') {
|
||||||
return nil
|
return nil
|
||||||
|
@ -712,18 +594,14 @@ func (p *parser) parseSelect() Expression {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if p := parseOnePattern(); p != nil {
|
c.Patterns = append(c.Patterns, parseOnePattern())
|
||||||
c.Patterns = append(c.Patterns, p)
|
|
||||||
} else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
c.ColonPos = p.scanner.Position
|
c.ColonPos = p.scanner.Position
|
||||||
if !p.accept(':') {
|
if !p.accept(':') {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if p.tok == scanner.Ident && p.scanner.TokenText() == "unset" {
|
if p.tok == scanner.Ident && p.scanner.TokenText() == "unset" {
|
||||||
c.Value = UnsetProperty{Position: p.scanner.Position}
|
c.Value = &UnsetProperty{Position: p.scanner.Position}
|
||||||
p.accept(scanner.Ident)
|
p.accept(scanner.Ident)
|
||||||
} else {
|
} else {
|
||||||
hasNonUnsetValue = true
|
hasNonUnsetValue = true
|
||||||
|
@ -742,16 +620,17 @@ func (p *parser) parseSelect() Expression {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
patternsEqual := func(a, b Expression) bool {
|
patternsEqual := func(a, b SelectPattern) bool {
|
||||||
switch a2 := a.(type) {
|
// We can ignore the bindings, they don't affect which pattern is matched
|
||||||
|
switch a2 := a.Value.(type) {
|
||||||
case *String:
|
case *String:
|
||||||
if b2, ok := b.(*String); ok {
|
if b2, ok := b.Value.(*String); ok {
|
||||||
return a2.Value == b2.Value
|
return a2.Value == b2.Value
|
||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
case *Bool:
|
case *Bool:
|
||||||
if b2, ok := b.(*Bool); ok {
|
if b2, ok := b.Value.(*Bool); ok {
|
||||||
return a2.Value == b2.Value
|
return a2.Value == b2.Value
|
||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
|
@ -762,7 +641,7 @@ func (p *parser) parseSelect() Expression {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
patternListsEqual := func(a, b []Expression) bool {
|
patternListsEqual := func(a, b []SelectPattern) bool {
|
||||||
if len(a) != len(b) {
|
if len(a) != len(b) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -775,18 +654,29 @@ func (p *parser) parseSelect() Expression {
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, c := range result.Cases {
|
for i, c := range result.Cases {
|
||||||
// Check for duplicates
|
// Check for duplicate patterns across different branches
|
||||||
for _, d := range result.Cases[i+1:] {
|
for _, d := range result.Cases[i+1:] {
|
||||||
if patternListsEqual(c.Patterns, d.Patterns) {
|
if patternListsEqual(c.Patterns, d.Patterns) {
|
||||||
p.errorf("Found duplicate select patterns: %v", c.Patterns)
|
p.errorf("Found duplicate select patterns: %v", c.Patterns)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// check for duplicate bindings within this branch
|
||||||
|
for i := range c.Patterns {
|
||||||
|
if c.Patterns[i].Binding.Name != "" {
|
||||||
|
for j := i + 1; j < len(c.Patterns); j++ {
|
||||||
|
if c.Patterns[i].Binding.Name == c.Patterns[j].Binding.Name {
|
||||||
|
p.errorf("Found duplicate select pattern binding: %s", c.Patterns[i].Binding.Name)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
// Check that the only all-default cases is the last one
|
// Check that the only all-default cases is the last one
|
||||||
if i < len(result.Cases)-1 {
|
if i < len(result.Cases)-1 {
|
||||||
isAllDefault := true
|
isAllDefault := true
|
||||||
for _, x := range c.Patterns {
|
for _, x := range c.Patterns {
|
||||||
if x2, ok := x.(*String); !ok || x2.Value != default_select_branch_name {
|
if x2, ok := x.Value.(*String); !ok || x2.Value != default_select_branch_name {
|
||||||
isAllDefault = false
|
isAllDefault = false
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -798,21 +688,6 @@ func (p *parser) parseSelect() Expression {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ty := UnsetType
|
|
||||||
for _, c := range result.Cases {
|
|
||||||
otherTy := c.Value.Type()
|
|
||||||
// Any other type can override UnsetType
|
|
||||||
if ty == UnsetType {
|
|
||||||
ty = otherTy
|
|
||||||
}
|
|
||||||
if otherTy != UnsetType && otherTy != ty {
|
|
||||||
p.errorf("Found select statement with differing types %q and %q in its cases", ty.String(), otherTy.String())
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
result.ExpressionType = ty
|
|
||||||
|
|
||||||
result.RBracePos = p.scanner.Position
|
result.RBracePos = p.scanner.Position
|
||||||
if !p.accept('}') {
|
if !p.accept('}') {
|
||||||
return nil
|
return nil
|
||||||
|
@ -913,79 +788,107 @@ func (p *parser) parseMapValue() *Map {
|
||||||
}
|
}
|
||||||
|
|
||||||
type Scope struct {
|
type Scope struct {
|
||||||
vars map[string]*Assignment
|
vars map[string]*Assignment
|
||||||
inheritedVars map[string]*Assignment
|
preventInheriting map[string]bool
|
||||||
|
parentScope *Scope
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewScope(s *Scope) *Scope {
|
func NewScope(s *Scope) *Scope {
|
||||||
newScope := &Scope{
|
return &Scope{
|
||||||
vars: make(map[string]*Assignment),
|
vars: make(map[string]*Assignment),
|
||||||
inheritedVars: make(map[string]*Assignment),
|
preventInheriting: make(map[string]bool),
|
||||||
|
parentScope: s,
|
||||||
}
|
}
|
||||||
|
|
||||||
if s != nil {
|
|
||||||
for k, v := range s.vars {
|
|
||||||
newScope.inheritedVars[k] = v
|
|
||||||
}
|
|
||||||
for k, v := range s.inheritedVars {
|
|
||||||
newScope.inheritedVars[k] = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return newScope
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Scope) Add(assignment *Assignment) error {
|
func (s *Scope) HandleAssignment(assignment *Assignment) error {
|
||||||
if old, ok := s.vars[assignment.Name]; ok {
|
switch assignment.Assigner {
|
||||||
return fmt.Errorf("variable already set, previous assignment: %s", old)
|
case "+=":
|
||||||
|
if !s.preventInheriting[assignment.Name] && s.parentScope.Get(assignment.Name) != nil {
|
||||||
|
return fmt.Errorf("modified non-local variable %q with +=", assignment.Name)
|
||||||
|
}
|
||||||
|
if old, ok := s.vars[assignment.Name]; !ok {
|
||||||
|
return fmt.Errorf("modified non-existent variable %q with +=", assignment.Name)
|
||||||
|
} else if old.Referenced {
|
||||||
|
return fmt.Errorf("modified variable %q with += after referencing", assignment.Name)
|
||||||
|
} else {
|
||||||
|
newValue, err := evaluateOperator(s, '+', old.Value, assignment.Value)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
old.Value = newValue
|
||||||
|
}
|
||||||
|
case "=":
|
||||||
|
if old, ok := s.vars[assignment.Name]; ok {
|
||||||
|
return fmt.Errorf("variable already set, previous assignment: %s", old)
|
||||||
|
}
|
||||||
|
|
||||||
|
if old := s.parentScope.Get(assignment.Name); old != nil && !s.preventInheriting[assignment.Name] {
|
||||||
|
return fmt.Errorf("variable already set in inherited scope, previous assignment: %s", old)
|
||||||
|
}
|
||||||
|
|
||||||
|
if newValue, err := assignment.Value.Eval(s); err != nil {
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
assignment.Value = newValue
|
||||||
|
}
|
||||||
|
s.vars[assignment.Name] = assignment
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("Unknown assigner '%s'", assignment.Assigner)
|
||||||
}
|
}
|
||||||
|
|
||||||
if old, ok := s.inheritedVars[assignment.Name]; ok {
|
|
||||||
return fmt.Errorf("variable already set in inherited scope, previous assignment: %s", old)
|
|
||||||
}
|
|
||||||
|
|
||||||
s.vars[assignment.Name] = assignment
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Scope) Remove(name string) {
|
func (s *Scope) Get(name string) *Assignment {
|
||||||
delete(s.vars, name)
|
if s == nil {
|
||||||
delete(s.inheritedVars, name)
|
return nil
|
||||||
|
}
|
||||||
|
if a, ok := s.vars[name]; ok {
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
if s.preventInheriting[name] {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return s.parentScope.Get(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Scope) Get(name string) (*Assignment, bool) {
|
func (s *Scope) GetLocal(name string) *Assignment {
|
||||||
|
if s == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
if a, ok := s.vars[name]; ok {
|
if a, ok := s.vars[name]; ok {
|
||||||
return a, true
|
return a
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
if a, ok := s.inheritedVars[name]; ok {
|
// DontInherit prevents this scope from inheriting the given variable from its
|
||||||
return a, false
|
// parent scope.
|
||||||
}
|
func (s *Scope) DontInherit(name string) {
|
||||||
|
s.preventInheriting[name] = true
|
||||||
return nil, false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Scope) String() string {
|
func (s *Scope) String() string {
|
||||||
vars := []string{}
|
var sb strings.Builder
|
||||||
|
s.stringInner(&sb)
|
||||||
|
return sb.String()
|
||||||
|
}
|
||||||
|
|
||||||
for k := range s.vars {
|
func (s *Scope) stringInner(sb *strings.Builder) {
|
||||||
vars = append(vars, k)
|
if s == nil {
|
||||||
|
return
|
||||||
}
|
}
|
||||||
for k := range s.inheritedVars {
|
vars := make([]string, 0, len(s.vars))
|
||||||
|
for k := range s.vars {
|
||||||
vars = append(vars, k)
|
vars = append(vars, k)
|
||||||
}
|
}
|
||||||
|
|
||||||
sort.Strings(vars)
|
sort.Strings(vars)
|
||||||
|
|
||||||
ret := []string{}
|
|
||||||
for _, v := range vars {
|
for _, v := range vars {
|
||||||
if assignment, ok := s.vars[v]; ok {
|
sb.WriteString(s.vars[v].String())
|
||||||
ret = append(ret, assignment.String())
|
sb.WriteRune('\n')
|
||||||
} else {
|
|
||||||
ret = append(ret, s.inheritedVars[v].String())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return strings.Join(ret, "\n")
|
s.parentScope.stringInner(sb)
|
||||||
}
|
}
|
||||||
|
|
|
@ -565,12 +565,7 @@ var validParseTestCases = []struct {
|
||||||
LiteralPos: mkpos(9, 2, 9),
|
LiteralPos: mkpos(9, 2, 9),
|
||||||
Value: "stuff",
|
Value: "stuff",
|
||||||
},
|
},
|
||||||
OrigValue: &String{
|
Assigner: "=",
|
||||||
LiteralPos: mkpos(9, 2, 9),
|
|
||||||
Value: "stuff",
|
|
||||||
},
|
|
||||||
Assigner: "=",
|
|
||||||
Referenced: true,
|
|
||||||
},
|
},
|
||||||
&Assignment{
|
&Assignment{
|
||||||
Name: "bar",
|
Name: "bar",
|
||||||
|
@ -579,21 +574,8 @@ var validParseTestCases = []struct {
|
||||||
Value: &Variable{
|
Value: &Variable{
|
||||||
Name: "foo",
|
Name: "foo",
|
||||||
NamePos: mkpos(25, 3, 9),
|
NamePos: mkpos(25, 3, 9),
|
||||||
Value: &String{
|
|
||||||
LiteralPos: mkpos(9, 2, 9),
|
|
||||||
Value: "stuff",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
OrigValue: &Variable{
|
Assigner: "=",
|
||||||
Name: "foo",
|
|
||||||
NamePos: mkpos(25, 3, 9),
|
|
||||||
Value: &String{
|
|
||||||
LiteralPos: mkpos(9, 2, 9),
|
|
||||||
Value: "stuff",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Assigner: "=",
|
|
||||||
Referenced: true,
|
|
||||||
},
|
},
|
||||||
&Assignment{
|
&Assignment{
|
||||||
Name: "baz",
|
Name: "baz",
|
||||||
|
@ -602,155 +584,26 @@ var validParseTestCases = []struct {
|
||||||
Value: &Operator{
|
Value: &Operator{
|
||||||
OperatorPos: mkpos(41, 4, 13),
|
OperatorPos: mkpos(41, 4, 13),
|
||||||
Operator: '+',
|
Operator: '+',
|
||||||
Value: &String{
|
|
||||||
LiteralPos: mkpos(9, 2, 9),
|
|
||||||
Value: "stuffstuff",
|
|
||||||
},
|
|
||||||
Args: [2]Expression{
|
Args: [2]Expression{
|
||||||
&Variable{
|
&Variable{
|
||||||
Name: "foo",
|
Name: "foo",
|
||||||
NamePos: mkpos(37, 4, 9),
|
NamePos: mkpos(37, 4, 9),
|
||||||
Value: &String{
|
|
||||||
LiteralPos: mkpos(9, 2, 9),
|
|
||||||
Value: "stuff",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
&Variable{
|
&Variable{
|
||||||
Name: "bar",
|
Name: "bar",
|
||||||
NamePos: mkpos(43, 4, 15),
|
NamePos: mkpos(43, 4, 15),
|
||||||
Value: &Variable{
|
|
||||||
Name: "foo",
|
|
||||||
NamePos: mkpos(25, 3, 9),
|
|
||||||
Value: &String{
|
|
||||||
LiteralPos: mkpos(9, 2, 9),
|
|
||||||
Value: "stuff",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
OrigValue: &Operator{
|
Assigner: "=",
|
||||||
OperatorPos: mkpos(41, 4, 13),
|
|
||||||
Operator: '+',
|
|
||||||
Value: &String{
|
|
||||||
LiteralPos: mkpos(9, 2, 9),
|
|
||||||
Value: "stuffstuff",
|
|
||||||
},
|
|
||||||
Args: [2]Expression{
|
|
||||||
&Variable{
|
|
||||||
Name: "foo",
|
|
||||||
NamePos: mkpos(37, 4, 9),
|
|
||||||
Value: &String{
|
|
||||||
LiteralPos: mkpos(9, 2, 9),
|
|
||||||
Value: "stuff",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
&Variable{
|
|
||||||
Name: "bar",
|
|
||||||
NamePos: mkpos(43, 4, 15),
|
|
||||||
Value: &Variable{
|
|
||||||
Name: "foo",
|
|
||||||
NamePos: mkpos(25, 3, 9),
|
|
||||||
Value: &String{
|
|
||||||
LiteralPos: mkpos(9, 2, 9),
|
|
||||||
Value: "stuff",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Assigner: "=",
|
|
||||||
Referenced: true,
|
|
||||||
},
|
},
|
||||||
&Assignment{
|
&Assignment{
|
||||||
Name: "boo",
|
Name: "boo",
|
||||||
NamePos: mkpos(49, 5, 3),
|
NamePos: mkpos(49, 5, 3),
|
||||||
EqualsPos: mkpos(53, 5, 7),
|
EqualsPos: mkpos(53, 5, 7),
|
||||||
Value: &Operator{
|
Value: &Variable{
|
||||||
Args: [2]Expression{
|
|
||||||
&Variable{
|
|
||||||
Name: "baz",
|
|
||||||
NamePos: mkpos(55, 5, 9),
|
|
||||||
Value: &Operator{
|
|
||||||
OperatorPos: mkpos(41, 4, 13),
|
|
||||||
Operator: '+',
|
|
||||||
Value: &String{
|
|
||||||
LiteralPos: mkpos(9, 2, 9),
|
|
||||||
Value: "stuffstuff",
|
|
||||||
},
|
|
||||||
Args: [2]Expression{
|
|
||||||
&Variable{
|
|
||||||
Name: "foo",
|
|
||||||
NamePos: mkpos(37, 4, 9),
|
|
||||||
Value: &String{
|
|
||||||
LiteralPos: mkpos(9, 2, 9),
|
|
||||||
Value: "stuff",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
&Variable{
|
|
||||||
Name: "bar",
|
|
||||||
NamePos: mkpos(43, 4, 15),
|
|
||||||
Value: &Variable{
|
|
||||||
Name: "foo",
|
|
||||||
NamePos: mkpos(25, 3, 9),
|
|
||||||
Value: &String{
|
|
||||||
LiteralPos: mkpos(9, 2, 9),
|
|
||||||
Value: "stuff",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
&Variable{
|
|
||||||
Name: "foo",
|
|
||||||
NamePos: mkpos(68, 6, 10),
|
|
||||||
Value: &String{
|
|
||||||
LiteralPos: mkpos(9, 2, 9),
|
|
||||||
Value: "stuff",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
OperatorPos: mkpos(66, 6, 8),
|
|
||||||
Operator: '+',
|
|
||||||
Value: &String{
|
|
||||||
LiteralPos: mkpos(9, 2, 9),
|
|
||||||
Value: "stuffstuffstuff",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
OrigValue: &Variable{
|
|
||||||
Name: "baz",
|
Name: "baz",
|
||||||
NamePos: mkpos(55, 5, 9),
|
NamePos: mkpos(55, 5, 9),
|
||||||
Value: &Operator{
|
|
||||||
OperatorPos: mkpos(41, 4, 13),
|
|
||||||
Operator: '+',
|
|
||||||
Value: &String{
|
|
||||||
LiteralPos: mkpos(9, 2, 9),
|
|
||||||
Value: "stuffstuff",
|
|
||||||
},
|
|
||||||
Args: [2]Expression{
|
|
||||||
&Variable{
|
|
||||||
Name: "foo",
|
|
||||||
NamePos: mkpos(37, 4, 9),
|
|
||||||
Value: &String{
|
|
||||||
LiteralPos: mkpos(9, 2, 9),
|
|
||||||
Value: "stuff",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
&Variable{
|
|
||||||
Name: "bar",
|
|
||||||
NamePos: mkpos(43, 4, 15),
|
|
||||||
Value: &Variable{
|
|
||||||
Name: "foo",
|
|
||||||
NamePos: mkpos(25, 3, 9),
|
|
||||||
Value: &String{
|
|
||||||
LiteralPos: mkpos(9, 2, 9),
|
|
||||||
Value: "stuff",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
Assigner: "=",
|
Assigner: "=",
|
||||||
},
|
},
|
||||||
|
@ -761,18 +614,6 @@ var validParseTestCases = []struct {
|
||||||
Value: &Variable{
|
Value: &Variable{
|
||||||
Name: "foo",
|
Name: "foo",
|
||||||
NamePos: mkpos(68, 6, 10),
|
NamePos: mkpos(68, 6, 10),
|
||||||
Value: &String{
|
|
||||||
LiteralPos: mkpos(9, 2, 9),
|
|
||||||
Value: "stuff",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
OrigValue: &Variable{
|
|
||||||
Name: "foo",
|
|
||||||
NamePos: mkpos(68, 6, 10),
|
|
||||||
Value: &String{
|
|
||||||
LiteralPos: mkpos(9, 2, 9),
|
|
||||||
Value: "stuff",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
Assigner: "+=",
|
Assigner: "+=",
|
||||||
},
|
},
|
||||||
|
@ -791,10 +632,6 @@ var validParseTestCases = []struct {
|
||||||
Value: &Operator{
|
Value: &Operator{
|
||||||
OperatorPos: mkpos(12, 2, 12),
|
OperatorPos: mkpos(12, 2, 12),
|
||||||
Operator: '+',
|
Operator: '+',
|
||||||
Value: &Int64{
|
|
||||||
LiteralPos: mkpos(9, 2, 9),
|
|
||||||
Value: -3,
|
|
||||||
},
|
|
||||||
Args: [2]Expression{
|
Args: [2]Expression{
|
||||||
&Int64{
|
&Int64{
|
||||||
LiteralPos: mkpos(9, 2, 9),
|
LiteralPos: mkpos(9, 2, 9),
|
||||||
|
@ -804,10 +641,6 @@ var validParseTestCases = []struct {
|
||||||
&Operator{
|
&Operator{
|
||||||
OperatorPos: mkpos(17, 2, 17),
|
OperatorPos: mkpos(17, 2, 17),
|
||||||
Operator: '+',
|
Operator: '+',
|
||||||
Value: &Int64{
|
|
||||||
LiteralPos: mkpos(14, 2, 14),
|
|
||||||
Value: 1,
|
|
||||||
},
|
|
||||||
Args: [2]Expression{
|
Args: [2]Expression{
|
||||||
&Int64{
|
&Int64{
|
||||||
LiteralPos: mkpos(14, 2, 14),
|
LiteralPos: mkpos(14, 2, 14),
|
||||||
|
@ -823,43 +656,7 @@ var validParseTestCases = []struct {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
OrigValue: &Operator{
|
Assigner: "=",
|
||||||
OperatorPos: mkpos(12, 2, 12),
|
|
||||||
Operator: '+',
|
|
||||||
Value: &Int64{
|
|
||||||
LiteralPos: mkpos(9, 2, 9),
|
|
||||||
Value: -3,
|
|
||||||
},
|
|
||||||
Args: [2]Expression{
|
|
||||||
&Int64{
|
|
||||||
LiteralPos: mkpos(9, 2, 9),
|
|
||||||
Value: -4,
|
|
||||||
Token: "-4",
|
|
||||||
},
|
|
||||||
&Operator{
|
|
||||||
OperatorPos: mkpos(17, 2, 17),
|
|
||||||
Operator: '+',
|
|
||||||
Value: &Int64{
|
|
||||||
LiteralPos: mkpos(14, 2, 14),
|
|
||||||
Value: 1,
|
|
||||||
},
|
|
||||||
Args: [2]Expression{
|
|
||||||
&Int64{
|
|
||||||
LiteralPos: mkpos(14, 2, 14),
|
|
||||||
Value: -5,
|
|
||||||
Token: "-5",
|
|
||||||
},
|
|
||||||
&Int64{
|
|
||||||
LiteralPos: mkpos(19, 2, 19),
|
|
||||||
Value: 6,
|
|
||||||
Token: "6",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Assigner: "=",
|
|
||||||
Referenced: false,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
nil,
|
nil,
|
||||||
|
@ -882,13 +679,7 @@ var validParseTestCases = []struct {
|
||||||
Value: 1000000,
|
Value: 1000000,
|
||||||
Token: "1000000",
|
Token: "1000000",
|
||||||
},
|
},
|
||||||
OrigValue: &Int64{
|
Assigner: "=",
|
||||||
LiteralPos: mkpos(9, 2, 9),
|
|
||||||
Value: 1000000,
|
|
||||||
Token: "1000000",
|
|
||||||
},
|
|
||||||
Assigner: "=",
|
|
||||||
Referenced: true,
|
|
||||||
},
|
},
|
||||||
&Assignment{
|
&Assignment{
|
||||||
Name: "bar",
|
Name: "bar",
|
||||||
|
@ -897,23 +688,8 @@ var validParseTestCases = []struct {
|
||||||
Value: &Variable{
|
Value: &Variable{
|
||||||
Name: "foo",
|
Name: "foo",
|
||||||
NamePos: mkpos(25, 3, 9),
|
NamePos: mkpos(25, 3, 9),
|
||||||
Value: &Int64{
|
|
||||||
LiteralPos: mkpos(9, 2, 9),
|
|
||||||
Value: 1000000,
|
|
||||||
Token: "1000000",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
OrigValue: &Variable{
|
Assigner: "=",
|
||||||
Name: "foo",
|
|
||||||
NamePos: mkpos(25, 3, 9),
|
|
||||||
Value: &Int64{
|
|
||||||
LiteralPos: mkpos(9, 2, 9),
|
|
||||||
Value: 1000000,
|
|
||||||
Token: "1000000",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Assigner: "=",
|
|
||||||
Referenced: true,
|
|
||||||
},
|
},
|
||||||
&Assignment{
|
&Assignment{
|
||||||
Name: "baz",
|
Name: "baz",
|
||||||
|
@ -922,164 +698,26 @@ var validParseTestCases = []struct {
|
||||||
Value: &Operator{
|
Value: &Operator{
|
||||||
OperatorPos: mkpos(41, 4, 13),
|
OperatorPos: mkpos(41, 4, 13),
|
||||||
Operator: '+',
|
Operator: '+',
|
||||||
Value: &Int64{
|
|
||||||
LiteralPos: mkpos(9, 2, 9),
|
|
||||||
Value: 2000000,
|
|
||||||
},
|
|
||||||
Args: [2]Expression{
|
Args: [2]Expression{
|
||||||
&Variable{
|
&Variable{
|
||||||
Name: "foo",
|
Name: "foo",
|
||||||
NamePos: mkpos(37, 4, 9),
|
NamePos: mkpos(37, 4, 9),
|
||||||
Value: &Int64{
|
|
||||||
LiteralPos: mkpos(9, 2, 9),
|
|
||||||
Value: 1000000,
|
|
||||||
Token: "1000000",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
&Variable{
|
&Variable{
|
||||||
Name: "bar",
|
Name: "bar",
|
||||||
NamePos: mkpos(43, 4, 15),
|
NamePos: mkpos(43, 4, 15),
|
||||||
Value: &Variable{
|
|
||||||
Name: "foo",
|
|
||||||
NamePos: mkpos(25, 3, 9),
|
|
||||||
Value: &Int64{
|
|
||||||
LiteralPos: mkpos(9, 2, 9),
|
|
||||||
Value: 1000000,
|
|
||||||
Token: "1000000",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
OrigValue: &Operator{
|
Assigner: "=",
|
||||||
OperatorPos: mkpos(41, 4, 13),
|
|
||||||
Operator: '+',
|
|
||||||
Value: &Int64{
|
|
||||||
LiteralPos: mkpos(9, 2, 9),
|
|
||||||
Value: 2000000,
|
|
||||||
},
|
|
||||||
Args: [2]Expression{
|
|
||||||
&Variable{
|
|
||||||
Name: "foo",
|
|
||||||
NamePos: mkpos(37, 4, 9),
|
|
||||||
Value: &Int64{
|
|
||||||
LiteralPos: mkpos(9, 2, 9),
|
|
||||||
Value: 1000000,
|
|
||||||
Token: "1000000",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
&Variable{
|
|
||||||
Name: "bar",
|
|
||||||
NamePos: mkpos(43, 4, 15),
|
|
||||||
Value: &Variable{
|
|
||||||
Name: "foo",
|
|
||||||
NamePos: mkpos(25, 3, 9),
|
|
||||||
Value: &Int64{
|
|
||||||
LiteralPos: mkpos(9, 2, 9),
|
|
||||||
Value: 1000000,
|
|
||||||
Token: "1000000",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Assigner: "=",
|
|
||||||
Referenced: true,
|
|
||||||
},
|
},
|
||||||
&Assignment{
|
&Assignment{
|
||||||
Name: "boo",
|
Name: "boo",
|
||||||
NamePos: mkpos(49, 5, 3),
|
NamePos: mkpos(49, 5, 3),
|
||||||
EqualsPos: mkpos(53, 5, 7),
|
EqualsPos: mkpos(53, 5, 7),
|
||||||
Value: &Operator{
|
Value: &Variable{
|
||||||
Args: [2]Expression{
|
|
||||||
&Variable{
|
|
||||||
Name: "baz",
|
|
||||||
NamePos: mkpos(55, 5, 9),
|
|
||||||
Value: &Operator{
|
|
||||||
OperatorPos: mkpos(41, 4, 13),
|
|
||||||
Operator: '+',
|
|
||||||
Value: &Int64{
|
|
||||||
LiteralPos: mkpos(9, 2, 9),
|
|
||||||
Value: 2000000,
|
|
||||||
},
|
|
||||||
Args: [2]Expression{
|
|
||||||
&Variable{
|
|
||||||
Name: "foo",
|
|
||||||
NamePos: mkpos(37, 4, 9),
|
|
||||||
Value: &Int64{
|
|
||||||
LiteralPos: mkpos(9, 2, 9),
|
|
||||||
Value: 1000000,
|
|
||||||
Token: "1000000",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
&Variable{
|
|
||||||
Name: "bar",
|
|
||||||
NamePos: mkpos(43, 4, 15),
|
|
||||||
Value: &Variable{
|
|
||||||
Name: "foo",
|
|
||||||
NamePos: mkpos(25, 3, 9),
|
|
||||||
Value: &Int64{
|
|
||||||
LiteralPos: mkpos(9, 2, 9),
|
|
||||||
Value: 1000000,
|
|
||||||
Token: "1000000",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
&Variable{
|
|
||||||
Name: "foo",
|
|
||||||
NamePos: mkpos(68, 6, 10),
|
|
||||||
Value: &Int64{
|
|
||||||
LiteralPos: mkpos(9, 2, 9),
|
|
||||||
Value: 1000000,
|
|
||||||
Token: "1000000",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
OperatorPos: mkpos(66, 6, 8),
|
|
||||||
Operator: '+',
|
|
||||||
Value: &Int64{
|
|
||||||
LiteralPos: mkpos(9, 2, 9),
|
|
||||||
Value: 3000000,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
OrigValue: &Variable{
|
|
||||||
Name: "baz",
|
Name: "baz",
|
||||||
NamePos: mkpos(55, 5, 9),
|
NamePos: mkpos(55, 5, 9),
|
||||||
Value: &Operator{
|
|
||||||
OperatorPos: mkpos(41, 4, 13),
|
|
||||||
Operator: '+',
|
|
||||||
Value: &Int64{
|
|
||||||
LiteralPos: mkpos(9, 2, 9),
|
|
||||||
Value: 2000000,
|
|
||||||
},
|
|
||||||
Args: [2]Expression{
|
|
||||||
&Variable{
|
|
||||||
Name: "foo",
|
|
||||||
NamePos: mkpos(37, 4, 9),
|
|
||||||
Value: &Int64{
|
|
||||||
LiteralPos: mkpos(9, 2, 9),
|
|
||||||
Value: 1000000,
|
|
||||||
Token: "1000000",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
&Variable{
|
|
||||||
Name: "bar",
|
|
||||||
NamePos: mkpos(43, 4, 15),
|
|
||||||
Value: &Variable{
|
|
||||||
Name: "foo",
|
|
||||||
NamePos: mkpos(25, 3, 9),
|
|
||||||
Value: &Int64{
|
|
||||||
LiteralPos: mkpos(9, 2, 9),
|
|
||||||
Value: 1000000,
|
|
||||||
Token: "1000000",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
Assigner: "=",
|
Assigner: "=",
|
||||||
},
|
},
|
||||||
|
@ -1090,20 +728,6 @@ var validParseTestCases = []struct {
|
||||||
Value: &Variable{
|
Value: &Variable{
|
||||||
Name: "foo",
|
Name: "foo",
|
||||||
NamePos: mkpos(68, 6, 10),
|
NamePos: mkpos(68, 6, 10),
|
||||||
Value: &Int64{
|
|
||||||
LiteralPos: mkpos(9, 2, 9),
|
|
||||||
Value: 1000000,
|
|
||||||
Token: "1000000",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
OrigValue: &Variable{
|
|
||||||
Name: "foo",
|
|
||||||
NamePos: mkpos(68, 6, 10),
|
|
||||||
Value: &Int64{
|
|
||||||
LiteralPos: mkpos(9, 2, 9),
|
|
||||||
Value: 1000000,
|
|
||||||
Token: "1000000",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
Assigner: "+=",
|
Assigner: "+=",
|
||||||
},
|
},
|
||||||
|
@ -1171,7 +795,7 @@ func TestParseValidInput(t *testing.T) {
|
||||||
for i, testCase := range validParseTestCases {
|
for i, testCase := range validParseTestCases {
|
||||||
t.Run(strconv.Itoa(i), func(t *testing.T) {
|
t.Run(strconv.Itoa(i), func(t *testing.T) {
|
||||||
r := bytes.NewBufferString(testCase.input)
|
r := bytes.NewBufferString(testCase.input)
|
||||||
file, errs := ParseAndEval("", r, NewScope(nil))
|
file, errs := Parse("", r)
|
||||||
if len(errs) != 0 {
|
if len(errs) != 0 {
|
||||||
t.Errorf("test case: %s", testCase.input)
|
t.Errorf("test case: %s", testCase.input)
|
||||||
t.Errorf("unexpected errors:")
|
t.Errorf("unexpected errors:")
|
||||||
|
@ -1236,6 +860,17 @@ func TestParserError(t *testing.T) {
|
||||||
`,
|
`,
|
||||||
err: "Duplicate select condition found: arch()",
|
err: "Duplicate select condition found: arch()",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "select with duplicate binding",
|
||||||
|
input: `
|
||||||
|
m {
|
||||||
|
foo: select((arch(), os()), {
|
||||||
|
(any @ bar, any @ bar): true,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
err: "Found duplicate select pattern binding: bar",
|
||||||
|
},
|
||||||
// TODO: test more parser errors
|
// TODO: test more parser errors
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1284,7 +919,7 @@ func TestParserEndPos(t *testing.T) {
|
||||||
|
|
||||||
r := bytes.NewBufferString(in)
|
r := bytes.NewBufferString(in)
|
||||||
|
|
||||||
file, errs := ParseAndEval("", r, NewScope(nil))
|
file, errs := Parse("", r)
|
||||||
if len(errs) != 0 {
|
if len(errs) != 0 {
|
||||||
t.Errorf("unexpected errors:")
|
t.Errorf("unexpected errors:")
|
||||||
for _, err := range errs {
|
for _, err := range errs {
|
||||||
|
@ -1318,9 +953,8 @@ func TestParserEndPos(t *testing.T) {
|
||||||
|
|
||||||
func TestParserNotEvaluated(t *testing.T) {
|
func TestParserNotEvaluated(t *testing.T) {
|
||||||
// When parsing without evaluation, create variables correctly
|
// When parsing without evaluation, create variables correctly
|
||||||
scope := NewScope(nil)
|
|
||||||
input := "FOO=abc\n"
|
input := "FOO=abc\n"
|
||||||
_, errs := Parse("", bytes.NewBufferString(input), scope)
|
file, errs := Parse("", bytes.NewBufferString(input))
|
||||||
if errs != nil {
|
if errs != nil {
|
||||||
t.Errorf("unexpected errors:")
|
t.Errorf("unexpected errors:")
|
||||||
for _, err := range errs {
|
for _, err := range errs {
|
||||||
|
@ -1328,11 +962,11 @@ func TestParserNotEvaluated(t *testing.T) {
|
||||||
}
|
}
|
||||||
t.FailNow()
|
t.FailNow()
|
||||||
}
|
}
|
||||||
assignment, found := scope.Get("FOO")
|
assignment, ok := file.Defs[0].(*Assignment)
|
||||||
if !found {
|
if !ok || assignment.Name != "FOO" {
|
||||||
t.Fatalf("Expected to find FOO after parsing %s", input)
|
t.Fatalf("Expected to find FOO after parsing %s", input)
|
||||||
}
|
}
|
||||||
if s := assignment.String(); strings.Contains(s, "PANIC") {
|
if assignment.Value.String() != "abc" {
|
||||||
t.Errorf("Attempt to print FOO returned %s", s)
|
t.Errorf("Attempt to print FOO returned %s", assignment.Value.String())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -99,7 +99,7 @@ func (p *printer) printAssignment(assignment *Assignment) {
|
||||||
p.requestSpace()
|
p.requestSpace()
|
||||||
p.printToken(assignment.Assigner, assignment.EqualsPos)
|
p.printToken(assignment.Assigner, assignment.EqualsPos)
|
||||||
p.requestSpace()
|
p.requestSpace()
|
||||||
p.printExpression(assignment.OrigValue)
|
p.printExpression(assignment.Value)
|
||||||
p.requestNewline()
|
p.requestNewline()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -134,7 +134,7 @@ func (p *printer) printExpression(value Expression) {
|
||||||
case *Select:
|
case *Select:
|
||||||
p.printSelect(v)
|
p.printSelect(v)
|
||||||
default:
|
default:
|
||||||
panic(fmt.Errorf("bad property type: %s", value.Type()))
|
panic(fmt.Errorf("bad property type: %v", value))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -143,7 +143,7 @@ func (p *printer) printSelect(s *Select) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if len(s.Cases) == 1 && len(s.Cases[0].Patterns) == 1 {
|
if len(s.Cases) == 1 && len(s.Cases[0].Patterns) == 1 {
|
||||||
if str, ok := s.Cases[0].Patterns[0].(*String); ok && str.Value == default_select_branch_name {
|
if str, ok := s.Cases[0].Patterns[0].Value.(*String); ok && str.Value == default_select_branch_name {
|
||||||
p.printExpression(s.Cases[0].Value)
|
p.printExpression(s.Cases[0].Value)
|
||||||
p.pos = s.RBracePos
|
p.pos = s.RBracePos
|
||||||
return
|
return
|
||||||
|
@ -196,22 +196,7 @@ func (p *printer) printSelect(s *Select) {
|
||||||
p.printToken("(", p.pos)
|
p.printToken("(", p.pos)
|
||||||
}
|
}
|
||||||
for i, pat := range c.Patterns {
|
for i, pat := range c.Patterns {
|
||||||
switch pat := pat.(type) {
|
p.printSelectPattern(pat)
|
||||||
case *String:
|
|
||||||
if pat.Value != default_select_branch_name {
|
|
||||||
p.printToken(strconv.Quote(pat.Value), pat.LiteralPos)
|
|
||||||
} else {
|
|
||||||
p.printToken("default", pat.LiteralPos)
|
|
||||||
}
|
|
||||||
case *Bool:
|
|
||||||
s := "false"
|
|
||||||
if pat.Value {
|
|
||||||
s = "true"
|
|
||||||
}
|
|
||||||
p.printToken(s, pat.LiteralPos)
|
|
||||||
default:
|
|
||||||
panic("Unhandled case")
|
|
||||||
}
|
|
||||||
if i < len(c.Patterns)-1 {
|
if i < len(c.Patterns)-1 {
|
||||||
p.printToken(",", p.pos)
|
p.printToken(",", p.pos)
|
||||||
p.requestSpace()
|
p.requestSpace()
|
||||||
|
@ -222,7 +207,7 @@ func (p *printer) printSelect(s *Select) {
|
||||||
}
|
}
|
||||||
p.printToken(":", c.ColonPos)
|
p.printToken(":", c.ColonPos)
|
||||||
p.requestSpace()
|
p.requestSpace()
|
||||||
if unset, ok := c.Value.(UnsetProperty); ok {
|
if unset, ok := c.Value.(*UnsetProperty); ok {
|
||||||
p.printToken(unset.String(), unset.Pos())
|
p.printToken(unset.String(), unset.Pos())
|
||||||
} else {
|
} else {
|
||||||
p.printExpression(c.Value)
|
p.printExpression(c.Value)
|
||||||
|
@ -240,6 +225,33 @@ func (p *printer) printSelect(s *Select) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *printer) printSelectPattern(pat SelectPattern) {
|
||||||
|
switch pat := pat.Value.(type) {
|
||||||
|
case *String:
|
||||||
|
if pat.Value == default_select_branch_name {
|
||||||
|
p.printToken("default", pat.LiteralPos)
|
||||||
|
} else if pat.Value == any_select_branch_name {
|
||||||
|
p.printToken("any", pat.LiteralPos)
|
||||||
|
} else {
|
||||||
|
p.printToken(strconv.Quote(pat.Value), pat.LiteralPos)
|
||||||
|
}
|
||||||
|
case *Bool:
|
||||||
|
s := "false"
|
||||||
|
if pat.Value {
|
||||||
|
s = "true"
|
||||||
|
}
|
||||||
|
p.printToken(s, pat.LiteralPos)
|
||||||
|
default:
|
||||||
|
panic("Unhandled case")
|
||||||
|
}
|
||||||
|
if pat.Binding.Name != "" {
|
||||||
|
p.requestSpace()
|
||||||
|
p.printToken("@", pat.Binding.Pos())
|
||||||
|
p.requestSpace()
|
||||||
|
p.printExpression(&pat.Binding)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (p *printer) printList(list []Expression, pos, endPos scanner.Position) {
|
func (p *printer) printList(list []Expression, pos, endPos scanner.Position) {
|
||||||
p.requestSpace()
|
p.requestSpace()
|
||||||
p.printToken("[", pos)
|
p.printToken("[", pos)
|
||||||
|
|
|
@ -733,6 +733,26 @@ foo {
|
||||||
default: [],
|
default: [],
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Select with bindings",
|
||||||
|
input: `
|
||||||
|
foo {
|
||||||
|
stuff: select(arch(), {
|
||||||
|
"x86": "a",
|
||||||
|
any
|
||||||
|
@ baz: "b" + baz,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
output: `
|
||||||
|
foo {
|
||||||
|
stuff: select(arch(), {
|
||||||
|
"x86": "a",
|
||||||
|
any @ baz: "b" + baz,
|
||||||
|
}),
|
||||||
|
}
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -744,7 +764,7 @@ func TestPrinter(t *testing.T) {
|
||||||
expected := testCase.output[1:]
|
expected := testCase.output[1:]
|
||||||
|
|
||||||
r := bytes.NewBufferString(in)
|
r := bytes.NewBufferString(in)
|
||||||
file, errs := Parse("", r, NewScope(nil))
|
file, errs := Parse("", r)
|
||||||
if len(errs) != 0 {
|
if len(errs) != 0 {
|
||||||
t.Errorf("test case: %s", in)
|
t.Errorf("test case: %s", in)
|
||||||
t.Errorf("unexpected errors:")
|
t.Errorf("unexpected errors:")
|
||||||
|
|
|
@ -282,8 +282,8 @@ func isListOfPrimitives(values []Expression) bool {
|
||||||
if len(values) == 0 {
|
if len(values) == 0 {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
switch values[0].Type() {
|
switch values[0].(type) {
|
||||||
case BoolType, StringType, Int64Type:
|
case *Bool, *String, *Int64:
|
||||||
return true
|
return true
|
||||||
default:
|
default:
|
||||||
return false
|
return false
|
||||||
|
|
|
@ -21,6 +21,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/google/blueprint/optional"
|
"github.com/google/blueprint/optional"
|
||||||
|
"github.com/google/blueprint/parser"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ConfigurableOptional is the same as ShallowOptional, but we use this separate
|
// ConfigurableOptional is the same as ShallowOptional, but we use this separate
|
||||||
|
@ -150,6 +151,17 @@ type ConfigurableValue struct {
|
||||||
boolValue bool
|
boolValue bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *ConfigurableValue) toExpression() parser.Expression {
|
||||||
|
switch c.typ {
|
||||||
|
case configurableValueTypeBool:
|
||||||
|
return &parser.Bool{Value: c.boolValue}
|
||||||
|
case configurableValueTypeString:
|
||||||
|
return &parser.String{Value: c.stringValue}
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("Unhandled configurableValueType: %s", c.typ.String()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (c *ConfigurableValue) String() string {
|
func (c *ConfigurableValue) String() string {
|
||||||
switch c.typ {
|
switch c.typ {
|
||||||
case configurableValueTypeString:
|
case configurableValueTypeString:
|
||||||
|
@ -193,6 +205,7 @@ const (
|
||||||
configurablePatternTypeString configurablePatternType = iota
|
configurablePatternTypeString configurablePatternType = iota
|
||||||
configurablePatternTypeBool
|
configurablePatternTypeBool
|
||||||
configurablePatternTypeDefault
|
configurablePatternTypeDefault
|
||||||
|
configurablePatternTypeAny
|
||||||
)
|
)
|
||||||
|
|
||||||
func (v *configurablePatternType) String() string {
|
func (v *configurablePatternType) String() string {
|
||||||
|
@ -203,6 +216,8 @@ func (v *configurablePatternType) String() string {
|
||||||
return "bool"
|
return "bool"
|
||||||
case configurablePatternTypeDefault:
|
case configurablePatternTypeDefault:
|
||||||
return "default"
|
return "default"
|
||||||
|
case configurablePatternTypeAny:
|
||||||
|
return "any"
|
||||||
default:
|
default:
|
||||||
panic("unimplemented")
|
panic("unimplemented")
|
||||||
}
|
}
|
||||||
|
@ -222,6 +237,7 @@ type ConfigurablePattern struct {
|
||||||
typ configurablePatternType
|
typ configurablePatternType
|
||||||
stringValue string
|
stringValue string
|
||||||
boolValue bool
|
boolValue bool
|
||||||
|
binding string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewStringConfigurablePattern(s string) ConfigurablePattern {
|
func NewStringConfigurablePattern(s string) ConfigurablePattern {
|
||||||
|
@ -251,6 +267,9 @@ func (p *ConfigurablePattern) matchesValue(v ConfigurableValue) bool {
|
||||||
if v.typ == configurableValueTypeUndefined {
|
if v.typ == configurableValueTypeUndefined {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if p.typ == configurablePatternTypeAny {
|
||||||
|
return true
|
||||||
|
}
|
||||||
if p.typ != v.typ.patternType() {
|
if p.typ != v.typ.patternType() {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -271,6 +290,9 @@ func (p *ConfigurablePattern) matchesValueType(v ConfigurableValue) bool {
|
||||||
if v.typ == configurableValueTypeUndefined {
|
if v.typ == configurableValueTypeUndefined {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
if p.typ == configurablePatternTypeAny {
|
||||||
|
return true
|
||||||
|
}
|
||||||
return p.typ == v.typ.patternType()
|
return p.typ == v.typ.patternType()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -282,27 +304,46 @@ func (p *ConfigurablePattern) matchesValueType(v ConfigurableValue) bool {
|
||||||
// different configurable properties.
|
// different configurable properties.
|
||||||
type ConfigurableCase[T ConfigurableElements] struct {
|
type ConfigurableCase[T ConfigurableElements] struct {
|
||||||
patterns []ConfigurablePattern
|
patterns []ConfigurablePattern
|
||||||
value *T
|
value parser.Expression
|
||||||
}
|
}
|
||||||
|
|
||||||
type configurableCaseReflection interface {
|
type configurableCaseReflection interface {
|
||||||
initialize(patterns []ConfigurablePattern, value interface{})
|
initialize(patterns []ConfigurablePattern, value parser.Expression)
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ configurableCaseReflection = &ConfigurableCase[string]{}
|
var _ configurableCaseReflection = &ConfigurableCase[string]{}
|
||||||
|
|
||||||
func NewConfigurableCase[T ConfigurableElements](patterns []ConfigurablePattern, value *T) ConfigurableCase[T] {
|
func NewConfigurableCase[T ConfigurableElements](patterns []ConfigurablePattern, value *T) ConfigurableCase[T] {
|
||||||
|
var valueExpr parser.Expression
|
||||||
|
if value == nil {
|
||||||
|
valueExpr = &parser.UnsetProperty{}
|
||||||
|
} else {
|
||||||
|
switch v := any(value).(type) {
|
||||||
|
case *string:
|
||||||
|
valueExpr = &parser.String{Value: *v}
|
||||||
|
case *bool:
|
||||||
|
valueExpr = &parser.Bool{Value: *v}
|
||||||
|
case *[]string:
|
||||||
|
innerValues := make([]parser.Expression, 0, len(*v))
|
||||||
|
for _, x := range *v {
|
||||||
|
innerValues = append(innerValues, &parser.String{Value: x})
|
||||||
|
}
|
||||||
|
valueExpr = &parser.List{Values: innerValues}
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("should be unreachable due to the ConfigurableElements restriction: %#v", value))
|
||||||
|
}
|
||||||
|
}
|
||||||
// Clone the values so they can't be modified from soong
|
// Clone the values so they can't be modified from soong
|
||||||
patterns = slices.Clone(patterns)
|
patterns = slices.Clone(patterns)
|
||||||
return ConfigurableCase[T]{
|
return ConfigurableCase[T]{
|
||||||
patterns: patterns,
|
patterns: patterns,
|
||||||
value: copyConfiguredValuePtr(value),
|
value: valueExpr,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ConfigurableCase[T]) initialize(patterns []ConfigurablePattern, value interface{}) {
|
func (c *ConfigurableCase[T]) initialize(patterns []ConfigurablePattern, value parser.Expression) {
|
||||||
c.patterns = patterns
|
c.patterns = patterns
|
||||||
c.value = value.(*T)
|
c.value = value
|
||||||
}
|
}
|
||||||
|
|
||||||
// for the given T, return the reflect.type of configurableCase[T]
|
// for the given T, return the reflect.type of configurableCase[T]
|
||||||
|
@ -371,6 +412,22 @@ type Configurable[T ConfigurableElements] struct {
|
||||||
marker configurableMarker
|
marker configurableMarker
|
||||||
propertyName string
|
propertyName string
|
||||||
inner *configurableInner[T]
|
inner *configurableInner[T]
|
||||||
|
// See Configurable.evaluate for a description of the postProcessor algorithm and
|
||||||
|
// why this is a 2d list
|
||||||
|
postProcessors *[][]postProcessor[T]
|
||||||
|
}
|
||||||
|
|
||||||
|
type postProcessor[T ConfigurableElements] struct {
|
||||||
|
f func(T) T
|
||||||
|
// start and end represent the range of configurableInners
|
||||||
|
// that this postprocessor is applied to. When appending two configurables
|
||||||
|
// together, the start and end values will stay the same for the left
|
||||||
|
// configurable's postprocessors, but the rights will be rebased by the
|
||||||
|
// number of configurableInners in the left configurable. This way
|
||||||
|
// the postProcessors still only apply to the configurableInners they
|
||||||
|
// origionally applied to before the appending.
|
||||||
|
start int
|
||||||
|
end int
|
||||||
}
|
}
|
||||||
|
|
||||||
type configurableInner[T ConfigurableElements] struct {
|
type configurableInner[T ConfigurableElements] struct {
|
||||||
|
@ -384,6 +441,7 @@ type configurableInner[T ConfigurableElements] struct {
|
||||||
type singleConfigurable[T ConfigurableElements] struct {
|
type singleConfigurable[T ConfigurableElements] struct {
|
||||||
conditions []ConfigurableCondition
|
conditions []ConfigurableCondition
|
||||||
cases []ConfigurableCase[T]
|
cases []ConfigurableCase[T]
|
||||||
|
scope *parser.Scope
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ignore the warning about the unused marker variable, it's used via reflection
|
// Ignore the warning about the unused marker variable, it's used via reflection
|
||||||
|
@ -398,6 +456,7 @@ func NewConfigurable[T ConfigurableElements](conditions []ConfigurableCondition,
|
||||||
// Clone the slices so they can't be modified from soong
|
// Clone the slices so they can't be modified from soong
|
||||||
conditions = slices.Clone(conditions)
|
conditions = slices.Clone(conditions)
|
||||||
cases = slices.Clone(cases)
|
cases = slices.Clone(cases)
|
||||||
|
var zeroPostProcessors [][]postProcessor[T]
|
||||||
return Configurable[T]{
|
return Configurable[T]{
|
||||||
inner: &configurableInner[T]{
|
inner: &configurableInner[T]{
|
||||||
single: singleConfigurable[T]{
|
single: singleConfigurable[T]{
|
||||||
|
@ -405,35 +464,81 @@ func NewConfigurable[T ConfigurableElements](conditions []ConfigurableCondition,
|
||||||
cases: cases,
|
cases: cases,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
postProcessors: &zeroPostProcessors,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func newConfigurableWithPropertyName[T ConfigurableElements](propertyName string, conditions []ConfigurableCondition, cases []ConfigurableCase[T], addScope bool) Configurable[T] {
|
||||||
|
result := NewConfigurable(conditions, cases)
|
||||||
|
result.propertyName = propertyName
|
||||||
|
if addScope {
|
||||||
|
for curr := result.inner; curr != nil; curr = curr.next {
|
||||||
|
curr.single.scope = parser.NewScope(nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Configurable[T]) AppendSimpleValue(value T) {
|
func (c *Configurable[T]) AppendSimpleValue(value T) {
|
||||||
value = copyConfiguredValue(value)
|
value = copyConfiguredValue(value)
|
||||||
// This may be a property that was never initialized from a bp file
|
// This may be a property that was never initialized from a bp file
|
||||||
if c.inner == nil {
|
if c.inner == nil {
|
||||||
c.inner = &configurableInner[T]{
|
c.initialize(nil, "", nil, []ConfigurableCase[T]{{
|
||||||
single: singleConfigurable[T]{
|
value: configuredValueToExpression(value),
|
||||||
cases: []ConfigurableCase[T]{{
|
}})
|
||||||
value: &value,
|
|
||||||
}},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
c.inner.appendSimpleValue(value)
|
c.inner.appendSimpleValue(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AddPostProcessor adds a function that will modify the result of
|
||||||
|
// Get() when Get() is called. It operates on all the current contents
|
||||||
|
// of the Configurable property, but if other values are appended to
|
||||||
|
// the Configurable property afterwards, the postProcessor will not run
|
||||||
|
// on them. This can be useful to essentially modify a configurable
|
||||||
|
// property without evaluating it.
|
||||||
|
func (c *Configurable[T]) AddPostProcessor(p func(T) T) {
|
||||||
|
// Add the new postProcessor on top of the tallest stack of postProcessors.
|
||||||
|
// See Configurable.evaluate for more details on the postProcessors algorithm
|
||||||
|
// and data structure.
|
||||||
|
num_links := c.inner.numLinks()
|
||||||
|
if c.postProcessors == nil {
|
||||||
|
var nilCases []ConfigurableCase[T]
|
||||||
|
c.initialize(nil, "", nil, nilCases)
|
||||||
|
}
|
||||||
|
if len(*c.postProcessors) == 0 {
|
||||||
|
*c.postProcessors = [][]postProcessor[T]{{{
|
||||||
|
f: p,
|
||||||
|
start: 0,
|
||||||
|
end: num_links,
|
||||||
|
}}}
|
||||||
|
} else {
|
||||||
|
deepestI := 0
|
||||||
|
deepestDepth := 0
|
||||||
|
for i := 0; i < len(*c.postProcessors); i++ {
|
||||||
|
if len((*c.postProcessors)[i]) > deepestDepth {
|
||||||
|
deepestDepth = len((*c.postProcessors)[i])
|
||||||
|
deepestI = i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(*c.postProcessors)[deepestI] = append((*c.postProcessors)[deepestI], postProcessor[T]{
|
||||||
|
f: p,
|
||||||
|
start: 0,
|
||||||
|
end: num_links,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Get returns the final value for the configurable property.
|
// Get returns the final value for the configurable property.
|
||||||
// A configurable property may be unset, in which case Get will return nil.
|
// A configurable property may be unset, in which case Get will return nil.
|
||||||
func (c *Configurable[T]) Get(evaluator ConfigurableEvaluator) ConfigurableOptional[T] {
|
func (c *Configurable[T]) Get(evaluator ConfigurableEvaluator) ConfigurableOptional[T] {
|
||||||
result := c.inner.evaluate(c.propertyName, evaluator)
|
result := c.evaluate(c.propertyName, evaluator)
|
||||||
return configuredValuePtrToOptional(result)
|
return configuredValuePtrToOptional(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetOrDefault is the same as Get, but will return the provided default value if the property was unset.
|
// GetOrDefault is the same as Get, but will return the provided default value if the property was unset.
|
||||||
func (c *Configurable[T]) GetOrDefault(evaluator ConfigurableEvaluator, defaultValue T) T {
|
func (c *Configurable[T]) GetOrDefault(evaluator ConfigurableEvaluator, defaultValue T) T {
|
||||||
result := c.inner.evaluate(c.propertyName, evaluator)
|
result := c.evaluate(c.propertyName, evaluator)
|
||||||
if result != nil {
|
if result != nil {
|
||||||
// Copy the result so that it can't be changed from soong
|
// Copy the result so that it can't be changed from soong
|
||||||
return copyConfiguredValue(*result)
|
return copyConfiguredValue(*result)
|
||||||
|
@ -441,6 +546,127 @@ func (c *Configurable[T]) GetOrDefault(evaluator ConfigurableEvaluator, defaultV
|
||||||
return defaultValue
|
return defaultValue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type valueAndIndices[T ConfigurableElements] struct {
|
||||||
|
value *T
|
||||||
|
replace bool
|
||||||
|
// Similar to start/end in postProcessor, these represent the origional
|
||||||
|
// range or configurableInners that this merged group represents. It's needed
|
||||||
|
// in order to apply recursive postProcessors to only the relevant
|
||||||
|
// configurableInners, even after those configurableInners have been merged
|
||||||
|
// in order to apply an earlier postProcessor.
|
||||||
|
start int
|
||||||
|
end int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Configurable[T]) evaluate(propertyName string, evaluator ConfigurableEvaluator) *T {
|
||||||
|
if c.inner == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(*c.postProcessors) == 0 {
|
||||||
|
// Use a simpler algorithm if there are no postprocessors
|
||||||
|
return c.inner.evaluate(propertyName, evaluator)
|
||||||
|
}
|
||||||
|
|
||||||
|
// The basic idea around evaluating with postprocessors is that each individual
|
||||||
|
// node in the chain (each configurableInner) is first evaluated, and then when
|
||||||
|
// a postprocessor operates on a certain range, that range is merged before passing
|
||||||
|
// it to the postprocessor. We want postProcessors to only accept a final merged
|
||||||
|
// value instead of a linked list, but at the same time, only operate over a portion
|
||||||
|
// of the list. If more configurables are appended onto this one, their values won't
|
||||||
|
// be operated on by the existing postProcessors, but they may have their own
|
||||||
|
// postprocessors.
|
||||||
|
//
|
||||||
|
// _____________________
|
||||||
|
// | __________|
|
||||||
|
// ______ | _____| ___
|
||||||
|
// | | | | | |
|
||||||
|
// a -> b -> c -> d -> e -> f -> g
|
||||||
|
//
|
||||||
|
// In this diagram, the letters along the bottom is the chain of configurableInners.
|
||||||
|
// The brackets on top represent postprocessors, where higher brackets are processed
|
||||||
|
// after lower ones.
|
||||||
|
//
|
||||||
|
// To evaluate this example, first we evaluate the raw values for all nodes a->g.
|
||||||
|
// Then we merge nodes a/b and d/e and apply the postprocessors to their merged values,
|
||||||
|
// and also to g. Those merged and postprocessed nodes are then reinserted into the
|
||||||
|
// list, and we move on to doing the higher level postprocessors (starting with the c->e one)
|
||||||
|
// in the same way. When all postprocessors are done, a final merge is done on anything
|
||||||
|
// leftover.
|
||||||
|
//
|
||||||
|
// The Configurable.postProcessors field is a 2d array to represent this hierarchy.
|
||||||
|
// The outer index moves right on this graph, the inner index goes up.
|
||||||
|
// When adding a new postProcessor, it will always be the last postProcessor to run
|
||||||
|
// until another is added or another configurable is appended. So in AddPostProcessor(),
|
||||||
|
// we add it to the tallest existing stack.
|
||||||
|
|
||||||
|
var currentValues []valueAndIndices[T]
|
||||||
|
for curr, i := c.inner, 0; curr != nil; curr, i = curr.next, i+1 {
|
||||||
|
value := curr.single.evaluateNonTransitive(propertyName, evaluator)
|
||||||
|
currentValues = append(currentValues, valueAndIndices[T]{
|
||||||
|
value: value,
|
||||||
|
replace: curr.replace,
|
||||||
|
start: i,
|
||||||
|
end: i + 1,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.postProcessors == nil || len(*c.postProcessors) == 0 {
|
||||||
|
return mergeValues(currentValues).value
|
||||||
|
}
|
||||||
|
|
||||||
|
foundPostProcessor := true
|
||||||
|
for depth := 0; foundPostProcessor; depth++ {
|
||||||
|
foundPostProcessor = false
|
||||||
|
var newValues []valueAndIndices[T]
|
||||||
|
i := 0
|
||||||
|
for _, postProcessorGroup := range *c.postProcessors {
|
||||||
|
if len(postProcessorGroup) > depth {
|
||||||
|
foundPostProcessor = true
|
||||||
|
postProcessor := postProcessorGroup[depth]
|
||||||
|
startI := 0
|
||||||
|
endI := 0
|
||||||
|
for currentValues[startI].start < postProcessor.start {
|
||||||
|
startI++
|
||||||
|
}
|
||||||
|
for currentValues[endI].end < postProcessor.end {
|
||||||
|
endI++
|
||||||
|
}
|
||||||
|
endI++
|
||||||
|
newValues = append(newValues, currentValues[i:startI]...)
|
||||||
|
merged := mergeValues(currentValues[startI:endI])
|
||||||
|
if merged.value != nil {
|
||||||
|
processed := postProcessor.f(*merged.value)
|
||||||
|
merged.value = &processed
|
||||||
|
}
|
||||||
|
newValues = append(newValues, merged)
|
||||||
|
i = endI
|
||||||
|
}
|
||||||
|
}
|
||||||
|
newValues = append(newValues, currentValues[i:]...)
|
||||||
|
currentValues = newValues
|
||||||
|
}
|
||||||
|
|
||||||
|
return mergeValues(currentValues).value
|
||||||
|
}
|
||||||
|
|
||||||
|
func mergeValues[T ConfigurableElements](values []valueAndIndices[T]) valueAndIndices[T] {
|
||||||
|
if len(values) < 0 {
|
||||||
|
panic("Expected at least 1 value in mergeValues")
|
||||||
|
}
|
||||||
|
result := values[0]
|
||||||
|
for i := 1; i < len(values); i++ {
|
||||||
|
if result.replace {
|
||||||
|
result.value = replaceConfiguredValues(result.value, values[i].value)
|
||||||
|
} else {
|
||||||
|
result.value = appendConfiguredValues(result.value, values[i].value)
|
||||||
|
}
|
||||||
|
result.end = values[i].end
|
||||||
|
result.replace = values[i].replace
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
func (c *configurableInner[T]) evaluate(propertyName string, evaluator ConfigurableEvaluator) *T {
|
func (c *configurableInner[T]) evaluate(propertyName string, evaluator ConfigurableEvaluator) *T {
|
||||||
if c == nil {
|
if c == nil {
|
||||||
return nil
|
return nil
|
||||||
|
@ -472,7 +698,12 @@ func (c *singleConfigurable[T]) evaluateNonTransitive(propertyName string, evalu
|
||||||
if len(c.cases) == 0 {
|
if len(c.cases) == 0 {
|
||||||
return nil
|
return nil
|
||||||
} else if len(c.cases) == 1 {
|
} else if len(c.cases) == 1 {
|
||||||
return c.cases[0].value
|
if result, err := expressionToConfiguredValue[T](c.cases[0].value, c.scope); err != nil {
|
||||||
|
evaluator.PropertyErrorf(propertyName, "%s", err.Error())
|
||||||
|
return nil
|
||||||
|
} else {
|
||||||
|
return result
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
evaluator.PropertyErrorf(propertyName, "Expected 0 or 1 branches in an unconfigured select, found %d", len(c.cases))
|
evaluator.PropertyErrorf(propertyName, "Expected 0 or 1 branches in an unconfigured select, found %d", len(c.cases))
|
||||||
return nil
|
return nil
|
||||||
|
@ -499,7 +730,13 @@ func (c *singleConfigurable[T]) evaluateNonTransitive(propertyName string, evalu
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if allMatch && !foundMatch {
|
if allMatch && !foundMatch {
|
||||||
result = case_.value
|
newScope := createScopeWithBindings(c.scope, case_.patterns, values)
|
||||||
|
if r, err := expressionToConfiguredValue[T](case_.value, newScope); err != nil {
|
||||||
|
evaluator.PropertyErrorf(propertyName, "%s", err.Error())
|
||||||
|
return nil
|
||||||
|
} else {
|
||||||
|
result = r
|
||||||
|
}
|
||||||
foundMatch = true
|
foundMatch = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -511,6 +748,27 @@ func (c *singleConfigurable[T]) evaluateNonTransitive(propertyName string, evalu
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func createScopeWithBindings(parent *parser.Scope, patterns []ConfigurablePattern, values []ConfigurableValue) *parser.Scope {
|
||||||
|
result := parent
|
||||||
|
for i, pattern := range patterns {
|
||||||
|
if pattern.binding != "" {
|
||||||
|
if result == parent {
|
||||||
|
result = parser.NewScope(parent)
|
||||||
|
}
|
||||||
|
err := result.HandleAssignment(&parser.Assignment{
|
||||||
|
Name: pattern.binding,
|
||||||
|
Value: values[i].toExpression(),
|
||||||
|
Assigner: "=",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
// This shouldn't happen due to earlier validity checks
|
||||||
|
panic(err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
func appendConfiguredValues[T ConfigurableElements](a, b *T) *T {
|
func appendConfiguredValues[T ConfigurableElements](a, b *T) *T {
|
||||||
if a == nil && b == nil {
|
if a == nil && b == nil {
|
||||||
return nil
|
return nil
|
||||||
|
@ -579,20 +837,27 @@ type configurableReflection interface {
|
||||||
// Same as configurableReflection, but since initialize needs to take a pointer
|
// Same as configurableReflection, but since initialize needs to take a pointer
|
||||||
// to a Configurable, it was broken out into a separate interface.
|
// to a Configurable, it was broken out into a separate interface.
|
||||||
type configurablePtrReflection interface {
|
type configurablePtrReflection interface {
|
||||||
initialize(propertyName string, conditions []ConfigurableCondition, cases any)
|
initialize(scope *parser.Scope, propertyName string, conditions []ConfigurableCondition, cases any)
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ configurableReflection = Configurable[string]{}
|
var _ configurableReflection = Configurable[string]{}
|
||||||
var _ configurablePtrReflection = &Configurable[string]{}
|
var _ configurablePtrReflection = &Configurable[string]{}
|
||||||
|
|
||||||
func (c *Configurable[T]) initialize(propertyName string, conditions []ConfigurableCondition, cases any) {
|
func (c *Configurable[T]) initialize(scope *parser.Scope, propertyName string, conditions []ConfigurableCondition, cases any) {
|
||||||
c.propertyName = propertyName
|
c.propertyName = propertyName
|
||||||
c.inner = &configurableInner[T]{
|
c.inner = &configurableInner[T]{
|
||||||
single: singleConfigurable[T]{
|
single: singleConfigurable[T]{
|
||||||
conditions: conditions,
|
conditions: conditions,
|
||||||
cases: cases.([]ConfigurableCase[T]),
|
cases: cases.([]ConfigurableCase[T]),
|
||||||
|
scope: scope,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
var postProcessors [][]postProcessor[T]
|
||||||
|
c.postProcessors = &postProcessors
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Configurable[T]) Append(other Configurable[T]) {
|
||||||
|
c.setAppend(other, false, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c Configurable[T]) setAppend(append any, replace bool, prepend bool) {
|
func (c Configurable[T]) setAppend(append any, replace bool, prepend bool) {
|
||||||
|
@ -600,12 +865,37 @@ func (c Configurable[T]) setAppend(append any, replace bool, prepend bool) {
|
||||||
if a.inner.isEmpty() {
|
if a.inner.isEmpty() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if prepend {
|
||||||
|
newBase := a.inner.numLinks()
|
||||||
|
*c.postProcessors = appendPostprocessors(*a.postProcessors, *c.postProcessors, newBase)
|
||||||
|
} else {
|
||||||
|
newBase := c.inner.numLinks()
|
||||||
|
*c.postProcessors = appendPostprocessors(*c.postProcessors, *a.postProcessors, newBase)
|
||||||
|
}
|
||||||
|
|
||||||
c.inner.setAppend(a.inner, replace, prepend)
|
c.inner.setAppend(a.inner, replace, prepend)
|
||||||
if c.inner == c.inner.next {
|
if c.inner == c.inner.next {
|
||||||
panic("pointer loop")
|
panic("pointer loop")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func appendPostprocessors[T ConfigurableElements](a, b [][]postProcessor[T], newBase int) [][]postProcessor[T] {
|
||||||
|
var result [][]postProcessor[T]
|
||||||
|
for i := 0; i < len(a); i++ {
|
||||||
|
result = append(result, slices.Clone(a[i]))
|
||||||
|
}
|
||||||
|
for i := 0; i < len(b); i++ {
|
||||||
|
n := slices.Clone(b[i])
|
||||||
|
for j := 0; j < len(n); j++ {
|
||||||
|
n[j].start += newBase
|
||||||
|
n[j].end += newBase
|
||||||
|
}
|
||||||
|
result = append(result, n)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
func (c *configurableInner[T]) setAppend(append *configurableInner[T], replace bool, prepend bool) {
|
func (c *configurableInner[T]) setAppend(append *configurableInner[T], replace bool, prepend bool) {
|
||||||
if c.isEmpty() {
|
if c.isEmpty() {
|
||||||
*c = *append.clone()
|
*c = *append.clone()
|
||||||
|
@ -644,13 +934,21 @@ func (c *configurableInner[T]) setAppend(append *configurableInner[T], replace b
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *configurableInner[T]) numLinks() int {
|
||||||
|
result := 0
|
||||||
|
for curr := c; curr != nil; curr = curr.next {
|
||||||
|
result++
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
func (c *configurableInner[T]) appendSimpleValue(value T) {
|
func (c *configurableInner[T]) appendSimpleValue(value T) {
|
||||||
if c.next == nil {
|
if c.next == nil {
|
||||||
c.replace = false
|
c.replace = false
|
||||||
c.next = &configurableInner[T]{
|
c.next = &configurableInner[T]{
|
||||||
single: singleConfigurable[T]{
|
single: singleConfigurable[T]{
|
||||||
cases: []ConfigurableCase[T]{{
|
cases: []ConfigurableCase[T]{{
|
||||||
value: &value,
|
value: configuredValueToExpression(value),
|
||||||
}},
|
}},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -678,46 +976,28 @@ func (c *singleConfigurable[T]) printfInto(value string) error {
|
||||||
if c.value == nil {
|
if c.value == nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
switch v := any(c.value).(type) {
|
if err := c.value.PrintfInto(value); err != nil {
|
||||||
case *string:
|
return err
|
||||||
if err := printfIntoString(v, value); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
case *[]string:
|
|
||||||
for i := range *v {
|
|
||||||
if err := printfIntoString(&((*v)[i]), value); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func printfIntoString(s *string, configValue string) error {
|
func (c Configurable[T]) clone() any {
|
||||||
count := strings.Count(*s, "%")
|
var newPostProcessors *[][]postProcessor[T]
|
||||||
if count == 0 {
|
if c.postProcessors != nil {
|
||||||
return nil
|
x := appendPostprocessors(*c.postProcessors, nil, 0)
|
||||||
|
newPostProcessors = &x
|
||||||
}
|
}
|
||||||
|
return Configurable[T]{
|
||||||
if count > 1 {
|
propertyName: c.propertyName,
|
||||||
return fmt.Errorf("list/value variable properties only support a single '%%'")
|
inner: c.inner.clone(),
|
||||||
|
postProcessors: newPostProcessors,
|
||||||
}
|
}
|
||||||
|
|
||||||
if !strings.Contains(*s, "%s") {
|
|
||||||
return fmt.Errorf("unsupported %% in value variable property")
|
|
||||||
}
|
|
||||||
|
|
||||||
*s = fmt.Sprintf(*s, configValue)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c Configurable[T]) clone() any {
|
func (c Configurable[T]) Clone() Configurable[T] {
|
||||||
return Configurable[T]{
|
return c.clone().(Configurable[T])
|
||||||
propertyName: c.propertyName,
|
|
||||||
inner: c.inner.clone(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *configurableInner[T]) clone() *configurableInner[T] {
|
func (c *configurableInner[T]) clone() *configurableInner[T] {
|
||||||
|
@ -755,6 +1035,9 @@ func (c *singleConfigurable[T]) isEmpty() bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if len(c.cases) == 1 && c.cases[0].value != nil {
|
if len(c.cases) == 1 && c.cases[0].value != nil {
|
||||||
|
if _, ok := c.cases[0].value.(*parser.UnsetProperty); ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
|
@ -774,7 +1057,7 @@ func (c *singleConfigurable[T]) alwaysHasValue() bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
for _, c := range c.cases {
|
for _, c := range c.cases {
|
||||||
if c.value == nil {
|
if _, isUnset := c.value.(*parser.UnsetProperty); isUnset || c.value == nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -785,17 +1068,84 @@ func (c Configurable[T]) configuredType() reflect.Type {
|
||||||
return reflect.TypeOf((*T)(nil)).Elem()
|
return reflect.TypeOf((*T)(nil)).Elem()
|
||||||
}
|
}
|
||||||
|
|
||||||
func copyConfiguredValuePtr[T ConfigurableElements](t *T) *T {
|
func expressionToConfiguredValue[T ConfigurableElements](expr parser.Expression, scope *parser.Scope) (*T, error) {
|
||||||
if t == nil {
|
expr, err := expr.Eval(scope)
|
||||||
return nil
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
switch t2 := any(*t).(type) {
|
switch e := expr.(type) {
|
||||||
case []string:
|
case *parser.UnsetProperty:
|
||||||
result := any(slices.Clone(t2)).(T)
|
return nil, nil
|
||||||
return &result
|
case *parser.String:
|
||||||
|
if result, ok := any(&e.Value).(*T); ok {
|
||||||
|
return result, nil
|
||||||
|
} else {
|
||||||
|
return nil, fmt.Errorf("can't assign string value to %s property", configuredTypeToString[T]())
|
||||||
|
}
|
||||||
|
case *parser.Bool:
|
||||||
|
if result, ok := any(&e.Value).(*T); ok {
|
||||||
|
return result, nil
|
||||||
|
} else {
|
||||||
|
return nil, fmt.Errorf("can't assign bool value to %s property", configuredTypeToString[T]())
|
||||||
|
}
|
||||||
|
case *parser.List:
|
||||||
|
result := make([]string, 0, len(e.Values))
|
||||||
|
for _, x := range e.Values {
|
||||||
|
if y, ok := x.(*parser.String); ok {
|
||||||
|
result = append(result, y.Value)
|
||||||
|
} else {
|
||||||
|
return nil, fmt.Errorf("expected list of strings but found list of %s", x.Type())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if result, ok := any(&result).(*T); ok {
|
||||||
|
return result, nil
|
||||||
|
} else {
|
||||||
|
return nil, fmt.Errorf("can't assign list of strings to list of %s property", configuredTypeToString[T]())
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
x := *t
|
// If the expression was not evaluated beforehand we could hit this error even when the types match,
|
||||||
return &x
|
// but that's an internal logic error.
|
||||||
|
return nil, fmt.Errorf("expected %s but found %s (%#v)", configuredTypeToString[T](), expr.Type().String(), expr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func configuredValueToExpression[T ConfigurableElements](value T) parser.Expression {
|
||||||
|
switch v := any(value).(type) {
|
||||||
|
case string:
|
||||||
|
return &parser.String{Value: v}
|
||||||
|
case bool:
|
||||||
|
return &parser.Bool{Value: v}
|
||||||
|
case []string:
|
||||||
|
values := make([]parser.Expression, 0, len(v))
|
||||||
|
for _, x := range v {
|
||||||
|
values = append(values, &parser.String{Value: x})
|
||||||
|
}
|
||||||
|
return &parser.List{Values: values}
|
||||||
|
default:
|
||||||
|
panic("unhandled type in configuredValueToExpression")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func configuredTypeToString[T ConfigurableElements]() string {
|
||||||
|
var zero T
|
||||||
|
switch any(zero).(type) {
|
||||||
|
case string:
|
||||||
|
return "string"
|
||||||
|
case bool:
|
||||||
|
return "bool"
|
||||||
|
case []string:
|
||||||
|
return "list of strings"
|
||||||
|
default:
|
||||||
|
panic("should be unreachable")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func copyConfiguredValue[T ConfigurableElements](t T) T {
|
||||||
|
switch t2 := any(t).(type) {
|
||||||
|
case []string:
|
||||||
|
return any(slices.Clone(t2)).(T)
|
||||||
|
default:
|
||||||
|
return t
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -812,18 +1162,75 @@ func configuredValuePtrToOptional[T ConfigurableElements](t *T) ConfigurableOpti
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func copyConfiguredValue[T ConfigurableElements](t T) T {
|
|
||||||
switch t2 := any(t).(type) {
|
|
||||||
case []string:
|
|
||||||
return any(slices.Clone(t2)).(T)
|
|
||||||
default:
|
|
||||||
return t
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// PrintfIntoConfigurable replaces %s occurrences in strings in Configurable properties
|
// PrintfIntoConfigurable replaces %s occurrences in strings in Configurable properties
|
||||||
// with the provided string value. It's intention is to support soong config value variables
|
// with the provided string value. It's intention is to support soong config value variables
|
||||||
// on Configurable properties.
|
// on Configurable properties.
|
||||||
func PrintfIntoConfigurable(c any, value string) error {
|
func PrintfIntoConfigurable(c any, value string) error {
|
||||||
return c.(configurableReflection).printfInto(value)
|
return c.(configurableReflection).printfInto(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func promoteValueToConfigurable(origional reflect.Value) reflect.Value {
|
||||||
|
var expr parser.Expression
|
||||||
|
var kind reflect.Kind
|
||||||
|
if origional.Kind() == reflect.Pointer && origional.IsNil() {
|
||||||
|
expr = &parser.UnsetProperty{}
|
||||||
|
kind = origional.Type().Elem().Kind()
|
||||||
|
} else {
|
||||||
|
if origional.Kind() == reflect.Pointer {
|
||||||
|
origional = origional.Elem()
|
||||||
|
}
|
||||||
|
kind = origional.Kind()
|
||||||
|
switch kind {
|
||||||
|
case reflect.String:
|
||||||
|
expr = &parser.String{Value: origional.String()}
|
||||||
|
case reflect.Bool:
|
||||||
|
expr = &parser.Bool{Value: origional.Bool()}
|
||||||
|
case reflect.Slice:
|
||||||
|
strList := origional.Interface().([]string)
|
||||||
|
exprList := make([]parser.Expression, 0, len(strList))
|
||||||
|
for _, x := range strList {
|
||||||
|
exprList = append(exprList, &parser.String{Value: x})
|
||||||
|
}
|
||||||
|
expr = &parser.List{Values: exprList}
|
||||||
|
default:
|
||||||
|
panic("can only convert string/bool/[]string to configurable")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
switch kind {
|
||||||
|
case reflect.String:
|
||||||
|
return reflect.ValueOf(Configurable[string]{
|
||||||
|
inner: &configurableInner[string]{
|
||||||
|
single: singleConfigurable[string]{
|
||||||
|
cases: []ConfigurableCase[string]{{
|
||||||
|
value: expr,
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
postProcessors: &[][]postProcessor[string]{},
|
||||||
|
})
|
||||||
|
case reflect.Bool:
|
||||||
|
return reflect.ValueOf(Configurable[bool]{
|
||||||
|
inner: &configurableInner[bool]{
|
||||||
|
single: singleConfigurable[bool]{
|
||||||
|
cases: []ConfigurableCase[bool]{{
|
||||||
|
value: expr,
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
postProcessors: &[][]postProcessor[bool]{},
|
||||||
|
})
|
||||||
|
case reflect.Slice:
|
||||||
|
return reflect.ValueOf(Configurable[[]string]{
|
||||||
|
inner: &configurableInner[[]string]{
|
||||||
|
single: singleConfigurable[[]string]{
|
||||||
|
cases: []ConfigurableCase[[]string]{{
|
||||||
|
value: expr,
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
postProcessors: &[][]postProcessor[[]string]{},
|
||||||
|
})
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("Can't convert %s property to a configurable", origional.Kind().String()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
95
proptools/configurable_test.go
Normal file
95
proptools/configurable_test.go
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
package proptools
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestPostProcessor(t *testing.T) {
|
||||||
|
// Same as the ascii art example in Configurable.evaluate()
|
||||||
|
prop := NewConfigurable[[]string](nil, nil)
|
||||||
|
prop.AppendSimpleValue([]string{"a"})
|
||||||
|
prop.AppendSimpleValue([]string{"b"})
|
||||||
|
prop.AddPostProcessor(addToElements("1"))
|
||||||
|
|
||||||
|
prop2 := NewConfigurable[[]string](nil, nil)
|
||||||
|
prop2.AppendSimpleValue([]string{"c"})
|
||||||
|
|
||||||
|
prop3 := NewConfigurable[[]string](nil, nil)
|
||||||
|
prop3.AppendSimpleValue([]string{"d"})
|
||||||
|
prop3.AppendSimpleValue([]string{"e"})
|
||||||
|
prop3.AddPostProcessor(addToElements("2"))
|
||||||
|
|
||||||
|
prop4 := NewConfigurable[[]string](nil, nil)
|
||||||
|
prop4.AppendSimpleValue([]string{"f"})
|
||||||
|
|
||||||
|
prop5 := NewConfigurable[[]string](nil, nil)
|
||||||
|
prop5.AppendSimpleValue([]string{"g"})
|
||||||
|
prop5.AddPostProcessor(addToElements("3"))
|
||||||
|
|
||||||
|
prop2.Append(prop3)
|
||||||
|
prop2.AddPostProcessor(addToElements("z"))
|
||||||
|
|
||||||
|
prop.Append(prop2)
|
||||||
|
prop.AddPostProcessor(addToElements("y"))
|
||||||
|
prop.Append(prop4)
|
||||||
|
prop.Append(prop5)
|
||||||
|
|
||||||
|
expected := []string{"a1y", "b1y", "czy", "d2zy", "e2zy", "f", "g3"}
|
||||||
|
x := prop.Get(&configurableEvalutorForTesting{})
|
||||||
|
if !reflect.DeepEqual(x.Get(), expected) {
|
||||||
|
t.Fatalf("Expected %v, got %v", expected, x.Get())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPostProcessorWhenPassedToHelperFunction(t *testing.T) {
|
||||||
|
prop := NewConfigurable[[]string](nil, nil)
|
||||||
|
prop.AppendSimpleValue([]string{"a"})
|
||||||
|
prop.AppendSimpleValue([]string{"b"})
|
||||||
|
|
||||||
|
helper := func(p Configurable[[]string]) {
|
||||||
|
p.AddPostProcessor(addToElements("1"))
|
||||||
|
}
|
||||||
|
|
||||||
|
helper(prop)
|
||||||
|
|
||||||
|
expected := []string{"a1", "b1"}
|
||||||
|
x := prop.Get(&configurableEvalutorForTesting{})
|
||||||
|
if !reflect.DeepEqual(x.Get(), expected) {
|
||||||
|
t.Fatalf("Expected %v, got %v", expected, x.Get())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func addToElements(s string) func([]string) []string {
|
||||||
|
return func(arr []string) []string {
|
||||||
|
for i := range arr {
|
||||||
|
arr[i] = arr[i] + s
|
||||||
|
}
|
||||||
|
return arr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type configurableEvalutorForTesting struct {
|
||||||
|
vars map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *configurableEvalutorForTesting) EvaluateConfiguration(condition ConfigurableCondition, property string) ConfigurableValue {
|
||||||
|
if condition.functionName != "f" {
|
||||||
|
panic("Expected functionName to be f")
|
||||||
|
}
|
||||||
|
if len(condition.args) != 1 {
|
||||||
|
panic("Expected exactly 1 arg")
|
||||||
|
}
|
||||||
|
val, ok := e.vars[condition.args[0]]
|
||||||
|
if ok {
|
||||||
|
return ConfigurableValueString(val)
|
||||||
|
}
|
||||||
|
return ConfigurableValueUndefined()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *configurableEvalutorForTesting) PropertyErrorf(property, fmtString string, args ...interface{}) {
|
||||||
|
panic(fmt.Sprintf(fmtString, args...))
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ ConfigurableEvaluator = (*configurableEvalutorForTesting)(nil)
|
|
@ -473,33 +473,7 @@ func ExtendBasicType(dstFieldValue, srcFieldValue reflect.Value, order Order) {
|
||||||
// structs when they want to change the default values of properties.
|
// structs when they want to change the default values of properties.
|
||||||
srcFieldType := srcFieldValue.Type()
|
srcFieldType := srcFieldValue.Type()
|
||||||
if isConfigurable(dstFieldValue.Type()) && !isConfigurable(srcFieldType) {
|
if isConfigurable(dstFieldValue.Type()) && !isConfigurable(srcFieldType) {
|
||||||
var value reflect.Value
|
srcFieldValue = promoteValueToConfigurable(srcFieldValue)
|
||||||
if srcFieldType.Kind() == reflect.Pointer {
|
|
||||||
srcFieldType = srcFieldType.Elem()
|
|
||||||
if srcFieldValue.IsNil() {
|
|
||||||
value = srcFieldValue
|
|
||||||
} else {
|
|
||||||
// Copy the pointer
|
|
||||||
value = reflect.New(srcFieldType)
|
|
||||||
value.Elem().Set(srcFieldValue.Elem())
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
value = reflect.New(srcFieldType)
|
|
||||||
value.Elem().Set(srcFieldValue)
|
|
||||||
}
|
|
||||||
caseType := configurableCaseType(srcFieldType)
|
|
||||||
case_ := reflect.New(caseType)
|
|
||||||
case_.Interface().(configurableCaseReflection).initialize(nil, value.Interface())
|
|
||||||
cases := reflect.MakeSlice(reflect.SliceOf(caseType), 0, 1)
|
|
||||||
cases = reflect.Append(cases, case_.Elem())
|
|
||||||
ct, err := configurableType(srcFieldType)
|
|
||||||
if err != nil {
|
|
||||||
// Should be unreachable due to earlier checks
|
|
||||||
panic(err.Error())
|
|
||||||
}
|
|
||||||
temp := reflect.New(ct)
|
|
||||||
temp.Interface().(configurablePtrReflection).initialize("", nil, cases.Interface())
|
|
||||||
srcFieldValue = temp.Elem()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
switch srcFieldValue.Kind() {
|
switch srcFieldValue.Kind() {
|
||||||
|
|
|
@ -20,6 +20,8 @@ import (
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/google/blueprint/parser"
|
||||||
)
|
)
|
||||||
|
|
||||||
type appendPropertyTestCase struct {
|
type appendPropertyTestCase struct {
|
||||||
|
@ -1257,172 +1259,166 @@ func appendPropertiesTestCases() []appendPropertyTestCase {
|
||||||
{
|
{
|
||||||
name: "Append configurable",
|
name: "Append configurable",
|
||||||
dst: &struct{ S Configurable[[]string] }{
|
dst: &struct{ S Configurable[[]string] }{
|
||||||
S: Configurable[[]string]{
|
S: NewConfigurable[[]string]([]ConfigurableCondition{{
|
||||||
inner: &configurableInner[[]string]{
|
functionName: "soong_config_variable",
|
||||||
single: singleConfigurable[[]string]{
|
args: []string{
|
||||||
conditions: []ConfigurableCondition{{
|
"my_namespace",
|
||||||
functionName: "soong_config_variable",
|
"foo",
|
||||||
args: []string{
|
|
||||||
"my_namespace",
|
|
||||||
"foo",
|
|
||||||
},
|
|
||||||
}},
|
|
||||||
cases: []ConfigurableCase[[]string]{{
|
|
||||||
patterns: []ConfigurablePattern{{
|
|
||||||
typ: configurablePatternTypeString,
|
|
||||||
stringValue: "a",
|
|
||||||
}},
|
|
||||||
value: &[]string{"1", "2"},
|
|
||||||
}},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
}},
|
||||||
|
[]ConfigurableCase[[]string]{{
|
||||||
|
patterns: []ConfigurablePattern{{
|
||||||
|
typ: configurablePatternTypeString,
|
||||||
|
stringValue: "a",
|
||||||
|
}},
|
||||||
|
value: &parser.List{Values: []parser.Expression{
|
||||||
|
&parser.String{Value: "1"},
|
||||||
|
&parser.String{Value: "2"},
|
||||||
|
}},
|
||||||
|
}},
|
||||||
|
),
|
||||||
},
|
},
|
||||||
src: &struct{ S Configurable[[]string] }{
|
src: &struct{ S Configurable[[]string] }{
|
||||||
S: Configurable[[]string]{
|
S: NewConfigurable([]ConfigurableCondition{{
|
||||||
inner: &configurableInner[[]string]{
|
functionName: "release_variable",
|
||||||
single: singleConfigurable[[]string]{
|
args: []string{
|
||||||
conditions: []ConfigurableCondition{{
|
"bar",
|
||||||
functionName: "release_variable",
|
|
||||||
args: []string{
|
|
||||||
"bar",
|
|
||||||
},
|
|
||||||
}},
|
|
||||||
cases: []ConfigurableCase[[]string]{{
|
|
||||||
patterns: []ConfigurablePattern{{
|
|
||||||
typ: configurablePatternTypeString,
|
|
||||||
stringValue: "b",
|
|
||||||
}},
|
|
||||||
value: &[]string{"3", "4"},
|
|
||||||
}},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
}},
|
||||||
|
[]ConfigurableCase[[]string]{{
|
||||||
|
patterns: []ConfigurablePattern{{
|
||||||
|
typ: configurablePatternTypeString,
|
||||||
|
stringValue: "b",
|
||||||
|
}},
|
||||||
|
value: &parser.List{Values: []parser.Expression{
|
||||||
|
&parser.String{Value: "3"},
|
||||||
|
&parser.String{Value: "4"},
|
||||||
|
}},
|
||||||
|
}},
|
||||||
|
),
|
||||||
},
|
},
|
||||||
out: &struct{ S Configurable[[]string] }{
|
out: &struct{ S Configurable[[]string] }{
|
||||||
S: Configurable[[]string]{
|
S: func() Configurable[[]string] {
|
||||||
inner: &configurableInner[[]string]{
|
result := NewConfigurable([]ConfigurableCondition{{
|
||||||
single: singleConfigurable[[]string]{
|
functionName: "soong_config_variable",
|
||||||
conditions: []ConfigurableCondition{{
|
args: []string{
|
||||||
functionName: "soong_config_variable",
|
"my_namespace",
|
||||||
args: []string{
|
"foo",
|
||||||
"my_namespace",
|
|
||||||
"foo",
|
|
||||||
},
|
|
||||||
}},
|
|
||||||
cases: []ConfigurableCase[[]string]{{
|
|
||||||
patterns: []ConfigurablePattern{{
|
|
||||||
typ: configurablePatternTypeString,
|
|
||||||
stringValue: "a",
|
|
||||||
}},
|
|
||||||
value: &[]string{"1", "2"},
|
|
||||||
}},
|
|
||||||
},
|
},
|
||||||
next: &configurableInner[[]string]{
|
}},
|
||||||
single: singleConfigurable[[]string]{
|
[]ConfigurableCase[[]string]{{
|
||||||
conditions: []ConfigurableCondition{{
|
patterns: []ConfigurablePattern{{
|
||||||
functionName: "release_variable",
|
typ: configurablePatternTypeString,
|
||||||
args: []string{
|
stringValue: "a",
|
||||||
"bar",
|
}},
|
||||||
},
|
value: &parser.List{Values: []parser.Expression{
|
||||||
}},
|
&parser.String{Value: "1"},
|
||||||
cases: []ConfigurableCase[[]string]{{
|
&parser.String{Value: "2"},
|
||||||
patterns: []ConfigurablePattern{{
|
}},
|
||||||
typ: configurablePatternTypeString,
|
}},
|
||||||
stringValue: "b",
|
)
|
||||||
}},
|
result.Append(NewConfigurable([]ConfigurableCondition{{
|
||||||
value: &[]string{"3", "4"},
|
functionName: "release_variable",
|
||||||
}},
|
args: []string{
|
||||||
},
|
"bar",
|
||||||
},
|
},
|
||||||
},
|
}},
|
||||||
},
|
[]ConfigurableCase[[]string]{{
|
||||||
|
patterns: []ConfigurablePattern{{
|
||||||
|
typ: configurablePatternTypeString,
|
||||||
|
stringValue: "b",
|
||||||
|
}},
|
||||||
|
value: &parser.List{Values: []parser.Expression{
|
||||||
|
&parser.String{Value: "3"},
|
||||||
|
&parser.String{Value: "4"},
|
||||||
|
}},
|
||||||
|
}}))
|
||||||
|
return result
|
||||||
|
}(),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Prepend configurable",
|
name: "Prepend configurable",
|
||||||
order: Prepend,
|
order: Prepend,
|
||||||
dst: &struct{ S Configurable[[]string] }{
|
dst: &struct{ S Configurable[[]string] }{
|
||||||
S: Configurable[[]string]{
|
S: NewConfigurable([]ConfigurableCondition{{
|
||||||
inner: &configurableInner[[]string]{
|
functionName: "soong_config_variable",
|
||||||
single: singleConfigurable[[]string]{
|
args: []string{
|
||||||
conditions: []ConfigurableCondition{{
|
"my_namespace",
|
||||||
functionName: "soong_config_variable",
|
"foo",
|
||||||
args: []string{
|
|
||||||
"my_namespace",
|
|
||||||
"foo",
|
|
||||||
},
|
|
||||||
}},
|
|
||||||
cases: []ConfigurableCase[[]string]{{
|
|
||||||
patterns: []ConfigurablePattern{{
|
|
||||||
typ: configurablePatternTypeString,
|
|
||||||
stringValue: "a",
|
|
||||||
}},
|
|
||||||
value: &[]string{"1", "2"},
|
|
||||||
}},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
}},
|
||||||
|
[]ConfigurableCase[[]string]{{
|
||||||
|
patterns: []ConfigurablePattern{{
|
||||||
|
typ: configurablePatternTypeString,
|
||||||
|
stringValue: "a",
|
||||||
|
}},
|
||||||
|
value: &parser.List{Values: []parser.Expression{
|
||||||
|
&parser.String{Value: "1"},
|
||||||
|
&parser.String{Value: "2"},
|
||||||
|
}},
|
||||||
|
}},
|
||||||
|
),
|
||||||
},
|
},
|
||||||
src: &struct{ S Configurable[[]string] }{
|
src: &struct{ S Configurable[[]string] }{
|
||||||
S: Configurable[[]string]{
|
S: NewConfigurable([]ConfigurableCondition{{
|
||||||
inner: &configurableInner[[]string]{
|
functionName: "release_variable",
|
||||||
single: singleConfigurable[[]string]{
|
args: []string{
|
||||||
conditions: []ConfigurableCondition{{
|
"bar",
|
||||||
functionName: "release_variable",
|
|
||||||
args: []string{
|
|
||||||
"bar",
|
|
||||||
},
|
|
||||||
}},
|
|
||||||
cases: []ConfigurableCase[[]string]{{
|
|
||||||
patterns: []ConfigurablePattern{{
|
|
||||||
typ: configurablePatternTypeString,
|
|
||||||
stringValue: "b",
|
|
||||||
}},
|
|
||||||
value: &[]string{"3", "4"},
|
|
||||||
}},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
}},
|
||||||
|
[]ConfigurableCase[[]string]{{
|
||||||
|
patterns: []ConfigurablePattern{{
|
||||||
|
typ: configurablePatternTypeString,
|
||||||
|
stringValue: "b",
|
||||||
|
}},
|
||||||
|
value: &parser.List{Values: []parser.Expression{
|
||||||
|
&parser.String{Value: "3"},
|
||||||
|
&parser.String{Value: "4"},
|
||||||
|
}},
|
||||||
|
}},
|
||||||
|
),
|
||||||
},
|
},
|
||||||
out: &struct{ S Configurable[[]string] }{
|
out: &struct{ S Configurable[[]string] }{
|
||||||
S: Configurable[[]string]{
|
S: func() Configurable[[]string] {
|
||||||
inner: &configurableInner[[]string]{
|
result := NewConfigurable(
|
||||||
single: singleConfigurable[[]string]{
|
[]ConfigurableCondition{{
|
||||||
conditions: []ConfigurableCondition{{
|
functionName: "release_variable",
|
||||||
functionName: "release_variable",
|
args: []string{
|
||||||
args: []string{
|
"bar",
|
||||||
"bar",
|
|
||||||
},
|
|
||||||
}},
|
|
||||||
cases: []ConfigurableCase[[]string]{{
|
|
||||||
patterns: []ConfigurablePattern{{
|
|
||||||
typ: configurablePatternTypeString,
|
|
||||||
stringValue: "b",
|
|
||||||
}},
|
|
||||||
value: &[]string{"3", "4"},
|
|
||||||
}},
|
|
||||||
},
|
|
||||||
next: &configurableInner[[]string]{
|
|
||||||
single: singleConfigurable[[]string]{
|
|
||||||
conditions: []ConfigurableCondition{{
|
|
||||||
functionName: "soong_config_variable",
|
|
||||||
args: []string{
|
|
||||||
"my_namespace",
|
|
||||||
"foo",
|
|
||||||
},
|
|
||||||
}},
|
|
||||||
cases: []ConfigurableCase[[]string]{{
|
|
||||||
patterns: []ConfigurablePattern{{
|
|
||||||
typ: configurablePatternTypeString,
|
|
||||||
stringValue: "a",
|
|
||||||
}},
|
|
||||||
value: &[]string{"1", "2"},
|
|
||||||
}},
|
|
||||||
},
|
},
|
||||||
},
|
}},
|
||||||
},
|
[]ConfigurableCase[[]string]{{
|
||||||
},
|
patterns: []ConfigurablePattern{{
|
||||||
|
typ: configurablePatternTypeString,
|
||||||
|
stringValue: "b",
|
||||||
|
}},
|
||||||
|
value: &parser.List{Values: []parser.Expression{
|
||||||
|
&parser.String{Value: "3"},
|
||||||
|
&parser.String{Value: "4"},
|
||||||
|
}},
|
||||||
|
}},
|
||||||
|
)
|
||||||
|
result.Append(NewConfigurable(
|
||||||
|
[]ConfigurableCondition{{
|
||||||
|
functionName: "soong_config_variable",
|
||||||
|
args: []string{
|
||||||
|
"my_namespace",
|
||||||
|
"foo",
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
[]ConfigurableCase[[]string]{{
|
||||||
|
patterns: []ConfigurablePattern{{
|
||||||
|
typ: configurablePatternTypeString,
|
||||||
|
stringValue: "a",
|
||||||
|
}},
|
||||||
|
value: &parser.List{Values: []parser.Expression{
|
||||||
|
&parser.String{Value: "1"},
|
||||||
|
&parser.String{Value: "2"},
|
||||||
|
}},
|
||||||
|
}}))
|
||||||
|
return result
|
||||||
|
}(),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -1892,31 +1888,24 @@ func appendMatchingPropertiesTestCases() []appendMatchingPropertiesTestCase {
|
||||||
order: Append,
|
order: Append,
|
||||||
dst: []interface{}{
|
dst: []interface{}{
|
||||||
&struct{ S Configurable[bool] }{
|
&struct{ S Configurable[bool] }{
|
||||||
S: Configurable[bool]{
|
S: NewConfigurable[bool]([]ConfigurableCondition{{
|
||||||
inner: &configurableInner[bool]{
|
functionName: "soong_config_variable",
|
||||||
single: singleConfigurable[bool]{
|
args: []string{
|
||||||
conditions: []ConfigurableCondition{{
|
"my_namespace",
|
||||||
functionName: "soong_config_variable",
|
"foo",
|
||||||
args: []string{
|
|
||||||
"my_namespace",
|
|
||||||
"foo",
|
|
||||||
},
|
|
||||||
}},
|
|
||||||
cases: []ConfigurableCase[bool]{{
|
|
||||||
patterns: []ConfigurablePattern{{
|
|
||||||
typ: configurablePatternTypeString,
|
|
||||||
stringValue: "a",
|
|
||||||
}},
|
|
||||||
value: BoolPtr(true),
|
|
||||||
}, {
|
|
||||||
patterns: []ConfigurablePattern{{
|
|
||||||
typ: configurablePatternTypeDefault,
|
|
||||||
}},
|
|
||||||
value: BoolPtr(false),
|
|
||||||
}},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
}}, []ConfigurableCase[bool]{{
|
||||||
|
patterns: []ConfigurablePattern{{
|
||||||
|
typ: configurablePatternTypeString,
|
||||||
|
stringValue: "a",
|
||||||
|
}},
|
||||||
|
value: &parser.Bool{Value: true},
|
||||||
|
}, {
|
||||||
|
patterns: []ConfigurablePattern{{
|
||||||
|
typ: configurablePatternTypeDefault,
|
||||||
|
}},
|
||||||
|
value: &parser.Bool{Value: false},
|
||||||
|
}}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
src: &struct{ S *bool }{
|
src: &struct{ S *bool }{
|
||||||
|
@ -1924,38 +1913,30 @@ func appendMatchingPropertiesTestCases() []appendMatchingPropertiesTestCase {
|
||||||
},
|
},
|
||||||
out: []interface{}{
|
out: []interface{}{
|
||||||
&struct{ S Configurable[bool] }{
|
&struct{ S Configurable[bool] }{
|
||||||
S: Configurable[bool]{
|
S: func() Configurable[bool] {
|
||||||
inner: &configurableInner[bool]{
|
result := NewConfigurable[bool]([]ConfigurableCondition{{
|
||||||
single: singleConfigurable[bool]{
|
functionName: "soong_config_variable",
|
||||||
conditions: []ConfigurableCondition{{
|
args: []string{
|
||||||
functionName: "soong_config_variable",
|
"my_namespace",
|
||||||
args: []string{
|
"foo",
|
||||||
"my_namespace",
|
|
||||||
"foo",
|
|
||||||
},
|
|
||||||
}},
|
|
||||||
cases: []ConfigurableCase[bool]{{
|
|
||||||
patterns: []ConfigurablePattern{{
|
|
||||||
typ: configurablePatternTypeString,
|
|
||||||
stringValue: "a",
|
|
||||||
}},
|
|
||||||
value: BoolPtr(true),
|
|
||||||
}, {
|
|
||||||
patterns: []ConfigurablePattern{{
|
|
||||||
typ: configurablePatternTypeDefault,
|
|
||||||
}},
|
|
||||||
value: BoolPtr(false),
|
|
||||||
}},
|
|
||||||
},
|
},
|
||||||
next: &configurableInner[bool]{
|
}},
|
||||||
single: singleConfigurable[bool]{
|
[]ConfigurableCase[bool]{{
|
||||||
cases: []ConfigurableCase[bool]{{
|
patterns: []ConfigurablePattern{{
|
||||||
value: BoolPtr(true),
|
typ: configurablePatternTypeString,
|
||||||
}},
|
stringValue: "a",
|
||||||
},
|
}},
|
||||||
},
|
value: &parser.Bool{Value: true},
|
||||||
},
|
}, {
|
||||||
},
|
patterns: []ConfigurablePattern{{
|
||||||
|
typ: configurablePatternTypeDefault,
|
||||||
|
}},
|
||||||
|
value: &parser.Bool{Value: false},
|
||||||
|
}},
|
||||||
|
)
|
||||||
|
result.AppendSimpleValue(true)
|
||||||
|
return result
|
||||||
|
}(),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -1964,31 +1945,26 @@ func appendMatchingPropertiesTestCases() []appendMatchingPropertiesTestCase {
|
||||||
order: Append,
|
order: Append,
|
||||||
dst: []interface{}{
|
dst: []interface{}{
|
||||||
&struct{ S Configurable[bool] }{
|
&struct{ S Configurable[bool] }{
|
||||||
S: Configurable[bool]{
|
S: NewConfigurable[bool]([]ConfigurableCondition{{
|
||||||
inner: &configurableInner[bool]{
|
functionName: "soong_config_variable",
|
||||||
single: singleConfigurable[bool]{
|
args: []string{
|
||||||
conditions: []ConfigurableCondition{{
|
"my_namespace",
|
||||||
functionName: "soong_config_variable",
|
"foo",
|
||||||
args: []string{
|
|
||||||
"my_namespace",
|
|
||||||
"foo",
|
|
||||||
},
|
|
||||||
}},
|
|
||||||
cases: []ConfigurableCase[bool]{{
|
|
||||||
patterns: []ConfigurablePattern{{
|
|
||||||
typ: configurablePatternTypeString,
|
|
||||||
stringValue: "a",
|
|
||||||
}},
|
|
||||||
value: BoolPtr(true),
|
|
||||||
}, {
|
|
||||||
patterns: []ConfigurablePattern{{
|
|
||||||
typ: configurablePatternTypeDefault,
|
|
||||||
}},
|
|
||||||
value: BoolPtr(false),
|
|
||||||
}},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
}},
|
||||||
|
[]ConfigurableCase[bool]{{
|
||||||
|
patterns: []ConfigurablePattern{{
|
||||||
|
typ: configurablePatternTypeString,
|
||||||
|
stringValue: "a",
|
||||||
|
}},
|
||||||
|
value: &parser.Bool{Value: true},
|
||||||
|
}, {
|
||||||
|
patterns: []ConfigurablePattern{{
|
||||||
|
typ: configurablePatternTypeDefault,
|
||||||
|
}},
|
||||||
|
value: &parser.Bool{Value: false},
|
||||||
|
}},
|
||||||
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
src: &struct{ S bool }{
|
src: &struct{ S bool }{
|
||||||
|
@ -1996,38 +1972,31 @@ func appendMatchingPropertiesTestCases() []appendMatchingPropertiesTestCase {
|
||||||
},
|
},
|
||||||
out: []interface{}{
|
out: []interface{}{
|
||||||
&struct{ S Configurable[bool] }{
|
&struct{ S Configurable[bool] }{
|
||||||
S: Configurable[bool]{
|
S: func() Configurable[bool] {
|
||||||
inner: &configurableInner[bool]{
|
result := NewConfigurable[bool](
|
||||||
single: singleConfigurable[bool]{
|
[]ConfigurableCondition{{
|
||||||
conditions: []ConfigurableCondition{{
|
functionName: "soong_config_variable",
|
||||||
functionName: "soong_config_variable",
|
args: []string{
|
||||||
args: []string{
|
"my_namespace",
|
||||||
"my_namespace",
|
"foo",
|
||||||
"foo",
|
|
||||||
},
|
|
||||||
}},
|
|
||||||
cases: []ConfigurableCase[bool]{{
|
|
||||||
patterns: []ConfigurablePattern{{
|
|
||||||
typ: configurablePatternTypeString,
|
|
||||||
stringValue: "a",
|
|
||||||
}},
|
|
||||||
value: BoolPtr(true),
|
|
||||||
}, {
|
|
||||||
patterns: []ConfigurablePattern{{
|
|
||||||
typ: configurablePatternTypeDefault,
|
|
||||||
}},
|
|
||||||
value: BoolPtr(false),
|
|
||||||
}},
|
|
||||||
},
|
|
||||||
next: &configurableInner[bool]{
|
|
||||||
single: singleConfigurable[bool]{
|
|
||||||
cases: []ConfigurableCase[bool]{{
|
|
||||||
value: BoolPtr(true),
|
|
||||||
}},
|
|
||||||
},
|
},
|
||||||
},
|
}},
|
||||||
},
|
[]ConfigurableCase[bool]{{
|
||||||
},
|
patterns: []ConfigurablePattern{{
|
||||||
|
typ: configurablePatternTypeString,
|
||||||
|
stringValue: "a",
|
||||||
|
}},
|
||||||
|
value: &parser.Bool{Value: true},
|
||||||
|
}, {
|
||||||
|
patterns: []ConfigurablePattern{{
|
||||||
|
typ: configurablePatternTypeDefault,
|
||||||
|
}},
|
||||||
|
value: &parser.Bool{Value: false},
|
||||||
|
}},
|
||||||
|
)
|
||||||
|
result.AppendSimpleValue(true)
|
||||||
|
return result
|
||||||
|
}(),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -158,7 +158,7 @@ func (ctx *unpackContext) buildPropertyMap(prefix string, properties []*parser.P
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.propertyMap[name] = &packedProperty{property, false}
|
ctx.propertyMap[name] = &packedProperty{property, false}
|
||||||
switch propValue := property.Value.Eval().(type) {
|
switch propValue := property.Value.(type) {
|
||||||
case *parser.Map:
|
case *parser.Map:
|
||||||
ctx.buildPropertyMap(name, propValue.Properties)
|
ctx.buildPropertyMap(name, propValue.Properties)
|
||||||
case *parser.List:
|
case *parser.List:
|
||||||
|
@ -313,7 +313,7 @@ func (ctx *unpackContext) unpackToStruct(namePrefix string, structValue reflect.
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
} else if isStruct(fieldValue.Type()) {
|
} else if isStruct(fieldValue.Type()) {
|
||||||
if property.Value.Eval().Type() != parser.MapType {
|
if property.Value.Type() != parser.MapType {
|
||||||
ctx.addError(&UnpackError{
|
ctx.addError(&UnpackError{
|
||||||
fmt.Errorf("can't assign %s value to map property %q",
|
fmt.Errorf("can't assign %s value to map property %q",
|
||||||
property.Value.Type(), property.Name),
|
property.Value.Type(), property.Name),
|
||||||
|
@ -354,15 +354,17 @@ func (ctx *unpackContext) unpackToConfigurable(propertyName string, property *pa
|
||||||
})
|
})
|
||||||
return reflect.New(configurableType), false
|
return reflect.New(configurableType), false
|
||||||
}
|
}
|
||||||
|
var postProcessors [][]postProcessor[string]
|
||||||
result := Configurable[string]{
|
result := Configurable[string]{
|
||||||
propertyName: property.Name,
|
propertyName: property.Name,
|
||||||
inner: &configurableInner[string]{
|
inner: &configurableInner[string]{
|
||||||
single: singleConfigurable[string]{
|
single: singleConfigurable[string]{
|
||||||
cases: []ConfigurableCase[string]{{
|
cases: []ConfigurableCase[string]{{
|
||||||
value: &v.Value,
|
value: v,
|
||||||
}},
|
}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
postProcessors: &postProcessors,
|
||||||
}
|
}
|
||||||
return reflect.ValueOf(&result), true
|
return reflect.ValueOf(&result), true
|
||||||
case *parser.Bool:
|
case *parser.Bool:
|
||||||
|
@ -374,15 +376,17 @@ func (ctx *unpackContext) unpackToConfigurable(propertyName string, property *pa
|
||||||
})
|
})
|
||||||
return reflect.New(configurableType), false
|
return reflect.New(configurableType), false
|
||||||
}
|
}
|
||||||
|
var postProcessors [][]postProcessor[bool]
|
||||||
result := Configurable[bool]{
|
result := Configurable[bool]{
|
||||||
propertyName: property.Name,
|
propertyName: property.Name,
|
||||||
inner: &configurableInner[bool]{
|
inner: &configurableInner[bool]{
|
||||||
single: singleConfigurable[bool]{
|
single: singleConfigurable[bool]{
|
||||||
cases: []ConfigurableCase[bool]{{
|
cases: []ConfigurableCase[bool]{{
|
||||||
value: &v.Value,
|
value: v,
|
||||||
}},
|
}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
postProcessors: &postProcessors,
|
||||||
}
|
}
|
||||||
return reflect.ValueOf(&result), true
|
return reflect.ValueOf(&result), true
|
||||||
case *parser.List:
|
case *parser.List:
|
||||||
|
@ -411,26 +415,22 @@ func (ctx *unpackContext) unpackToConfigurable(propertyName string, property *pa
|
||||||
value[i] = exprUnpacked.Interface().(string)
|
value[i] = exprUnpacked.Interface().(string)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
var postProcessors [][]postProcessor[[]string]
|
||||||
result := Configurable[[]string]{
|
result := Configurable[[]string]{
|
||||||
propertyName: property.Name,
|
propertyName: property.Name,
|
||||||
inner: &configurableInner[[]string]{
|
inner: &configurableInner[[]string]{
|
||||||
single: singleConfigurable[[]string]{
|
single: singleConfigurable[[]string]{
|
||||||
cases: []ConfigurableCase[[]string]{{
|
cases: []ConfigurableCase[[]string]{{
|
||||||
value: &value,
|
value: v,
|
||||||
}},
|
}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
postProcessors: &postProcessors,
|
||||||
}
|
}
|
||||||
return reflect.ValueOf(&result), true
|
return reflect.ValueOf(&result), true
|
||||||
default:
|
default:
|
||||||
panic("This should be unreachable because ConfigurableElements only accepts slices of strings")
|
panic("This should be unreachable because ConfigurableElements only accepts slices of strings")
|
||||||
}
|
}
|
||||||
case *parser.Operator:
|
|
||||||
property.Value = v.Value.Eval()
|
|
||||||
return ctx.unpackToConfigurable(propertyName, property, configurableType, configuredType)
|
|
||||||
case *parser.Variable:
|
|
||||||
property.Value = v.Value.Eval()
|
|
||||||
return ctx.unpackToConfigurable(propertyName, property, configurableType, configuredType)
|
|
||||||
case *parser.Select:
|
case *parser.Select:
|
||||||
resultPtr := reflect.New(configurableType)
|
resultPtr := reflect.New(configurableType)
|
||||||
result := resultPtr.Elem()
|
result := resultPtr.Elem()
|
||||||
|
@ -448,19 +448,15 @@ func (ctx *unpackContext) unpackToConfigurable(propertyName string, property *pa
|
||||||
|
|
||||||
configurableCaseType := configurableCaseType(configuredType)
|
configurableCaseType := configurableCaseType(configuredType)
|
||||||
cases := reflect.MakeSlice(reflect.SliceOf(configurableCaseType), 0, len(v.Cases))
|
cases := reflect.MakeSlice(reflect.SliceOf(configurableCaseType), 0, len(v.Cases))
|
||||||
for i, c := range v.Cases {
|
for _, c := range v.Cases {
|
||||||
p := &parser.Property{
|
|
||||||
Name: property.Name + "[" + strconv.Itoa(i) + "]",
|
|
||||||
NamePos: c.ColonPos,
|
|
||||||
Value: c.Value,
|
|
||||||
}
|
|
||||||
|
|
||||||
patterns := make([]ConfigurablePattern, len(c.Patterns))
|
patterns := make([]ConfigurablePattern, len(c.Patterns))
|
||||||
for i, pat := range c.Patterns {
|
for i, pat := range c.Patterns {
|
||||||
switch pat := pat.(type) {
|
switch pat := pat.Value.(type) {
|
||||||
case *parser.String:
|
case *parser.String:
|
||||||
if pat.Value == "__soong_conditions_default__" {
|
if pat.Value == "__soong_conditions_default__" {
|
||||||
patterns[i].typ = configurablePatternTypeDefault
|
patterns[i].typ = configurablePatternTypeDefault
|
||||||
|
} else if pat.Value == "__soong_conditions_any__" {
|
||||||
|
patterns[i].typ = configurablePatternTypeAny
|
||||||
} else {
|
} else {
|
||||||
patterns[i].typ = configurablePatternTypeString
|
patterns[i].typ = configurablePatternTypeString
|
||||||
patterns[i].stringValue = pat.Value
|
patterns[i].stringValue = pat.Value
|
||||||
|
@ -471,42 +467,15 @@ func (ctx *unpackContext) unpackToConfigurable(propertyName string, property *pa
|
||||||
default:
|
default:
|
||||||
panic("unimplemented")
|
panic("unimplemented")
|
||||||
}
|
}
|
||||||
}
|
patterns[i].binding = pat.Binding.Name
|
||||||
|
|
||||||
var value reflect.Value
|
|
||||||
// Map the "unset" keyword to a nil pointer in the cases map
|
|
||||||
if _, ok := c.Value.(parser.UnsetProperty); ok {
|
|
||||||
value = reflect.Zero(reflect.PointerTo(configuredType))
|
|
||||||
} else {
|
|
||||||
var err error
|
|
||||||
switch configuredType.Kind() {
|
|
||||||
case reflect.String, reflect.Bool:
|
|
||||||
value, err = propertyToValue(reflect.PointerTo(configuredType), p)
|
|
||||||
if err != nil {
|
|
||||||
ctx.addError(&UnpackError{
|
|
||||||
err,
|
|
||||||
c.Value.Pos(),
|
|
||||||
})
|
|
||||||
return reflect.New(configurableType), false
|
|
||||||
}
|
|
||||||
case reflect.Slice:
|
|
||||||
if configuredType.Elem().Kind() != reflect.String {
|
|
||||||
panic("This should be unreachable because ConfigurableElements only accepts slices of strings")
|
|
||||||
}
|
|
||||||
value, ok = ctx.unpackToSlice(p.Name, p, reflect.PointerTo(configuredType))
|
|
||||||
if !ok {
|
|
||||||
return reflect.New(configurableType), false
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
panic("This should be unreachable because ConfigurableElements only accepts strings, boools, or slices of strings")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case_ := reflect.New(configurableCaseType)
|
case_ := reflect.New(configurableCaseType)
|
||||||
case_.Interface().(configurableCaseReflection).initialize(patterns, value.Interface())
|
case_.Interface().(configurableCaseReflection).initialize(patterns, c.Value)
|
||||||
cases = reflect.Append(cases, case_.Elem())
|
cases = reflect.Append(cases, case_.Elem())
|
||||||
}
|
}
|
||||||
resultPtr.Interface().(configurablePtrReflection).initialize(
|
resultPtr.Interface().(configurablePtrReflection).initialize(
|
||||||
|
v.Scope,
|
||||||
property.Name,
|
property.Name,
|
||||||
conditions,
|
conditions,
|
||||||
cases.Interface(),
|
cases.Interface(),
|
||||||
|
@ -537,7 +506,7 @@ func (ctx *unpackContext) unpackToConfigurable(propertyName string, property *pa
|
||||||
// If the given property is a select, returns an error saying that you can't assign a select to
|
// If the given property is a select, returns an error saying that you can't assign a select to
|
||||||
// a non-configurable property. Otherwise returns nil.
|
// a non-configurable property. Otherwise returns nil.
|
||||||
func selectOnNonConfigurablePropertyError(property *parser.Property) error {
|
func selectOnNonConfigurablePropertyError(property *parser.Property) error {
|
||||||
if _, ok := property.Value.Eval().(*parser.Select); !ok {
|
if _, ok := property.Value.(*parser.Select); !ok {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -570,7 +539,7 @@ func (ctx *unpackContext) unpackToSlice(
|
||||||
// does.
|
// does.
|
||||||
func (ctx *unpackContext) unpackToSliceInner(
|
func (ctx *unpackContext) unpackToSliceInner(
|
||||||
sliceName string, property *parser.Property, sliceType reflect.Type) (reflect.Value, bool) {
|
sliceName string, property *parser.Property, sliceType reflect.Type) (reflect.Value, bool) {
|
||||||
propValueAsList, ok := property.Value.Eval().(*parser.List)
|
propValueAsList, ok := property.Value.(*parser.List)
|
||||||
if !ok {
|
if !ok {
|
||||||
if err := selectOnNonConfigurablePropertyError(property); err != nil {
|
if err := selectOnNonConfigurablePropertyError(property); err != nil {
|
||||||
ctx.addError(err)
|
ctx.addError(err)
|
||||||
|
@ -590,33 +559,24 @@ func (ctx *unpackContext) unpackToSliceInner(
|
||||||
}
|
}
|
||||||
|
|
||||||
// The function to construct an item value depends on the type of list elements.
|
// The function to construct an item value depends on the type of list elements.
|
||||||
var getItemFunc func(*parser.Property, reflect.Type) (reflect.Value, bool)
|
getItemFunc := func(property *parser.Property, t reflect.Type) (reflect.Value, bool) {
|
||||||
switch exprs[0].Type() {
|
switch property.Value.(type) {
|
||||||
case parser.BoolType, parser.StringType, parser.Int64Type:
|
case *parser.Bool, *parser.String, *parser.Int64:
|
||||||
getItemFunc = func(property *parser.Property, t reflect.Type) (reflect.Value, bool) {
|
|
||||||
value, err := propertyToValue(t, property)
|
value, err := propertyToValue(t, property)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.addError(err)
|
ctx.addError(err)
|
||||||
return value, false
|
return value, false
|
||||||
}
|
}
|
||||||
return value, true
|
return value, true
|
||||||
}
|
case *parser.List:
|
||||||
case parser.ListType:
|
|
||||||
getItemFunc = func(property *parser.Property, t reflect.Type) (reflect.Value, bool) {
|
|
||||||
return ctx.unpackToSlice(property.Name, property, t)
|
return ctx.unpackToSlice(property.Name, property, t)
|
||||||
}
|
case *parser.Map:
|
||||||
case parser.MapType:
|
|
||||||
getItemFunc = func(property *parser.Property, t reflect.Type) (reflect.Value, bool) {
|
|
||||||
itemValue := reflect.New(t).Elem()
|
itemValue := reflect.New(t).Elem()
|
||||||
ctx.unpackToStruct(property.Name, itemValue)
|
ctx.unpackToStruct(property.Name, itemValue)
|
||||||
return itemValue, true
|
return itemValue, true
|
||||||
|
default:
|
||||||
|
panic(fmt.Errorf("bizarre property expression type: %v, %#v", property.Value.Type(), property.Value))
|
||||||
}
|
}
|
||||||
case parser.NotEvaluatedType:
|
|
||||||
getItemFunc = func(property *parser.Property, t reflect.Type) (reflect.Value, bool) {
|
|
||||||
return reflect.New(t), false
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
panic(fmt.Errorf("bizarre property expression type: %v", exprs[0].Type()))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
itemProperty := &parser.Property{NamePos: property.NamePos, ColonPos: property.ColonPos}
|
itemProperty := &parser.Property{NamePos: property.NamePos, ColonPos: property.ColonPos}
|
||||||
|
@ -657,7 +617,7 @@ func propertyToValue(typ reflect.Type, property *parser.Property) (reflect.Value
|
||||||
|
|
||||||
switch kind := baseType.Kind(); kind {
|
switch kind := baseType.Kind(); kind {
|
||||||
case reflect.Bool:
|
case reflect.Bool:
|
||||||
b, ok := property.Value.Eval().(*parser.Bool)
|
b, ok := property.Value.(*parser.Bool)
|
||||||
if !ok {
|
if !ok {
|
||||||
if err := selectOnNonConfigurablePropertyError(property); err != nil {
|
if err := selectOnNonConfigurablePropertyError(property); err != nil {
|
||||||
return value, err
|
return value, err
|
||||||
|
@ -672,7 +632,7 @@ func propertyToValue(typ reflect.Type, property *parser.Property) (reflect.Value
|
||||||
value = reflect.ValueOf(b.Value)
|
value = reflect.ValueOf(b.Value)
|
||||||
|
|
||||||
case reflect.Int64:
|
case reflect.Int64:
|
||||||
b, ok := property.Value.Eval().(*parser.Int64)
|
b, ok := property.Value.(*parser.Int64)
|
||||||
if !ok {
|
if !ok {
|
||||||
return value, &UnpackError{
|
return value, &UnpackError{
|
||||||
fmt.Errorf("can't assign %s value to int64 property %q",
|
fmt.Errorf("can't assign %s value to int64 property %q",
|
||||||
|
@ -683,7 +643,7 @@ func propertyToValue(typ reflect.Type, property *parser.Property) (reflect.Value
|
||||||
value = reflect.ValueOf(b.Value)
|
value = reflect.ValueOf(b.Value)
|
||||||
|
|
||||||
case reflect.String:
|
case reflect.String:
|
||||||
s, ok := property.Value.Eval().(*parser.String)
|
s, ok := property.Value.(*parser.String)
|
||||||
if !ok {
|
if !ok {
|
||||||
if err := selectOnNonConfigurablePropertyError(property); err != nil {
|
if err := selectOnNonConfigurablePropertyError(property); err != nil {
|
||||||
return value, err
|
return value, err
|
||||||
|
|
|
@ -18,6 +18,7 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
"text/scanner"
|
||||||
|
|
||||||
"github.com/google/blueprint/parser"
|
"github.com/google/blueprint/parser"
|
||||||
)
|
)
|
||||||
|
@ -732,16 +733,21 @@ var validUnpackTestCases = []struct {
|
||||||
&struct {
|
&struct {
|
||||||
Foo Configurable[string]
|
Foo Configurable[string]
|
||||||
}{
|
}{
|
||||||
Foo: Configurable[string]{
|
Foo: newConfigurableWithPropertyName(
|
||||||
propertyName: "foo",
|
"foo",
|
||||||
inner: &configurableInner[string]{
|
nil,
|
||||||
single: singleConfigurable[string]{
|
[]ConfigurableCase[string]{{
|
||||||
cases: []ConfigurableCase[string]{{
|
value: &parser.String{
|
||||||
value: StringPtr("bar"),
|
LiteralPos: scanner.Position{
|
||||||
}},
|
Offset: 17,
|
||||||
|
Line: 3,
|
||||||
|
Column: 10,
|
||||||
|
},
|
||||||
|
Value: "bar",
|
||||||
},
|
},
|
||||||
},
|
}},
|
||||||
},
|
false,
|
||||||
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -756,16 +762,22 @@ var validUnpackTestCases = []struct {
|
||||||
&struct {
|
&struct {
|
||||||
Foo Configurable[bool]
|
Foo Configurable[bool]
|
||||||
}{
|
}{
|
||||||
Foo: Configurable[bool]{
|
Foo: newConfigurableWithPropertyName(
|
||||||
propertyName: "foo",
|
"foo",
|
||||||
inner: &configurableInner[bool]{
|
nil,
|
||||||
single: singleConfigurable[bool]{
|
[]ConfigurableCase[bool]{{
|
||||||
cases: []ConfigurableCase[bool]{{
|
value: &parser.Bool{
|
||||||
value: BoolPtr(true),
|
LiteralPos: scanner.Position{
|
||||||
}},
|
Offset: 17,
|
||||||
|
Line: 3,
|
||||||
|
Column: 10,
|
||||||
|
},
|
||||||
|
Value: true,
|
||||||
|
Token: "true",
|
||||||
},
|
},
|
||||||
},
|
}},
|
||||||
},
|
false,
|
||||||
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -780,16 +792,43 @@ var validUnpackTestCases = []struct {
|
||||||
&struct {
|
&struct {
|
||||||
Foo Configurable[[]string]
|
Foo Configurable[[]string]
|
||||||
}{
|
}{
|
||||||
Foo: Configurable[[]string]{
|
Foo: newConfigurableWithPropertyName(
|
||||||
propertyName: "foo",
|
"foo",
|
||||||
inner: &configurableInner[[]string]{
|
nil,
|
||||||
single: singleConfigurable[[]string]{
|
[]ConfigurableCase[[]string]{{
|
||||||
cases: []ConfigurableCase[[]string]{{
|
value: &parser.List{
|
||||||
value: &[]string{"a", "b"},
|
LBracePos: scanner.Position{
|
||||||
}},
|
Offset: 17,
|
||||||
|
Line: 3,
|
||||||
|
Column: 10,
|
||||||
|
},
|
||||||
|
RBracePos: scanner.Position{
|
||||||
|
Offset: 26,
|
||||||
|
Line: 3,
|
||||||
|
Column: 19,
|
||||||
|
},
|
||||||
|
Values: []parser.Expression{
|
||||||
|
&parser.String{
|
||||||
|
LiteralPos: scanner.Position{
|
||||||
|
Offset: 18,
|
||||||
|
Line: 3,
|
||||||
|
Column: 11,
|
||||||
|
},
|
||||||
|
Value: "a",
|
||||||
|
},
|
||||||
|
&parser.String{
|
||||||
|
LiteralPos: scanner.Position{
|
||||||
|
Offset: 23,
|
||||||
|
Line: 3,
|
||||||
|
Column: 16,
|
||||||
|
},
|
||||||
|
Value: "b",
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
}},
|
||||||
},
|
false,
|
||||||
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -808,42 +847,60 @@ var validUnpackTestCases = []struct {
|
||||||
&struct {
|
&struct {
|
||||||
Foo Configurable[string]
|
Foo Configurable[string]
|
||||||
}{
|
}{
|
||||||
Foo: Configurable[string]{
|
Foo: newConfigurableWithPropertyName(
|
||||||
propertyName: "foo",
|
"foo",
|
||||||
inner: &configurableInner[string]{
|
[]ConfigurableCondition{{
|
||||||
single: singleConfigurable[string]{
|
functionName: "soong_config_variable",
|
||||||
conditions: []ConfigurableCondition{{
|
args: []string{
|
||||||
functionName: "soong_config_variable",
|
"my_namespace",
|
||||||
args: []string{
|
"my_variable",
|
||||||
"my_namespace",
|
},
|
||||||
"my_variable",
|
}},
|
||||||
},
|
[]ConfigurableCase[string]{
|
||||||
|
{
|
||||||
|
patterns: []ConfigurablePattern{{
|
||||||
|
typ: configurablePatternTypeString,
|
||||||
|
stringValue: "a",
|
||||||
}},
|
}},
|
||||||
cases: []ConfigurableCase[string]{
|
value: &parser.String{
|
||||||
{
|
LiteralPos: scanner.Position{
|
||||||
patterns: []ConfigurablePattern{{
|
Offset: 90,
|
||||||
typ: configurablePatternTypeString,
|
Line: 4,
|
||||||
stringValue: "a",
|
Column: 11,
|
||||||
}},
|
|
||||||
value: StringPtr("a2"),
|
|
||||||
},
|
},
|
||||||
{
|
Value: "a2",
|
||||||
patterns: []ConfigurablePattern{{
|
},
|
||||||
typ: configurablePatternTypeString,
|
},
|
||||||
stringValue: "b",
|
{
|
||||||
}},
|
patterns: []ConfigurablePattern{{
|
||||||
value: StringPtr("b2"),
|
typ: configurablePatternTypeString,
|
||||||
|
stringValue: "b",
|
||||||
|
}},
|
||||||
|
value: &parser.String{
|
||||||
|
LiteralPos: scanner.Position{
|
||||||
|
Offset: 106,
|
||||||
|
Line: 5,
|
||||||
|
Column: 11,
|
||||||
},
|
},
|
||||||
{
|
Value: "b2",
|
||||||
patterns: []ConfigurablePattern{{
|
},
|
||||||
typ: configurablePatternTypeDefault,
|
},
|
||||||
}},
|
{
|
||||||
value: StringPtr("c2"),
|
patterns: []ConfigurablePattern{{
|
||||||
|
typ: configurablePatternTypeDefault,
|
||||||
|
}},
|
||||||
|
value: &parser.String{
|
||||||
|
LiteralPos: scanner.Position{
|
||||||
|
Offset: 126,
|
||||||
|
Line: 6,
|
||||||
|
Column: 15,
|
||||||
},
|
},
|
||||||
|
Value: "c2",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
true,
|
||||||
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -866,75 +923,117 @@ var validUnpackTestCases = []struct {
|
||||||
&struct {
|
&struct {
|
||||||
Foo Configurable[string]
|
Foo Configurable[string]
|
||||||
}{
|
}{
|
||||||
Foo: Configurable[string]{
|
Foo: func() Configurable[string] {
|
||||||
propertyName: "foo",
|
result := newConfigurableWithPropertyName(
|
||||||
inner: &configurableInner[string]{
|
"foo",
|
||||||
single: singleConfigurable[string]{
|
[]ConfigurableCondition{{
|
||||||
conditions: []ConfigurableCondition{{
|
functionName: "soong_config_variable",
|
||||||
functionName: "soong_config_variable",
|
args: []string{
|
||||||
args: []string{
|
"my_namespace",
|
||||||
"my_namespace",
|
"my_variable",
|
||||||
"my_variable",
|
|
||||||
},
|
|
||||||
}},
|
|
||||||
cases: []ConfigurableCase[string]{
|
|
||||||
{
|
|
||||||
patterns: []ConfigurablePattern{{
|
|
||||||
typ: configurablePatternTypeString,
|
|
||||||
stringValue: "a",
|
|
||||||
}},
|
|
||||||
value: StringPtr("a2"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
patterns: []ConfigurablePattern{{
|
|
||||||
typ: configurablePatternTypeString,
|
|
||||||
stringValue: "b",
|
|
||||||
}},
|
|
||||||
value: StringPtr("b2"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
patterns: []ConfigurablePattern{{
|
|
||||||
typ: configurablePatternTypeDefault,
|
|
||||||
}},
|
|
||||||
value: StringPtr("c2"),
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
}},
|
||||||
next: &configurableInner[string]{
|
[]ConfigurableCase[string]{
|
||||||
single: singleConfigurable[string]{
|
{
|
||||||
conditions: []ConfigurableCondition{{
|
patterns: []ConfigurablePattern{{
|
||||||
functionName: "soong_config_variable",
|
typ: configurablePatternTypeString,
|
||||||
args: []string{
|
stringValue: "a",
|
||||||
"my_namespace",
|
|
||||||
"my_2nd_variable",
|
|
||||||
},
|
|
||||||
}},
|
}},
|
||||||
cases: []ConfigurableCase[string]{
|
value: &parser.String{
|
||||||
{
|
LiteralPos: scanner.Position{
|
||||||
patterns: []ConfigurablePattern{{
|
Offset: 90,
|
||||||
typ: configurablePatternTypeString,
|
Line: 4,
|
||||||
stringValue: "d",
|
Column: 11,
|
||||||
}},
|
|
||||||
value: StringPtr("d2"),
|
|
||||||
},
|
},
|
||||||
{
|
Value: "a2",
|
||||||
patterns: []ConfigurablePattern{{
|
},
|
||||||
typ: configurablePatternTypeString,
|
},
|
||||||
stringValue: "e",
|
{
|
||||||
}},
|
patterns: []ConfigurablePattern{{
|
||||||
value: StringPtr("e2"),
|
typ: configurablePatternTypeString,
|
||||||
|
stringValue: "b",
|
||||||
|
}},
|
||||||
|
value: &parser.String{
|
||||||
|
LiteralPos: scanner.Position{
|
||||||
|
Offset: 106,
|
||||||
|
Line: 5,
|
||||||
|
Column: 11,
|
||||||
},
|
},
|
||||||
{
|
Value: "b2",
|
||||||
patterns: []ConfigurablePattern{{
|
},
|
||||||
typ: configurablePatternTypeDefault,
|
},
|
||||||
}},
|
{
|
||||||
value: StringPtr("f2"),
|
patterns: []ConfigurablePattern{{
|
||||||
|
typ: configurablePatternTypeDefault,
|
||||||
|
}},
|
||||||
|
value: &parser.String{
|
||||||
|
LiteralPos: scanner.Position{
|
||||||
|
Offset: 126,
|
||||||
|
Line: 6,
|
||||||
|
Column: 15,
|
||||||
},
|
},
|
||||||
|
Value: "c2",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
true,
|
||||||
},
|
)
|
||||||
|
result.Append(newConfigurableWithPropertyName(
|
||||||
|
"",
|
||||||
|
[]ConfigurableCondition{{
|
||||||
|
functionName: "soong_config_variable",
|
||||||
|
args: []string{
|
||||||
|
"my_namespace",
|
||||||
|
"my_2nd_variable",
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
[]ConfigurableCase[string]{
|
||||||
|
{
|
||||||
|
patterns: []ConfigurablePattern{{
|
||||||
|
typ: configurablePatternTypeString,
|
||||||
|
stringValue: "d",
|
||||||
|
}},
|
||||||
|
value: &parser.String{
|
||||||
|
LiteralPos: scanner.Position{
|
||||||
|
Offset: 218,
|
||||||
|
Line: 8,
|
||||||
|
Column: 11,
|
||||||
|
},
|
||||||
|
Value: "d2",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
patterns: []ConfigurablePattern{{
|
||||||
|
typ: configurablePatternTypeString,
|
||||||
|
stringValue: "e",
|
||||||
|
}},
|
||||||
|
value: &parser.String{
|
||||||
|
LiteralPos: scanner.Position{
|
||||||
|
Offset: 234,
|
||||||
|
Line: 9,
|
||||||
|
Column: 11,
|
||||||
|
},
|
||||||
|
Value: "e2",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
patterns: []ConfigurablePattern{{
|
||||||
|
typ: configurablePatternTypeDefault,
|
||||||
|
}},
|
||||||
|
value: &parser.String{
|
||||||
|
LiteralPos: scanner.Position{
|
||||||
|
Offset: 254,
|
||||||
|
Line: 10,
|
||||||
|
Column: 15,
|
||||||
|
},
|
||||||
|
Value: "f2",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
))
|
||||||
|
return result
|
||||||
|
}(),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -953,26 +1052,37 @@ var validUnpackTestCases = []struct {
|
||||||
Foo Configurable[string]
|
Foo Configurable[string]
|
||||||
Bar Configurable[bool]
|
Bar Configurable[bool]
|
||||||
}{
|
}{
|
||||||
Foo: Configurable[string]{
|
Foo: newConfigurableWithPropertyName(
|
||||||
propertyName: "foo",
|
"foo",
|
||||||
inner: &configurableInner[string]{
|
nil,
|
||||||
single: singleConfigurable[string]{
|
[]ConfigurableCase[string]{{
|
||||||
cases: []ConfigurableCase[string]{{
|
value: &parser.String{
|
||||||
value: StringPtr("asdf"),
|
LiteralPos: scanner.Position{
|
||||||
}},
|
Offset: 25,
|
||||||
|
Line: 2,
|
||||||
|
Column: 25,
|
||||||
|
},
|
||||||
|
Value: "asdf",
|
||||||
},
|
},
|
||||||
},
|
}},
|
||||||
},
|
false,
|
||||||
Bar: Configurable[bool]{
|
),
|
||||||
propertyName: "bar",
|
Bar: newConfigurableWithPropertyName(
|
||||||
inner: &configurableInner[bool]{
|
"bar",
|
||||||
single: singleConfigurable[bool]{
|
nil,
|
||||||
cases: []ConfigurableCase[bool]{{
|
[]ConfigurableCase[bool]{{
|
||||||
value: BoolPtr(true),
|
value: &parser.Bool{
|
||||||
}},
|
LiteralPos: scanner.Position{
|
||||||
|
Offset: 54,
|
||||||
|
Line: 3,
|
||||||
|
Column: 23,
|
||||||
|
},
|
||||||
|
Value: true,
|
||||||
|
Token: "true",
|
||||||
},
|
},
|
||||||
},
|
}},
|
||||||
},
|
false,
|
||||||
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in a new issue