tree-sitter-objectscript-routine 1.7.3

ObjectScript routine grammar for tree-sitter
Documentation
#include "../../common/scanner.h"
#include "tree_sitter/parser.h"
#include <string.h>

// Keep this in sync with grammar externals.
enum TokenType {
  COMPILED_HEADER = OBJECTSCRIPT_CORE_TOKEN_TYPE_MAX,
  ROUTINE,
  RTN_DOT,
};

struct ObjectScript_Routine_Scanner {
  struct ObjectScript_Core_Scanner core_scanner;
};

static bool lex_routine_keyword(TSLexer *lexer) {
  static const char keyword[] = "ROUTINE";

  for (size_t i = 0; keyword[i] != 0; i++) {
    if (ascii_toupper_i32(lexer->lookahead) != (int32_t)keyword[i]) {
      return false;
    }
    advance(lexer);
  }

  lexer->mark_end(lexer);
  lexer->result_symbol = ROUTINE;
  return true;
}

static bool lex_rtn_dot(TSLexer *lexer) {
  if (lexer->get_column(lexer) != 0) return false;
  if (lexer->lookahead != '.') return false;

  advance(lexer);

  // Only match a line that is exactly "." with no trailing content.
  if (lexer->eof(lexer) || lexer->lookahead == '\n' || lexer->lookahead == '\r') {
    lexer->mark_end(lexer);
    lexer->result_symbol = RTN_DOT;
    return true;
  }

  return false;
}

static bool scan(void *payload, TSLexer *lexer, const bool *valid_symbols) {
  struct ObjectScript_Routine_Scanner *scanner =
      (struct ObjectScript_Routine_Scanner *)payload;

  // Tree-sitter marks all terminals as valid during error recovery.
  if (valid_symbols[SENTINEL]) {
    return false;
  }

  if (valid_symbols[ROUTINE] && lexer->get_column(lexer) == 0 && lex_routine_keyword(lexer)) {
    return true;
  } else if (valid_symbols[COMPILED_HEADER]) {
    lexer->mark_end(lexer);
    while(!lexer->eof(lexer) && lexer->lookahead != '\n') {
      advance(lexer);
    }
    if (lexer->lookahead == '\n') {
      advance(lexer);
      if (lexer->lookahead != '%') {
        return false;
      }
      advance(lexer);
      if (lexer->lookahead != 'R') return false;
      advance(lexer);

      if (lexer->lookahead != 'O') return false;
      advance(lexer);
      lexer->result_symbol = COMPILED_HEADER;
      int newline_count = 0;
      while(newline_count < 2 &&!lexer->eof(lexer)) {
        if (lexer->lookahead == '\n') 
        {newline_count++;}
        advance(lexer);
      }
      lexer->mark_end(lexer);
      return true;
    }
  }

  if (valid_symbols[RTN_DOT] && lex_rtn_dot(lexer)) {
    return true;
  }

  bool matched = ObjectScript_Core_Scanner_scan(&scanner->core_scanner, lexer,
                                                valid_symbols);
  return matched;
}

void *tree_sitter_objectscript_routine_external_scanner_create() {
  struct ObjectScript_Routine_Scanner *scanner =
      (struct ObjectScript_Routine_Scanner *)calloc(
          1, sizeof(struct ObjectScript_Routine_Scanner));
  // scanner->saw_compiled_header = false;
  // scanner->saw_routine_header = false;
  ObjectScript_Core_Scanner_init(&scanner->core_scanner);
  scanner->core_scanner.column1_statement_mode = false;
  return scanner;
}

bool tree_sitter_objectscript_routine_external_scanner_scan(
    void *payload, TSLexer *lexer, const bool *valid_symbols) {
  return scan(payload, lexer, valid_symbols);
}

unsigned tree_sitter_objectscript_routine_external_scanner_serialize(void *payload,
                                                             char *buffer) {
  struct ObjectScript_Routine_Scanner *scanner =
      (struct ObjectScript_Routine_Scanner *)payload;
  memcpy(buffer, scanner, sizeof(struct ObjectScript_Routine_Scanner));
  return sizeof(struct ObjectScript_Routine_Scanner);
}

void tree_sitter_objectscript_routine_external_scanner_deserialize(
    void *payload, const char *buffer, unsigned length) {
  memcpy(payload, buffer, length);
}

void tree_sitter_objectscript_routine_external_scanner_destroy(void *payload) {
  struct ObjectScript_Routine_Scanner *scanner =
      (struct ObjectScript_Routine_Scanner *)payload;
  free(scanner);
}