|
|
package astexpr
|
|
|
|
|
|
import (
|
|
|
"fmt"
|
|
|
"github.com/go-gota/gota/dataframe"
|
|
|
"github.com/go-gota/gota/series"
|
|
|
"gitlab.fjmaimaimai.com/allied-creation/character-library-metadata-bastion/pkg/domain"
|
|
|
"gitlab.fjmaimaimai.com/allied-creation/character-library-metadata-bastion/pkg/infrastructure/utils"
|
|
|
"strings"
|
|
|
)
|
|
|
|
|
|
type Calculator struct {
|
|
|
ExprAST ExprAST
|
|
|
DataTable *domain.DataTable
|
|
|
Result []string
|
|
|
}
|
|
|
|
|
|
func NewCalculator(expr string) (*Calculator, error) {
|
|
|
toks, err := ParseToken(expr)
|
|
|
if err != nil {
|
|
|
return nil, err
|
|
|
}
|
|
|
ast := NewAST(toks, expr)
|
|
|
if ast.Err != nil {
|
|
|
return nil, ast.Err
|
|
|
}
|
|
|
ar := ast.ParseExpression()
|
|
|
if ast.Err != nil {
|
|
|
return nil, ast.Err
|
|
|
}
|
|
|
|
|
|
cal := &Calculator{
|
|
|
ExprAST: ar,
|
|
|
}
|
|
|
return cal, nil
|
|
|
}
|
|
|
|
|
|
func (cal *Calculator) SetDataTable(t *domain.DataTable) *Calculator {
|
|
|
cal.DataTable = t
|
|
|
return cal
|
|
|
}
|
|
|
|
|
|
func (cal *Calculator) Exec() error {
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
func (cal *Calculator) ExprASTResult(ast ExprAST) (*param, error) {
|
|
|
switch ast.(type) {
|
|
|
case BinaryExprAST:
|
|
|
var l, r *param
|
|
|
var err error
|
|
|
ast := ast.(BinaryExprAST)
|
|
|
l, err = cal.ExprASTResult(ast.Lhs)
|
|
|
if err != nil {
|
|
|
return nil, err
|
|
|
}
|
|
|
r, err = cal.ExprASTResult(ast.Rhs)
|
|
|
if err != nil {
|
|
|
return nil, err
|
|
|
}
|
|
|
switch ast.Op {
|
|
|
case "+", "-", "*", "/", "%":
|
|
|
return cal.OpCalc(ast.Op, l, r), nil
|
|
|
default:
|
|
|
|
|
|
}
|
|
|
case NumberExprAST:
|
|
|
f := ast.(NumberExprAST)
|
|
|
return NewResult([]string{f.Str}), nil
|
|
|
case ValueExprAST:
|
|
|
f := ast.(ValueExprAST)
|
|
|
return NewResult([]string{f.Val}), nil
|
|
|
case FieldExprAST:
|
|
|
f := ast.(FieldExprAST)
|
|
|
values := cal.DataTable.Values(&domain.Field{SQLName: f.Field.FieldSqlName})
|
|
|
return NewResult(values), nil
|
|
|
case FunCallerExprAST:
|
|
|
f := ast.(FunCallerExprAST)
|
|
|
//def := defFunc[f.Name]
|
|
|
//def.fun(f.Args...)
|
|
|
|
|
|
args := make([]*param, 0)
|
|
|
for i := range f.Args {
|
|
|
argValue, err := cal.ExprASTResult(f.Args[i])
|
|
|
if err != nil {
|
|
|
return nil, err
|
|
|
}
|
|
|
args = append(args, argValue)
|
|
|
}
|
|
|
return cal.callDef(f.Name, args), nil
|
|
|
}
|
|
|
return nil, nil
|
|
|
}
|
|
|
|
|
|
func (cal *Calculator) callDef(name string, args []*param) *param {
|
|
|
switch strings.ToLower(name) {
|
|
|
case "sum":
|
|
|
return cal.sum(args...)
|
|
|
case "sumifs":
|
|
|
return cal.sumifs(args...)
|
|
|
case "countifs":
|
|
|
return cal.countifs(args...)
|
|
|
}
|
|
|
return cal.sum(args...)
|
|
|
}
|
|
|
|
|
|
func (cal *Calculator) sum(params ...*param) *param {
|
|
|
var res = make([]string, 0)
|
|
|
var total float64
|
|
|
for _, p := range params {
|
|
|
for _, v := range p.data {
|
|
|
total += utils.NewNumberString(v).MustFloat64()
|
|
|
}
|
|
|
}
|
|
|
res = append(res, utils.AssertString(total))
|
|
|
return NewResult(res)
|
|
|
}
|
|
|
|
|
|
func (cal *Calculator) sumifs(params ...*param) *param {
|
|
|
var list = make([]series.Series, 0)
|
|
|
var filters = make([]dataframe.F, 0)
|
|
|
for i := 0; i < len(params)-1; i++ {
|
|
|
col := colName(i)
|
|
|
if i == 0 {
|
|
|
list = append(list, series.New(params[i].Data(), series.Float, col))
|
|
|
continue
|
|
|
}
|
|
|
if i%2 == 1 {
|
|
|
list = append(list, series.New(params[i+1].Data(), series.String, col))
|
|
|
if f, ok := cal.resolverFilter(col, params[i]); ok {
|
|
|
filters = append(filters, f)
|
|
|
}
|
|
|
i++
|
|
|
}
|
|
|
}
|
|
|
df := dataframe.New(list...)
|
|
|
df = df.FilterAggregation(dataframe.And, filters...)
|
|
|
s := df.Col("A0")
|
|
|
return NewResult(s.Records())
|
|
|
}
|
|
|
|
|
|
func (cal *Calculator) countifs(params ...*param) *param {
|
|
|
var list = make([]series.Series, 0)
|
|
|
var filters = make([]dataframe.F, 0)
|
|
|
for i := 0; i < len(params)-1; i++ {
|
|
|
col := colName(i)
|
|
|
if i%2 == 0 {
|
|
|
list = append(list, series.New(params[i].Data(), series.String, col))
|
|
|
if f, ok := cal.resolverFilter(col, params[i+1]); ok {
|
|
|
filters = append(filters, f)
|
|
|
}
|
|
|
i++
|
|
|
}
|
|
|
}
|
|
|
df := dataframe.New(list...)
|
|
|
df = df.FilterAggregation(dataframe.And, filters...)
|
|
|
count := df.Col("A0").Len()
|
|
|
return NewResult([]string{fmt.Sprintf("%d", count)})
|
|
|
}
|
|
|
|
|
|
func (cal *Calculator) resolverFilter(key string, param *param) (dataframe.F, bool) {
|
|
|
if len(param.Data()) == 1 {
|
|
|
condition := param.Data()[0]
|
|
|
tokens, _ := ParseToken(formatTok(condition))
|
|
|
|
|
|
switch tokens[0].Type {
|
|
|
case Operator, CompareOperator:
|
|
|
if tokens[0].Tok == "*" {
|
|
|
return dataframe.F{Colname: key, Comparator: series.CompFunc, Comparando: func(el series.Element) bool {
|
|
|
return strings.Contains(el.String(), strings.Trim(formatTok(condition), "*"))
|
|
|
}}, true
|
|
|
}
|
|
|
return dataframe.F{Colname: key, Comparator: series.Comparator(tokens[0].Tok), Comparando: formatTok(tokens[1].Tok)}, true
|
|
|
case Identifier, Literal, StringArgs:
|
|
|
if tokens[len(tokens)-1].Tok == "*" || tokens[0].Tok == "*" {
|
|
|
return dataframe.F{Colname: key, Comparator: series.CompFunc, Comparando: func(el series.Element) bool {
|
|
|
return strings.Contains(el.String(), strings.Trim(formatTok(condition), "*"))
|
|
|
}}, true
|
|
|
}
|
|
|
return dataframe.F{Colname: key, Comparator: series.Eq, Comparando: formatTok(condition)}, true
|
|
|
}
|
|
|
}
|
|
|
return dataframe.F{}, false
|
|
|
}
|
|
|
|
|
|
func colName(i int) string {
|
|
|
return fmt.Sprintf("A%v", i)
|
|
|
}
|
|
|
|
|
|
func formatTok(tok string) string {
|
|
|
return strings.Trim(tok, `"`)
|
|
|
}
|
|
|
|
|
|
func (cal *Calculator) OpCalc(op string, lp *param, rp *param) *param {
|
|
|
var res = make([]string, 0)
|
|
|
temp := make([]string, 0)
|
|
|
temp = lp.Data()
|
|
|
l := lp.Data()
|
|
|
r := rp.Data()
|
|
|
if lp.Len() < rp.Len() {
|
|
|
l = r
|
|
|
r = temp
|
|
|
}
|
|
|
rIsSingleValue := len(r) == 1
|
|
|
var rValue string
|
|
|
if rIsSingleValue {
|
|
|
rValue = r[0]
|
|
|
}
|
|
|
for i, lValue := range l {
|
|
|
if rIsSingleValue {
|
|
|
res = append(res, opCalc(op, lValue, rValue))
|
|
|
continue
|
|
|
}
|
|
|
if i >= len(r) {
|
|
|
break
|
|
|
}
|
|
|
res = append(res, opCalc(op, lValue, r[i]))
|
|
|
}
|
|
|
return NewResult(res)
|
|
|
}
|
|
|
|
|
|
func opCalc(op, v1, v2 string) string {
|
|
|
fv1 := utils.NumberString(v1).MustFloat64()
|
|
|
fv2 := utils.NumberString(v2).MustFloat64()
|
|
|
switch op {
|
|
|
case "+":
|
|
|
return utils.AssertString(fv1 + fv2)
|
|
|
case "-":
|
|
|
return utils.AssertString(fv1 - fv2)
|
|
|
case "*":
|
|
|
return utils.AssertString(fv1 * fv2)
|
|
|
case "/":
|
|
|
return utils.AssertString(fv1 / fv2)
|
|
|
}
|
|
|
return ""
|
|
|
}
|
|
|
|
|
|
type param struct {
|
|
|
data []string
|
|
|
}
|
|
|
|
|
|
func (p *param) Len() int {
|
|
|
return len(p.data)
|
|
|
}
|
|
|
|
|
|
func (p *param) Data() []string {
|
|
|
return p.data
|
|
|
}
|
|
|
|
|
|
func NewResult(data []string) *param {
|
|
|
return ¶m{
|
|
|
data: data,
|
|
|
}
|
|
|
} |
...
|
...
|
|