From b6cbff2eb17efa4fb72049f3293509001b383f23 Mon Sep 17 00:00:00 2001 From: Allen Webster Date: Fri, 17 Sep 2021 00:48:06 -0700 Subject: [PATCH] tidying up naming scheme and parameters in helper inspection API; [examples] sketch of type metadata example --- bin/build_examples.sh | 5 +- bin/run_examples.sh | 5 + docs/metadesk_reference.mdesk | 24 ++-- examples/mdesk_files/types.mdesk | 56 ++++++++ examples/mdesk_files/user_errors.mdesk | 5 + examples/metaprograms/type_metadata.c | 171 +++++++++++++++++++++++++ examples/metaprograms/user_errors.c | 7 - intro_notes.txt | 2 - source/md.c | 20 +-- source/md.h | 6 +- tests/sanity_tests.c | 10 +- 11 files changed, 269 insertions(+), 42 deletions(-) create mode 100644 examples/mdesk_files/types.mdesk create mode 100644 examples/metaprograms/type_metadata.c diff --git a/bin/build_examples.sh b/bin/build_examples.sh index 56cbe14..295368d 100755 --- a/bin/build_examples.sh +++ b/bin/build_examples.sh @@ -11,9 +11,10 @@ bin/bld_core.sh show_ctx metasrc="examples/metaprograms" -bin/bld_core.sh unit datadesk_like $metasrc/datadesk_like_template.c -bin/bld_core.sh unit user_errors $metasrc/user_errors.c +bin/bld_core.sh unit type_metadata $metasrc/type_metadata.c bin/bld_core.sh unit parse_check $metasrc/parse_check.c +bin/bld_core.sh unit user_errors $metasrc/user_errors.c +bin/bld_core.sh unit datadesk_like $metasrc/datadesk_like_template.c echo diff --git a/bin/run_examples.sh b/bin/run_examples.sh index d1cbd9a..74a40cb 100755 --- a/bin/run_examples.sh +++ b/bin/run_examples.sh @@ -8,6 +8,11 @@ cd .. build_path=$root_path/build examples_path=$root_path/examples +echo ~~~ Running Type Metadata Example ~~~ +cd $build_path +./type_metadata.exe $examples_path/mdesk_files/types.mdesk +echo + echo ~~~ Running Error Generation Example ~~~ cd $build_path ./user_errors.exe $examples_path/mdesk_files/user_errors.mdesk diff --git a/docs/metadesk_reference.mdesk b/docs/metadesk_reference.mdesk index 2d134d6..6a6ec58 100644 --- a/docs/metadesk_reference.mdesk +++ b/docs/metadesk_reference.mdesk @@ -1659,13 +1659,11 @@ MD_ParseWholeFile: @send(Nodes) @doc("Finds a node in the range defined by @code 'first' and @code 'one_past_last', with the string matching @code 'string' in accordance with @code 'flags', or returns a nil node pointer if it is not found.") -@see(MD_NodeFromIndex) +@see(MD_NodeAtIndex) @see(MD_S8Match) -@func MD_NodeFromString: { +@func MD_FirstNodeWithString: { @doc("The first node in the range to search.") first: *MD_Node, - @doc("One past the last node in the range to search. A nil node, if the entire available range starting with @code 'first' is to be searched.") - one_past_last: *MD_Node, @doc("The string to search for.") string: MD_String8, @doc("Controls what is considered a match, when doing string matching.") @@ -1676,21 +1674,21 @@ MD_ParseWholeFile: @send(Nodes) @doc("Finds the @code 'n'th node in the range defined by @code 'first' and @code 'one_past_last', or returns a nil node pointer if it is not found. @code '0' would match @code 'first', @code '1' would match @code 'first->next', and so on.") -@see(MD_NodeFromString) -@func MD_NodeFromIndex: { +@see(MD_FirstNodeWithString) +@func MD_NodeAtIndex: { @doc("The first node in the range to search.") first: *MD_Node, - @doc("One past the last node in the range to search. A nil node, if the entire available range starting with @code 'first' is to be searched.") - one_past_last: *MD_Node, @doc("The index to search for.") n: int, @doc("The found node, or a nil node pointer if no node was found.") return: *MD_Node, }; +// TODO(allen): MD_FirstNodeWithFlags + @send(Nodes) @doc("Finds the child index of @code 'node', with @code '0' being the first child, @code '1' being the second, and so on.") -@see(MD_NodeFromIndex) +@see(MD_NodeAtIndex) @func MD_IndexFromNode: { node: *MD_Node, return: int, @@ -1707,7 +1705,7 @@ MD_ParseWholeFile: @send(Nodes) @doc("Finds a child of @code 'node' with a string matching @code 'child_string', where the rules of matching are determined by @code 'flags'.") -@see(MD_NodeFromString) +@see(MD_FirstNodeWithString) @see(MD_TagFromString) @func MD_ChildFromString: { @doc("The parent whose children are to be searched.") @@ -1722,7 +1720,7 @@ MD_ParseWholeFile: @send(Nodes) @doc("Finds a tag on @code 'node' with a string matching @code 'tag_string', where the rules of matching are determined by @code 'flags'.") -@see(MD_NodeFromString) +@see(MD_FirstNodeWithString) @see(MD_ChildFromString) @func MD_TagFromString: { @doc("The parent whose tags are to be searched.") @@ -1737,7 +1735,7 @@ MD_ParseWholeFile: @send(Nodes) @doc("Finds a child of @code 'node' with an index matching @code 'n'. Returns a nil node pointer if no such child is found.") -@see(MD_NodeFromIndex) +@see(MD_NodeAtIndex) @see(MD_IndexFromNode) @see(MD_TagFromIndex) @func MD_ChildFromIndex: { @@ -1751,7 +1749,7 @@ MD_ParseWholeFile: @send(Nodes) @doc("Finds a tag on @code 'node' with an index matching @code 'n'. Returns a nil node pointer if no such tag is found.") -@see(MD_NodeFromIndex) +@see(MD_NodeAtIndex) @see(MD_IndexFromNode) @see(MD_ChildFromIndex) @func MD_TagFromIndex: { diff --git a/examples/mdesk_files/types.mdesk b/examples/mdesk_files/types.mdesk new file mode 100644 index 0000000..f19f0c2 --- /dev/null +++ b/examples/mdesk_files/types.mdesk @@ -0,0 +1,56 @@ +/* +** Setup as input to the type metadata example +*/ + +@type(basic) U32: 4; +@type(basic) F32: 4; +@type(basic) V2F32: 8; + +@type(struct) +Circle: +{ + r: F32; + pos: V2F32; +} + +@type(struct) +RoundedSegment: +{ + r: F32; + p1: V2F32; + p2: V2F32; +} + +@type(struct) +RoundedPolygon: +{ + r: F32; + count: U32; + p: @array(count) V2F32; +} + +@type(enum: U32) +Shape: +{ + Circle: 1, + Segment: 2, + Polygon: 3, +} + +@map(Shape -> `$Type`; complete) +type_info_from_shape: +{ + Circle -> Circle, + Segment -> RoundedSegment, + Polygon -> RoundedPolygon, +} + +@map(Shape -> U32; default: 0; auto: 64) +max_slot_from_shape: +{ + Polygon -> 12, +} + + + + diff --git a/examples/mdesk_files/user_errors.mdesk b/examples/mdesk_files/user_errors.mdesk index 48cbef3..de010d2 100644 --- a/examples/mdesk_files/user_errors.mdesk +++ b/examples/mdesk_files/user_errors.mdesk @@ -1,3 +1,6 @@ +/* +** Setup as input to the user-errors example +*/ @foo @bar Foo: { @@ -6,6 +9,8 @@ 1, 2, 3, } +[50 + 200] + @baz Blah: { x100 y200 z300 diff --git a/examples/metaprograms/type_metadata.c b/examples/metaprograms/type_metadata.c new file mode 100644 index 0000000..e8fda88 --- /dev/null +++ b/examples/metaprograms/type_metadata.c @@ -0,0 +1,171 @@ +/* +** Example: type-metadata +** +** This example shows how one might use metadesk to define types with a +** metadesk file and generate the types along with metadata such as field +** layouts, string tables, and maps. +** +*/ + +//~ includes and globals ////////////////////////////////////////////////////// + +#include "md.h" +#include "md.c" + +static MD_Arena *arena = 0; + +//~ types ///////////////////////////////////////////////////////////////////// + +typedef enum TypeKind +{ + TypeKind_Null, + TypeKind_Basic, + TypeKind_Struct, + TypeKind_Enum, +} TypeKind; + +typedef struct TypeInfo TypeInfo; +struct TypeInfo +{ + TypeInfo *next; + TypeKind kind; + MD_Node *node; +}; + +typedef struct MapInfo MapInfo; +struct MapInfo +{ + MapInfo *next; + MD_Node *node; +}; + + +//~ node maps ///////////////////////////////////////////////////////////////// + +TypeInfo *first_type = 0; +TypeInfo *last_type = 0; +MD_Map type_map = {0}; + +MapInfo *first_map = 0; +MapInfo *last_map = 0; +MD_Map map_map = {0}; + + +//~ main ////////////////////////////////////////////////////////////////////// + +int +main(int argc, char **argv) +{ + // setup the global arena + arena = MD_ArenaAlloc(1ull << 40); + + // parse all files passed to the command line + MD_Node *list = MD_MakeList(arena); + for(int i = 1; i < argc; i += 1) + { + // parse the file + MD_String8 file_name = MD_S8CString(argv[i]); + MD_ParseResult parse_result = MD_ParseWholeFile(arena, file_name); + + // print metadesk errors + for (MD_Message *message = parse_result.errors.first; + message != 0; + message = message->next) + { + MD_CodeLoc code_loc = MD_CodeLocFromNode(message->node); + MD_PrintMessage(stderr, code_loc, message->kind, message->string); + } + + // save to parse results list + MD_PushNewReference(arena, list, parse_result.node); + } + + // init maps + type_map = MD_MapMake(arena); + map_map = MD_MapMake(arena); + + // gather types & maps + for(MD_EachNode(ref, list->first_child)) + { + MD_Node *root = MD_ResolveNodeFromReference(ref); + for(MD_EachNode(node, root->first_child)) + { + // gather type + MD_Node *type_tag = MD_TagFromString(node, MD_S8Lit("type"), 0); + + if (!MD_NodeIsNil(type_tag)) + { + TypeKind kind = TypeKind_Null; + MD_Node *tag_arg_node = type_tag->first_child; + MD_String8 tag_arg_str = tag_arg_node->string; + if (MD_S8Match(tag_arg_str, MD_S8Lit("basic"), 0)){ + kind = TypeKind_Basic; + } + else if (MD_S8Match(tag_arg_str, MD_S8Lit("struct"), 0)){ + kind = TypeKind_Struct; + } + else if (MD_S8Match(tag_arg_str, MD_S8Lit("enum"), 0)){ + kind = TypeKind_Enum; + } + + if (kind == TypeKind_Null){ + MD_CodeLoc loc = MD_CodeLocFromNode(node); + MD_PrintMessageFmt(stderr, loc, MD_MessageKind_Error, + "Unrecognized type kind '%.*s'\n", + MD_S8VArg(tag_arg_str)); + } + else{ + TypeInfo *type_info = MD_PushArray(arena, TypeInfo, 1); + type_info->kind = kind; + type_info->node = node; + + MD_QueuePush(first_type, last_type, type_info); + MD_MapInsert(arena, &type_map, MD_MapKeyStr(node->string), type_info); + } + } + + // gather map + MD_Node *map_tag = MD_TagFromString(node, MD_S8Lit("map"), 0); + + if (!MD_NodeIsNil(map_tag)) + { + MapInfo *map_info = MD_PushArray(arena, MapInfo, 1); + map_info->node = node; + + MD_QueuePush(first_map, last_map, map_info); + MD_MapInsert(arena, &map_map, MD_MapKeyStr(node->string), map_info); + } + } + } + + // print state + for (TypeInfo *type = first_type; + type != 0; + type = type->next){ + char *kind_string = "ERROR"; + switch (type->kind){ + default:break; + case TypeKind_Basic: kind_string = "basic"; break; + case TypeKind_Struct: kind_string = "struct"; break; + case TypeKind_Enum: kind_string = "enum"; break; + } + + MD_Node *node = type->node; + printf("%.*s: %s\n", MD_S8VArg(node->string), kind_string); + } + + for (MapInfo *map = first_map; + map != 0; + map = map->next){ + MD_Node *node = map->node; + printf("%.*s: map\n", MD_S8VArg(node->string)); + } + + // TODO metadata hand-written portion + // TODO check types & build member lists + // TODO check maps & build case lists + // TODO generate type definitions + // TODO generate function declarations + // TODO generate metadata tables + // TODO generate function definitions +} diff --git a/examples/metaprograms/user_errors.c b/examples/metaprograms/user_errors.c index a564a81..abae2b5 100644 --- a/examples/metaprograms/user_errors.c +++ b/examples/metaprograms/user_errors.c @@ -19,13 +19,6 @@ static MD_Arena *arena = 0; int main(int argc, char **argv) { - char *argv_dummy[] = { - 0, - "W:/metadesk/examples/mdesk_files/user_errors.mdesk" - }; - argc = 2; - argv = argv_dummy; - // setup the global arena arena = MD_ArenaAlloc(1ull << 40); diff --git a/intro_notes.txt b/intro_notes.txt index 9c9025f..13984d6 100644 --- a/intro_notes.txt +++ b/intro_notes.txt @@ -8,8 +8,6 @@ Example Programs: [x] Metadesk hello world [x] Metadesk parse checker [x] User error checking -[ ] Example of helpers: string helpers, linked lists, map type - printing errors, cmd line, file iter [x] Datadesk-like setup [ ] Example type metadata [ ] Example of simple expression parser diff --git a/source/md.c b/source/md.c index a502a77..eade35e 100644 --- a/source/md.c +++ b/source/md.c @@ -2695,10 +2695,10 @@ MD_PushNewReference(MD_Arena *arena, MD_Node *list, MD_Node *target) //~ Introspection Helpers MD_FUNCTION MD_Node * -MD_NodeFromString(MD_Node *first, MD_Node *one_past_last, MD_String8 string, MD_MatchFlags flags) +MD_FirstNodeWithString(MD_Node *first, MD_String8 string, MD_MatchFlags flags) { MD_Node *result = MD_NilNode(); - for(MD_Node *node = first; !MD_NodeIsNil(node) && node != one_past_last; node = node->next) + for(MD_Node *node = first; !MD_NodeIsNil(node); node = node->next) { if(MD_S8Match(string, node->string, flags)) { @@ -2710,13 +2710,13 @@ MD_NodeFromString(MD_Node *first, MD_Node *one_past_last, MD_String8 string, MD_ } MD_FUNCTION MD_Node * -MD_NodeFromIndex(MD_Node *first, MD_Node *one_past_last, int n) +MD_NodeAtIndex(MD_Node *first, int n) { MD_Node *result = MD_NilNode(); if(n >= 0) { int idx = 0; - for(MD_Node *node = first; !MD_NodeIsNil(node) && node != one_past_last; node = node->next, idx += 1) + for(MD_Node *node = first; !MD_NodeIsNil(node); node = node->next, idx += 1) { if(idx == n) { @@ -2729,10 +2729,10 @@ MD_NodeFromIndex(MD_Node *first, MD_Node *one_past_last, int n) } MD_FUNCTION MD_Node * -MD_NodeFromFlags(MD_Node *first, MD_Node *one_past_last, MD_NodeFlags flags) +MD_FirstNodeWithFlags(MD_Node *first, MD_NodeFlags flags) { MD_Node *result = MD_NilNode(); - for(MD_Node *n = first; n != one_past_last && !MD_NodeIsNil(n); n = n->next) + for(MD_Node *n = first; !MD_NodeIsNil(n); n = n->next) { if(n->flags & flags) { @@ -2765,25 +2765,25 @@ MD_RootFromNode(MD_Node *node) MD_FUNCTION MD_Node * MD_ChildFromString(MD_Node *node, MD_String8 child_string, MD_MatchFlags flags) { - return MD_NodeFromString(node->first_child, MD_NilNode(), child_string, flags); + return MD_FirstNodeWithString(node->first_child, child_string, flags); } MD_FUNCTION MD_Node * MD_TagFromString(MD_Node *node, MD_String8 tag_string, MD_MatchFlags flags) { - return MD_NodeFromString(node->first_tag, MD_NilNode(), tag_string, flags); + return MD_FirstNodeWithString(node->first_tag, tag_string, flags); } MD_FUNCTION MD_Node * MD_ChildFromIndex(MD_Node *node, int n) { - return MD_NodeFromIndex(node->first_child, MD_NilNode(), n); + return MD_NodeAtIndex(node->first_child, n); } MD_FUNCTION MD_Node * MD_TagFromIndex(MD_Node *node, int n) { - return MD_NodeFromIndex(node->first_tag, MD_NilNode(), n); + return MD_NodeAtIndex(node->first_tag, n); } MD_FUNCTION MD_Node * diff --git a/source/md.h b/source/md.h index cc8d84e..1be81e0 100644 --- a/source/md.h +++ b/source/md.h @@ -1068,9 +1068,9 @@ MD_FUNCTION MD_Node *MD_PushNewReference(MD_Arena *arena, MD_Node *list, MD_Node // These calls are for getting info from nodes, and introspecting // on trees that are returned to you by the parser. -MD_FUNCTION MD_Node * MD_NodeFromString(MD_Node *first, MD_Node *one_past_last, MD_String8 string, MD_MatchFlags flags); -MD_FUNCTION MD_Node * MD_NodeFromIndex(MD_Node *first, MD_Node *one_past_last, int n); -MD_FUNCTION MD_Node * MD_NodeFromFlags(MD_Node *first, MD_Node *one_past_last, MD_NodeFlags flags); +MD_FUNCTION MD_Node * MD_FirstNodeWithString(MD_Node *first, MD_String8 string, MD_MatchFlags flags); +MD_FUNCTION MD_Node * MD_NodeAtIndex(MD_Node *first, int n); +MD_FUNCTION MD_Node * MD_FirstNodeWithFlags(MD_Node *first, MD_NodeFlags flags); MD_FUNCTION int MD_IndexFromNode(MD_Node *node); MD_FUNCTION MD_Node * MD_RootFromNode(MD_Node *node); MD_FUNCTION MD_Node * MD_ChildFromString(MD_Node *node, MD_String8 child_string, MD_MatchFlags flags); diff --git a/tests/sanity_tests.c b/tests/sanity_tests.c index a487415..334720d 100644 --- a/tests/sanity_tests.c +++ b/tests/sanity_tests.c @@ -550,7 +550,7 @@ int main(void) MD_ParseResult parse = MD_ParseOneNode(arena, MD_S8Lit("foo:{x y z; a b c}"), 0); MD_Node *node = parse.node; MD_Node *group_first = node->first_child; - MD_Node *group_opl = MD_NodeFromFlags(group_first->next, MD_NilNode(), MD_NodeFlag_IsAfterSemicolon); + MD_Node *group_opl = MD_FirstNodeWithFlags(group_first->next, MD_NodeFlag_IsAfterSemicolon); TestResult(MD_S8Match(group_first->string, MD_S8Lit("x"), 0)); TestResult(MD_S8Match(group_first->next->string, MD_S8Lit("y"), 0)); @@ -558,7 +558,7 @@ int main(void) TestResult(group_opl == group_first->next->next->next); group_first = group_opl; - group_opl = MD_NodeFromFlags(group_first->next, MD_NilNode(), MD_NodeFlag_IsAfterSemicolon); + group_opl = MD_FirstNodeWithFlags(group_first->next, MD_NodeFlag_IsAfterSemicolon); TestResult(MD_S8Match(group_first->string, MD_S8Lit("a"), 0)); TestResult(MD_S8Match(group_first->next->string, MD_S8Lit("b"), 0)); @@ -573,21 +573,21 @@ int main(void) MD_Node *group_opl = 0; group_first = node->first_child; - group_opl = MD_NodeFromFlags(group_first->next, MD_NilNode(), MD_NodeFlag_IsAfterComma); + group_opl = MD_FirstNodeWithFlags(group_first->next, MD_NodeFlag_IsAfterComma); TestResult(MD_S8Match(group_first->string, MD_S8Lit("a"), 0)); TestResult(MD_S8Match(group_first->next->string, MD_S8Lit("b"), 0)); TestResult(MD_S8Match(group_first->next->next->string, MD_S8Lit("c"), 0)); TestResult(group_opl == group_first->next->next->next); group_first = group_opl; - group_opl = MD_NodeFromFlags(group_first->next, MD_NilNode(), MD_NodeFlag_IsAfterComma); + group_opl = MD_FirstNodeWithFlags(group_first->next, MD_NodeFlag_IsAfterComma); TestResult(MD_S8Match(group_first->string, MD_S8Lit("d"), 0)); TestResult(MD_S8Match(group_first->next->string, MD_S8Lit("e"), 0)); TestResult(MD_S8Match(group_first->next->next->string, MD_S8Lit("f"), 0)); TestResult(group_opl == group_first->next->next->next); group_first = group_opl; - group_opl = MD_NodeFromFlags(group_first->next, MD_NilNode(), MD_NodeFlag_IsAfterComma); + group_opl = MD_FirstNodeWithFlags(group_first->next, MD_NodeFlag_IsAfterComma); TestResult(MD_S8Match(group_first->string, MD_S8Lit("g"), 0)); TestResult(MD_S8Match(group_first->next->string, MD_S8Lit("h"), 0)); TestResult(MD_S8Match(group_first->next->next->string, MD_S8Lit("i"), 0));