From 7288a778c386cd9d1f9a88e1a503e38f5541e4d8 Mon Sep 17 00:00:00 2001 From: Ben Johnson Date: Mon, 24 Nov 2014 20:43:23 -0700 Subject: [PATCH] Add influxql.Call. --- influxql/ast.go | 6 ++++-- influxql/parser.go | 43 ++++++++++++++++++++++++++++++++++++++++- influxql/parser_test.go | 24 +++++++++++++++++++++++ 3 files changed, 70 insertions(+), 3 deletions(-) diff --git a/influxql/ast.go b/influxql/ast.go index 664a1fea34..9b8c10801f 100644 --- a/influxql/ast.go +++ b/influxql/ast.go @@ -183,7 +183,7 @@ type VarRef struct { // Call represents a function call. type Call struct { Name string - Expr Expr + Args []Expr } // NumberLiteral represents a numeric literal. @@ -266,7 +266,9 @@ func Walk(v Visitor, node Node) { Walk(v, n.RHS) case *Call: - Walk(v, n.Expr) + for _, expr := range n.Args { + Walk(v, expr) + } } } diff --git a/influxql/parser.go b/influxql/parser.go index 0121b3688f..a76bcc771d 100644 --- a/influxql/parser.go +++ b/influxql/parser.go @@ -459,7 +459,14 @@ func (p *Parser) parseUnaryExpr() (Expr, error) { tok, pos, lit := p.scanIgnoreWhitespace() switch tok { case IDENT: - return &VarRef{Val: lit}, nil + // If the next immediate token is a left parentheses, parse as function call. + // Otherwise parse as a variable reference. + if tok0, _, _ := p.scan(); tok0 == LPAREN { + return p.parseCall(lit) + } else { + p.unscan() + return &VarRef{Val: lit}, nil + } case STRING: return &StringLiteral{Val: lit}, nil case NUMBER: @@ -478,6 +485,40 @@ func (p *Parser) parseUnaryExpr() (Expr, error) { } } +// parseCall parses a function call. +// This function assumes the function name and LPAREN have been consumed. +func (p *Parser) parseCall(name string) (*Call, error) { + // If there's a right paren then just return immediately. + if tok, _, _ := p.scan(); tok == RPAREN { + return &Call{Name: name}, nil + } + p.unscan() + + // Otherwise parse function call arguments. + var args []Expr + for { + // Parse an expression argument. + arg, err := p.ParseExpr() + if err != nil { + return nil, err + } + args = append(args, arg) + + // If there's not a comma next then stop parsing arguments. + if tok, _, _ := p.scan(); tok != COMMA { + p.unscan() + break + } + } + + // There should be a right parentheses at the end. + if tok, pos, lit := p.scan(); tok != RPAREN { + return nil, newParseError(tokstr(tok, lit), []string{")"}, pos) + } + + return &Call{Name: name, Args: args}, nil +} + // scan returns the next token from the underlying scanner. func (p *Parser) scan() (tok Token, pos Pos, lit string) { return p.s.Scan() } diff --git a/influxql/parser_test.go b/influxql/parser_test.go index 21951a03b4..e202f2b2ab 100644 --- a/influxql/parser_test.go +++ b/influxql/parser_test.go @@ -234,6 +234,30 @@ func TestParser_ParseExpr(t *testing.T) { RHS: &influxql.BooleanLiteral{Val: true}, }, }, + + // 9. Function call (empty) + { + s: `my_func()`, + expr: &influxql.Call{ + Name: "my_func", + }, + }, + + // 10. Function call (multi-arg) + { + s: `my_func(1, 2 + 3)`, + expr: &influxql.Call{ + Name: "my_func", + Args: []influxql.Expr{ + &influxql.NumberLiteral{Val: 1}, + &influxql.BinaryExpr{ + Op: influxql.ADD, + LHS: &influxql.NumberLiteral{Val: 2}, + RHS: &influxql.NumberLiteral{Val: 3}, + }, + }, + }, + }, } for i, tt := range tests {