facil.io

#GFM Markdown Parser

c
#define FIO_GFM
#include "fio-stl.h"

A flat-state, non-recursive parser for GitHub Flavored Markdown. It emits block and inline events through three callbacks — push, write, pop — without allocating heap memory itself. fio_gfm_parse is the only public entry point.

The parser depends on FIO_ENTITY for HTML entity decoding.


#Configuration Macros

#FIO_GFM_MAX_DEPTH

c
#define FIO_GFM_MAX_DEPTH 255

Maximum container nesting depth. Must fit in a byte (≤ 255).

#FIO_GFM_MAX_TABLE_COLUMNS

c
#define FIO_GFM_MAX_TABLE_COLUMNS 64

Maximum table columns. Alignment is bit-packed, so four columns fit in one byte.

#FIO_GFM_REF_CACHE_SIZE

c
#define FIO_GFM_REF_CACHE_SIZE 128

Maximum reference definitions held in the fast inline cache.


#Error Codes

#FIO_GFM_ERR_GENERIC, FIO_GFM_ERR_DEPTH, FIO_GFM_ERR_INPUT

c
#define FIO_GFM_ERR_GENERIC -1
#define FIO_GFM_ERR_DEPTH   -2
#define FIO_GFM_ERR_INPUT   -3

Negative error codes used internally and returned through fio_gfm_parse.


#Flags

#FIO_GFM_F_TIGHT

c
#define FIO_GFM_F_TIGHT ((uint8_t)1U << 0)

The list is tight (no blank lines between items).

#FIO_GFM_F_LOOSE_SEEN

c
#define FIO_GFM_F_LOOSE_SEEN ((uint8_t)1U << 1)

A blank line was seen between list items.

#FIO_GFM_F_TASK

c
#define FIO_GFM_F_TASK ((uint8_t)1U << 2)

The list item is a task-list item.

#FIO_GFM_F_TASK_CHECKED

c
#define FIO_GFM_F_TASK_CHECKED ((uint8_t)1U << 3)

The task-list marker is checked ([x]).


#Types

#fio_gfm_type_e

c
typedef enum {
  FIO_GFM_PARAGRAPH = 1,
  FIO_GFM_HEADING,
  FIO_GFM_THEMATIC_BREAK,
  FIO_GFM_BLOCKQUOTE,
  FIO_GFM_LIST_UNORDERED,
  FIO_GFM_LIST_ORDERED,
  FIO_GFM_LIST_ITEM,
  FIO_GFM_CODE_BLOCK,
  FIO_GFM_HTML_BLOCK,
  FIO_GFM_TABLE,
  FIO_GFM_TABLE_ROW,
  FIO_GFM_TABLE_CELL,
  FIO_GFM_EMPHASIS,
  FIO_GFM_STRONG,
  FIO_GFM_STRIKETHROUGH,
  FIO_GFM_LINK,
  FIO_GFM_TEXT,
  FIO_GFM_SOFT_BREAK,
  FIO_GFM_HARD_BREAK,
  FIO_GFM_CODE_SPAN,
  FIO_GFM_IMAGE,
  FIO_GFM_AUTOLINK,
  FIO_GFM_INLINE_HTML,
  FIO_GFM_FOOTNOTE_REF,
} fio_gfm_type_e;

Event type. Values 112 are block sections; 1316 are inline sections; 1723 are text and leaf content emitted with write.

#fio_gfm_align_e

c
typedef enum {
  FIO_GFM_ALIGN_NONE = 0,
  FIO_GFM_ALIGN_LEFT,
  FIO_GFM_ALIGN_RIGHT,
  FIO_GFM_ALIGN_CENTER,
} fio_gfm_align_e;

Table cell alignment.

#fio_gfm_event_s

c
typedef struct {
  void *udata;
  fio_buf_info_s source;
  fio_buf_info_s text;
  fio_buf_info_s marker;
  fio_buf_info_s info;
  fio_buf_info_s destination;
  fio_buf_info_s title;
  fio_buf_info_s reference;
  uint32_t list_start;
  uint16_t columns;
  uint16_t column;
  uint8_t type;
  uint8_t heading_level;
  uint8_t flags;
  uint8_t align;
  uint8_t padding;
} fio_gfm_event_s;

Event passed to every callback.

Fields:

  • udata — live user pointer; the parser copies it back after each callback.
  • source — full source slice for this event.
  • text — literal text / label / code content.
  • marker — marker slice (#, fence, list bullet, etc.).
  • info — fenced code info string (language tag).
  • destination — link/image/autolink URL.
  • title — link/image title.
  • reference — reference label for ref-style links/images.
  • list_start — ordered list start number.
  • columns / column — table column count and cell index.
  • typefio_gfm_type_e value.
  • heading_level16 for headings.
  • flagsFIO_GFM_F_*.
  • alignfio_gfm_align_e for table cells.
  • padding — virtual leading spaces from tab expansion.

#fio_gfm_callbacks_s

c
typedef struct {
  int (*push)(fio_gfm_event_s *e);
  int (*write)(fio_gfm_event_s *e);
  int (*pop)(fio_gfm_event_s *e);
} fio_gfm_callbacks_s;

Parser callbacks. All are optional. Return 0 to continue, non-zero to abort. Positive values are user errors; negative values are reserved for parser errors.


#Parsing

#fio_gfm_parse

c
SFUNC size_t fio_gfm_parse(const fio_gfm_callbacks_s *callbacks,
                           void *udata,
                           fio_buf_info_s source);

Parses a complete Markdown document and emits events. The document must be fully available in source; streaming is not supported.

Parameters:

  • callbacks — pointer to callback struct.
  • udata — user pointer copied into every event; callbacks may update it.
  • source — full document bytes.

Returns: the number of bytes consumed. If this equals source.len, parsing completed. A smaller value means a callback aborted or a parser error occurred.


#Example

c
#define FIO_GFM
#include "fio-stl.h"

static int on_push(fio_gfm_event_s *e) {
  fprintf(stderr, "open type=%u\n", e->type);
  return 0;
}

static int on_write(fio_gfm_event_s *e) {
  fprintf(stderr, "text: %.*s\n", (int)e->text.len, e->text.buf);
  return 0;
}

static int on_pop(fio_gfm_event_s *e) {
  fprintf(stderr, "close type=%u\n", e->type);
  return 0;
}

int main(void) {
  fio_gfm_callbacks_s cb = {
      .push = on_push,
      .write = on_write,
      .pop = on_pop,
  };
  fio_buf_info_s src = FIO_BUF_INFO2("# Hello\n\nworld.\n", 16);
  size_t n = fio_gfm_parse(&cb, NULL, src);
  fprintf(stderr, "consumed %zu of %zu bytes\n", n, src.len);
  return 0;
}