package parser import ( "regexp" "strconv" "strings" "time" "github.com/influxdata/platform/query/ast" ) func toIfaceSlice(v interface{}) []interface{} { if v == nil { return nil } return v.([]interface{}) } func program(body interface{}, text []byte, pos position) (*ast.Program, error) { return &ast.Program{ Body: body.([]ast.Statement), BaseNode: base(text, pos), }, nil } func srcElems(head, tails interface{}) ([]ast.Statement, error) { elems := []ast.Statement{head.(ast.Statement)} for _, tail := range toIfaceSlice(tails) { elem := toIfaceSlice(tail)[1] // Skip whitespace elems = append(elems, elem.(ast.Statement)) } return elems, nil } func blockstmt(body interface{}, text []byte, pos position) (*ast.BlockStatement, error) { bodySlice := toIfaceSlice(body) statements := make([]ast.Statement, len(bodySlice)) for i, s := range bodySlice { stmt := toIfaceSlice(s)[1] // Skip whitespace statements[i] = stmt.(ast.Statement) } return &ast.BlockStatement{ BaseNode: base(text, pos), Body: statements, }, nil } func varstmt(declaration interface{}, text []byte, pos position) (*ast.VariableDeclaration, error) { return &ast.VariableDeclaration{ Declarations: []*ast.VariableDeclarator{declaration.(*ast.VariableDeclarator)}, BaseNode: base(text, pos), }, nil } func vardecl(id, initializer interface{}, text []byte, pos position) (*ast.VariableDeclarator, error) { return &ast.VariableDeclarator{ ID: id.(*ast.Identifier), Init: initializer.(ast.Expression), }, nil } func exprstmt(expr interface{}, text []byte, pos position) (*ast.ExpressionStatement, error) { return &ast.ExpressionStatement{ Expression: expr.(ast.Expression), BaseNode: base(text, pos), }, nil } func returnstmt(argument interface{}, text []byte, pos position) (*ast.ReturnStatement, error) { return &ast.ReturnStatement{ BaseNode: base(text, pos), Argument: argument.(ast.Expression), }, nil } func pipeExprs(head, tail interface{}, text []byte, pos position) (*ast.PipeExpression, error) { var arg ast.Expression arg = head.(ast.Expression) var pe *ast.PipeExpression for _, t := range toIfaceSlice(tail) { pe = toIfaceSlice(t)[1].(*ast.PipeExpression) pe.Argument = arg arg = pe } return pe, nil } func incompletePipeExpr(call interface{}, text []byte, pos position) (*ast.PipeExpression, error) { return &ast.PipeExpression{ Call: call.(*ast.CallExpression), BaseNode: base(text, pos), }, nil } func memberexprs(head, tail interface{}, text []byte, pos position) (ast.Expression, error) { res := head.(ast.Expression) for _, prop := range toIfaceSlice(tail) { res = &ast.MemberExpression{ Object: res, Property: prop.(ast.Expression), BaseNode: base(text, pos), } } return res, nil } func memberexpr(object, property interface{}, text []byte, pos position) (*ast.MemberExpression, error) { m := &ast.MemberExpression{ BaseNode: base(text, pos), } if object != nil { m.Object = object.(ast.Expression) } if property != nil { m.Property = property.(*ast.Identifier) } return m, nil } func callexpr(callee, args interface{}, text []byte, pos position) (*ast.CallExpression, error) { c := &ast.CallExpression{ BaseNode: base(text, pos), } if callee != nil { c.Callee = callee.(ast.Expression) } if args != nil { c.Arguments = []ast.Expression{args.(*ast.ObjectExpression)} } return c, nil } func callexprs(head, tail interface{}, text []byte, pos position) (ast.Expression, error) { expr := head.(ast.Expression) for _, i := range toIfaceSlice(tail) { switch elem := i.(type) { case *ast.CallExpression: elem.Callee = expr expr = elem case *ast.MemberExpression: elem.Object = expr expr = elem } } return expr, nil } func arrowfunc(params interface{}, body interface{}, text []byte, pos position) *ast.ArrowFunctionExpression { paramsSlice := toIfaceSlice(params) paramsList := make([]*ast.Property, len(paramsSlice)) for i, p := range paramsSlice { paramsList[i] = p.(*ast.Property) } return &ast.ArrowFunctionExpression{ BaseNode: base(text, pos), Params: paramsList, Body: body.(ast.Node), } } func objectexpr(first, rest interface{}, text []byte, pos position) (*ast.ObjectExpression, error) { props := []*ast.Property{first.(*ast.Property)} if rest != nil { for _, prop := range toIfaceSlice(rest) { props = append(props, prop.(*ast.Property)) } } return &ast.ObjectExpression{ Properties: props, BaseNode: base(text, pos), }, nil } func property(key, value interface{}, text []byte, pos position) (*ast.Property, error) { var v ast.Expression if value != nil { v = value.(ast.Expression) } return &ast.Property{ Key: key.(*ast.Identifier), Value: v, BaseNode: base(text, pos), }, nil } func identifier(text []byte, pos position) (*ast.Identifier, error) { return &ast.Identifier{ Name: string(text), BaseNode: base(text, pos), }, nil } func array(first, rest interface{}, text []byte, pos position) *ast.ArrayExpression { var elements []ast.Expression if first != nil { elements = append(elements, first.(ast.Expression)) } if rest != nil { for _, el := range rest.([]interface{}) { elements = append(elements, el.(ast.Expression)) } } return &ast.ArrayExpression{ Elements: elements, BaseNode: base(text, pos), } } func logicalExpression(head, tails interface{}, text []byte, pos position) (ast.Expression, error) { res := head.(ast.Expression) for _, tail := range toIfaceSlice(tails) { right := toIfaceSlice(tail) res = &ast.LogicalExpression{ Left: res, Right: right[3].(ast.Expression), Operator: right[1].(ast.LogicalOperatorKind), BaseNode: base(text, pos), } } return res, nil } func logicalOp(text []byte) (ast.LogicalOperatorKind, error) { return ast.LogicalOperatorLookup(strings.ToLower(string(text))), nil } func binaryExpression(head, tails interface{}, text []byte, pos position) (ast.Expression, error) { res := head.(ast.Expression) for _, tail := range toIfaceSlice(tails) { right := toIfaceSlice(tail) res = &ast.BinaryExpression{ Left: res, Right: right[3].(ast.Expression), Operator: right[1].(ast.OperatorKind), BaseNode: base(text, pos), } } return res, nil } func unaryExpression(op, argument interface{}, text []byte, pos position) (*ast.UnaryExpression, error) { return &ast.UnaryExpression{ Operator: op.(ast.OperatorKind), Argument: argument.(ast.Expression), BaseNode: base(text, pos), }, nil } func operator(text []byte) (ast.OperatorKind, error) { return ast.OperatorLookup(strings.ToLower(string(text))), nil } func stringLiteral(text []byte, pos position) (*ast.StringLiteral, error) { s, err := strconv.Unquote(string(text)) if err != nil { return nil, err } return &ast.StringLiteral{ BaseNode: base(text, pos), Value: s, }, nil } func pipeLiteral(text []byte, pos position) *ast.PipeLiteral { return &ast.PipeLiteral{ BaseNode: base(text, pos), } } func booleanLiteral(b bool, text []byte, pos position) (*ast.BooleanLiteral, error) { return &ast.BooleanLiteral{ BaseNode: base(text, pos), Value: b, }, nil } func integerLiteral(text []byte, pos position) (*ast.IntegerLiteral, error) { n, err := strconv.ParseInt(string(text), 10, 64) if err != nil { return nil, err } return &ast.IntegerLiteral{ BaseNode: base(text, pos), Value: n, }, nil } func numberLiteral(text []byte, pos position) (*ast.FloatLiteral, error) { n, err := strconv.ParseFloat(string(text), 64) if err != nil { return nil, err } return &ast.FloatLiteral{ BaseNode: base(text, pos), Value: n, }, nil } func regexLiteral(chars interface{}, text []byte, pos position) (*ast.RegexpLiteral, error) { b := new(strings.Builder) for _, char := range toIfaceSlice(chars) { b.Write(char.([]byte)) } r, err := regexp.Compile(b.String()) if err != nil { return nil, err } return &ast.RegexpLiteral{ BaseNode: base(text, pos), Value: r, }, nil } func durationLiteral(text []byte, pos position) (*ast.DurationLiteral, error) { d, err := time.ParseDuration(string(text)) if err != nil { return nil, err } return &ast.DurationLiteral{ BaseNode: base(text, pos), Value: d, }, nil } func datetime(text []byte, pos position) (*ast.DateTimeLiteral, error) { t, err := time.Parse(time.RFC3339Nano, string(text)) if err != nil { return nil, err } return &ast.DateTimeLiteral{ BaseNode: base(text, pos), Value: t, }, nil } func base(text []byte, pos position) *ast.BaseNode { return &ast.BaseNode{ Loc: &ast.SourceLocation{ Start: ast.Position{ Line: pos.line, Column: pos.col, }, End: ast.Position{ Line: pos.line, Column: pos.col + len(text), }, Source: source(text), }, } } func source(text []byte) *string { str := string(text) return &str }