// CALL statement parsing: braced subqueries with the GQL variable-scope // clause, and procedure calls with expression arguments and YIELD.
package parser import ( ) // parseCall dispatches the three CALL forms: CALL { body }, CALL (vars) { // body }, and CALL proc(args) YIELD items. func ( *parser) () (ast.Clause, error) { .i++ // CALL switch .peek().Kind { case TokLBrace: return .parseCallSubquery(nil) case TokLParen: .i++ var []string for .peek().Kind != TokRParen { , := .identName("an imported variable") if != nil { return nil, } = append(, ) if !.acceptTok(TokComma) { break } } if , := .expect(TokRParen, "')'"); != nil { return nil, } return .parseCallSubquery() default: return .parseCallProc() } } // parseCallSubquery parses { body } where body is part (UNION part)*. // Scope-clause imports synthesize a leading importing With in every branch // (the clause shape the Rust binder consumes). func ( *parser) ( []string) (ast.Clause, error) { if , := .expect(TokLBrace, "'{'"); != nil { return nil, } var ast.Query , := .parsePart() if != nil { return nil, } .Parts = append(.Parts, *) for .acceptKw("union") { := ast.UnionDistinct if .acceptKw("all") { = ast.UnionAll } , := .parsePart() if != nil { return nil, } .Union = append(.Union, ) .Parts = append(.Parts, *) } if , := .expect(TokRBrace, "'}'"); != nil { return nil, } if len() > 0 { := make([]ast.ReturnItem, len()) for , := range { [] = ast.ReturnItem{Expr: &ast.Var{Name: }} } for := range .Parts { := &ast.With{Proj: ast.Projection{Items: }} .Parts[].Clauses = append([]ast.Clause{}, .Parts[].Clauses...) } } return &ast.CallSubquery{Query: , Imports: }, nil } // parseCallProc is: CALL name[.name]*(args) YIELD field [AS alias][, ...]. // Args are general expressions; constant ones resolve at plan time and the // rest evaluate per input row (a correlated call). func ( *parser) () (ast.Clause, error) { , := .identName("a procedure name") if != nil { return nil, } := for .peek().Kind == TokDot { .i++ , := .identName("a procedure name segment") if != nil { return nil, } += "." + } if , := .expect(TokLParen, "'('"); != nil { return nil, } var []ast.Expr for .peek().Kind != TokRParen { , := .parseExpr() if != nil { return nil, } = append(, ) if !.acceptTok(TokComma) { break } } if , := .expect(TokRParen, "')'"); != nil { return nil, } if := .expectKw("yield"); != nil { return nil, } var []ast.YieldItem for { , := .identName("a YIELD field") if != nil { return nil, } := ast.YieldItem{Field: } if .acceptKw("as") { , := .identName("an alias") if != nil { return nil, } .Alias = } = append(, ) if !.acceptTok(TokComma) { break } } return &ast.CallProc{Proc: , Args: , Yields: }, nil } // parseLiteralTok reads one literal token (int, float, string, true, // false, null, $param). func ( *parser) () (ast.Literal, *Error) { := .peek() switch { case .Kind == TokInt: .i++ , := strconv.ParseInt(.Text, 10, 64) if != nil { // 2^63 is MinInt64's magnitude: it overflows as a positive but // is valid under a unary minus. Read it as MinInt64 so the // unary-minus fold in the Pratt parser produces MinInt64 // instead of re-overflowing (mirrors the Rust parser exactly). if .Text == "9223372036854775808" { return ast.IntLit(math.MinInt64), nil } return ast.Literal{}, errf(.Pos, "bad integer %q", .Text) } return ast.IntLit(), nil case .Kind == TokFloat: .i++ , := strconv.ParseFloat(.Text, 64) if != nil { return ast.Literal{}, errf(.Pos, "bad float %q", .Text) } return ast.FloatLit(), nil case .Kind == TokStr: .i++ return ast.StrLit(.Text), nil case .Kind == TokParam: .i++ return ast.NamedParamLit(.Text), nil case kwIs(, "true"): .i++ return ast.BoolLit(true), nil case kwIs(, "false"): .i++ return ast.BoolLit(false), nil case kwIs(, "null"): .i++ return ast.NullLit(), nil } return ast.Literal{}, errf(.Pos, "expected a literal, found %q", .Text) }