Initial import
This commit is contained in:
26
.gitignore
vendored
Normal file
26
.gitignore
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
Makefile
|
||||
Makefile.bak
|
||||
Makefile.in
|
||||
aclocal.m4
|
||||
autom4te.cache/
|
||||
config.h
|
||||
config.h.in
|
||||
config.h.in~
|
||||
config.log
|
||||
config.status
|
||||
configure
|
||||
depcomp
|
||||
install-sh
|
||||
missing
|
||||
src/.deps/
|
||||
src/Makefile
|
||||
src/Makefile.in
|
||||
src/sermon
|
||||
src/sermon_lexer.c
|
||||
src/sermon_lexer.o
|
||||
src/sermon_parser.c
|
||||
src/sermon_parser.h
|
||||
src/sermon_parser.o
|
||||
src/sermon_util.o
|
||||
stamp-h1
|
||||
ylwrap
|
||||
2
Makefile.am
Normal file
2
Makefile.am
Normal file
@@ -0,0 +1,2 @@
|
||||
SUBDIRS = src
|
||||
dist_doc_DATA = README
|
||||
11
configure.ac
Normal file
11
configure.ac
Normal file
@@ -0,0 +1,11 @@
|
||||
AC_INIT([sermon], [0.9], [david.a.baer@gmail.com])
|
||||
AM_INIT_AUTOMAKE([-Wall -Werror foreign])
|
||||
AC_PROG_CC
|
||||
AC_PROG_LEX
|
||||
AC_PROG_YACC
|
||||
AC_CONFIG_HEADERS([config.h])
|
||||
AC_CONFIG_FILES([
|
||||
Makefile
|
||||
src/Makefile
|
||||
])
|
||||
AC_OUTPUT
|
||||
5
src/Makefile.am
Normal file
5
src/Makefile.am
Normal file
@@ -0,0 +1,5 @@
|
||||
bin_PROGRAMS = sermon
|
||||
BUILT_SOURCES = sermon_lexer.c sermon_parser.c sermon_parser.h
|
||||
AM_YFLAGS = -d --location
|
||||
sermon_SOURCES = sermon_lexer.l sermon_parser.y sermon_util.c
|
||||
LIBS = $(LEXLIB)
|
||||
99
src/queue.h
Normal file
99
src/queue.h
Normal file
@@ -0,0 +1,99 @@
|
||||
/***************************************************************************
|
||||
* queue.h - macros for defining a double-ended queue
|
||||
*
|
||||
* Copyright (C) 2015 by David A. Baer, All Rights Reserved
|
||||
*
|
||||
* Description:
|
||||
*
|
||||
* These macros are designed to make it easier to process input whose
|
||||
* length is unknown before it terminates (e.g. a series of numbers).
|
||||
* The APPEND_QUEUE macro adds a new element to the list. The
|
||||
* QUEUE_LENGTH macro provides the current length of the queue. The
|
||||
* QUEUE_TO_ARRAY macro can convert the queue into a more compact
|
||||
* array.
|
||||
*
|
||||
* Usage:
|
||||
*
|
||||
* DEFINE_QUEUE(MyType, QueueOfMyType);
|
||||
* NEW_QUEUE(QueueOfMyType, myq);
|
||||
* APPEND_QUEUE(QueueOfMyType, myq, VALUE1);
|
||||
* APPEND_QUEUE(QueueOfMyType, myq, VALUE2);
|
||||
* ...
|
||||
* FOREACH_QUEUE(QueueOfMyType, myq, iterator) {
|
||||
* (some action)
|
||||
* }
|
||||
* FOREACH_QUEUE_END <--- NOTE: THIS IS MANDATORY
|
||||
* len = QUEUE_LENGTH(myq);
|
||||
* QUEUE_TO_ARRAY(QueueOfMyType, myq, MyType, targetptr);
|
||||
*
|
||||
*
|
||||
*/
|
||||
#ifndef _QUEUE_H
|
||||
#define _QUEUE_H
|
||||
#include <stdlib.h>
|
||||
|
||||
#define DEFINE_QUEUE(T, N) \
|
||||
struct _##N##Node { \
|
||||
T data; \
|
||||
struct _##N##Node* next; \
|
||||
};\
|
||||
typedef struct { \
|
||||
int length; \
|
||||
struct _##N##Node *head, *tail; \
|
||||
} N
|
||||
|
||||
#define NEW_QUEUE(T, N) \
|
||||
T N = (T) { .length = 0, .head = NULL, .tail = NULL }
|
||||
|
||||
#define APPEND_QUEUE(T, Q, E) { \
|
||||
struct _##T##Node* n = (struct _##T##Node*)malloc(sizeof(struct _##T##Node)); \
|
||||
if (!n) { perror ("Could not allocate space for new queue element."); exit(1); } \
|
||||
n->data = E; \
|
||||
n->next = NULL; \
|
||||
if ((Q).head == NULL) (Q).head = n; \
|
||||
if ((Q).tail != NULL) (Q).tail->next = n; \
|
||||
(Q).tail = n; \
|
||||
(Q).length++; \
|
||||
}
|
||||
|
||||
#define QUEUE_LENGTH(Q) Q.length
|
||||
|
||||
#define FOREACH_QUEUE(T, Q, N) { \
|
||||
struct _##T##Node* N = NULL; \
|
||||
for (N = (Q).head; N != NULL; N = (N)->next)
|
||||
|
||||
#define FOREACH_QUEUE_END }
|
||||
|
||||
#define DESTROY_QUEUE(T, Q) { \
|
||||
struct _##T##Node* ptr = (Q).head; \
|
||||
while (ptr != NULL) { \
|
||||
struct _##T##Node* temp = ptr->next; \
|
||||
free(ptr); \
|
||||
ptr = temp; \
|
||||
} \
|
||||
(Q).head = (Q).tail = NULL; \
|
||||
(Q).length = 0; \
|
||||
}
|
||||
|
||||
#define DESTROY_QUEUE_FUNC(T, Q, F) { \
|
||||
struct _##T##Node* ptr = (Q).head; \
|
||||
while (ptr != NULL) { \
|
||||
struct _##T##Node* temp = ptr->next; \
|
||||
F(ptr->data); \
|
||||
free(ptr); \
|
||||
ptr = temp; \
|
||||
} \
|
||||
(Q).head = (Q).tail = NULL; \
|
||||
(Q).length = 0; \
|
||||
}
|
||||
|
||||
#define QUEUE_TO_ARRAY(QT, Q, T, DST) { \
|
||||
int i = 0; \
|
||||
DST = (T*)calloc(Q.length,sizeof(T)); \
|
||||
if (!DST) { perror("Could not allocate space for array."); exit(1); } \
|
||||
FOREACH_QUEUE(QT, Q, ptr) \
|
||||
DST[i++] = ptr->data; \
|
||||
FOREACH_QUEUE_END; \
|
||||
}
|
||||
|
||||
#endif /* !def _QUEUE_H */
|
||||
41
src/sermon.h
Normal file
41
src/sermon.h
Normal file
@@ -0,0 +1,41 @@
|
||||
#ifndef _SERMON_H
|
||||
#define _SERMON_H
|
||||
|
||||
typedef struct {
|
||||
char* headerType;
|
||||
char* headerValue;
|
||||
} SermonHeader;
|
||||
|
||||
typedef enum {
|
||||
PARA_DEFAULT,
|
||||
PARA_BLOCKQUOTE
|
||||
} SermonParagraphType;
|
||||
|
||||
typedef struct {
|
||||
SermonParagraphType paraType;
|
||||
char* paraText;
|
||||
} SermonParagraph;
|
||||
|
||||
typedef struct {
|
||||
char* refId;
|
||||
char* refText;
|
||||
} SermonReference;
|
||||
|
||||
typedef struct {
|
||||
/* header fields */
|
||||
char* sermonTitle;
|
||||
char* sermonAuthor;
|
||||
char* sermonDate;
|
||||
char* sermonOccasion;
|
||||
char* sermonText;
|
||||
|
||||
int numParagraphs;
|
||||
SermonParagraph* sermonParagraphs;
|
||||
int numReferences;
|
||||
SermonReference* sermonReferences;
|
||||
} Sermon;
|
||||
|
||||
void InitSermon(Sermon* srm);
|
||||
void FreeSermon(Sermon* srm);
|
||||
|
||||
#endif /* !def _SERMON_H */
|
||||
35
src/sermon_lexer.l
Normal file
35
src/sermon_lexer.l
Normal file
@@ -0,0 +1,35 @@
|
||||
%{
|
||||
#include <string.h>
|
||||
#include "sermon_parser.h"
|
||||
|
||||
#ifdef LEXDEBUG
|
||||
#define LEXPRINT(x...) fprintf(stderr, x);
|
||||
#else
|
||||
#define LEXPRINT(x...)
|
||||
#endif
|
||||
%}
|
||||
%s HEADER HEADERVAL REFERENCEINTRO REFERENCENAME REFERENCE BLOCK
|
||||
WHITESPACE [ \t]
|
||||
ID [A-Za-z_][A-Za-z_0-9]*
|
||||
%%
|
||||
<INITIAL>^[[]\*[^*]*\*\] { /* comment */ LEXPRINT("Comment: %s\n", yytext); }
|
||||
<INITIAL>^[[] { BEGIN(HEADER); yylloc.first_column = yylloc.last_column = 1; return '['; }
|
||||
<HEADER,HEADERVAL>\] { BEGIN(INITIAL); yylloc.first_column = ++yylloc.last_column; return ']'; }
|
||||
<HEADER>[^:]* { yylloc.first_column = yylloc.last_column + 1; yylloc.last_column = yylloc.first_column + strlen(yytext) - 1; yylval.sval = strdup(yytext); return HEADERTYPE; }
|
||||
<HEADER>:{WHITESPACE}* { BEGIN(HEADERVAL); yylloc.first_column = yylloc.last_column + 1; yylloc.last_column = yylloc.first_column + strlen(yytext) - 1; return ':'; }
|
||||
<HEADERVAL>[^\]]* { yylloc.first_column = yylloc.last_column + 1; yylloc.last_column = yylloc.first_column + strlen(yytext) - 1; yylval.sval = strdup(yytext); return HEADERVALUE; }
|
||||
<INITIAL>^\{ { BEGIN(REFERENCEINTRO); yylloc.first_column = yylloc.last_column = 1; return '{'; }
|
||||
<REFERENCEINTRO>ref { yylloc.first_column = yylloc.last_column + 1; yylloc.last_column = yylloc.first_column + 2; return KW_REF; }
|
||||
<REFERENCEINTRO>: { BEGIN(REFERENCENAME); yylloc.first_column = ++yylloc.last_column; return ':'; }
|
||||
<REFERENCENAME>{ID} { yylloc.first_column = yylloc.last_column + 1; yylloc.last_column = yylloc.first_column + strlen(yytext) + 1; yylval.sval = strdup(yytext); return ID; }
|
||||
<REFERENCENAME>: { BEGIN(REFERENCE); yylloc.first_column = ++yylloc.last_column; return ':'; }
|
||||
<REFERENCE>[^}]* { yylloc.first_column = yylloc.last_column + 1; yylloc.last_column = yylloc.first_column + strlen(yytext) + 1; yylval.sval = strdup(yytext); return REFTEXT; }
|
||||
<REFERENCE>[}] { BEGIN(INITIAL); yylloc.first_column = ++yylloc.last_column; return '}'; }
|
||||
^[^[{>\n].* { yylloc.first_column = ++yylloc.last_column; yylval.sval = strdup(yytext); LEXPRINT("LINE: %s\n", yytext); return LINE; }
|
||||
^>{WHITESPACE}*\n { yylloc.first_column = yylloc.last_column = 0; yylloc.first_line++; yylloc.last_line++; return '\n'; }
|
||||
^>{WHITESPACE}* { BEGIN(BLOCK); yylloc.first_column = yylloc.last_column = 1; return '>'; }
|
||||
<BLOCK>..* { BEGIN(INITIAL); yylloc.first_column = yylloc.last_column + 1; yylloc.last_column = yylloc.first_column + strlen(yytext) - 1; yylval.sval = strdup(yytext); return LINE; }
|
||||
<INITIAL>\n { yylloc.first_column = yylloc.last_column = 0; yylloc.first_line++; yylloc.last_line++; return '\n'; }
|
||||
<<EOF>> { return 0; }
|
||||
%%
|
||||
|
||||
163
src/sermon_parser.y
Normal file
163
src/sermon_parser.y
Normal file
@@ -0,0 +1,163 @@
|
||||
%{
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "queue.h"
|
||||
#include "sermon.h"
|
||||
|
||||
#ifdef PARSE_DEBUG
|
||||
#define PARSEPRINT(x...) fprintf(stderr, x);
|
||||
#else
|
||||
#define PARSEPRINT(x...)
|
||||
#endif
|
||||
|
||||
int yylex();
|
||||
extern char* yytext;
|
||||
extern FILE* yyin;
|
||||
|
||||
DEFINE_QUEUE(char *, LineQueue);
|
||||
DEFINE_QUEUE(SermonParagraph, ParagraphQueue);
|
||||
DEFINE_QUEUE(SermonReference, ReferenceQueue);
|
||||
|
||||
NEW_QUEUE(LineQueue, lineQ);
|
||||
NEW_QUEUE(ParagraphQueue, paragraphQ);
|
||||
NEW_QUEUE(ReferenceQueue, referenceQ);
|
||||
|
||||
char* lineQueueToString(LineQueue* lq) {
|
||||
int paraLength = 0, idx = 0;
|
||||
char *dest = NULL;
|
||||
FOREACH_QUEUE(LineQueue, *lq, ptr)
|
||||
paraLength += strlen(ptr->data) + 1;
|
||||
FOREACH_QUEUE_END
|
||||
paraLength--;
|
||||
dest = (char*)malloc(paraLength + 1);
|
||||
memset(dest, 0, paraLength + 1);
|
||||
FOREACH_QUEUE(LineQueue, *lq, ptr) {
|
||||
strncat(dest + idx, ptr->data, paraLength - idx);
|
||||
idx += strlen(ptr->data);
|
||||
if (ptr->next != NULL) dest[idx++] = ' ';
|
||||
free(ptr->data);
|
||||
}
|
||||
FOREACH_QUEUE_END
|
||||
dest[idx] = '\0';
|
||||
return dest;
|
||||
}
|
||||
|
||||
void yyerror(Sermon*, const char*);
|
||||
|
||||
%}
|
||||
%union {
|
||||
char* sval;
|
||||
char cval;
|
||||
}
|
||||
|
||||
%token <sval> HEADERTYPE
|
||||
%token <sval> HEADERVALUE
|
||||
%token <sval> ID
|
||||
%token <sval> REFTEXT
|
||||
%token <sval> LINE
|
||||
%token KW_REF
|
||||
%parse-param {Sermon* sermon}
|
||||
%initial-action {
|
||||
};
|
||||
%%
|
||||
sermon:
|
||||
headerlist sermontext references {
|
||||
sermon->numParagraphs = QUEUE_LENGTH(paragraphQ);
|
||||
sermon->numReferences = QUEUE_LENGTH(referenceQ);
|
||||
if (sermon->numParagraphs) QUEUE_TO_ARRAY(ParagraphQueue, paragraphQ, SermonParagraph, sermon->sermonParagraphs);
|
||||
if (sermon->numReferences) QUEUE_TO_ARRAY(ReferenceQueue, referenceQ, SermonReference, sermon->sermonReferences);
|
||||
DESTROY_QUEUE(ParagraphQueue, paragraphQ);
|
||||
DESTROY_QUEUE(ReferenceQueue, referenceQ);
|
||||
}
|
||||
;
|
||||
break:
|
||||
break '\n'
|
||||
| /* empty */
|
||||
;
|
||||
headerlist:
|
||||
headerlist header break
|
||||
| /* empty */
|
||||
;
|
||||
header:
|
||||
'[' HEADERTYPE ':' HEADERVALUE ']' {
|
||||
PARSEPRINT("Parsed header %s\n", $2);
|
||||
if (strcmp($2, "title") == 0) { sermon->sermonTitle = $4; }
|
||||
else if (strcmp($2, "author") == 0) { sermon->sermonAuthor = $4; }
|
||||
else if (strcmp($2, "text") == 0) { sermon->sermonText = $4; }
|
||||
else if (strcmp($2, "occasion") == 0) { sermon->sermonOccasion = $4; }
|
||||
else if (strcmp($2, "date") == 0) { sermon->sermonDate = $4; }
|
||||
else { free($4); }
|
||||
free($2);
|
||||
}
|
||||
;
|
||||
sermontext:
|
||||
sermontext block break
|
||||
| /* empty */
|
||||
;
|
||||
block:
|
||||
para {
|
||||
SermonParagraph p = {
|
||||
.paraType = PARA_DEFAULT,
|
||||
.paraText = lineQueueToString(&lineQ)
|
||||
};
|
||||
APPEND_QUEUE(ParagraphQueue, paragraphQ, p);
|
||||
PARSEPRINT("Parsed paragraph:\n%s\n\n", p.paraText);
|
||||
}
|
||||
| blockquote {
|
||||
SermonParagraph p = {
|
||||
.paraType = PARA_BLOCKQUOTE,
|
||||
.paraText = lineQueueToString(&lineQ)
|
||||
};
|
||||
APPEND_QUEUE(ParagraphQueue, paragraphQ, p);
|
||||
PARSEPRINT("Parsed paragraph:\n%s\n\n", p.paraText);
|
||||
}
|
||||
;
|
||||
para:
|
||||
para LINE '\n' { APPEND_QUEUE(LineQueue, lineQ, $2); }
|
||||
| LINE '\n' { DESTROY_QUEUE(LineQueue, lineQ); APPEND_QUEUE(LineQueue, lineQ, $1); }
|
||||
;
|
||||
blockquote:
|
||||
blockquote '>' LINE '\n' { APPEND_QUEUE(LineQueue, lineQ, $3); }
|
||||
| '>' LINE '\n' { DESTROY_QUEUE(LineQueue, lineQ); APPEND_QUEUE(LineQueue, lineQ, $2); }
|
||||
;
|
||||
references:
|
||||
references reference break
|
||||
| /* empty */
|
||||
;
|
||||
reference:
|
||||
'{' KW_REF ':' ID ':' REFTEXT '}' { SermonReference r = { .refId = $4, .refText = $6 }; APPEND_QUEUE(ReferenceQueue, referenceQ, r); }
|
||||
;
|
||||
%%
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
Sermon sermon;
|
||||
int i = 0, block = 0, normal = 0;
|
||||
InitSermon(&sermon);
|
||||
yyin = fopen(argv[1], "rt");
|
||||
yyparse(&sermon);
|
||||
printf("Parsed sermon.\n");
|
||||
printf("TITLE=%s\n", sermon.sermonTitle ? sermon.sermonTitle : "none");
|
||||
printf("AUTHOR=%s\n", sermon.sermonAuthor ? sermon.sermonAuthor : "none");
|
||||
printf("DATE=%s\n", sermon.sermonDate ? sermon.sermonDate : "none");
|
||||
printf("OCCASION=%s\n", sermon.sermonOccasion ? sermon.sermonOccasion : "none");
|
||||
printf("TEXT=%s\n", sermon.sermonText ? sermon.sermonText : "none");
|
||||
printf("\nThere are %d paragraphs", sermon.numParagraphs);
|
||||
for (i = 0; i < sermon.numParagraphs; i++) {
|
||||
if (sermon.sermonParagraphs[i].paraType == PARA_DEFAULT) normal++;
|
||||
else if (sermon.sermonParagraphs[i].paraType == PARA_BLOCKQUOTE) block++;
|
||||
}
|
||||
printf(" (%d regular, %d blockquote)\n", normal, block);
|
||||
printf("\nThere are %d references.\n", sermon.numReferences);
|
||||
for (i = 0; i < sermon.numReferences; i++) {
|
||||
printf(" - %s: %s\n", sermon.sermonReferences[i].refId, sermon.sermonReferences[i].refText);
|
||||
}
|
||||
printf("\n");
|
||||
FreeSermon(&sermon);
|
||||
fclose(yyin);
|
||||
}
|
||||
|
||||
void yyerror(Sermon* s, const char* msg) {
|
||||
fprintf(stderr, "Parse error (%d:%d): %s - \"%s\"\n", yylloc.first_line, yylloc.first_column, msg, yytext);
|
||||
exit(1);
|
||||
}
|
||||
31
src/sermon_util.c
Normal file
31
src/sermon_util.c
Normal file
@@ -0,0 +1,31 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "sermon.h"
|
||||
|
||||
void InitSermon(Sermon* srm) {
|
||||
memset(srm, 0, sizeof(Sermon));
|
||||
}
|
||||
|
||||
void FreeSermon(Sermon* srm) {
|
||||
int i = 0;
|
||||
if (srm->sermonTitle) free(srm->sermonTitle);
|
||||
if (srm->sermonAuthor) free(srm->sermonAuthor);
|
||||
if (srm->sermonDate) free(srm->sermonDate);
|
||||
if (srm->sermonOccasion) free(srm->sermonOccasion);
|
||||
if (srm->sermonText) free(srm->sermonText);
|
||||
|
||||
if (srm->numParagraphs) {
|
||||
for (i = 0; i < srm->numParagraphs; i++) {
|
||||
free(srm->sermonParagraphs[i].paraText);
|
||||
}
|
||||
free(srm->sermonParagraphs);
|
||||
}
|
||||
if (srm->numReferences) {
|
||||
for (i = 0; i < srm->numReferences; i++) {
|
||||
free(srm->sermonReferences[i].refId);
|
||||
free(srm->sermonReferences[i].refText);
|
||||
}
|
||||
free(srm->sermonReferences);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user