Files
ed 3e064f6443 progress on sanity_tests.c, adding back md_str8_styllize
Eventually a heavy reduction of base & OS should be provided via libgen or done here to reduce the overall surface area of the library for the user.
Base and os were dragged from raddbg and the original metadesk didn't need as many definitions.
2025-02-13 09:53:46 -05:00

621 lines
25 KiB
C

#ifdef INTELLISENSE_DIRECTIVES
# pragma once
# include "os/os.h"
#endif
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
// String Identifier Style
typedef enum MD_IdentifierStyle MD_IdentifierStyle;
enum MD_IdentifierStyle
{
MD_IdentifierStyle_UpperCamelCase,
MD_IdentifierStyle_LowerCamelCase,
MD_IdentifierStyle_UpperCase,
MD_IdentifierStyle_LowerCase,
};
MD_API MD_String8 md_str8_stylize(MD_Arena* arena, MD_String8 string, MD_IdentifierStyle style, MD_String8 separator);
////////////////////////////////
//~ rjf: Messages
typedef enum MD_MsgKind
{
MD_MsgKind_Null,
MD_MsgKind_Note,
MD_MsgKind_Warning,
MD_MsgKind_Error,
MD_MsgKind_FatalError,
}
MD_MsgKind;
typedef struct MD_Node MD_Node;
typedef struct MD_Msg MD_Msg;
struct MD_Msg
{
MD_Msg* next;
MD_Node* node;
MD_MsgKind kind;
MD_String8 string;
};
typedef struct MD_MsgList MD_MsgList;
struct MD_MsgList
{
MD_Msg* first;
MD_Msg* last;
MD_U64 count;
MD_MsgKind worst_message_kind;
};
////////////////////////////////
//~ rjf: MD_Token Types
typedef MD_U32 MD_TokenFlags;
enum
{
// TODO(Ed): Track type of comment, and opening/closing main delimiter.
// (The parser needs that info later and just forces md_str_matches that werent necessary)
// rjf: base kind info
MD_TokenFlag_Identifier = (1 << 0),
MD_TokenFlag_Numeric = (1 << 1),
MD_TokenFlag_StringLiteral = (1 << 2),
MD_TokenFlag_Symbol = (1 << 3),
MD_TokenFlag_Reserved = (1 << 4),
MD_TokenFlag_Comment = (1 << 5),
MD_TokenFlag_Whitespace = (1 << 6),
MD_TokenFlag_Newline = (1 << 7),
// rjf: decoration info
MD_TokenFlag_StringSingleQuote = (1 << 8),
MD_TokenFlag_StringDoubleQuote = (1 << 9),
MD_TokenFlag_StringTick = (1 << 10),
MD_TokenFlag_StringTriplet = (1 << 11),
// rjf: error info
MD_TokenFlag_BrokenComment = (1 << 12),
MD_TokenFlag_BrokenStringLiteral = (1 << 13),
MD_TokenFlag_BadCharacter = (1 << 14),
};
typedef MD_U32 MD_TokenFlagGroups;
enum
{
MD_TokenFlagGroup_Comment = MD_TokenFlag_Comment,
MD_TokenFlagGroup_Whitespace = (MD_TokenFlag_Whitespace| MD_TokenFlag_Newline),
MD_TokenFlagGroup_Irregular = (MD_TokenFlagGroup_Comment | MD_TokenFlagGroup_Whitespace),
MD_TokenFlagGroup_Regular = ~MD_TokenFlagGroup_Irregular,
MD_TokenFlagGroup_Label = (MD_TokenFlag_Identifier | MD_TokenFlag_Numeric | MD_TokenFlag_StringLiteral | MD_TokenFlag_Symbol),
MD_TokenFlagGroup_Error = (MD_TokenFlag_BrokenComment | MD_TokenFlag_BrokenStringLiteral | MD_TokenFlag_BadCharacter),
};
typedef struct MD_Token MD_Token;
struct MD_Token
{
MD_Rng1U64 range;
MD_TokenFlags flags;
};
typedef struct MD_TokenChunkNode MD_TokenChunkNode;
struct MD_TokenChunkNode
{
MD_TokenChunkNode* next;
MD_Token* v;
MD_U64 count;
MD_U64 cap;
};
typedef struct MD_TokenChunkList MD_TokenChunkList;
struct MD_TokenChunkList
{
MD_TokenChunkNode* first;
MD_TokenChunkNode* last;
MD_U64 chunk_count;
MD_U64 total_token_count;
};
typedef struct MD_TokenArray MD_TokenArray;
struct MD_TokenArray
{
MD_Token* v;
MD_U64 count;
};
////////////////////////////////
//~ rjf: MD_Node Types
typedef enum MD_NodeKind MD_NodeKind;
enum MD_NodeKind
{
MD_NodeKind_Nil,
MD_NodeKind_File,
MD_NodeKind_ErrorMarker,
MD_NodeKind_Main,
MD_NodeKind_Tag,
MD_NodeKind_List,
MD_NodeKind_Reference,
MD_NodeKind_COUNT
};
typedef MD_U32 MD_NodeFlags;
enum
{
MD_NodeFlag_MaskSetDelimiters = (0x3F << 0),
MD_NodeFlag_HasParenLeft = (1 << 0),
MD_NodeFlag_HasParenRight = (1 << 1),
MD_NodeFlag_HasBracketLeft = (1 << 2),
MD_NodeFlag_HasBracketRight = (1 << 3),
MD_NodeFlag_HasBraceLeft = (1 << 4),
MD_NodeFlag_HasBraceRight = (1 << 5),
MD_NodeFlag_MaskSeparators = (0xF << 6),
MD_NodeFlag_IsBeforeSemicolon = (1 << 6),
MD_NodeFlag_IsAfterSemicolon = (1 << 7),
MD_NodeFlag_IsBeforeComma = (1 << 8),
MD_NodeFlag_IsAfterComma = (1 << 9),
MD_NodeFlag_MaskStringDelimiters = (0xF << 10),
MD_NodeFlag_StringSingleQuote = (1 << 10),
MD_NodeFlag_StringDoubleQuote = (1 << 11),
MD_NodeFlag_StringTick = (1 << 12),
MD_NodeFlag_StringTriplet = (1 << 13),
MD_NodeFlag_MaskLabelKind = (0xF << 14),
MD_NodeFlag_Numeric = (1 << 14),
MD_NodeFlag_Identifier = (1 << 15),
MD_NodeFlag_StringLiteral = (1 << 16),
MD_NodeFlag_Symbol = (1 << 17),
};
#define MD_NodeFlag_AfterFromBefore(f) ((f) << 1)
typedef struct MD_Node MD_Node;
struct MD_Node
{
// rjf: tree links
MD_Node* next;
MD_Node* prev;
MD_Node* parent;
MD_Node* first;
MD_Node* last;
// rjf: tag links
MD_Node* first_tag;
MD_Node* last_tag;
// rjf: node info
MD_NodeKind kind;
MD_NodeFlags flags;
MD_String8 string;
MD_String8 raw_string;
// rjf: source code info
MD_U64 src_offset;
// rjf: user-controlled generation number
//
// (unused by mdesk layer, but can be used by usage code to use MD_Node trees
// in a "retained mode" way, where stable generational handles can be formed
// to nodes)
MD_U64 user_gen;
// rjf: extra padding to 128 bytes
MD_U64 _unused_[2];
};
typedef struct MD_NodeRec MD_NodeRec;
struct MD_NodeRec
{
MD_Node* next;
MD_S32 md_push_count;
MD_S32 pop_count;
};
////////////////////////////////
//~ rjf: Text -> Tokens Types
typedef struct MD_TokenizeResult MD_TokenizeResult;
struct MD_TokenizeResult
{
MD_TokenArray tokens;
MD_MsgList msgs;
};
////////////////////////////////
//~ rjf: Tokens -> Tree Types
typedef struct MD_ParseResult MD_ParseResult;
struct MD_ParseResult
{
MD_Node* root;
MD_MsgList msgs;
};
////////////////////////////////
// MD_Context
typedef struct MD_Context MD_Context;
struct MD_Context
{
// Currently, this is only relevant if the user is utilizing this library via bindings
// or they are not utilizing metadesk's hosted `md_entry_point` runtime
// Its recommended that the user hooks up
// their own arena allocators to the thread context
// which will be utilized for thread local scratch arena
MD_TCTX thread_ctx;
// Just as with MD_TCTX its recommended the user setup the arena allocators
// for te os context
MD_OS_Context os_ctx;
// This skips the md_os_init process but that means the user is expected to setup
// the thread context's arenas for the process
MD_B32 dont_init_os;
};
////////////////////////////////
//~ rjf: Globals
inline MD_Node*
md_nil_node()
{
md_read_only md_local_persist
MD_Node nil =
{
&nil,
&nil,
&nil,
&nil,
&nil,
&nil,
&nil,
};
return &nil;
}
////////////////////////////////
// MD_Context lifetime Functios
// NOTE(Ed): The are see comments in MD_Context struct, this is only necessary when not utilizing
// the metadesk os runtime provided md_entry_point interface
MD_API void md_init(MD_Context* ctx);
MD_API void md_deinit(MD_Context* ctx); // Does nothing for now.
////////////////////////////////
//~ rjf: Message Type Functions
void md_msg_list_push__arena (MD_Arena* arena, MD_MsgList* msgs, MD_Node* node, MD_MsgKind kind, MD_String8 string);
MD_API void md_msg_list_push__ainfo (MD_AllocatorInfo ainfo, MD_MsgList* msgs, MD_Node* node, MD_MsgKind kind, MD_String8 string);
void md_msg_list_pushf__arena(MD_Arena* arena, MD_MsgList* msgs, MD_Node* node, MD_MsgKind kind, char *fmt, ...);
void md_msg_list_pushf__ainfo(MD_AllocatorInfo ainfo, MD_MsgList* msgs, MD_Node* node, MD_MsgKind kind, char *fmt, ...);
#define md_msg_list_push(allocator, msgs, node, kind, string) _Generic(allocator, MD_Arena*: md_msg_list_push__arena, MD_AllocatorInfo: md_msg_list_push__ainfo, default: md_assert_generic_sel_fail) md_generic_call(allocator, msgs, node, kind, string)
#define md_msg_list_pushf(allocator, msgs, node, kind, string, fmt, ...) _Generic(allocator, MD_Arena*: md_msg_list_pushf__arena, MD_AllocatorInfo: md_msg_list_pushf__ainfo, default: md_assert_generic_sel_fail) md_generic_call(allocator, msgs, node, kind, fmt, __VA_ARGS__)
MD_API void md_msg_list_concat_in_place(MD_MsgList* dst, MD_MsgList* to_push);
md_force_inline void md_msg_list_push__arena(MD_Arena* arena, MD_MsgList* msgs, MD_Node* node, MD_MsgKind kind, MD_String8 string) { md_msg_list_push__ainfo(md_arena_allocator(arena), msgs, node, kind, string); }
inline void
md_msg_list_pushf__arena(MD_Arena* arena, MD_MsgList* msgs, MD_Node* node, MD_MsgKind kind, char* fmt, ...) {
va_list args;
va_start(args, fmt);
MD_String8 string = md_str8fv(arena, fmt, args);
md_msg_list_push(arena, msgs, node, kind, string);
va_end(args);
}
inline void
md_msg_list_pushf__ainfo(MD_AllocatorInfo ainfo, MD_MsgList* msgs, MD_Node* node, MD_MsgKind kind, char* fmt, ...) {
va_list args;
va_start(args, fmt);
MD_String8 string = md_str8fv(ainfo, fmt, args);
md_msg_list_push(ainfo, msgs, node, kind, string);
va_end(args);
}
////////////////////////////////
//~ rjf: MD_Token Type Functions
MD_Token md_token_make (MD_Rng1U64 range, MD_TokenFlags flags);
MD_B32 md_token_match(MD_Token a, MD_Token b);
MD_API MD_String8 content_string_from_token_flags_str8(MD_TokenFlags flags, MD_String8 string);
MD_String8List md_string_list_from_token_flags__arena(MD_Arena* arena, MD_TokenFlags flags);
MD_API MD_String8List md_string_list_from_token_flags__ainfo(MD_AllocatorInfo ainfo, MD_TokenFlags flags);
MD_API void md_token_chunk_list_push__arena (MD_Arena* arena, MD_TokenChunkList* list, MD_U64 cap, MD_Token token);
MD_API void md_token_chunk_list_push__ainfo (MD_AllocatorInfo ainfo, MD_TokenChunkList* list, MD_U64 cap, MD_Token token);
MD_API MD_TokenArray md_token_array_from_chunk_list__arena (MD_Arena* arena, MD_TokenChunkList* chunks);
MD_API MD_TokenArray md_token_array_from_chunk_list__ainfo (MD_AllocatorInfo ainfo, MD_TokenChunkList* chunks);
#define md_string_list_from_token_flags(allocator, flags) _Generic(allocator, MD_Arena*: md_string_list_from_token_flags__arena, MD_AllocatorInfo: md_string_list_from_token_flags__ainfo, default: md_assert_generic_sel_fail) md_generic_call(allocator, flags)
#define md_token_chunk_list_push(allocator, list, cap, token) _Generic(allocator, MD_Arena*: md_token_chunk_list_push__arena, MD_AllocatorInfo: md_token_chunk_list_push__ainfo, default: md_assert_generic_sel_fail) md_generic_call(allocator, list, cap, token)
#define md_token_array_from_chunk_list(allocator, chunks) _Generic(allocator, MD_Arena*: md_token_array_from_chunk_list__arena, MD_AllocatorInfo: md_token_array_from_chunk_list__ainfo, default: md_assert_generic_sel_fail) md_generic_call(allocator, chunks)
md_force_inline MD_String8List md_string_list_from_token_flags__arena(MD_Arena* arena, MD_TokenFlags flags) { return md_string_list_from_token_flags__ainfo(md_arena_allocator(arena), flags); }
md_force_inline void md_token_chunk_list_push__arena (MD_Arena* arena, MD_TokenChunkList* list, MD_U64 cap, MD_Token token) { md_token_chunk_list_push__ainfo(md_arena_allocator(arena), list, cap, token); }
md_force_inline MD_TokenArray md_token_array_from_chunk_list__arena (MD_Arena* arena, MD_TokenChunkList* chunks) { return md_token_array_from_chunk_list__ainfo(md_arena_allocator(arena), chunks); }
inline MD_Token
md_token_make(MD_Rng1U64 range, MD_TokenFlags flags) {
MD_Token token = { range, flags };
return token;
}
inline MD_B32
md_token_match(MD_Token a, MD_Token b) {
return (a.range.md_min == b.range.md_min &&
a.range.md_max == b.range.md_max &&
a.flags == b.flags );
}
////////////////////////////////
//~ rjf: MD_Node Type Functions
//- rjf: flag conversions
inline MD_NodeFlags
md_node_flags_from_token_flags(MD_TokenFlags flags)
{
MD_NodeFlags result = 0;
result |= MD_NodeFlag_Identifier *!! (flags & MD_TokenFlag_Identifier );
result |= MD_NodeFlag_Numeric *!! (flags & MD_TokenFlag_Numeric );
result |= MD_NodeFlag_StringLiteral *!! (flags & MD_TokenFlag_StringLiteral );
result |= MD_NodeFlag_Symbol *!! (flags & MD_TokenFlag_Symbol );
result |= MD_NodeFlag_StringSingleQuote *!! (flags & MD_TokenFlag_StringSingleQuote);
result |= MD_NodeFlag_StringDoubleQuote *!! (flags & MD_TokenFlag_StringDoubleQuote);
result |= MD_NodeFlag_StringTick *!! (flags & MD_TokenFlag_StringTick );
result |= MD_NodeFlag_StringTriplet *!! (flags & MD_TokenFlag_StringTriplet );
return result;
}
//- rjf: nil
MD_B32 md_node_is_nil(MD_Node* node) { return (node == 0 || node == md_nil_node() || node->kind == MD_NodeKind_Nil); }
//- rjf: iteration
#define md_each_node(it, first) (MD_Node* it = first; !md_node_is_nil(it); it = it->next)
MD_API MD_NodeRec md_node_rec_depth_first(MD_Node* node, MD_Node* subtree_root, MD_U64 child_off, MD_U64 sib_off);
#define md_node_rec_depth_first_pre(node, subtree_root) md_node_rec_depth_first((node), (subtree_root), md_offset_of(MD_Node, first), md_offset_of(MD_Node, next))
#define md_node_rec_depth_first_pre_rev(node, subtree_root) md_node_rec_depth_first((node), (subtree_root), md_offset_of(MD_Node, last), md_offset_of(MD_Node, prev))
//- rjf: tree building
MD_Node* md_push_node__arena(MD_Arena* arena, MD_NodeKind kind, MD_NodeFlags flags, MD_String8 string, MD_String8 raw_string, MD_U64 src_offset);
MD_Node* md_push_node__ainfo(MD_AllocatorInfo ainfo, MD_NodeKind kind, MD_NodeFlags flags, MD_String8 string, MD_String8 raw_string, MD_U64 src_offset);
#define md_push_node(allocator, kind, flags, string, raw_string, src_offset) _Generic(allocator, MD_Arena*: md_push_node__arena, MD_AllocatorInfo: md_push_node__ainfo, default: md_assert_generic_sel_fail) md_generic_call(allocator, kind, flags, string, raw_string, src_offset)
void md_node_insert_tag (MD_Node* parent, MD_Node* prev_child, MD_Node* node);
void md_node_insert_child(MD_Node* parent, MD_Node* prev_child, MD_Node* node);
void md_node_push_child (MD_Node* parent, MD_Node* node);
void md_node_push_tag (MD_Node* parent, MD_Node* node);
void md_unhook (MD_Node* node);
inline MD_Node* md_push_node__arena(MD_Arena* arena, MD_NodeKind kind, MD_NodeFlags flags, MD_String8 string, MD_String8 raw_string, MD_U64 src_offset) { md_push_node__ainfo(md_arena_allocator(arena), kind, flags, string, raw_string, src_offset); }
inline MD_Node*
md_push_node__ainfo(MD_AllocatorInfo ainfo, MD_NodeKind kind, MD_NodeFlags flags, MD_String8 string, MD_String8 raw_string, MD_U64 src_offset) {
MD_Node* node = md_alloc_array(ainfo, MD_Node, 1);
node->first = node->last = node->parent = node->next = node->prev = node->first_tag = node->last_tag = md_nil_node();
node->kind = kind;
node->flags = flags;
node->string = string;
node->raw_string = raw_string;
node->src_offset = src_offset;
return node;
}
inline void
md_node_insert_child(MD_Node* parent, MD_Node* prev_child, MD_Node* node) {
node->parent = parent;
md_dll_insert_npz(md_nil_node(), parent->first, parent->last, prev_child, node, next, prev);
}
inline void
md_node_insert_tag(MD_Node* parent, MD_Node* prev_child, MD_Node* node) {
node->kind = MD_NodeKind_Tag;
node->parent = parent;
md_dll_insert_npz(md_nil_node(), parent->first_tag, parent->last_tag, prev_child, node, next, prev);
}
inline void
md_node_push_child(MD_Node* parent, MD_Node* node) {
node->parent = parent;
md_dll_push_back_npz(md_nil_node(), parent->first, parent->last, node, next, prev);
}
inline void
md_node_push_tag(MD_Node* parent, MD_Node* node) {
node->kind = MD_NodeKind_Tag;
node->parent = parent;
md_dll_push_back_npz(md_nil_node(), parent->first_tag, parent->last_tag, node, next, prev);
}
//- rjf: tree introspection
MD_Node* md_node_from_chain_string(MD_Node* first, MD_Node* opl, MD_String8 string, MD_StringMatchFlags flags);
MD_Node* md_node_from_chain_index (MD_Node* first, MD_Node* opl, MD_U64 index);
MD_Node* md_node_from_chain_flags (MD_Node* first, MD_Node* opl, MD_NodeFlags flags);
MD_U64 md_index_from_node (MD_Node* node);
MD_Node* md_root_from_node (MD_Node* node);
MD_Node* md_child_from_string (MD_Node* node, MD_String8 child_string, MD_StringMatchFlags flags);
MD_Node* md_tag_from_string (MD_Node* node, MD_String8 tag_string, MD_StringMatchFlags flags);
MD_Node* md_child_from_index (MD_Node* node, MD_U64 index);
MD_Node* md_tag_from_index (MD_Node* node, MD_U64 index);
MD_Node* md_tag_arg_from_index (MD_Node* node, MD_String8 tag_string, MD_StringMatchFlags flags, MD_U64 index);
MD_Node* md_tag_arg_from_string (MD_Node* node, MD_String8 tag_string, MD_StringMatchFlags tag_str_flags, MD_String8 arg_string, MD_StringMatchFlags arg_str_flags);
MD_B32 md_node_has_child (MD_Node* node, MD_String8 string, MD_StringMatchFlags flags);
MD_B32 md_node_has_tag (MD_Node* node, MD_String8 string, MD_StringMatchFlags flags);
MD_U64 md_child_count_from_node (MD_Node* node);
MD_U64 md_tag_count_from_node (MD_Node* node);
MD_API MD_String8 md_string_from_children__arena(MD_Arena* arena, MD_Node* root);
MD_String8 md_string_from_children__ainfo(MD_AllocatorInfo ainfo, MD_Node* root);
#define md_string_from_children(allocator, root) _Generic(allocator, MD_Arena*: md_string_from_children__arena, MD_AllocatorInfo: md_string_from_children__ainfo, default: md_assert_generic_sel_fail) md_generic_call(allocator, root)
md_force_inline MD_String8 md_string_from_children__arena(MD_Arena* arena, MD_Node* root) { return md_string_from_children__ainfo(md_arena_allocator(arena), root); }
inline MD_Node*
md_node_from_chain_string(MD_Node* first, MD_Node* opl, MD_String8 string, MD_StringMatchFlags flags)
{
MD_Node* result = md_nil_node();
for (MD_Node* n = first; !md_node_is_nil(n) && n != opl; n = n->next)
{
if (md_str8_match(n->string, string, flags)) {
result = n;
break;
}
}
return result;
}
inline MD_Node*
md_node_from_chain_index(MD_Node* first, MD_Node* opl, MD_U64 index) {
MD_Node* result = md_nil_node();
MD_S64 idx = 0;
for (MD_Node* n = first; !md_node_is_nil(n) && n != opl; n = n->next, idx += 1)
{
if (index == idx) {
result = n;
break;
}
}
return result;
}
inline MD_Node*
md_node_from_chain_flags(MD_Node* first, MD_Node* opl, MD_NodeFlags flags) {
MD_Node* result = md_nil_node();
for (MD_Node* n = first; !md_node_is_nil(n) && n != opl; n = n->next)
{
if (n->flags & flags) {
result = n;
break;
}
}
return result;
}
inline MD_U64
md_index_from_node(MD_Node* node) {
MD_U64 index = 0;
for (MD_Node* n = node->prev; !md_node_is_nil(n); n = n->prev) {
index += 1;
}
return index;
}
inline MD_Node*
md_root_from_node(MD_Node* node) {
MD_Node* result = node;
for (MD_Node* p = node->parent; (p->kind == MD_NodeKind_Main || p->kind == MD_NodeKind_Tag) && !md_node_is_nil(p); p = p->parent) {
result = p;
}
return result;
}
inline MD_Node* md_child_from_string(MD_Node* node, MD_String8 child_string, MD_StringMatchFlags flags) { return md_node_from_chain_string(node->first, md_nil_node(), child_string, flags); }
inline MD_Node* md_tag_from_string (MD_Node* node, MD_String8 tag_string, MD_StringMatchFlags flags) { return md_node_from_chain_string(node->first_tag, md_nil_node(), tag_string, flags); }
inline MD_Node* md_child_from_index (MD_Node* node, MD_U64 index) { return md_node_from_chain_index (node->first, md_nil_node(), index); }
inline MD_Node* md_tag_from_index (MD_Node* node, MD_U64 index) { return md_node_from_chain_index (node->first_tag, md_nil_node(), index); }
inline MD_Node*
md_tag_arg_from_index(MD_Node* node, MD_String8 tag_string, MD_StringMatchFlags flags, MD_U64 index) {
MD_Node* tag = md_tag_from_string(node, tag_string, flags);
return md_child_from_index(tag, index);
}
inline MD_Node*
md_tag_arg_from_string(MD_Node* node, MD_String8 tag_string, MD_StringMatchFlags tag_str_flags, MD_String8 arg_string, MD_StringMatchFlags arg_str_flags) {
MD_Node* tag = md_tag_from_string (node, tag_string, tag_str_flags);
MD_Node* arg = md_child_from_string(tag, arg_string, arg_str_flags);
return arg;
}
inline MD_B32 md_node_has_child(MD_Node* node, MD_String8 string, MD_StringMatchFlags flags) { return !md_node_is_nil(md_child_from_string(node, string, flags)); }
inline MD_B32 md_node_has_tag (MD_Node* node, MD_String8 string, MD_StringMatchFlags flags) { return !md_node_is_nil(md_tag_from_string (node, string, flags)); }
inline MD_U64
md_child_count_from_node(MD_Node *node) {
MD_U64 result = 0;
for (MD_Node* child = node->first; !md_node_is_nil(child); child = child->next) {
result += 1;
}
return result;
}
inline MD_U64
md_tag_count_from_node(MD_Node* node) {
MD_U64 result = 0;
for (MD_Node* child = node->first_tag; !md_node_is_nil(child); child = child->next) {
result += 1;
}
return result;
}
//- rjf: tree comparison
MD_API MD_B32 tree_match(MD_Node* a, MD_Node* b, MD_StringMatchFlags flags);
MD_API MD_B32 md_node_match(MD_Node* a, MD_Node* b, MD_StringMatchFlags flags);
//- rjf: tree duplication
MD_API MD_Node* md_tree_copy__arena(MD_Arena* arena, MD_Node* src_root);
MD_API MD_Node* md_tree_copy__ainfo(MD_AllocatorInfo ainfo, MD_Node* src_root);
#define md_treecopy(allocator, src_root) _Generic(allocator, MD_Arena*: md_tree_copy__arena, MD_AllocatorInfo: md_tree_copy__ainfo, default: md_assert_generic_sel_fail) md_generic_call(allocator, src_root)
md_force_inline MD_Node* md_tree_copy__arena(MD_Arena* arena, MD_Node* src_root) { return md_tree_copy__ainfo(md_arena_allocator(arena), src_root); }
////////////////////////////////
//~ rjf: Text -> Tokens Functions
MD_API MD_TokenizeResult md_tokenize_from_text__arena(MD_Arena* arena, MD_String8 text);
MD_API MD_TokenizeResult md_tokenize_from_text__ainfo(MD_AllocatorInfo ainfo, MD_String8 text);
#define md_tokenize_from_text(allocator, text) _Generic(allocator, MD_Arena*: md_tokenize_from_text__arena, MD_AllocatorInfo: md_tokenize_from_text__ainfo, default: md_assert_generic_sel_fail) md_generic_call(allocator, text)
md_force_inline MD_TokenizeResult md_tokenize_from_text__arena(MD_Arena* arena, MD_String8 text) { return md_tokenize_from_text__ainfo(md_arena_allocator(arena), text); }
////////////////////////////////
//~ rjf: Tokens -> Tree Functions
MD_API MD_ParseResult md_parse_from_text_tokens__arena(MD_Arena* arena, MD_String8 filename, MD_String8 text, MD_TokenArray tokens);
MD_API MD_ParseResult md_parse_from_text_tokens__ainfo(MD_AllocatorInfo ainfo, MD_String8 filename, MD_String8 text, MD_TokenArray tokens);
#define md_parse_from_text_tokens(allocator, filename, text, tokens) _Generic(allocator, MD_Arena*: md_parse_from_text_tokens__arena, MD_AllocatorInfo: md_parse_from_text_tokens__ainfo, default: md_assert_generic_sel_fail) md_generic_call(allocator, filename, text, tokens)
md_force_inline MD_ParseResult md_parse_from_text_tokens__arena(MD_Arena* arena, MD_String8 filename, MD_String8 text, MD_TokenArray tokens) { return md_parse_from_text_tokens__ainfo(md_arena_allocator(arena), filename, text, tokens); }
////////////////////////////////
//~ rjf: Bundled Text -> Tree Functions
MD_API MD_ParseResult md_parse_from_text__arena(MD_Arena* arena, MD_String8 filename, MD_String8 text);
MD_API MD_ParseResult md_parse_from_text__ainfo(MD_AllocatorInfo ainfo, MD_String8 filename, MD_String8 text);
#define md_parse_from_text(allocator, filename, text) _Generic(allocator, MD_Arena*: md_parse_from_text__arena, MD_AllocatorInfo: md_parse_from_text__ainfo, default: md_assert_generic_sel_fail) md_generic_call(allocator, filename, text)
#define md_tree_from_string(allocator, string) (md_parse_from_text((allocator), md_str8_zero(), (string)).root)
md_force_inline MD_ParseResult md_parse_from_text__arena(MD_Arena* arena, MD_String8 filename, MD_String8 text) { return md_parse_from_text__ainfo(md_arena_allocator(arena), filename, text); }
////////////////////////////////
//~ rjf: Tree -> Text Functions
MD_API MD_String8List md_debug_string_list_from_tree__arena(MD_Arena* arena, MD_Node* root);
MD_API MD_String8List md_debug_string_list_from_tree__ainfo(MD_AllocatorInfo ainfo, MD_Node* root);
#define md_debug_string_list_from_tree(allocator, string) _Generic(allocator, MD_Arena*: md_debug_string_list_from_tree__arena, MD_AllocatorInfo: md_debug_string_list_from_tree__ainfo, default: md_assert_generic_sel_fail) md_generic_call(allocator, string)
md_force_inline MD_String8List md_debug_string_list_from_tree__arena(MD_Arena* arena, MD_Node* root) { return md_debug_string_list_from_tree__ainfo(md_arena_allocator(arena), root); }