#include <string.h>
#include <tree_sitter/parser.h>
static inline void advance(TSLexer *lexer) { lexer->advance(lexer, false); }
static inline void skip(TSLexer *lexer) { lexer->advance(lexer, true); }
enum TokenType {
SPACE,
SPACE_INDEXING,
RANGE,
RANGE_LT,
RANGE_EQ,
RANGE_LT_EQ,
INTEGER,
FLOAT,
RAW_STRING_CONTENT,
RAW_STRING_ESCAPE,
RAW_STRING_END
};
typedef enum { SCAN_NONE, SCAN_DONE, SCAN_FAIL } ScanResult;
void *tree_sitter_macaulay2_external_scanner_create() { return NULL; }
void tree_sitter_macaulay2_external_scanner_destroy(void *payload) {}
unsigned tree_sitter_macaulay2_external_scanner_serialize(void *payload,
char *buffer) {
return 0;
}
void tree_sitter_macaulay2_external_scanner_deserialize(void *payload,
const char *buffer,
unsigned length) {}
static bool is_alpha(int32_t c) {
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
}
static bool is_digit(int32_t c) { return c >= '0' && c <= '9'; }
static bool is_inline_whitespace(int32_t c) { return c == ' ' || c == '\t'; }
static bool is_whitespace(int32_t c) {
return is_inline_whitespace(c) || c == '\n' || c == '\r';
}
static bool is_ident_char(int32_t c) {
return is_alpha(c) || is_digit(c) || c == '\'' || c == '$';
}
static bool emit(TSLexer *lexer, enum TokenType symbol) {
lexer->result_symbol = symbol;
return true;
}
static bool is_adjacency_blocking_keyword_ahead(TSLexer *lexer) {
char buffer[16];
int i = 0;
while (is_ident_char(lexer->lookahead) && i < 15) {
buffer[i++] = (char)lexer->lookahead;
advance(lexer);
}
if (is_ident_char(lexer->lookahead))
return false;
buffer[i] = '\0';
if (i == 0)
return false;
static const char *const keywords[] = {
"if", "then", "else", "from",
"to", "when", "do", "in",
"of", "list", "for", "while",
"and", "or", "xor", "SPACE",
"break", "continue", "return", "try",
"catch", "throw", "time", "timing",
"elapsedTime", "elapsedTiming", "profile", "shield",
"TEST", "breakpoint", "except", "trap",
};
for (size_t k = 0; k < sizeof(keywords) / sizeof(keywords[0]); k++) {
if (strcmp(buffer, keywords[k]) == 0)
return true;
}
return false;
}
static void skip_whitespace(TSLexer *lexer) {
while (is_inline_whitespace(lexer->lookahead))
skip(lexer);
}
static void skip_number_whitespace(TSLexer *lexer) {
while (is_whitespace(lexer->lookahead))
skip(lexer);
}
static void consume_inline_whitespace(TSLexer *lexer) {
while (is_inline_whitespace(lexer->lookahead))
advance(lexer);
}
static bool match_int(TSLexer *lexer) {
if (!is_digit(lexer->lookahead))
return false;
while (is_digit(lexer->lookahead))
advance(lexer);
return true;
}
static bool can_scan_dot_operator(const bool *valid_symbols) {
return valid_symbols[SPACE] || valid_symbols[RANGE] ||
valid_symbols[RANGE_LT] || valid_symbols[RANGE_EQ] ||
valid_symbols[RANGE_LT_EQ];
}
static bool can_scan_number(const bool *valid_symbols) {
return valid_symbols[INTEGER] || valid_symbols[FLOAT];
}
static bool can_scan_adjacency(const bool *valid_symbols) {
return valid_symbols[SPACE] || valid_symbols[SPACE_INDEXING];
}
static bool can_scan_raw_string(const bool *valid_symbols) {
return valid_symbols[RAW_STRING_CONTENT] ||
valid_symbols[RAW_STRING_ESCAPE] || valid_symbols[RAW_STRING_END];
}
static ScanResult scan_dot_operator(TSLexer *lexer, const bool *valid_symbols) {
if (lexer->lookahead != '.')
return SCAN_NONE;
lexer->mark_end(lexer);
advance(lexer);
if (valid_symbols[SPACE] && is_digit(lexer->lookahead))
return emit(lexer, SPACE) ? SCAN_DONE : SCAN_FAIL;
bool separated_range = false;
while (is_inline_whitespace(lexer->lookahead)) {
separated_range = true;
advance(lexer);
}
if (lexer->lookahead != '.')
return SCAN_NONE;
advance(lexer);
if (separated_range && lexer->lookahead == '.')
advance(lexer);
lexer->mark_end(lexer);
int32_t c = lexer->lookahead;
if (valid_symbols[RANGE] && c != '<' && c != '=')
return emit(lexer, RANGE) ? SCAN_DONE : SCAN_FAIL;
if (valid_symbols[RANGE_EQ] && c == '=') {
advance(lexer);
lexer->mark_end(lexer);
return emit(lexer, RANGE_EQ) ? SCAN_DONE : SCAN_FAIL;
}
if (c == '<') {
advance(lexer);
c = lexer->lookahead;
if (c == '=') {
if (valid_symbols[RANGE_LT_EQ]) {
advance(lexer);
lexer->mark_end(lexer);
return emit(lexer, RANGE_LT_EQ) ? SCAN_DONE : SCAN_FAIL;
}
} else if (valid_symbols[RANGE_LT]) {
lexer->mark_end(lexer);
return emit(lexer, RANGE_LT) ? SCAN_DONE : SCAN_FAIL;
}
}
return SCAN_FAIL;
}
static bool scan_number(TSLexer *lexer, const bool *valid_symbols) {
bool has_dot = false;
bool has_digit = false;
bool has_e = false;
if (match_int(lexer)) {
has_digit = true;
lexer->mark_end(lexer);
}
if (lexer->lookahead == '.') {
has_dot = true;
advance(lexer);
if (lexer->lookahead == '.') {
if (has_digit && valid_symbols[INTEGER])
return emit(lexer, INTEGER);
return false;
}
if (is_digit(lexer->lookahead)) {
match_int(lexer);
lexer->mark_end(lexer);
has_digit = true;
} else {
lexer->mark_end(lexer);
consume_inline_whitespace(lexer);
if (lexer->lookahead == '.') {
if (has_digit && valid_symbols[INTEGER])
return emit(lexer, INTEGER);
return false;
}
}
}
if (match_int(lexer)) {
lexer->mark_end(lexer);
has_digit = true;
}
if (!has_digit)
return false;
if (lexer->lookahead == 'p') {
advance(lexer);
bool has_prec = match_int(lexer);
if (!has_prec)
return false;
lexer->mark_end(lexer);
}
if (lexer->lookahead == 'e' || lexer->lookahead == 'E') {
has_e = true;
advance(lexer);
if (lexer->lookahead == '+' || lexer->lookahead == '-')
advance(lexer);
bool valid_exp = match_int(lexer);
if (!valid_exp)
return false;
lexer->mark_end(lexer);
}
if (valid_symbols[FLOAT] && (has_dot || has_e))
return emit(lexer, FLOAT);
if (valid_symbols[INTEGER] && !has_dot && !has_e)
return emit(lexer, INTEGER);
return false;
}
static bool scan_raw_str_content_step(TSLexer *lexer, bool anything_found,
int slashes_found) {
while (!lexer->eof(lexer)) {
if (lexer->lookahead != '/') {
advance(lexer);
lexer->mark_end(lexer);
anything_found = true;
slashes_found = 0;
continue;
}
if (slashes_found == 2)
return anything_found;
advance(lexer);
slashes_found += 1;
}
return anything_found;
}
static bool emit_raw_str_content(TSLexer *lexer, const bool *valid_symbols) {
if (!valid_symbols[RAW_STRING_CONTENT])
return false;
if (!scan_raw_str_content_step(lexer, false, 0))
return false;
return emit(lexer, RAW_STRING_CONTENT);
}
static bool scan_raw_string(TSLexer *lexer, const bool *valid_symbols) {
if (lexer->eof(lexer))
return false;
if (lexer->lookahead != '/')
return emit_raw_str_content(lexer, valid_symbols);
advance(lexer); if (lexer->eof(lexer))
return false;
if (lexer->lookahead != '/')
return false;
advance(lexer); lexer->mark_end(lexer);
int extra = 0;
while (extra < 3 && lexer->lookahead == '/') {
advance(lexer);
extra++;
}
if (extra == 0) {
if (valid_symbols[RAW_STRING_CONTENT])
return emit(lexer, RAW_STRING_CONTENT);
return false;
}
if (extra == 1) {
lexer->mark_end(lexer); if (valid_symbols[RAW_STRING_END])
return emit(lexer, RAW_STRING_END);
return false;
}
if (valid_symbols[RAW_STRING_ESCAPE])
return emit(lexer, RAW_STRING_ESCAPE);
return false;
}
static bool scan_adjacency(TSLexer *lexer, const bool *valid_symbols,
int32_t c) {
lexer->mark_end(lexer);
if (c == '\n' || c == '\r')
return false;
if (c == '<') {
advance(lexer);
if (lexer->lookahead == '|') {
if (valid_symbols[SPACE_INDEXING])
return emit(lexer, SPACE_INDEXING);
if (valid_symbols[SPACE])
return emit(lexer, SPACE);
}
return false;
}
if (c == '/') {
advance(lexer);
if (lexer->lookahead == '/') {
advance(lexer);
if (lexer->lookahead == '/')
return emit(lexer, SPACE);
}
return false;
}
if (is_alpha(c)) {
if (is_adjacency_blocking_keyword_ahead(lexer))
return false;
return emit(lexer, SPACE);
}
if (c == '[') {
if (valid_symbols[SPACE_INDEXING])
return emit(lexer, SPACE_INDEXING);
if (valid_symbols[SPACE])
return emit(lexer, SPACE);
}
if (c == '(') {
advance(lexer);
if (lexer->lookahead == '*') {
advance(lexer);
if (lexer->lookahead == ')')
return false;
}
return emit(lexer, SPACE);
}
if (is_digit(c) || c == '{' || c == '"')
return emit(lexer, SPACE);
return false;
}
bool tree_sitter_macaulay2_external_scanner_scan(void *payload, TSLexer *lexer,
const bool *valid_symbols) {
if (can_scan_raw_string(valid_symbols))
return scan_raw_string(lexer, valid_symbols);
if (can_scan_number(valid_symbols) && !can_scan_adjacency(valid_symbols) &&
!can_scan_dot_operator(valid_symbols))
skip_number_whitespace(lexer);
else
skip_whitespace(lexer);
if (lexer->eof(lexer))
return false;
int32_t c = lexer->lookahead;
if (can_scan_dot_operator(valid_symbols)) {
ScanResult result = scan_dot_operator(lexer, valid_symbols);
if (result == SCAN_DONE)
return true;
if (result == SCAN_FAIL)
return false;
}
if (can_scan_number(valid_symbols))
return scan_number(lexer, valid_symbols);
return can_scan_adjacency(valid_symbols) &&
scan_adjacency(lexer, valid_symbols, c);
}