Add support for parsing global variables, not just function declarations.

This commit is contained in:
Jordan Bancino 2023-04-27 20:04:15 +00:00
parent 9292f1d9da
commit a00ded6d06
5 changed files with 217 additions and 44 deletions

View file

@ -505,8 +505,8 @@ HeaderParse(Stream * stream, HeaderExpr * expr)
{ {
int i = wordLen; int i = wordLen;
expr->type = HP_DECLARATION; expr->type = HP_GLOBAL;
strncpy(expr->data.declaration.returnType, word, wordLimit); strncpy(expr->data.global.type, word, wordLimit);
if (strcmp(word, "struct") == 0 || if (strcmp(word, "struct") == 0 ||
strcmp(word, "enum") == 0 || strcmp(word, "enum") == 0 ||
@ -515,8 +515,9 @@ HeaderParse(Stream * stream, HeaderExpr * expr)
Free(word); Free(word);
word = HeaderConsumeWord(expr); word = HeaderConsumeWord(expr);
wordLen = strlen(word); wordLen = strlen(word);
expr->data.declaration.returnType[i] = ' '; expr->data.global.type[i] = ' ';
strncpy(expr->data.declaration.returnType + i + 1, word, wordLen + 1);
strncpy(expr->data.global.type + i + 1, word, wordLen + 1);
i += wordLen + 1; i += wordLen + 1;
} }
@ -525,13 +526,16 @@ HeaderParse(Stream * stream, HeaderExpr * expr)
c = HeaderConsumeWhitespace(expr); c = HeaderConsumeWhitespace(expr);
if (c == '*') if (c == '*')
{ {
expr->data.declaration.returnType[i] = ' '; expr->data.global.type[i] = ' ';
i++; i++;
expr->data.declaration.returnType[i] = '*'; expr->data.global.type[i] = '*';
i++; i++;
while ((c = HeaderConsumeWhitespace(expr)) == '*') while ((c = HeaderConsumeWhitespace(expr)) == '*')
{ {
expr->data.declaration.returnType[i] = c; expr->data.global.type[i] = c;
i++; i++;
} }
} }
@ -550,45 +554,94 @@ HeaderParse(Stream * stream, HeaderExpr * expr)
} }
else else
{ {
strncpy(expr->data.declaration.name, word, wordLimit); strncpy(expr->data.global.name, word, wordLimit);
Free(word); Free(word);
word = NULL; word = NULL;
c = HeaderConsumeWhitespace(expr); c = HeaderConsumeWhitespace(expr);
if (c != '(')
if (c == ';')
{
/* That's the end of the global. */
}
else if (c == '[')
{
/* Looks like we have an array. Slurp all the dimensions */
int block = 1;
int i = wordLen;
expr->data.global.name[i] = '[';
i++;
while (1)
{
if (i >= HEADER_EXPR_MAX - wordLen)
{
expr->type = HP_PARSE_ERROR;
expr->data.error.msg = "Memory limit exceeded while parsing global array.";
expr->data.error.lineNo = expr->state.lineNo;
return;
}
c = StreamGetc(expr->state.stream);
if (StreamEof(expr->state.stream) || StreamError(expr->state.stream))
{
expr->type = HP_SYNTAX_ERROR;
expr->data.error.msg = "Unterminated global array.";
expr->data.error.lineNo = expr->state.lineNo;
return;
}
if (c == ';')
{
expr->data.global.name[i] = '\0';
break;
}
else
{
expr->data.global.name[i] = c;
i++;
}
}
}
else if (c == '(')
{
expr->type = HP_DECLARATION;
expr->data.declaration.args = ArrayCreate();
do
{
word = HeaderConsumeArg(expr);
ArrayAdd(expr->data.declaration.args, word);
word = NULL;
}
while ((!StreamEof(expr->state.stream)) && ((c = HeaderConsumeWhitespace(expr)) != ')'));
if (StreamEof(expr->state.stream))
{
expr->type = HP_SYNTAX_ERROR;
expr->data.error.msg = "End of file reached before ')'.";
expr->data.error.lineNo = expr->state.lineNo;
return;
}
c = HeaderConsumeWhitespace(expr);
if (c != ';')
{
expr->type = HP_SYNTAX_ERROR;
expr->data.error.msg = "Expected ';'.";
expr->data.error.lineNo = expr->state.lineNo;
return;
}
}
else
{ {
expr->type = HP_SYNTAX_ERROR; expr->type = HP_SYNTAX_ERROR;
expr->data.error.msg = "Expected '('"; expr->data.error.msg = "Expected ';', '[', or '('";
expr->data.error.lineNo = expr->state.lineNo; expr->data.error.lineNo = expr->state.lineNo;
return; return;
} }
expr->data.declaration.args = ArrayCreate();
do
{
word = HeaderConsumeArg(expr);
ArrayAdd(expr->data.declaration.args, word);
word = NULL;
}
while ((!StreamEof(expr->state.stream)) && ((c = HeaderConsumeWhitespace(expr)) != ')'));
if (StreamEof(expr->state.stream))
{
expr->type = HP_SYNTAX_ERROR;
expr->data.error.msg = "End of file reached before ')'.";
expr->data.error.lineNo = expr->state.lineNo;
return;
}
c = HeaderConsumeWhitespace(expr);
if (c != ';')
{
expr->type = HP_SYNTAX_ERROR;
expr->data.error.msg = "Expected ';'.";
expr->data.error.lineNo = expr->state.lineNo;
return;
}
} }
} }
} }

View file

@ -36,6 +36,7 @@ typedef enum HeaderExprType
HP_PREPROCESSOR_DIRECTIVE, HP_PREPROCESSOR_DIRECTIVE,
HP_TYPEDEF, HP_TYPEDEF,
HP_DECLARATION, HP_DECLARATION,
HP_GLOBAL,
HP_SYNTAX_ERROR, HP_SYNTAX_ERROR,
HP_PARSE_ERROR, HP_PARSE_ERROR,
HP_EOF HP_EOF
@ -48,6 +49,12 @@ typedef struct HeaderDeclaration
Array *args; Array *args;
} HeaderDeclaration; } HeaderDeclaration;
typedef struct HeaderGlobal
{
char type[64];
char name[HEADER_EXPR_MAX - 64];
} HeaderGlobal;
typedef struct HeaderExpr typedef struct HeaderExpr
{ {
HeaderExprType type; HeaderExprType type;
@ -55,6 +62,7 @@ typedef struct HeaderExpr
{ {
char text[HEADER_EXPR_MAX]; char text[HEADER_EXPR_MAX];
HeaderDeclaration declaration; HeaderDeclaration declaration;
HeaderGlobal global;
struct struct
{ {
int lineNo; int lineNo;

View file

@ -24,6 +24,20 @@
#ifndef TELODENDRIA_H #ifndef TELODENDRIA_H
#define TELODENDRIA_H #define TELODENDRIA_H
/***
* @Nm Telodendria
* @Nd Branding and callback functions specific to Telodendria.
* @Dd March 12 2023
* @Xr Memory Log
*
* This API provides the callbacks used to hook Telodendria into the
* various other APIs. It exists primarily to be called by
* .Fn main ,
* but these functions are not static so
* .Fn main
* can be in a separate compilation unit.
*/
#include <Memory.h> #include <Memory.h>
#include <Log.h> #include <Log.h>
#include <HttpRouter.h> #include <HttpRouter.h>
@ -31,22 +45,64 @@
#define TELODENDRIA_LOGO_WIDTH 56 #define TELODENDRIA_LOGO_WIDTH 56
#define TELODENDRIA_LOGO_HEIGHT 22 #define TELODENDRIA_LOGO_HEIGHT 22
/**
* This holds the Telodendria ASCII art logo.
* .Va TELODENDRIA_LOGO_HEIGHT
* and
* .Va TELODENDRIA_LOGO_WIDTH
* are the sizes of each dimension of the two-dimensional array.
* .Va TELODENDRIA_LOGO_HEIGHT
* is the number of lines the logo contains, and
* .Va TELODENDRIA_LOGO_WIDTH
* is the number of characters in each line.
* .Pp
* .Sy NOTE:
* the Telodendria logo belongs solely to the Telodendria project. If
* this code is modified and distributed as something other than a
* packaging of the official Telodendria source package, the logo
* must be replaced with a different one, or removed entirely. Consult
* the licensing section of
* .Xr telodendria 7
* for details.
*/
extern const char extern const char
TelodendriaLogo[TELODENDRIA_LOGO_HEIGHT][TELODENDRIA_LOGO_WIDTH]; TelodendriaLogo[TELODENDRIA_LOGO_HEIGHT][TELODENDRIA_LOGO_WIDTH];
#define TELODENDRIA_HEADER_WIDTH 56 #define TELODENDRIA_HEADER_WIDTH 56
#define TELODENDRIA_HEADER_HEIGHT 6 #define TELODENDRIA_HEADER_HEIGHT 6
/**
* This holds the Telodendria ASCII art header. It follows the same
* conventions as the logo, but is not under any particular
* restrictions other than those spelled out in the license.
*/
extern const char extern const char
TelodendriaHeader[TELODENDRIA_HEADER_HEIGHT][TELODENDRIA_HEADER_WIDTH]; TelodendriaHeader[TELODENDRIA_HEADER_HEIGHT][TELODENDRIA_HEADER_WIDTH];
extern void /**
TelodendriaMemoryHook(MemoryAction, MemoryInfo *, void *); * This function follows the function prototype required by
* .Fn MemoryHook .
* It is executed every time an allocation, re-allocation, or free
* occurs, and is responsible for logging memory operations to the
* log.
*/
extern void TelodendriaMemoryHook(MemoryAction, MemoryInfo *, void *);
extern void /**
TelodendriaGenerateMemReport(void); * Generate a memory report in the current working directory. This
* function is intended to be called after all memory has supposedly
* been freed. It allocates no new memory of its own (except what is
* required by FILE pointers) and simply dumps all of the allocated
* memory out to a file if there is any memory allocated.
* .Pp
* This function is used to detect and fix memory leaks.
*/
extern void TelodendriaGenerateMemReport(void);
extern void /**
TelodendriaPrintHeader(void); * Print the logo and header, along with the copyright year and holder,
* and the version number, out to the global log.
*/
extern void TelodendriaPrintHeader(void);
#endif #endif

View file

@ -118,6 +118,7 @@ recipe_docs() {
if [ $(mod_time "$header") -ge $(mod_time "$man") ]; then if [ $(mod_time "$header") -ge $(mod_time "$man") ]; then
echo "DOC $basename" echo "DOC $basename"
if ! hdoc -D Os=Telodendria -i "$header" -o "$man"; then if ! hdoc -D Os=Telodendria -i "$header" -o "$man"; then
rm "$man"
exit 1 exit 1
fi fi
fi fi

View file

@ -46,6 +46,12 @@ typedef struct DocTypedef
char text[HEADER_EXPR_MAX]; char text[HEADER_EXPR_MAX];
} DocTypedef; } DocTypedef;
typedef struct DocGlobal
{
char docs[HEADER_EXPR_MAX];
HeaderGlobal global;
} DocGlobal;
static void static void
ParseMainBlock(HashMap * registers, Array * descr, char *comment) ParseMainBlock(HashMap * registers, Array * descr, char *comment)
{ {
@ -105,6 +111,9 @@ main(int argc, char **argv)
Array *typedefs = ArrayCreate(); Array *typedefs = ArrayCreate();
DocTypedef *type = NULL; DocTypedef *type = NULL;
Array *globals = ArrayCreate();
DocGlobal *global = NULL;
char comment[HEADER_EXPR_MAX]; char comment[HEADER_EXPR_MAX];
int isDocumented = 0; int isDocumented = 0;
@ -287,6 +296,24 @@ main(int argc, char **argv)
isDocumented = 0; isDocumented = 0;
} }
break; break;
case HP_GLOBAL:
if (!isDocumented)
{
StreamPrintf(StreamStderr(),
"Error: Global %s is undocumented.\n",
expr.data.global.name);
exit = EXIT_FAILURE;
goto finish;
}
else
{
global = Malloc(sizeof(DocGlobal));
global->global = expr.data.global;
strncpy(global->docs, comment, sizeof(global->docs));
ArrayAdd(globals, global);
isDocumented = 0;
}
break;
default: default:
StreamPrintf(StreamStderr(), "Unknown header type: %d\n", expr.type); StreamPrintf(StreamStderr(), "Unknown header type: %d\n", expr.type);
StreamPrintf(StreamStderr(), "This is a programming error.\n"); StreamPrintf(StreamStderr(), "This is a programming error.\n");
@ -352,6 +379,34 @@ last:
StreamPutc(out, '\n'); StreamPutc(out, '\n');
} }
if (ArraySize(globals))
{
StreamPrintf(out, ".Sh GLOBALS\n");
for (i = 0; i < ArraySize(globals); i++)
{
char *line;
global = ArrayGet(globals, i);
StreamPrintf(out, ".Ss %s %s\n", global->global.type, global->global.name);
line = strtok(global->docs, "\n");
while (line)
{
while (*line && (isspace(*line) || *line == '*'))
{
line++;
}
if (*line)
{
StreamPrintf(out, "%s\n", line);
}
line = strtok(NULL, "\n");
}
}
}
if (ArraySize(typedefs)) if (ArraySize(typedefs))
{ {
StreamPrintf(out, ".Sh TYPE DECLARATIONS\n"); StreamPrintf(out, ".Sh TYPE DECLARATIONS\n");