willbooster-tree-sitter-cpp 23.0.0

C++ grammar for tree-sitter
Documentation
#ifndef TREE_SITTER_C_CONDITIONAL_MACRO_H_
#define TREE_SITTER_C_CONDITIONAL_MACRO_H_

#include "functionMacro.h"

typedef struct {
    TSSymbol ifdef, ifndef, leading_ifdef, leading_ifndef;
    // Optional, unordered ASCII words of at most FUNCTION_MACRO_KEYWORD_MAX_LENGTH bytes.
    // Excludes unknown type aliases and marker names; recognized C type/modifier prefixes are unchanged.
    const char *const *reserved_words;
    unsigned reserved_word_count;
} ConditionalMacroSymbols;

static bool conditional_macro_body(TSLexer *lexer, const ConditionalMacroSymbols *symbols, bool *escaped);
static bool conditional_macro_word(TSLexer *lexer, MacroName *name, bool escaped);
static bool conditional_macro_spacing(TSLexer *lexer, bool *separated, bool *escaped);
static bool conditional_macro_next_line(TSLexer *lexer, bool *escaped);
static bool conditional_macro_directive(TSLexer *lexer, MacroName *name);
static bool conditional_macro_marker_line(TSLexer *lexer, const ConditionalMacroSymbols *symbols, bool *escaped);
static bool conditional_macro_closing(TSLexer *lexer);
static bool conditional_macro_type_prefix(TSLexer *lexer, const ConditionalMacroSymbols *symbols, bool escaped);
static bool conditional_macro_skip_lines(TSLexer *lexer, bool *escaped);
static bool conditional_macro_reserved_word(const ConditionalMacroSymbols *symbols, const char *word);
static bool conditional_macro_modifier_group(TSLexer *lexer, const char *word, bool *escaped);
static bool conditional_macro_attribute_declaration(TSLexer *lexer, bool *escaped);

static bool scan_conditional_macro(TSLexer *lexer, const ConditionalMacroSymbols *symbols, const bool *valid_symbols) {
    char directive[7] = {0};
    MacroName command = {.word = directive, .capacity = sizeof(directive) - 1, .ascii = true};
    if (!conditional_macro_directive(lexer, &command) || !command.ascii) return false;
    bool ifdef = strcmp(directive, "ifdef") == 0;
    if (!ifdef && strcmp(directive, "ifndef") != 0) return false;
    TSSymbol leading_symbol = ifdef ? symbols->leading_ifdef : symbols->leading_ifndef;
    TSSymbol generic_symbol = ifdef ? symbols->ifdef : symbols->ifndef;
    bool leading = valid_symbols[leading_symbol];
    bool generic = valid_symbols[generic_symbol];
    if (!leading && !generic) return false;
    lexer->mark_end(lexer);

    bool escaped = false;
    if (!conditional_macro_body(lexer, symbols, &escaped)) return false;
    if (leading && conditional_macro_type_prefix(lexer, symbols, escaped)) {
        lexer->result_symbol = leading_symbol;
        return true;
    }
    lexer->result_symbol = generic_symbol;
    return generic;
}

static bool conditional_macro_body(TSLexer *lexer, const ConditionalMacroSymbols *symbols, bool *escaped) {
    bool separated = false;
    MacroName condition = {.ascii = true};
    if (!conditional_macro_spacing(lexer, &separated, escaped) || !separated ||
        !conditional_macro_word(lexer, &condition, *escaped) || !conditional_macro_next_line(lexer, escaped)) return false;
    if (!conditional_macro_marker_line(lexer, symbols, escaped) || !conditional_macro_closing(lexer)) return false;
    return conditional_macro_skip_lines(lexer, escaped) && !lexer->eof(lexer) && lexer->lookahead != '#';
}

static bool conditional_macro_closing(TSLexer *lexer) {
    char directive[7] = {0};
    MacroName command = {.word = directive, .capacity = sizeof(directive) - 1, .ascii = true};
    if (!conditional_macro_directive(lexer, &command) || !command.ascii || strcmp(directive, "endif") != 0)
        return false;
    bool separated = false;
    bool escaped = false;
    if (!conditional_macro_spacing(lexer, &separated, &escaped)) return false;
    return !escaped && (lexer->eof(lexer) || lexer->lookahead == '\n' || lexer->lookahead == '\r');
}


typedef enum {
    CONDITIONAL_TYPE, CONDITIONAL_MODIFIER, CONDITIONAL_TYPE_MODIFIER, CONDITIONAL_ALIAS, CONDITIONAL_RESERVED,
    CONDITIONAL_INVALID,
} ConditionalMacroPrefix;

static ConditionalMacroPrefix conditional_macro_prefix_kind(const ConditionalMacroSymbols *symbols, const MacroName *name, const char *word);

static ConditionalMacroPrefix conditional_macro_prefix(TSLexer *lexer, const ConditionalMacroSymbols *symbols, bool *escaped);

static bool conditional_macro_type_prefix(TSLexer *lexer, const ConditionalMacroSymbols *symbols, bool escaped) {
    ConditionalMacroPrefix kind;
    do {
        kind = conditional_macro_prefix(lexer, symbols, &escaped);
    } while (kind == CONDITIONAL_MODIFIER);
    if (kind == CONDITIONAL_TYPE) return true;
    if (kind != CONDITIONAL_ALIAS) return false;
    if (lexer->lookahead == '*') return true;
    MacroName declarator = {.ascii = true};
    return conditional_macro_word(lexer, &declarator, escaped);
}

static ConditionalMacroPrefix conditional_macro_prefix(TSLexer *lexer, const ConditionalMacroSymbols *symbols, bool *escaped) {
    if (lexer->lookahead == '[') {
        return conditional_macro_attribute_declaration(lexer, escaped) ? CONDITIONAL_MODIFIER : CONDITIONAL_INVALID;
    }
    char word[FUNCTION_MACRO_KEYWORD_MAX_LENGTH + 1] = {0};
    MacroName type = {.word = word, .capacity = sizeof(word) - 1, .ascii = true};
    if (!conditional_macro_word(lexer, &type, *escaped)) return CONDITIONAL_INVALID;
    ConditionalMacroPrefix kind = conditional_macro_prefix_kind(symbols, &type, word);
    if (kind == CONDITIONAL_TYPE) return kind;
    if (!conditional_macro_skip_lines(lexer, escaped)) return CONDITIONAL_INVALID;
    if (kind == CONDITIONAL_TYPE_MODIFIER && lexer->lookahead == '(') return CONDITIONAL_TYPE;
    if (kind == CONDITIONAL_MODIFIER || kind == CONDITIONAL_TYPE_MODIFIER) {
        return conditional_macro_modifier_group(lexer, word, escaped) ? CONDITIONAL_MODIFIER : CONDITIONAL_INVALID;
    }
    return kind;
}

static ConditionalMacroPrefix conditional_macro_prefix_kind(const ConditionalMacroSymbols *symbols, const MacroName *name, const char *word) {
    if (name->ascii && strcmp(word, pragma_word) == 0) return CONDITIONAL_MODIFIER;
    if (!name->ascii) return CONDITIONAL_ALIAS;
    if (!function_macro_keyword(word)) {
        return conditional_macro_reserved_word(symbols, word) ? CONDITIONAL_RESERVED : CONDITIONAL_ALIAS;
    }
    bool type = function_macro_word_in(word, function_macro_type_keywords,
        sizeof(function_macro_type_keywords) / sizeof(function_macro_type_keywords[0]));
    bool modifier = (!type && function_macro_object_prefix_keyword(word)) ||
        function_macro_word_in(word, function_macro_declaration_modifiers,
            sizeof(function_macro_declaration_modifiers) / sizeof(function_macro_declaration_modifiers[0]));
    if (type) return modifier ? CONDITIONAL_TYPE_MODIFIER : CONDITIONAL_TYPE;
    return modifier ? CONDITIONAL_MODIFIER : CONDITIONAL_RESERVED;
}

static bool conditional_macro_reserved_word(const ConditionalMacroSymbols *symbols, const char *word) {
    if (!symbols->reserved_words) return false;
    for (unsigned i = 0; i < symbols->reserved_word_count; i++) {
        if (strcmp(word, symbols->reserved_words[i]) == 0) return true;
    }
    return false;
}

static bool conditional_macro_modifier_group(TSLexer *lexer, const char *word, bool *escaped) {
    bool grouped = strcmp(word, pragma_word) == 0 || function_macro_word_in(word, function_macro_group_modifiers,
        sizeof(function_macro_group_modifiers) / sizeof(function_macro_group_modifiers[0]));
    if (!grouped || lexer->lookahead != '(') return true;
    bool literal = false;
    return function_macro_balanced(lexer, '(', ')', &literal) && conditional_macro_skip_lines(lexer, escaped);
}

static bool conditional_macro_attribute_declaration(TSLexer *lexer, bool *escaped) {
    lexer->advance(lexer, false);
    if (!conditional_macro_skip_lines(lexer, escaped) || lexer->lookahead != '[') return false;
    bool literal = false;
    if (!function_macro_balanced(lexer, '[', ']', &literal) ||
        !conditional_macro_skip_lines(lexer, escaped) || lexer->lookahead != ']') return false;
    lexer->advance(lexer, false);
    return conditional_macro_skip_lines(lexer, escaped);
}

static bool conditional_macro_skip_lines(TSLexer *lexer, bool *escaped) {
    bool separated = false;
    for (;;) {
        *escaped = false;
        if (!conditional_macro_spacing(lexer, &separated, escaped)) return false;
        if (*escaped || !scan_preproc_newline(lexer, false)) return true;
    }
}

static bool conditional_macro_marker_line(TSLexer *lexer, const ConditionalMacroSymbols *symbols, bool *escaped) {
    char word[FUNCTION_MACRO_KEYWORD_MAX_LENGTH + 1] = {0};
    MacroName marker = {.word = word, .capacity = sizeof(word) - 1, .ascii = true};
    if (!conditional_macro_word(lexer, &marker, *escaped) ||
        (marker.ascii && (function_macro_keyword(word) || conditional_macro_reserved_word(symbols, word)))) return false;
    return conditional_macro_next_line(lexer, escaped);
}

static bool conditional_macro_directive(TSLexer *lexer, MacroName *name) {
    if (lexer->lookahead != '#') return false;
    lexer->advance(lexer, false);
    while (lexer->lookahead == ' ' || lexer->lookahead == '\t') lexer->advance(lexer, false);
    return conditional_macro_word(lexer, name, false);
}

static bool conditional_macro_next_line(TSLexer *lexer, bool *escaped) {
    bool separated = false;
    *escaped = false;
    if (!conditional_macro_spacing(lexer, &separated, escaped) || *escaped ||
        !scan_preproc_newline(lexer, false)) return false;
    for (;;) {
        if (!conditional_macro_spacing(lexer, &separated, escaped) || lexer->eof(lexer)) return false;
        if (*escaped || !scan_preproc_newline(lexer, false)) return true;
    }
}

static bool conditional_macro_spacing(TSLexer *lexer, bool *separated, bool *escaped) {
    while (lexer->lookahead != '\n' && lexer->lookahead != '\r') {
        int32_t c = lexer->lookahead;
        if (pragma_space(c)) {
            *separated = true;
            lexer->advance(lexer, false);
            continue;
        }
        if (c == '/') {
            *separated = true;
            if (!function_macro_argument_slash(lexer, false)) return false;
            continue;
        }
        if (c != '\\') return true;
        lexer->advance(lexer, false);
        if (scan_preproc_newline(lexer, false)) continue;
        *escaped = lexer->lookahead == 'u' || lexer->lookahead == 'U';
        return *escaped;
    }
    return true;
}

static bool conditional_macro_word(TSLexer *lexer, MacroName *name, bool escaped) {
    bool has_name = escaped;
    if (escaped) {
        name->ascii = false;
        if (!scan_macro_name_ucn(lexer)) return false;
    }
    for (;;) {
        MacroNameCharacter character = scan_macro_name_character(lexer, has_name, name);
        if (character == MACRO_NAME_END) return has_name;
        if (character == MACRO_NAME_SPLICE) {
            return has_name && lexer->lookahead != '\\' && !preproc_word(lexer->lookahead, true);
        }
        if (character != MACRO_NAME_CHARACTER) return false;
        has_name = true;
    }
}

#endif