parseContextTokens is the core context-aware parsing routine. It takes a preprocessed []models.TokenWithSpan and respects ctx for cancellation.
(ctx context.Context, tokens []models.TokenWithSpan)
| 444 | // parseContextTokens is the core context-aware parsing routine. It takes |
| 445 | // a preprocessed []models.TokenWithSpan and respects ctx for cancellation. |
| 446 | func (p *Parser) parseContextTokens(ctx context.Context, tokens []models.TokenWithSpan) (*ast.AST, error) { |
| 447 | // Check context before starting |
| 448 | if err := ctx.Err(); err != nil { |
| 449 | return nil, err |
| 450 | } |
| 451 | |
| 452 | // Store context for use during parsing |
| 453 | p.ctx = ctx |
| 454 | defer func() { p.ctx = nil }() // Clear context when done |
| 455 | |
| 456 | p.tokens = tokens |
| 457 | p.currentPos = 0 |
| 458 | if len(tokens) > 0 { |
| 459 | p.currentToken = tokens[0] |
| 460 | } |
| 461 | |
| 462 | // Get a pre-allocated AST from the pool |
| 463 | result := ast.NewAST() |
| 464 | |
| 465 | // Pre-allocate statements slice based on a reasonable estimate |
| 466 | estimatedStmts := 1 // Most SQL queries have just one statement |
| 467 | if len(tokens) > 100 { |
| 468 | estimatedStmts = 2 // For larger inputs, allocate more |
| 469 | } |
| 470 | result.Statements = make([]ast.Statement, 0, estimatedStmts) |
| 471 | |
| 472 | // Parse statements using Type (int) comparisons for speed |
| 473 | for p.currentPos < len(tokens) && !p.isType(models.TokenTypeEOF) { |
| 474 | // Check context before each statement |
| 475 | if err := ctx.Err(); err != nil { |
| 476 | // Clean up the AST on error |
| 477 | ast.ReleaseAST(result) |
| 478 | // Context cancellation is not a parsing error, return the context error directly |
| 479 | return nil, fmt.Errorf("parsing cancelled: %w", err) |
| 480 | } |
| 481 | |
| 482 | // Skip semicolons between statements |
| 483 | if p.isType(models.TokenTypeSemicolon) { |
| 484 | p.advance() |
| 485 | continue |
| 486 | } |
| 487 | |
| 488 | stmt, err := p.parseStatement() |
| 489 | if err != nil { |
| 490 | // Clean up the AST on error |
| 491 | ast.ReleaseAST(result) |
| 492 | return nil, err |
| 493 | } |
| 494 | result.Statements = append(result.Statements, stmt) |
| 495 | |
| 496 | // Optionally consume semicolon after statement |
| 497 | if p.isType(models.TokenTypeSemicolon) { |
| 498 | p.advance() |
| 499 | } |
| 500 | } |
| 501 | |
| 502 | // Check if we got any statements |
| 503 | if len(result.Statements) == 0 { |
no test coverage detected