#include "creole.h" #include #include #include #include #include #include #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, "", level); process(begin, stop, false, out); fprintf(out, "", 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); }