Add influxql.Call.

pull/1159/head
Ben Johnson 2014-11-24 20:43:23 -07:00
parent 00f969af1f
commit 7288a778c3
3 changed files with 70 additions and 3 deletions

View File

@ -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)
}
}
}

View File

@ -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() }

View File

@ -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 {