/*
** 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;
}
