All of the data structures and prototypes needed for the library are in the header file "parser.h". This file is the best resource for the parser and is updated with each MGF release.
extern int (*mg_ehand[MG_NENTITIES])(int argc, char **argv);Before parsing begins, this dispatch table is initialized to point to the routines that will handle each supported entity. Every entity handler has the same basic prototype, which is the same as the main function, i.e:
extern int handler(int argc, char **argv);The first argument is the number of words in the MGF entity (counting the entity itself) and the second argument is an array of nul-terminated strings with the entity and its arguments. The function should return zero or one of the error codes defined in "parser.h". A non-zero return value causes the parser to abort, returning the error up through its call stack to the entry function, usually mg_load.
A special function pointer for undefined entities is defined as follows:
extern int (*mg_uhand)(int argc, char **argv);By default, this points to the library function mg_defuhand, which prints an error message on the first unknown entity and keeps a count from then on, which is stored in the global unsigned integer mg_nunknown. If the mg_uhand pointer is assigned a value of NULL instead, parsing will abort at the first unrecognized entity. The reason this is not the default action is that ignoring unknown entities offers a certain base level of forward compatibility. Ignoring things one does not understand is not the best approach, but it is usually better than quitting with an error message if the input is in fact valid, but is a later version of the standard. The real solution is to update the interpreter by linking to a new version of the parser, or use a new version of the mgfilt command to convert the new MGF input to an older standard.
The mg_uhand pointer may also be used to customize the language for a particular application by adding entities, though this is discouraged because it tends to weaken the standard.
The skeletal framework for an MGF loader or translator is to assign function pointers to the mg_ehand array, call the parser initialization function mg_init, then call the file loader function mg_load once for each input file. This will in turn make calls back to the functions assigned to mg_ehand. To give a simple example, let us look at a translator that understands only flat polygonal faces, putting out vertex locations immediately after each "face" keyword:
#include < stdio.h > #include "parser.h" int myfaceh(ac, av) /* face handling routine */ int ac; char **av; { C_VERTEX *vp; /* vertex structure pointer */ FVECT vert; /* vertex point location */ int i; if (ac < 4) /* check # arguments */ return(MG_EARGC); printf("face\n"); /* begin face output */ for (i = 1; i < ac; i++) { if ((vp = c_getvert(av[i])) == NULL) /* vertex from name */ return(MG_EUNDEF); xf_xfmpoint(vert, vp -> p); /* apply transform */ printf("%15.9f %15.9f %15.9f\n", vert[0], vert[1], vert[2]); /* output vertex */ } printf(";\n"); /* end of face output */ return(MG_OK); /* normal exit */ } main(argc, argv) /* translate MGF file(s) */ int argc; char **argv; { int i; /* initialize dispatch table */ mg_ehand[MG_E_FACE] = myfaceh; /* ours */ mg_ehand[MG_E_VERTEX] = c_hvertex; /* parser lib */ mg_ehand[MG_E_POINT] = c_hvertex; /* parser lib */ mg_ehand[MG_E_XF] = xf_handler; /* parser lib */ mg_init(); /* initialize parser */ for (i = 1; i < argc; i++) /* load each file argument */ if (mg_load(argv[i]) != MG_OK) /* and check for error */ exit(1); exit(0); /* all done! */ }Hopefully, this example demonstrates just how easy it is to write an MGF translator. Of course, translators get more complicated the more entity types they support, but the point is that one does not have to support every entity -- the parser handles what the translator does not. Also, the library includes many general entity handlers, further reducing the burden on the programmer. This same principle means that it is not necessary to modify an existing program to accommodate a new version of MGF -- one need only link to the new parser library to comply with the new standard.
The rest of the routines in a translator or loader program are called indirectly through the mg_ehand dispatch table, and they are the ones that do the real work of supporting the MGF entities. In addition to converting or discarding entities that the calling program does not know or care about, the parser library includes a set of context handlers that greatly simplify the translation process. There are three handlers for each of the three named contexts and their constituents, and two handlers for the two hierarchical context entities. To use these handlers, one simply sets the appropriate positions in the mg_ehand dispatch table to point to these functions. Additional functions and global data structures provide convenient access to the relevant contexts, and all of these are detailed in the following manual pages.