]> ocean-lang.org Git - ocean/blob - csrc/boot-strap/libmdcode.c
mdcode, md2c - extract C code from a literate markdown program
[ocean] / csrc / boot-strap / libmdcode.c
1 #line 100 "../mdcode.mdc"
2 #define _GNU_SOURCE
3 #include <unistd.h>
4 #include <stdlib.h>
5 #include <stdio.h>
6
7 #include "mdcode.h"
8 #line 461 "../mdcode.mdc"
9 #include  <ctype.h>
10 #include  <string.h>
11
12 #line 179 "../mdcode.mdc"
13 struct psection {
14         struct section;
15         struct code_node *last;
16         int refcnt;
17         int indent;
18 };
19
20 #line 216 "../mdcode.mdc"
21 static void code_linearize(struct code_node *code)
22 {
23         struct code_node *t;
24         for (t = code; t; t = t->next)
25                 t->indent = 0;
26         for (; code; code = code->next)
27                 if (code->child) {
28                         struct code_node *next = code->next;
29                         struct psection *pchild =
30                                 (struct psection *)code->child;
31                         int indent = pchild->indent;
32                         code->next = code->child->code;
33                         code->child->code = NULL;
34                         code->child = NULL;
35                         for (t = code; t->next; t = t->next)
36                                 t->next->indent = code->indent + indent;
37                         t->next = next;
38                 }
39 }
40
41 #line 239 "../mdcode.mdc"
42 void code_free(struct code_node *code)
43 {
44         while (code) {
45                 struct code_node *this;
46                 if (code->child)
47                         code_linearize(code);
48                 this = code;
49                 code = code->next;
50                 free(this);
51         }
52 }
53
54 #line 268 "../mdcode.mdc"
55 static void code_add_text(struct psection *where, struct text txt,
56                           int line_no)
57 {
58         struct code_node *n;
59         if (txt.len == 0)
60                 return;
61         n = malloc(sizeof(*n));
62         n->code = txt;
63         n->indent = 0;
64         n->line_no = line_no;
65         n->next = NULL;
66         n->child = NULL;
67         if (where->last)
68                 where->last->next = n;
69         else
70                 where->code = n;
71         where->last = n;
72 }
73
74 #line 290 "../mdcode.mdc"
75 void code_add_link(struct psection *where, struct psection *to,
76                    int indent)
77 {
78         struct code_node *n;
79
80         to->indent = indent;
81         to->refcnt++;   // this will be checked elsewhere
82         if (where->last && where->last->child == NULL) {
83                 where->last->child = to;
84                 return;
85         }
86         n = malloc(sizeof(*n));
87         n->code.len = 0;
88         n->indent = 0;
89         n->line_no = 0;
90         n->next = NULL;
91         n->child = to;
92         if (where->last)
93                 where->last->next = n;
94         else
95                 where->code = n;
96         where->last = n;
97 }
98
99 #line 329 "../mdcode.mdc"
100 static int text_cmp(struct text a, struct text b)
101 {
102         if (a.len != b.len)
103                 return a.len - b.len;
104         return strncmp(a.txt, b.txt, a.len);
105 }
106
107 static struct psection *section_find(struct psection **list, struct text name)
108 {
109         struct psection *new;
110         while (*list) {
111                 int cmp = text_cmp((*list)->section, name);
112                 if (cmp == 0)
113                         return *list;
114                 if (cmp > 0)
115                         break;
116                 list = (struct psection **)&((*list)->next);
117         }
118         /* Add this section */
119         new = malloc(sizeof(*new));
120         new->next = *list;
121         *list = new;
122         new->section = name;
123         new->code = NULL;
124         new->last = NULL;
125         new->refcnt = 0;
126         new->indent = 0;
127         return new;
128 }
129
130 #line 410 "../mdcode.mdc"
131 static char *skip_lws(char *pos, char *end)
132 {
133         while (pos < end && (*pos == ' ' || *pos == '\t'))
134                 pos++;
135         return pos;
136 }
137
138 static char *skip_line(char *pos, char *end)
139 {
140         while (pos < end && *pos != '\n')
141                 pos++;
142         if (pos < end)
143                 pos++;
144         return pos;
145 }
146
147 static char *skip_para(char *pos, char *end, int *line_no)
148 {
149         /* Might return a pointer to a blank line, as only
150          * one trailing blank line is skipped
151          */
152         if (*pos == '#') {
153                 pos = skip_line(pos, end);
154                 (*line_no) += 1;
155                 return pos;
156         }
157         while (pos < end &&
158                *pos != '#' &&
159                *(pos = skip_lws(pos, end)) != '\n') {
160                 pos = skip_line(pos, end);
161                 (*line_no) += 1;
162         }
163         if (pos < end && *pos == '\n') {
164                 pos++;
165                 (*line_no) += 1;
166         }
167         return pos;
168 }
169
170 #line 466 "../mdcode.mdc"
171 static struct text take_header(char *pos, char *end)
172 {
173         struct text section;
174
175         while (pos < end && *pos == '#')
176                 pos++;
177         while (pos < end && *pos == ' ')
178                 pos++;
179         section.txt = pos;
180         while (pos < end && *pos != '\n')
181                 pos++;
182         while (pos > section.txt &&
183                (pos[-1] == '#' || pos[-1] == ' '))
184                 pos--;
185         section.len = pos - section.txt;
186         return section;
187 }
188
189 static int is_list(char *pos, char *end)
190 {
191         if (strchr("-*+", *pos))
192                 return 1;
193         if (isdigit(*pos)) {
194                 while (pos < end && isdigit(*pos))
195                         pos += 1;
196                 if  (pos < end && *pos == '.')
197                         return 1;
198         }
199         return 0;
200 }
201
202 static int matches(char *start, char *pos, char *end)
203 {
204         if (start == NULL)
205                 return matches("\t", pos, end) ||
206                        matches("    ", pos, end);
207         return (pos + strlen(start) < end &&
208                 strncmp(pos, start, strlen(start)) == 0);
209 }
210
211 #line 538 "../mdcode.mdc"
212 static int count_space(char *sol, char *p)
213 {
214         int c = 0;
215         while (sol < p) {
216                 if (sol[0] == ' ')
217                         c++;
218                 if (sol[0] == '\t')
219                         c+= 8;
220                 sol++;
221         }
222         return c;
223 }
224
225
226 static char *take_code(char *pos, char *end, char *marker,
227                        struct psection **table, struct text section,
228                        int *line_nop)
229 {
230         char *start = pos;
231         int line_no = *line_nop;
232         int start_line = line_no;
233         struct psection *sect;
234
235         sect = section_find(table, section);
236
237         while (pos < end) {
238                 char *sol, *t;
239                 struct text ref;
240
241                 if (marker && matches(marker, pos, end))
242                         break;
243                 if (!marker &&
244                     (skip_lws(pos, end))[0] != '\n' &&
245                     !matches(NULL, pos, end))
246                         /* Paragraph not indented */
247                         break;
248
249                 /* Still in code - check for reference */
250                 sol = pos;
251                 if (!marker) {
252                         if (*sol == '\t')
253                                 sol++;
254                         else if (strcmp(sol, "    ") == 0)
255                                 sol += 4;
256                 }
257                 t = skip_lws(sol, end);
258                 if (t[0] != '#' || t[1] != '#') {
259                         /* Just regular code here */
260                         pos = skip_line(sol, end);
261                         line_no++;
262                         continue;
263                 }
264
265                 if (pos > start) {
266                         struct text txt;
267                         txt.txt = start;
268                         txt.len = pos - start;
269                         code_add_text(sect, txt, start_line);
270                 }
271                 ref = take_header(t, end);
272                 if (ref.len) {
273                         struct psection *refsec = section_find(table, ref);
274                         code_add_link(sect, refsec, count_space(sol, t));
275                 }
276                 pos = skip_line(t, end);
277                 line_no++;
278                 start = pos;
279                 start_line = line_no;
280         }
281         if (pos > start) {
282                 struct text txt;
283                 txt.txt = start;
284                 txt.len = pos - start;
285                 code_add_text(sect, txt, start_line);
286         }
287         if (marker) {
288                 pos = skip_line(pos, end);
289                 line_no++;
290         }
291         *line_nop = line_no;
292         return pos;
293 }
294
295 #line 630 "../mdcode.mdc"
296 static struct psection *code_find(char *pos, char *end)
297 {
298         struct psection *table = NULL;
299         int in_list = 0;
300         int line_no = 1;
301         struct text section = {0};
302
303         while (pos < end) {
304                 if (pos[0] == '#') {
305                         section = take_header(pos, end);
306                         in_list = 0;
307                         pos = skip_line(pos, end);
308                         line_no++;
309                 } else if (is_list(pos, end)) {
310                         in_list = 1;
311                         pos = skip_para(pos, end, &line_no);
312                 } else if (!in_list && matches(NULL, pos, end)) {
313                         pos = take_code(pos, end, NULL, &table,
314                                         section, &line_no);
315                 } else if (matches("```", pos, end)) {
316                         in_list = 0;
317                         pos = skip_line(pos, end);
318                         line_no++;
319                         pos = take_code(pos, end, "```", &table,
320                                         section, &line_no);
321                 } else if (matches("~~~", pos, end)) {
322                         in_list = 0;
323                         pos = skip_line(pos, end);
324                         line_no++;
325                         pos = take_code(pos, end, "~~~", &table,
326                                         section, &line_no);
327                 } else {
328                         if (!isspace(*pos))
329                                 in_list = 0;
330                         pos = skip_para(pos, end, &line_no);
331                 }
332         }
333         return table;
334 }
335
336 #line 690 "../mdcode.mdc"
337 struct section *code_extract(char *pos, char *end, code_err_fn error)
338 {
339         struct psection *table;
340         struct section *result = NULL;
341         struct section *tofree = NULL;
342
343         table = code_find(pos, end);
344
345         while (table) {
346                 struct psection *t = (struct psection*)table->next;
347                 if (table->last == NULL) {
348                         char *msg;
349                         asprintf(&msg,
350                                 "Section \"%.*s\" is referenced but not declared",
351                                  table->section.len, table->section.txt);
352                         error(msg);
353                         free(msg);
354                 }
355                 if (table->refcnt == 0) {
356                         /* Root-section,  return it */
357                         table->next = result;
358                         result = table;
359                         code_linearize(result->code);
360                 } else {
361                         table->next = tofree;
362                         tofree = table;
363                         if (table->refcnt > 1) {
364                                 char *msg;
365                                 asprintf(&msg,
366                                          "Section \"%.*s\" referenced multiple times (%d).",
367                                          table->section.len, table->section.txt,
368                                          table->refcnt);
369                                 error(msg);
370                                 free(msg);
371                         }
372                 }
373                 table = t;
374         }
375         while (tofree) {
376                 struct section *t = tofree->next;
377                 free(tofree);
378                 tofree = t;
379         }
380         return result;
381 }
382
383 #line 109 "../mdcode.mdc"
384