#include "tree_sitter/parser.h"
#include <stdlib.h>
#include <string.h>
#include <wctype.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,
};
typedef struct {
uint8_t depth; } ScannerState;
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;
buffer[0] = (char)state->depth;
return 1;
}
void tree_sitter_al_external_scanner_deserialize(
void *payload, const char *buffer, unsigned length
) {
ScannerState *state = (ScannerState *)payload;
state->depth = (length > 0) ? (uint8_t)buffer[0] : 0;
}
static bool is_identifier_start(int32_t c) {
return iswalpha(c) || c == '_';
}
static bool is_identifier_char(int32_t c) {
return iswalnum(c) || c == '_';
}
static void skip_whitespace(TSLexer *lexer) {
while (lexer->lookahead == ' ' || lexer->lookahead == '\t' ||
lexer->lookahead == '\r' || lexer->lookahead == '\n' ||
lexer->lookahead == '\f') {
lexer->advance(lexer, true);
}
}
static bool read_keyword_ci(TSLexer *lexer, const char *keyword) {
for (int i = 0; keyword[i] != '\0'; i++) {
if (towlower(lexer->lookahead) != keyword[i]) return false;
lexer->advance(lexer, false);
}
if (is_identifier_char(lexer->lookahead)) return false;
return true;
}
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 (lexer->lookahead == ' ' || lexer->lookahead == '\t' ||
lexer->lookahead == '\r' || lexer->lookahead == '\n' ||
lexer->lookahead == '\f') {
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;
}
}
static const char *const TRANSPARENT_DIRECTIVES[] = {
"pragma", "endregion", "region", "define", "undef", NULL,
};
static const char *const DIRECTIVE_ENDIF[] = { "endif", NULL };
static const char *const DIRECTIVE_ELSE_ENDIF[] = { "else", "endif", NULL };
static bool peek_directive_ci_skip_extras(TSLexer *lexer, const char *const *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;
while (is_identifier_char(lexer->lookahead)) {
if (len < sizeof(word) - 1) word[len] = (char)towlower(lexer->lookahead);
len++;
lexer->advance(lexer, false);
}
if (len >= sizeof(word)) return false; word[len] = '\0';
for (int i = 0; targets[i] != NULL; i++) {
if (strcmp(word, targets[i]) == 0) return true;
}
bool transparent = false;
for (int i = 0; TRANSPARENT_DIRECTIVES[i] != NULL; i++) {
if (strcmp(word, TRANSPARENT_DIRECTIVES[i]) == 0) { 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]) {
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);
}
if (valid_symbols[PREPROC_OPEN] && read_keyword_ci(lexer, "if")) {
state->depth++;
lexer->result_symbol = PREPROC_OPEN;
return true;
}
if (valid_symbols[PREPROC_CLOSE] && read_keyword_ci(lexer, "endif")) {
if (state->depth > 0) state->depth--;
lexer->result_symbol = PREPROC_CLOSE;
return true;
}
return false;
}
}
if (valid_symbols[BEGIN_KEYWORD] && state->depth == 0) {
skip_whitespace(lexer);
if (read_keyword_ci(lexer, "begin")) {
lexer->result_symbol = BEGIN_KEYWORD;
return true;
}
}
if (valid_symbols[END_KEYWORD] && state->depth == 0) {
skip_whitespace(lexer);
if (read_keyword_ci(lexer, "end")) {
lexer->result_symbol = END_KEYWORD;
return true;
}
}
if (valid_symbols[PREPROC_SPLIT_BEGIN] && state->depth > 0) {
skip_whitespace(lexer);
if (read_keyword_ci(lexer, "begin")) {
lexer->mark_end(lexer); if (peek_directive_ci_skip_extras(lexer, DIRECTIVE_ENDIF)) {
lexer->result_symbol = PREPROC_SPLIT_BEGIN;
return true;
}
return false;
}
return false;
}
if (valid_symbols[PREPROC_SPLIT_END] && state->depth > 0) {
skip_whitespace(lexer);
if (read_keyword_ci(lexer, "end")) {
lexer->mark_end(lexer); if (!skip_whitespace_and_comments(lexer)) return false;
if (lexer->lookahead == ';') {
lexer->advance(lexer, false);
if (peek_directive_ci_skip_extras(lexer, DIRECTIVE_ELSE_ENDIF)) {
lexer->result_symbol = PREPROC_SPLIT_END;
return true;
}
}
return false;
}
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;
while (lexer->lookahead == ' ' || lexer->lookahead == '\t' ||
lexer->lookahead == '\r' || lexer->lookahead == '\n' ||
lexer->lookahead == '\f') {
lexer->advance(lexer, 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;
while (lexer->lookahead == ' ' || lexer->lookahead == '\t' ||
lexer->lookahead == '\r' || lexer->lookahead == '\n' ||
lexer->lookahead == '\f') {
lexer->advance(lexer, 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;
}
while (lexer->lookahead == ' ' || lexer->lookahead == '\t') {
lexer->advance(lexer, false);
}
if (lexer->lookahead == ':') {
lexer->result_symbol = VAR_ATTRIBUTE_OPEN;
return true;
}
if (lexer->lookahead != ',') return false;
lexer->advance(lexer, false); while (lexer->lookahead == ' ' || lexer->lookahead == '\t') {
lexer->advance(lexer, false);
}
}
}
return false;
}
}
if (valid_symbols[CONTINUE_AS_IDENTIFIER]) {
while (lexer->lookahead == ' ' || lexer->lookahead == '\t' ||
lexer->lookahead == '\r' || lexer->lookahead == '\n' ||
lexer->lookahead == '\f') {
lexer->advance(lexer, true);
}
const char *keyword = "continue";
int pos = 0;
bool match = true;
if (!is_identifier_start(lexer->lookahead)) {
return false;
}
while (is_identifier_char(lexer->lookahead)) {
if (pos < 8) {
if (towlower(lexer->lookahead) != keyword[pos]) {
match = false;
}
pos++;
} else {
match = false;
pos++;
}
lexer->advance(lexer, false);
}
if (match && pos == 8) {
lexer->mark_end(lexer);
while (lexer->lookahead == ' ' || lexer->lookahead == '\t' ||
lexer->lookahead == '\r' || lexer->lookahead == '\n' ||
lexer->lookahead == '\f') {
lexer->advance(lexer, false);
}
if (lexer->lookahead == ':') {
lexer->advance(lexer, false);
if (lexer->lookahead == '=') {
lexer->result_symbol = CONTINUE_AS_IDENTIFIER;
return true;
}
}
}
return false;
}
if (valid_symbols[PROPERTY_NAME]) {
while (lexer->lookahead == ' ' || lexer->lookahead == '\t' ||
lexer->lookahead == '\r' || lexer->lookahead == '\n' ||
lexer->lookahead == '\f') {
lexer->advance(lexer, true); }
if (!is_identifier_start(lexer->lookahead)) return false;
lexer->mark_end(lexer);
while (is_identifier_char(lexer->lookahead)) {
lexer->advance(lexer, false);
}
lexer->mark_end(lexer);
if (!skip_whitespace_and_comments(lexer)) return false;
if (lexer->lookahead == '=') {
lexer->result_symbol = PROPERTY_NAME;
return true;
}
return false;
}
return false;
}