|
|
|
@ -9,6 +9,8 @@ |
|
|
|
|
#include "unicode.h" |
|
|
|
|
#include "util.h" |
|
|
|
|
|
|
|
|
|
char *strstr(const char *haystack, const char *needle); |
|
|
|
|
|
|
|
|
|
static int parse_section(struct parser *p) { |
|
|
|
|
str_t *section = str_create(); |
|
|
|
|
uint32_t ch; |
|
|
|
@ -300,6 +302,186 @@ static void parse_literal(struct parser *p, int *indent) { |
|
|
|
|
} while (ch != UTF8_INVALID); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
enum table_align { |
|
|
|
|
ALIGN_LEFT, |
|
|
|
|
ALIGN_CENTER, |
|
|
|
|
ALIGN_RIGHT, |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
struct table_row { |
|
|
|
|
struct table_cell *cell; |
|
|
|
|
struct table_row *next; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
struct table_cell { |
|
|
|
|
enum table_align align; |
|
|
|
|
str_t *contents; |
|
|
|
|
struct table_cell *next; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
static void parse_table(struct parser *p, uint32_t style) { |
|
|
|
|
struct table_row *table = NULL; |
|
|
|
|
struct table_row *currow = NULL, *prevrow = NULL; |
|
|
|
|
struct table_cell *curcell = NULL; |
|
|
|
|
int column = 0; |
|
|
|
|
uint32_t ch; |
|
|
|
|
parser_pushch(p, '|'); |
|
|
|
|
|
|
|
|
|
do { |
|
|
|
|
if ((ch = parser_getch(p)) == UTF8_INVALID) { |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
switch (ch) { |
|
|
|
|
case '\n': |
|
|
|
|
goto commit_table; |
|
|
|
|
case '|': |
|
|
|
|
prevrow = currow; |
|
|
|
|
currow = calloc(1, sizeof(struct table_row)); |
|
|
|
|
if (prevrow) { |
|
|
|
|
// TODO: Verify the number of columns match
|
|
|
|
|
prevrow->next = currow; |
|
|
|
|
} |
|
|
|
|
curcell = calloc(1, sizeof(struct table_cell)); |
|
|
|
|
currow->cell = curcell; |
|
|
|
|
column = 0; |
|
|
|
|
if (!table) { |
|
|
|
|
table = currow; |
|
|
|
|
} |
|
|
|
|
break; |
|
|
|
|
case ':': |
|
|
|
|
if (!currow) { |
|
|
|
|
parser_fatal(p, "Cannot start a column without " |
|
|
|
|
"starting a row first"); |
|
|
|
|
} else { |
|
|
|
|
struct table_cell *prev = curcell; |
|
|
|
|
curcell = calloc(1, sizeof(struct table_cell)); |
|
|
|
|
if (prev) { |
|
|
|
|
prev->next = curcell; |
|
|
|
|
} |
|
|
|
|
++column; |
|
|
|
|
} |
|
|
|
|
break; |
|
|
|
|
default: |
|
|
|
|
parser_fatal(p, "Expected either '|' or ':'"); |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
if ((ch = parser_getch(p)) == UTF8_INVALID) { |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
switch (ch) { |
|
|
|
|
case '[': |
|
|
|
|
curcell->align = ALIGN_LEFT; |
|
|
|
|
break; |
|
|
|
|
case '-': |
|
|
|
|
curcell->align = ALIGN_CENTER; |
|
|
|
|
break; |
|
|
|
|
case ']': |
|
|
|
|
curcell->align = ALIGN_RIGHT; |
|
|
|
|
break; |
|
|
|
|
case ' ': |
|
|
|
|
if (prevrow) { |
|
|
|
|
struct table_cell *pcell = prevrow->cell; |
|
|
|
|
for (int i = 0; i <= column && pcell; ++i, pcell = pcell->next) { |
|
|
|
|
if (i == column) { |
|
|
|
|
curcell->align = pcell->align; |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
parser_fatal(p, "No previous row to infer alignment from"); |
|
|
|
|
} |
|
|
|
|
break; |
|
|
|
|
default: |
|
|
|
|
parser_fatal(p, "Expected one of '[', '-', ']', or ' '"); |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
if ((ch = parser_getch(p)) != ' ') { |
|
|
|
|
parser_fatal(p, "Expected ' '"); |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
// Read out remainder of the text
|
|
|
|
|
curcell->contents = str_create(); |
|
|
|
|
while ((ch = parser_getch(p)) != UTF8_INVALID) { |
|
|
|
|
switch (ch) { |
|
|
|
|
case '\n': |
|
|
|
|
goto commit_cell; |
|
|
|
|
default: |
|
|
|
|
assert(str_append_ch(curcell->contents, ch) != -1); |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
commit_cell: |
|
|
|
|
if (strstr(curcell->contents->str, "T{") |
|
|
|
|
|| strstr(curcell->contents->str, "T}")) { |
|
|
|
|
parser_fatal(p, "Cells cannot contain T{ or T} " |
|
|
|
|
"due to roff limitations"); |
|
|
|
|
} |
|
|
|
|
} while (ch != UTF8_INVALID); |
|
|
|
|
commit_table: |
|
|
|
|
|
|
|
|
|
if (ch == UTF8_INVALID) { |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
roff_macro(p, "TS", NULL); |
|
|
|
|
|
|
|
|
|
const char *_style = NULL; |
|
|
|
|
switch (style) { |
|
|
|
|
case '[': |
|
|
|
|
_style = "allbox "; |
|
|
|
|
break; |
|
|
|
|
case '|': |
|
|
|
|
_style = ""; |
|
|
|
|
break; |
|
|
|
|
case ']': |
|
|
|
|
_style = "box "; |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fprintf(p->output, "%s tab(:);\n", _style); |
|
|
|
|
|
|
|
|
|
// Print alignments first
|
|
|
|
|
currow = table; |
|
|
|
|
while (currow) { |
|
|
|
|
curcell = currow->cell; |
|
|
|
|
while (curcell) { |
|
|
|
|
fprintf(p->output, "%c%s", "lcr"[curcell->align], |
|
|
|
|
curcell->next ? " " : ""); |
|
|
|
|
curcell = curcell->next; |
|
|
|
|
} |
|
|
|
|
fprintf(p->output, "%s\n", currow->next ? "" : "."); |
|
|
|
|
currow = currow->next; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Then contents
|
|
|
|
|
currow = table; |
|
|
|
|
while (currow) { |
|
|
|
|
curcell = currow->cell; |
|
|
|
|
fprintf(p->output, "T{\n"); |
|
|
|
|
while (curcell) { |
|
|
|
|
parser_pushstr(p, curcell->contents->str); |
|
|
|
|
parse_text(p); |
|
|
|
|
if (curcell->next) { |
|
|
|
|
fprintf(p->output, "\nT}:T{\n"); |
|
|
|
|
} else { |
|
|
|
|
fprintf(p->output, "\nT}"); |
|
|
|
|
} |
|
|
|
|
struct table_cell *prev = curcell; |
|
|
|
|
curcell = curcell->next; |
|
|
|
|
str_free(prev->contents); |
|
|
|
|
free(prev); |
|
|
|
|
} |
|
|
|
|
fprintf(p->output, "\n"); |
|
|
|
|
struct table_row *prev = currow; |
|
|
|
|
currow = currow->next; |
|
|
|
|
free(prev); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
roff_macro(p, "TE", NULL); |
|
|
|
|
roff_macro(p, "sp", "1", NULL); |
|
|
|
|
roff_macro(p, "RE", NULL); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void parse_document(struct parser *p) { |
|
|
|
|
uint32_t ch; |
|
|
|
|
int indent = 0; |
|
|
|
@ -332,6 +514,14 @@ static void parse_document(struct parser *p) { |
|
|
|
|
case '`': |
|
|
|
|
parse_literal(p, &indent); |
|
|
|
|
break; |
|
|
|
|
case '[': |
|
|
|
|
case '|': |
|
|
|
|
case ']': |
|
|
|
|
if (indent != 0) { |
|
|
|
|
parser_fatal(p, "Tables cannot be indented"); |
|
|
|
|
} |
|
|
|
|
parse_table(p, ch); |
|
|
|
|
break; |
|
|
|
|
case ' ': |
|
|
|
|
parser_fatal(p, "Tabs are required for indentation"); |
|
|
|
|
break; |
|
|
|
|