#include <tree_sitter/parser.h>
#include <cwctype>
#include <string>
#define debug 0
#define print(...) \
if (debug) printf(__VA_ARGS__)
#define peek() lexer->lookahead
#define next() \
{ \
print("next %s\n", str(peek()).c_str()); \
lexer->advance(lexer, false); \
}
#define skip() \
{ \
print("skip %s\n", str(peek()).c_str()); \
lexer->advance(lexer, true); \
}
#define stop() \
{ \
print("stop %s\n", str(peek()).c_str()); \
lexer->mark_end(lexer); \
}
#define set(symbol) \
{ \
print("set %s\n", TokenTypes[symbol]); \
lexer->result_symbol = symbol; \
}
#define ret(function, result) \
print("%s() -> %s\n", function, result ? "true" : "false"); \
return result;
namespace {
using std::string;
enum TokenType {
HEREDOC_START,
HEREDOC_START_NEWLINE,
HEREDOC_BODY,
HEREDOC_END_NEWLINE,
HEREDOC_END,
EMBEDDED_OPENING_BRACE,
};
const char *TokenTypes[] = {
"HEREDOC_START", "HEREDOC_START_NEWLINE", "HEREDOC_BODY", "HEREDOC_END_NEWLINE", "HEREDOC_END", "EMBEDDED_OPENING_BRACE", };
static string str(int32_t chr) {
switch (chr) {
case '\n':
return "\\n";
case '\r':
return "\\r";
case '\t':
return "\\t";
case ' ':
return "\\s";
case '\0':
return "\\0";
default:
if (iswspace(chr)) {
return "\\s";
}
string str;
str += chr;
return str;
}
}
struct Scanner {
unsigned serialize(char *buffer) {
if (delimiter.length() + 2 >= TREE_SITTER_SERIALIZATION_BUFFER_SIZE) return 0;
buffer[0] = is_nowdoc;
buffer[1] = did_start;
buffer[2] = did_end;
delimiter.copy(&buffer[3], delimiter.length());
return delimiter.length() + 3;
}
void deserialize(const char *buffer, unsigned length) {
if (length == 0) {
is_nowdoc = false;
did_start = false;
did_end = false;
delimiter.clear();
} else {
is_nowdoc = buffer[0];
did_start = buffer[1];
did_end = buffer[2];
delimiter.assign(&buffer[3], &buffer[length]);
}
}
bool scan(TSLexer *lexer, const bool *expected) {
print("\n> ");
if (expected[HEREDOC_START]) print("%s ", TokenTypes[HEREDOC_START]);
if (expected[HEREDOC_START_NEWLINE]) print("%s ", TokenTypes[HEREDOC_START_NEWLINE]);
if (expected[HEREDOC_BODY]) print("%s ", TokenTypes[HEREDOC_BODY]);
if (expected[HEREDOC_END_NEWLINE]) print("%s ", TokenTypes[HEREDOC_END_NEWLINE]);
if (expected[HEREDOC_END]) print("%s ", TokenTypes[HEREDOC_END]);
if (expected[EMBEDDED_OPENING_BRACE]) print("%s ", TokenTypes[EMBEDDED_OPENING_BRACE]);
print("\n");
if ((expected[HEREDOC_BODY] || expected[HEREDOC_END] || expected[EMBEDDED_OPENING_BRACE]) &&
!delimiter.empty()) {
return scan_body(lexer);
}
if (expected[HEREDOC_START]) {
return scan_start(lexer);
}
return false;
}
bool scan_start(TSLexer *lexer) {
print("scan_start() <-\n");
while (iswspace(peek())) skip();
is_nowdoc = peek() == '\'';
delimiter.clear();
int32_t quote = 0;
if (is_nowdoc || peek() == '"') {
quote = peek();
next();
}
if (iswalpha(peek()) || peek() == '_') {
delimiter += peek();
next();
while (iswalnum(peek()) || peek() == '_') {
delimiter += peek();
next();
}
}
print("del %s\n", delimiter.c_str());
if (peek() == quote) {
next();
} else if (quote != 0) {
ret("scan_start", false);
}
if (peek() != '\n' || delimiter.empty()) {
return false;
}
set(HEREDOC_START);
stop();
next();
if (scan_delimiter(lexer)) {
if (peek() == ';') next();
if (peek() == '\n') {
did_end = true;
}
}
ret("scan_start", true);
}
bool scan_delimiter(TSLexer *lexer) {
print("scan_delimiter() <-\n");
for (unsigned long index = 0; index < delimiter.length(); index++) {
if (delimiter[index] == peek()) {
next();
} else {
ret("scan_delimiter", false);
}
}
ret("scan_delimiter", true);
}
bool scan_body(TSLexer *lexer) {
print("scan_body() <-\n");
bool did_advance = false;
for (;;) {
if (peek() == '\0') {
return false;
}
if (peek() == '\\') {
next();
next();
did_advance = true;
continue;
}
if ((peek() == '{' || peek() == '$') && !is_nowdoc) {
stop();
if (peek() == '{') {
next();
if (peek() == '$' && !did_advance) {
stop();
next();
if (is_identifier_start_char(peek())) {
set(EMBEDDED_OPENING_BRACE);
ret("scan_body", true);
}
}
}
if (peek() == '$') {
next();
if (is_identifier_start_char(peek())) {
set(HEREDOC_BODY);
ret("scan_body", did_advance);
}
}
did_advance = true;
continue;
}
if (did_end || peek() == '\n') {
if (did_advance) {
stop();
next();
} else if (peek() == '\n') {
if (did_end) {
skip();
} else {
next();
stop();
}
}
if (scan_delimiter(lexer)) {
if (!did_advance && did_end) stop();
if (peek() == ';') next();
if (peek() == '\n') {
if (did_advance) {
set(HEREDOC_BODY);
did_start = true;
did_end = true;
} else if (did_end) {
set(HEREDOC_END);
delimiter.clear();
is_nowdoc = false;
did_start = false;
did_end = false;
} else {
set(HEREDOC_END_NEWLINE);
did_start = true;
did_end = true;
}
ret("scan_body", true);
}
} else if (!did_start && !did_advance) {
did_start = true;
set(HEREDOC_START_NEWLINE);
ret("scan_body", true);
}
did_advance = true;
continue;
}
next();
did_advance = true;
}
}
bool is_identifier_start_char(int32_t c) {
return (c == '_') || ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || (128 <= c && c <= 255);
}
string delimiter;
bool is_nowdoc;
bool did_start;
bool did_end;
};
}
extern "C" {
void *tree_sitter_hack_external_scanner_create() { return new Scanner(); }
bool tree_sitter_hack_external_scanner_scan(void *payload, TSLexer *lexer, const bool *expected) {
Scanner *scanner = static_cast<Scanner *>(payload);
return scanner->scan(lexer, expected);
}
unsigned tree_sitter_hack_external_scanner_serialize(void *payload, char *state) {
Scanner *scanner = static_cast<Scanner *>(payload);
return scanner->serialize(state);
}
void tree_sitter_hack_external_scanner_deserialize(
void *payload, const char *state, unsigned length) {
Scanner *scanner = static_cast<Scanner *>(payload);
scanner->deserialize(state, length);
}
void tree_sitter_hack_external_scanner_destroy(void *payload) {
Scanner *scanner = static_cast<Scanner *>(payload);
delete scanner;
}
}