# frozen_string_literal: true
require 'ruby_grammar_builder'
require 'walk_up'

require_relative walk_up_until("paths.rb")
require_relative PathFor[:pattern]["numeric"]
require_relative PathFor[:pattern]["trigraph_support"]
require_relative PathFor[:pattern]["predefined_macros"]
require_relative PathFor[:pattern]["assembly"]
require_relative PathFor[:pattern]["std_space"]
require_relative PathFor[:pattern]["backslash_escapes"]
require_relative PathFor[:pattern]["raw_strings"]
require_relative './tokens.rb'

#
#
# create grammar!
#
#
# grammar = Grammar.fromTmLanguage("./original.tmLanguage.json")
grammar = Grammar.new(
    name: "C++",
    scope_name: "source.cpp",
    fileTypes: [
		"cc",
		"cpp",
		"cp",
		"cxx",
		"c++",
		"C",
		"h",
		"hh",
		"hpp",
		"h++"
    ],
    version: "",
    information_for_contributors: [
        "This code was auto generated by a much-more-readable ruby file",
        "This file essentially an updated/improved fork of the atom syntax",
        "see https://github.com/jeff-hykin/better-cpp-syntax/blob/master",
    ],
)

#
#
# Setup Utils (things used by many other patterns)
#
#
    grammar.import(PathFor[:pattern]["comments"])
    grammar.import(PathFor[:pattern]["std_space"])
    std_space                    = grammar[:std_space]
    basic_space                  = zeroOrMoreOf(match: /\s/, dont_back_track?: true).lookBehindToAvoid(@standard_character)
    universal_character          = Pattern.new(/\\u[0-9a-fA-F]{4}/).or(/\\U[0-9a-fA-F]{8}/)
    first_character              = Pattern.new(/[a-zA-Z_]/).or(universal_character)
    subsequent_character         = Pattern.new(/[a-zA-Z0-9_]/).or(universal_character)
    identifier                   = grammar[:identifier] = first_character.then(zeroOrMoreOf(subsequent_character))
    leading_space                = Pattern.new(/\s*+/)
    grammar[:semicolon] = @semicolon = Pattern.new(
            match: /;/,
            tag_as: "punctuation.terminator.statement",
        )
    grammar[:comma] = Pattern.new(
            match: /,/,
            tag_as: "punctuation.separator.delimiter.comma"
        )
    possible_beginning_of_statement = Pattern.new(/^|\G/).or(lookBehindFor(/;|\}/))
    grammar[:assignment_operator] = assignment_operator = Pattern.new(
        match: /\=/,
        tag_as: "keyword.operator.assignment",
    )
    assignment_operators = oneOf([
        Pattern.new(
            match: /(?:%=|\+=|-=|\*=|(?<!\()\/=)/,
            tag_as: "keyword.operator.assignment.compound"
        ),
        Pattern.new(
            match: /(?:&=|\^=|<<=|>>=|\|=)/,
            tag_as: "keyword.operator.assignment.compound.bitwise"
        ),
        assignment_operator,
    ])
    array_brackets = Pattern.new(
            match: /\[/,
            tag_as: "punctuation.definition.begin.bracket.square"
        ).then(
            match: /\w*/,
            includes: [:evaluation_context]
        ).then(
            match: /\]/,
            tag_as: "punctuation.definition.end.bracket.square"
        ).maybe(@spaces)
    functionCallGenerator = ->(repository_name:nil, match_name: nil, tag_name_as: nil, tag_content_as: nil, tag_parenthese_as: nil) do
        new_range = PatternRange.new(
            tag_content_as: "meta.#{tag_content_as}",
            start_pattern: Pattern.new(
                    match: match_name,
                    tag_as: tag_name_as,
                ).then(std_space).then(
                    match: /\(/,
                    tag_as: "punctuation.section.arguments.begin.bracket.round.#{tag_parenthese_as}"
                ),
            end_pattern: Pattern.new(
                    match: /\)/,
                    tag_as: "punctuation.section.arguments.end.bracket.round.#{tag_parenthese_as}"
                ),
            includes: [
                :evaluation_context
            ]
            )
        if repository_name
            grammar[repository_name.to_sym] = new_range
        end
        return new_range
    end

    def generateBlockFinder( name:"", tag_as:"", start_pattern:nil, needs_semicolon: true, primary_includes: [], head_includes:[], body_includes: [ :$initial_context ], tail_includes: [ :$initial_context ], secondary_includes:[])
        lookahead_endings = /[;>\[\]=]/
        if needs_semicolon
            end_pattern = Pattern.new(
                match: Pattern.new(
                        lookBehindFor(@close_curly_brace).maybe(@spaces).then(@semicolon)
                    ).or(
                        @semicolon
                    ).or(
                        lookAheadFor(lookahead_endings)
                    )
                )
        else
            end_pattern = lookBehindFor(@close_curly_brace).or(lookAheadFor(lookahead_endings))
        end
        return PatternRange.new(
            tag_as: tag_as,
            start_pattern: Pattern.new(
                    match: start_pattern,
                    tag_as: "meta.head."+name,
                ),
            end_pattern: end_pattern,
            includes: [
                *primary_includes,
                # Head
                PatternRange.new(
                    tag_as: "meta.head."+name,
                    start_pattern: /\G ?/,
                    zeroLengthStart?: true,
                    end_pattern: Pattern.new(
                        match: @open_curly_brace.or(lookAheadFor(/;/)),
                        tag_as: "punctuation.section.block.begin.bracket.curly."+name
                    ),
                    includes: head_includes
                ),
                # Body
                PatternRange.new(
                    tag_as: "meta.body."+name, # body is everything in the {}'s
                    start_pattern: lookBehindFor(@open_curly_brace),
                    end_pattern: Pattern.new(
                            match: @close_curly_brace,
                            tag_as: "punctuation.section.block.end.bracket.curly."+name
                        ),
                    includes: body_includes
                ),
                # Tail
                PatternRange.new(
                    tag_as: "meta.tail."+name,
                    start_pattern: lookBehindFor(@close_curly_brace).then(/[\s]*/),
                    end_pattern: Pattern.new(/[\s]*/).lookAheadFor(/;/),
                    includes: tail_includes
                ),
                *secondary_includes
            ]
        )
    end
#
#
# Contexts
#
#
    grammar.import(PathFor[:pattern]["preprocessor"])
    grammar[:ever_present_context] = [
            *grammar[:preprocessor_context],
            :comments,
            :line_continuation_character,
        ]
    grammar[:$initial_context] = [
            :ever_present_context,
            # declarations (contains only the head of somthing)
            # TODO: class pre-definition
            # TODO: initializer calls
            # definitions (contains a body of something)
            :constructor_root,
            :destructor_root,
            :function_definition,
            :simple_array_assignment,
            :operator_overload,
            :using_namespace,
            :type_alias,
            :using_name, # this needs to be below type_alias
            :namespace_alias,
            :namespace_block,
            :extern_block,
            :typedef_class,
            :typedef_struct,
            :typedef_union,
            :misc_keywords,               # eventuall remove this in favor of finding a complete statements
            :standard_declares, # struct/enum/union/class
            :class_block,
            :struct_block,
            :union_block,
            :enum_block,
            :template_isolated_definition,  # TODO: template definitions should be mixed into the seperate definitions (class/struct/function, etc)
            :template_definition,
            :template_explicit_instantiation,
            :access_control_keywords,       # TODO: these should only be allowed inside of class/struct (maybe namespace?) blocks
            :block,                         # TODO: after all function definition patterns are fixed, remove this from here and put it in the function definition context
            # statements
            :static_assert,                 # it is unclear to me if static_assert can appear in the root context or not, so I'm leaving it here to be safe. https://en.cppreference.com/w/cpp/language/static_assert
            :assembly,                      # it is unclear to me is assembly can be in the root context or not, so I'm leaving it here to be safe
            :function_pointer,

            # eventually this needs to be removed
            :evaluation_context
        ]
    grammar[:function_body_context] = grammar[:$initial_context].without(
            # function bodies cant contain any of theses:
            :constructor_root,
            :destructor_root,
            :operator_overload,
            :namespace_block,
            :extern_block,
            :function_definition,
            :operator_overload,
            :template_isolated_definition,
            :template_definition,
            :template_explicit_instantiation,
            # this shouldn't be in $initial_context (it will eventually be removed), and it shouldn't really be in function body either
            :evaluation_context,
        ) + [
            :over_qualified_types,
            :normal_variable_assignment,
            :normal_variable_declaration,
            # functions can exclusively contain theses:
            # TODO: fill out all of the statements here, variable declares, assignment, etc
            # control flow
            :switch_statement, # needs to be above the "switch" keyword (which will eventually be removed)
            :goto_statement,
            # eventually, probably, the evaluation_context should be removed from here
            :evaluation_context,
            :label
        ]
    grammar[:evaluation_context] = [
            :ever_present_context,
            # literals
            :string_context,
            :number_literal,
            # variable-like
            :method_access, # needs to be above :function_call, needs to be above operator
            :member_access,
            :predefined_macros,
            # operators
            :operators,
            :memory_operators,
            :wordlike_operators,
            :type_casting_operators,
            # keywords
            :control_flow_keywords,
            :exception_keywords,
            :the_this_keyword,
            :language_constants,
            # types, modifiers, and specifiers
            :constructor_bracket_call,
            :simple_constructor_call,
            :simple_array_assignment,
            :builtin_storage_type_initilizer, # needs to be above storage types
            :qualifiers_and_specifiers_post_parameters, # TODO this needs to be integrated into the function definition pattern
            :functional_specifiers_pre_parameters,      # TODO: these probably need to be moved inside the function definition pattern
            :storage_types,
            # misc
            :lambdas,
            :attributes_context, # this is here because it needs to be lower than :operators. TODO: once all the contexts are cleaned up, this should be put in a better spot
            :parentheses,
            :function_call,
            :scope_resolution_inner_generated,
            :square_brackets,
            # :empty_square_brackets, (see https://github.com/jeff-hykin/better-cpp-syntax/pull/380#issuecomment-542491824)
            :semicolon,
            :comma,
            :unknown_variable,
        ]
    grammar[:function_parameter_context] = [
            :ever_present_context, # comments and macros
            :parameter,
            :comma,
        ]
    grammar[:template_definition_context] = [
            :scope_resolution_template_definition_inner_generated,
            :template_definition_argument,
            :template_argument_defaulted,
            :template_call_innards,
            :evaluation_context
        ]
    grammar[:template_call_context] = [
            :ever_present_context,
            :template_call_range_helper,
            :storage_types,
            :language_constants,
            :scope_resolution_template_call_inner_generated,
            :operators,
            :number_literal,
            :string_context,
            :comma_in_template_argument,
            :qualified_type,
        ]
    grammar[:attributes_context] = [
            :cpp_attributes,
            :gcc_attributes,
            :ms_attributes,
            :alignas_attribute,
        ]
    grammar[:storage_types] = [
            :storage_specifiers,
            :inline_builtin_storage_type,
            :decltype,
            :typename,
        ]
#
#
# Numbers
#
#
    grammar[:number_literal] = numeric_constant(allow_user_defined_literals: true)
#
# Variable
#
    # todo: make a better name for this function
    variableBounds = ->(regex_pattern) do
        lookBehindToAvoid(@standard_character).then(regex_pattern).lookAheadToAvoid(@standard_character)
    end
    variable_name_without_bounds = identifier
    # word bounds are inefficient, but they are accurate
    variable_name = variableBounds[variable_name_without_bounds]
    
    # now import doxygen
    require_relative PathFor[:pattern]["doxygen"]
    grammar[:comments] = [
        *doxygen(variable_name),
        *grammar[:comments]
    ]
    
#
# Constants
#
    grammar[:language_constants] = Pattern.new(
        match: variableBounds[@cpp_tokens.that(:isLiteral)],
        tag_as: "constant.language.$match"
    )

#
# Built-In Types
#
    grammar[:inline_builtin_storage_type] = inline_builtin_storage_type = Pattern.new(
        basic_space.then(
            Pattern.new(
                # primitive builtins
                match: @cpp_tokens.that(:isPrimitive),
                tag_as: "storage.type.primitive storage.type.built-in.primitive",
            ).or(
                # non-primitive builtins
                match: @cpp_tokens.that(not(:isPrimitive), :isType),
                tag_as: "storage.type storage.type.built-in",
            ).or(
                # pthread
                match: @cpp_support_tokens.that(:isPthreadType),
                tag_as: "support.type.posix-reserved.pthread support.type.built-in.posix-reserved.pthread",
            ).or(
                # posix support type
                match: Pattern.new(/[a-zA-Z_]/).zeroOrMoreOf(@standard_character).then(/_t/) ,
                tag_as: "support.type.posix-reserved support.type.built-in.posix-reserved",
            )
        ).lookAheadToAvoid(@standard_character)
    )
    grammar[:decltype] = functionCallGenerator[
        repository_name: "decltype_specifier",
        match_name: variableBounds[/decltype/],
        tag_name_as: "keyword.operator.functionlike keyword.other.decltype storage.type.decltype",
        tag_content_as: "arguments.decltype",
        tag_parenthese_as: "decltype"
    ]

#
# Keywords and Keyword-ish things
#
    grammar[:using_name] = Pattern.new(match: /using/, tag_as: "keyword.other.using.directive").then(@spaces).lookAheadToAvoid(/namespace\b/)
    grammar[:functional_specifiers_pre_parameters] = Pattern.new(
        match: variableBounds[ @cpp_tokens.that(:isFunctionSpecifier) ],
        tag_as: "storage.modifier.specifier.functional.pre-parameters.$match"
    )
    grammar[:qualifiers_and_specifiers_post_parameters_inline] = Pattern.new(
        should_partially_match: ["final override;", "const noexcept {"],
        match: oneOrMoreOf(std_space.then(
            Pattern.new(
                should_fully_match: ["override", "final", "const", "noexcept"],
                should_not_fully_match: ["const noexcept"],
                should_not_partial_match: ["return"],
                tag_as: "storage.modifier.specifier.functional.post-parameters.$match",
                match: variableBounds[ @cpp_tokens.that(:canAppearAfterParametersBeforeBody, not(:isFunctionLike)) ],
            )
        )).lookAheadFor(
            Pattern.new(/\s*/).then(
                oneOf([
                    "{",
                    ";",
                    "\n",
                    "\r",
                    "=", # for = delete; or = default;
                ])
            )
        )
    )
    grammar[:qualifiers_and_specifiers_post_parameters] = [
        functionCallGenerator[
            repository_name: "requires_keyword",
            match_name: variableBounds[/requires/],
            tag_name_as: "keyword.other.functionlike keyword.other.requires",
            tag_content_as: "arguments.requires",
            tag_parenthese_as: "requires"
        ],
        grammar[:qualifiers_and_specifiers_post_parameters_inline],
    ]
    grammar[:storage_specifiers] = storage_specifier = Pattern.new(
        std_space.then(
            match: variableBounds[ @cpp_tokens.that(:isStorageSpecifier) ],
            tag_as: "storage.modifier.specifier.$match"
        )
    )
    grammar[:access_control_keywords] = Pattern.new(
        std_space.then(
            tag_as: "storage.type.modifier.access.control.$reference(access_specifier)",
            match: Pattern.new(
                Pattern.new(
                    match: @cpp_tokens.that(:isAccessSpecifier),
                    reference: "access_specifier"
                ).maybe(@spaces).then(
                    match: /:/,
                    tag_as: "punctuation.separator.colon.access.control"
                ),
            )
        )
    )
    grammar[:exception_keywords] = Pattern.new(
        std_space.then(
            match: variableBounds[ @cpp_tokens.that(:isExceptionRelated) ],
            tag_as: "keyword.control.exception.$match"
        )
    )
    #
    # misc keywords
    #
    # these are keywords that SHOULD get there own patterns, but the work has not yet been put in for them
    grammar[:misc_keywords] = Pattern.new(
        std_space.then(
            match: variableBounds[ @cpp_tokens.that(:isCurrentlyAMiscKeyword) ],
            tag_as: "keyword.other.$match"
        )
    )
    grammar[:the_this_keyword] = the_this_keyword = Pattern.new(
        std_space.then(
            match: variableBounds[ /this/ ],
            tag_as: "variable.language.this"
        )
    )
    # TODO: enhance casting operators to include <>'s
    grammar[:type_casting_operators] = Pattern.new(
        std_space.then(
            match: variableBounds[ @cpp_tokens.that(:isTypeCastingOperator) ],
            tag_as: "keyword.operator.wordlike keyword.operator.cast.$match"
        )
    )
    grammar[:memory_operators] = Pattern.new(
        std_space.then(
            tag_as: "keyword.operator.wordlike",
            match: Pattern.new(
                Pattern.new(
                    Pattern.new(
                        Pattern.new(
                            match: /delete/,
                            tag_as: "keyword.operator.delete.array"
                        ).maybe(@spaces).then(
                            match: /\[\]/,
                            tag_as: "keyword.operator.delete.array.bracket"
                        )
                    ).or(
                        match: /delete/,
                        tag_as: "keyword.operator.delete"
                    ).or(
                        match: /new/,
                        tag_as: "keyword.operator.new"
                    )
                ).lookAheadToAvoid(@standard_character)
            )
        )
    )
    grammar[:control_flow_keywords] = control_flow_keywords = Pattern.new(
        std_space.then(
            match: variableBounds[ @cpp_tokens.that(:isControlFlow) ],
            tag_as: "keyword.control.$match"
        )
    )
    declaration_storage_specifiers = Pattern.new(
        match: oneOrMoreOf(storage_specifier.groupless.then(@spaces)),
        includes: [
            :storage_specifiers
        ]
    )
#
# Control flow
#
    grammar[:goto_statement] = Pattern.new(
        should_fully_match: [ "goto label", "  goto label" ],
        match: std_space.then(
            Pattern.new(
                match: variableBounds[/goto/],
                tag_as: "keyword.control.goto",
            ).then(std_space).then(
                match: identifier,
                tag_as: "entity.name.label.call"
            )
        )
    )
    grammar[:label] = Pattern.new(
            std_space.then(
                tag_as: "entity.name.label",
                match: variableBounds[identifier],
            ).then(@word_boundary).lookBehindToAvoid(/case|default/).then(
                std_space
            ).then(
                match: /:/,
                tag_as: "punctuation.separator.label",
            ).lookAheadToAvoid(/:/)
        )
    grammar[:default_statement] = PatternRange.new(
            tag_as: "meta.conditional.case",
            start_pattern: Pattern.new(
                std_space.then(
                    match: variableBounds[ /default/ ],
                    tag_as: "keyword.control.default"
                )
            ),
            end_pattern: Pattern.new(
                match: /:/,
                tag_as: "punctuation.separator.colon.case.default"
            ),
            includes: [:evaluation_context,]
        )
    grammar[:case_statement] = PatternRange.new(
            tag_as: "meta.conditional.case",
            start_pattern: Pattern.new(
                std_space.then(
                    match: variableBounds[ /case/ ],
                    tag_as: "keyword.control.case"
                )
            ),
            end_pattern: Pattern.new(
                match: /:/,
                tag_as: "punctuation.separator.colon.case"
            ),
            includes: [:evaluation_context,]
        )
    grammar[:switch_conditional_parentheses] = PatternRange.new(
            tag_as: "meta.conditional.switch",
            start_pattern: Pattern.new(
                std_space.then(
                    match: /\(/,
                    tag_as: 'punctuation.section.parens.begin.bracket.round.conditional.switch'
                )
            ),
            end_pattern: Pattern.new(
                match: /\)/,
                tag_as: 'punctuation.section.parens.end.bracket.round.conditional.switch'
            ),
            includes: [ :range_for_inner, :evaluation_context, ]
        )
    grammar[:switch_statement] = generateBlockFinder(
            name: "switch",
            tag_as: "meta.block.switch",
            start_pattern: Pattern.new(
                std_space.then(
                    match: variableBounds[/switch/],
                    tag_as: "keyword.control.switch"
                )
            ),
            head_includes: [
                :switch_conditional_parentheses,
                :$initial_context
            ],
            body_includes: [
                :default_statement,
                :case_statement,
                :$initial_context,
            ],
            needs_semicolon: false,
        )
#
# C++ Attributes
#
    generateAttributeRangeFinder = ->(start_pattern, end_pattern) do
        return PatternRange.new(
            tag_as: "support.other.attribute",
            start_pattern: Pattern.new(
                match: start_pattern,
                tag_as: "punctuation.section.attribute.begin",
            ),
            end_pattern: Pattern.new(
                match: end_pattern,
                tag_as: "punctuation.section.attribute.end",
            ),
            includes: [
                # allow nested attributes
                :attributes_context,
                PatternRange.new(
                    start_pattern: /\(/,
                    end_pattern: /\)/,
                    includes: [
                        :attributes_context,
                        :string_context,
                        :ever_present_context,
                    ],
                ),
                Pattern.new(match: /using/, tag_as: "keyword.other.using.directive").then(@spaces).then(
                    match: variable_name,
                    tag_as: "entity.name.namespace",
                ),
                Pattern.new(match: /,/, tag_as: "punctuation.separator.attribute"),
                Pattern.new(match: /:/, tag_as: "punctuation.accessor.attribute"),
                Pattern.new(
                    match: variable_name.lookAheadFor(/::/),
                    tag_as: "entity.name.namespace"
                ),
                Pattern.new(match: variable_name, tag_as: "entity.other.attribute.$match"),
                :number_literal,
                :ever_present_context,
            ]
        )
    end

    cpp_attribute_start = Pattern.new(/\[\[/)
    cpp_attribute_end   = Pattern.new(/\]\]/)
    gcc_attribute_start = Pattern.new(/__attribute(?:__)?\s*\(\s*\(/)
    gcc_attribute_end   = Pattern.new(/\)\s*\)/)
    ms_attribute_start  = Pattern.new(/__declspec\(/)
    ms_attribute_end    = Pattern.new(/\)/)
    alignas_start       = Pattern.new(/alignas\(/)
    alignas_end         = Pattern.new(/\)/)

    grammar[:cpp_attributes   ] = generateAttributeRangeFinder[ cpp_attribute_start, cpp_attribute_end ]
    grammar[:gcc_attributes   ] = generateAttributeRangeFinder[ gcc_attribute_start, gcc_attribute_end ]
    grammar[:ms_attributes    ] = generateAttributeRangeFinder[ ms_attribute_start , ms_attribute_end  ]
    grammar[:alignas_attribute] = generateAttributeRangeFinder[ alignas_start      , alignas_end       ]

    inline_attribute = Pattern.new(
        should_fully_match:["[[nodiscard]]","__attribute((packed))","__declspec(fastcall)","__attribute__((constructor(101)))"],
        should_partial_match: ["struct [[deprecated]] st"],
        # match one of the three attribute styles
        match: Pattern.new(
                cpp_attribute_start.then(/.*?/).then(cpp_attribute_end)
            ).or(
                gcc_attribute_start.then(/.*?/).then(gcc_attribute_end)
            ).or(
                ms_attribute_start.then(/.*?/).then(ms_attribute_end)
            ).or(
                alignas_start.then(/.*?/).then(alignas_end)
            ).lookAheadToAvoid(/\)/),
        includes: [
            :attributes_context,
            :number_literal, # for alignas
        ],
    )
#
# Templates
#
    # this is effectively what is happening:
    #   some_number_of_angle_brackets = oneOrMoreOf(no_anglebrackets_at_all.or(balanced_brackets))
    # this is actually what is happening: (recursion)
    some_number_of_angle_brackets = Pattern.new(
        should_fully_match: [ "<>", "<testing, testing>", "<testing<>, testing>", "<'<'>", "<\">\">" ],
        should_not_fully_match: [ "testing<>" ],
        reference: "angle_brackets",
        match: Pattern.new(
            lookBehindToAvoid(/</).then(
                /</
            ).lookAheadToAvoid(/</).zeroOrMoreOf(
                oneOf([
                    grammar[:inline_comment],
                    Pattern.new(/"/).then(Pattern.new(zeroOrMoreOf(match: /[^"]/).or(/\\"/))).then(/"/),
                    Pattern.new(/'/).then(Pattern.new(zeroOrMoreOf(match: /[^']/).or(/\\'/))).then(/'/),
                    recursivelyMatch("angle_brackets"),
                    oneOrMoreOf(
                        # match: /[^'"<]/,
                        match: /[^'"<>\/]|\/[^*]/,
                        dont_back_track?: true,
                    ),
                ])
            ).then(/>/)
        )
    )


    grammar[:comma_in_template_argument] = Pattern.new(
        match: /,/,
        tag_as: "punctuation.separator.delimiter.comma.template.argument"
    )
    # note: template_call should ideally be a Range(), the reason its not is
    # because it's embedded inside of other patterns
    grammar[:template_call_innards] = template_call = Pattern.new(
        tag_as: 'meta.template.call',
        # to match the characters in the middle of a template call
        match: some_number_of_angle_brackets.zeroOrMoreOf(match: /\s/, dont_back_track?: true),
        includes: [ :template_call_range_helper ]
    )
    grammar[:template_call_range] = PatternRange.new(
        tag_as: 'meta.template.call',
        start_pattern: Pattern.new(
            match: /</,
            tag_as: "punctuation.section.angle-brackets.begin.template.call"
        ),
        end_pattern: Pattern.new(
            match: />/,
            tag_as: "punctuation.section.angle-brackets.end.template.call"
        ),
        includes: [:template_call_context]
    )
    # What does this helper do? it prevents template_call_range from incorrectly matching " 1 << 2" as a template call by having a pattern that matches the "1 <<" part first
    grammar[:template_call_range_helper] = [
        Pattern.new(
            @word_boundary.then(grammar[:number_literal]).then(std_space).then(
                match:/<</,
                tag_as: "keyword.operator.bitwise.shift",
            ),
        ),
        Pattern.new(
            @word_boundary.then(grammar[:number_literal]).then(std_space).then(
                match:/</,
                tag_as: "keyword.operator.comparison",
            ),
        ),
        :template_call_range,
    ]
    template_start = lookBehindToAvoid(@standard_character).then(
            match: /template/,
            tag_as: "storage.type.template"
        ).maybe(@spaces).then(
            match: /</,
            tag_as: "punctuation.section.angle-brackets.begin.template.definition"
        )
    # a template definition that is by itself on a line (this is ideal)
    grammar[:template_isolated_definition] = Pattern.new(
        match: template_start.then(
                match:  zeroOrMoreOf(/./),
                tag_as: "meta.template.definition",
                includes: [:template_definition_context],
            ).then(
                match: Pattern.new(/>/),
                tag_as: "punctuation.section.angle-brackets.end.template.definition"
            ).maybe(@spaces).then(/$/),
        )
    grammar[:template_definition] = PatternRange.new(
        tag_as: 'meta.template.definition',
        start_pattern: template_start,
        end_pattern: Pattern.new(
                match: />/,
                tag_as: "punctuation.section.angle-brackets.end.template.definition"
            ),
        includes: [
            # a template call inside of a non-isolated template definition
            # however this is rolling the dice: because if there is a less-than operator in a defaulted argument, then this pattern will screw everything up
            # a better solution would be nice, but its going to be difficult/impossible
            PatternRange.new(
                start_pattern: Pattern.new(
                        match: lookBehindFor(/\w/).maybe(@spaces).then(/</),
                        tag_as: "punctuation.section.angle-brackets.begin.template.call"
                    ),
                end_pattern: Pattern.new(
                        match: />/,
                        tag_as: "punctuation.section.angle-brackets.end.template.call"
                    ),
                includes: [:template_call_context]
            ),
            :template_definition_context,
        ]
        )
    grammar[:template_definition_argument] = std_space.then(
        Pattern.new(
            should_fully_match: [
                "typename", "typename T", "typename... T", "template <typename> class T",
                "class T = A", "template <typename> typename T = X"
            ],
            match:
            # case 1: only one word
            Pattern.new(
                match: variable_name_without_bounds,
                tag_as: "storage.type.template.argument.$match",
            ).or(
                # case 2: normal situation (ex: "typename T")
                Pattern.new(
                    match: oneOrMoreOf(variable_name_without_bounds.then(@spaces)),
                    includes: [
                        Pattern.new(
                            match: variable_name_without_bounds,
                            tag_as: "storage.type.template.argument.$match",
                        )
                    ],
                ).then(
                    match: variable_name_without_bounds,
                    tag_as: "entity.name.type.template",
                )
            ).or(
                # case 3: ellipses (ex: "typename... Args")
                Pattern.new(
                    match: variable_name_without_bounds,
                    tag_as: "storage.type.template.argument.$match",
                ).maybe(@spaces).then(
                    match: /\.\.\./,
                    tag_as: "punctuation.vararg-ellipses.template.definition",
                ).maybe(@spaces).then(
                    match: variable_name_without_bounds,
                    tag_as: "entity.name.type.template"
                )
            ).or(
                # case 4: template template parameter
                Pattern.new(
                    template_start
                ).maybe(@spaces).then(
                    match: variable_name_without_bounds,
                    tag_as: "storage.type.template.argument.$match",
                ).maybe(@spaces).maybe(
                    match: variable_name_without_bounds,
                    tag_as: "entity.name.type.template",
                ).maybe(@spaces).then(
                    match: />/,
                    tag_as: "punctuation.section.angle-brackets.end.template.definition"
                ).maybe(@spaces).then(
                    match: /class|typename/,
                    tag_as: "storage.type.template.argument.$match"
                ).maybe(
                    Pattern.new(@spaces).then(
                        match: variable_name_without_bounds,
                        tag_as: "entity.name.type.template",
                    )
                )
            ).maybe(@spaces).maybe(
                assignment_operator.maybe(@spaces).then(variable_name_without_bounds)
            ).then(
                Pattern.new(
                    match: /,/,
                    tag_as: "punctuation.separator.delimiter.comma.template.argument",
                ).or(
                    lookAheadFor(/>|$/)
                )
            )
        )
    )
    grammar[:template_argument_defaulted] = Pattern.new(
        should_partial_match: ["<typename T = void>", "< class T=void>", "<int  =0>"],
        match: lookBehindFor(/<|,/).maybe(@spaces).then(
            match: variable_name_without_bounds,
            tag_as: "storage.type.template.argument.$match",
        ).then(@spaces).maybe(
                match: variable_name_without_bounds,
                tag_as: "entity.name.type.template"
        ).maybe(@spaces).then(assignment_operator)
    )

#
# Explicit template instantiation
#
    grammar[:template_explicit_instantiation] = Pattern.new(
        tag_as: "meta.template.explicit-instantiation",
        match: lookBehindToAvoid(@standard_character).maybe(
            Pattern.new(
                match: /extern/,
                tag_as: "storage.modifier.specifier.extern",
            ).then(@spaces)
        ).then(
            match: /template/,
            tag_as: "storage.type.template"
        ).then(@spaces)
    )

#
# Scope resolution
#
    one_scope_resolution = Pattern.new(
        Pattern.new(
            match: variable_name,
            word_cannot_be_any_of: @cpp_tokens.representationsThat(
                :isWord,
                not(:isType),
                not(:isVariable),
                not(:isValidFunctionName),
                not(:isLiteral)
            )
        ).then(/\s*+/).maybe(
            template_call
        ).then(/::/)
    )
    inline_scope_resolution = ->(tag_extension) do
        Pattern.new(
            match: Pattern.new(
                maybe(/::/).zeroOrMoreOf(
                    match: one_scope_resolution.reTag(keep: false),
                    dont_back_track?: true
                )
            ),
            includes: [
                Pattern.new(
                    match: /::/,
                    tag_as: "punctuation.separator.namespace.access punctuation.separator.scope-resolution"+tag_extension
                ),
                Pattern.new(
                    match: variableBounds[identifier],
                    tag_as: "entity.name.scope-resolution"+tag_extension
                ),
                :template_call_range_helper
            ]
        )
    end
    scope_operator = Pattern.new(
        match: /::/,
        tag_as: "punctuation.separator.namespace.access punctuation.separator.scope-resolution"
    )
    generateScopeResolutionFinder = ->(tag_extension, grammar_name) do
        hidden_grammar_name = (grammar_name.to_s+"_inner_generated").to_sym
        tagged_scope_operator = scope_operator.reTag(
            append: tag_extension[1..-1]
        )
        grammar[grammar_name] = Pattern.new(
            # find the whole scope resolution
            should_fully_match: [ "name::name2::name3::" ],
            match: maybe(tagged_scope_operator).zeroOrMoreOf(one_scope_resolution.reTag(keep: false)).then(/\s*+/),
            includes: [
                    # then tag every `name::` seperately
                    hidden_grammar_name,
                ]
            )
        grammar[hidden_grammar_name] = Pattern.new(
            should_fully_match: ["name::", "name::name2::name3::"],
            match: grammar[grammar_name].then(
                    tag_as: "entity.name.scope-resolution"+tag_extension,
                    match: variable_name,
                    word_cannot_be_any_of: @cpp_tokens.representationsThat(
                        :isWord,
                        not(:isType),
                        not(:isVariable),
                        not(:isValidFunctionName),
                        not(:isLiteral)
                    ),
                ).then(/\s*+/).maybe(
                    template_call
                ).then(tagged_scope_operator),
            )
    end
    scope_resolution = generateScopeResolutionFinder["", :scope_resolution]
    generateScopeResolutionFinder[".template.call"       ,:scope_resolution_template_call       ]
    generateScopeResolutionFinder[".template.definition" ,:scope_resolution_template_definition ]
    generateScopeResolutionFinder[".function.call"       ,:scope_resolution_function_call       ]
    generateScopeResolutionFinder[".function.definition" ,:scope_resolution_function_definition ]
    generateScopeResolutionFinder[".namespace.alias"     ,:scope_resolution_namespace_alias     ]
    generateScopeResolutionFinder[".namespace.using"     ,:scope_resolution_namespace_using     ]
    generateScopeResolutionFinder[".namespace.block"     ,:scope_resolution_namespace_block     ]
    generateScopeResolutionFinder[".parameter"           ,:scope_resolution_parameter           ]
    generateScopeResolutionFinder[".function.definition.operator-overload" ,:scope_resolution_function_definition_operator_overload]

#
# Types
#
    ref_deref = ->(pointer_tag:"storage.modifier.pointer", reference_tag: "storage.modifier.reference") do
        Pattern.new(
            # even though some of these "should_fully_match" cases are invalid in C++, there examples here for demoing how the pattern DOES work but not the ideal. It currently works this way for performance/convenience
            should_fully_match: [ "*", "**", "**&", "* *", "*   *   *", "**&&**", "&&&", "&&   &", "&   & &", "& &&" ],
            should_not_fully_match: [ "    *    ", " ** " , "&&&    ", "&&   &    " ],
            match: Pattern.new(
                # match as many as possible, but (see next comment)
                maybe(std_space).zeroOrMoreOf(
                    oneOf(["&","*"]).then(std_space)
                # only match the space between them (not the space after the last one)
                ).then(oneOf(["&","*"])),
            ),
            includes: [
                # tag all the *'s
                Pattern.new(
                    match: /\*/,
                    tag_as: pointer_tag,
                ),
                # if there's an invalid number of &'s (3 or more)
                Pattern.new(
                    should_fully_match: [ "&&&", "&&   &", "&   & &", "& &&" ],
                    should_not_fully_match: ["   &&   ",  "&&&    ", "&&   &    " ],
                    should_not_partial_match: [ "&&", "&", "& &  " ],
                    tag_as: "invalid.illegal.reference-type",
                    match: Pattern.new(
                        match: Pattern.new(/\&/).then(std_space),
                        at_least: 2.times
                    ).then(/\&/)
                ),
                # tag all the &'s
                Pattern.new(
                    match: /\&/,
                    tag_as: reference_tag,
                ),
            ]
        )
    end
    non_type_keywords = @cpp_tokens.that(:isWord, not(:isType), not(:isTypeCreator))
    builtin_type_creators_and_specifiers = @cpp_tokens.that(:isTypeSpecifier).or(@cpp_tokens.that(:isTypeCreator))
    grammar[:qualified_type] = qualified_type = Pattern.new(
        should_fully_match: [
            "void",
            "A",
            "A::B",
            "A::B<C>::D<E>",
            "unsigned char",
            "long long int",
            "unsigned short int",
            "struct a",
            "void",
            "iterator",
            "original",
            "bore"
        ],
        should_not_partial_match: ["return", "static const"],
        tag_as: "meta.qualified-type",
        match: Pattern.new(
            leading_space.maybe(
                inline_attribute
            ).then(
                std_space
            ).zeroOrMoreOf(
                builtin_type_creators_and_specifiers.then(std_space)
            ).maybe(
                inline_scope_resolution[".type"].then(std_space)
            ).then(
                lookAheadToAvoid(non_type_keywords.then(@word_boundary))
            ).then(
                identifier,
            ).then(@word_boundary).maybe(
                some_number_of_angle_brackets
            ).lookAheadToAvoid(/[\w<:.]/)
        ).reTag(keep: false),
        includes: [
            scope_operator,
            Pattern.new(
                match: variableBounds[ @cpp_tokens.that(:isTypeCreator)],
                tag_as: "storage.type.$match"
            ),
            :attributes_context,
            :storage_types,
            :number_literal,
            :string_context,
            :comma,
            :scope_resolution_inner_generated,
            :template_call_range_helper,
            Pattern.new(
                match: identifier,
                tag_as: "entity.name.type",
            ),
        ],
    )
    constructor_call_base = lookAheadToAvoid(/class|struct|union|enum|explicit|new|delete|operator|template|throw|decltype|typename|override|final/).then(/\b/).then(
        qualified_type
    )
    generateVariableVariations = ->(tag_name) do
        Pattern.new(
            tag_as: "variable.lower-case #{tag_name}",
            match: /\b[a-z0-9]+\b/,
        ).or(
            tag_as: "variable.snake-case #{tag_name}",
            match: /\b[a-zA-Z0-9]+_[a-zA-Z0-9]*\b/,
        ).or(
            tag_as: "variable.camel-case #{tag_name}",
            match: /\b[a-z]+[A-Z][a-zA-Z0-9]*\b/,
        ).or(
            tag_as: "variable.upper-case #{tag_name}",
            match: /\b[A-Z][A-Z_0-9]*\b/,
        ).or(
            tag_as: "variable.other.unknown.$match",
            match: variable_name,
        )
    end
    grammar[:constructor_bracket_call] = Pattern.new(
        constructor_call_base.then(std_space).then(
            generateVariableVariations["variable.other.object.construction"],
        ).then(std_space).lookAheadFor(/\{/)
    )
    grammar[:simple_constructor_call] = Pattern.new(
        constructor_call_base.lookAheadFor(
            std_space.lookAheadFor(variable_name.lookAheadFor(std_space.lookAheadFor(/(?:\{|\()/)))
        )
    )
    grammar[:simple_array_assignment] = Pattern.new(
        qualified_type.maybe(ref_deref[]).then(std_space).then(
            generateVariableVariations["variable.other.assignment"],
        ).then(
            match: /\[/,
            tag_as: "punctuation.definition.begin.bracket.square.array.type",
        ).then(
            match: / */,
        ).then(
            match: /\]/,
            tag_as: "punctuation.definition.end.bracket.square.array.type",
        ).then(std_space).then(
            assignment_operators
        )
    )
    
    grammar[:unknown_variable] = generateVariableVariations["variable.other.unknown.$match"]
    normal_type_pattern = maybe(declaration_storage_specifiers.then(std_space)).then(qualified_type.maybe(ref_deref[]))
    # normal variable assignment
    grammar[:variable_assignment] =  Pattern.new(
        tag_as: "meta.assignment",
        match: Pattern.new(
            normal_type_pattern.then(std_space).then(
                generateVariableVariations["variable.other.assignment"],
            ).then(std_space).then(
                assignment_operators
            )
        )
    )
    grammar[:variable_declare] = Pattern.new(
        tag_as: "meta.declaration",
        match: Pattern.new(
            normal_type_pattern.then(std_space).then(
                generateVariableVariations["variable.other.declare"],
            ).then(std_space).lookAheadFor(/;|,|\[/).lookAheadToAvoid(/[^=]++=/)
        )
    )
    grammar[:normal_variable_assignment] = PatternRange.new(
        tag_as: "meta.assignment",
        start_pattern: Pattern.new(
            Pattern.new(/^/).then(std_space).then(
                grammar[:variable_assignment]
            )
        ),
        end_pattern: lookAheadFor(/;/),
        includes: [
            :normal_variable_assignment,
            :variable_assignment,
            :$initial_context,
        ],
    )
    # TODO:
        # missing binding assignments
        # missing array declarations
        # missing initalizer forms ( Thing somthin(); Thing somethin{};)
    grammar[:normal_variable_declaration] = PatternRange.new(
        tag_as: "meta.declaration",
        start_pattern: Pattern.new(
            Pattern.new(/^/).then(std_space).then(
                grammar[:variable_declare]
            )
        ),
        end_pattern: lookAheadFor(/;/),
        includes: [
            :normal_variable_assignment,
            :variable_assignment,
            :$initial_context,
        ],
    )
    # TODO: create a :type that includes inline function-pointer types and array types
    grammar[:simple_type] = qualified_type.maybe(ref_deref[])
    grammar[:type_alias] = Pattern.new(
        tag_as: "meta.declaration.type.alias",
        should_fully_match: ["using A = B;", "using _Sat = std::allocator_traits<Allocator>;","using const_pointer_t = const uint8_t *\n","using T = typename std::iterator_traits<InputIt>::value_type;", "using pcb = details::pointer_control_block<Y, default_delete<Y>>;"],
        should_not_partial_match: ["using namespace std;","using std::swap;", "using B::B;", "usingA = B;"],
        match: Pattern.new(
                match:/using/,
                tag_as: "keyword.other.using.directive",
            ).then(@spaces).lookAheadToAvoid(/namespace/).then(
                Pattern.new(
                    match: identifier,
                    tag_as: "entity.name.type",
                ),
            ).maybe(@spaces).maybe(
                inline_attribute
            ).maybe(@spaces).then(
                assignment_operator
            ).maybe(@spaces).maybe(
                match: /typename/,
                tag_as: "keyword.other.typename",
            ).maybe(@spaces).maybe(
                declaration_storage_specifiers
            ).then(
                qualified_type.or(
                    match: Pattern.new(/.*/).lookBehindToAvoid(/;/),
                    tag_as: "meta.declaration.type.alias.value.unknown",
                    includes: [
                        :evaluation_context,
                    ]
                )
            ).then(
                maybe(ref_deref[].then(std_space))
            ).maybe(
                array_brackets
            ).maybe(@spaces).then(@semicolon.or(/\n/)),
    )
    grammar[:typename] = Pattern.new(
        should_fully_match: ["typename Alloc::select_on_container_copy_construction"],
        should_not_partial_match: ["this_typename_is_valid"],
        match: Pattern.new(
            match: std_space.then(variableBounds[/typename/]),
            tag_as: "storage.modifier"
        ).then(std_space).then(qualified_type)
    )
#
# Support
#
    # generally this section is for things that need a #include, (the support category)
    # it will be for things such as cout, cin, vector, string, map, etc
    grammar[:predefined_macros] = predefinedMacros()

#
# Functions, Operator Overload
#
    optional_calling_convention = maybe(
            match: /__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall/,
            tag_as: "storage.type.modifier.calling-convention"
        ).then(std_space)
    avoid_invalid_function_names = @cpp_tokens.lookBehindToAvoidWordsThat(:isWord,  not(:isPreprocessorDirective), not(:isValidFunctionName))
    look_ahead_for_function_name = lookAheadFor(variable_name_without_bounds.maybe(@spaces).maybe(inline_attribute).maybe(@spaces).then(/\(/))

    deleted_and_defaulted_keyword = -> (subtags: []) do
        # highest priority tag needs to be at the back (But its easier for humans to thing first==most-important)
        subtags = subtags.reverse
        # for operator() or function() = delete ;
        # https://en.cppreference.com/w/cpp/language/function#Deleted_functions
        # or operator[=/<=>]() = default ;
        # https://en.cppreference.com/w/cpp/language/member_functions#Special_member_functions
        assignment_operator.then(std_space).then(
            Pattern.new(
                match: /default/,
                tag_as: subtags.map { |subtag| "keyword.other.default.#{subtag}" }.join(' '),
            ).or(
                match: /delete/,
                tag_as: subtags.map { |subtag| "keyword.other.delete.#{subtag}" }.join(' '),
            )
        )
    end

    grammar[:function_definition] = generateBlockFinder(
        name:"function.definition",
        tag_as:"meta.function.definition",
        start_pattern: Pattern.new(
            possible_beginning_of_statement.or(lookBehindFor(/>|\*\//)).then(
                /\s*+/, #std_space
            ).maybe(
                Pattern.new(
                    match: variableBounds[/template/],
                    tag_as: "storage.type.template",
                ).then(std_space)
            ).maybe(inline_attribute).zeroOrMoreOf(
                dont_back_track: true,
                match: storage_modifiers = Pattern.new(
                    Pattern.new(
                        match: variableBounds[@cpp_tokens.that(:isFunctionSpecifier).or(@cpp_tokens.that(:isStorageSpecifier))],
                        tag_as: "storage.modifier.$match"
                    ).then(std_space)
                ),
                includes: [storage_modifiers],
            ).then(
                grammar[:simple_type].then(std_space).then(
                    optional_calling_convention
                ).then(
                    grammar[:scope_resolution_function_definition]
                ).then(
                    match: variable_name_without_bounds,
                    tag_as: "entity.name.function.definition"
                ).then(
                    avoid_invalid_function_names
                ).then(std_space).lookAheadFor(/\(/)
            )
        ),
        head_includes:[
            :ever_present_context, # comments and macros
            PatternRange.new(
                tag_content_as: "meta.function.definition.parameters",
                start_pattern: Pattern.new(
                    match: /\(/,
                    tag_as: "punctuation.section.parameters.begin.bracket.round"
                    ),
                end_pattern: Pattern.new(
                    match: /\)/,
                    tag_as: "punctuation.section.parameters.end.bracket.round"
                    ),
                includes: [
                    :ever_present_context,
                    # the evaluation_context is included here as workaround for function-initializations like issue #282, and #198
                    # std::mt19937 eng1(std::time(nullptr));
                    :parameter_or_maybe_value,
                    :comma,
                    # the evaluation_context is included here as workaround for function-initializations like issue #282, and #198
                    # e.g. std::string ("hello");
                    :evaluation_context,
                ]
            ),
            # arrow return type
            Pattern.new(
                lookBehindFor(/^|\)/).maybe(@spaces).then(
                    match: /->/,
                    tag_as: "punctuation.definition.function.return-type"
                ).then(std_space).then(
                    qualified_type
                ),
            ),
            # initial context is here for things like noexcept()
            # TODO: fix this pattern an make it more strict
            :$initial_context
        ],
        needs_semicolon: false,
        body_includes: [ :function_body_context ],
    )
    grammar[:operator_overload] = generateBlockFinder(
        name:"function.definition.special.operator-overload",
        tag_as:"meta.function.definition.special.operator-overload",
        start_pattern: Pattern.new(
            # find the return type (if there is one)
            std_space.maybe(grammar[:simple_type].then(std_space)).then(optional_calling_convention).then(
                std_space
            ).then(
                inline_scope_resolution[".operator"]
            ).then(
                match: /operator/,
                tag_as: "keyword.other.operator.overload",
            # find any scope resolutions
            ).maybe(
                std_space.then(
                    tag_as: "storage.modifier.const",
                    match: variableBounds[ /const/ ],
                )
            # find any scope resolutions
            ).then(std_space).then(
                inline_scope_resolution[".operator-overload"]
            # find the actual operator/type
            ).then(
                # operator
                Pattern.new(
                    match: @cpp_tokens.that(:canAppearAfterOperatorKeyword),
                    tag_as: "entity.name.operator",
                # type
                ).or(
                    match: variableBounds[identifier].then(
                        # possible pointer/reference variation
                        maybe(ref_deref[pointer_tag:"entity.name.operator.type.pointer", reference_tag:"entity.name.operator.type.reference"])
                    ).then(std_space).maybe(
                        # possible array variation
                        match: /\[\]/,
                        tag_as: "entity.name.operator.type.array"
                    ),
                    tag_as: "entity.name.operator.type",
                # custom literal
                ).or(
                    # see https://en.cppreference.com/w/cpp/language/user_literal
                    Pattern.new(
                        match: /""/,
                        tag_as: "entity.name.operator.custom-literal",
                    ).then(std_space).then(
                        match: variableBounds[identifier],
                        tag_as: "entity.name.operator.custom-literal",
                    )
                )
            # lookahead for the start of a template type or the start of the function parameters
            ).then(std_space).lookAheadFor(/;|\<|\(/)
        ),
        head_includes:[
            :ever_present_context, # comments and macros
            :template_call_range_helper,
            PatternRange.new(
                tag_content_as: "meta.function.definition.parameters.special.operator-overload",
                start_pattern: Pattern.new(
                    match: /\(/,
                    tag_as: "punctuation.section.parameters.begin.bracket.round.special.operator-overload"
                    ),
                end_pattern: Pattern.new(
                    match: /\)/,
                    tag_as: "punctuation.section.parameters.end.bracket.round.special.operator-overload"
                    ),
                includes: [
                    :function_parameter_context,
                    # TODO: the evaluation_context is included here as workaround for function-initializations like issue #198
                    # e.g. std::string ("hello");
                    :evaluation_context,
                ]
            ),
            :qualifiers_and_specifiers_post_parameters,
            deleted_and_defaulted_keyword[subtags: ["function"]],
            # initial context is here for things like noexcept()
            # TODO: fix this pattern an make it more strict
            :$initial_context
        ],
        needs_semicolon: false,
        body_includes: [ :function_body_context ],
    )
    # static assert is special as it can be outside of normal places function calls can be
    grammar[:static_assert] = PatternRange.new(
        start_pattern: Pattern.new(
            std_space.then(
                match: variableBounds[/static_assert|_Static_assert/],
                tag_as: "keyword.other.static-assert",
            ).then(std_space).then(
                match: /\(/,
                tag_as: "punctuation.section.arguments.begin.bracket.round.static-assert",
            )
        ),
        end_pattern: Pattern.new(
            match: /\)/,
            tag_as: "punctuation.section.arguments.end.bracket.round.static-assert",
        ),
        includes: [
            # special handling for the assert message
            PatternRange.new(
                start_pattern: Pattern.new(
                    match: /,/,
                    tag_as: "punctuation.separator.delimiter.comma",
                ).maybe(@spaces).lookAheadFor(maybe(Pattern.new(/L|u8|u|U/).maybe(@spaces).then(/\"/))),
                end_pattern: lookAheadFor(/\)/),
                tag_as: "meta.static-assert.message",
                includes: [
                    :string_context,
                ]
            ),
            :evaluation_context,
        ]
    )
    # a full match example of function call would be: aNameSpace::subClass<TemplateArg>FunctionName<5>(
    grammar[:function_call] = [
        PatternRange.new(
            start_pattern: Pattern.new(
                    grammar[:scope_resolution_function_call]
                ).then(
                    # same as normal function call, just detect if upper-case
                    match: /[A-Z][A-Z_0-9]*/,
                    tag_as: "entity.name.function.call.upper-case entity.name.function.call"
                ).then(
                    avoid_invalid_function_names
                ).then(std_space).maybe(
                    template_call
                ).then(
                    match: /\(/,
                    tag_as: "punctuation.section.arguments.begin.bracket.round.function.call punctuation.section.arguments.begin.bracket.round.function.call.upper-case"
                ),
            end_pattern: Pattern.new(
                    match: /\)/,
                    tag_as: "punctuation.section.arguments.end.bracket.round.function.call punctuation.section.arguments.begin.bracket.round.function.call.upper-case"
                ),
            includes: [ :evaluation_context ]
        ),
        PatternRange.new(
            start_pattern: Pattern.new(
                    grammar[:scope_resolution_function_call]
                ).then(
                    match: variable_name_without_bounds,
                    tag_as: "entity.name.function.call"
                ).then(
                    avoid_invalid_function_names
                ).then(std_space).maybe(
                    template_call
                ).then(
                    match: /\(/,
                    tag_as: "punctuation.section.arguments.begin.bracket.round.function.call"
                ),
            end_pattern: Pattern.new(
                    match: /\)/,
                    tag_as: "punctuation.section.arguments.end.bracket.round.function.call"
                ),
            includes: [ :evaluation_context ]
        ),
    ]

#
# Initializers
#
    grammar[:curly_initializer] = PatternRange.new(
        tag_as: "meta.initialization",
        start_pattern: Pattern.new(
            qualified_type.then(std_space).then(
                match: /\{/,
                tag_as: "punctuation.section.arguments.begin.bracket.curly.initializer",
            )
        ),
        end_pattern: Pattern.new(
            match: /\}/,
            tag_as: "punctuation.section.arguments.end.bracket.curly.initializer",
        ),
        includes: [
            PatternRange.new(
                tag_as: "meta.initialization.parameter",
                start_pattern: Pattern.new(
                    Pattern.new(
                        tag_as: "punctuation.accessor.initializer variable.parameter.initializer",
                        match: ".",
                    ).then(
                        tag_as: "variable.parameter.initializer",
                        match: identifier,
                    )
                ),
                end_pattern: grammar[:comma].or(lookAheadFor(/\}/)),
                includes: [
                    :evaluation_context,
                ],
            ),
            :evaluation_context,
            :comma
        ]
    )
    grammar[:builtin_storage_type_initilizer] =  PatternRange.new(
        start_pattern: Pattern.new(
            inline_builtin_storage_type.then(basic_space).then(
                match: /\(/,
                tag_as: "punctuation.section.arguments.begin.bracket.round.initializer"
            )
        ),
        end_pattern: Pattern.new(
            match: /\)/,
            tag_as: "punctuation.section.arguments.end.bracket.round.initializer"
        ),
        includes: [ :evaluation_context ],
    )
#
# Constructor / Destructor
#
    # see https://en.cppreference.com/w/cpp/language/default_constructor
    constructor = ->(start_pattern) do
        generateBlockFinder(
            name:"function.definition.special.constructor",
            tag_as:"meta.function.definition.special.constructor",
            start_pattern: start_pattern,
            head_includes:[
                :ever_present_context, # comments and macros
                deleted_and_defaulted_keyword[subtags: ["constructor", "function"]],
                :functional_specifiers_pre_parameters,
                # ini context
                PatternRange.new(
                    start_pattern: Pattern.new(
                        match: /:/,
                        tag_as: "punctuation.separator.initializers"
                    ),
                    end_pattern: lookAheadFor(/\{/),
                    includes: [
                        PatternRange.new(
                            tag_content_as: "meta.parameter.initialization",
                            start_pattern: Pattern.new(
                                Pattern.new(
                                    match: variableBounds[identifier],
                                    tag_as: "entity.name.function.call.initializer",
                                ).maybe(
                                    template_call,
                                ).then(
                                    match: /\(/,
                                    tag_as: "punctuation.section.arguments.begin.bracket.round.function.call.initializer",
                                )
                            ),
                            end_pattern: Pattern.new(
                                match: /\)/,
                                tag_as: "punctuation.section.arguments.end.bracket.round.function.call.initializer",
                            ),
                            includes: [:evaluation_context]
                        ),
                        PatternRange.new(
                            tag_content_as: "meta.parameter.initialization",
                            start_pattern: Pattern.new(
                                Pattern.new(
                                    match: variableBounds[identifier],
                                    tag_as: "entity.name.function.call.initializer",
                                ).then(
                                    match: /\{/,
                                    tag_as: "punctuation.section.arguments.begin.bracket.round.function.call.initializer",
                                )
                            ),
                            end_pattern: Pattern.new(
                                match: /\}/,
                                tag_as: "punctuation.section.arguments.end.bracket.round.function.call.initializer",
                            ),
                            includes: [:evaluation_context]
                        ),
                        grammar[:comma],
                        :comments,
                    ]
                ),
                # parameters
                PatternRange.new(
                    tag_content_as: "meta.function.definition.parameters.special.constructor",
                    start_pattern: Pattern.new(
                        match: /\(/,
                        tag_as: "punctuation.section.parameters.begin.bracket.round.special.constructor"
                        ),
                    end_pattern: Pattern.new(
                        match: /\)/,
                        tag_as: "punctuation.section.parameters.end.bracket.round.special.constructor"
                        ),
                    includes: [
                        :function_parameter_context,
                        # TODO: the evaluation_context is included here as workaround for function-initializations like issue #198
                        # e.g. std::string ("hello");
                        :evaluation_context,
                    ]
                ),
                #final & override
                :qualifiers_and_specifiers_post_parameters,
                # initial context is here for things like noexcept()
                # TODO: fix this pattern an make it more strict
                :$initial_context
            ],
            needs_semicolon: false,
            body_includes: [ :function_body_context ],
        )
    end
    grammar[:constructor_inline] = constructor[
        # find the begining of the line
        Pattern.new(/^/).then(std_space).zeroOrMoreOf(
            should_fully_match: ["constexpr", "explicit", "explicit constexpr"],
            match:  @cpp_tokens.that(:isFunctionSpecifier).then(std_space),
            includes: [
                :functional_specifiers_pre_parameters
            ]
        ).then(optional_calling_convention).then(
            tag_as: "entity.name.function.constructor entity.name.function.definition.special.constructor",
            match: variableBounds[identifier].lookAheadFor(/\(/)
        )
    ]
    grammar[:constructor_root] = constructor[
        leading_space.then(optional_calling_convention).then(
            inline_scope_resolution[".constructor"]
        ).then(
            match: Pattern.new(
                Pattern.new(
                    match: variableBounds[identifier],
                    reference: "class_name",
                    dont_back_track?: true
                ).then(std_space).then(
                    /::/
                ).then(std_space).matchResultOf(
                    "class_name"
                ).then(std_space).lookAheadFor(/\(/)
            ),
            includes: [
                # the first part
                Pattern.new(
                    match: identifier.lookAheadFor(/:/),
                    tag_as: "entity.name.type.constructor",
                ),
                # the second part
                Pattern.new(
                    match: lookBehindFor(/:/).then(identifier),
                    tag_as: "entity.name.function.definition.special.constructor"
                ),
                # the scope operator
                Pattern.new(
                    match: /::/,
                    tag_as: "punctuation.separator.namespace.access punctuation.separator.scope-resolution.constructor"
                )
            ]
        )
    ]

    # see https://en.cppreference.com/w/cpp/language/destructor
    destructor = ->(start_pattern) do
        generateBlockFinder(
            name:"function.definition.special.member.destructor",
            tag_as:"meta.function.definition.special.member.destructor",
            start_pattern: start_pattern,
            head_includes:[
                :ever_present_context, # comments and macros
                deleted_and_defaulted_keyword[subtags: [ "destructor", "constructor", "function", ]],
                PatternRange.new(
                    tag_content_as: "meta.function.definition.parameters.special.member.destructor",
                    start_pattern: Pattern.new(
                        match: /\(/,
                        tag_as: "punctuation.section.parameters.begin.bracket.round.special.member.destructor"
                        ),
                    end_pattern: Pattern.new(
                        match: /\)/,
                        tag_as: "punctuation.section.parameters.end.bracket.round.special.member.destructor"
                        ),
                    # destructors cant have arguments
                    includes: []
                ),
                :qualifiers_and_specifiers_post_parameters,
                # initial context is here for things like noexcept()
                # TODO: fix this pattern an make it more strict
                :$initial_context
            ],
            needs_semicolon: false,
            body_includes: [ :function_body_context ],
        )
    end
    grammar[:destructor_inline] = destructor[
        Pattern.new(
            # find the begining of the line
            Pattern.new(/^/).then(std_space).then(optional_calling_convention).zeroOrMoreOf(
                match:  @cpp_tokens.that(:isFunctionSpecifier).then(std_space),
                includes: [
                    :functional_specifiers_pre_parameters
                ]
            ).then(
                tag_as: "entity.name.function.destructor entity.name.function.definition.special.member.destructor",
                match: Pattern.new(/~/).then(variableBounds[identifier].lookAheadFor(/\(/))
            )
        )
    ]
    grammar[:destructor_root] = destructor[
        std_space.then(optional_calling_convention).then(
            inline_scope_resolution[".destructor"]
        ).then(
            match: Pattern.new(
                Pattern.new(
                        match: variableBounds[identifier],
                        reference: "class_name",
                        dont_back_track?: true
                ).then(std_space).then(
                    /::/
                ).then(std_space).then(/~/).matchResultOf(
                    "class_name"
                ).then(std_space).lookAheadFor(/\(/)
            ),
            includes: [
                # the first part
                Pattern.new(
                    match: identifier.lookAheadFor(/:/),
                    tag_as: "entity.name.type.destructor",
                ),
                # the second part
                Pattern.new(
                    match: lookBehindFor(/:/).then(/~/).then(identifier),
                    tag_as: "entity.name.function.definition.special.member.destructor"
                ),
                # the scope operator
                Pattern.new(
                    match: /::/,
                    tag_as: "punctuation.separator.namespace.access punctuation.separator.scope-resolution.destructor"
                )
            ]
        )
    ]
#
# Operators
#
    grammar[:operators] = []
    grammar[:wordlike_operators] = [
        Pattern.new(
            match: variableBounds[ @cpp_tokens.that(:isOperator, :isWord, not(:isTypeCastingOperator), not(:isControlFlow), not(:isFunctionLike)) ],
            tag_as: "keyword.operator.wordlike keyword.operator.$match",
        ),
    ]
    array_of_function_like_operators = @cpp_tokens.tokens.select { |each| each[:isOperator] && each[:isFunctionLike] && each[:isWord] && !each[:isSpecifier] }
    for each in array_of_function_like_operators
        name = each[:name]
        grammar[:operators].push(functionCallGenerator[
            repository_name: "#{name}_operator",
            match_name: variableBounds[/#{name}/],
            tag_name_as: "keyword.operator.functionlike keyword.operator.#{name}",
            tag_content_as: "arguments.operator.#{name}",
            tag_parenthese_as: "operator.#{name}"
        ])
    end
    # edgecase for `sizeof...`
    grammar[:operators].push(functionCallGenerator[
        repository_name: "sizeof_variadic_operator",
        match_name: /\bsizeof\.\.\./,
        tag_name_as: "keyword.operator.functionlike keyword.operator.sizeof.variadic",
        tag_content_as: "arguments.operator.sizeof.variadic",
        tag_parenthese_as: "operator.sizeof.variadic"
    ])

    grammar[:ternary_operator] = PatternRange.new(
        apply_end_pattern_last: true,
        start_pattern: Pattern.new(
            match: /\?/,
            tag_as: "keyword.operator.ternary",
        ),
        end_pattern: Pattern.new(
            match: /:/,
            tag_as: "keyword.operator.ternary"
        ),
        includes: grammar[:evaluation_context]
    )

    grammar[:operators] += [
        Pattern.new(
            match: /--/,
            tag_as: "keyword.operator.decrement"
        ),
        Pattern.new(
            match: /\+\+/,
            tag_as: "keyword.operator.increment"
        ),
        Pattern.new(
            match: /(?:<<|>>)/,
            tag_as: "keyword.operator.bitwise.shift"
        ),
        Pattern.new(
            match: /(?:!=|<=|>=|==|<|>)/,
            tag_as: "keyword.operator.comparison"
        ),
        Pattern.new(
            match: /(?:&&|!|\|\|)/,
            tag_as: "keyword.operator.logical"
        ),
        Pattern.new(
            match: /(?:&|\||\^|~)/,
            tag_as: "keyword.operator.bitwise"
        ),
        assignment_operators,
        Pattern.new(
            match: /(?:%|\*|\/|-|\+)/,
            tag_as: "keyword.operator.arithmetic"
        ),
        :ternary_operator,
    ]
#
# function pointer
#
    after_declaration = std_space.maybe(grammar[:qualifiers_and_specifiers_post_parameters_inline].then(std_space)).lookAheadFor(/[{=,);>]|\n/).lookAheadToAvoid(/\(/)
    functionPointerGenerator = ->(identifier_tag) do
        return PatternRange.new(
            start_pattern: grammar[:simple_type].then(std_space).then(
                    match: /\(/,
                    tag_as: "punctuation.section.parens.begin.bracket.round.function.pointer"
                ).then(
                    match: /\*/,
                    tag_as: "punctuation.definition.function.pointer.dereference",
                ).maybe(@spaces).maybe(
                    match: identifier,
                    tag_as: identifier_tag
                ).maybe(@spaces).zeroOrMoreOf(
                    # an array of function pointers ?
                    array_brackets
                ).then(
                    # closing ) for the variable name
                    match: /\)/,
                    tag_as: "punctuation.section.parens.end.bracket.round.function.pointer"
                ).maybe(@spaces).then(
                    # opening ( for the parameter types
                    match: /\(/,
                    tag_as: "punctuation.section.parameters.begin.bracket.round.function.pointer"
                ),
            end_pattern: Pattern.new(
                    match: /\)/,
                    tag_as: "punctuation.section.parameters.end.bracket.round.function.pointer"
                ).then(after_declaration),
            includes: [
                :function_parameter_context,
            ]
        )
    end
    grammar[:function_pointer] = functionPointerGenerator["variable.other.definition.pointer.function"]
    grammar[:function_pointer_parameter] = functionPointerGenerator["variable.parameter.pointer.function"]
    grammar[:typedef_function_pointer] = PatternRange.new(
        start_pattern: Pattern.new(
            match: variableBounds[/typedef/],
            tag_as: "keyword.other.typedef"
        ).maybe(@spaces).lookAheadFor(Pattern.new(/.*\(\*\s*/).then(identifier).then(/\s*\)/)),
        end_pattern: lookBehindFor(/;/),
        includes: [
            functionPointerGenerator["entity.name.type.alias entity.name.type.pointer.function"]
        ]
    )
#
# Parameters
#
    parameter_ending = lookAheadFor(/\)/).or(grammar[:comma])
    grammar[:parameter_or_maybe_value] = PatternRange.new(
        tag_as: "meta.parameter",
        start_pattern: std_space.lookAheadFor(/\w/),
        end_pattern: parameter_ending,
        includes: [
            :ever_present_context, #macros and comments
            :function_pointer_parameter,
            # all of these (indented) are here because of #282
                :memory_operators,
                :builtin_storage_type_initilizer,
                :curly_initializer,
            :decltype,
            :vararg_ellipses,
            # this next pattern is for finding preceding modifiers like "const" or "short const" and then forcing the word after them to be tagged as a type "const aType" "short const int"
            Pattern.new(
                Pattern.new(
                    match: oneOrMoreOf(
                        # a specifier
                        Pattern.new(
                            match: @cpp_tokens.that(:isStorageSpecifier),
                            tag_as: "storage.modifier.specifier.parameter",
                        ).then(std_space)
                    ),
                    includes: [
                        :storage_types
                    ]
                ).then(std_space).then(
                    Pattern.new(
                        inline_builtin_storage_type
                    ).or(
                        match: variableBounds[identifier].then(
                                @word_boundary
                            ).then(
                                @cpp_tokens.lookBehindToAvoidWordsThat(:isStorageSpecifier)
                            ),
                        tag_as: "entity.name.type.parameter",
                    )
                ).then(std_space).lookAheadFor(/,|\)|=/),
            ),
            :storage_types,
            :function_call, # function call is here because of #282, it should be below decltype() and function pointer, storage types, and modifiers but above scope resolution
            :scope_resolution_parameter_inner_generated,
            # match the class/struct/enum/union keywords
            Pattern.new(
                match: variableBounds[@cpp_tokens.that(:isTypeCreator)],
                tag_as: "storage.type.$match"
            ),
            # This is a range for when there is a variable-default assignment
            PatternRange.new(
                start_pattern: lookBehindFor(/=/),
                end_pattern: parameter_ending,
                # this is for everything after the =, but before the next parameter
                includes: [ :evaluation_context ]
            ),
            # assignment_operator,
            # # standard parameter
            Pattern.new(
                # avoid
                # 1. \s (otherwise the checks below wont work)
                # 2. \( the start of the function
                # 3. , the start of a previous parameter
                # 4. : then end of a scope resolution::
                lookBehindToAvoid(/\s|\(|,|:/).then(std_space).then(
                    match: identifier,
                    tag_as: "variable.parameter",
                ).then(
                    std_space
                # the parameter can end with
                # 1. ) the end of the function parentheses
                # 2. , the end of the parameter/start of the next parameter
                # 3. [ the start of an array-type parameter
                # 4. a C++ style comment
                # 5. \n a newline in the case of:
                #     func(
                #         int thing
                #     )
                ).lookAheadFor(Pattern.new(/\)|,|\[|=|\/\//).or(@end_of_line))
            ),
            :attributes_context,
            # find the array []'s
            PatternRange.new(
                tag_as: "meta.bracket.square.array",
                # \G only match at the begining
                start_pattern: Pattern.new(
                    match: /\[/,
                    tag_as: "punctuation.definition.begin.bracket.square.array.type"
                ),
                end_pattern: Pattern.new(
                    match: /\]/,
                    tag_as: "punctuation.definition.end.bracket.square.array.type"
                ),
                includes: [ :evaluation_context ]
            ),
            # # any words that are not storage types/modifiers, scope resolutions, or parameters are assumed to be user-defined types
            Pattern.new(
                match: identifier.then(@cpp_tokens.lookBehindToAvoidWordsThat(:isTypeCreator)),
                tag_as: "entity.name.type.parameter"
            ),
            :template_call_range_helper,
            # # tag the reference and dereference operator
            ref_deref[],
            :evaluation_context, # this is part of the #282 workaround
            :ever_present_context,
        ]
    )
    grammar[:parameter] = PatternRange.new(
        tag_as: "meta.parameter",
        start_pattern: std_space.lookAheadFor(/\w/),
        end_pattern: parameter_ending,
        includes: [
            :ever_present_context, #macros and comments
            :string_context,
            :function_pointer_parameter,
            :decltype,
            :vararg_ellipses,
            # this next pattern is for finding preceding modifiers like "const" or "short const" and then forcing the word after them to be tagged as a type "const aType" "short const int"
            Pattern.new(
                Pattern.new(
                    match: oneOrMoreOf(
                        # a specifier
                        Pattern.new(
                            match: @cpp_tokens.that(:isStorageSpecifier),
                            tag_as: "storage.modifier.specifier.parameter",
                        ).then(std_space)
                    ),
                    includes: [
                        :storage_types
                    ]
                ).then(std_space).then(
                    Pattern.new(
                        inline_builtin_storage_type
                    ).or(
                        match: variableBounds[identifier].then(
                                @word_boundary
                            ).then(
                                @cpp_tokens.lookBehindToAvoidWordsThat(:isStorageSpecifier)
                            ),
                        tag_as: "entity.name.type.parameter",
                    )
                ).then(std_space).lookAheadFor(/,|\)|=/),
            ),
            :storage_types,
            :scope_resolution_parameter_inner_generated,
            # match the class/struct/enum/union keywords
            Pattern.new(
                match: variableBounds[@cpp_tokens.that(:isTypeCreator)],
                tag_as: "storage.type.$match"
            ),
            # This is a range for when there is a variable-default assignment
            PatternRange.new(
                start_pattern: lookBehindFor(/=/),
                end_pattern: parameter_ending,
                # this is for everything after the =, but before the next parameter
                includes: [ :evaluation_context ]
            ),
            assignment_operator,
            # standard parameter
            Pattern.new(
                # avoid
                # 1. \s (otherwise the checks below wont work)
                # 2. \( the start of the function
                # 3. , the start of a previous parameter
                # 4. : then end of a scope resolution::
                lookBehindToAvoid(/\s|\(|,|:/).then(std_space).then(
                    match: identifier,
                    tag_as: "variable.parameter",
                ).then(
                    std_space
                # the parameter can end with
                # 1. ) the end of the function parentheses
                # 2. , the end of the parameter/start of the next parameter
                # 3. [ the start of an array-type parameter
                # 4. = the start of a default-value assignment
                # 5. \n a newline in the case of:
                #     func(
                #         int thing
                #     )
                ).lookAheadFor(/\)|,|\[|=|\n/)
            ),
            :attributes_context,
            # find the array []'s
            PatternRange.new(
                tag_as: "meta.bracket.square.array",
                # \G only match at the begining
                start_pattern: Pattern.new(
                    match: /\[/,
                    tag_as: "punctuation.definition.begin.bracket.square.array.type"
                ),
                end_pattern: Pattern.new(
                    match: /\]/,
                    tag_as: "punctuation.definition.end.bracket.square.array.type"
                ),
                includes: [ :evaluation_context ]
            ),
            # any words that are not storage types/modifiers, scope resolutions, or parameters are assumed to be user-defined types
            Pattern.new(
                match: identifier.then(@cpp_tokens.lookBehindToAvoidWordsThat(:isTypeCreator)),
                tag_as: "entity.name.type.parameter"
            ),
            :template_call_range_helper,
            # tag the reference and dereference operator
            ref_deref[],
            :ever_present_context,
        ]
    )
#
# Access . .* -> ->*
#
    dot_operator = Pattern.new(/\.\*/).or(/\./)
    arrow_operator = Pattern.new(/->\*/).or(/->/)
    dot_or_arrow_operator = Pattern.new(/(?:\.\*|\.|->|->\*)/)
    member_operator = Pattern.new(
            match: dot_operator,
            tag_as: "punctuation.separator.dot-access"
        ).or(
            match: arrow_operator,
            tag_as: "punctuation.separator.pointer-access"
        )
    subsequent_object_with_operator = variable_name_without_bounds.maybe(@spaces).then(member_operator.groupless).maybe(@spaces)
    # TODO: the member_access and method_access can probably be simplified considerably
    # TODO: member_access and method_access might also need additional matching to handle scope resolutions
    generatePartialMemberFinder = ->(tag_name) do
        the_this_keyword.or(
            generateVariableVariations[tag_name].or(lookBehindFor(/\]|\)/)).maybe(@spaces),
        ).then(
            member_operator
        )
    end
    partial_member = generatePartialMemberFinder["variable.other.object.access.$match"]
    member_context = [
            mid_member = Pattern.new(
                match: lookBehindFor(dot_or_arrow_operator).maybe(
                    @spaces
                ).then(
                    generatePartialMemberFinder["variable.other.object.property"]
                )
            ),
            partial_member,
            :member_access,
            :method_access,
        ]
    member_start = partial_member.then(
            match: zeroOrMoreOf(subsequent_object_with_operator.groupless),
            includes: member_context
        ).maybe(@spaces)
    # access to attribute
    type_represenetations = @cpp_tokens.representationsThat(:isType)
    lookahead_friedly_types_pattern = /#{type_represenetations.map { |each| each+"[^\\w]" } .join('|')}/
    grammar[:member_access] = member_access = Pattern.new(
        match: member_start.then(
                match: @word_boundary.lookAheadToAvoid(lookahead_friedly_types_pattern).then(variable_name_without_bounds).then(@word_boundary).lookAheadToAvoid(/\(/),
                tag_as: "variable.other.property"
            )
        )
    # access to method
    grammar[:method_access] = method_access = PatternRange.new(
            start_pattern: member_start.then(
                    # the ~ is for destructors
                    match: maybe(/~/).then(variable_name_without_bounds),
                    tag_as: "entity.name.function.member"
                ).maybe(@spaces).then(
                    match: /\(/,
                    tag_as: "punctuation.section.arguments.begin.bracket.round.function.member"
                ),
            end_pattern: Pattern.new(
                    match: /\)/,
                    tag_as: "punctuation.section.arguments.end.bracket.round.function.member"
                ),
            includes: [:evaluation_context],
        )
#
# Namespace
#
    # see https://en.cppreference.com/w/cpp/language/namespace
    grammar[:using_namespace] = PatternRange.new(
        tag_as: "meta.using-namespace",
        start_pattern: lookBehindToAvoid(@standard_character).then(
                match: /using/,
                tag_as: "keyword.other.using.directive",
            ).then(@spaces).then(
                match: /namespace/,
                tag_as: "keyword.other.namespace.directive storage.type.namespace.directive"
            ).then(@spaces).maybe(
                grammar[:scope_resolution_namespace_using]
            ).then(
                match: variable_name,
                tag_as: "entity.name.namespace"
            ).lookAheadFor(
                /;|\n/
            ),
        end_pattern: @semicolon,
        )
    grammar[:namespace_alias] = Pattern.new(
        tag_as: "meta.declaration.namespace.alias",
        should_fully_match: ["namespace foo = bar;", "namespace fs=boost::filesystem;", "namespace std = ::std;"],
        should_not_partial_match: ["namespace foo {", "using namespace std;"],
        match: lookBehindToAvoid(@standard_character).then(
                    match: /namespace/,
                    tag_as: "keyword.other.namespace.alias storage.type.namespace.alias"
                ).then(@spaces).then(
                    match: variable_name,
                    tag_as: "entity.name.namespace.alias",
                ).maybe(@spaces).then(assignment_operator).maybe(@spaces).then(
                    tag_as: "meta.declaration.namespace.alias.value",
                    match: grammar[:scope_resolution_namespace_alias].maybe(@spaces).then(
                            match: variable_name,
                            tag_as: "entity.name.namespace",
                    ).maybe(@spaces).then(
                        @semicolon.or(/\n/)
                    ),
                ),
    )
    grammar[:namespace_block] = generateBlockFinder(
        name: "namespace",
        tag_as: "meta.block.namespace",
        needs_semicolon: false,
        start_pattern: Pattern.new(
                match: variableBounds[/namespace/],
                tag_as: "keyword.other.namespace.definition storage.type.namespace.definition"
            ),
        head_includes: [
            :ever_present_context,
            :attributes_context,
            :normal_variable_assignment,
            :normal_variable_declaration,
            grammar[:scope_resolution_namespace_block].maybe(@spaces).then(
                    match: variable_name,
                    tag_as: "entity.name.namespace",
                ).maybe(@spaces).maybe(
                    Pattern.new(
                        match: /::/,
                        tag_as: "punctuation.separator.scope-resolution.namespace.block"
                    ).maybe(@spaces).then(
                        match: /inline/,
                        tag_as: "storage.modifier.inline"
                    )
                )
        ],
        )

#
# Lambdas
#
    only_balanced_square_bracke = Pattern.new(
        should_fully_match: [ "[]", "[testing, testing]", "[testing[], testing]" ],
        should_not_fully_match: [ "testing[]" ],
        reference: "square_brackets",
        match: Pattern.new(
            lookBehindToAvoid(/\[/).then(
                /\[/
            ).lookAheadToAvoid(/\[/).oneOrMoreOf(
                dont_back_track?: true,
                match: Pattern.new(
                    zeroOrMoreOf(
                        match: /[^\[\]]/,
                        dont_back_track?: true,
                    ).maybe(
                        recursivelyMatch("square_brackets")
                    )
                ),
            ).then(/\]/)
        )
    )
    array_of_invalid_function_names = @cpp_tokens.representationsThat(:canAppearBeforeLambdaCapture)
    non_variable_name = /#{array_of_invalid_function_names.map { |each| '\W'+each+'|^'+each } .join('|')}/
    grammar[:lambdas] = lambdas = PatternRange.new(
        start_pattern: Pattern.new(
                should_fully_match: [
                    "[]",
                    "[=]",
                    "[&]",
                    "[x,y,x]",
                    "[x, y, &z, w = 1 + 1]",
                    "[ a = blah[1324 + blah[39430]], b, c ]"
                ],
                should_partial_match: [ "[]", "[=](", "[&]{", "[x,y,x]", "[x, y, &z, w = 1 + 1] (", "[ a = blah[1324], b, c ] {" ],
                should_not_partial_match: [ "delete[]", "thing[]", "thing []", "thing     []", "thing[0][0] = 0" ],
                match: Pattern.new(
                    Pattern.new(
                        match: lookBehindFor(/[^\s]|^/).lookBehindToAvoid(/[\w\]\)\[\*&">]/).or(lookBehindFor(non_variable_name)).maybe(@spaces).then(
                            match: Pattern.new(/\[/).lookAheadToAvoid(/\[| *+"| *+\d/),
                            tag_as: "punctuation.definition.capture.begin.lambda",
                        )
                    ).then(
                        match: zeroOrMoreOf(
                            match: Pattern.new(/[^\[\]]/).or(only_balanced_square_bracke),
                            dont_back_track?: true,
                        ),
                        tag_as: "meta.lambda.capture",
                        # the zeroOrMoreOf() is for other []'s that are inside of the lambda capture
                        # this pattern is still imperfect: if someone had a string literal with ['s in it, it could fail
                        includes: [
                            :the_this_keyword,
                            Pattern.new(
                                match: identifier,
                                tag_as: "variable.parameter.capture",
                            ).then(std_space).then(
                                lookAheadFor(/\]|\z|$/).or(
                                    grammar[:comma]
                                ).or(
                                    grammar[:assignment_operator]
                                )
                            ),
                            :evaluation_context
                        ],
                    ).then(
                        match: Pattern.new(/\]/).lookAheadToAvoid(std_space.then(/[\[\];=]/)),
                        tag_as: "punctuation.definition.capture.end.lambda",
                    )
                )
            ),
        end_pattern: Pattern.new(
                match: lookBehindFor(/[;}]/),
            ),
        includes: [
            # check for parameters first
            PatternRange.new(
                tag_as: 'meta.function.definition.parameters.lambda',
                start_pattern: Pattern.new(
                        match: /\(/,
                        tag_as:  "punctuation.definition.parameters.begin.lambda",
                    ),
                end_pattern: Pattern.new(
                        match: /\)/,
                        tag_as:  "punctuation.definition.parameters.end.lambda",
                    ),
                includes: [ :function_parameter_context ]
            ),
            # specifiers
            Pattern.new(
                match: variableBounds[ @cpp_tokens.that(:isLambdaSpecifier) ],
                tag_as: "storage.modifier.lambda.$match"
            ),
            # check for the -> syntax
            PatternRange.new(
                start_pattern: Pattern.new(
                        match: /->/,
                        tag_as: "punctuation.definition.lambda.return-type"
                    ),
                end_pattern: Pattern.new(
                        match: lookAheadFor(/\{/),
                    ),
                includes: [
                    :comments,
                    Pattern.new(
                        tag_as: "storage.type.return-type.lambda",
                        match: /\S+/,
                    ),
                ],
            ),
            # then find the body
            PatternRange.new(
                tag_as: "meta.function.definition.body.lambda",
                start_pattern: Pattern.new(
                        match: /\{/,
                        tag_as:  "punctuation.section.block.begin.bracket.curly.lambda",
                    ),
                end_pattern: Pattern.new(
                        match: /\}/,
                        tag_as:  "punctuation.section.block.end.bracket.curly.lambda",
                    ),
                includes: [ :function_body_context ]
            ),
        ]
    )
#
# Classes, structs, unions, enums
#
    grammar[:enumerator_list] = Pattern.new(
        tag_as: "meta.enum.definition",
        match: Pattern.new(
            Pattern.new(
                match: variable_name,
                tag_as: "variable.other.enummember",
            ).maybe(@spaces).maybe(
                inline_attribute
            ).maybe(@spaces).maybe(
                Pattern.new(
                    match: /\=/,
                    tag_as: "keyword.operator.assignment",
                ).maybe(@spaces).then(
                    match: /.+?/,
                    includes: [ :evaluation_context ]
                ).maybe(@spaces)
            ).then(
                Pattern.new(
                    match: Pattern.new(Pattern.new(/[,;]/).lookAheadToAvoid(/'/)).or(/\n/),
                    includes: [
                        :comma,
                        :semicolon,
                    ],
                ).or(
                    lookAheadFor(/\}[^']/)).or(
                        lookAheadFor(Pattern.new(/\/\//).or(/\/\*/)
                    )
                )
            )
        ),
    )
    # see https://en.cppreference.com/w/cpp/language/enum
    # this range matches both the case with brackets and the case without brackets
    grammar[:enum_block] = generateBlockFinder(
            name: "enum",
            tag_as: "meta.block.enum",
            start_pattern: Pattern.new(
                    match: variableBounds[ /enum/ ],
                    tag_as: "storage.type.enum"
                ).maybe(
                    @spaces.then(
                        # see "Scoped enumerations" on  https://en.cppreference.com/w/cpp/language/enum
                        match: /class|struct/,
                        tag_as: "storage.type.enum.enum-key.$match",
                    )
                ).then(@spaces.or(inline_attribute).or(lookAheadFor(/{/))).maybe(@spaces).maybe(
                    match: variable_name,
                    tag_as: "entity.name.type.enum",
                ).maybe(
                    maybe(@spaces).then(
                        match: /:/,
                        tag_as: "punctuation.separator.colon.type-specifier",
                    ).maybe(@spaces).maybe(
                        scope_resolution
                    ).maybe(@spaces).then(
                        match: variable_name,
                        tag_as: "storage.type.integral.$match",
                    )
            ),
            head_includes: [ :$initial_context ],
            body_includes: [ :ever_present_context, :enumerator_list, :comments, :comma, :semicolon ],
        )
    # the following are basically the equivlent of:
    #     @cpp_tokens.that(:isAccessSpecifier).or(/,/).or(/:/)
    # that ^ causes an error in the lookBehindFor() so it has to be manually spread
    can_come_before_a_inherited_class = @cpp_tokens.representationsThat(:isInheritanceSpecifier) + [ ',', ':', ]
    can_come_before_a_inherited_class_regex = /#{can_come_before_a_inherited_class.join('|')}/
    grammar[:inheritance_context] = [
        :ever_present_context,
        Pattern.new(
            match: /,/,
            tag_as: "punctuation.separator.delimiter.comma.inheritance"
        ),
        Pattern.new(
            match: variableBounds[ @cpp_tokens.that(:isAccessSpecifier) ],
            tag_as: "storage.type.modifier.access.$match",
        ),
        Pattern.new(
            match: variableBounds[ /virtual/ ],
            tag_as: "storage.type.modifier.virtual",
        ),
        lookBehindFor(can_come_before_a_inherited_class_regex).maybe(@spaces).lookAheadToAvoid(@cpp_tokens.that(:isAccessSpecifier).or(/virtual/)).then(
            qualified_type
        ),
    ]
    final_modifier = Pattern.new(
        match: variableBounds[/final/],
        tag_as: "storage.type.modifier.final",
    )
    generateClassOrStructBlockFinder = ->(name, tail_includes = [:$initial_context]) do
        return generateBlockFinder(
            tag_as: "meta.block.#{name}",
            name: name,
            start_pattern: Pattern.new(
                    should_not_fully_match: ["#{name} foo {","#{name} foo{"],
                    should_partial_match: ["#{name} foo f;", "#{name} st s;"],
                    match: Pattern.new(
                        reference: "storage_type",
                        match: variableBounds[ /#{name}/ ],
                        tag_as: "storage.type.$match",
                    ).then(
                        std_space
                    ).then(
                        lookAheadFor(/{/).or(
                            maybe(
                                inline_attribute.then(std_space)
                            ).maybe(
                                zeroOrMoreOf(
                                    match: variable_name.then(std_space),
                                    dont_back_track?: true,
                                    includes: [
                                        # needs std_space so that it can beat-out the class/struct name (below)
                                        final_modifier.then(std_space),
                                        # the last word is assumed to be the class/stuct name
                                        Pattern.new(
                                            Pattern.new(
                                                match: variable_name,
                                                tag_as: "entity.name.type.#{name}",
                                            ).then(std_space).maybe(
                                                final_modifier.then(std_space)
                                            ).lookAheadFor(/:|{|$/)
                                        ),
                                        # a known macro
                                        Pattern.new(
                                            match: /DLLEXPORT/,
                                            tag_as: "entity.name.other.preprocessor.macro.predefined.DLLEXPORT",
                                        ),
                                        # this is for the use of unknown macros (cannot contain arguments)
                                        Pattern.new(
                                            match: identifier,
                                            tag_as: "entity.name.other.preprocessor.macro.predefined.probably.$match",
                                        ),
                                    ]
                                )
                            ).maybe(
                                #
                                # inheritance
                                #
                                std_space.then(
                                    match: Pattern.new(/:/).lookAheadToAvoid(/:/),
                                    tag_as: "punctuation.separator.colon.inheritance"
                                )
                            )
                        )
                    ),
                ),
            head_includes: [
                :ever_present_context, # directives and comments
                :inheritance_context,
                :template_call_range_helper,
            ],
            body_includes: [ :function_pointer, :static_assert, :constructor_inline, :destructor_inline, :operator_overload, :normal_variable_declaration, :normal_variable_assignment, :$initial_context ],
            tail_includes: tail_includes
        )
    end
    grammar[:class_block]  = generateClassOrStructBlockFinder["class"]
    grammar[:struct_block] = generateClassOrStructBlockFinder["struct"]
    grammar[:union_block]  = generateClassOrStructBlockFinder["union"]
    # the following is a legacy pattern, I'm not sure if it is still accurate
    # I have no idea why it matches a double quote
    grammar[:extern_block] = generateBlockFinder(
        name: 'extern',
        tag_as: "meta.block.extern",
        start_pattern: Pattern.new(
                std_space.then(
                    match: /extern/,
                    tag_as: "storage.type.extern"
                )
            ).lookAheadFor(/\s*\"/),
        head_includes: [ :$initial_context ],
        secondary_includes: [ :$initial_context ]
    )
    generateTypedefClassOrStructBlockFinder = ->(name) do
        return PatternRange.new(
            start_pattern: Pattern.new(
                match: variableBounds[/typedef/],
                tag_as: "keyword.other.typedef"
            ).maybe(@spaces).lookAheadFor(variableBounds[/#{name}/]),
            end_pattern: lookBehindFor(/;/),
            includes: [
                generateClassOrStructBlockFinder[name, [
                    maybe(ref_deref[]).then(std_space).then(
                        match: variable_name,
                        tag_as: "entity.name.type.alias"
                    ),
                    /,/,
                ]]
            ]
        )
    end
    grammar[:typedef_class]  = generateTypedefClassOrStructBlockFinder["class"]
    grammar[:typedef_struct] = generateTypedefClassOrStructBlockFinder["struct"]
    grammar[:typedef_union]  = generateTypedefClassOrStructBlockFinder["union"]

    generateDeclareFor = ->(name) do
        Pattern.new(
            should_partial_match: [ "#{name} crypto_aead *tfm = crypto_aead_reqtfm(req);", "#{name} aegis_block blocks[AEGIS128L_STATE_BLOCKS];" ],
            match: Pattern.new(
                match: variableBounds[/#{name}/],
                tag_as: "storage.type.#{name}.declare",
            ).then(std_space).then(
                match: variable_name,
                tag_as: "entity.name.type.#{name}",
            ).maybe(
                ref_deref[]
            ).then(std_space).then(
                @cpp_tokens.lookAheadToAvoidWordsThat(:isClassInheritenceSpecifier)
            ).then(
                match: variable_name,
                tag_as: "variable.other.object.declare",
            ).then(std_space).lookAheadFor(/\S/).lookAheadToAvoid(/[:{a-zA-Z]/)
        )
    end
    grammar[:standard_declares] = [
        grammar[:struct_declare] = generateDeclareFor["struct"],
        grammar[:union_declare ] = generateDeclareFor["union"],
        grammar[:enum_declare  ] = generateDeclareFor["enum"],
        grammar[:class_declare ] = generateDeclareFor["class"],
    ]
    generateOverqualifiedTypeFor = ->(name) do
        Pattern.new(
            should_partial_match: [ "#{name} skcipher_walk *walk," ],
            match: Pattern.new(
                match: /\b#{name}/,
                tag_as: "storage.type.#{name}.parameter",
            ).then(std_space).then(
                match: variable_name,
                tag_as: "entity.name.type.#{name}.parameter",
            ).then(std_space).maybe(
                ref_deref[].then(std_space)
            # this is a maybe because its possible to have a type declare without an actual parameter
            ).maybe(
                match: variable_name,
                tag_as: "variable.other.object.declare",
            ).then(std_space).maybe(
                Pattern.new(/\[/).then(std_space).then(/\]/).then(std_space),
            ).lookAheadFor(/,|\)|\n/)
        )
    end
    grammar[:over_qualified_types] = [
        grammar[:parameter_struct] = generateOverqualifiedTypeFor["struct"],
        grammar[:parameter_enum  ] = generateOverqualifiedTypeFor["enum"],
        grammar[:parameter_union ] = generateOverqualifiedTypeFor["union"],
        grammar[:parameter_class ] = generateOverqualifiedTypeFor["class"],
    ]

#
# Misc
#
    grammar[:assembly] = assembly_pattern(std_space, identifier)
    grammar[:backslash_escapes] = backslash_escapes()
    grammar[:string_escaped_char] = grammar[:backslash_escapes]
    grammar[:range_for_inner] =  PatternRange.new(
            tag_as: "meta.parens.control.for",
            start_pattern: Pattern.new(
                lookBehindFor(/\Wfor \(|^for \(|\Wfor\(|^for\(/), # lookbehinds must be a fixed length otherwise \s* would've been used
            ),
            end_pattern: lookAheadFor(/\)/),
            includes: [
                # normal ranged for loop
                Pattern.new(
                    Pattern.new(
                        tag_as: "meta.type",
                        match: normal_type_pattern,
                    ).then(std_space).then(
                        match: identifier,
                        tag_as: "variable.other.object.declare.for",
                    ).then(std_space).then(
                        match: /:/,
                        tag_as: "punctuation.separator.colon.range-based"
                    ).lookAheadToAvoid(/:/),
                ),
                # tuple ranged for loop
                Pattern.new(
                    tag_as: "meta.binding",
                    match: Pattern.new(
                        Pattern.new(
                            tag_as: "meta.type",
                            match: normal_type_pattern,
                        ).then(std_space).then(
                            match: '[',
                            tag_as: "punctuation.definition.begin.bracket.square.binding",
                        ).then(std_space).then(
                            Pattern.new(
                                match: identifier,
                                tag_as: "variable.other.for",
                            ).zeroOrMoreOf(
                                std_space.then(grammar[:comma]).then(std_space).then(
                                    match: identifier,
                                    tag_as: "variable.other.for",
                                )
                            )
                        ).then(std_space).then(
                            match: ']',
                            tag_as: "punctuation.definition.end.bracket.square.binding",
                        ).then(std_space).then(
                            match: /:/,
                            tag_as: "punctuation.separator.colon.range-based"
                        ).lookAheadToAvoid(/:/),
                    )
                ),
                :evaluation_context,
            ],
        )
#
# Misc Legacy
#
    grammar[:square_brackets] = LegacyPattern.new({
        name: "meta.bracket.square.access",
        begin: "([a-zA-Z_][a-zA-Z_0-9]*|(?<=[\\]\\)]))?(\\[)(?!\\])",
        beginCaptures: {
            "1" => {
                name: "variable.other.object"
            },
            "2" => {
                name: "punctuation.definition.begin.bracket.square"
            }
        },
        end: "\\]",
        endCaptures: {
            "0" => {
                name: "punctuation.definition.end.bracket.square"
            }
        },
        patterns: [
            {
                include: "#evaluation_context"
            }
        ]
    })
    grammar[:empty_square_brackets] = LegacyPattern.new ({
        name: "storage.modifier.array.bracket.square",
        match: lookBehindToAvoid("delete").then("[").maybe(@spaces).then("]").evaluate,
    })

    getLiteralSuffixPattern = -> (name) do
        Pattern.new(
            should_fully_match: ["s", "sv", "reserved", "abc123"],
            should_not_fully_match: ["_s", "_sv", "_reserved", "_abc123"],
            tag_as: "keyword.other.suffix.literal.user-defined.reserved.#{name}",
            match: Pattern.new(/[a-zA-Z]/).or(universal_character).then(zeroOrMoreOf(subsequent_character))
        ).or(Pattern.new(
            should_fully_match: ["_s", "_sv", "_reserved", "_abc123"],
            tag_as: "keyword.other.suffix.literal.user-defined.#{name}",
            match: Pattern.new(/_/).then(zeroOrMoreOf(subsequent_character))
        ))
    end

    grammar[:string_context] = [
            PatternRange.new(
                tag_as: "string.quoted.double",
                start_pattern: Pattern.new(
                    tag_as: "punctuation.definition.string.begin",
                    match: maybe(match: /u|u8|U|L/, tag_as: "meta.encoding").then(/"/),
                ),
                end_pattern: Pattern.new(
                    tag_as: "punctuation.definition.string.end",
                    match: /"/,
                ).maybe(getLiteralSuffixPattern["string"]),
                includes: [
                    # universal characters \u00AF, \U0001234F
                    Pattern.new(
                        match: universal_character,
                        tag_as: "constant.character.escape",
                    ),
                    # normal escapes \r, \n, \t
                    Pattern.new(
                        match: /\\['"?\\abfnrtv]/,
                        tag_as: "constant.character.escape",
                    ),
                    # octal escapes \017
                    Pattern.new(
                        match: Pattern.new("\\").then(
                            match: /[0-7]/,
                            at_least: 1.times,
                            at_most: 3.times,
                        ),
                        tag_as: "constant.character.escape",
                    ),
                    # hex escapes
                    hex_pattern = Pattern.new(
                        match: Pattern.new(/\\x/).zeroOrMoreOf(/0/).then(
                            match: /[0-9a-fA-F]/,
                            how_many_times?: 2.times,
                        ).lookAheadToAvoid(/[0-9a-fA-F]/),
                        tag_as: "constant.character.escape",
                    ).or(
                        match: Pattern.new(/\\x[0-9a-fA-F]*/).or(/\\x/),
                        tag_as: "invalid.illegal.unknown-escape",
                    ),
                    :string_escapes_context_c
                ]
            ),
            PatternRange.new(
                tag_as: "string.quoted.single",
                start_pattern: Pattern.new(
                    tag_as: "punctuation.definition.string.begin",
                    match: lookBehindToAvoid(/[0-9A-Fa-f]/).maybe(
                        match: /u|u8|U|L/,
                        tag_as: "meta.encoding"
                    ).then(/'/),
                ),
                end_pattern: Pattern.new(
                    tag_as: "punctuation.definition.string.end",
                    match: /'/,
                ).maybe(getLiteralSuffixPattern["character"]),
                includes: [
                    hex_pattern,
                    :string_escapes_context_c,
                    :line_continuation_character,
                ]
            ),
            getRawStringPatterns()
        ]
    grammar[:block] = PatternRange.new(
        tag_as: "meta.block",
        start_pattern: Pattern.new(
            match: /{/,
            tag_as: "punctuation.section.block.begin.bracket.curly"
        ),
        end_pattern: Pattern.new(
            match: /}/,
            tag_as: "punctuation.section.block.end.bracket.curly"
        ),
        includes: [
            :function_body_context
        ]
    )
    grammar[:line_continuation_character] = Pattern.new(
        match: /\\\n/,
        tag_as: "constant.character.escape.line-continuation",
    )
    grammar[:parentheses] = PatternRange.new(
        tag_as: "meta.parens",
        start_pattern: Pattern.new(
            match: /\(/,
            tag_as: "punctuation.section.parens.begin.bracket.round"
        ),
        end_pattern: Pattern.new(
            match: /\)/,
            tag_as: "punctuation.section.parens.end.bracket.round"
        ),
        includes: [
            :range_for_inner,
            # TODO: for typecasting (eventually this should be replaced)
            :over_qualified_types,
            # TODO: for range-based for loops (eventually this should be replaced)
            Pattern.new(
                match: lookBehindToAvoid(/:/).then(/:/).lookAheadToAvoid(/:/),
                tag_as: "punctuation.separator.colon.range-based"
            ),
            :evaluation_context
        ]
    )
    grammar[:string_escapes_context_c] = [
            grammar[:backslash_escapes],
            {
                match: "\\\\.",
                name: "invalid.illegal.unknown-escape"
            },
            {
                # %
                # (\d+\$)?                             # field (argument #)
                # [#0\- +']*                           # flags
                # [,;:_]?                              # separator character (AltiVec)
                # ((-?\d+)|\*(-?\d+\$)?)?              # minimum field width
                # (\.((-?\d+)|\*(-?\d+\$)?)?)?         # precision
                # (hh|h|ll|l|j|t|z|q|L|vh|vl|v|hv|hl)? # length modifier
                # [diouxXDOUeEfFgGaACcSspn%]           # conversion type
                match: "(?x) (?!%')(?!%\")%\n(\\d+\\$)?\t\t\t\t\t\t   # field (argument #)\n[#0\\- +']*\t\t\t\t\t\t  # flags\n[,;:_]?\t\t\t\t\t\t\t  # separator character (AltiVec)\n((-?\\d+)|\\*(-?\\d+\\$)?)?\t\t  # minimum field width\n(\\.((-?\\d+)|\\*(-?\\d+\\$)?)?)?\t# precision\n(hh|h|ll|l|j|t|z|q|L|vh|vl|v|hv|hl)? # length modifier\n[diouxXDOUeEfFgGaACcSspn%]\t\t   # conversion type",
                name: "constant.other.placeholder"
            },
            # I don't think these are actual escapes, and they incorrectly mark valid strings
            # It might be related to printf and format from C (which is low priority for C++)
            # {
            #     match: "(%)(?!\"\\s*(PRI|SCN))",
            #     captures: {
            #         "1" => {
            #             name: "constant.other.placeholder"
            #         }
            #     }
            # }
        ]
    grammar[:vararg_ellipses] = Pattern.new(
        match: /(?<!\.)\.\.\.(?!\.)/,
        tag_as: "punctuation.vararg-ellipses",
    )


#
# Generate macro versions of all ranged patterns
#
    # assign that to the macro context
    grammar[:macro_context] = ["source.cpp.embedded.macro"]

#
# Save
#
name = "cpp"
grammar.save_to(
    syntax_name: name,
    syntax_dir: "./autogenerated",
    tag_dir: "./autogenerated",
)
# create a duplicate grammar with all pattern ranges bailed-out
system(
    "node",
    PathFor[:generate_macro_bailout],
    PathFor[:syntax],
    PathFor[:embedded_syntax],
)
