Files
wehub-resource-sync 41cb1c0170
OpenSSF Scorecard / scorecard (push) Failing after 0s
DCO / dco (push) Failing after 0s
CodeQL SAST / analyze (push) Failing after 1s
Deploy Pages / deploy (push) Failing after 1s
chore: import upstream snapshot with attribution
2026-07-13 12:28:05 +08:00

765 lines
22 KiB
C

#ifndef TREE_SITTER_FSHARP_SCANNER_H_
#define TREE_SITTER_FSHARP_SCANNER_H_
#include "tree_sitter/alloc.h"
#include "tree_sitter/array.h"
#include "tree_sitter/parser.h"
enum TokenType {
NEWLINE,
INDENT,
DEDENT,
THEN,
ELSE,
ELIF,
PREPROC_IF,
PREPROC_ELSE,
PREPROC_END,
CLASS,
STRUCT,
INTERFACE,
END,
AND,
WITH,
TRIPLE_QUOTE_CONTENT,
BLOCK_COMMENT_CONTENT,
INSIDE_STRING,
NEWLINE_NO_ALIGNED,
TUPLE_MARKER,
ERROR_SENTINEL
};
typedef struct {
Array(uint16_t) indents;
Array(uint16_t) preprocessor_indents;
} Scanner;
static inline void advance(TSLexer *lexer) { lexer->advance(lexer, false); }
static inline void skip(TSLexer *lexer) { lexer->advance(lexer, true); }
static inline bool scan_block_comment(TSLexer *lexer) {
lexer->mark_end(lexer);
if (lexer->lookahead != '(')
return false;
advance(lexer);
if (lexer->lookahead != '*')
return false;
advance(lexer);
while (true) {
switch (lexer->lookahead) {
case '(':
scan_block_comment(lexer);
break;
case '*':
advance(lexer);
if (lexer->lookahead == ')') {
advance(lexer);
return true;
}
break;
case '\0':
return true;
default:
advance(lexer);
}
}
}
static inline bool is_infix_op_start(TSLexer *lexer) {
switch (lexer->lookahead) {
case '+':
skip(lexer);
return lexer->lookahead != '0' && lexer->lookahead != '1' &&
lexer->lookahead != '2' && lexer->lookahead != '3' &&
lexer->lookahead != '4' && lexer->lookahead != '5' &&
lexer->lookahead != '6' && lexer->lookahead != '7' &&
lexer->lookahead != '8' && lexer->lookahead != '9';
case '-':
skip(lexer);
return lexer->lookahead != '0' && lexer->lookahead != '1' &&
lexer->lookahead != '2' && lexer->lookahead != '3' &&
lexer->lookahead != '4' && lexer->lookahead != '5' &&
lexer->lookahead != '6' && lexer->lookahead != '7' &&
lexer->lookahead != '8' && lexer->lookahead != '9';
case '%':
case '&':
case '=':
case '?':
case '<':
case '>':
case '^':
return true;
case '/':
skip(lexer);
return lexer->lookahead != '/';
case '.':
skip(lexer);
return lexer->lookahead != '.';
case '!':
skip(lexer);
return lexer->lookahead == '=';
case ':':
skip(lexer);
return lexer->lookahead == '=' || lexer->lookahead == ':' ||
lexer->lookahead == '?' || lexer->lookahead == ' ' ||
lexer->lookahead == '>';
case 'o':
skip(lexer);
return lexer->lookahead == 'r';
case '@':
case '$':
skip(lexer);
return lexer->lookahead != '"';
default:
return false;
}
}
static inline bool is_bracket_end(TSLexer *lexer) {
switch (lexer->lookahead) {
case ')':
case ']':
case '}':
return true;
default:
return false;
}
}
static bool scan(Scanner *scanner, TSLexer *lexer, const bool *valid_symbols) {
if (valid_symbols[ERROR_SENTINEL]) {
if (scanner->indents.size > 1) {
array_pop(&scanner->indents);
lexer->result_symbol = DEDENT;
return true;
}
if (scanner->preprocessor_indents.size > 0) {
array_pop(&scanner->preprocessor_indents);
lexer->result_symbol = PREPROC_END;
return true;
}
return false;
}
if (valid_symbols[INSIDE_STRING]) {
return false;
}
if (valid_symbols[TRIPLE_QUOTE_CONTENT]) {
lexer->mark_end(lexer);
while (true) {
if (lexer->lookahead == '\0') {
break;
}
if (lexer->lookahead != '"') {
advance(lexer);
} else {
lexer->mark_end(lexer);
skip(lexer);
if (lexer->lookahead == '"') {
skip(lexer);
if (lexer->lookahead == '"') {
skip(lexer);
break;
}
}
lexer->mark_end(lexer);
}
}
lexer->result_symbol = TRIPLE_QUOTE_CONTENT;
return true;
}
lexer->mark_end(lexer);
bool found_end_of_line = false;
bool found_end_of_line_semi_colon = false;
bool found_start_of_infix_op = false;
bool found_bracket_end = false;
bool found_preprocessor_end = false;
bool found_preproc_if = false;
bool found_comment_start = false;
uint32_t indent_length = lexer->get_column(lexer);
for (;;) {
if (lexer->lookahead == '\n') {
found_end_of_line = true;
indent_length = 0;
skip(lexer);
} else if (lexer->lookahead == ' ') {
indent_length++;
skip(lexer);
} else if (lexer->lookahead == '\r' || lexer->lookahead == '\f') {
indent_length = 0;
skip(lexer);
} else if (lexer->lookahead == '\t') {
indent_length += 8;
skip(lexer);
} else if (lexer->eof(lexer)) {
found_end_of_line = true;
break;
} else if (lexer->lookahead == '/') {
skip(lexer);
if (!valid_symbols[INSIDE_STRING] && lexer->lookahead == '/') {
if (!found_preproc_if) {
return false;
}
while (lexer->lookahead != '\n' && !lexer->eof(lexer)) {
skip(lexer);
}
} else {
return false;
}
} else if (lexer->lookahead == '#') {
advance(lexer);
if (lexer->lookahead == 'e') {
advance(lexer);
if (lexer->lookahead == 'n') {
advance(lexer);
if (lexer->lookahead == 'd') {
advance(lexer);
if (lexer->lookahead == 'i') {
advance(lexer);
if (lexer->lookahead == 'f') {
advance(lexer);
found_preprocessor_end = true;
if (scanner->indents.size > 0 &&
scanner->preprocessor_indents.size > 0) {
uint16_t current_indent_length =
*array_back(&scanner->indents);
uint16_t current_preproc_length =
*array_back(&scanner->preprocessor_indents);
if (current_preproc_length < current_indent_length) {
array_pop(&scanner->indents);
lexer->result_symbol = DEDENT;
return true;
}
}
if (valid_symbols[PREPROC_END]) {
if (scanner->preprocessor_indents.size > 0) {
array_pop(&scanner->preprocessor_indents);
}
lexer->mark_end(lexer);
lexer->result_symbol = PREPROC_END;
return true;
}
}
}
}
} else if (lexer->lookahead == 'l') {
advance(lexer);
if (lexer->lookahead == 's') {
advance(lexer);
if (lexer->lookahead == 'e') {
advance(lexer);
if (scanner->indents.size > 0 &&
scanner->preprocessor_indents.size > 0) {
uint16_t current_indent_length = *array_back(&scanner->indents);
uint16_t current_preproc_length =
*array_back(&scanner->preprocessor_indents);
if (current_preproc_length < current_indent_length) {
array_pop(&scanner->indents);
lexer->result_symbol = DEDENT;
return true;
}
}
if (valid_symbols[PREPROC_ELSE]) {
lexer->mark_end(lexer);
lexer->result_symbol = PREPROC_ELSE;
return true;
}
}
}
}
} else if (lexer->lookahead == 'i') {
advance(lexer);
if (lexer->lookahead == 'f') {
advance(lexer);
found_preproc_if = true;
if (valid_symbols[NEWLINE] || valid_symbols[INDENT]) {
while (lexer->lookahead != '\n' && !lexer->eof(lexer)) {
skip(lexer);
}
} else {
if (scanner->indents.size > 0) {
if (valid_symbols[PREPROC_IF]) {
uint16_t current_indent_length = *array_back(&scanner->indents);
array_push(&scanner->preprocessor_indents,
current_indent_length);
} else {
array_pop(&scanner->indents);
lexer->result_symbol = DEDENT;
return true;
}
} else {
lexer->mark_end(lexer);
lexer->result_symbol = PREPROC_IF;
return true;
}
}
}
} else {
if (found_end_of_line && valid_symbols[NEWLINE_NO_ALIGNED]) {
lexer->result_symbol = NEWLINE_NO_ALIGNED;
return true;
}
return false;
}
} else {
break;
}
}
if (valid_symbols[CLASS] && lexer->lookahead == 'c') {
lexer->mark_end(lexer);
indent_length = lexer->get_column(lexer);
advance(lexer);
if (lexer->lookahead == 'l') {
advance(lexer);
if (lexer->lookahead == 'a') {
advance(lexer);
if (lexer->lookahead == 's') {
advance(lexer);
if (lexer->lookahead == 's') {
advance(lexer);
lexer->mark_end(lexer);
lexer->result_symbol = CLASS;
return true;
}
}
}
}
} else if (valid_symbols[STRUCT] && lexer->lookahead == 's') {
lexer->mark_end(lexer);
indent_length = lexer->get_column(lexer);
advance(lexer);
if (lexer->lookahead == 't') {
advance(lexer);
if (lexer->lookahead == 'r') {
advance(lexer);
if (lexer->lookahead == 'u') {
advance(lexer);
if (lexer->lookahead == 'c') {
advance(lexer);
if (lexer->lookahead == 't') {
advance(lexer);
lexer->mark_end(lexer);
lexer->result_symbol = STRUCT;
return true;
}
}
}
}
}
} else if (valid_symbols[INTERFACE] && lexer->lookahead == 'i') {
lexer->mark_end(lexer);
indent_length = lexer->get_column(lexer);
advance(lexer);
if (lexer->lookahead == 'n') {
advance(lexer);
if (lexer->lookahead == 't') {
advance(lexer);
if (lexer->lookahead == 'e') {
advance(lexer);
if (lexer->lookahead == 'r') {
advance(lexer);
if (lexer->lookahead == 'f') {
advance(lexer);
if (lexer->lookahead == 'a') {
advance(lexer);
if (lexer->lookahead == 'c') {
advance(lexer);
if (lexer->lookahead == 'e') {
advance(lexer);
lexer->mark_end(lexer);
lexer->result_symbol = INTERFACE;
return true;
}
}
}
}
}
}
}
}
}
if (found_end_of_line && valid_symbols[NEWLINE_NO_ALIGNED] &&
!found_start_of_infix_op && !found_preprocessor_end) {
lexer->result_symbol = NEWLINE_NO_ALIGNED;
return true;
}
if (valid_symbols[NEWLINE] && lexer->lookahead == ';') {
advance(lexer);
while (lexer->lookahead == ' ' || lexer->lookahead == '\n') {
advance(lexer);
}
found_end_of_line = true;
found_end_of_line_semi_colon = true;
lexer->mark_end(lexer);
}
if (lexer->lookahead == 't' &&
(valid_symbols[THEN] || valid_symbols[DEDENT])) {
advance(lexer);
if (lexer->lookahead == 'h') {
advance(lexer);
if (lexer->lookahead == 'e') {
advance(lexer);
if (lexer->lookahead == 'n') {
advance(lexer);
// the 'THEN' token is only valid if we have popped the appropriate
// amount of dedent tokens.
// If 'THEN' is not valid we just continue to pop dedent tokens.
if (valid_symbols[THEN]) {
lexer->mark_end(lexer);
lexer->result_symbol = THEN;
return true;
} else {
array_pop(&scanner->indents);
lexer->result_symbol = DEDENT;
return true;
}
}
}
}
} else if (lexer->lookahead == 'a' &&
(valid_symbols[AND] || valid_symbols[DEDENT])) {
advance(lexer);
if (lexer->lookahead == 'n') {
advance(lexer);
if (lexer->lookahead == 'd') {
advance(lexer);
if (lexer->lookahead == ' ') {
// the 'AND' token is only valid if we have popped the appropriate
// amount of dedent tokens.
// If 'AND' is not valid we just continue to pop dedent tokens.
if (valid_symbols[AND]) {
lexer->mark_end(lexer);
lexer->result_symbol = AND;
return true;
} else {
array_pop(&scanner->indents);
lexer->result_symbol = DEDENT;
return true;
}
}
}
}
} else if (lexer->lookahead == 'w' &&
(valid_symbols[WITH] || valid_symbols[DEDENT])) {
advance(lexer);
if (lexer->lookahead == 'i') {
advance(lexer);
if (lexer->lookahead == 't') {
advance(lexer);
if (lexer->lookahead == 'h') {
advance(lexer);
if (lexer->lookahead == ' ') {
// the 'WITH' token is only valid if we have popped the appropriate
// amount of dedent tokens.
// If 'WITH' is not valid we just continue to pop dedent tokens.
if (valid_symbols[WITH]) {
lexer->mark_end(lexer);
lexer->result_symbol = WITH;
return true;
} else {
array_pop(&scanner->indents);
lexer->result_symbol = DEDENT;
return true;
}
}
}
}
}
} else if (lexer->lookahead == 'e' &&
(valid_symbols[ELSE] || valid_symbols[ELIF] ||
valid_symbols[END] || valid_symbols[DEDENT])) {
advance(lexer);
int16_t token_indent_level = lexer->get_column(lexer);
if (lexer->lookahead == 'l') {
advance(lexer);
if (lexer->lookahead == 's' &&
(valid_symbols[ELSE] || valid_symbols[DEDENT])) {
advance(lexer);
if (lexer->lookahead == 'e') {
advance(lexer);
if (valid_symbols[ELSE]) {
if (scanner->indents.size > 0 &&
token_indent_level < *array_back(&scanner->indents)) {
array_pop(&scanner->indents);
lexer->result_symbol = DEDENT;
return true;
} else {
lexer->mark_end(lexer);
for (;;) {
if (lexer->lookahead == ' ' || lexer->lookahead == '\n' ||
lexer->lookahead == '\r' || lexer->lookahead == '\t') {
advance(lexer);
} else {
break;
}
}
if (lexer->lookahead == 'i') {
advance(lexer);
if (lexer->lookahead == 'f') {
advance(lexer);
if (lexer->lookahead == ' ' || lexer->lookahead == '\n' ||
lexer->lookahead == '\t') {
lexer->mark_end(lexer);
lexer->result_symbol = ELIF;
return true;
}
}
}
lexer->result_symbol = ELSE;
return true;
}
} else {
array_pop(&scanner->indents);
lexer->result_symbol = DEDENT;
return true;
}
}
} else if (lexer->lookahead == 'i' &&
(valid_symbols[ELIF] || valid_symbols[DEDENT])) {
advance(lexer);
if (lexer->lookahead == 'f') {
advance(lexer);
if (valid_symbols[ELIF]) {
if (scanner->indents.size > 0 &&
token_indent_level < *array_back(&scanner->indents)) {
array_pop(&scanner->indents);
lexer->result_symbol = DEDENT;
return true;
} else {
lexer->mark_end(lexer);
lexer->result_symbol = ELIF;
return true;
}
} else {
array_pop(&scanner->indents);
lexer->result_symbol = DEDENT;
return true;
}
}
}
} else if (lexer->lookahead == 'n' &&
(valid_symbols[END] || valid_symbols[DEDENT])) {
advance(lexer);
if (lexer->lookahead == 'd') {
advance(lexer);
if (lexer->lookahead == ' ' || lexer->lookahead == '\n' ||
lexer->eof(lexer)) {
if (valid_symbols[END]) {
lexer->mark_end(lexer);
lexer->result_symbol = END;
return true;
} else if (valid_symbols[DEDENT] && scanner->indents.size > 0) {
array_pop(&scanner->indents);
lexer->result_symbol = DEDENT;
return true;
}
}
}
}
} else if (is_bracket_end(lexer)) {
found_bracket_end = true;
} else if (is_infix_op_start(lexer)) {
found_start_of_infix_op = true;
} else if (lexer->lookahead == '|') {
skip(lexer);
switch (lexer->lookahead) {
case ']':
case '}':
found_bracket_end = true;
break;
case '>':
found_start_of_infix_op = true;
break;
case ' ':
if (indent_length == 0) {
indent_length = 1;
}
if (scanner->indents.size > 0) {
uint16_t current_indent_length = *array_back(&scanner->indents);
if (found_end_of_line && indent_length == current_indent_length &&
indent_length > 0 && !found_start_of_infix_op &&
!found_bracket_end) {
if (valid_symbols[NEWLINE] && !found_preprocessor_end) {
lexer->result_symbol = NEWLINE;
return true;
}
}
}
break;
default:
found_start_of_infix_op = true;
break;
}
} else if (lexer->lookahead == '(') {
skip(lexer);
if (lexer->lookahead == '*') {
found_comment_start = true;
}
}
if (valid_symbols[NEWLINE] && found_end_of_line_semi_colon &&
!found_comment_start) {
lexer->result_symbol = NEWLINE;
return true;
}
if (valid_symbols[INDENT] && !found_bracket_end && !found_preprocessor_end) {
array_push(&scanner->indents, indent_length);
lexer->result_symbol = INDENT;
return true;
}
if (scanner->indents.size > 0) {
uint16_t current_indent_length = *array_back(&scanner->indents);
if (found_bracket_end && valid_symbols[DEDENT]) {
array_pop(&scanner->indents);
lexer->result_symbol = DEDENT;
return true;
}
if (found_end_of_line) {
if (indent_length == current_indent_length && indent_length > 0 &&
!found_start_of_infix_op && !found_bracket_end) {
if (valid_symbols[NEWLINE] && !found_preprocessor_end &&
!found_comment_start) {
lexer->result_symbol = NEWLINE;
return true;
}
}
bool can_dedent_preproc;
if (scanner->preprocessor_indents.size > 0) {
uint16_t current_preproc_length =
*array_back(&scanner->preprocessor_indents);
can_dedent_preproc = current_preproc_length < indent_length;
} else {
can_dedent_preproc = true;
}
bool can_dedent_infix_op;
if (found_start_of_infix_op) {
can_dedent_infix_op = indent_length + 2 < current_indent_length;
} else {
can_dedent_infix_op = true;
}
if (indent_length < current_indent_length && !found_bracket_end &&
can_dedent_preproc && can_dedent_infix_op &&
!valid_symbols[TUPLE_MARKER]) {
array_pop(&scanner->indents);
lexer->result_symbol = DEDENT;
return true;
}
}
}
if (valid_symbols[BLOCK_COMMENT_CONTENT]) {
lexer->mark_end(lexer);
while (true) {
if (lexer->lookahead == '\0') {
break;
}
if (lexer->lookahead != '(' && lexer->lookahead != '*') {
advance(lexer);
} else if (lexer->lookahead == '*') {
lexer->mark_end(lexer);
advance(lexer);
if (lexer->lookahead == ')') {
break;
}
} else if (scan_block_comment(lexer)) {
lexer->mark_end(lexer);
advance(lexer);
if (lexer->lookahead == '*') {
break;
}
}
}
lexer->result_symbol = BLOCK_COMMENT_CONTENT;
return true;
}
return false;
}
static unsigned serialize(Scanner *scanner, char *buffer) {
size_t size = 0;
size_t preprocessor_count = scanner->preprocessor_indents.size;
if (preprocessor_count > UINT8_MAX) {
preprocessor_count = UINT8_MAX;
}
buffer[size++] = (char)scanner->preprocessor_indents.size;
for (size_t iter = 0; iter < preprocessor_count &&
size < TREE_SITTER_SERIALIZATION_BUFFER_SIZE;
iter++) {
char e = *array_get(&scanner->preprocessor_indents, iter);
buffer[size++] = e;
}
uint32_t iter = 1;
for (; iter < scanner->indents.size &&
size < TREE_SITTER_SERIALIZATION_BUFFER_SIZE;
++iter) {
buffer[size++] = (char)*array_get(&scanner->indents, iter);
}
return size;
}
static void deserialize(Scanner *scanner, const char *buffer, unsigned length) {
array_delete(&scanner->indents);
array_push(&scanner->indents, 0);
array_delete(&scanner->preprocessor_indents);
if (length > 0) {
size_t size = 0;
size_t preprocessor_count = (uint8_t)buffer[size++];
for (; size <= preprocessor_count; size++) {
array_push(&scanner->preprocessor_indents, (unsigned char)buffer[size]);
}
for (; size < length; size++) {
array_push(&scanner->indents, (unsigned char)buffer[size]);
}
assert(size == length);
}
}
static Scanner *create() {
Scanner *scanner = ts_calloc(1, sizeof(Scanner));
array_init(&scanner->indents);
array_init(&scanner->preprocessor_indents);
deserialize(scanner, NULL, 0);
return scanner;
}
static void destroy(Scanner *scanner) {
array_delete(&scanner->indents);
array_delete(&scanner->preprocessor_indents);
ts_free(scanner);
}
#endif // TREE_SITTER_FSHARP_SCANNER_H_