plum
git clone https://git.pyrossh.dev/plum
A statically typed, imperative programming language inspired by rust, python
plum-tooling/tree-sitter-plum/src/scanner.c
| f997bee | 1 | #include "tree_sitter/array.h" |
| f997bee | 2 | #include "tree_sitter/parser.h" |
| f997bee | 3 | |
| f997bee | 4 | #include <assert.h> |
| f997bee | 5 | #include <stdint.h> |
| f997bee | 6 | #include <stdio.h> |
| f997bee | 7 | #include <string.h> |
| 4bd2c1c | 8 | |
| 4bd2c1c | 9 | enum TokenType { |
| f997bee | 10 | NEWLINE, |
| f997bee | 11 | INDENT, |
| f997bee | 12 | DEDENT, |
| f997bee | 13 | STRING_START, |
| f997bee | 14 | STRING_CONTENT, |
| f997bee | 15 | ESCAPE_INTERPOLATION, |
| f997bee | 16 | STRING_END, |
| f997bee | 17 | COMMENT, |
| f997bee | 18 | CLOSE_PAREN, |
| f997bee | 19 | CLOSE_BRACKET, |
| f997bee | 20 | CLOSE_BRACE, |
| 3971d41 | 21 | ARM_BREAK, |
| 4bd2c1c | 22 | }; |
| 4bd2c1c | 23 | |
| f997bee | 24 | typedef enum { |
| f997bee | 25 | SingleQuote = 1 << 0, |
| f997bee | 26 | DoubleQuote = 1 << 1, |
| f997bee | 27 | BackQuote = 1 << 2, |
| f997bee | 28 | Raw = 1 << 3, |
| f997bee | 29 | Format = 1 << 4, |
| f997bee | 30 | Triple = 1 << 5, |
| f997bee | 31 | Bytes = 1 << 6, |
| f997bee | 32 | } Flags; |
| f997bee | 33 | |
| f997bee | 34 | typedef struct { |
| f997bee | 35 | char flags; |
| f997bee | 36 | } Delimiter; |
| f997bee | 37 | |
| f997bee | 38 | static inline Delimiter new_delimiter() { return (Delimiter){0}; } |
| f997bee | 39 | |
| f997bee | 40 | static inline bool is_format(Delimiter *delimiter) { return delimiter->flags & Format; } |
| f997bee | 41 | |
| f997bee | 42 | static inline bool is_raw(Delimiter *delimiter) { return delimiter->flags & Raw; } |
| f997bee | 43 | |
| f997bee | 44 | static inline bool is_triple(Delimiter *delimiter) { return delimiter->flags & Triple; } |
| f997bee | 45 | |
| f997bee | 46 | static inline bool is_bytes(Delimiter *delimiter) { return delimiter->flags & Bytes; } |
| f997bee | 47 | |
| f997bee | 48 | static inline int32_t end_character(Delimiter *delimiter) { |
| f997bee | 49 | if (delimiter->flags & SingleQuote) { |
| f997bee | 50 | return '\''; |
| f997bee | 51 | } |
| f997bee | 52 | if (delimiter->flags & DoubleQuote) { |
| f997bee | 53 | return '"'; |
| f997bee | 54 | } |
| f997bee | 55 | if (delimiter->flags & BackQuote) { |
| f997bee | 56 | return '`'; |
| f997bee | 57 | } |
| f997bee | 58 | return 0; |
| f997bee | 59 | } |
| f997bee | 60 | |
| f997bee | 61 | static inline void set_format(Delimiter *delimiter) { delimiter->flags |= Format; } |
| f997bee | 62 | |
| f997bee | 63 | static inline void set_raw(Delimiter *delimiter) { delimiter->flags |= Raw; } |
| f997bee | 64 | |
| f997bee | 65 | static inline void set_triple(Delimiter *delimiter) { delimiter->flags |= Triple; } |
| f997bee | 66 | |
| f997bee | 67 | static inline void set_bytes(Delimiter *delimiter) { delimiter->flags |= Bytes; } |
| f997bee | 68 | |
| f997bee | 69 | static inline void set_end_character(Delimiter *delimiter, int32_t character) { |
| f997bee | 70 | switch (character) { |
| f997bee | 71 | case '\'': |
| f997bee | 72 | delimiter->flags |= SingleQuote; |
| f997bee | 73 | break; |
| f997bee | 74 | case '"': |
| f997bee | 75 | delimiter->flags |= DoubleQuote; |
| f997bee | 76 | break; |
| f997bee | 77 | case '`': |
| f997bee | 78 | delimiter->flags |= BackQuote; |
| f997bee | 79 | break; |
| f997bee | 80 | default: |
| f997bee | 81 | assert(false); |
| f997bee | 82 | } |
| f997bee | 83 | } |
| f997bee | 84 | |
| f997bee | 85 | typedef struct { |
| f997bee | 86 | Array(uint16_t) indents; |
| f997bee | 87 | Array(Delimiter) delimiters; |
| f997bee | 88 | bool inside_f_string; |
| f997bee | 89 | } Scanner; |
| f997bee | 90 | |
| f997bee | 91 | static inline void advance(TSLexer *lexer) { lexer->advance(lexer, false); } |
| f997bee | 92 | |
| f997bee | 93 | static inline void skip(TSLexer *lexer) { lexer->advance(lexer, true); } |
| f997bee | 94 | |
| f997bee | 95 | bool tree_sitter_plum_external_scanner_scan(void *payload, TSLexer *lexer, const bool *valid_symbols) { |
| f997bee | 96 | Scanner *scanner = (Scanner *)payload; |
| f997bee | 97 | |
| f997bee | 98 | bool error_recovery_mode = valid_symbols[STRING_CONTENT] && valid_symbols[INDENT]; |
| f997bee | 99 | bool within_brackets = valid_symbols[CLOSE_BRACE] || valid_symbols[CLOSE_PAREN] || valid_symbols[CLOSE_BRACKET]; |
| f997bee | 100 | |
| f997bee | 101 | bool advanced_once = false; |
| f997bee | 102 | if (valid_symbols[ESCAPE_INTERPOLATION] && scanner->delimiters.size > 0 && |
| f997bee | 103 | (lexer->lookahead == '{' || lexer->lookahead == '}') && !error_recovery_mode) { |
| f997bee | 104 | Delimiter *delimiter = array_back(&scanner->delimiters); |
| f997bee | 105 | if (is_format(delimiter)) { |
| f997bee | 106 | lexer->mark_end(lexer); |
| f997bee | 107 | bool is_left_brace = lexer->lookahead == '{'; |
| f997bee | 108 | advance(lexer); |
| f997bee | 109 | advanced_once = true; |
| f997bee | 110 | if ((lexer->lookahead == '{' && is_left_brace) || (lexer->lookahead == '}' && !is_left_brace)) { |
| f997bee | 111 | advance(lexer); |
| f997bee | 112 | lexer->mark_end(lexer); |
| f997bee | 113 | lexer->result_symbol = ESCAPE_INTERPOLATION; |
| f997bee | 114 | return true; |
| f997bee | 115 | } |
| f997bee | 116 | return false; |
| f997bee | 117 | } |
| f997bee | 118 | } |
| f997bee | 119 | |
| f997bee | 120 | if (valid_symbols[STRING_CONTENT] && scanner->delimiters.size > 0 && !error_recovery_mode) { |
| f997bee | 121 | Delimiter *delimiter = array_back(&scanner->delimiters); |
| f997bee | 122 | int32_t end_char = end_character(delimiter); |
| f997bee | 123 | bool has_content = advanced_once; |
| f997bee | 124 | while (lexer->lookahead) { |
| f997bee | 125 | if ((advanced_once || lexer->lookahead == '{' || lexer->lookahead == '}') && is_format(delimiter)) { |
| f997bee | 126 | lexer->mark_end(lexer); |
| f997bee | 127 | lexer->result_symbol = STRING_CONTENT; |
| f997bee | 128 | return has_content; |
| f997bee | 129 | } |
| f997bee | 130 | if (lexer->lookahead == '\\') { |
| f997bee | 131 | if (is_raw(delimiter)) { |
| f997bee | 132 | // Step over the backslash. |
| f997bee | 133 | advance(lexer); |
| f997bee | 134 | // Step over any escaped quotes. |
| f997bee | 135 | if (lexer->lookahead == end_character(delimiter) || lexer->lookahead == '\\') { |
| f997bee | 136 | advance(lexer); |
| f997bee | 137 | } |
| f997bee | 138 | // Step over newlines |
| f997bee | 139 | if (lexer->lookahead == '\r') { |
| f997bee | 140 | advance(lexer); |
| f997bee | 141 | if (lexer->lookahead == '\n') { |
| f997bee | 142 | advance(lexer); |
| f997bee | 143 | } |
| f997bee | 144 | } else if (lexer->lookahead == '\n') { |
| f997bee | 145 | advance(lexer); |
| f997bee | 146 | } |
| f997bee | 147 | continue; |
| f997bee | 148 | } |
| f997bee | 149 | if (is_bytes(delimiter)) { |
| f997bee | 150 | lexer->mark_end(lexer); |
| f997bee | 151 | advance(lexer); |
| f997bee | 152 | if (lexer->lookahead == 'N' || lexer->lookahead == 'u' || lexer->lookahead == 'U') { |
| f997bee | 153 | // In bytes string, \N{...}, \uXXXX and \UXXXXXXXX are |
| f997bee | 154 | // not escape sequences |
| f997bee | 155 | // https://docs.plum.org/3/reference/lexical_analysis.html#string-and-bytes-literals |
| f997bee | 156 | advance(lexer); |
| f997bee | 157 | } else { |
| f997bee | 158 | lexer->result_symbol = STRING_CONTENT; |
| f997bee | 159 | return has_content; |
| f997bee | 160 | } |
| f997bee | 161 | } else { |
| f997bee | 162 | lexer->mark_end(lexer); |
| f997bee | 163 | lexer->result_symbol = STRING_CONTENT; |
| f997bee | 164 | return has_content; |
| f997bee | 165 | } |
| f997bee | 166 | } else if (lexer->lookahead == end_char) { |
| f997bee | 167 | if (is_triple(delimiter)) { |
| f997bee | 168 | lexer->mark_end(lexer); |
| f997bee | 169 | advance(lexer); |
| f997bee | 170 | if (lexer->lookahead == end_char) { |
| f997bee | 171 | advance(lexer); |
| f997bee | 172 | if (lexer->lookahead == end_char) { |
| f997bee | 173 | if (has_content) { |
| f997bee | 174 | lexer->result_symbol = STRING_CONTENT; |
| f997bee | 175 | } else { |
| f997bee | 176 | advance(lexer); |
| f997bee | 177 | lexer->mark_end(lexer); |
| f997bee | 178 | array_pop(&scanner->delimiters); |
| f997bee | 179 | lexer->result_symbol = STRING_END; |
| f997bee | 180 | scanner->inside_f_string = false; |
| f997bee | 181 | } |
| f997bee | 182 | return true; |
| f997bee | 183 | } |
| f997bee | 184 | lexer->mark_end(lexer); |
| f997bee | 185 | lexer->result_symbol = STRING_CONTENT; |
| f997bee | 186 | return true; |
| f997bee | 187 | } |
| f997bee | 188 | lexer->mark_end(lexer); |
| f997bee | 189 | lexer->result_symbol = STRING_CONTENT; |
| f997bee | 190 | return true; |
| f997bee | 191 | } |
| f997bee | 192 | if (has_content) { |
| f997bee | 193 | lexer->result_symbol = STRING_CONTENT; |
| f997bee | 194 | } else { |
| f997bee | 195 | advance(lexer); |
| f997bee | 196 | array_pop(&scanner->delimiters); |
| f997bee | 197 | lexer->result_symbol = STRING_END; |
| f997bee | 198 | scanner->inside_f_string = false; |
| f997bee | 199 | } |
| f997bee | 200 | lexer->mark_end(lexer); |
| f997bee | 201 | return true; |
| f997bee | 202 | |
| f997bee | 203 | } else if (lexer->lookahead == '\n' && has_content && !is_triple(delimiter)) { |
| f997bee | 204 | return false; |
| f997bee | 205 | } |
| f997bee | 206 | advance(lexer); |
| f997bee | 207 | has_content = true; |
| f997bee | 208 | } |
| f997bee | 209 | } |
| f997bee | 210 | |
| f997bee | 211 | lexer->mark_end(lexer); |
| f997bee | 212 | |
| f997bee | 213 | bool found_end_of_line = false; |
| f997bee | 214 | uint32_t indent_length = 0; |
| f997bee | 215 | int32_t first_comment_indent_length = -1; |
| f997bee | 216 | for (;;) { |
| f997bee | 217 | if (lexer->lookahead == '\n') { |
| f997bee | 218 | found_end_of_line = true; |
| f997bee | 219 | indent_length = 0; |
| f997bee | 220 | skip(lexer); |
| f997bee | 221 | } else if (lexer->lookahead == ' ') { |
| f997bee | 222 | indent_length++; |
| f997bee | 223 | skip(lexer); |
| f997bee | 224 | } else if (lexer->lookahead == '\r' || lexer->lookahead == '\f') { |
| f997bee | 225 | indent_length = 0; |
| f997bee | 226 | skip(lexer); |
| f997bee | 227 | } else if (lexer->lookahead == '\t') { |
| f997bee | 228 | indent_length += 8; |
| f997bee | 229 | skip(lexer); |
| f997bee | 230 | } else if (lexer->lookahead == '#' && (valid_symbols[INDENT] || valid_symbols[DEDENT] || |
| 660674c | 231 | valid_symbols[NEWLINE])) { |
| f997bee | 232 | // If we haven't found an EOL yet, |
| f997bee | 233 | // then this is a comment after an expression: |
| f997bee | 234 | // foo = bar # comment |
| f997bee | 235 | // Just return, since we don't want to generate an indent/dedent |
| f997bee | 236 | // token. |
| f997bee | 237 | if (!found_end_of_line) { |
| f997bee | 238 | return false; |
| f997bee | 239 | } |
| f997bee | 240 | if (first_comment_indent_length == -1) { |
| f997bee | 241 | first_comment_indent_length = (int32_t)indent_length; |
| f997bee | 242 | } |
| f997bee | 243 | while (lexer->lookahead && lexer->lookahead != '\n') { |
| f997bee | 244 | skip(lexer); |
| f997bee | 245 | } |
| f997bee | 246 | skip(lexer); |
| f997bee | 247 | indent_length = 0; |
| f997bee | 248 | } else if (lexer->lookahead == '\\') { |
| f997bee | 249 | skip(lexer); |
| f997bee | 250 | if (lexer->lookahead == '\r') { |
| f997bee | 251 | skip(lexer); |
| f997bee | 252 | } |
| f997bee | 253 | if (lexer->lookahead == '\n' || lexer->eof(lexer)) { |
| f997bee | 254 | skip(lexer); |
| f997bee | 255 | } else { |
| f997bee | 256 | return false; |
| f997bee | 257 | } |
| f997bee | 258 | } else if (lexer->eof(lexer)) { |
| f997bee | 259 | indent_length = 0; |
| f997bee | 260 | found_end_of_line = true; |
| f997bee | 261 | break; |
| f997bee | 262 | } else { |
| f997bee | 263 | break; |
| f997bee | 264 | } |
| f997bee | 265 | } |
| f997bee | 266 | |
| 35af6cf | 267 | // A closure literal's indented body, used directly as a call argument, can be |
| 35af6cf | 268 | // immediately followed by a closing bracket with no intervening newline at all |
| 35af6cf | 269 | // (`each(|v|\n v)`), since the body's last statement and the bracket that ends |
| 35af6cf | 270 | // the enclosing call are on the same line. The loop above only ever sets |
| 35af6cf | 271 | // `found_end_of_line` by actually consuming a `\n`, so that shape reaches here |
| 35af6cf | 272 | // with `found_end_of_line == false` and no dedent is ever considered — even |
| 35af6cf | 273 | // though the grammar, mid-parse, is already asking for one (`valid_symbols[DEDENT]` |
| 35af6cf | 274 | // is true here exactly when a `body` node needs to close before the bracket can be |
| 35af6cf | 275 | // shifted). Treat that specific situation as equivalent to "found a blank new |
| 35af6cf | 276 | // line" for dedent purposes only: there's no partial line indentation to measure |
| 35af6cf | 277 | // (we're mid-expression, not at a line start), so it behaves as `indent_length == 0`, |
| 35af6cf | 278 | // which is `< current_indent_length` for any real indented block. Never treat it as |
| 35af6cf | 279 | // a NEWLINE — the grammar isn't expecting one at a raw closing bracket. |
| 35af6cf | 280 | // |
| 35af6cf | 281 | // (A comma continuing the argument list on the same line, e.g. |
| 35af6cf | 282 | // `each(|v|\n v, extra)`, is the same underlying shape but isn't handled here: |
| 35af6cf | 283 | // at that exact position the parser's automaton doesn't have `valid_symbols[DEDENT]` |
| 35af6cf | 284 | // true, so there's no signal to act on without guessing. Out of scope for now — |
| 35af6cf | 285 | // put the closure last, or give it its own line, to work around it.) |
| 35af6cf | 286 | bool at_closing_bracket_without_newline = |
| 35af6cf | 287 | !found_end_of_line && within_brackets && valid_symbols[DEDENT] && |
| 35af6cf | 288 | (lexer->lookahead == ')' || lexer->lookahead == ']' || lexer->lookahead == '}'); |
| 35af6cf | 289 | if (at_closing_bracket_without_newline) { |
| 35af6cf | 290 | indent_length = 0; |
| 35af6cf | 291 | } |
| 35af6cf | 292 | |
| 35af6cf | 293 | if (found_end_of_line || at_closing_bracket_without_newline) { |
| f997bee | 294 | if (scanner->indents.size > 0) { |
| f997bee | 295 | uint16_t current_indent_length = *array_back(&scanner->indents); |
| f997bee | 296 | |
| f997bee | 297 | if (valid_symbols[INDENT] && indent_length > current_indent_length) { |
| f997bee | 298 | array_push(&scanner->indents, indent_length); |
| f997bee | 299 | lexer->result_symbol = INDENT; |
| f997bee | 300 | return true; |
| f997bee | 301 | } |
| f997bee | 302 | |
| f997bee | 303 | bool next_tok_is_string_start = |
| f997bee | 304 | lexer->lookahead == '\"' || lexer->lookahead == '\'' || lexer->lookahead == '`'; |
| f997bee | 305 | |
| f997bee | 306 | if ((valid_symbols[DEDENT] || |
| f997bee | 307 | (!valid_symbols[NEWLINE] && !(valid_symbols[STRING_START] && next_tok_is_string_start) && |
| f997bee | 308 | !within_brackets)) && |
| f997bee | 309 | indent_length < current_indent_length && !scanner->inside_f_string && |
| f997bee | 310 | |
| f997bee | 311 | // Wait to create a dedent token until we've consumed any |
| f997bee | 312 | // comments |
| f997bee | 313 | // whose indentation matches the current block. |
| f997bee | 314 | first_comment_indent_length < (int32_t)current_indent_length) { |
| f997bee | 315 | array_pop(&scanner->indents); |
| f997bee | 316 | lexer->result_symbol = DEDENT; |
| f997bee | 317 | return true; |
| f997bee | 318 | } |
| 3971d41 | 319 | |
| 3971d41 | 320 | // `guard_case` arms are subject-less match clauses that all start with the |
| 3971d41 | 321 | // same `|` token that also denotes the bitwise-or operator (and, for an |
| 3971d41 | 322 | // inline expression body, could otherwise be mistaken for a continuation of |
| 3971d41 | 323 | // that body's own binary expression, e.g. `| x > y => 1` immediately |
| 3971d41 | 324 | // followed by `| x == y => 0` parsing as `1 | x == y`). Since the grammar |
| 3971d41 | 325 | // never asks for `_newline`/`_dedent` between two arms at the *same* |
| 3971d41 | 326 | // indentation (nothing to indent into, nothing to dedent out of), there is |
| 3971d41 | 327 | // otherwise no signal at all marking that boundary — the raw `\r?\n` is a |
| 3971d41 | 328 | // grammar-level extra and is silently transparent. ARM_BREAK plugs that gap: |
| 3971d41 | 329 | // requested only from the guard_case-repeat position, it fires whenever the |
| 3971d41 | 330 | // next significant token starts a fresh line at exactly the current block's |
| 3971d41 | 331 | // indentation, forcing the parser to end the current arm there instead of |
| 3971d41 | 332 | // extending its expression across the line boundary. |
| 3971d41 | 333 | if (valid_symbols[ARM_BREAK] && found_end_of_line && !error_recovery_mode && |
| 3971d41 | 334 | indent_length == current_indent_length) { |
| 3971d41 | 335 | lexer->result_symbol = ARM_BREAK; |
| 3971d41 | 336 | return true; |
| 3971d41 | 337 | } |
| f997bee | 338 | } |
| f997bee | 339 | |
| 35af6cf | 340 | if (found_end_of_line && valid_symbols[NEWLINE] && !error_recovery_mode) { |
| f997bee | 341 | lexer->result_symbol = NEWLINE; |
| f997bee | 342 | return true; |
| f997bee | 343 | } |
| f997bee | 344 | } |
| f997bee | 345 | |
| f997bee | 346 | if (first_comment_indent_length == -1 && valid_symbols[STRING_START]) { |
| f997bee | 347 | Delimiter delimiter = new_delimiter(); |
| f997bee | 348 | |
| f997bee | 349 | bool has_flags = false; |
| 0d43379 | 350 | // while (lexer->lookahead) { |
| 0d43379 | 351 | // if (lexer->lookahead == 'f' || lexer->lookahead == 'F') { |
| 0d43379 | 352 | // set_format(&delimiter); |
| 0d43379 | 353 | // } else if (lexer->lookahead == 'r' || lexer->lookahead == 'R') { |
| 0d43379 | 354 | // set_raw(&delimiter); |
| 0d43379 | 355 | // } else if (lexer->lookahead == 'b' || lexer->lookahead == 'B') { |
| 0d43379 | 356 | // set_bytes(&delimiter); |
| 0d43379 | 357 | // } else if (lexer->lookahead != 'u' && lexer->lookahead != 'U') { |
| 0d43379 | 358 | // break; |
| 0d43379 | 359 | // } |
| 0d43379 | 360 | // has_flags = true; |
| 0d43379 | 361 | // advance(lexer); |
| 0d43379 | 362 | // } |
| f997bee | 363 | |
| f997bee | 364 | if (lexer->lookahead == '`') { |
| f997bee | 365 | set_end_character(&delimiter, '`'); |
| f997bee | 366 | advance(lexer); |
| f997bee | 367 | lexer->mark_end(lexer); |
| f997bee | 368 | } else if (lexer->lookahead == '\'') { |
| f997bee | 369 | set_end_character(&delimiter, '\''); |
| f997bee | 370 | advance(lexer); |
| f997bee | 371 | lexer->mark_end(lexer); |
| f997bee | 372 | if (lexer->lookahead == '\'') { |
| f997bee | 373 | advance(lexer); |
| f997bee | 374 | if (lexer->lookahead == '\'') { |
| f997bee | 375 | advance(lexer); |
| f997bee | 376 | lexer->mark_end(lexer); |
| f997bee | 377 | set_triple(&delimiter); |
| f997bee | 378 | } |
| f997bee | 379 | } |
| f997bee | 380 | } else if (lexer->lookahead == '"') { |
| 0d43379 | 381 | set_format(&delimiter); |
| f997bee | 382 | set_end_character(&delimiter, '"'); |
| f997bee | 383 | advance(lexer); |
| f997bee | 384 | lexer->mark_end(lexer); |
| f997bee | 385 | if (lexer->lookahead == '"') { |
| f997bee | 386 | advance(lexer); |
| f997bee | 387 | if (lexer->lookahead == '"') { |
| f997bee | 388 | advance(lexer); |
| f997bee | 389 | lexer->mark_end(lexer); |
| f997bee | 390 | set_triple(&delimiter); |
| f997bee | 391 | } |
| f997bee | 392 | } |
| f997bee | 393 | } |
| f997bee | 394 | |
| f997bee | 395 | if (end_character(&delimiter)) { |
| f997bee | 396 | array_push(&scanner->delimiters, delimiter); |
| f997bee | 397 | lexer->result_symbol = STRING_START; |
| f997bee | 398 | scanner->inside_f_string = is_format(&delimiter); |
| f997bee | 399 | return true; |
| f997bee | 400 | } |
| f997bee | 401 | if (has_flags) { |
| f997bee | 402 | return false; |
| f997bee | 403 | } |
| f997bee | 404 | } |
| f997bee | 405 | |
| f997bee | 406 | return false; |
| f997bee | 407 | } |
| f997bee | 408 | |
| f997bee | 409 | unsigned tree_sitter_plum_external_scanner_serialize(void *payload, char *buffer) { |
| f997bee | 410 | Scanner *scanner = (Scanner *)payload; |
| f997bee | 411 | |
| f997bee | 412 | size_t size = 0; |
| f997bee | 413 | |
| f997bee | 414 | buffer[size++] = (char)scanner->inside_f_string; |
| f997bee | 415 | |
| f997bee | 416 | size_t delimiter_count = scanner->delimiters.size; |
| f997bee | 417 | if (delimiter_count > UINT8_MAX) { |
| f997bee | 418 | delimiter_count = UINT8_MAX; |
| f997bee | 419 | } |
| f997bee | 420 | buffer[size++] = (char)delimiter_count; |
| f997bee | 421 | |
| f997bee | 422 | if (delimiter_count > 0) { |
| f997bee | 423 | memcpy(&buffer[size], scanner->delimiters.contents, delimiter_count); |
| f997bee | 424 | } |
| f997bee | 425 | size += delimiter_count; |
| f997bee | 426 | |
| f997bee | 427 | uint32_t iter = 1; |
| f997bee | 428 | for (; iter < scanner->indents.size && size < TREE_SITTER_SERIALIZATION_BUFFER_SIZE; ++iter) { |
| f997bee | 429 | buffer[size++] = (char)*array_get(&scanner->indents, iter); |
| f997bee | 430 | } |
| f997bee | 431 | |
| f997bee | 432 | return size; |
| f997bee | 433 | } |
| f997bee | 434 | |
| f997bee | 435 | void tree_sitter_plum_external_scanner_deserialize(void *payload, const char *buffer, unsigned length) { |
| f997bee | 436 | Scanner *scanner = (Scanner *)payload; |
| f997bee | 437 | |
| f997bee | 438 | array_delete(&scanner->delimiters); |
| f997bee | 439 | array_delete(&scanner->indents); |
| f997bee | 440 | array_push(&scanner->indents, 0); |
| f997bee | 441 | |
| f997bee | 442 | if (length > 0) { |
| f997bee | 443 | size_t size = 0; |
| f997bee | 444 | |
| f997bee | 445 | scanner->inside_f_string = (bool)buffer[size++]; |
| f997bee | 446 | |
| f997bee | 447 | size_t delimiter_count = (uint8_t)buffer[size++]; |
| f997bee | 448 | if (delimiter_count > 0) { |
| f997bee | 449 | array_reserve(&scanner->delimiters, delimiter_count); |
| f997bee | 450 | scanner->delimiters.size = delimiter_count; |
| f997bee | 451 | memcpy(scanner->delimiters.contents, &buffer[size], delimiter_count); |
| f997bee | 452 | size += delimiter_count; |
| f997bee | 453 | } |
| f997bee | 454 | |
| f997bee | 455 | for (; size < length; size++) { |
| f997bee | 456 | array_push(&scanner->indents, (unsigned char)buffer[size]); |
| f997bee | 457 | } |
| f997bee | 458 | } |
| f997bee | 459 | } |
| f997bee | 460 | |
| f997bee | 461 | void *tree_sitter_plum_external_scanner_create() { |
| f997bee | 462 | #if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) |
| f997bee | 463 | _Static_assert(sizeof(Delimiter) == sizeof(char), ""); |
| f997bee | 464 | #else |
| f997bee | 465 | assert(sizeof(Delimiter) == sizeof(char)); |
| f997bee | 466 | #endif |
| f997bee | 467 | Scanner *scanner = calloc(1, sizeof(Scanner)); |
| f997bee | 468 | array_init(&scanner->indents); |
| f997bee | 469 | array_init(&scanner->delimiters); |
| f997bee | 470 | tree_sitter_plum_external_scanner_deserialize(scanner, NULL, 0); |
| f997bee | 471 | return scanner; |
| f997bee | 472 | } |
| f997bee | 473 | |
| f997bee | 474 | void tree_sitter_plum_external_scanner_destroy(void *payload) { |
| f997bee | 475 | Scanner *scanner = (Scanner *)payload; |
| f997bee | 476 | array_delete(&scanner->indents); |
| f997bee | 477 | array_delete(&scanner->delimiters); |
| f997bee | 478 | free(scanner); |
| 5d12d71 | 479 | } |