#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,
};
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 peek_keyword_ci(TSLexer *lexer, const char *keyword) {
skip_whitespace(lexer);
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;
}
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]) {
return false;
}
if (valid_symbols[PREPROC_OPEN] || valid_symbols[PREPROC_CLOSE]) {
skip_whitespace(lexer);
if (lexer->lookahead == '#') {
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_keyword_ci(lexer, "#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); 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);
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 (read_keyword_ci(lexer, "else") || read_keyword_ci(lexer, "endif")) {
lexer->result_symbol = PREPROC_SPLIT_END;
return true;
}
}
}
return 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);
while (lexer->lookahead == ' ' || lexer->lookahead == '\t' ||
lexer->lookahead == '\r' || lexer->lookahead == '\f') {
lexer->advance(lexer, false);
}
if (lexer->lookahead == '=') {
lexer->result_symbol = PROPERTY_NAME;
return true;
}
return false;
}
return false;
}