mirror of
https://github.com/Ed94/metadesk.git
synced 2026-08-06 15:48:47 +00:00
[examples] rearrange examples to give big examples their own folder; add hand written files for type info example
This commit is contained in:
@@ -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*/
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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 !
|
||||
}
|
||||
@@ -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"
|
||||
|
||||
..: . + .
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user