#include "tree_sitter/parser.h"
#include <stdio.h>
#include <wctype.h>
enum TokenType {
AUTOMATIC_SEMICOLON,
TEMPLATE_CHARS,
TERNARY_QMARK,
ELVIS_OPERATOR,
LOGICAL_OR,
REGEX_PATTERN,
QUERY_TEXT,
TAG_LINEFEED,
CFML_TEMPLATE_CONTENT,
CFML_COMMENT
};
void *tree_sitter_cfscript_external_scanner_create() { return NULL; }
void tree_sitter_cfscript_external_scanner_destroy(void *p) {}
unsigned tree_sitter_cfscript_external_scanner_serialize(void *payload, char *buffer) { return 0; }
void tree_sitter_cfscript_external_scanner_deserialize(void *p, const char *b, unsigned n) {}
static inline void advance(TSLexer *lexer) { lexer->advance(lexer, false); }
static inline void skip(TSLexer *lexer) { lexer->advance(lexer, true); }
static bool scan_template_chars(TSLexer *lexer) {
lexer->result_symbol = TEMPLATE_CHARS;
for (bool has_content = false;; has_content = true) {
lexer->mark_end(lexer);
switch (lexer->lookahead) {
case '`':
return has_content;
case '\0':
return false;
case '$':
advance(lexer);
if (lexer->lookahead == '{') {
return has_content;
}
break;
case '\\':
return has_content;
default:
advance(lexer);
}
}
}
typedef enum {
REJECT, NO_NEWLINE, ACCEPT, } WhitespaceResult;
static WhitespaceResult scan_whitespace_and_comments(TSLexer *lexer, bool *scanned_comment, bool consume) {
bool saw_block_newline = false;
for (;;) {
while (iswspace(lexer->lookahead) ) {
skip(lexer);
}
if (lexer->lookahead == '/') {
skip(lexer);
if (lexer->lookahead == '/') {
skip(lexer);
while (lexer->lookahead != 0 && lexer->lookahead != '\n' && lexer->lookahead != 0x2028 &&
lexer->lookahead != 0x2029) {
skip(lexer);
}
*scanned_comment = true;
} else if (lexer->lookahead == '*') {
skip(lexer);
while (lexer->lookahead != 0) {
if (lexer->lookahead == '*') {
skip(lexer);
if (lexer->lookahead == '/') {
skip(lexer);
*scanned_comment = true;
if (lexer->lookahead != '/' && !consume) {
return saw_block_newline ? ACCEPT : NO_NEWLINE;
}
break;
}
} else if (lexer->lookahead == '\n' || lexer->lookahead == 0x2028 || lexer->lookahead == 0x2029) {
saw_block_newline = true;
skip(lexer);
} else {
skip(lexer);
}
}
} else {
return REJECT;
}
} else {
return ACCEPT;
}
}
}
static bool scan_cfml_word_operator(TSLexer *lexer) {
char buf[11] = {0};
int len = 0;
for (; len < 10 && iswalpha(lexer->lookahead); len++) {
buf[len] = towlower(lexer->lookahead);
skip(lexer);
}
bool at_end = !iswalnum(lexer->lookahead);
if (!at_end) return false;
return (len == 2 && (
(buf[0] == 'o' && buf[1] == 'r') ||
(buf[0] == 'e' && buf[1] == 'q') ||
(buf[0] == 'g' && buf[1] == 't') ||
(buf[0] == 'g' && buf[1] == 'e') ||
(buf[0] == 'l' && buf[1] == 't') ||
(buf[0] == 'l' && buf[1] == 'e') ||
(buf[0] == 'i' && buf[1] == 'n')
)) || (len == 3 && (
(buf[0] == 'a' && buf[1] == 'n' && buf[2] == 'd') ||
(buf[0] == 'n' && buf[1] == 'e' && buf[2] == 'q') ||
(buf[0] == 'n' && buf[1] == 'o' && buf[2] == 't') ||
(buf[0] == 'g' && buf[1] == 't' && buf[2] == 'e') ||
(buf[0] == 'l' && buf[1] == 't' && buf[2] == 'e') ||
(buf[0] == 'm' && buf[1] == 'o' && buf[2] == 'd')
)) || (len == 10 &&
buf[0] == 'i' && buf[1] == 'n' && buf[2] == 's' && buf[3] == 't' &&
buf[4] == 'a' && buf[5] == 'n' && buf[6] == 'c' && buf[7] == 'e' &&
buf[8] == 'o' && buf[9] == 'f'
);
}
static bool scan_automatic_semicolon(TSLexer *lexer, bool comment_condition, bool *scanned_comment) {
lexer->result_symbol = AUTOMATIC_SEMICOLON;
lexer->mark_end(lexer);
for (;;) {
if (lexer->lookahead == 0) {
return true;
}
if (lexer->lookahead == '/') {
WhitespaceResult result = scan_whitespace_and_comments(lexer, scanned_comment, false);
if (result == REJECT) {
return false;
}
if (result == ACCEPT && comment_condition && lexer->lookahead != ',' && lexer->lookahead != '=') {
return true;
}
}
if (lexer->lookahead == '}') {
return true;
}
if (lexer->is_at_included_range_start(lexer)) {
return true;
}
if (lexer->lookahead == '\n' || lexer->lookahead == 0x2028 || lexer->lookahead == 0x2029) {
break;
}
if (!iswspace(lexer->lookahead)) {
return false;
}
skip(lexer);
}
skip(lexer);
if (scan_whitespace_and_comments(lexer, scanned_comment, true) == REJECT) {
return false;
}
switch (lexer->lookahead) {
case '`':
case ',':
case ':':
case ';':
case '*':
case '%':
case '>':
case '<':
case '=':
case '[':
case '(':
case '?':
case '^':
case '|':
case '&':
case '/':
return false;
case '.':
skip(lexer);
return iswdigit(lexer->lookahead);
case '+':
skip(lexer);
return lexer->lookahead == '+';
case '-':
skip(lexer);
return lexer->lookahead == '-';
case '!':
skip(lexer);
return lexer->lookahead != '=';
case 'a': case 'A':
case 'o': case 'O':
case 'e': case 'E':
case 'n': case 'N':
case 'g': case 'G':
case 'l': case 'L':
case 'm': case 'M':
case 'i':
return !scan_cfml_word_operator(lexer);
default:
break;
}
return true;
}
static bool scan_ternary_qmark(TSLexer *lexer) {
for (;;) {
if (!iswspace(lexer->lookahead)) {
break;
}
skip(lexer);
}
if (lexer->lookahead == '?') {
advance(lexer);
while (lexer->lookahead == ' ' || lexer->lookahead == '\t') {
advance(lexer);
}
if (lexer->lookahead == ':') {
advance(lexer);
lexer->mark_end(lexer);
lexer->result_symbol = ELVIS_OPERATOR;
return true;
} else if (lexer->lookahead == '?') {
return false;
}
lexer->mark_end(lexer);
lexer->result_symbol = TERNARY_QMARK;
if (lexer->lookahead == '.') {
advance(lexer);
if (iswdigit(lexer->lookahead)) {
return true;
}
return false;
}
return true;
}
return false;
}
static bool scan_query_text(TSLexer *lexer) {
bool saw_text = false;
bool at_newline = false;
while (lexer->lookahead != 0 && lexer->lookahead != '"') {
bool is_wspace = iswspace(lexer->lookahead);
if (lexer->lookahead == '\n') {
at_newline = true;
} else {
at_newline &= is_wspace;
if (!at_newline) {
saw_text = true;
}
}
advance(lexer);
}
lexer->result_symbol = QUERY_TEXT;
return saw_text;
}
static bool scan_cfml_template_content(TSLexer *lexer) {
lexer->result_symbol = CFML_TEMPLATE_CONTENT;
bool has_content = false;
while (lexer->lookahead != 0) {
if (lexer->lookahead == '`') {
lexer->mark_end(lexer);
advance(lexer);
if (lexer->lookahead == '`') {
advance(lexer);
if (lexer->lookahead == '`') {
return has_content;
}
}
has_content = true;
} else {
advance(lexer);
has_content = true;
}
}
return false;
}
static bool scan_cfml_comment(TSLexer *lexer) {
if (lexer->lookahead != '<') return false;
advance(lexer);
if (lexer->lookahead != '!') return false;
advance(lexer);
for (int i = 0; i < 3; i++) {
if (lexer->lookahead != '-') return false;
advance(lexer);
}
unsigned depth = 1;
while (lexer->lookahead) {
if (lexer->lookahead == '<') {
advance(lexer);
if (lexer->lookahead != '!') continue;
advance(lexer);
unsigned dashes = 0;
while (lexer->lookahead == '-') {
dashes++;
advance(lexer);
}
if (dashes >= 3) depth++;
continue;
}
if (lexer->lookahead == '-') {
unsigned dashes = 0;
while (lexer->lookahead == '-') {
dashes++;
advance(lexer);
}
if (dashes >= 3 && lexer->lookahead == '>') {
advance(lexer);
if (--depth == 0) {
lexer->result_symbol = CFML_COMMENT;
lexer->mark_end(lexer);
return true;
}
}
continue;
}
advance(lexer);
}
return false;
}
bool tree_sitter_cfscript_external_scanner_scan(void *payload, TSLexer *lexer, const bool *valid_symbols) {
if (valid_symbols[CFML_TEMPLATE_CONTENT]) {
return scan_cfml_template_content(lexer);
}
if (valid_symbols[TEMPLATE_CHARS]) {
if (valid_symbols[AUTOMATIC_SEMICOLON]) {
return false;
}
return scan_template_chars(lexer);
}
if (valid_symbols[QUERY_TEXT] && scan_query_text(lexer)) {
return true;
}
if (valid_symbols[CFML_COMMENT] && !valid_symbols[AUTOMATIC_SEMICOLON] &&
!valid_symbols[TERNARY_QMARK] && !valid_symbols[ELVIS_OPERATOR]) {
while (iswspace(lexer->lookahead)) skip(lexer);
return lexer->lookahead == '<' && scan_cfml_comment(lexer);
}
if (valid_symbols[AUTOMATIC_SEMICOLON] && !valid_symbols[TAG_LINEFEED]) {
bool scanned_comment = false;
bool ret = scan_automatic_semicolon(lexer, !valid_symbols[LOGICAL_OR], &scanned_comment);
if (!ret && !scanned_comment && valid_symbols[TERNARY_QMARK] && lexer->lookahead == '?') {
return scan_ternary_qmark(lexer);
}
if (!ret && valid_symbols[CFML_COMMENT] && lexer->lookahead == '<' && scan_cfml_comment(lexer)) {
return true;
}
return ret;
}
if (valid_symbols[AUTOMATIC_SEMICOLON] && valid_symbols[TAG_LINEFEED]) {
lexer->mark_end(lexer);
while (iswspace(lexer->lookahead)) skip(lexer);
if (lexer->lookahead == '}' || lexer->lookahead == 0) {
lexer->result_symbol = AUTOMATIC_SEMICOLON;
return true;
}
}
if (valid_symbols[TERNARY_QMARK] || valid_symbols[ELVIS_OPERATOR]) {
return scan_ternary_qmark(lexer);
}
return false;
}