Add functions packages support (#419)

* Add packaged functions support

* Add tests for math.floor func

* Export FunctionsTable

* Extract func stuff into its own package

* Rename stuff

* Fix tests

* Move doc package to cmd/genji

* Adjust naming, typos
This commit is contained in:
Jean Hadrien Chabran
2021-07-17 13:07:16 +02:00
committed by GitHub
parent c0861ed2c5
commit e556fc3048
31 changed files with 870 additions and 238 deletions

View File

@@ -6,6 +6,7 @@ import (
"github.com/genjidb/genji/document"
"github.com/genjidb/genji/internal/environment"
"github.com/genjidb/genji/internal/expr"
"github.com/genjidb/genji/internal/expr/functions"
"github.com/genjidb/genji/internal/sql/scanner"
"github.com/genjidb/genji/internal/stringutil"
)
@@ -190,11 +191,30 @@ func (p *Parser) parseUnaryExpr(allowed ...scanner.Token) (expr.Expr, error) {
p.Unscan()
return p.parseCastExpression()
case scanner.IDENT:
// if the next token is a left parenthesis, this is a function
if tok1, _, _ := p.Scan(); tok1 == scanner.LPAREN {
tok1, _, _ := p.Scan()
// if the next token is a left parenthesis, this is a global function
if tok1 == scanner.LPAREN {
p.Unscan()
p.Unscan()
return p.parseFunction()
} else {
// it may be a package function instead.
if tok1 == scanner.DOT {
if tok2, _, _ := p.Scan(); tok2 == scanner.IDENT {
if tok3, _, _ := p.Scan(); tok3 == scanner.LPAREN {
p.Unscan()
p.Unscan()
p.Unscan()
p.Unscan()
return p.parseFunction()
} else {
p.Unscan()
p.Unscan()
}
} else {
p.Unscan()
}
}
}
p.Unscan()
p.Unscan()
@@ -577,11 +597,23 @@ func (p *Parser) parseExprList(leftToken, rightToken scanner.Token) (expr.Litera
// an optional coma-separated list of expressions and a closing parenthesis.
func (p *Parser) parseFunction() (expr.Expr, error) {
// Parse function name.
fname, err := p.parseIdent()
funcName, err := p.parseIdent()
if err != nil {
return nil, err
}
// Parse optional package name
var pkgName string
if tok, _, _ := p.Scan(); tok == scanner.DOT {
pkgName = funcName
funcName, err = p.parseIdent()
if err != nil {
return nil, err
}
} else {
p.Unscan()
}
// Parse required ( token.
if err := p.parseTokens(scanner.LPAREN); err != nil {
return nil, err
@@ -593,13 +625,17 @@ func (p *Parser) parseFunction() (expr.Expr, error) {
return nil, newParseError(scanner.Tokstr(tok, lit), []string{")"}, pos)
}
return &expr.CountFunc{Wildcard: true}, nil
return &functions.Count{Wildcard: true}, nil
}
p.Unscan()
// Check if the function is called without arguments.
if tok, _, _ := p.ScanIgnoreWhitespace(); tok == scanner.RPAREN {
return p.functions.GetFunc(fname)
def, err := p.packagesTable.GetFunc(pkgName, funcName)
if err != nil {
return nil, err
}
return def.Function()
}
p.Unscan()
@@ -625,7 +661,11 @@ func (p *Parser) parseFunction() (expr.Expr, error) {
return nil, err
}
return p.functions.GetFunc(fname, exprs...)
def, err := p.packagesTable.GetFunc(pkgName, funcName)
if err != nil {
return nil, err
}
return def.Function(exprs...)
}
// parseCastExpression parses a string of the form CAST(expr AS type).
@@ -657,7 +697,7 @@ func (p *Parser) parseCastExpression() (expr.Expr, error) {
return nil, err
}
return expr.CastFunc{Expr: e, CastAs: tp}, nil
return functions.Cast{Expr: e, CastAs: tp}, nil
}
// tokenIsAllowed is a helper function that determines if a token is allowed.