#include <tree_sitter/parser.h>
enum TokenType {
STRING_FRAGMENT,
INDENTED_STRING_FRAGMENT,
PATH_START,
PATH_FRAGMENT,
DOLLAR_ESCAPE,
INDENTED_DOLLAR_ESCAPE,
};
static void advance(TSLexer *lexer) { lexer->advance(lexer, false); }
static void skip(TSLexer *lexer) { lexer->advance(lexer, true); }
static bool scan_dollar_escape(TSLexer *lexer) {
lexer->result_symbol = DOLLAR_ESCAPE;
advance(lexer);
lexer->mark_end(lexer);
if (lexer->lookahead == '$') {
return true;
} else {
return false;
}
}
static bool scan_indented_dollar_escape(TSLexer *lexer) {
lexer->result_symbol = INDENTED_DOLLAR_ESCAPE;
advance(lexer);
lexer->mark_end(lexer);
if (lexer->lookahead == '$') {
return true;
} else {
if (lexer->lookahead == '\\') {
advance(lexer);
if (lexer->lookahead == '$') {
lexer->mark_end(lexer);
return true;
}
}
return false;
}
}
static bool scan_string_fragment(TSLexer *lexer) {
lexer->result_symbol = STRING_FRAGMENT;
for (bool has_content = false;; has_content = true) {
lexer->mark_end(lexer);
switch (lexer->lookahead) {
case '"':
case '\\':
return has_content;
case '$':
advance(lexer);
if (lexer->lookahead == '{') {
return has_content;
} else if (lexer->lookahead != '"' && lexer->lookahead != '\\') {
advance(lexer);
}
break;
case '\0':
return false;
default:
advance(lexer);
}
}
}
static bool scan_indented_string_fragment(TSLexer *lexer) {
lexer->result_symbol = INDENTED_STRING_FRAGMENT;
for (bool has_content = false;; has_content = true) {
lexer->mark_end(lexer);
switch (lexer->lookahead) {
case '$':
advance(lexer);
if (lexer->lookahead == '{') {
return has_content;
} else if (lexer->lookahead != '\'') {
advance(lexer);
}
break;
case '\'':
advance(lexer);
if (lexer->lookahead == '\'') {
return has_content;
}
break;
case '\0':
return false;
default:
advance(lexer);
}
}
}
static bool is_path_char(int32_t c) {
return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') ||
(c >= 'A' && c <= 'Z') || c == '-' || c == '+' || c == '_' ||
c == '.' || c == '/';
}
static bool scan_path_start(TSLexer *lexer) {
lexer->result_symbol = PATH_START;
bool have_sep = false;
bool have_after_sep = false;
int32_t c = lexer->lookahead;
while (c == ' ' || c == '\n' || c == '\r' || c == '\t') {
skip(lexer);
c = lexer->lookahead;
}
while (true) {
lexer->mark_end(lexer);
c = lexer->lookahead;
if (c == '/') {
have_sep = true;
} else if (is_path_char(c)) {
if (have_sep) {
have_after_sep = true;
}
} else if (c == '$') {
return have_sep;
} else {
return have_after_sep;
}
advance(lexer);
}
}
static bool scan_path_fragment(TSLexer *lexer) {
lexer->result_symbol = PATH_FRAGMENT;
for (bool has_content = false;; has_content = true) {
lexer->mark_end(lexer);
if (!is_path_char(lexer->lookahead)) {
return has_content;
}
advance(lexer);
}
}
void *tree_sitter_nix_external_scanner_create() { return NULL; }
bool tree_sitter_nix_external_scanner_scan(void *payload, TSLexer *lexer,
const bool *valid_symbols) {
if (valid_symbols[STRING_FRAGMENT] &&
valid_symbols[INDENTED_STRING_FRAGMENT] && valid_symbols[PATH_START] &&
valid_symbols[PATH_FRAGMENT] && valid_symbols[DOLLAR_ESCAPE] &&
valid_symbols[INDENTED_DOLLAR_ESCAPE]) {
return false;
} else if (valid_symbols[STRING_FRAGMENT]) {
if (lexer->lookahead == '\\') {
return scan_dollar_escape(lexer);
}
return scan_string_fragment(lexer);
} else if (valid_symbols[INDENTED_STRING_FRAGMENT]) {
if (lexer->lookahead == '\'') {
lexer->mark_end(lexer);
advance(lexer);
if (lexer->lookahead == '\'') {
return scan_indented_dollar_escape(lexer);
}
}
return scan_indented_string_fragment(lexer);
} else if (valid_symbols[PATH_FRAGMENT] && is_path_char(lexer->lookahead)) {
return scan_path_fragment(lexer);
} else if (valid_symbols[PATH_START]) {
return scan_path_start(lexer);
}
return false;
}
unsigned tree_sitter_nix_external_scanner_serialize(void *payload,
char *buffer) {
return 0;
}
void tree_sitter_nix_external_scanner_deserialize(void *payload,
const char *buffer,
unsigned length) {}
void tree_sitter_nix_external_scanner_destroy(void *payload) {}