Hints on writing a reader
This is what I do...
All functions return an error status as a pointer to a string. NULL
means no error, otherwise it is a tag, which can be looked up.
Working from the bottom up:
const char *readmstoken(char *name)
{
const char *err = 0;
if (1 != fscanf(mscript_iofp, " %32s", name))
{
if (ferror(mscript_iofp))
{ err = "Xmsread"; }
else
{ err = "Xmstoken"; }
}
return err;
}
readmstoken() reads the next token knowing it is not a string in double
quotes. This will never be more than 31 chars.
const char *readmsstring(char *value)
{
const char *err = 0;
int c;
BOOL done;
insist(value);
while ((c = fgetc(mscript_iofp)) == ' ') { }
if (c != '\"')
{ err = "Xmsnodq"; }
done = FALSE;
while (!err && !done)
{
while ((c = fgetc(mscript_iofp)) != '\"')
{ *value++ = c; }
c = fgetc(mscript_iofp);
if (c != '\"')
{ ungetc(c, mscript_iofp); done = TRUE; }
else
{ *value++ = c; }
}
if (ferror(mscript_iofp))
{ err = "Xmsread"; }
return err;
}
readmsstring() reads a string in double quotes, knowing that is what to
expect.
const char *mscriptskipstring(void)
{
....
}
Same as readmsstring(), except ignores what it reads.
const char *mscriptskipvalue(const char *keyword)
{
const char *err = 0;
char name[max_token_size];
if (keyword[strlen(keyword) - 1] == '$')
{
if (!err) { err = mscriptskipstring(); }
}
else
{
if (!err) { err = readmstoken(name); }
if (0 != strcmp(name, "{"))
{
return err; /* simple value, done */
}
else /* complex value, recurse */
{
if (!err) { err = readmstoken(name); }
while (!err && 0 != strcmp(name, "}"))
{
if (name[strlen(name) - 1] == '$')
{
if (!err) { err = mscriptskipstring(); }
}
else
{
if (!err) { err = mscriptskipvalue(name); }
}
if (!err) { err = readmstoken(name); }
}
}
}
return err;
}
mscriptskipvalue() is called when an unknown name token is found.
const char *readmsstructstart(void)
{
const char *err = 0;
char token[max_token_size];
if (!err) { err = readmstoken(token); }
if (!err && 0 != strcmp(token, "{"))
{ err = "Xmssyntax"; }
return err;
}
When a known name is encountered, which is follwed by a complex
value, readmsstructstart() checks the '{' and reads past it.
const char *readmsstartarray(int *n)
{
const char *err = 0;
char token[max_token_size];
int x = 0;
if (!err) { err = readmstoken(token); }
if (!err && 0 != strcmp(token, "{"))
{ err = "Xmssyntax"; }
if (!err) { err = readmstoken(token); }
if (!err && 0 != strcmp(token, "nof"))
{ err = "Xmssyntax"; }
if (!err) { err = readmstoken(token); }
x = atoi(token);
if (!err && x <= 0)
{ err = "Xmssyntax"; }
*n = x;
return err;
}
When a known name is encountered, which is followed by an array (list),
readmsstartarray() checks the start, returns the size in *n
const char *readmsstruct(initialisestructFunc initf,
readtokeninstructFunc f, void *p)
{
const char *err = 0;
char token[max_token_size];
initf(p);
err = readmsstructstart();
if (!err) { err = readmstoken(token); }
while (!err && strcmp(token, "}") != 0)
{
if (!err) { err = (*f)(p, token); }
if (!err) { err = readmstoken(token); }
}
return err;
}
This reads the inside of an arbitary structure. It calls initf() to
intialise the structure, and f() to read each value, passing
the name token to f().
const char *readmsbeam(void *p, const char *token)
{
const char *err = 0;
MScriptBeamNode *mbm = (MScriptBeamNode *)p;
if (0 == strcmp("id", token))
{
if (!err) { err = readmsint(&mbm->ID); }
}
else if (0 == strcmp("nofnodes", token))
{
if (!err) { err = readmsint(&mbm->nofnodes); }
}
else if (0 == strcmp("nofleft", token))
{
if (!err) { err = readmsint(&mbm->nofleft); }
}
else if (0 == strcmp("nofright", token))
{
if (!err) { err = readmsint(&mbm->nofright); }
}
else
{
if (!err) { err = mscriptskipvalue(token); } /* skip unknown fields. */
}
return err;
}
readmsbeam() is a typical function that is called by readmsstruct().
readmsint() reads an integer value.
Reading an array is very similar to reading a structure, but a bit more complex,
and you have to allocate memory.