[examples] rearrange examples to give big examples their own folder; add hand written files for type info example

This commit is contained in:
Allen Webster
2021-09-19 16:28:06 -07:00
parent 0a07520fac
commit 2be6caf79a
14 changed files with 91 additions and 7 deletions
+108
View File
@@ -0,0 +1,108 @@
/*
** Example: datadesk-like-template
**
** This example is setup as a copy-pastable template for creating metadesk
** based metaprograms that have the same structure as datadesk metaprograms.
**
** Datadesk was a precursor language to metadesk. This example is mostly meant
** to help datadesk users understand metadesk and migrate onto it.
**
** A "datadesk-like" metaprogram is passed the input metacode files on the
** command line. These files are parsed. Then a set of three user-defined
** functions that form the "custom layer" are called. The "custom layer"
** defines all the additional analysis and code generation.
**
*/
//~ Includes and globals //////////////////////////////////////////////////////
#define MD_ENABLE_PRINT_HELPERS 1
#include "md.h"
#include "md.c"
static MD_Arena *arena = 0;
//~ Declare user defined functions (the datadesk "custom layer") //////////////
static void Initialize(void); // Runs at the beginning of generation.
static void TopLevel(MD_Node *node); // Runs once for each top-level node from each file.
static void CleanUp(void); // Runs at the end of generation.
//~ main //////////////////////////////////////////////////////////////////////
int main(int argument_count, char **arguments)
{
// setup the global arena
arena = MD_ArenaAlloc();
// parse all files passed to the command line
MD_b32 failed_parse = 0;
MD_Node *list = MD_MakeList(arena);
for(int i = 1; i < argument_count; i += 1)
{
// parse the file
MD_String8 file_name = MD_S8CString(arguments[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(stdout, code_loc, message->kind, message->string);
}
// save to parse results list
MD_PushNewReference(arena, list, parse_result.node);
// mark failure state
if (parse_result.errors.max_message_kind >= MD_MessageKind_Error)
{
failed_parse = 1;
}
}
// call the "custom layer"
if (!failed_parse)
{
Initialize();
for(MD_EachNode(ref, list->first_child))
{
MD_Node *root = MD_ResolveNodeFromReference(ref);
for(MD_EachNode(node, root->first_child))
{
TopLevel(node);
}
}
CleanUp();
}
return 0;
}
//~ The "custom layer" ////////////////////////////////////////////////////////
static void
Initialize(void)
{
/*TODO*/
}
static void
TopLevel(MD_Node *node)
{
/*TODO*/
}
static void
CleanUp(void)
{
/*TODO*/
}
+26
View File
@@ -0,0 +1,26 @@
/*
** Example: hello world
*/
//~ includes and globals //////////////////////////////////////////////////////
#include "md.h"
#include "md.c"
static MD_Arena *arena = 0;
//~ main //////////////////////////////////////////////////////////////////////
int
main(int argc, char **argv){
// setup the global arena
arena = MD_ArenaAlloc();
// parse a string
MD_String8 name = MD_S8Lit("<name>");
MD_String8 hello_world = MD_S8Lit("hello world");
MD_ParseResult parse = MD_ParseWholeString(arena, name, hello_world);
// print the results
MD_PrintDebugDumpFromNode(stdout, parse.node, MD_GenerateFlags_All);
}
+20
View File
@@ -0,0 +1,20 @@
////////////////////////////////
// The Hello World file
hello world!
"hello world!"
(hello world!)
[hello world!]
{hello world!}
hello: world!
hello: (world!)
@exclaim "hello world"
@message
{
@recipient world
@contents hello
@punctuate !
}
+96
View File
@@ -0,0 +1,96 @@
////////////////////////////////
// Identifiers (MD_NodeFlag_Identifier)
abc _foo_ x123
almostAnyCIdentifier // not including $
////////////////////////////////
// Numerics (MD_NodeFlag_Numeric)
123 0x123ABC 0b10101 123.456
0.123 123_456_789
123abc456xyz
123e+100
456E-100
789e+E-e+E-100100xyz
////////////////////////////////
// String Literals (MD_NodeFlag_StringLiteral)
//(MD_NodeFlag_StringDoubleQuote)
"Hello World"
" 'Hello World` "
"\"Hello World\""
"\\\a\b\c\d\e\f\""
""
//(MD_NodeFlag_StringSingleQuote)
'Hello World'
' "Hello World` '
''
//(MD_NodeFlag_StringTick)
`Hello World`
` "Hello World' `
``
////////////////////////////////
// Multi-Line String Literals (MD_NodeFlag_StringLiteral|MD_NodeFlag_StringTriplet)
//(MD_NodeFlag_StringDoubleQuote)
"""
This string can go on
for as many lines as I want.
And it can say things like
"Hello World"
or you know, whatever.
"""
//(MD_NodeFlag_StringSingleQuote)
'''
Multi-line strings can start
with a triplet of any of the
string markers.
'''
//(MD_NodeFlag_Tick)
```
```
////////////////////////////////
// Symbols (MD_NodeFlag_Symbol)
+ - * / = & ^ % ! | ? < > . ~
$
++ -- ** == && ^^ %% !! || ??
<< >> .. ~~ $$ -> <- => <= ?.
+= -= *= /= |= &= ^= %= != |=
...
+- +++++++ ??? !? $! $$$ <->
+-*/=&^%~|?<>.~$
/*
** A few punctuation characters are not counted with the rest
** of the symbol characters. These are the 'reserved' characters:
** ( ) { } [ ] : ; , @ # \
**
** And the sequences // and /* start comments so no symbol
** can start with either of those pairs.
**
** Block comments are nested */
*/
////////////////////////////////
// Identifiers, Numbers, Strings, and Symbols are the different kinds
// of 'labels' in Metadesk. They can all form main metadesk nodes:
_foo_: { bar123456; _123; }
0: (1.0.1.1, 1.2.0.0)
```
#include <stdio.h>
int main(){
printf("Hello World\n");
}
```: "Hello World"
..: . + .
+101
View File
@@ -0,0 +1,101 @@
/*
** Example: parse-check
**
** This example shows how to use the metadesk library to parse metadesk files,
** print errors, and dump verbose feedback on the resulting metadesk trees.
** This is also a nice utility for checking and inspecting your metadesk files.
**
*/
//~ includes and globals //////////////////////////////////////////////////////
#include "md.h"
#include "md.c"
// @notes For simple single-threaded memory management in a run-once-and-exit
// utility, a single global arena is our recommended approach.
static MD_Arena *arena = 0;
//~ main //////////////////////////////////////////////////////////////////////
int main(int argc, char **argv)
{
// setup the global arena
arena = MD_ArenaAlloc();
// 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
// @notes Here we rely on MD_ParseWholeFile which loads the file itself
// and then does the whole parse. In a simple utility program like
// this metadesk's default implementations for the overrides make
// this work.
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)
{
// @notes To print a message from the parse, technically we can do
// whatever we want. But we'll use the message format suggested
// by the metadesk library. First we get the code location - which
// means the file name and line number - for the node on this
// message. Then we pass the details of this message to the
// MD_PrintMessage helper function.
MD_CodeLoc code_loc = MD_CodeLocFromNode(message->node);
MD_PrintMessage(stdout, code_loc, message->kind, message->string);
}
// save to parse results list
// @notes Metadesk message kinds are sorted in order of severity. So we
// can easily check the severity of a entire parse by looking at the
// `max_message_kind` field on the errors from the parse result. Here
// only push the parse result onto our list if there were no errors.
if (parse_result.errors.max_message_kind < MD_MessageKind_Error)
{
MD_PushNewReference(arena, list, parse_result.node);
}
}
// print the verbose parse results
// @notes The Metadesk library provides a macros for iterating chains of
// MD_Node as shown here. The first parameter to the macro names an
// MD_Node pointer in the for loop that iterates through each node in the
// list. The second parameter is a pointer to the first node of the list.
// Generally we past the `first_child` of a list or parent MD_Node, but we
// don't always have to.
for (MD_EachNode(root_it, list->first_child))
{
// @notes The `list` we have been building does not contain a normal
// MD_Node chain like in the trees returned from the parser. Instead
// it contains a chain of 'reference' nodes, which can point to any
// metadesk node from a previous parse. So our `root_it` is iterating
// a list of reference nodes. Here we resolve the reference node to
// get the root of the parse tree.
MD_Node *root = MD_ResolveNodeFromReference(root_it);
for (MD_EachNode(node, root->first_child))
{
// @notes The Metadesk library likes to use MD_String8List for
// functions that build and return big strings. This simplifies
// memory management for big strings, and means a series of string
// builders can be called back to back and gathered into one list.
// When the string needs to be finalized into a single contiguous
// block a user can just call `MD_S8ListJoin` as shown here.
MD_String8List stream = {0};
MD_DebugDumpFromNode(arena, &stream, node, 0, MD_S8Lit(" "), MD_GenerateFlags_Tree);
MD_String8 str = MD_S8ListJoin(arena, stream, 0);
fwrite(str.str, str.size, 1, stdout);
fwrite("\n", 1, 1, stdout);
}
}
return 0;
}
+204
View File
@@ -0,0 +1,204 @@
////////////////////////////////
// Unlabeled Sets
//(MD_NodeFlag_HasParenLeft|MD_NodeFlag_HasParenRight)
(foo bar)
(
foo
bar
)
()
//(MD_NodeFlag_HasBracketLeft|MD_NodeFlag_HasBracketRight)
[foo bar]
//(MD_NodeFlag_HasBracketLeft|MD_NodeFlag_HasParenRight)
[foo bar)
//(MD_NodeFlag_HasParenLeft|MD_NodeFlag_HasBracketRight)
(foo bar]
//(MD_NodeFlag_HasBraceLeft|MD_NodeFlag_HasBraceRight)
{foo bar}
/*
** Mixing { with ) or ] generates an error.
** Same with ( or [ with }.
** This is itended to make {} a useful way to delimit a set when extra help
** via errors from the parser is preferable to flexibility.
*/
////////////////////////////////
// Labeled Sets
foo: (bar baz) 123: (foo bar)
"foo": (bar baz) +++: (foo bar)
foo: ()
"""
foo
""": (bar baz)
foo: [bar]
foo: [foo)
foo: (bar]
foo: {bar}
/*
** When a set has a label, it's node will have the label as it's string
** contents, and the children as it's set contnts. The node will have flags
** for both it's label and it's set properties. For instance the root node
** parsed from
** +++: (foo bar)
** has the flags:
** MD_NodeFlag_HasParenLeft|MD_NodeFlag_HasParenRight|MD_NodeFlag_Symbol
*/
////////////////////////////////
// Nested Sets
{
(foo bar)
foo: (bar)
[a b c]
[({})]
}
////////////////////////////////
// Undelimited Sets (<no left/right flags>)
// delimited by newline
foo: bar baz
// starts on the next line, delimited by new line
foo:
bar baz
// starts on the next line, consumes entire multi-line string
foo:
"""
bar
"""
// kind of weird, but you can chain things on so long as a newline
// never appears outside of a multi-line string to end the set
foo: """
bar
""" baz """
and-back-to-foo-again
"""
// nesting undelimited sets
foo: bar:
baz: foo_again
// undelimited sets may not contain unlabeled delimited sets
// the following forms one undelimited set followed by a delimited set
// the delimited set is a sibling to foo, not a child to foo.
foo: bar (baz)
// undelimited sets *may* however contain labeled delimited sets
// now foo is an undelimited set that contains bar, and bar is a
// delimited set that contains baz
foo: bar: (baz)
// again things can get weird. Here bar and baz are both delimited
// sets contained in foo, because at there are no newlines at the
// level of the foo node until after the baz set.
foo:
bar: (
a
b
c
) baz: (
1
2
3
)
////////////////////////////////
// Seperators
// A separator can be placed after any member of delimited sets.
// The separator does not effect the shape of the tree but it does
// attach flags to the nodes before and after it.
(
word1
//(MD_NodeFlag_IsBeforeComma)
word2,
//(MD_NodeFlag_IsBeforeComma|MD_NodeFlag_IsAfterComma)
word3,
//(MD_NodeFlag_IsAfterComma)
word4
)
(
word1
//(MD_NodeFlag_IsBeforeSemicolon)
word2;
//(MD_NodeFlag_IsBeforeSemicolon|MD_NodeFlag_IsAfterSemicolon)
word3;
//(MD_NodeFlag_IsAfterSemicolon)
word4
)
(
word1
//(MD_NodeFlag_IsBeforeComma)
word2,
//(MD_NodeFlag_IsBeforeSemicolon|MD_NodeFlag_IsAfterComma)
word3;
//(MD_NodeFlag_IsAfterSemicolon|MD_NodeFlag_IsBeforeComma)
word4,
)
// A separator ends an undelimited set early. Here the commas and semi-colon
// split this line into three undelimited sets. The "before" flag goes on the
// undelimited set, not the child. For instance, here the set foo gets the flag
// (MD_NodeFlag_IsBeforeComma)
foo: bar, baz: foo_again; foo: bar
////////////////////////////////
// Tags
// tags are made with an @ before identifier
@foo bar
// tags can be attached to any node
@foo "string"
// any number of tags can be attached to a node
@foo @bar @foo @bar baz
// tags can be attached to sets
@foo (bar baz)
@foo [bar baz]
@foo bar: {baz}
@foo bar: baz
// tags can be attached to nodes inside sets
@foo (@bar baz)
@foo bar: @baz foo_again
// tags can have children of their own
// set with () and no space after the identifier
@foo(bar) baz
@foo(bar) (baz baz)
@foo(bar bar) foo
@foo(bar baz) @bar fooz
// the children of a tag can be sets
@foo({bar baz} [biz boo]) far
@foo(bar: baz: 123) xyz
// the children of a tag are actually just a set delimited with ()
@tag(tag_child, 123;
"strings etc") node
// the children of a tag can even be tagged
@tag(tag_child, @number 123) node
@tag(tag_child, @number(integer) 123) node