Files
Nuake-custom/Nuake/Vendors/katana-parser/selector.c
2025-01-30 22:41:17 -05:00

659 lines
27 KiB
C

/**
* Copyright (c) 2015 QFish <im@qfi.sh>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "selector.h"
#include <string.h>
#include <assert.h>
#undef assert
#define assert(x)
// Refs:
// http://www.w3.org/TR/css3-selectors/
//
static KatanaPseudoType name_to_pseudo_type(const char* name, bool hasArguments);
bool katana_selector_crosses_tree_scopes(const KatanaSelector* selector)
{
// TODO: To be supported
return false;
}
// bool katana_is_attribute_selector(const KatanaSelector* selector)
// {
// return selector->match == KatanaSelectorMatchAttributeExact
// || selector->match == KatanaSelectorMatchAttributeSet
// || selector->match == KatanaSelectorMatchAttributeList
// || selector->match == KatanaSelectorMatchAttributeHyphen
// || selector->match == KatanaSelectorMatchAttributeContain
// || selector->match == KatanaSelectorMatchAttributeBegin
// || selector->match == KatanaSelectorMatchAttributeEnd;
// }
KatanaPseudoType katana_parse_pseudo_type(const char* name, bool hasArguments)
{
KatanaPseudoType pseudoType = name_to_pseudo_type(name, hasArguments);
if (pseudoType != KatanaPseudoUnknown)
return pseudoType;
if (katana_string_has_prefix(name, "-webkit-"))
return KatanaPseudoWebKitCustomElement;
return KatanaPseudoUnknown;
}
void katana_selector_extract_pseudo_type(KatanaSelector* selector)
{
if (selector->pseudo == KatanaPseudoNotParsed)
selector->pseudo = KatanaPseudoUnknown;
if (selector->match != KatanaSelectorMatchPseudoClass && selector->match != KatanaSelectorMatchPseudoElement && selector->match != KatanaSelectorMatchPagePseudoClass)
return;
bool hasArguments = (NULL != selector->data->argument) || (NULL != selector->data->selectors);
selector->pseudo = katana_parse_pseudo_type(selector->data->value, hasArguments);
bool element = false; // pseudo-element
bool compat = false; // single colon compatbility mode
bool isPagePseudoClass = false; // Page pseudo-class
switch (selector->pseudo) {
case KatanaPseudoAfter:
case KatanaPseudoBefore:
case KatanaPseudoFirstLetter:
case KatanaPseudoFirstLine:
compat = true;
case KatanaPseudoBackdrop:
case KatanaPseudoCue:
case KatanaPseudoResizer:
case KatanaPseudoScrollbar:
case KatanaPseudoScrollbarCorner:
case KatanaPseudoScrollbarButton:
case KatanaPseudoScrollbarThumb:
case KatanaPseudoScrollbarTrack:
case KatanaPseudoScrollbarTrackPiece:
case KatanaPseudoSelection:
case KatanaPseudoWebKitCustomElement:
case KatanaPseudoContent:
case KatanaPseudoShadow:
element = true;
break;
case KatanaPseudoUnknown:
case KatanaPseudoEmpty:
case KatanaPseudoFirstChild:
case KatanaPseudoFirstOfType:
case KatanaPseudoLastChild:
case KatanaPseudoLastOfType:
case KatanaPseudoOnlyChild:
case KatanaPseudoOnlyOfType:
case KatanaPseudoNthChild:
case KatanaPseudoNthOfType:
case KatanaPseudoNthLastChild:
case KatanaPseudoNthLastOfType:
case KatanaPseudoLink:
case KatanaPseudoVisited:
case KatanaPseudoAny:
case KatanaPseudoAnyLink:
case KatanaPseudoAutofill:
case KatanaPseudoHover:
case KatanaPseudoDrag:
case KatanaPseudoFocus:
case KatanaPseudoActive:
case KatanaPseudoChecked:
case KatanaPseudoEnabled:
case KatanaPseudoFullPageMedia:
case KatanaPseudoDefault:
case KatanaPseudoDisabled:
case KatanaPseudoOptional:
case KatanaPseudoRequired:
case KatanaPseudoReadOnly:
case KatanaPseudoReadWrite:
case KatanaPseudoScope:
case KatanaPseudoValid:
case KatanaPseudoInvalid:
case KatanaPseudoIndeterminate:
case KatanaPseudoTarget:
case KatanaPseudoLang:
case KatanaPseudoNot:
case KatanaPseudoRoot:
case KatanaPseudoWindowInactive:
case KatanaPseudoCornerPresent:
case KatanaPseudoDecrement:
case KatanaPseudoIncrement:
case KatanaPseudoHorizontal:
case KatanaPseudoVertical:
case KatanaPseudoStart:
case KatanaPseudoEnd:
case KatanaPseudoDoubleButton:
case KatanaPseudoSingleButton:
case KatanaPseudoNoButton:
case KatanaPseudoNotParsed:
case KatanaPseudoFullScreen:
case KatanaPseudoFullScreenDocument:
case KatanaPseudoFullScreenAncestor:
case KatanaPseudoInRange:
case KatanaPseudoOutOfRange:
case KatanaPseudoFutureCue:
case KatanaPseudoPastCue:
case KatanaPseudoHost:
case KatanaPseudoHostContext:
case KatanaPseudoUnresolved:
case KatanaPseudoSpatialNavigationFocus:
case KatanaPseudoListBox:
break;
case KatanaPseudoFirstPage:
case KatanaPseudoLeftPage:
case KatanaPseudoRightPage:
isPagePseudoClass = true;
break;
}
bool matchPagePseudoClass = (selector->match == KatanaSelectorMatchPagePseudoClass);
if (matchPagePseudoClass != isPagePseudoClass)
selector->pseudo = KatanaPseudoUnknown;
else if (selector->match == KatanaSelectorMatchPseudoClass && element) {
if (!compat)
selector->pseudo = KatanaPseudoUnknown;
else
selector->match = KatanaSelectorMatchPseudoElement;
} else if (selector->match == KatanaSelectorMatchPseudoElement && !element)
selector->pseudo = KatanaPseudoUnknown;
}
bool katana_selector_matches_pseudo_element(KatanaSelector* selector)
{
if (selector->pseudo == KatanaPseudoUnknown)
katana_selector_extract_pseudo_type(selector);
return selector->match == KatanaSelectorMatchPseudoElement;
}
bool katana_selector_is_custom_pseudo_element(KatanaSelector* selector)
{
return selector->match == KatanaSelectorMatchPseudoElement && selector->pseudo == KatanaPseudoWebKitCustomElement;
}
bool katana_selector_is_direct_adjacent(KatanaSelector* selector)
{
return selector->relation == KatanaSelectorRelationDirectAdjacent || selector->relation == KatanaSelectorRelationIndirectAdjacent;
}
bool katana_selector_is_adjacent(KatanaSelector* selector)
{
return selector->relation == KatanaSelectorRelationDirectAdjacent;
}
bool katana_selector_is_shadow(KatanaSelector* selector)
{
return selector->relation == KatanaSelectorRelationShadowPseudo || selector->relation == KatanaSelectorRelationShadowDeep;
}
bool katana_selector_is_sibling(KatanaSelector* selector)
{
katana_selector_extract_pseudo_type(selector);
KatanaPseudoType type = selector->pseudo;
return selector->relation == KatanaSelectorRelationDirectAdjacent
|| selector->relation == KatanaSelectorRelationIndirectAdjacent
|| type == KatanaPseudoEmpty
|| type == KatanaPseudoFirstChild
|| type == KatanaPseudoFirstOfType
|| type == KatanaPseudoLastChild
|| type == KatanaPseudoLastOfType
|| type == KatanaPseudoOnlyChild
|| type == KatanaPseudoOnlyOfType
|| type == KatanaPseudoNthChild
|| type == KatanaPseudoNthOfType
|| type == KatanaPseudoNthLastChild
|| type == KatanaPseudoNthLastOfType;
}
bool katana_selector_is_attribute(const KatanaSelector* selector)
{
return selector->match >= KatanaSelectorMatchFirstAttribute;
}
bool katana_selector_is_content_pseudo_element(KatanaSelector* selector)
{
katana_selector_extract_pseudo_type(selector);
return selector->match == KatanaSelectorMatchPseudoElement && selector->pseudo == KatanaPseudoContent;
}
bool katana_selector_is_shadow_pseudo_element(KatanaSelector* selector)
{
return selector->match == KatanaSelectorMatchPseudoElement
&& selector->pseudo == KatanaPseudoShadow;
}
bool katana_selector_is_host_pseudo_class(KatanaSelector* selector)
{
return selector->match == KatanaSelectorMatchPseudoClass && (selector->pseudo == KatanaPseudoHost || selector->pseudo == KatanaPseudoHostContext);
}
bool katana_selector_is_tree_boundary_crossing(KatanaSelector* selector)
{
katana_selector_extract_pseudo_type(selector);
return selector->match == KatanaSelectorMatchPseudoClass && (selector->pseudo == KatanaPseudoHost || selector->pseudo == KatanaPseudoHostContext);
}
bool katana_selector_is_insertion_point_crossing(KatanaSelector* selector)
{
katana_selector_extract_pseudo_type(selector);
return (selector->match == KatanaSelectorMatchPseudoClass && selector->pseudo == KatanaPseudoHostContext)
|| (selector->match == KatanaSelectorMatchPseudoElement && selector->pseudo == KatanaPseudoContent);
}
KatanaParserString* katana_build_relation_selector_string(KatanaParser* parser, const char* relation, KatanaParserString* string, KatanaParserString* next, KatanaSelector* tagHistory)
{
if ( NULL != relation ) {
katana_string_prepend_characters(parser, relation, string);
}
if ( NULL != next ) {
katana_string_append_string(parser, next, string);
}
KatanaParserString * result = katana_selector_to_string(parser, tagHistory, (KatanaParserString*)string);
katana_parser_deallocate(parser, (void*) string->data);
katana_parser_deallocate(parser, (void*) string);
return result;
}
KatanaParserString* katana_selector_to_string(KatanaParser* parser, KatanaSelector* selector, KatanaParserString* next)
{
KatanaParserString* string = katana_parser_allocate(parser, sizeof(KatanaParserString));
katana_string_init(parser, string);
bool tag_is_implicit = true;
if (selector->match == KatanaSelectorMatchTag && tag_is_implicit)
{
if ( NULL == selector->tag->prefix )
katana_string_append_characters(parser, selector->tag->local, string);
else {
katana_string_append_characters(parser, selector->tag->prefix, string);
katana_string_append_characters(parser, "|", string);
katana_string_append_characters(parser, selector->tag->local, string);
}
}
const KatanaSelector* cs = selector;
while (true) {
if (cs->match == KatanaSelectorMatchId) {
katana_string_append_characters(parser, "#", string);
katana_string_append_characters(parser, cs->data->value, string);
} else if (cs->match == KatanaSelectorMatchClass) {
katana_string_append_characters(parser, ".", string);
katana_string_append_characters(parser, cs->data->value, string);
} else if (cs->match == KatanaSelectorMatchPseudoClass || cs->match == KatanaSelectorMatchPagePseudoClass) {
katana_string_append_characters(parser, ":", string);
katana_string_append_characters(parser, cs->data->value, string);
switch (cs->pseudo) {
case KatanaPseudoAny:
case KatanaPseudoNot:
case KatanaPseudoHost:
case KatanaPseudoHostContext: {
if ( cs->data->selectors ) {
KatanaArray* sels = cs->data->selectors;
for (size_t i=0; i<sels->length; i++) {
KatanaParserString* str = katana_selector_to_string(parser, sels->data[i], NULL);
katana_string_append_string(parser, str, string);
katana_parser_deallocate(parser, (void*) str->data);
katana_parser_deallocate(parser, (void*) str);
if ( i != sels->length -1 ) {
katana_string_append_characters(parser, ", ", string);
}
}
katana_string_append_characters(parser, ")", string);
}
}
break;
case KatanaPseudoLang:
case KatanaPseudoNthChild:
case KatanaPseudoNthLastChild:
case KatanaPseudoNthOfType:
case KatanaPseudoNthLastOfType: {
katana_string_append_characters(parser, cs->data->argument, string);
katana_string_append_characters(parser, ")", string);
}
break;
default:
break;
}
} else if (cs->match == KatanaSelectorMatchPseudoElement) {
katana_string_append_characters(parser, "::", string);
katana_string_append_characters(parser, cs->data->value, string);
} else if (katana_selector_is_attribute(cs)) {
katana_string_append_characters(parser, "[", string);
if (NULL != cs->data->attribute->prefix) {
katana_string_append_characters(parser, cs->data->attribute->prefix, string);
katana_string_append_characters(parser, "|", string);
}
katana_string_append_characters(parser, cs->data->attribute->local, string);
switch (cs->match) {
case KatanaSelectorMatchAttributeExact:
katana_string_append_characters(parser, "=", string);
break;
case KatanaSelectorMatchAttributeSet:
katana_string_append_characters(parser, "]", string);
break;
case KatanaSelectorMatchAttributeList:
katana_string_append_characters(parser, "~=", string);
break;
case KatanaSelectorMatchAttributeHyphen:
katana_string_append_characters(parser, "|=", string);
break;
case KatanaSelectorMatchAttributeBegin:
katana_string_append_characters(parser, "^=", string);
break;
case KatanaSelectorMatchAttributeEnd:
katana_string_append_characters(parser, "$=", string);
break;
case KatanaSelectorMatchAttributeContain:
katana_string_append_characters(parser, "*=", string);
break;
default:
break;
}
if (cs->match != KatanaSelectorMatchAttributeSet) {
katana_string_append_characters(parser, "\"", string);
katana_string_append_characters(parser, cs->data->value, string);
katana_string_append_characters(parser, "\"", string);
if (cs->data->bits.attributeMatchType == KatanaAttributeMatchTypeCaseInsensitive)
katana_string_append_characters(parser, " i", string);
katana_string_append_characters(parser, "]", string);
}
}
if (cs->relation != KatanaSelectorRelationSubSelector || !cs->tagHistory)
break;
cs = cs->tagHistory;
}
KatanaSelector* tagHistory = cs->tagHistory;
if ( NULL != tagHistory ) {
switch (cs->relation) {
case KatanaSelectorRelationDescendant:
{
return katana_build_relation_selector_string(parser, " ", string, next, tagHistory);
}
case KatanaSelectorRelationChild:
{
return katana_build_relation_selector_string(parser, " > ", string, next, tagHistory);
}
case KatanaSelectorRelationShadowDeep:
{
return katana_build_relation_selector_string(parser, " /deep/ ", string, next, tagHistory);
}
case KatanaSelectorRelationDirectAdjacent:
{
return katana_build_relation_selector_string(parser, " + ", string, next, tagHistory);
}
case KatanaSelectorRelationIndirectAdjacent:
{
return katana_build_relation_selector_string(parser, " ~ ", string, next, tagHistory);
}
case KatanaSelectorRelationSubSelector:
{
return NULL;
}
case KatanaSelectorRelationShadowPseudo:
{
return katana_build_relation_selector_string(parser, NULL, string, next, tagHistory);
}
}
}
if ( NULL != next ) {
katana_string_append_string(parser, (KatanaParserString*)next, string);
}
return (KatanaParserString*)string;
}
unsigned calc_specificity_for_one_selector(const KatanaSelector* selector)
{
switch ( selector->match ) {
case KatanaSelectorMatchId:
return 0x10000;
case KatanaSelectorMatchPseudoClass:
case KatanaSelectorMatchAttributeExact:
case KatanaSelectorMatchClass:
case KatanaSelectorMatchAttributeSet:
case KatanaSelectorMatchAttributeList:
case KatanaSelectorMatchAttributeHyphen:
case KatanaSelectorMatchPseudoElement:
case KatanaSelectorMatchAttributeContain:
case KatanaSelectorMatchAttributeBegin:
case KatanaSelectorMatchAttributeEnd:
return 0x100;
case KatanaSelectorMatchTag:
return !stricmp(selector->tag->local, "*") ? 0 : 1;
case KatanaSelectorMatchUnknown:
case KatanaSelectorMatchPagePseudoClass:
return 0;
}
return 0;
}
unsigned katana_calc_specificity_for_selector(KatanaSelector* selector)
{
if ( NULL == selector ) {
return 0;
}
static const unsigned idMask = 0xff0000;
static const unsigned classMask = 0xff00;
static const unsigned elementMask = 0xff;
unsigned total = 0;
unsigned temp = 0;
for (const KatanaSelector * next = selector; next; next = next->tagHistory)
{
temp = total + calc_specificity_for_one_selector(next);
if ((temp & idMask) < (total & idMask))
total |= idMask;
else if ((temp & classMask) < (total & classMask))
total |= classMask;
else if ((temp & elementMask) < (total & elementMask))
total |= elementMask;
else
total = temp;
}
return total;
}
// Could be made smaller and faster by replacing pointer with an
// offset into a string buffer and making the bit fields smaller but
// that could not be maintained by hand.
typedef struct {
const char* string;
unsigned type:8;
} KatanaNameToPseudoStruct;
// These tables should be kept sorted.
const static KatanaNameToPseudoStruct kPseudoTypeWithoutArgumentsMap[] = {
{"-internal-list-box", KatanaPseudoListBox},
{"-internal-media-controls-cast-button", KatanaPseudoWebKitCustomElement},
{"-internal-media-controls-overlay-cast-button", KatanaPseudoWebKitCustomElement},
{"-internal-spatial-navigation-focus", KatanaPseudoSpatialNavigationFocus},
{"-webkit-any-link", KatanaPseudoAnyLink},
{"-webkit-autofill", KatanaPseudoAutofill},
{"-webkit-drag", KatanaPseudoDrag},
{"-webkit-full-page-media", KatanaPseudoFullPageMedia},
{"-webkit-full-screen", KatanaPseudoFullScreen},
{"-webkit-full-screen-ancestor", KatanaPseudoFullScreenAncestor},
{"-webkit-full-screen-document", KatanaPseudoFullScreenDocument},
{"-webkit-resizer", KatanaPseudoResizer},
{"-webkit-scrollbar", KatanaPseudoScrollbar},
{"-webkit-scrollbar-button", KatanaPseudoScrollbarButton},
{"-webkit-scrollbar-corner", KatanaPseudoScrollbarCorner},
{"-webkit-scrollbar-thumb", KatanaPseudoScrollbarThumb},
{"-webkit-scrollbar-track", KatanaPseudoScrollbarTrack},
{"-webkit-scrollbar-track-piece", KatanaPseudoScrollbarTrackPiece},
{"active", KatanaPseudoActive},
{"after", KatanaPseudoAfter},
{"backdrop", KatanaPseudoBackdrop},
{"before", KatanaPseudoBefore},
{"checked", KatanaPseudoChecked},
{"content", KatanaPseudoContent},
{"corner-present", KatanaPseudoCornerPresent},
{"cue", KatanaPseudoWebKitCustomElement},
{"decrement", KatanaPseudoDecrement},
{"default", KatanaPseudoDefault},
{"disabled", KatanaPseudoDisabled},
{"double-button", KatanaPseudoDoubleButton},
{"empty", KatanaPseudoEmpty},
{"enabled", KatanaPseudoEnabled},
{"end", KatanaPseudoEnd},
{"first", KatanaPseudoFirstPage},
{"first-child", KatanaPseudoFirstChild},
{"first-letter", KatanaPseudoFirstLetter},
{"first-line", KatanaPseudoFirstLine},
{"first-of-type", KatanaPseudoFirstOfType},
{"focus", KatanaPseudoFocus},
{"future", KatanaPseudoFutureCue},
{"horizontal", KatanaPseudoHorizontal},
{"host", KatanaPseudoHost},
{"hover", KatanaPseudoHover},
{"in-range", KatanaPseudoInRange},
{"increment", KatanaPseudoIncrement},
{"indeterminate", KatanaPseudoIndeterminate},
{"invalid", KatanaPseudoInvalid},
{"last-child", KatanaPseudoLastChild},
{"last-of-type", KatanaPseudoLastOfType},
{"left", KatanaPseudoLeftPage},
{"link", KatanaPseudoLink},
{"no-button", KatanaPseudoNoButton},
{"only-child", KatanaPseudoOnlyChild},
{"only-of-type", KatanaPseudoOnlyOfType},
{"optional", KatanaPseudoOptional},
{"out-of-range", KatanaPseudoOutOfRange},
{"past", KatanaPseudoPastCue},
{"read-only", KatanaPseudoReadOnly},
{"read-write", KatanaPseudoReadWrite},
{"required", KatanaPseudoRequired},
{"right", KatanaPseudoRightPage},
{"root", KatanaPseudoRoot},
{"scope", KatanaPseudoScope},
{"selection", KatanaPseudoSelection},
{"shadow", KatanaPseudoShadow},
{"single-button", KatanaPseudoSingleButton},
{"start", KatanaPseudoStart},
{"target", KatanaPseudoTarget},
{"unresolved", KatanaPseudoUnresolved},
{"valid", KatanaPseudoValid},
{"vertical", KatanaPseudoVertical},
{"visited", KatanaPseudoVisited},
{"window-inactive", KatanaPseudoWindowInactive},
};
const static KatanaNameToPseudoStruct kPseudoTypeWithArgumentsMap[] = {
{"-webkit-any(", KatanaPseudoAny},
{"cue(", KatanaPseudoCue},
{"host(", KatanaPseudoHost},
{"host-context(", KatanaPseudoHostContext},
{"lang(", KatanaPseudoLang},
{"not(", KatanaPseudoNot},
{"nth-child(", KatanaPseudoNthChild},
{"nth-last-child(", KatanaPseudoNthLastChild},
{"nth-last-of-type(", KatanaPseudoNthLastOfType},
{"nth-of-type(", KatanaPseudoNthOfType},
};
static const KatanaNameToPseudoStruct* lower_bound(const KatanaNameToPseudoStruct *map,
size_t count, const char* key);
static KatanaPseudoType name_to_pseudo_type(const char* name, bool hasArguments)
{
if (NULL == name)
return KatanaPseudoUnknown;
const KatanaNameToPseudoStruct* pseudoTypeMap;
size_t count;
if (hasArguments) {
pseudoTypeMap = kPseudoTypeWithArgumentsMap;
count = sizeof(kPseudoTypeWithArgumentsMap) / sizeof(KatanaNameToPseudoStruct);
} else {
pseudoTypeMap = kPseudoTypeWithoutArgumentsMap;
count = sizeof(kPseudoTypeWithoutArgumentsMap) / sizeof(KatanaNameToPseudoStruct);
}
const KatanaNameToPseudoStruct* match = lower_bound(pseudoTypeMap, count, name);
if ( match == (pseudoTypeMap + count)
|| 0 != stricmp(match->string, name) )
return KatanaPseudoUnknown;
return match->type;
}
static const KatanaNameToPseudoStruct* lower_bound(const KatanaNameToPseudoStruct *array,
size_t size, const char* key) {
const KatanaNameToPseudoStruct* it;
const KatanaNameToPseudoStruct* first = array;
size_t count = size, step;
while (count > 0) {
it = first;
step = count / 2;
it += step;
if (strncmp(it->string, key, strlen(key)) < 0) {
first = ++it;
count -= step + 1;
} else count = step;
}
return first;
}
#if KATANA_PARSER_DEBUG
void test_lower_bound()
{
const KatanaNameToPseudoStruct* pseudoTypeMap;
size_t count;
pseudoTypeMap = kPseudoTypeWithArgumentsMap;
count = sizeof(kPseudoTypeWithArgumentsMap) / sizeof(KatanaNameToPseudoStruct);
for ( size_t i = 0; i < count; i++ ) {
const KatanaNameToPseudoStruct* res = lower_bound(pseudoTypeMap, count, pseudoTypeMap[i].string);
assert(pseudoTypeMap[i].type == res->type);
}
pseudoTypeMap = kPseudoTypeWithoutArgumentsMap;
count = sizeof(kPseudoTypeWithoutArgumentsMap) / sizeof(KatanaNameToPseudoStruct);
for ( size_t i = 0; i < count; i++ ) {
const KatanaNameToPseudoStruct* res = lower_bound(pseudoTypeMap, count, pseudoTypeMap[i].string);
assert(pseudoTypeMap[i].type == res->type);
}
}
#endif // #if KATANA_PARSER_DEBUG