diff options
Diffstat (limited to 'src/creole.c')
-rw-r--r-- | src/creole.c | 111 |
1 files changed, 111 insertions, 0 deletions
diff --git a/src/creole.c b/src/creole.c new file mode 100644 index 0000000..f69c543 --- /dev/null +++ b/src/creole.c @@ -0,0 +1,111 @@ +#include "creole.h" + +#include <assert.h> +#include <ctype.h> +#include <stdarg.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> + +#define LENGTH(x) (sizeof(x)/sizeof((x)[0])) + +#define DEBUG(...) (fprintf(stderr, __VA_ARGS__), fflush(stderr)) + +void process(const char *begin, const char *end, bool new_block, FILE *out); +int do_headers(const char *begin, const char *end, bool new_block, FILE *out); + +// A parser takes a (sub)string and returns the number of characters consumed, if any. +// +// The parameter `new_block` determines whether `begin` points to the beginning of a new block. +// The sign of the return value determines whether a new block should begin, after the consumed text. +typedef int (* parser_t)(const char *begin, const char *end, bool new_block, FILE *out); + +static parser_t parsers[] = { do_headers }; + +int do_headers(const char *begin, const char *end, bool new_block, FILE *out) { + if (!new_block) { // Headers are block-level elements. + return 0; + } + + if (*begin != '=') { + return 0; + } + + unsigned level = 0; + while (*begin == '=') { + level += 1; + begin += 1; + } + DEBUG("level %d\n", level); + + while (isspace(*begin)) { + begin += 1; + } + + const char *stop = end; + while (stop + 1 != end && stop[1] != '\n') { + stop += 1; + } + while (*stop == '=') { + stop -= 1; + } + + fprintf(out, "<h%u>", level); + process(begin, stop, false, out); + fprintf(out, "</h%u>", level); + + return -(stop - begin); +} + +void process(const char *begin, const char *end, bool new_block, FILE *out) { + const char *p = begin; + while (p < end) { + // Eat all newlines if we're starting a block. + if (new_block) { + while (*p == '\n') { + p += 1; + if (p == end) { + return; + } + } + } + + // Greedily try all parsers. + int affected; + for (unsigned i = 0; i < LENGTH(parsers); ++i) { + DEBUG("%p\n", parsers[i]); + affected = parsers[i](p, end, new_block, out); + if (affected) { + break; + } + } + if (affected) { + p += abs(affected); + } else { + fputc(*p, out); + p += 1; + } + + if (p + 1 == end) { + // Don't print single newline at end. + if (*p == '\n') { + return; + } + } else { + // Determine whether we've reached a new block. + if (p[0] == '\n' && p[1] == '\n') { + // Double newline characters separate blocks; + // if we've found them, we're starting a new block + new_block = true; + } else { + // ...otherwise the parser gets to decide. + new_block = affected < 0; + } + } + } +} + +void render_creole(FILE *out, const char *source, size_t source_length) +{ + process(source, source + source_length, true, out); +} |