edmondop opened a new issue, #2612:
URL: https://github.com/apache/datafusion-sqlparser-rs/issues/2612

   Follow-up to the discussion in #2610.
   
   **Problem**
   
   The parser lets a dialect take over at specific points (`parse_prefix`, 
`parse_infix`, `parse_statement`): it receives `&mut Parser`, and returning 
`None` falls back to the default. The tokenizer has no equivalent. A dialect 
can only answer yes/no questions the tokenizer asks (`is_identifier_part`, 
`supports_triple_quoted_string`, and about 20 others), so it can toggle 
existing lexemes but not add new ones.
   
   Dialect-specific lexing therefore lives in `Tokenizer::next_token` behind 
`dialect_of!` checks:
   
   - `b'...'` byte strings (BigQuery, MySQL) and bit strings (Postgres)
   - `r'...'` raw strings (BigQuery)
   - `//` as a line comment (Snowflake) or integer division (DuckDB)
   - `#` as a line comment (Snowflake, BigQuery, MySQL, Hive)
   - `\r` ending a line comment (Postgres)
   
   A downstream dialect cannot add a branch like these. Its only option is 
`tokenize_with_location_into_buf_with_mapper`, which rewrites one finished 
token at a time and cannot see characters or merge tokens.
   
   Example: in dbt models, a Jinja comment `{# ... #}` breaks under every 
dialect that treats `#` as a line comment, because the tokenizer consumes the 
rest of the line, including `#}`.
   
   **Proposal**
   
   1. Expose the character stream that `next_token` reads from (today the 
private `State`: `peek`, `next`, `location`), or a narrow public wrapper around 
it.
   2. Add a hook with the same contract as the parser hooks:
      ```rust
      /// Dialect-specific tokenizer override, called before the default rules.
      ///
      /// If `None` is returned, falls back to the default behavior.
      fn next_token(
          &self,
          _chars: &mut State,
          _prev_token: Option<&Token>,
      ) -> Option<Result<Token, TokenizerError>> {
          None
      }
      ```
   3. As a separate step, move the existing `dialect_of!` branches into the 
hook implementations of their dialects. That would show the hook covers syntax 
already in the tree, and would remove `dialect_of!` from the tokenizer.
   
   **Open questions**
   
   - Lookahead: a hook that returns `None` must not consume input. `State` 
wraps `Peekable<Chars>` and allows one character of lookahead. Should `State` 
be `Clone`, or offer `peek_nth`?
   - `Token` is a closed enum, so a dialect-specific lexeme must map to an 
existing variant (`Word`, `CustomBinaryOperator`, ...). Is that enough, or 
should there be a `Token::Custom` variant?
   - Cost: one extra dynamic call per token. The tokenizer already calls 
several dialect methods per token, and `sqlparser_bench` can measure the 
difference.
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to