lang: Edges should allow lists of strings

This continues the earlier patch that allowed resource names to be lists
of strings so that edges can now allow the same. This also includes a
new fancy test!
This commit is contained in:
James Shubin
2019-01-20 17:21:09 -05:00
parent d6bbb94be5
commit db1dbe7a27
3 changed files with 152 additions and 44 deletions

View File

@@ -0,0 +1,18 @@
Edge: test[hey] -> test[amazing] # test[hey] -> test[amazing]
Edge: test[hey] -> test[cool] # test[hey] -> test[cool]
Edge: test[hey] -> test[hello] # test[hey] -> test[hello]
Edge: test[hey] -> test[world] # test[hey] -> test[world]
Edge: test[hey] -> test[wow] # test[hey] -> test[wow]
Edge: test[there] -> test[amazing] # test[there] -> test[amazing]
Edge: test[there] -> test[cool] # test[there] -> test[cool]
Edge: test[there] -> test[hello] # test[there] -> test[hello]
Edge: test[there] -> test[world] # test[there] -> test[world]
Edge: test[there] -> test[wow] # test[there] -> test[wow]
Vertex: test[amazing]
Vertex: test[cool]
Vertex: test[hello]
Vertex: test[hey]
Vertex: test[name]
Vertex: test[there]
Vertex: test[world]
Vertex: test[wow]

View File

@@ -0,0 +1,18 @@
# this must pass type unification and generate output
# single resource
test "name" {}
# single resource, defined by list variable
$names = ["hey", "there",]
test $names {}
# multiples resources, defined by list
test ["hello", "world",] {
Depend => Test[$names],
}
$morenames = ["wow", "cool", "amazing",]
test $morenames {}
Test[$names] -> Test[$morenames]

View File

@@ -531,45 +531,66 @@ func (obj *StmtRes) edges(resName string) ([]*interfaces.Edge, error) {
} }
} }
v, err := x.EdgeHalf.Name.Value() nameValue, err := x.EdgeHalf.Name.Value()
if err != nil { if err != nil {
return nil, err return nil, err
} }
name := v.Str() // must not panic
kind := x.EdgeHalf.Kind
var notify bool
switch p := x.Property; p { // the edge name can be a single string or a list of strings...
// a -> b
// a notify b
// a before b
case EdgeNotify:
notify = true
fallthrough
case EdgeBefore:
if m, exists := to[kind]; !exists {
to[kind] = make(map[string]bool)
} else if n, exists := m[name]; exists {
notify = notify || n // collate
}
to[kind][name] = notify // to this from self
// b -> a names := []string{} // list of names to build
// b listen a switch {
// b depend a case types.TypeStr.Cmp(nameValue.Type()) == nil:
case EdgeListen: name := nameValue.Str() // must not panic
notify = true names = append(names, name)
fallthrough
case EdgeDepend: case types.NewType("[]str").Cmp(nameValue.Type()) == nil:
if m, exists := from[kind]; !exists { for _, x := range nameValue.List() { // must not panic
from[kind] = make(map[string]bool) name := x.Str() // must not panic
} else if n, exists := m[name]; exists { names = append(names, name)
notify = notify || n // collate
} }
from[kind][name] = notify // from this to self
default: default:
return nil, fmt.Errorf("unknown property: %s", p) // programming error
return nil, fmt.Errorf("unhandled resource name type: %+v", nameValue.Type())
}
kind := x.EdgeHalf.Kind
for _, name := range names {
var notify bool
switch p := x.Property; p {
// a -> b
// a notify b
// a before b
case EdgeNotify:
notify = true
fallthrough
case EdgeBefore:
if m, exists := to[kind]; !exists {
to[kind] = make(map[string]bool)
} else if n, exists := m[name]; exists {
notify = notify || n // collate
}
to[kind][name] = notify // to this from self
// b -> a
// b listen a
// b depend a
case EdgeListen:
notify = true
fallthrough
case EdgeDepend:
if m, exists := from[kind]; !exists {
from[kind] = make(map[string]bool)
} else if n, exists := m[name]; exists {
notify = notify || n // collate
}
from[kind][name] = notify // from this to self
default:
return nil, fmt.Errorf("unknown property: %s", p)
}
} }
} }
@@ -1434,26 +1455,64 @@ func (obj *StmtEdge) Output() (*interfaces.Output, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
name1 := nameValue1.Str() // must not panic
// the edge name can be a single string or a list of strings...
names1 := []string{} // list of names to build
switch {
case types.TypeStr.Cmp(nameValue1.Type()) == nil:
name := nameValue1.Str() // must not panic
names1 = append(names1, name)
case types.NewType("[]str").Cmp(nameValue1.Type()) == nil:
for _, x := range nameValue1.List() { // must not panic
name := x.Str() // must not panic
names1 = append(names1, name)
}
default:
// programming error
return nil, fmt.Errorf("unhandled resource name type: %+v", nameValue1.Type())
}
nameValue2, err := obj.EdgeHalfList[i+1].Name.Value() nameValue2, err := obj.EdgeHalfList[i+1].Name.Value()
if err != nil { if err != nil {
return nil, err return nil, err
} }
name2 := nameValue2.Str() // must not panic
edge := &interfaces.Edge{ names2 := []string{} // list of names to build
Kind1: obj.EdgeHalfList[i].Kind, switch {
Name1: name1, case types.TypeStr.Cmp(nameValue2.Type()) == nil:
Send: obj.EdgeHalfList[i].SendRecv, name := nameValue2.Str() // must not panic
names2 = append(names2, name)
Kind2: obj.EdgeHalfList[i+1].Kind, case types.NewType("[]str").Cmp(nameValue2.Type()) == nil:
Name2: name2, for _, x := range nameValue2.List() { // must not panic
Recv: obj.EdgeHalfList[i+1].SendRecv, name := x.Str() // must not panic
names2 = append(names2, name)
}
Notify: obj.Notify, default:
// programming error
return nil, fmt.Errorf("unhandled resource name type: %+v", nameValue2.Type())
}
for _, name1 := range names1 {
for _, name2 := range names2 {
edge := &interfaces.Edge{
Kind1: obj.EdgeHalfList[i].Kind,
Name1: name1,
Send: obj.EdgeHalfList[i].SendRecv,
Kind2: obj.EdgeHalfList[i+1].Kind,
Name2: name2,
Recv: obj.EdgeHalfList[i+1].SendRecv,
Notify: obj.Notify,
}
edges = append(edges, edge)
}
} }
edges = append(edges, edge)
} }
return &interfaces.Output{ return &interfaces.Output{
@@ -1531,11 +1590,24 @@ func (obj *StmtEdgeHalf) Unify() ([]interfaces.Invariant, error) {
} }
invariants = append(invariants, invars...) invariants = append(invariants, invars...)
// name must be a string // name must be a string or a list
invar := &unification.EqualsInvariant{ ors := []interfaces.Invariant{}
invarStr := &unification.EqualsInvariant{
Expr: obj.Name, Expr: obj.Name,
Type: types.TypeStr, Type: types.TypeStr,
} }
ors = append(ors, invarStr)
invarListStr := &unification.EqualsInvariant{
Expr: obj.Name,
Type: types.NewType("[]str"),
}
ors = append(ors, invarListStr)
invar := &unification.ExclusiveInvariant{
Invariants: ors, // one and only one of these should be true
}
invariants = append(invariants, invar) invariants = append(invariants, invar)
return invariants, nil return invariants, nil