mirror of
https://github.com/ClusterCockpit/cc-backend
synced 2024-12-25 12:59:06 +01:00
make nodelist parser more slurm-like
This commit is contained in:
parent
515fdbd44c
commit
daa2fd638b
@ -8,6 +8,29 @@ import (
|
|||||||
"github.com/ClusterCockpit/cc-backend/log"
|
"github.com/ClusterCockpit/cc-backend/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type NodeList [][]interface {
|
||||||
|
consume(input string) (next string, ok bool)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (nl *NodeList) Contains(name string) bool {
|
||||||
|
var ok bool
|
||||||
|
for _, term := range *nl {
|
||||||
|
str := name
|
||||||
|
for _, expr := range term {
|
||||||
|
str, ok = expr.consume(str)
|
||||||
|
if !ok {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ok && str == "" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
type NLExprString string
|
type NLExprString string
|
||||||
|
|
||||||
func (nle NLExprString) consume(input string) (next string, ok bool) {
|
func (nle NLExprString) consume(input string) (next string, ok bool) {
|
||||||
@ -18,6 +41,17 @@ func (nle NLExprString) consume(input string) (next string, ok bool) {
|
|||||||
return "", false
|
return "", false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type NLExprIntRanges []NLExprIntRange
|
||||||
|
|
||||||
|
func (nles NLExprIntRanges) consume(input string) (next string, ok bool) {
|
||||||
|
for _, nle := range nles {
|
||||||
|
if next, ok := nle.consume(input); ok {
|
||||||
|
return next, ok
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
|
||||||
type NLExprIntRange struct {
|
type NLExprIntRange struct {
|
||||||
start, end int64
|
start, end int64
|
||||||
zeroPadded bool
|
zeroPadded bool
|
||||||
@ -51,36 +85,31 @@ func (nle NLExprIntRange) consume(input string) (next string, ok bool) {
|
|||||||
return "", false
|
return "", false
|
||||||
}
|
}
|
||||||
|
|
||||||
type NodeList [][]interface {
|
|
||||||
consume(input string) (next string, ok bool)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (nl *NodeList) Contains(name string) bool {
|
|
||||||
var ok bool
|
|
||||||
for _, term := range *nl {
|
|
||||||
str := name
|
|
||||||
for _, expr := range term {
|
|
||||||
str, ok = expr.consume(str)
|
|
||||||
if !ok {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ok && str == "" {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func ParseNodeList(raw string) (NodeList, error) {
|
func ParseNodeList(raw string) (NodeList, error) {
|
||||||
nl := NodeList{}
|
|
||||||
|
|
||||||
isLetter := func(r byte) bool { return ('a' <= r && r <= 'z') || ('A' <= r && r <= 'Z') }
|
isLetter := func(r byte) bool { return ('a' <= r && r <= 'z') || ('A' <= r && r <= 'Z') }
|
||||||
isDigit := func(r byte) bool { return '0' <= r && r <= '9' }
|
isDigit := func(r byte) bool { return '0' <= r && r <= '9' }
|
||||||
|
|
||||||
for _, rawterm := range strings.Split(raw, ",") {
|
rawterms := []string{}
|
||||||
|
prevterm := 0
|
||||||
|
for i := 0; i < len(raw); i++ {
|
||||||
|
if raw[i] == '[' {
|
||||||
|
for i < len(raw) && raw[i] != ']' {
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
if i == len(raw) {
|
||||||
|
return nil, fmt.Errorf("node list: unclosed '['")
|
||||||
|
}
|
||||||
|
} else if raw[i] == ',' {
|
||||||
|
rawterms = append(rawterms, raw[prevterm:i])
|
||||||
|
prevterm = i + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if prevterm != len(raw) {
|
||||||
|
rawterms = append(rawterms, raw[prevterm:])
|
||||||
|
}
|
||||||
|
|
||||||
|
nl := NodeList{}
|
||||||
|
for _, rawterm := range rawterms {
|
||||||
exprs := []interface {
|
exprs := []interface {
|
||||||
consume(input string) (next string, ok bool)
|
consume(input string) (next string, ok bool)
|
||||||
}{}
|
}{}
|
||||||
@ -99,31 +128,37 @@ func ParseNodeList(raw string) (NodeList, error) {
|
|||||||
return nil, fmt.Errorf("node list: unclosed '['")
|
return nil, fmt.Errorf("node list: unclosed '['")
|
||||||
}
|
}
|
||||||
|
|
||||||
minus := strings.Index(rawterm[i:i+end], "-")
|
parts := strings.Split(rawterm[i+1:i+end], ",")
|
||||||
if minus == -1 {
|
nles := NLExprIntRanges{}
|
||||||
return nil, fmt.Errorf("node list: no '-' found inside '[...]'")
|
for _, part := range parts {
|
||||||
|
minus := strings.Index(part, "-")
|
||||||
|
if minus == -1 {
|
||||||
|
return nil, fmt.Errorf("node list: no '-' found inside '[...]'")
|
||||||
|
}
|
||||||
|
|
||||||
|
s1, s2 := part[0:minus], part[minus+1:]
|
||||||
|
if len(s1) != len(s2) || len(s1) == 0 {
|
||||||
|
return nil, fmt.Errorf("node list: %#v and %#v are not of equal length or of length zero", s1, s2)
|
||||||
|
}
|
||||||
|
|
||||||
|
x1, err := strconv.ParseInt(s1, 10, 32)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("node list: %w", err)
|
||||||
|
}
|
||||||
|
x2, err := strconv.ParseInt(s2, 10, 32)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("node list: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
nles = append(nles, NLExprIntRange{
|
||||||
|
start: x1,
|
||||||
|
end: x2,
|
||||||
|
digits: len(s1),
|
||||||
|
zeroPadded: true,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
s1, s2 := rawterm[i+1:i+minus], rawterm[i+minus+1:i+end]
|
exprs = append(exprs, nles)
|
||||||
if len(s1) != len(s2) || len(s1) == 0 {
|
|
||||||
return nil, fmt.Errorf("node list: %#v and %#v are not of equal length or of length zero", s1, s2)
|
|
||||||
}
|
|
||||||
|
|
||||||
x1, err := strconv.ParseInt(s1, 10, 32)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("node list: %w", err)
|
|
||||||
}
|
|
||||||
x2, err := strconv.ParseInt(s2, 10, 32)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("node list: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
exprs = append(exprs, NLExprIntRange{
|
|
||||||
start: x1,
|
|
||||||
end: x2,
|
|
||||||
digits: len(s1),
|
|
||||||
zeroPadded: true,
|
|
||||||
})
|
|
||||||
i += end
|
i += end
|
||||||
} else {
|
} else {
|
||||||
return nil, fmt.Errorf("node list: invalid character: %#v", rune(c))
|
return nil, fmt.Errorf("node list: invalid character: %#v", rune(c))
|
||||||
|
@ -10,11 +10,6 @@ func TestNodeList(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// fmt.Printf("terms\n")
|
|
||||||
// for i, term := range nl.terms {
|
|
||||||
// fmt.Printf("term %d: %#v\n", i, term)
|
|
||||||
// }
|
|
||||||
|
|
||||||
if nl.Contains("hello") || nl.Contains("woody") {
|
if nl.Contains("hello") || nl.Contains("woody") {
|
||||||
t.Fail()
|
t.Fail()
|
||||||
}
|
}
|
||||||
@ -35,3 +30,26 @@ func TestNodeList(t *testing.T) {
|
|||||||
t.Fail()
|
t.Fail()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestNodeListCommasInBrackets(t *testing.T) {
|
||||||
|
nl, err := ParseNodeList("a[1000-2000,2010-2090,3000-5000]")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if nl.Contains("hello") || nl.Contains("woody") {
|
||||||
|
t.Fatal("1")
|
||||||
|
}
|
||||||
|
|
||||||
|
if nl.Contains("a0") || nl.Contains("a0000") || nl.Contains("a5001") || nl.Contains("a2005") {
|
||||||
|
t.Fatal("2")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !nl.Contains("a1001") || !nl.Contains("a2000") {
|
||||||
|
t.Fatal("3")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !nl.Contains("a2042") || !nl.Contains("a4321") || !nl.Contains("a3000") {
|
||||||
|
t.Fatal("4")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user