Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 23 additions & 5 deletions pkg/parser/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -612,11 +612,10 @@ func parseExprWithoutPipe(e string) (Expr, string, error) {
return nil, "", ErrMissingExpr
}

if '0' <= e[0] && e[0] <= '9' || e[0] == '-' || e[0] == '+' {
val, valStr, e, err := parseConst(e)
r, _ := utf8.DecodeRuneInString(e)
if !unicode.IsLetter(r) {
return &expr{val: val, etype: EtConst, valStr: valStr}, e, err
if IsDigit(e[0]) || e[0] == '-' || e[0] == '+' {
exp, rest, ok := parseConstExpr(e)
if ok {
return exp, rest, nil
}
}

Expand Down Expand Up @@ -655,6 +654,25 @@ func parseExprWithoutPipe(e string) (Expr, string, error) {
return &expr{target: name}, e, nil
}

func parseConstExpr(e string) (*expr, string, bool) {
val, valStr, rest, err := parseConst(e)
if err != nil {
// A failure means the greedily-slurped run of [0-9.+-eE] characters is
// not a valid float, e.g. a root metric name like "587-AL". Let the
// caller parse the original token as a metric name instead.
return nil, e, false
}
Comment on lines +658 to +664

if rest != "" {
r, size := utf8.DecodeRuneInString(rest)
if size > 0 && unicode.IsLetter(r) {
return nil, e, false
}
}

return &expr{val: val, etype: EtConst, valStr: valStr}, rest, true
}

func parseExprInner(e string) (Expr, string, error) {
exp, e, err := parseExprWithoutPipe(e)
if err != nil {
Expand Down
52 changes: 52 additions & 0 deletions pkg/parser/parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,58 @@ func TestParseExpr(t *testing.T) {
},
},

// Issue #524: a root metric name that starts with one or more digits
// followed by a dash (e.g. "587-AL") must parse as a metric name, the
// same as graphite-web, instead of failing ParseFloat on "587-".
{
"587-AL.random.a",
&expr{target: "587-AL.random.a"},
},
{
"587-AL",
&expr{target: "587-AL"},
},
// Guard against over-correction: these already parsed as names and
// must keep doing so.
{
"AAA-587",
&expr{target: "AAA-587"},
},
{
"587AAA",
&expr{target: "587AAA"},
},
{
"AAA587",
&expr{target: "AAA587"},
},
// Genuine numeric constants must still parse as EtConst.
{
"-1",
&expr{val: -1, etype: EtConst, valStr: "-1"},
},
{
"+2.5",
&expr{val: 2.5, etype: EtConst, valStr: "+2.5"},
},
{
"1e3",
&expr{val: 1000, etype: EtConst, valStr: "1e3"},
},
// A function call with a digit-dash series arg parses without error and
// preserves the inner metric name.
{
"sumSeries(587-AL.random.*)",
&expr{
target: "sumSeries",
etype: EtFunc,
args: []*expr{
{target: "587-AL.random.*"},
},
argString: "587-AL.random.*",
},
},

{
"func1(metric1, -3 , 'foo' )",
&expr{
Expand Down
Loading