#include "tree_sitter/alloc.h"
#include "tree_sitter/parser.h"
#include <ctype.h>
#include <wctype.h>
enum TokenType {
LINE_CONTINUATION,
INTEGER_LITERAL,
FLOAT_LITERAL,
BOZ_LITERAL,
STRING_LITERAL,
END_OF_STATEMENT
};
typedef struct {
bool in_line_continuation;
} Scanner;
static inline void advance(TSLexer *lexer) { lexer->advance(lexer, false); }
static inline void skip(TSLexer *lexer) { lexer->advance(lexer, true); }
static bool is_ident_char(char chr) { return iswalnum(chr) || chr == '_'; }
static bool is_boz_sentinel(char chr) {
switch (chr) {
case 'B':
case 'b':
case 'O':
case 'o':
case 'Z':
case 'z':
return true;
default:
return false;
}
}
static bool is_exp_sentinel(char chr) {
switch (chr) {
case 'D':
case 'd':
case 'E':
case 'e':
return true;
default:
return false;
}
}
static bool scan_int(TSLexer *lexer) {
if (!iswdigit(lexer->lookahead)) {
return false;
}
while (iswdigit(lexer->lookahead)) {
advance(lexer); }
if (lexer->lookahead == '&') {
skip(lexer);
while (iswspace(lexer->lookahead)) {
skip(lexer);
}
if (lexer->lookahead == '&') {
skip(lexer);
scan_int(lexer);
}
}
lexer->mark_end(lexer);
return true;
}
static bool scan_number(TSLexer *lexer) {
lexer->result_symbol = INTEGER_LITERAL;
bool digits = scan_int(lexer);
if (lexer->lookahead == '.') {
advance(lexer);
if (digits && !iswalnum(lexer->lookahead)) {
lexer->mark_end(lexer); }
lexer->result_symbol = FLOAT_LITERAL;
}
digits = scan_int(lexer) || digits;
if (digits) {
if (is_exp_sentinel(lexer->lookahead)) {
advance(lexer);
if (lexer->lookahead == '+' || lexer->lookahead == '-') {
advance(lexer);
}
if (!scan_int(lexer)) {
return true; }
lexer->mark_end(lexer);
lexer->result_symbol = FLOAT_LITERAL;
}
if (lexer->lookahead == '_') {
advance(lexer);
if (!isalnum(lexer->lookahead)) {
return true; }
while (is_ident_char(lexer->lookahead)) {
advance(lexer); }
lexer->mark_end(lexer);
}
}
return digits;
}
static bool scan_boz(TSLexer *lexer) {
lexer->result_symbol = BOZ_LITERAL;
bool boz_prefix = false;
char quote = '\0';
if (is_boz_sentinel(lexer->lookahead)) {
advance(lexer);
boz_prefix = true;
}
if (lexer->lookahead == '\'' || lexer->lookahead == '"') {
quote = lexer->lookahead;
advance(lexer);
if (!isxdigit(lexer->lookahead)) {
return false;
}
while (isxdigit(lexer->lookahead)) {
advance(lexer); }
if (lexer->lookahead != quote) {
return false;
}
advance(lexer); if (!boz_prefix && !is_boz_sentinel(lexer->lookahead)) {
return false; }
lexer->mark_end(lexer);
return true;
}
return false;
}
static bool scan_end_of_statement(Scanner *scanner, TSLexer *lexer) {
if (lexer->lookahead == ';' || lexer->eof(lexer)) {
skip(lexer);
lexer->result_symbol = END_OF_STATEMENT;
return true;
}
if (scanner->in_line_continuation) {
return false;
}
if (lexer->lookahead == '\r') {
skip(lexer);
if (lexer->lookahead == '\n') {
skip(lexer);
}
} else {
if (lexer->lookahead == '\n') {
skip(lexer);
} else if (lexer->lookahead != '!') {
return false;
}
}
lexer->result_symbol = END_OF_STATEMENT;
return true;
}
static bool scan_start_line_continuation(Scanner *scanner, TSLexer *lexer) {
scanner->in_line_continuation = (lexer->lookahead == '&');
if (!scanner->in_line_continuation) {
return false;
}
advance(lexer);
lexer->result_symbol = LINE_CONTINUATION;
return true;
}
static bool scan_end_line_continuation(Scanner *scanner, TSLexer *lexer) {
if (!scanner->in_line_continuation) {
return false;
}
if (lexer->lookahead == '!') {
return false;
}
scanner->in_line_continuation = false;
if (lexer->lookahead == '&') {
advance(lexer);
}
lexer->result_symbol = LINE_CONTINUATION;
return true;
}
static bool scan_string_literal(TSLexer *lexer) {
const char opening_quote = lexer->lookahead;
if (opening_quote != '"' && opening_quote != '\'') {
return false;
}
advance(lexer);
lexer->result_symbol = STRING_LITERAL;
while (lexer->lookahead != '\n' && !lexer->eof(lexer)) {
if (lexer->lookahead == '&') {
advance(lexer);
while (iswblank(lexer->lookahead)) {
advance(lexer);
}
if (lexer->lookahead == '\n' || lexer->lookahead == '\r') {
while (iswspace(lexer->lookahead)) {
advance(lexer);
}
}
}
if (lexer->lookahead == opening_quote) {
advance(lexer);
if (lexer->lookahead != opening_quote) {
return true;
}
}
advance(lexer);
}
return false;
}
static bool scan(Scanner *scanner, TSLexer *lexer, const bool *valid_symbols) {
while (iswblank(lexer->lookahead)) {
skip(lexer);
}
if (valid_symbols[END_OF_STATEMENT]) {
if (scan_end_of_statement(scanner, lexer)) {
return true;
}
}
while (iswspace(lexer->lookahead)) {
skip(lexer);
}
if (scan_end_line_continuation(scanner, lexer)) {
return true;
}
if (valid_symbols[STRING_LITERAL]) {
if (scan_string_literal(lexer)) {
return true;
}
}
if (valid_symbols[INTEGER_LITERAL] || valid_symbols[FLOAT_LITERAL] ||
valid_symbols[BOZ_LITERAL]) {
if (scan_number(lexer)) {
return true;
}
if (scan_boz(lexer)) {
return true;
}
}
if (scan_start_line_continuation(scanner, lexer)) {
return true;
}
return false;
}
void *tree_sitter_fortran_external_scanner_create() {
return ts_calloc(1, sizeof(bool));
}
bool tree_sitter_fortran_external_scanner_scan(void *payload, TSLexer *lexer,
const bool *valid_symbols) {
Scanner *scanner = (Scanner *)payload;
return scan(scanner, lexer, valid_symbols);
}
unsigned tree_sitter_fortran_external_scanner_serialize(void *payload,
char *buffer) {
Scanner *scanner = (Scanner *)payload;
buffer[0] = (char)scanner->in_line_continuation;
return 1;
}
void tree_sitter_fortran_external_scanner_deserialize(void *payload,
const char *buffer,
unsigned length) {
Scanner *scanner = (Scanner *)payload;
if (length > 0) {
scanner->in_line_continuation = buffer[0];
}
}
void tree_sitter_fortran_external_scanner_destroy(void *payload) {
Scanner *scanner = (Scanner *)payload;
ts_free(scanner);
}