Tokenize procedural SQL text into a token stream. The tokenizer is keyword-aware: it recognizes procedural keywords and captures everything else as `SqlFragment` tokens. String literals are preserved (not split on keywords inside strings).
(input: &str)
| 79 | /// captures everything else as `SqlFragment` tokens. String literals are |
| 80 | /// preserved (not split on keywords inside strings). |
| 81 | pub fn tokenize(input: &str) -> Result<Vec<Token>, ProceduralError> { |
| 82 | let mut tokens = Vec::new(); |
| 83 | let bytes = input.as_bytes(); |
| 84 | let len = bytes.len(); |
| 85 | let mut i = 0; |
| 86 | |
| 87 | while i < len { |
| 88 | // Skip whitespace. |
| 89 | if bytes[i].is_ascii_whitespace() { |
| 90 | i += 1; |
| 91 | continue; |
| 92 | } |
| 93 | |
| 94 | // Skip SQL comments. |
| 95 | if i + 1 < len && bytes[i] == b'-' && bytes[i + 1] == b'-' { |
| 96 | while i < len && bytes[i] != b'\n' { |
| 97 | i += 1; |
| 98 | } |
| 99 | continue; |
| 100 | } |
| 101 | |
| 102 | // Semicolon. |
| 103 | if bytes[i] == b';' { |
| 104 | tokens.push(Token::Semicolon); |
| 105 | i += 1; |
| 106 | continue; |
| 107 | } |
| 108 | |
| 109 | // `:=` assignment. |
| 110 | if i + 1 < len && bytes[i] == b':' && bytes[i + 1] == b'=' { |
| 111 | tokens.push(Token::Assign); |
| 112 | i += 2; |
| 113 | continue; |
| 114 | } |
| 115 | |
| 116 | // `..` range operator. |
| 117 | if i + 1 < len && bytes[i] == b'.' && bytes[i + 1] == b'.' { |
| 118 | tokens.push(Token::DotDot); |
| 119 | i += 2; |
| 120 | continue; |
| 121 | } |
| 122 | |
| 123 | // String literal. |
| 124 | if bytes[i] == b'\'' { |
| 125 | let (lit, end) = read_string_literal(input, i)?; |
| 126 | tokens.push(Token::StringLit(lit)); |
| 127 | i = end; |
| 128 | continue; |
| 129 | } |
| 130 | |
| 131 | // Number literal. |
| 132 | if bytes[i].is_ascii_digit() { |
| 133 | let start = i; |
| 134 | while i < len { |
| 135 | if bytes[i].is_ascii_digit() { |
| 136 | i += 1; |
| 137 | } else if bytes[i] == b'.' { |
| 138 | // Check for `..` (range operator) — don't consume the dot. |