#include "tree_sitter/parser.h"
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "unicode_id.h"
enum TokenType {
PROPERTY_NAME = 0,
CONTINUE_AS_IDENTIFIER = 1,
PREPROC_OPEN = 2,
PREPROC_CLOSE = 3,
BEGIN_KEYWORD = 4,
END_KEYWORD = 5,
PREPROC_SPLIT_BEGIN = 6,
PREPROC_SPLIT_END = 7,
VAR_ATTRIBUTE_OPEN = 8,
CALC_FORMULA_PROPERTY_NAME = 9,
};
typedef uint32_t ScannerDepth;
typedef struct {
ScannerDepth depth;
} ScannerState;
#define SCANNER_DEPTH_IS_UNSIGNED ((ScannerDepth)-1 > (ScannerDepth)0)
#define SCANNER_DEPTH_OK (sizeof(ScannerDepth) >= 4 && SCANNER_DEPTH_IS_UNSIGNED)
#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
_Static_assert(SCANNER_DEPTH_OK,
"ScannerDepth must be an unsigned type of at least 32 bits: a narrower or "
"signed depth counter wraps, and every state->depth > 0 guard then reads "
"genuine #if nesting as not nested");
#else
typedef char scanner_depth_must_not_wrap[SCANNER_DEPTH_OK ? 1 : -1];
#endif
void *tree_sitter_al_external_scanner_create() {
ScannerState *state = calloc(1, sizeof(ScannerState));
return state;
}
void tree_sitter_al_external_scanner_destroy(void *payload) {
free(payload);
}
unsigned tree_sitter_al_external_scanner_serialize(void *payload, char *buffer) {
ScannerState *state = (ScannerState *)payload;
memcpy(buffer, &state->depth, sizeof(state->depth));
return (unsigned)sizeof(state->depth);
}
void tree_sitter_al_external_scanner_deserialize(
void *payload, const char *buffer, unsigned length
) {
ScannerState *state = (ScannerState *)payload;
state->depth = 0;
if (length >= sizeof(state->depth)) {
memcpy(&state->depth, buffer, sizeof(state->depth));
}
}
static bool is_identifier_start(int32_t c) {
return al_is_identifier_start(c);
}
static bool is_identifier_char(int32_t c) {
return al_is_identifier_char(c);
}
static char keyword_byte(int32_t c) {
if (c >= 'A' && c <= 'Z') return (char)(c + ('a' - 'A'));
if (c >= 0 && c < 128) return (char)c;
return (char)0x01;
}
static bool is_extra_space(int32_t c) {
return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f' ||
c == '\v' || c == 0xFEFF;
}
static void skip_whitespace(TSLexer *lexer) {
while (is_extra_space(lexer->lookahead)) {
lexer->advance(lexer, true);
}
}
static bool read_word_ci(TSLexer *lexer, char *buf, size_t cap, size_t *out_len) {
size_t len = 0;
while (is_identifier_char(lexer->lookahead)) {
if (len < cap - 1) buf[len] = keyword_byte(lexer->lookahead);
len++;
lexer->advance(lexer, false);
}
*out_len = len;
buf[len > cap - 1 ? cap - 1 : len] = '\0';
return len <= cap - 1;
}
enum IdentifierWord {
WORD_NOT_IDENTIFIER = 0, WORD_OTHER, WORD_BEGIN,
WORD_END,
WORD_CONTINUE,
WORD_CALCFORMULA, };
static enum IdentifierWord read_identifier_word(TSLexer *lexer) {
if (!is_identifier_start(lexer->lookahead)) return WORD_NOT_IDENTIFIER;
char buf[12]; size_t len = 0;
if (!read_word_ci(lexer, buf, sizeof(buf), &len)) {
return WORD_OTHER; }
if (len == 5 && strcmp(buf, "begin") == 0) return WORD_BEGIN;
if (len == 3 && strcmp(buf, "end") == 0) return WORD_END;
if (len == 8 && strcmp(buf, "continue") == 0) return WORD_CONTINUE;
if (len == 11 && strcmp(buf, "calcformula") == 0) return WORD_CALCFORMULA;
return WORD_OTHER;
}
static bool skip_comment(TSLexer *lexer) {
lexer->advance(lexer, false); if (lexer->lookahead == '/') {
while (lexer->lookahead != 0 && lexer->lookahead != '\n') {
lexer->advance(lexer, false);
}
return true;
}
if (lexer->lookahead == '*') {
lexer->advance(lexer, false);
while (lexer->lookahead != 0) {
if (lexer->lookahead == '*') {
lexer->advance(lexer, false);
if (lexer->lookahead == '/') {
lexer->advance(lexer, false);
return true;
}
continue;
}
lexer->advance(lexer, false);
}
return true; }
return false; }
static void skip_whitespace_nomark(TSLexer *lexer) {
while (is_extra_space(lexer->lookahead)) {
lexer->advance(lexer, false);
}
}
static bool skip_whitespace_and_comments(TSLexer *lexer) {
while (true) {
skip_whitespace_nomark(lexer);
if (lexer->lookahead != '/') return true;
if (!skip_comment(lexer)) return false;
}
}
typedef struct {
const char *name;
bool whole_word;
} DirectiveMatch;
static const DirectiveMatch TRANSPARENT_DIRECTIVES[] = {
{ "pragma", true }, { "endregion", true }, { "region", true },
{ "define", true }, { "undef", true }, { NULL, false },
};
static const DirectiveMatch DIRECTIVE_ENDIF[] = { { "endif", true }, { NULL, false } };
static const DirectiveMatch DIRECTIVE_BRANCH_OR_ENDIF[] = {
{ "elif", false }, { "else", false }, { "endif", true }, { NULL, false },
};
static bool directive_matches(const DirectiveMatch *d, const char *word, bool truncated) {
size_t n = strlen(d->name);
if (strncmp(word, d->name, n) != 0) return false;
if (!d->whole_word) return true;
return !truncated && word[n] == '\0';
}
static bool peek_directive_ci_skip_extras(TSLexer *lexer, const DirectiveMatch *targets) {
while (true) {
if (!skip_whitespace_and_comments(lexer)) return false;
if (lexer->lookahead != '#') return false;
lexer->advance(lexer, false);
while (lexer->lookahead == ' ' || lexer->lookahead == '\t') {
lexer->advance(lexer, false);
}
char word[16];
size_t len = 0;
bool truncated = !read_word_ci(lexer, word, sizeof(word), &len);
for (int i = 0; targets[i].name != NULL; i++) {
if (directive_matches(&targets[i], word, truncated)) return true;
}
bool transparent = false;
for (int i = 0; TRANSPARENT_DIRECTIVES[i].name != NULL; i++) {
if (directive_matches(&TRANSPARENT_DIRECTIVES[i], word, truncated)) {
transparent = true;
break;
}
}
if (!transparent) return false;
while (lexer->lookahead != '\0' && lexer->lookahead != '\n') {
lexer->advance(lexer, false);
}
}
}
bool tree_sitter_al_external_scanner_scan(
void *payload,
TSLexer *lexer,
const bool *valid_symbols
) {
ScannerState *state = (ScannerState *)payload;
if (valid_symbols[PROPERTY_NAME] && valid_symbols[CONTINUE_AS_IDENTIFIER] &&
valid_symbols[PREPROC_OPEN] && valid_symbols[PREPROC_CLOSE] &&
valid_symbols[BEGIN_KEYWORD] && valid_symbols[END_KEYWORD] &&
valid_symbols[PREPROC_SPLIT_BEGIN] &&
valid_symbols[PREPROC_SPLIT_END] &&
valid_symbols[VAR_ATTRIBUTE_OPEN] &&
valid_symbols[CALC_FORMULA_PROPERTY_NAME]) {
return false;
}
if (valid_symbols[PREPROC_OPEN] || valid_symbols[PREPROC_CLOSE]) {
skip_whitespace(lexer);
if (lexer->lookahead == '#') {
lexer->advance(lexer, false);
while (lexer->lookahead == ' ' || lexer->lookahead == '\t') {
lexer->advance(lexer, false);
}
char word[8];
size_t len = 0;
if (!read_word_ci(lexer, word, sizeof(word), &len)) return false;
if (valid_symbols[PREPROC_OPEN] && len == 2 && strcmp(word, "if") == 0) {
state->depth++;
lexer->result_symbol = PREPROC_OPEN;
return true;
}
if (valid_symbols[PREPROC_CLOSE] && len == 5 && strcmp(word, "endif") == 0) {
if (state->depth > 0) state->depth--;
lexer->result_symbol = PREPROC_CLOSE;
return true;
}
return false;
}
}
if (valid_symbols[VAR_ATTRIBUTE_OPEN]) {
skip_whitespace(lexer);
if (lexer->lookahead == '[') {
lexer->advance(lexer, false);
lexer->mark_end(lexer);
int bracket_depth = 1;
bool in_string = false;
while (bracket_depth > 0 && lexer->lookahead != 0) {
if (in_string) {
if (lexer->lookahead == '\'') {
lexer->advance(lexer, false);
if (lexer->lookahead == '\'') {
lexer->advance(lexer, false);
continue;
}
in_string = false;
continue;
}
} else {
if (lexer->lookahead == '/') {
skip_comment(lexer);
continue;
}
if (lexer->lookahead == '\'') {
in_string = true;
} else if (lexer->lookahead == '[') {
bracket_depth++;
} else if (lexer->lookahead == ']') {
bracket_depth--;
if (bracket_depth == 0) {
lexer->advance(lexer, false); break;
}
}
}
lexer->advance(lexer, false);
}
if (bracket_depth != 0) return false;
if (!skip_whitespace_and_comments(lexer)) return false;
if (lexer->lookahead == '[') {
while (lexer->lookahead == '[') {
int inner_bracket_depth = 1;
bool inner_in_string = false;
lexer->advance(lexer, false); while (inner_bracket_depth > 0 && lexer->lookahead != 0) {
if (inner_in_string) {
if (lexer->lookahead == '\'') {
lexer->advance(lexer, false);
if (lexer->lookahead == '\'') {
lexer->advance(lexer, false);
continue;
}
inner_in_string = false;
continue;
}
} else {
if (lexer->lookahead == '/') {
skip_comment(lexer);
continue;
}
if (lexer->lookahead == '\'') {
inner_in_string = true;
} else if (lexer->lookahead == '[') {
inner_bracket_depth++;
} else if (lexer->lookahead == ']') {
inner_bracket_depth--;
if (inner_bracket_depth == 0) {
lexer->advance(lexer, false); break;
}
}
}
lexer->advance(lexer, false);
}
if (inner_bracket_depth != 0) return false;
if (!skip_whitespace_and_comments(lexer)) return false;
}
}
if (lexer->lookahead == '"' || is_identifier_start(lexer->lookahead)) {
while (true) {
if (lexer->lookahead == '"') {
lexer->advance(lexer, false);
while (lexer->lookahead != 0 && lexer->lookahead != '"') {
lexer->advance(lexer, false);
}
if (lexer->lookahead != '"') return false; lexer->advance(lexer, false);
} else if (is_identifier_start(lexer->lookahead)) {
while (is_identifier_char(lexer->lookahead)) {
lexer->advance(lexer, false);
}
} else {
return false;
}
if (!skip_whitespace_and_comments(lexer)) return false;
if (lexer->lookahead == ':') {
lexer->result_symbol = VAR_ATTRIBUTE_OPEN;
return true;
}
if (lexer->lookahead != ',') return false;
lexer->advance(lexer, false); if (!skip_whitespace_and_comments(lexer)) return false;
}
}
return false;
}
}
if (valid_symbols[BEGIN_KEYWORD] || valid_symbols[PREPROC_SPLIT_BEGIN] ||
valid_symbols[END_KEYWORD] || valid_symbols[PREPROC_SPLIT_END] ||
valid_symbols[CONTINUE_AS_IDENTIFIER] || valid_symbols[PROPERTY_NAME] ||
valid_symbols[CALC_FORMULA_PROPERTY_NAME]) {
skip_whitespace(lexer);
enum IdentifierWord word = read_identifier_word(lexer);
if (word == WORD_NOT_IDENTIFIER) return false; lexer->mark_end(lexer);
if (word == WORD_BEGIN &&
(valid_symbols[BEGIN_KEYWORD] || valid_symbols[PREPROC_SPLIT_BEGIN])) {
if (state->depth > 0 && valid_symbols[PREPROC_SPLIT_BEGIN] &&
peek_directive_ci_skip_extras(lexer, DIRECTIVE_ENDIF)) {
lexer->result_symbol = PREPROC_SPLIT_BEGIN;
return true;
}
if (valid_symbols[BEGIN_KEYWORD]) {
lexer->result_symbol = BEGIN_KEYWORD;
return true;
}
return false;
}
if (word == WORD_END &&
(valid_symbols[END_KEYWORD] || valid_symbols[PREPROC_SPLIT_END])) {
if (state->depth > 0 && valid_symbols[PREPROC_SPLIT_END] &&
skip_whitespace_and_comments(lexer) && lexer->lookahead == ';') {
lexer->advance(lexer, false);
if (peek_directive_ci_skip_extras(lexer, DIRECTIVE_BRANCH_OR_ENDIF)) {
lexer->result_symbol = PREPROC_SPLIT_END;
return true;
}
}
if (valid_symbols[END_KEYWORD]) {
lexer->result_symbol = END_KEYWORD;
return true;
}
return false;
}
if (valid_symbols[PROPERTY_NAME] || valid_symbols[CALC_FORMULA_PROPERTY_NAME]) {
if (!skip_whitespace_and_comments(lexer)) return false;
if (lexer->lookahead == '=') {
lexer->result_symbol =
(word == WORD_CALCFORMULA && valid_symbols[CALC_FORMULA_PROPERTY_NAME])
? CALC_FORMULA_PROPERTY_NAME
: PROPERTY_NAME;
if (lexer->result_symbol == PROPERTY_NAME && !valid_symbols[PROPERTY_NAME]) {
return false;
}
return true;
}
}
if (word == WORD_CONTINUE && valid_symbols[CONTINUE_AS_IDENTIFIER]) {
skip_whitespace_nomark(lexer);
int32_t c = lexer->lookahead;
if (c == '(' || c == '.' || c == '[') {
lexer->result_symbol = CONTINUE_AS_IDENTIFIER;
return true;
}
if (c == ':') {
lexer->advance(lexer, false);
if (lexer->lookahead == '=' || lexer->lookahead == ':') {
lexer->result_symbol = CONTINUE_AS_IDENTIFIER;
return true;
}
} else if (c == '+' || c == '-' || c == '*' || c == '/') {
lexer->advance(lexer, false);
if (lexer->lookahead == '=') {
lexer->result_symbol = CONTINUE_AS_IDENTIFIER;
return true;
}
}
}
return false;
}
return false;
}