184 lines
5.9 KiB
C
Generated
184 lines
5.9 KiB
C
Generated
#include "tree_sitter/parser.h"
|
|
|
|
/// Block comments and immediate parentheses are easy to parse, but strings
|
|
/// require extra-attention.
|
|
///
|
|
/// The main problems that arise when parsing strings are:
|
|
/// 1. Triple quoted strings allow single quotes inside. e.g. """ "foo" """.
|
|
/// 2. Strings can have arbitrary interpolations, including other strings.
|
|
/// e.g. "echo $("foo")"
|
|
/// 3. Non-standard string literals don't allow interpolations or escape
|
|
/// sequences, but you can always write \" and \`.
|
|
/// All of the above also applies to command literals.
|
|
enum TokenType {
|
|
BLOCK_COMMENT_REST,
|
|
IMMEDIATE_PAREN,
|
|
IMMEDIATE_BRACKET,
|
|
IMMEDIATE_BRACE,
|
|
IMMEDIATE_STRING_START,
|
|
IMMEDIATE_COMMAND_START,
|
|
CONTENT_CMD_1,
|
|
CONTENT_CMD_1_RAW,
|
|
CONTENT_CMD_3,
|
|
CONTENT_CMD_3_RAW,
|
|
CONTENT_STR_1,
|
|
CONTENT_STR_1_RAW,
|
|
CONTENT_STR_3,
|
|
CONTENT_STR_3_RAW,
|
|
END_CMD,
|
|
END_STR,
|
|
};
|
|
|
|
void *tree_sitter_julia_external_scanner_create() {
|
|
return NULL;
|
|
}
|
|
|
|
void tree_sitter_julia_external_scanner_destroy(void *payload) {}
|
|
|
|
unsigned tree_sitter_julia_external_scanner_serialize(void *payload, char *buffer) { return 0; }
|
|
|
|
void tree_sitter_julia_external_scanner_deserialize(void *payload, const char *buffer, unsigned size) {}
|
|
|
|
// Scanner functions
|
|
|
|
static void advance(TSLexer *lexer) { lexer->advance(lexer, false); }
|
|
|
|
static void mark_end(TSLexer *lexer) { lexer->mark_end(lexer); }
|
|
|
|
static bool scan_content(TSLexer *lexer, TSSymbol content_symbol, char end_char, unsigned n_delim, bool interp) {
|
|
TSSymbol end_symbol = (end_char == '"') ? END_STR : END_CMD;
|
|
bool has_content = false;
|
|
int32_t next;
|
|
while ((next = lexer->lookahead)) {
|
|
mark_end(lexer);
|
|
if (interp && (next == '$' || next == '\\')) {
|
|
lexer->result_symbol = content_symbol;
|
|
return has_content;
|
|
} else if (next == '\\') {
|
|
// Parse backslash in raw strings (check escaped delimiters and '\\')
|
|
advance(lexer);
|
|
next = lexer->lookahead;
|
|
if (next == end_char || next == '\\') {
|
|
lexer->result_symbol = content_symbol;
|
|
return has_content;
|
|
}
|
|
} else {
|
|
bool is_end_delimiter = true;
|
|
for (unsigned i = 1; i <= n_delim; i++) {
|
|
if (lexer->lookahead == end_char) {
|
|
advance(lexer);
|
|
} else {
|
|
is_end_delimiter = false;
|
|
break;
|
|
}
|
|
}
|
|
if (is_end_delimiter) {
|
|
if (has_content) {
|
|
lexer->result_symbol = content_symbol;
|
|
return true;
|
|
} else {
|
|
mark_end(lexer);
|
|
lexer->result_symbol = end_symbol;
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
advance(lexer);
|
|
has_content = true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static bool scan_block_comment(TSLexer *lexer) {
|
|
// NOTE: The first `#=` is scanned by tree-sitter
|
|
bool after_eq = false;
|
|
unsigned nesting_depth = 1;
|
|
for (;;) {
|
|
switch (lexer->lookahead) {
|
|
case '=':
|
|
advance(lexer);
|
|
after_eq = true;
|
|
break;
|
|
case '#':
|
|
advance(lexer);
|
|
if (after_eq) {
|
|
after_eq = false;
|
|
nesting_depth--;
|
|
if (nesting_depth == 0) {
|
|
lexer->result_symbol = BLOCK_COMMENT_REST;
|
|
return true;
|
|
}
|
|
} else {
|
|
after_eq = false;
|
|
if (lexer->lookahead == '=') {
|
|
advance(lexer);
|
|
nesting_depth++;
|
|
}
|
|
}
|
|
break;
|
|
case '\0':
|
|
return false;
|
|
default:
|
|
advance(lexer);
|
|
after_eq = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool tree_sitter_julia_external_scanner_scan(void *payload, TSLexer *lexer, const bool *valid_symbols) {
|
|
if (valid_symbols[IMMEDIATE_PAREN] && lexer->lookahead == '(') {
|
|
lexer->result_symbol = IMMEDIATE_PAREN;
|
|
return true;
|
|
} else if (valid_symbols[IMMEDIATE_BRACKET] && lexer->lookahead == '[') {
|
|
lexer->result_symbol = IMMEDIATE_BRACKET;
|
|
return true;
|
|
} else if (valid_symbols[IMMEDIATE_BRACE] && lexer->lookahead == '{') {
|
|
lexer->result_symbol = IMMEDIATE_BRACE;
|
|
return true;
|
|
} else if (valid_symbols[IMMEDIATE_STRING_START] && lexer->lookahead == '"') {
|
|
lexer->result_symbol = IMMEDIATE_STRING_START;
|
|
return true;
|
|
} else if (valid_symbols[IMMEDIATE_COMMAND_START] && lexer->lookahead == '`') {
|
|
lexer->result_symbol = IMMEDIATE_COMMAND_START;
|
|
return true;
|
|
}
|
|
|
|
if (valid_symbols[BLOCK_COMMENT_REST] && scan_block_comment(lexer)) {
|
|
return true;
|
|
}
|
|
|
|
if (valid_symbols[CONTENT_STR_1] && scan_content(lexer, CONTENT_STR_1, '"', 1, true)) {
|
|
return true;
|
|
}
|
|
|
|
if (valid_symbols[CONTENT_STR_3] && scan_content(lexer, CONTENT_STR_3, '"', 3, true)) {
|
|
return true;
|
|
}
|
|
|
|
if (valid_symbols[CONTENT_CMD_1] && scan_content(lexer, CONTENT_CMD_1, '`', 1, true)) {
|
|
return true;
|
|
}
|
|
|
|
if (valid_symbols[CONTENT_CMD_3] && scan_content(lexer, CONTENT_CMD_3, '`', 3, true)) {
|
|
return true;
|
|
}
|
|
|
|
if (valid_symbols[CONTENT_STR_1_RAW] && scan_content(lexer, CONTENT_STR_1_RAW, '"', 1, false)) {
|
|
return true;
|
|
}
|
|
|
|
if (valid_symbols[CONTENT_STR_3_RAW] && scan_content(lexer, CONTENT_STR_3_RAW, '"', 3, false)) {
|
|
return true;
|
|
}
|
|
|
|
if (valid_symbols[CONTENT_CMD_1_RAW] && scan_content(lexer, CONTENT_CMD_1_RAW, '`', 1, false)) {
|
|
return true;
|
|
}
|
|
|
|
if (valid_symbols[CONTENT_CMD_3_RAW] && scan_content(lexer, CONTENT_CMD_3_RAW, '`', 3, false)) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
} |