Separate blueprint parsing and evaluating
Before this cl, blueprint expressions were evaluated as they were parsed. We want to add a feature to select statements where we can bind the value of soome value from soong into a blueprint variable, that then can be used like a regular variable in the .bp file. This means that select statements need to hold whole unevalated expression trees, and have the ability to evaluate them later on when the value of the bound variable is known. This cl doesn't implement the new select syntax, but it does split blueprint's parsing and evaluating into two separate stages. We also store expressions in selects and evaluate them when the select is resolved. I didn't do extensive performance evaluation, but a simple comparison of the time of `touch Android.bp && m nothing` before/after this cl showed a 1 second speedup. (That was probably just noise) Bug: 323382414 Test: m nothing --no-skip-soong-tests Change-Id: I12f373719991afeb4aec76517153f32229d97ff2
This commit is contained in:
parent
b7e4d26faf
commit
1e62c68bfe
16 changed files with 823 additions and 964 deletions
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
343
parser/ast.go
343
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 }
|
||||||
|
@ -139,11 +139,19 @@ 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
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExpressionsAreSame tells whether the two values are the same Expression.
|
// ExpressionsAreSame tells whether the two values are the same Expression.
|
||||||
|
@ -157,9 +165,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 +178,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 +188,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 +211,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 +222,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 +231,137 @@ 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) 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 +372,27 @@ 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) 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 +412,30 @@ 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) String() string {
|
func (x *Map) String() string {
|
||||||
|
@ -379,8 +528,29 @@ 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) String() string {
|
func (x *List) String() string {
|
||||||
|
@ -407,8 +577,26 @@ 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) String() string {
|
func (x *String) String() string {
|
||||||
|
@ -433,8 +621,12 @@ 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) String() string {
|
func (x *Int64) String() string {
|
||||||
|
@ -459,8 +651,12 @@ 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) String() string {
|
func (x *Bool) String() string {
|
||||||
|
@ -542,29 +738,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 +782,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 +806,14 @@ 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
|
||||||
|
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 (s *Select) String() string {
|
func (s *Select) String() string {
|
||||||
|
@ -642,10 +821,10 @@ 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 SelectCase struct {
|
type SelectCase struct {
|
||||||
|
@ -681,21 +860,25 @@ 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 (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
|
||||||
|
|
372
parser/parser.go
372
parser/parser.go
|
@ -45,22 +45,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 +71,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 +130,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 +247,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 +285,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 +345,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 +352,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 +405,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)
|
||||||
|
@ -723,7 +580,7 @@ func (p *parser) parseSelect() Expression {
|
||||||
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
|
||||||
|
@ -798,21 +655,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 +755,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:")
|
||||||
|
@ -1284,7 +908,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 +942,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 +951,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))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -222,7 +222,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)
|
||||||
|
|
|
@ -744,7 +744,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
|
||||||
|
@ -282,27 +283,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]
|
||||||
|
@ -384,6 +404,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
|
||||||
|
@ -415,7 +436,7 @@ func (c *Configurable[T]) AppendSimpleValue(value T) {
|
||||||
c.inner = &configurableInner[T]{
|
c.inner = &configurableInner[T]{
|
||||||
single: singleConfigurable[T]{
|
single: singleConfigurable[T]{
|
||||||
cases: []ConfigurableCase[T]{{
|
cases: []ConfigurableCase[T]{{
|
||||||
value: &value,
|
value: configuredValueToExpression(value),
|
||||||
}},
|
}},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -472,7 +493,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 +525,12 @@ func (c *singleConfigurable[T]) evaluateNonTransitive(propertyName string, evalu
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if allMatch && !foundMatch {
|
if allMatch && !foundMatch {
|
||||||
result = case_.value
|
if r, err := expressionToConfiguredValue[T](case_.value, c.scope); err != nil {
|
||||||
|
evaluator.PropertyErrorf(propertyName, "%s", err.Error())
|
||||||
|
return nil
|
||||||
|
} else {
|
||||||
|
result = r
|
||||||
|
}
|
||||||
foundMatch = true
|
foundMatch = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -579,18 +610,19 @@ 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,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -650,7 +682,7 @@ func (c *configurableInner[T]) appendSimpleValue(value T) {
|
||||||
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,41 +710,13 @@ 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 {
|
|
||||||
count := strings.Count(*s, "%")
|
|
||||||
if count == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if count > 1 {
|
|
||||||
return fmt.Errorf("list/value variable properties only support a single '%%'")
|
|
||||||
}
|
|
||||||
|
|
||||||
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() any {
|
||||||
return Configurable[T]{
|
return Configurable[T]{
|
||||||
propertyName: c.propertyName,
|
propertyName: c.propertyName,
|
||||||
|
@ -755,6 +759,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 +781,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 +792,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 +886,72 @@ 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,
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
case reflect.Bool:
|
||||||
|
return reflect.ValueOf(Configurable[bool]{
|
||||||
|
inner: &configurableInner[bool]{
|
||||||
|
single: singleConfigurable[bool]{
|
||||||
|
cases: []ConfigurableCase[bool]{{
|
||||||
|
value: expr,
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
case reflect.Slice:
|
||||||
|
return reflect.ValueOf(Configurable[[]string]{
|
||||||
|
inner: &configurableInner[[]string]{
|
||||||
|
single: singleConfigurable[[]string]{
|
||||||
|
cases: []ConfigurableCase[[]string]{{
|
||||||
|
value: expr,
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("Can't convert %s property to a configurable", origional.Kind().String()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
@ -1272,7 +1274,10 @@ func appendPropertiesTestCases() []appendPropertyTestCase {
|
||||||
typ: configurablePatternTypeString,
|
typ: configurablePatternTypeString,
|
||||||
stringValue: "a",
|
stringValue: "a",
|
||||||
}},
|
}},
|
||||||
value: &[]string{"1", "2"},
|
value: &parser.List{Values: []parser.Expression{
|
||||||
|
&parser.String{Value: "1"},
|
||||||
|
&parser.String{Value: "2"},
|
||||||
|
}},
|
||||||
}},
|
}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -1293,7 +1298,10 @@ func appendPropertiesTestCases() []appendPropertyTestCase {
|
||||||
typ: configurablePatternTypeString,
|
typ: configurablePatternTypeString,
|
||||||
stringValue: "b",
|
stringValue: "b",
|
||||||
}},
|
}},
|
||||||
value: &[]string{"3", "4"},
|
value: &parser.List{Values: []parser.Expression{
|
||||||
|
&parser.String{Value: "3"},
|
||||||
|
&parser.String{Value: "4"},
|
||||||
|
}},
|
||||||
}},
|
}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -1315,7 +1323,10 @@ func appendPropertiesTestCases() []appendPropertyTestCase {
|
||||||
typ: configurablePatternTypeString,
|
typ: configurablePatternTypeString,
|
||||||
stringValue: "a",
|
stringValue: "a",
|
||||||
}},
|
}},
|
||||||
value: &[]string{"1", "2"},
|
value: &parser.List{Values: []parser.Expression{
|
||||||
|
&parser.String{Value: "1"},
|
||||||
|
&parser.String{Value: "2"},
|
||||||
|
}},
|
||||||
}},
|
}},
|
||||||
},
|
},
|
||||||
next: &configurableInner[[]string]{
|
next: &configurableInner[[]string]{
|
||||||
|
@ -1331,7 +1342,10 @@ func appendPropertiesTestCases() []appendPropertyTestCase {
|
||||||
typ: configurablePatternTypeString,
|
typ: configurablePatternTypeString,
|
||||||
stringValue: "b",
|
stringValue: "b",
|
||||||
}},
|
}},
|
||||||
value: &[]string{"3", "4"},
|
value: &parser.List{Values: []parser.Expression{
|
||||||
|
&parser.String{Value: "3"},
|
||||||
|
&parser.String{Value: "4"},
|
||||||
|
}},
|
||||||
}},
|
}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -1358,7 +1372,10 @@ func appendPropertiesTestCases() []appendPropertyTestCase {
|
||||||
typ: configurablePatternTypeString,
|
typ: configurablePatternTypeString,
|
||||||
stringValue: "a",
|
stringValue: "a",
|
||||||
}},
|
}},
|
||||||
value: &[]string{"1", "2"},
|
value: &parser.List{Values: []parser.Expression{
|
||||||
|
&parser.String{Value: "1"},
|
||||||
|
&parser.String{Value: "2"},
|
||||||
|
}},
|
||||||
}},
|
}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -1379,7 +1396,10 @@ func appendPropertiesTestCases() []appendPropertyTestCase {
|
||||||
typ: configurablePatternTypeString,
|
typ: configurablePatternTypeString,
|
||||||
stringValue: "b",
|
stringValue: "b",
|
||||||
}},
|
}},
|
||||||
value: &[]string{"3", "4"},
|
value: &parser.List{Values: []parser.Expression{
|
||||||
|
&parser.String{Value: "3"},
|
||||||
|
&parser.String{Value: "4"},
|
||||||
|
}},
|
||||||
}},
|
}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -1400,7 +1420,10 @@ func appendPropertiesTestCases() []appendPropertyTestCase {
|
||||||
typ: configurablePatternTypeString,
|
typ: configurablePatternTypeString,
|
||||||
stringValue: "b",
|
stringValue: "b",
|
||||||
}},
|
}},
|
||||||
value: &[]string{"3", "4"},
|
value: &parser.List{Values: []parser.Expression{
|
||||||
|
&parser.String{Value: "3"},
|
||||||
|
&parser.String{Value: "4"},
|
||||||
|
}},
|
||||||
}},
|
}},
|
||||||
},
|
},
|
||||||
next: &configurableInner[[]string]{
|
next: &configurableInner[[]string]{
|
||||||
|
@ -1417,7 +1440,10 @@ func appendPropertiesTestCases() []appendPropertyTestCase {
|
||||||
typ: configurablePatternTypeString,
|
typ: configurablePatternTypeString,
|
||||||
stringValue: "a",
|
stringValue: "a",
|
||||||
}},
|
}},
|
||||||
value: &[]string{"1", "2"},
|
value: &parser.List{Values: []parser.Expression{
|
||||||
|
&parser.String{Value: "1"},
|
||||||
|
&parser.String{Value: "2"},
|
||||||
|
}},
|
||||||
}},
|
}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -1907,12 +1933,12 @@ func appendMatchingPropertiesTestCases() []appendMatchingPropertiesTestCase {
|
||||||
typ: configurablePatternTypeString,
|
typ: configurablePatternTypeString,
|
||||||
stringValue: "a",
|
stringValue: "a",
|
||||||
}},
|
}},
|
||||||
value: BoolPtr(true),
|
value: &parser.Bool{Value: true},
|
||||||
}, {
|
}, {
|
||||||
patterns: []ConfigurablePattern{{
|
patterns: []ConfigurablePattern{{
|
||||||
typ: configurablePatternTypeDefault,
|
typ: configurablePatternTypeDefault,
|
||||||
}},
|
}},
|
||||||
value: BoolPtr(false),
|
value: &parser.Bool{Value: false},
|
||||||
}},
|
}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -1939,18 +1965,18 @@ func appendMatchingPropertiesTestCases() []appendMatchingPropertiesTestCase {
|
||||||
typ: configurablePatternTypeString,
|
typ: configurablePatternTypeString,
|
||||||
stringValue: "a",
|
stringValue: "a",
|
||||||
}},
|
}},
|
||||||
value: BoolPtr(true),
|
value: &parser.Bool{Value: true},
|
||||||
}, {
|
}, {
|
||||||
patterns: []ConfigurablePattern{{
|
patterns: []ConfigurablePattern{{
|
||||||
typ: configurablePatternTypeDefault,
|
typ: configurablePatternTypeDefault,
|
||||||
}},
|
}},
|
||||||
value: BoolPtr(false),
|
value: &parser.Bool{Value: false},
|
||||||
}},
|
}},
|
||||||
},
|
},
|
||||||
next: &configurableInner[bool]{
|
next: &configurableInner[bool]{
|
||||||
single: singleConfigurable[bool]{
|
single: singleConfigurable[bool]{
|
||||||
cases: []ConfigurableCase[bool]{{
|
cases: []ConfigurableCase[bool]{{
|
||||||
value: BoolPtr(true),
|
value: &parser.Bool{Value: true},
|
||||||
}},
|
}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -1979,12 +2005,12 @@ func appendMatchingPropertiesTestCases() []appendMatchingPropertiesTestCase {
|
||||||
typ: configurablePatternTypeString,
|
typ: configurablePatternTypeString,
|
||||||
stringValue: "a",
|
stringValue: "a",
|
||||||
}},
|
}},
|
||||||
value: BoolPtr(true),
|
value: &parser.Bool{Value: true},
|
||||||
}, {
|
}, {
|
||||||
patterns: []ConfigurablePattern{{
|
patterns: []ConfigurablePattern{{
|
||||||
typ: configurablePatternTypeDefault,
|
typ: configurablePatternTypeDefault,
|
||||||
}},
|
}},
|
||||||
value: BoolPtr(false),
|
value: &parser.Bool{Value: false},
|
||||||
}},
|
}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -2011,18 +2037,18 @@ func appendMatchingPropertiesTestCases() []appendMatchingPropertiesTestCase {
|
||||||
typ: configurablePatternTypeString,
|
typ: configurablePatternTypeString,
|
||||||
stringValue: "a",
|
stringValue: "a",
|
||||||
}},
|
}},
|
||||||
value: BoolPtr(true),
|
value: &parser.Bool{Value: true},
|
||||||
}, {
|
}, {
|
||||||
patterns: []ConfigurablePattern{{
|
patterns: []ConfigurablePattern{{
|
||||||
typ: configurablePatternTypeDefault,
|
typ: configurablePatternTypeDefault,
|
||||||
}},
|
}},
|
||||||
value: BoolPtr(false),
|
value: &parser.Bool{Value: false},
|
||||||
}},
|
}},
|
||||||
},
|
},
|
||||||
next: &configurableInner[bool]{
|
next: &configurableInner[bool]{
|
||||||
single: singleConfigurable[bool]{
|
single: singleConfigurable[bool]{
|
||||||
cases: []ConfigurableCase[bool]{{
|
cases: []ConfigurableCase[bool]{{
|
||||||
value: BoolPtr(true),
|
value: &parser.Bool{Value: true},
|
||||||
}},
|
}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -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),
|
||||||
|
@ -359,7 +359,7 @@ func (ctx *unpackContext) unpackToConfigurable(propertyName string, property *pa
|
||||||
inner: &configurableInner[string]{
|
inner: &configurableInner[string]{
|
||||||
single: singleConfigurable[string]{
|
single: singleConfigurable[string]{
|
||||||
cases: []ConfigurableCase[string]{{
|
cases: []ConfigurableCase[string]{{
|
||||||
value: &v.Value,
|
value: v,
|
||||||
}},
|
}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -379,7 +379,7 @@ func (ctx *unpackContext) unpackToConfigurable(propertyName string, property *pa
|
||||||
inner: &configurableInner[bool]{
|
inner: &configurableInner[bool]{
|
||||||
single: singleConfigurable[bool]{
|
single: singleConfigurable[bool]{
|
||||||
cases: []ConfigurableCase[bool]{{
|
cases: []ConfigurableCase[bool]{{
|
||||||
value: &v.Value,
|
value: v,
|
||||||
}},
|
}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -416,7 +416,7 @@ func (ctx *unpackContext) unpackToConfigurable(propertyName string, property *pa
|
||||||
inner: &configurableInner[[]string]{
|
inner: &configurableInner[[]string]{
|
||||||
single: singleConfigurable[[]string]{
|
single: singleConfigurable[[]string]{
|
||||||
cases: []ConfigurableCase[[]string]{{
|
cases: []ConfigurableCase[[]string]{{
|
||||||
value: &value,
|
value: v,
|
||||||
}},
|
}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -425,12 +425,6 @@ func (ctx *unpackContext) unpackToConfigurable(propertyName string, property *pa
|
||||||
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,13 +442,7 @@ 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.(type) {
|
||||||
|
@ -473,40 +461,12 @@ func (ctx *unpackContext) unpackToConfigurable(propertyName string, property *pa
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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 +497,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 +530,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 +550,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 +608,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 +623,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 +634,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"
|
||||||
)
|
)
|
||||||
|
@ -737,7 +738,14 @@ var validUnpackTestCases = []struct {
|
||||||
inner: &configurableInner[string]{
|
inner: &configurableInner[string]{
|
||||||
single: singleConfigurable[string]{
|
single: singleConfigurable[string]{
|
||||||
cases: []ConfigurableCase[string]{{
|
cases: []ConfigurableCase[string]{{
|
||||||
value: StringPtr("bar"),
|
value: &parser.String{
|
||||||
|
LiteralPos: scanner.Position{
|
||||||
|
Offset: 17,
|
||||||
|
Line: 3,
|
||||||
|
Column: 10,
|
||||||
|
},
|
||||||
|
Value: "bar",
|
||||||
|
},
|
||||||
}},
|
}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -761,7 +769,15 @@ var validUnpackTestCases = []struct {
|
||||||
inner: &configurableInner[bool]{
|
inner: &configurableInner[bool]{
|
||||||
single: singleConfigurable[bool]{
|
single: singleConfigurable[bool]{
|
||||||
cases: []ConfigurableCase[bool]{{
|
cases: []ConfigurableCase[bool]{{
|
||||||
value: BoolPtr(true),
|
value: &parser.Bool{
|
||||||
|
LiteralPos: scanner.Position{
|
||||||
|
Offset: 17,
|
||||||
|
Line: 3,
|
||||||
|
Column: 10,
|
||||||
|
},
|
||||||
|
Value: true,
|
||||||
|
Token: "true",
|
||||||
|
},
|
||||||
}},
|
}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -785,7 +801,36 @@ var validUnpackTestCases = []struct {
|
||||||
inner: &configurableInner[[]string]{
|
inner: &configurableInner[[]string]{
|
||||||
single: singleConfigurable[[]string]{
|
single: singleConfigurable[[]string]{
|
||||||
cases: []ConfigurableCase[[]string]{{
|
cases: []ConfigurableCase[[]string]{{
|
||||||
value: &[]string{"a", "b"},
|
value: &parser.List{
|
||||||
|
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",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
}},
|
}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -812,6 +857,7 @@ var validUnpackTestCases = []struct {
|
||||||
propertyName: "foo",
|
propertyName: "foo",
|
||||||
inner: &configurableInner[string]{
|
inner: &configurableInner[string]{
|
||||||
single: singleConfigurable[string]{
|
single: singleConfigurable[string]{
|
||||||
|
scope: parser.NewScope(nil),
|
||||||
conditions: []ConfigurableCondition{{
|
conditions: []ConfigurableCondition{{
|
||||||
functionName: "soong_config_variable",
|
functionName: "soong_config_variable",
|
||||||
args: []string{
|
args: []string{
|
||||||
|
@ -825,20 +871,41 @@ var validUnpackTestCases = []struct {
|
||||||
typ: configurablePatternTypeString,
|
typ: configurablePatternTypeString,
|
||||||
stringValue: "a",
|
stringValue: "a",
|
||||||
}},
|
}},
|
||||||
value: StringPtr("a2"),
|
value: &parser.String{
|
||||||
|
LiteralPos: scanner.Position{
|
||||||
|
Offset: 90,
|
||||||
|
Line: 4,
|
||||||
|
Column: 11,
|
||||||
|
},
|
||||||
|
Value: "a2",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
patterns: []ConfigurablePattern{{
|
patterns: []ConfigurablePattern{{
|
||||||
typ: configurablePatternTypeString,
|
typ: configurablePatternTypeString,
|
||||||
stringValue: "b",
|
stringValue: "b",
|
||||||
}},
|
}},
|
||||||
value: StringPtr("b2"),
|
value: &parser.String{
|
||||||
|
LiteralPos: scanner.Position{
|
||||||
|
Offset: 106,
|
||||||
|
Line: 5,
|
||||||
|
Column: 11,
|
||||||
|
},
|
||||||
|
Value: "b2",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
patterns: []ConfigurablePattern{{
|
patterns: []ConfigurablePattern{{
|
||||||
typ: configurablePatternTypeDefault,
|
typ: configurablePatternTypeDefault,
|
||||||
}},
|
}},
|
||||||
value: StringPtr("c2"),
|
value: &parser.String{
|
||||||
|
LiteralPos: scanner.Position{
|
||||||
|
Offset: 126,
|
||||||
|
Line: 6,
|
||||||
|
Column: 15,
|
||||||
|
},
|
||||||
|
Value: "c2",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -870,6 +937,7 @@ var validUnpackTestCases = []struct {
|
||||||
propertyName: "foo",
|
propertyName: "foo",
|
||||||
inner: &configurableInner[string]{
|
inner: &configurableInner[string]{
|
||||||
single: singleConfigurable[string]{
|
single: singleConfigurable[string]{
|
||||||
|
scope: parser.NewScope(nil),
|
||||||
conditions: []ConfigurableCondition{{
|
conditions: []ConfigurableCondition{{
|
||||||
functionName: "soong_config_variable",
|
functionName: "soong_config_variable",
|
||||||
args: []string{
|
args: []string{
|
||||||
|
@ -883,25 +951,47 @@ var validUnpackTestCases = []struct {
|
||||||
typ: configurablePatternTypeString,
|
typ: configurablePatternTypeString,
|
||||||
stringValue: "a",
|
stringValue: "a",
|
||||||
}},
|
}},
|
||||||
value: StringPtr("a2"),
|
value: &parser.String{
|
||||||
|
LiteralPos: scanner.Position{
|
||||||
|
Offset: 90,
|
||||||
|
Line: 4,
|
||||||
|
Column: 11,
|
||||||
|
},
|
||||||
|
Value: "a2",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
patterns: []ConfigurablePattern{{
|
patterns: []ConfigurablePattern{{
|
||||||
typ: configurablePatternTypeString,
|
typ: configurablePatternTypeString,
|
||||||
stringValue: "b",
|
stringValue: "b",
|
||||||
}},
|
}},
|
||||||
value: StringPtr("b2"),
|
value: &parser.String{
|
||||||
|
LiteralPos: scanner.Position{
|
||||||
|
Offset: 106,
|
||||||
|
Line: 5,
|
||||||
|
Column: 11,
|
||||||
|
},
|
||||||
|
Value: "b2",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
patterns: []ConfigurablePattern{{
|
patterns: []ConfigurablePattern{{
|
||||||
typ: configurablePatternTypeDefault,
|
typ: configurablePatternTypeDefault,
|
||||||
}},
|
}},
|
||||||
value: StringPtr("c2"),
|
value: &parser.String{
|
||||||
|
LiteralPos: scanner.Position{
|
||||||
|
Offset: 126,
|
||||||
|
Line: 6,
|
||||||
|
Column: 15,
|
||||||
|
},
|
||||||
|
Value: "c2",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
next: &configurableInner[string]{
|
next: &configurableInner[string]{
|
||||||
single: singleConfigurable[string]{
|
single: singleConfigurable[string]{
|
||||||
|
scope: parser.NewScope(nil),
|
||||||
conditions: []ConfigurableCondition{{
|
conditions: []ConfigurableCondition{{
|
||||||
functionName: "soong_config_variable",
|
functionName: "soong_config_variable",
|
||||||
args: []string{
|
args: []string{
|
||||||
|
@ -915,20 +1005,41 @@ var validUnpackTestCases = []struct {
|
||||||
typ: configurablePatternTypeString,
|
typ: configurablePatternTypeString,
|
||||||
stringValue: "d",
|
stringValue: "d",
|
||||||
}},
|
}},
|
||||||
value: StringPtr("d2"),
|
value: &parser.String{
|
||||||
|
LiteralPos: scanner.Position{
|
||||||
|
Offset: 218,
|
||||||
|
Line: 8,
|
||||||
|
Column: 11,
|
||||||
|
},
|
||||||
|
Value: "d2",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
patterns: []ConfigurablePattern{{
|
patterns: []ConfigurablePattern{{
|
||||||
typ: configurablePatternTypeString,
|
typ: configurablePatternTypeString,
|
||||||
stringValue: "e",
|
stringValue: "e",
|
||||||
}},
|
}},
|
||||||
value: StringPtr("e2"),
|
value: &parser.String{
|
||||||
|
LiteralPos: scanner.Position{
|
||||||
|
Offset: 234,
|
||||||
|
Line: 9,
|
||||||
|
Column: 11,
|
||||||
|
},
|
||||||
|
Value: "e2",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
patterns: []ConfigurablePattern{{
|
patterns: []ConfigurablePattern{{
|
||||||
typ: configurablePatternTypeDefault,
|
typ: configurablePatternTypeDefault,
|
||||||
}},
|
}},
|
||||||
value: StringPtr("f2"),
|
value: &parser.String{
|
||||||
|
LiteralPos: scanner.Position{
|
||||||
|
Offset: 254,
|
||||||
|
Line: 10,
|
||||||
|
Column: 15,
|
||||||
|
},
|
||||||
|
Value: "f2",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -958,7 +1069,14 @@ var validUnpackTestCases = []struct {
|
||||||
inner: &configurableInner[string]{
|
inner: &configurableInner[string]{
|
||||||
single: singleConfigurable[string]{
|
single: singleConfigurable[string]{
|
||||||
cases: []ConfigurableCase[string]{{
|
cases: []ConfigurableCase[string]{{
|
||||||
value: StringPtr("asdf"),
|
value: &parser.String{
|
||||||
|
LiteralPos: scanner.Position{
|
||||||
|
Offset: 25,
|
||||||
|
Line: 2,
|
||||||
|
Column: 25,
|
||||||
|
},
|
||||||
|
Value: "asdf",
|
||||||
|
},
|
||||||
}},
|
}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -968,7 +1086,15 @@ var validUnpackTestCases = []struct {
|
||||||
inner: &configurableInner[bool]{
|
inner: &configurableInner[bool]{
|
||||||
single: singleConfigurable[bool]{
|
single: singleConfigurable[bool]{
|
||||||
cases: []ConfigurableCase[bool]{{
|
cases: []ConfigurableCase[bool]{{
|
||||||
value: BoolPtr(true),
|
value: &parser.Bool{
|
||||||
|
LiteralPos: scanner.Position{
|
||||||
|
Offset: 54,
|
||||||
|
Line: 3,
|
||||||
|
Column: 23,
|
||||||
|
},
|
||||||
|
Value: true,
|
||||||
|
Token: "true",
|
||||||
|
},
|
||||||
}},
|
}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in a new issue