#include "tree_sitter/parser.h"
#include <stdint.h>
#include <stdio.h>
#include <assert.h>
#include <ctype.h>
#include <string.h>
#include <wchar.h>
#include <wctype.h>
enum TokenType {
AUTOMATIC_SEMICOLON,
TEMPLATE_CHARS,
TERNARY_QMARK,
LOGICAL_OR,
ESCAPE_SEQUENCE,
REGEX_PATTERN,
JSX_TEXT,
LINE_ENDING,
SOFT_LINE_ENDING,
BLOCK_CLOSE,
BLOCK_CONTINUATION,
BLOCK_QUOTE_START,
INDENTED_CHUNK_START,
ATX_H1_MARKER,
ATX_H2_MARKER,
ATX_H3_MARKER,
ATX_H4_MARKER,
ATX_H5_MARKER,
ATX_H6_MARKER,
SETEXT_H1_UNDERLINE,
SETEXT_H2_UNDERLINE,
THEMATIC_BREAK,
LIST_MARKER_MINUS,
LIST_MARKER_PLUS,
LIST_MARKER_STAR,
LIST_MARKER_PARENTHESIS,
LIST_MARKER_DOT,
LIST_MARKER_MINUS_DONT_INTERRUPT,
LIST_MARKER_PLUS_DONT_INTERRUPT,
LIST_MARKER_STAR_DONT_INTERRUPT,
LIST_MARKER_PARENTHESIS_DONT_INTERRUPT,
LIST_MARKER_DOT_DONT_INTERRUPT,
FENCED_CODE_BLOCK_START_BACKTICK,
FENCED_CODE_BLOCK_START_TILDE,
BLANK_LINE_START,
FENCED_CODE_BLOCK_END_BACKTICK,
FENCED_CODE_BLOCK_END_TILDE,
CLOSE_BLOCK,
NO_INDENTED_CHUNK,
ERROR,
TRIGGER_ERROR,
TOKEN_EOF,
MINUS_METADATA,
PLUS_METADATA,
PIPE_TABLE_START,
PIPE_TABLE_LINE_ENDING,
CODE_SPAN_START,
CODE_SPAN_CLOSE,
UNCLOSED_SPAN,
};
typedef enum {
BLOCK_QUOTE,
LIST_ITEM,
LIST_ITEM_1_INDENTATION,
LIST_ITEM_2_INDENTATION,
LIST_ITEM_3_INDENTATION,
LIST_ITEM_4_INDENTATION,
LIST_ITEM_5_INDENTATION,
LIST_ITEM_6_INDENTATION,
LIST_ITEM_7_INDENTATION,
LIST_ITEM_8_INDENTATION,
LIST_ITEM_9_INDENTATION,
LIST_ITEM_10_INDENTATION,
LIST_ITEM_11_INDENTATION,
LIST_ITEM_12_INDENTATION,
LIST_ITEM_13_INDENTATION,
LIST_ITEM_14_INDENTATION,
LIST_ITEM_MAX_INDENTATION,
FENCED_CODE_BLOCK,
ANONYMOUS,
} Block;
static bool is_punctuation(char chr) {
return (chr >= '!' && chr <= '/') || (chr >= ':' && chr <= '@') ||
(chr >= '[' && chr <= '`') || (chr >= '{' && chr <= '~');
}
static uint8_t list_item_indentation(Block block) {
return (uint8_t)(block - LIST_ITEM + 2);
}
static const bool paragraph_interrupt_symbols[] = {
false, false, false, false, true, false, true, true, true, true, true, true, true, true, true, true, true, true, true, true, false, false, false, false, false, true, true, true, false, false, false, false, false, false, false, false, false, true, false, };
static const uint8_t STATE_MATCHING = 0x1 << 0;
static const uint8_t STATE_WAS_SOFT_LINE_BREAK = 0x1 << 1;
static const uint8_t STATE_CLOSE_BLOCK = 0x1 << 4;
static size_t roundup_32(size_t x) {
x--;
x |= x >> 1;
x |= x >> 2;
x |= x >> 4;
x |= x >> 8;
x |= x >> 16;
x++;
return x;
}
typedef struct {
struct {
size_t size;
size_t capacity;
Block *items;
} open_blocks;
uint8_t state;
uint8_t matched;
uint8_t indentation;
uint8_t column;
uint8_t fenced_code_block_delimiter_length;
uint8_t code_span_delimiter_length;
bool simulate;
} Scanner;
static size_t advance(Scanner *s, TSLexer *lexer) {
size_t size = 1;
if (lexer->lookahead == '\t') {
size = 4 - s->column;
s->column = 0;
} else {
s->column = (s->column + 1) % 4;
}
lexer->advance(lexer, false);
return size;
}
static inline void skip(TSLexer *lexer) { lexer->advance(lexer, true); }
static bool scan_template_chars(Scanner *s, 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(s, lexer);
if (lexer->lookahead == '{') {
return has_content;
}
break;
case '\\':
return has_content;
default:
advance(s, lexer);
}
}
}
typedef enum {
REJECT, NO_NEWLINE, ACCEPT, } WhitespaceResult;
static WhitespaceResult scan_whitespace_and_comments(Scanner *s, 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_automatic_semicolon(Scanner *s, 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(s, 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(s, 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 'i':
skip(lexer);
if (lexer->lookahead != 'n') {
return true;
}
skip(lexer);
if (!iswalpha(lexer->lookahead)) {
return false;
}
for (unsigned i = 0; i < 8; i++) {
if (lexer->lookahead != "stanceof"[i]) {
return true;
}
skip(lexer);
}
if (!iswalpha(lexer->lookahead)) {
return false;
}
break;
default:
break;
}
return true;
}
static bool scan_ternary_qmark(Scanner *s, TSLexer *lexer) {
for (;;) {
if (!iswspace(lexer->lookahead)) {
break;
}
skip(lexer);
}
if (lexer->lookahead == '?') {
advance(s, lexer);
if (lexer->lookahead == '?') {
return false;
}
lexer->mark_end(lexer);
lexer->result_symbol = TERNARY_QMARK;
if (lexer->lookahead == '.') {
advance(s, lexer);
if (iswdigit(lexer->lookahead)) {
return true;
}
return false;
}
return true;
}
return false;
}
static bool scan_jsx_text(Scanner *s, TSLexer *lexer) {
bool saw_text = false;
bool at_newline = false;
while (lexer->lookahead != 0 && lexer->lookahead != '<' && lexer->lookahead != '>' && lexer->lookahead != '{' &&
lexer->lookahead != '}' && 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(s, lexer);
}
lexer->result_symbol = JSX_TEXT;
return saw_text;
}
static void push_block(Scanner *s, Block b) {
if (s->open_blocks.size == s->open_blocks.capacity) {
s->open_blocks.capacity =
s->open_blocks.capacity ? s->open_blocks.capacity << 1 : 8;
void *tmp = realloc(s->open_blocks.items,
sizeof(Block) * s->open_blocks.capacity);
assert(tmp != NULL);
s->open_blocks.items = tmp;
}
s->open_blocks.items[s->open_blocks.size++] = b;
}
static inline Block pop_block(Scanner *s) {
return s->open_blocks.items[--s->open_blocks.size];
}
static unsigned serialize(Scanner *s, char *buffer) {
unsigned size = 0;
buffer[size++] = (char)s->state;
buffer[size++] = (char)s->matched;
buffer[size++] = (char)s->indentation;
buffer[size++] = (char)s->column;
buffer[size++] = (char)s->fenced_code_block_delimiter_length;
buffer[size++] = (char)s->code_span_delimiter_length;
size_t blocks_count = s->open_blocks.size;
if (blocks_count > 0) {
memcpy(&buffer[size], s->open_blocks.items,
blocks_count * sizeof(Block));
size += blocks_count * sizeof(Block);
}
return size;
}
static void deserialize(Scanner *s, const char *buffer, unsigned length) {
s->open_blocks.size = 0;
s->open_blocks.capacity = 0;
s->state = 0;
s->matched = 0;
s->indentation = 0;
s->column = 0;
s->fenced_code_block_delimiter_length = 0;
s->code_span_delimiter_length = 0;
if (length > 0) {
size_t size = 0;
s->state = (uint8_t)buffer[size++];
s->matched = (uint8_t)buffer[size++];
s->indentation = (uint8_t)buffer[size++];
s->column = (uint8_t)buffer[size++];
s->fenced_code_block_delimiter_length = (uint8_t)buffer[size++];
s->code_span_delimiter_length = (uint8_t)buffer[size++];
size_t blocks_size = length - size;
if (blocks_size > 0) {
size_t blocks_count = blocks_size / sizeof(Block);
if (s->open_blocks.capacity < blocks_count) {
size_t capacity = roundup_32(blocks_count);
void *tmp = realloc(s->open_blocks.items,
sizeof(Block) * capacity);
assert(tmp != NULL);
s->open_blocks.items = tmp;
s->open_blocks.capacity = capacity;
}
memcpy(s->open_blocks.items, &buffer[size], blocks_size);
s->open_blocks.size = blocks_count;
}
}
}
static void mark_end(Scanner *s, TSLexer *lexer) {
if (!s->simulate) {
lexer->mark_end(lexer);
}
}
static bool error(TSLexer *lexer) {
lexer->result_symbol = ERROR;
return true;
}
static bool match(Scanner *s, TSLexer *lexer, Block block) {
switch (block) {
case LIST_ITEM:
case LIST_ITEM_1_INDENTATION:
case LIST_ITEM_2_INDENTATION:
case LIST_ITEM_3_INDENTATION:
case LIST_ITEM_4_INDENTATION:
case LIST_ITEM_5_INDENTATION:
case LIST_ITEM_6_INDENTATION:
case LIST_ITEM_7_INDENTATION:
case LIST_ITEM_8_INDENTATION:
case LIST_ITEM_9_INDENTATION:
case LIST_ITEM_10_INDENTATION:
case LIST_ITEM_11_INDENTATION:
case LIST_ITEM_12_INDENTATION:
case LIST_ITEM_13_INDENTATION:
case LIST_ITEM_14_INDENTATION:
case LIST_ITEM_MAX_INDENTATION:
while (s->indentation < list_item_indentation(block)) {
if (lexer->lookahead == ' ' || lexer->lookahead == '\t') {
s->indentation += advance(s, lexer);
} else {
break;
}
}
if (s->indentation >= list_item_indentation(block)) {
s->indentation -= list_item_indentation(block);
return true;
}
if (lexer->lookahead == '\n' || lexer->lookahead == '\r') {
s->indentation = 0;
return true;
}
break;
case BLOCK_QUOTE:
while (lexer->lookahead == ' ' || lexer->lookahead == '\t') {
s->indentation += advance(s, lexer);
}
if (lexer->lookahead == '>') {
advance(s, lexer);
s->indentation = 0;
if (lexer->lookahead == ' ' || lexer->lookahead == '\t') {
s->indentation += advance(s, lexer) - 1;
}
return true;
}
break;
case FENCED_CODE_BLOCK:
case ANONYMOUS:
return true;
}
return false;
}
static bool parse_fenced_code_block(Scanner *s, const char delimiter,
TSLexer *lexer, const bool *valid_symbols) {
uint8_t level = 0;
while (lexer->lookahead == delimiter) {
advance(s, lexer);
level++;
}
mark_end(s, lexer);
if ((delimiter == '`' ? valid_symbols[FENCED_CODE_BLOCK_END_BACKTICK]
: valid_symbols[FENCED_CODE_BLOCK_END_TILDE]) &&
s->indentation < 4 && level >= s->fenced_code_block_delimiter_length) {
while (lexer->lookahead == ' ' || lexer->lookahead == '\t') {
advance(s, lexer);
}
if (lexer->lookahead == '\n' || lexer->lookahead == '\r') {
s->fenced_code_block_delimiter_length = 0;
lexer->result_symbol = delimiter == '`'
? FENCED_CODE_BLOCK_END_BACKTICK
: FENCED_CODE_BLOCK_END_TILDE;
return true;
}
}
if ((delimiter == '`' ? valid_symbols[FENCED_CODE_BLOCK_START_BACKTICK]
: valid_symbols[FENCED_CODE_BLOCK_START_TILDE]) &&
level >= 3) {
bool info_string_has_backtick = false;
if (delimiter == '`') {
while (lexer->lookahead != '\n' && lexer->lookahead != '\r' &&
!lexer->eof(lexer)) {
if (lexer->lookahead == '`') {
info_string_has_backtick = true;
break;
}
advance(s, lexer);
}
}
if (!info_string_has_backtick) {
lexer->result_symbol = delimiter == '`'
? FENCED_CODE_BLOCK_START_BACKTICK
: FENCED_CODE_BLOCK_START_TILDE;
if (!s->simulate)
push_block(s, FENCED_CODE_BLOCK);
s->fenced_code_block_delimiter_length = level;
s->indentation = 0;
return true;
}
}
return false;
}
static bool parse_backtick(Scanner *s, TSLexer *lexer,
const bool *valid_symbols) {
uint8_t level = 0;
uint8_t *delimiter_length = &s->code_span_delimiter_length;
while (lexer->lookahead == '`') {
lexer->advance(lexer, false);
level++;
}
lexer->mark_end(lexer);
if (level == *delimiter_length && valid_symbols[CODE_SPAN_CLOSE]) {
*delimiter_length = 0;
lexer->result_symbol = CODE_SPAN_CLOSE;
return true;
}
if (valid_symbols[CODE_SPAN_START]) {
size_t close_level = 0;
while (!lexer->eof(lexer)) {
if (lexer->lookahead == '`') {
close_level++;
} else {
if (close_level == level) {
break;
}
close_level = 0;
}
lexer->advance(lexer, false);
}
if (close_level == level) {
*delimiter_length = level;
lexer->result_symbol = CODE_SPAN_START;
return true;
}
if (valid_symbols[UNCLOSED_SPAN]) {
lexer->result_symbol = UNCLOSED_SPAN;
return true;
}
}
return false;
}
static bool parse_star(Scanner *s, TSLexer *lexer, const bool *valid_symbols) {
advance(s, lexer);
mark_end(s, lexer);
size_t star_count = 1;
uint8_t extra_indentation = 0;
for (;;) {
if (lexer->lookahead == '*') {
if (star_count == 1 && extra_indentation >= 1 &&
valid_symbols[LIST_MARKER_STAR]) {
mark_end(s, lexer);
}
star_count++;
advance(s, lexer);
} else if (lexer->lookahead == ' ' || lexer->lookahead == '\t') {
if (star_count == 1) {
extra_indentation += advance(s, lexer);
} else {
advance(s, lexer);
}
} else {
break;
}
}
bool line_end = lexer->lookahead == '\n' || lexer->lookahead == '\r';
bool dont_interrupt = false;
if (star_count == 1 && line_end) {
extra_indentation = 1;
dont_interrupt = s->matched == s->open_blocks.size;
}
bool thematic_break = star_count >= 3 && line_end;
bool list_marker_star = star_count >= 1 && extra_indentation >= 1;
if (valid_symbols[THEMATIC_BREAK] && thematic_break && s->indentation < 4) {
lexer->result_symbol = THEMATIC_BREAK;
mark_end(s, lexer);
s->indentation = 0;
return true;
}
if ((dont_interrupt ? valid_symbols[LIST_MARKER_STAR_DONT_INTERRUPT]
: valid_symbols[LIST_MARKER_STAR]) &&
list_marker_star) {
if (star_count == 1) {
mark_end(s, lexer);
}
extra_indentation--;
if (extra_indentation <= 3) {
extra_indentation += s->indentation;
s->indentation = 0;
} else {
uint8_t temp = s->indentation;
s->indentation = extra_indentation;
extra_indentation = temp;
}
if (!s->simulate)
push_block(s, (Block)(LIST_ITEM + extra_indentation));
lexer->result_symbol =
dont_interrupt ? LIST_MARKER_STAR_DONT_INTERRUPT : LIST_MARKER_STAR;
return true;
}
return false;
}
static bool parse_thematic_break_underscore(Scanner *s, TSLexer *lexer,
const bool *valid_symbols) {
advance(s, lexer);
mark_end(s, lexer);
size_t underscore_count = 1;
for (;;) {
if (lexer->lookahead == '_') {
underscore_count++;
advance(s, lexer);
} else if (lexer->lookahead == ' ' || lexer->lookahead == '\t') {
advance(s, lexer);
} else {
break;
}
}
bool line_end = lexer->lookahead == '\n' || lexer->lookahead == '\r';
if (underscore_count >= 3 && line_end && valid_symbols[THEMATIC_BREAK]) {
lexer->result_symbol = THEMATIC_BREAK;
mark_end(s, lexer);
s->indentation = 0;
return true;
}
return false;
}
static bool parse_block_quote(Scanner *s, TSLexer *lexer,
const bool *valid_symbols) {
if (valid_symbols[BLOCK_QUOTE_START]) {
advance(s, lexer);
s->indentation = 0;
if (lexer->lookahead == ' ' || lexer->lookahead == '\t') {
s->indentation += advance(s, lexer) - 1;
}
lexer->result_symbol = BLOCK_QUOTE_START;
if (!s->simulate)
push_block(s, BLOCK_QUOTE);
return true;
}
return false;
}
static bool parse_atx_heading(Scanner *s, TSLexer *lexer,
const bool *valid_symbols) {
if (valid_symbols[ATX_H1_MARKER] && s->indentation <= 3) {
mark_end(s, lexer);
uint16_t level = 0;
while (lexer->lookahead == '#' && level <= 6) {
advance(s, lexer);
level++;
}
if (level <= 6 &&
(lexer->lookahead == ' ' || lexer->lookahead == '\t' ||
lexer->lookahead == '\n' || lexer->lookahead == '\r')) {
lexer->result_symbol = ATX_H1_MARKER + (level - 1);
s->indentation = 0;
mark_end(s, lexer);
return true;
}
}
return false;
}
static bool parse_setext_underline(Scanner *s, TSLexer *lexer,
const bool *valid_symbols) {
if (valid_symbols[SETEXT_H1_UNDERLINE] &&
s->matched == s->open_blocks.size) {
mark_end(s, lexer);
while (lexer->lookahead == '=') {
advance(s, lexer);
}
while (lexer->lookahead == ' ' || lexer->lookahead == '\t') {
advance(s, lexer);
}
if (lexer->lookahead == '\n' || lexer->lookahead == '\r') {
lexer->result_symbol = SETEXT_H1_UNDERLINE;
mark_end(s, lexer);
return true;
}
}
return false;
}
static bool parse_plus(Scanner *s, TSLexer *lexer, const bool *valid_symbols) {
if (s->indentation <= 3 &&
(valid_symbols[LIST_MARKER_PLUS] ||
valid_symbols[LIST_MARKER_PLUS_DONT_INTERRUPT] ||
valid_symbols[PLUS_METADATA])) {
advance(s, lexer);
if (valid_symbols[PLUS_METADATA] && lexer->lookahead == '+') {
advance(s, lexer);
if (lexer->lookahead != '+') {
return false;
}
advance(s, lexer);
while (lexer->lookahead == ' ' || lexer->lookahead == '\t') {
advance(s, lexer);
}
if (lexer->lookahead != '\n' && lexer->lookahead != '\r') {
return false;
}
for (;;) {
if (lexer->lookahead == '\r') {
advance(s, lexer);
if (lexer->lookahead == '\n') {
advance(s, lexer);
}
} else {
advance(s, lexer);
}
size_t plus_count = 0;
while (lexer->lookahead == '+') {
plus_count++;
advance(s, lexer);
}
if (plus_count == 3) {
while (lexer->lookahead == ' ' ||
lexer->lookahead == '\t') {
advance(s, lexer);
}
if (lexer->lookahead == '\r' || lexer->lookahead == '\n') {
if (lexer->lookahead == '\r') {
advance(s, lexer);
if (lexer->lookahead == '\n') {
advance(s, lexer);
}
} else {
advance(s, lexer);
}
mark_end(s, lexer);
lexer->result_symbol = PLUS_METADATA;
return true;
}
}
while (lexer->lookahead != '\n' && lexer->lookahead != '\r' &&
!lexer->eof(lexer)) {
advance(s, lexer);
}
if (lexer->eof(lexer)) {
break;
}
}
} else {
uint8_t extra_indentation = 0;
while (lexer->lookahead == ' ' || lexer->lookahead == '\t') {
extra_indentation += advance(s, lexer);
}
bool dont_interrupt = false;
if (lexer->lookahead == '\r' || lexer->lookahead == '\n') {
extra_indentation = 1;
dont_interrupt = true;
}
dont_interrupt =
dont_interrupt && s->matched == s->open_blocks.size;
if (extra_indentation >= 1 &&
(dont_interrupt ? valid_symbols[LIST_MARKER_PLUS_DONT_INTERRUPT]
: valid_symbols[LIST_MARKER_PLUS])) {
lexer->result_symbol = dont_interrupt
? LIST_MARKER_PLUS_DONT_INTERRUPT
: LIST_MARKER_PLUS;
extra_indentation--;
if (extra_indentation <= 3) {
extra_indentation += s->indentation;
s->indentation = 0;
} else {
uint8_t temp = s->indentation;
s->indentation = extra_indentation;
extra_indentation = temp;
}
if (!s->simulate)
push_block(s, (Block)(LIST_ITEM + extra_indentation));
return true;
}
}
}
return false;
}
static bool parse_ordered_list_marker(Scanner *s, TSLexer *lexer,
const bool *valid_symbols) {
if (s->indentation <= 3 &&
(valid_symbols[LIST_MARKER_PARENTHESIS] ||
valid_symbols[LIST_MARKER_DOT] ||
valid_symbols[LIST_MARKER_PARENTHESIS_DONT_INTERRUPT] ||
valid_symbols[LIST_MARKER_DOT_DONT_INTERRUPT])) {
size_t digits = 1;
bool dont_interrupt = lexer->lookahead != '1';
advance(s, lexer);
while (isdigit(lexer->lookahead)) {
dont_interrupt = true;
digits++;
advance(s, lexer);
}
if (digits >= 1 && digits <= 9) {
bool dot = false;
bool parenthesis = false;
if (lexer->lookahead == '.') {
advance(s, lexer);
dot = true;
} else if (lexer->lookahead == ')') {
advance(s, lexer);
parenthesis = true;
}
if (dot || parenthesis) {
uint8_t extra_indentation = 0;
while (lexer->lookahead == ' ' || lexer->lookahead == '\t') {
extra_indentation += advance(s, lexer);
}
bool line_end =
lexer->lookahead == '\n' || lexer->lookahead == '\r';
if (line_end) {
extra_indentation = 1;
dont_interrupt = true;
}
dont_interrupt =
dont_interrupt && s->matched == s->open_blocks.size;
if (extra_indentation >= 1 &&
(dot ? (dont_interrupt
? valid_symbols[LIST_MARKER_DOT_DONT_INTERRUPT]
: valid_symbols[LIST_MARKER_DOT])
: (dont_interrupt
? valid_symbols
[LIST_MARKER_PARENTHESIS_DONT_INTERRUPT]
: valid_symbols[LIST_MARKER_PARENTHESIS]))) {
lexer->result_symbol =
dot ? LIST_MARKER_DOT : LIST_MARKER_PARENTHESIS;
extra_indentation--;
if (extra_indentation <= 3) {
extra_indentation += s->indentation;
s->indentation = 0;
} else {
uint8_t temp = s->indentation;
s->indentation = extra_indentation;
extra_indentation = temp;
}
if (!s->simulate)
push_block(
s, (Block)(LIST_ITEM + extra_indentation + digits));
return true;
}
}
}
}
return false;
}
static bool parse_minus(Scanner *s, TSLexer *lexer, const bool *valid_symbols) {
if (s->indentation <= 3 &&
(valid_symbols[LIST_MARKER_MINUS] ||
valid_symbols[LIST_MARKER_MINUS_DONT_INTERRUPT] ||
valid_symbols[SETEXT_H2_UNDERLINE] || valid_symbols[THEMATIC_BREAK] ||
valid_symbols[MINUS_METADATA])) {
mark_end(s, lexer);
bool whitespace_after_minus = false;
bool minus_after_whitespace = false;
size_t minus_count = 0;
uint8_t extra_indentation = 0;
for (;;) {
if (lexer->lookahead == '-') {
if (minus_count == 1 && extra_indentation >= 1) {
mark_end(s, lexer);
}
minus_count++;
advance(s, lexer);
minus_after_whitespace = whitespace_after_minus;
} else if (lexer->lookahead == ' ' || lexer->lookahead == '\t') {
if (minus_count == 1) {
extra_indentation += advance(s, lexer);
} else {
advance(s, lexer);
}
whitespace_after_minus = true;
} else {
break;
}
}
bool line_end = lexer->lookahead == '\n' || lexer->lookahead == '\r';
bool dont_interrupt = false;
if (minus_count == 1 && line_end) {
extra_indentation = 1;
dont_interrupt = true;
}
dont_interrupt = dont_interrupt && s->matched == s->open_blocks.size;
bool thematic_break = minus_count >= 3 && line_end;
bool underline =
minus_count >= 1 && !minus_after_whitespace && line_end &&
s->matched ==
s->open_blocks
.size; bool list_marker_minus = minus_count >= 1 && extra_indentation >= 1;
bool success = false;
if (valid_symbols[SETEXT_H2_UNDERLINE] && underline) {
lexer->result_symbol = SETEXT_H2_UNDERLINE;
mark_end(s, lexer);
s->indentation = 0;
success = true;
} else if (valid_symbols[THEMATIC_BREAK] &&
thematic_break) { lexer->result_symbol = THEMATIC_BREAK;
mark_end(s, lexer);
s->indentation = 0;
success = true;
} else if ((dont_interrupt
? valid_symbols[LIST_MARKER_MINUS_DONT_INTERRUPT]
: valid_symbols[LIST_MARKER_MINUS]) &&
list_marker_minus) {
if (minus_count == 1) {
mark_end(s, lexer);
}
extra_indentation--;
if (extra_indentation <= 3) {
extra_indentation += s->indentation;
s->indentation = 0;
} else {
uint8_t temp = s->indentation;
s->indentation = extra_indentation;
extra_indentation = temp;
}
if (!s->simulate)
push_block(s, (Block)(LIST_ITEM + extra_indentation));
lexer->result_symbol = dont_interrupt
? LIST_MARKER_MINUS_DONT_INTERRUPT
: LIST_MARKER_MINUS;
return true;
}
if (minus_count == 3 && (!minus_after_whitespace) && line_end &&
valid_symbols[MINUS_METADATA]) {
for (;;) {
if (lexer->lookahead == '\r') {
advance(s, lexer);
if (lexer->lookahead == '\n') {
advance(s, lexer);
}
} else {
advance(s, lexer);
}
minus_count = 0;
while (lexer->lookahead == '-') {
minus_count++;
advance(s, lexer);
}
if (minus_count == 3) {
while (lexer->lookahead == ' ' ||
lexer->lookahead == '\t') {
advance(s, lexer);
}
if (lexer->lookahead == '\r' || lexer->lookahead == '\n') {
if (lexer->lookahead == '\r') {
advance(s, lexer);
if (lexer->lookahead == '\n') {
advance(s, lexer);
}
} else {
advance(s, lexer);
}
mark_end(s, lexer);
lexer->result_symbol = MINUS_METADATA;
return true;
}
}
while (lexer->lookahead != '\n' && lexer->lookahead != '\r' &&
!lexer->eof(lexer)) {
advance(s, lexer);
}
if (lexer->eof(lexer)) {
break;
}
}
}
if (success) {
return true;
}
}
return false;
}
static bool parse_pipe_table(Scanner *s, TSLexer *lexer,
const bool *valid_symbols) {
(void)(valid_symbols);
mark_end(s, lexer);
size_t cell_count = 0;
bool starting_pipe = false;
bool ending_pipe = false;
bool empty = true;
if (lexer->lookahead == '|') {
starting_pipe = true;
advance(s, lexer);
}
while (lexer->lookahead != '\r' && lexer->lookahead != '\n' &&
!lexer->eof(lexer)) {
if (lexer->lookahead == '|') {
cell_count++;
ending_pipe = true;
advance(s, lexer);
} else {
if (lexer->lookahead != ' ' && lexer->lookahead != '\t') {
ending_pipe = false;
}
if (lexer->lookahead == '\\') {
advance(s, lexer);
if (is_punctuation((char)lexer->lookahead)) {
advance(s, lexer);
}
} else {
advance(s, lexer);
}
}
}
if (empty && cell_count == 0 && !(starting_pipe && ending_pipe)) {
return false;
}
if (!ending_pipe) {
cell_count++;
}
if (lexer->lookahead == '\n') {
advance(s, lexer);
} else if (lexer->lookahead == '\r') {
advance(s, lexer);
if (lexer->lookahead == '\n') {
advance(s, lexer);
}
} else {
return false;
}
s->indentation = 0;
s->column = 0;
for (;;) {
if (lexer->lookahead == ' ' || lexer->lookahead == '\t') {
s->indentation += advance(s, lexer);
} else {
break;
}
}
s->simulate = true;
uint8_t matched_temp = 0;
while (matched_temp < (uint8_t)s->open_blocks.size) {
if (match(s, lexer, s->open_blocks.items[matched_temp])) {
matched_temp++;
} else {
return false;
}
}
size_t delimiter_cell_count = 0;
if (lexer->lookahead == '|') {
advance(s, lexer);
}
for (;;) {
while (lexer->lookahead == ' ' || lexer->lookahead == '\t') {
advance(s, lexer);
}
if (lexer->lookahead == '|') {
delimiter_cell_count++;
advance(s, lexer);
continue;
}
if (lexer->lookahead == ':') {
advance(s, lexer);
if (lexer->lookahead != '-') {
return false;
}
}
bool had_one_minus = false;
while (lexer->lookahead == '-') {
had_one_minus = true;
advance(s, lexer);
}
if (had_one_minus) {
delimiter_cell_count++;
}
if (lexer->lookahead == ':') {
if (!had_one_minus) {
return false;
}
advance(s, lexer);
}
while (lexer->lookahead == ' ' || lexer->lookahead == '\t') {
advance(s, lexer);
}
if (lexer->lookahead == '|') {
if (!had_one_minus) {
delimiter_cell_count++;
}
advance(s, lexer);
continue;
}
if (lexer->lookahead != '\r' && lexer->lookahead != '\n') {
return false;
} else {
break;
}
}
if (cell_count != delimiter_cell_count) {
return false;
}
lexer->result_symbol = PIPE_TABLE_START;
return true;
}
static bool scan(Scanner *s, TSLexer *lexer, const bool *valid_symbols) {
if (valid_symbols[TEMPLATE_CHARS]) {
if (valid_symbols[AUTOMATIC_SEMICOLON]) {
return false;
}
return scan_template_chars(s, lexer);
}
if (valid_symbols[JSX_TEXT] && scan_jsx_text(s, lexer)) {
return true;
}
if (valid_symbols[AUTOMATIC_SEMICOLON]) {
bool scanned_comment = false;
bool ret = scan_automatic_semicolon(s, lexer, !valid_symbols[LOGICAL_OR], &scanned_comment);
if (!ret && !scanned_comment && valid_symbols[TERNARY_QMARK] && lexer->lookahead == '?') {
return scan_ternary_qmark(s, lexer);
}
return ret;
}
if (valid_symbols[TERNARY_QMARK]) {
return scan_ternary_qmark(s, lexer);
}
if (valid_symbols[TRIGGER_ERROR]) {
return error(lexer);
}
if (valid_symbols[CLOSE_BLOCK]) {
s->state |= STATE_CLOSE_BLOCK;
lexer->result_symbol = CLOSE_BLOCK;
return true;
}
if (lexer->eof(lexer)) {
if (valid_symbols[TOKEN_EOF]) {
lexer->result_symbol = TOKEN_EOF;
return true;
}
if (s->open_blocks.size > 0) {
lexer->result_symbol = BLOCK_CLOSE;
if (!s->simulate)
pop_block(s);
return true;
}
return false;
}
if (!(s->state & STATE_MATCHING)) {
for (;;) {
if (lexer->lookahead == ' ' || lexer->lookahead == '\t') {
s->indentation += advance(s, lexer);
} else {
break;
}
}
if (valid_symbols[INDENTED_CHUNK_START] &&
!valid_symbols[NO_INDENTED_CHUNK]) {
if (s->indentation >= 4 && lexer->lookahead != '\n' &&
lexer->lookahead != '\r') {
lexer->result_symbol = INDENTED_CHUNK_START;
s->indentation -= 4;
return true;
}
}
switch (lexer->lookahead) {
case '\r':
case '\n':
if (valid_symbols[BLANK_LINE_START]) {
lexer->result_symbol = BLANK_LINE_START;
return true;
}
break;
case '`':
if (parse_fenced_code_block(s, '`', lexer, valid_symbols)) {
return true;
}
return parse_backtick(s, lexer, valid_symbols);
case '~':
return parse_fenced_code_block(s, '~', lexer, valid_symbols);
case '*':
return parse_star(s, lexer, valid_symbols);
case '_':
return parse_thematic_break_underscore(s, lexer, valid_symbols);
case '>':
return parse_block_quote(s, lexer, valid_symbols);
case '#':
return parse_atx_heading(s, lexer, valid_symbols);
case '=':
return parse_setext_underline(s, lexer, valid_symbols);
case '+':
return parse_plus(s, lexer, valid_symbols);
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
return parse_ordered_list_marker(s, lexer, valid_symbols);
case '-':
return parse_minus(s, lexer, valid_symbols);
}
if (lexer->lookahead != '\r' && lexer->lookahead != '\n' &&
valid_symbols[PIPE_TABLE_START]) {
return parse_pipe_table(s, lexer, valid_symbols);
}
} else { bool partial_success = false;
while (s->matched < (uint8_t)s->open_blocks.size) {
if (s->matched == (uint8_t)s->open_blocks.size - 1 &&
(s->state & STATE_CLOSE_BLOCK)) {
if (!partial_success)
s->state &= ~STATE_CLOSE_BLOCK;
break;
}
if (match(s, lexer, s->open_blocks.items[s->matched])) {
partial_success = true;
s->matched++;
} else {
if (s->state & STATE_WAS_SOFT_LINE_BREAK) {
s->state &= (~STATE_MATCHING);
}
break;
}
}
if (partial_success) {
if (s->matched == s->open_blocks.size) {
s->state &= (~STATE_MATCHING);
}
lexer->result_symbol = BLOCK_CONTINUATION;
return true;
}
if (!(s->state & STATE_WAS_SOFT_LINE_BREAK)) {
lexer->result_symbol = BLOCK_CLOSE;
pop_block(s);
if (s->matched == s->open_blocks.size) {
s->state &= (~STATE_MATCHING);
}
return true;
}
}
if ((valid_symbols[LINE_ENDING] || valid_symbols[SOFT_LINE_ENDING] ||
valid_symbols[PIPE_TABLE_LINE_ENDING]) &&
(lexer->lookahead == '\n' || lexer->lookahead == '\r')) {
if (lexer->lookahead == '\r') {
advance(s, lexer);
if (lexer->lookahead == '\n') {
advance(s, lexer);
}
} else {
advance(s, lexer);
}
s->indentation = 0;
s->column = 0;
if (!(s->state & STATE_CLOSE_BLOCK) &&
(valid_symbols[SOFT_LINE_ENDING] ||
valid_symbols[PIPE_TABLE_LINE_ENDING])) {
lexer->mark_end(lexer);
for (;;) {
if (lexer->lookahead == ' ' || lexer->lookahead == '\t') {
s->indentation += advance(s, lexer);
} else {
break;
}
}
s->simulate = true;
uint8_t matched_temp = s->matched;
s->matched = 0;
bool one_will_be_matched = false;
while (s->matched < (uint8_t)s->open_blocks.size) {
if (match(s, lexer, s->open_blocks.items[s->matched])) {
s->matched++;
one_will_be_matched = true;
} else {
break;
}
}
bool all_will_be_matched = s->matched == s->open_blocks.size;
if (!lexer->eof(lexer) &&
!scan(s, lexer, paragraph_interrupt_symbols)) {
s->matched = matched_temp;
s->matched = 0;
s->indentation = 0;
s->column = 0;
if (one_will_be_matched) {
s->state |= STATE_MATCHING;
} else {
s->state &= (~STATE_MATCHING);
}
if (valid_symbols[PIPE_TABLE_LINE_ENDING]) {
if (all_will_be_matched) {
lexer->result_symbol = PIPE_TABLE_LINE_ENDING;
return true;
}
} else {
lexer->result_symbol = SOFT_LINE_ENDING;
s->state |= STATE_WAS_SOFT_LINE_BREAK;
return true;
}
} else {
s->matched = matched_temp;
}
s->indentation = 0;
s->column = 0;
}
if (valid_symbols[LINE_ENDING]) {
s->matched = 0;
if (s->open_blocks.size > 0) {
s->state |= STATE_MATCHING;
} else {
s->state &= (~STATE_MATCHING);
}
s->state &= (~STATE_WAS_SOFT_LINE_BREAK);
lexer->result_symbol = LINE_ENDING;
return true;
}
}
return false;
}
bool tree_sitter_mdx_external_scanner_scan(void *payload, TSLexer *lexer,
const bool *valid_symbols) {
Scanner *scanner = (Scanner *)payload;
scanner->simulate = false;
return scan(scanner, lexer, valid_symbols);
}
void *tree_sitter_mdx_external_scanner_create(void) {
Scanner *s = (Scanner *)malloc(sizeof(Scanner));
s->open_blocks.items = (Block *)calloc(1, sizeof(Block));
#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)
_Static_assert(ATX_H6_MARKER == ATX_H1_MARKER + 5, "");
#else
assert(ATX_H6_MARKER == ATX_H1_MARKER + 5);
#endif
deserialize(s, NULL, 0);
return s;
}
unsigned tree_sitter_mdx_external_scanner_serialize(void *payload,
char *buffer) {
Scanner *scanner = (Scanner *)payload;
return serialize(scanner, buffer);
}
void tree_sitter_mdx_external_scanner_deserialize(void *payload,
const char *buffer,
unsigned length) {
Scanner *scanner = (Scanner *)payload;
deserialize(scanner, buffer, length);
}
void tree_sitter_mdx_external_scanner_destroy(void *payload) {
Scanner *scanner = (Scanner *)payload;
free(scanner->open_blocks.items);
free(scanner);
}