Files
2025-02-08 12:41:07 -05:00

116 lines
4.6 KiB
C

/*
** Example: user errors
**
** This example shows how to print custom error messages.
**
** Pass user_errors.mdesk to see the custom messages in action.
**
** We will be adding two custom rules that check for certain properties of the
** top level nodes in a metadesk file:
** 1. No top level node should have multiple tags
** 2. Top level sets with brackets '[]' are expected to be unnamed
**
*/
//~ 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 all files passed to the command line
Node *list = MD_MakeList(arena);
for(int i = 1; i < argc; i += 1)
{
// parse the file
MD_String8 file_name = MD_S8CString(argv[i]);
ParseResult parse_result = MD_ParseWholeFile(arena, file_name);
// @notes Here we are printing out all the messages that came back from
// the parser. We could try to wait and print all the errors at once,
// or perform the custom checks in this block to put all the error
// output in a single place. But all that work deferring tasks or
// analyzing can quickly make things complicated. Out preferred method
// is to process the Metadesk file in stages. This is the parsing
// stage so this is where the parsing errors are reported. Custom
// errors get added in later stages whenever the analyzer finds them.
// 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);
}
// check for custom errors
for(each_node(ref, list->first_child))
{
Node *root = MD_ResolveNodeFromReference(ref);
// @notes The custom rules in this example apply to the top level nodes
// so we can check for them by visiting each node from the root.
// The example "type metadata" includes more advanced custom errors
// that are only discovered after more analysis.
for(each_node(node, root->first_child))
{
// top level node should have one or zero tags.
// @notes This helper gives us a way to treat the tags on `node`
// as an array and access it by index. If there's a tag at index 1
// then we know there are at least two tags, which is against
// the first custom rule.
Node *tag_2 = MD_TagFromIndex(node, 1);
if (!MD_NodeIsNil(tag_2))
{
// @notes The MD_PrintMessage helper formats a message for us
// with a filename and line number, and prints it to a FILE.
// We provide it with the coordinates with a MD_CodeLoc, which
// we can get from any metadesk node in the parse tree with
// the helper MD_CodeLoc. In this case we will get an error
// message pointed right at the offending tag.
//
// Messages don't have to be printed with MD_PrintMessage.
// In cases where the errors have a final destination other
// than a FILE, the message can be saved as a string with
// MD_FormatMessage.
MD_CodeLoc loc = MD_CodeLocFromNode(tag_2);
MD_PrintMessage(stderr, loc, MD_MessageKind_Error,
MD_S8Lit("Not supposed to have multiple tags."));
}
// top level sets with brackets should not have names
// @notes The second custom rule applies to sets with "[]"
// delimiters. We can easily detect nodes for those sets by
// checking the node flags:
if ((node->flags & NodeFlag_HasBracketLeft) ||
(node->flags & NodeFlag_HasBracketRight))
{
if (node->string.size > 0)
{
MD_CodeLoc loc = MD_CodeLocFromNode(node);
MD_PrintMessage(stderr, loc, MD_MessageKind_Error,
MD_S8Lit("Nodes with brackets should not have names."));
}
}
}
}
return 0;
}