Refactor, getting rid of parsing modes (just use node types instead)

This commit is contained in:
Nathan Fisher 2023-11-04 00:19:11 -04:00
parent e69a1ceaa1
commit ae253cec83
2 changed files with 129 additions and 154 deletions

View file

@ -21,7 +21,7 @@ int gemtextParserInit(gemtextParser *parser, FILE *stream) {
int ret = 0;
parser->stream = stream;
parser->mode = normalMode;
parser->nodeType = unset;
parser->state = lineStart;
parser->linkUrl = NULL;
ret = lineBufferInit(&parser->buffer);
@ -42,9 +42,9 @@ gemtextParser* gemtextParserNew(FILE *stream) {
void gemtextParserDeinit(gemtextParser *parser) {
fclose(parser->stream);
free(parser->buffer.buf);
if (parser->mode == linkMode && parser->linkUrl != NULL) {
if (parser->nodeType == linkNode && parser->linkUrl != NULL) {
free(parser->linkUrl);
} else if (parser->mode == preformattedMode && parser->altText != NULL) {
} else if (parser->nodeType == preformattedNode && parser->altText != NULL) {
free(parser->altText);
}
}
@ -54,66 +54,66 @@ void gemtextParserDestroy(gemtextParser *parser) {
free(parser);
}
int gemtextNodeQueueInit(gemtextNodeQueue *queue) {
int gemtextNodeQueueInit(gemtextNodeQueue *nq) {
int ret;
queue->head = NULL;
queue->tail = NULL;
ret = pthread_mutex_init(&queue->mutex, NULL);
nq->head = NULL;
nq->tail = NULL;
ret = pthread_mutex_init(&nq->mutex, NULL);
if (ret != 0)
return ret;
return pthread_cond_init(&queue->cond, NULL);
return pthread_cond_init(&nq->cond, NULL);
}
void gemtextNodeQueuePush(gemtextNodeQueue *queue, gemtextNode *node) {
pthread_mutex_lock(&queue->mutex);
if (queue->tail == NULL) {
queue->tail = queue->head = node;
void gemtextNodeQueuePush(gemtextNodeQueue *nq, gemtextNode *node) {
pthread_mutex_lock(&nq->mutex);
if (nq->tail == NULL) {
nq->tail = nq->head = node;
} else {
node->next = queue->tail;
queue->tail->prev = node;
queue->tail = node;
node->next = nq->tail;
nq->tail->prev = node;
nq->tail = node;
}
queue->count++;
pthread_mutex_unlock(&queue->mutex);
nq->count++;
pthread_mutex_unlock(&nq->mutex);
}
gemtextNode* gemtextNodeQueuePop(gemtextNodeQueue *lq) {
gemtextNode* gemtextNodeQueuePop(gemtextNodeQueue *nq) {
gemtextNode *node;
while (lq->count == 0)
pthread_cond_wait(&lq->cond, &lq->mutex);
pthread_mutex_lock(&lq->mutex);
lq->count++;
node = lq->head;
while (nq->count == 0)
pthread_cond_wait(&nq->cond, &nq->mutex);
pthread_mutex_lock(&nq->mutex);
nq->count++;
node = nq->head;
if (node->nodeType == endOfStream)
return node;
if (lq->tail == lq->head) {
lq->tail = lq->head = NULL;
if (nq->tail == nq->head) {
nq->tail = nq->head = NULL;
} else {
lq->head = lq->head->prev;
nq->head = nq->head->prev;
}
pthread_mutex_unlock(&lq->mutex);
pthread_mutex_unlock(&nq->mutex);
node->prev = node->next = NULL;
return node;
}
gemtextNode* gemtextNodeQueueTryPop(gemtextNodeQueue *lq) {
gemtextNode* gemtextNodeQueueTryPop(gemtextNodeQueue *nq) {
gemtextNode *node;
if (lq->count == 0)
if (nq->count == 0)
return NULL;
pthread_mutex_lock(&lq->mutex);
lq->count++;
node = lq->head;
pthread_mutex_lock(&nq->mutex);
nq->count++;
node = nq->head;
if (node->nodeType == endOfStream)
return node;
if (lq->tail == lq->head) {
lq->tail = lq->head = NULL;
if (nq->tail == nq->head) {
nq->tail = nq->head = NULL;
} else {
lq->head = lq->head->prev;
nq->head = nq->head->prev;
}
pthread_mutex_unlock(&lq->mutex);
pthread_mutex_unlock(&nq->mutex);
node->prev = node->next = NULL;
return node;
}
@ -203,7 +203,7 @@ void lineBufferReset(lineBuffer *lb) {
lb->cursor = lb->buf;
}
int gemtextParserSendPreformatted(gemtextParser *parser, gemtextNodeQueue *lq) {
int gemtextParserSendPreformatted(gemtextParser *parser, gemtextNodeQueue *nq) {
preformattedBlock *block;
gemtextNode *node;
char *buf;
@ -223,14 +223,14 @@ int gemtextParserSendPreformatted(gemtextParser *parser, gemtextNodeQueue *lq) {
parser->altText = NULL;
block->body = buf;
node->block = block;
gemtextNodeQueuePush(lq, node);
gemtextNodeQueuePush(nq, node);
lineBufferReset(&parser->buffer);
parser->state = lineStart;
parser->mode = normalMode;
parser->nodeType = unset;
return 0;
}
int gemtextParserSendLink(gemtextParser *parser, gemtextNodeQueue *lq) {
int gemtextParserSendLink(gemtextParser *parser, gemtextNodeQueue *nq) {
gemtextLink *link;
gemtextNode *node;
char *url = NULL, *display = NULL;
@ -258,28 +258,28 @@ int gemtextParserSendLink(gemtextParser *parser, gemtextNodeQueue *lq) {
link->display = display;
node->nodeType = linkNode;
node->link = link;
gemtextNodeQueuePush(lq, node);
gemtextNodeQueuePush(nq, node);
lineBufferReset(&parser->buffer);
parser->state = lineStart;
parser->mode = normalMode;
parser->nodeType = unset;
parser->linkUrl = NULL;
return 0;
}
int gemtextParserSend(gemtextParser *parser, gemtextNodeType lt, gemtextNodeQueue *lq) {
int gemtextParserSend(gemtextParser *parser, gemtextNodeQueue *nq) {
gemtextNode *node;
char *buf;
node = calloc(1, sizeof(gemtextNode));
if (node == NULL) return errno;
node->nodeType = lt;
node->nodeType = parser->nodeType;
buf = strndup(parser->buffer.buf, parser->buffer.len);
if (buf == NULL) return errno;
node->str = buf;
gemtextNodeQueuePush(lq, node);
gemtextNodeQueuePush(nq, node);
lineBufferReset(&parser->buffer);
parser->state = lineStart;
parser->mode = normalMode;
parser->nodeType = unset;
return 0;
}
@ -287,7 +287,7 @@ void logParseError(int err) {
//todo
}
void switchMode(gemtextParser *parser, gemtextParserMode mode, char c) {
void switchMode(gemtextParser *parser, gemtextNodeType node_type, char c) {
lineBufferReset(&parser->buffer);
switch (c) {
case ' ':
@ -299,11 +299,11 @@ void switchMode(gemtextParser *parser, gemtextParserMode mode, char c) {
lineBufferAppendCharUnchecked(&parser->buffer, c);
parser->state = normalState;
}
parser->mode = mode;
parser->nodeType = node_type;
}
void enterPreformattedMode(gemtextParser *parser) {
parser->mode = preformattedMode;
parser->nodeType = preformattedNode;
parser->state = trimStart;
lineBufferReset(&parser->buffer);
}
@ -312,7 +312,7 @@ int parseLink(gemtextParser *parser, gemtextNodeQueue *lq, char c) {
int ret = 0;
char *buf = NULL;
assert(parser->mode == linkMode);
assert(parser->nodeType == linkNode);
switch (parser->state) {
case lineStart:
if (c != ' ' && c != '\t') {
@ -320,7 +320,7 @@ int parseLink(gemtextParser *parser, gemtextNodeQueue *lq, char c) {
lineBufferAppendCharUnchecked(&parser->buffer, c);
parser->state = normalState;
} else if (c == '\n') {
ret = gemtextParserSend(parser, normalNode, lq);
ret = gemtextParserSend(parser, lq);
}
break;
case normalState:
@ -360,7 +360,7 @@ int parseLink(gemtextParser *parser, gemtextNodeQueue *lq, char c) {
int parsePreformatted(gemtextParser *parser, gemtextNodeQueue *lq, char c) {
char *buf = NULL;
assert(parser->mode == preformattedMode);
assert(parser->nodeType == preformattedNode);
switch (parser->state) {
case trimStart:
if (c == '\n') {
@ -437,12 +437,12 @@ int parseQuote(gemtextParser *parser, gemtextNodeQueue *lq, char c) {
lineBufferRewind(&parser->buffer);
} else {
lineBufferRewind(&parser->buffer);
ret = gemtextParserSend(parser, quoteNode, lq);
ret = gemtextParserSend(parser, lq);
if (ret) return ret;
ret = fseek(parser->stream, -1, SEEK_CUR);
if (ret) return ret;
parser->state = lineStart;
parser->mode = normalMode;
parser->nodeType = normalNode;
}
break;
case normalState:
@ -456,7 +456,10 @@ int parseQuote(gemtextParser *parser, gemtextNodeQueue *lq, char c) {
parser->buffer.len--;
parser->buffer.cursor--;
} else if (c == '\n') {
ret = gemtextParserSend(parser, normalNode, lq);
if (parser->buffer.len == 1) {
parser->nodeType = normalNode;
}
ret = gemtextParserSend(parser, lq);
} else {
parser->state = normalState;
}
@ -469,7 +472,7 @@ int parseQuote(gemtextParser *parser, gemtextNodeQueue *lq, char c) {
return ret;
}
int parseGeneric(gemtextParser *parser, gemtextNodeQueue *lq, gemtextNodeType lt, char c) {
int parseGeneric(gemtextParser *parser, gemtextNodeQueue *nq, char c) {
int ret = 0;
switch (parser->state) {
@ -480,14 +483,14 @@ int parseGeneric(gemtextParser *parser, gemtextNodeQueue *lq, gemtextNodeType lt
parser->buffer.len--;
parser->buffer.cursor--;
} else if (c == '\n') {
ret = gemtextParserSend(parser, lt, lq);
ret = gemtextParserSend(parser, nq);
} else {
parser->state = normalState;
}
break;
case normalState:
if (c == '\n') {
ret = gemtextParserSend(parser, lt, lq);
ret = gemtextParserSend(parser, nq);
}
break;
default:
@ -498,7 +501,7 @@ int parseGeneric(gemtextParser *parser, gemtextNodeQueue *lq, gemtextNodeType lt
return ret;
}
int parseNormal(gemtextParser *parser, gemtextNodeQueue *lq, char c) {
int parseNormal(gemtextParser *parser, gemtextNodeQueue *nq, char c) {
int ret;
switch (parser->state) {
@ -508,12 +511,12 @@ int parseNormal(gemtextParser *parser, gemtextNodeQueue *lq, char c) {
parser->state = firstLinkChar;
break;
case '>':
parser->mode = quoteMode;
parser->nodeType = quoteNode;
parser->state = trimStart;
lineBufferRewind(&parser->buffer);
break;
case '*':
parser->mode = listMode;
parser->nodeType = listNode;
parser->state = trimStart;
lineBufferRewind(&parser->buffer);
break;
@ -524,7 +527,8 @@ int parseNormal(gemtextParser *parser, gemtextNodeQueue *lq, char c) {
parser->state = firstBacktickChar;
break;
case '\n':
ret = gemtextParserSend(parser, normalNode, lq);
parser->nodeType = normalNode;
ret = gemtextParserSend(parser, nq);
if (ret) return ret;
break;
default:
@ -533,10 +537,11 @@ int parseNormal(gemtextParser *parser, gemtextNodeQueue *lq, char c) {
break;
case firstLinkChar:
if (c == '>') {
parser->mode = linkMode;
parser->nodeType = linkNode;
parser->state = lineStart;
} else if (c == '\n') {
ret = gemtextParserSend(parser, normalNode, lq);
parser->nodeType = normalNode;
ret = gemtextParserSend(parser, nq);
if (ret) return ret;
} else {
parser->state = normalState;
@ -546,52 +551,57 @@ int parseNormal(gemtextParser *parser, gemtextNodeQueue *lq, char c) {
if (c == '#') {
parser->state = secondHashChar;
} else if (c == '\n') {
ret = gemtextParserSend(parser, normalNode, lq);
parser->nodeType = normalNode;
ret = gemtextParserSend(parser, nq);
if (ret) return ret;
} else {
switchMode(parser, h1Mode, c);
switchMode(parser, h1Node, c);
}
break;
case secondHashChar:
if (c == '#') {
parser->mode = h3Mode;
parser->nodeType = h3Node;
parser->state = trimStart;
lineBufferReset(&parser->buffer);
} else if (c == '\n') {
ret = gemtextParserSend(parser, normalNode, lq);
parser->nodeType = normalNode;
ret = gemtextParserSend(parser, nq);
if (ret) return ret;
} else {
switchMode(parser, h2Mode, c);
switchMode(parser, h2Node, c);
}
break;
case thirdHashChar:
if (c == '\n') {
ret = gemtextParserSend(parser, normalNode, lq);
parser->nodeType = normalNode;
ret = gemtextParserSend(parser, nq);
if (ret) return ret;
} else {
switchMode(parser, h3Mode, c);
switchMode(parser, h3Node, c);
}
break;
case firstBacktickChar:
if (c == '\n') {
ret = gemtextParserSend(parser, normalNode, lq);
parser->nodeType = normalNode;
ret = gemtextParserSend(parser, nq);
if (ret) return ret;
} else if (c == '`') {
parser->state = secondBacktickChar;
} else {
parser->state = normalState;
parser->mode = normalMode;
parser->nodeType = normalNode;
}
break;
case secondBacktickChar:
if (c == '`') {
enterPreformattedMode(parser);
} else if (c == '\n') {
ret = gemtextParserSend(parser, normalNode, lq);
parser->nodeType = normalNode;
ret = gemtextParserSend(parser, nq);
if (ret) return ret;
} else {
parser->state = normalState;
parser->mode = normalMode;
parser->nodeType = normalNode;
}
break;
default:
@ -600,7 +610,7 @@ int parseNormal(gemtextParser *parser, gemtextNodeQueue *lq, char c) {
return 0;
}
int parseGemtext(gemtextParser *parser, gemtextNodeQueue *lq) {
int parseGemtext(gemtextParser *parser, gemtextNodeQueue *nq) {
char c;
int ret;
gemtextNode *node;
@ -615,30 +625,15 @@ int parseGemtext(gemtextParser *parser, gemtextNodeQueue *lq) {
}
} else {
if (parser->state != lineStart && parser->state != trimStart) {
switch (parser->mode) {
case normalMode:
ret = gemtextParserSend(parser, normalNode, lq);
switch (parser->nodeType) {
case preformattedNode:
ret = gemtextParserSendPreformatted(parser, nq);
break;
case preformattedMode:
ret = gemtextParserSendPreformatted(parser, lq);
case linkNode:
ret = gemtextParserSendLink(parser, nq);
break;
case quoteMode:
ret = gemtextParserSend(parser, quoteNode, lq);
break;
case linkMode:
ret = gemtextParserSendLink(parser, lq);
break;
case h1Mode:
ret = gemtextParserSend(parser, h1Node, lq);
break;
case h2Mode:
ret = gemtextParserSend(parser, h2Node, lq);
break;
case h3Mode:
ret = gemtextParserSend(parser, h3Node, lq);
break;
case listMode:
ret = gemtextParserSend(parser, listNode, lq);
default:
ret = gemtextParserSend(parser, nq);
break;
}
if (ret) return ret;
@ -648,33 +643,25 @@ int parseGemtext(gemtextParser *parser, gemtextNodeQueue *lq) {
node->nodeType = endOfStream;
node->prev = node->next = NULL;
node->str = NULL;
gemtextNodeQueuePush(lq, node);
gemtextNodeQueuePush(nq, node);
break;
}
switch (parser->mode) {
case normalMode:
ret = parseNormal(parser, lq, c);
switch (parser->nodeType) {
case unset:
case normalNode:
ret = parseNormal(parser, nq, c);
break;
case preformattedMode:
ret = parsePreformatted(parser, lq, c);
case preformattedNode:
ret = parsePreformatted(parser, nq, c);
break;
case quoteMode:
ret = parseQuote(parser, lq, c);
case quoteNode:
ret = parseQuote(parser, nq, c);
break;
case linkMode:
ret = parseLink(parser, lq, c);
case linkNode:
ret = parseLink(parser, nq, c);
break;
case h1Mode:
ret = parseGeneric(parser, lq, h1Node, c);
break;
case h2Mode:
ret = parseGeneric(parser, lq, h2Node, c);
break;
case h3Mode:
ret = parseGeneric(parser, lq, h3Node, c);
break;
case listMode:
ret = parseGeneric(parser, lq, listNode, c);
default:
ret = parseGeneric(parser, nq, c);
break;
}
if (ret) {

View file

@ -10,19 +10,6 @@
#define LBUF_SIZE 512 ///< The default size of a lineBuffer
/** The main modes which the parser can operate in */
typedef enum {
normalMode, /**< A normal text line is being parsed, or the parser is
still determining the line type */
preformattedMode, ///< A Preformatted block is being parsed
quoteMode, ///< A Quote block is being parsed
linkMode, ///< A hyperlink is being parsed
h1Mode, ///< An H1 heading is being parsed
h2Mode, ///< An H2 heading is being parsed
h3Mode, ///< An H3 heading is being parsed
listMode, ///< A list member is being parsed
} gemtextParserMode;
/** An enumeration representing the state of the parsing action. These values
* are to be taken in context with the current gemtextParserMode */
typedef enum {
@ -47,15 +34,16 @@ typedef enum {
* An enum type representing the various line types in gemtext markup
*/
typedef enum {
normalNode = 0, ///< A normal text line
linkNode = 1, ///< A link line
listNode = 2, ///< A list member
h1Node = 3, ///< An H1 heading
h2Node = 4, ///< An H2 heading
h3Node = 5, ///< An H3 heading
preformattedNode = 6, ///< A preformatted text block
quoteNode = 7, ///< A Quote block
endOfStream = 8, /**< Notifies the receiver that the stream is over and no
unset = 0, ///< The node type has not yet been set
normalNode = 1, ///< A normal text line
linkNode = 2, ///< A link line
listNode = 3, ///< A list member
h1Node = 4, ///< An H1 heading
h2Node = 5, ///< An H2 heading
h3Node = 6, ///< An H3 heading
preformattedNode = 7, ///< A preformatted text block
quoteNode = 8, ///< A Quote block
endOfStream = 9, /**< Notifies the receiver that the stream is over and no
more lines are to be expected */
} gemtextNodeType;
@ -91,7 +79,7 @@ typedef struct {
*/
typedef struct {
FILE *stream; /**< A stream of bytes to read gemtext from */
gemtextParserMode mode; /**< The current parsing mode */
gemtextNodeType nodeType; /**< The current parsing mode */
gemtextParserState state; /**< The state of the parser within each mode */
lineBuffer buffer; /**< The internal buffer used to store bytes until
a gemtextLine is ready to be sent */
@ -170,31 +158,31 @@ void gemtextParserDestroy(gemtextParser *parser);
* ### Return values
* Returns 0 on success. If there is a failure initializing the internal
* mutex or condition variable, an error code is returned instead.
* \param queue The already allocated gemtextNodeQueue
* \param nq The already allocated gemtextNodeQueue
*/
int gemtextNodeQueueInit(gemtextNodeQueue *queue);
int gemtextNodeQueueInit(gemtextNodeQueue *nq);
/**
* Pushes a gemtextLine into the queue. This function will not fail, but
* can block if another thread holds the gemtextQueue's internal mutex.
* \param queue The queue which will receive the gemtext line
* \param line The gemtextLine to be queued
* \param nq The queue which will receive the gemtext line
* \param node The gemtextNode to be queued
*/
void gemtextNodeQueuePush(gemtextNodeQueue *queue, gemtextNode *node);
void gemtextNodeQueuePush(gemtextNodeQueue *nq, gemtextNode *node);
/**
* Gets the oldest line inserted in the queue. This function will either
* return a valid gemtextLine or block until one becomes available.
* \param lq The queue from which we are attempting to pop a line
* \param nq The queue from which we are attempting to pop a line
*/
gemtextNode* gemtextNodeQueuePop(gemtextNodeQueue *lq);
gemtextNode* gemtextNodeQueuePop(gemtextNodeQueue *nq);
/**
* Attempts to get the oldest line inserted in the queue. If there are no lines
* left in the queue, returns NULL.
* \param lq The queue from which we are attempting to pop a line
* \param nq The queue from which we are attempting to pop a line
*/
gemtextNode* gemtextNodeQueueTryPop(gemtextNodeQueue *lq);
gemtextNode* gemtextNodeQueueTryPop(gemtextNodeQueue *nq);
/**
* Frees all memory associated with a gemtextLine structure
@ -258,8 +246,8 @@ void lineBufferReset(lineBuffer *lb);
* ### Return values
* Returns 0 on success, any other number is an error code
* \param parser A gemtextParser struct used to maintain state while parsing
* \param lq A gemtextNodeQueue which will receive gemtextLine elements as they are parsed
* \param nq A gemtextNodeQueue which will receive gemtextLine elements as they are parsed
*/
int parseGemtext(gemtextParser *parser, gemtextNodeQueue *lq);
int parseGemtext(gemtextParser *parser, gemtextNodeQueue *nq);
#endif