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