Initial import

This commit is contained in:
David Baer
2015-08-05 22:22:49 -04:00
commit 0b3456cca6
10 changed files with 417 additions and 0 deletions

26
.gitignore vendored Normal file
View 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
View File

@@ -0,0 +1,2 @@
SUBDIRS = src
dist_doc_DATA = README

4
README Normal file
View File

@@ -0,0 +1,4 @@
sermon 0.9
==========
Sermon markup converter

11
configure.ac Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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);
}
}