2018-09-18 22:42:38 +02:00
|
|
|
package compiler
|
|
|
|
|
|
|
|
import (
|
2019-01-19 17:00:49 +02:00
|
|
|
"strings"
|
|
|
|
|
2018-09-18 22:42:38 +02:00
|
|
|
"github.com/MontFerret/ferret/pkg/parser"
|
|
|
|
"github.com/MontFerret/ferret/pkg/runtime"
|
|
|
|
"github.com/MontFerret/ferret/pkg/runtime/core"
|
|
|
|
"github.com/MontFerret/ferret/pkg/stdlib"
|
|
|
|
"github.com/pkg/errors"
|
|
|
|
)
|
|
|
|
|
|
|
|
type FqlCompiler struct {
|
|
|
|
funcs map[string]core.Function
|
|
|
|
}
|
|
|
|
|
2018-09-19 03:41:16 +02:00
|
|
|
func New(setters ...Option) *FqlCompiler {
|
|
|
|
c := &FqlCompiler{}
|
|
|
|
opts := &Options{}
|
|
|
|
|
|
|
|
for _, setter := range setters {
|
|
|
|
setter(opts)
|
|
|
|
}
|
|
|
|
|
|
|
|
if !opts.noStdlib {
|
|
|
|
c.funcs = stdlib.NewLib()
|
|
|
|
} else {
|
|
|
|
c.funcs = make(map[string]core.Function)
|
|
|
|
}
|
|
|
|
|
2019-02-04 22:30:12 +02:00
|
|
|
return c
|
2018-09-18 22:42:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func (c *FqlCompiler) RegisterFunction(name string, fun core.Function) error {
|
|
|
|
_, exists := c.funcs[name]
|
|
|
|
|
|
|
|
if exists {
|
|
|
|
return errors.Errorf("function already exists: %s", name)
|
|
|
|
}
|
|
|
|
|
2018-09-19 03:41:16 +02:00
|
|
|
c.funcs[strings.ToUpper(name)] = fun
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2018-11-06 17:17:48 +02:00
|
|
|
func (c *FqlCompiler) RemoveFunction(name string) {
|
|
|
|
delete(c.funcs, strings.ToUpper(name))
|
|
|
|
}
|
|
|
|
|
2018-09-19 03:41:16 +02:00
|
|
|
func (c *FqlCompiler) RegisterFunctions(funcs map[string]core.Function) error {
|
|
|
|
for name, fun := range funcs {
|
|
|
|
if err := c.RegisterFunction(name, fun); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
2018-09-18 22:42:38 +02:00
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *FqlCompiler) Compile(query string) (program *runtime.Program, err error) {
|
|
|
|
if query == "" {
|
|
|
|
return nil, ErrEmptyQuery
|
|
|
|
}
|
|
|
|
|
|
|
|
defer func() {
|
|
|
|
if r := recover(); r != nil {
|
|
|
|
// find out exactly what the error was and set err
|
|
|
|
switch x := r.(type) {
|
|
|
|
case string:
|
|
|
|
err = errors.New(x)
|
|
|
|
case error:
|
|
|
|
err = x
|
|
|
|
default:
|
|
|
|
err = errors.New("unknown panic")
|
|
|
|
}
|
|
|
|
|
|
|
|
program = nil
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
2018-11-05 18:45:33 +02:00
|
|
|
p := parser.New(query)
|
|
|
|
p.AddErrorListener(&errorListener{})
|
|
|
|
|
2018-09-28 06:28:33 +02:00
|
|
|
l := newVisitor(query, c.funcs)
|
2018-09-18 22:42:38 +02:00
|
|
|
|
|
|
|
res := p.Visit(l).(*result)
|
|
|
|
|
|
|
|
if res.Ok() {
|
|
|
|
program = res.Data().(*runtime.Program)
|
|
|
|
} else {
|
|
|
|
err = res.Error()
|
|
|
|
}
|
|
|
|
|
|
|
|
return program, err
|
|
|
|
}
|
2018-09-28 01:05:56 +02:00
|
|
|
|
2018-09-28 04:10:17 +02:00
|
|
|
func (c *FqlCompiler) MustCompile(query string) *runtime.Program {
|
2018-09-28 01:05:56 +02:00
|
|
|
program, err := c.Compile(query)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return program
|
|
|
|
}
|
2019-01-19 17:00:49 +02:00
|
|
|
|
|
|
|
func (c *FqlCompiler) RegisteredFunctions() (funcs []string) {
|
|
|
|
for k := range c.funcs {
|
|
|
|
funcs = append(funcs, k)
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|