jgm / peg-markdown
- Source
- Commits
- Network (2)
- Issues (0)
- Downloads (12)
- Wiki (1)
- Graphs
-
Tree:
8452e4e
peg-markdown / utility_functions.c
| bea5f65c » | jgm | 2008-06-12 | 1 | /* utility_functions.c - List manipulation functions, element | |
| 2 | * constructors, and macro definitions for leg markdown parser. */ | ||||
| 3 | |||||
| 4 | extern int strcasecmp(const char *string1, const char *string2); | ||||
| 5 | |||||
| 6 | /********************************************************************** | ||||
| 7 | |||||
| 8 | List manipulation functions | ||||
| 9 | |||||
| 10 | ***********************************************************************/ | ||||
| 11 | |||||
| 2a7ef09f » | jgm | 2008-06-12 | 12 | /* cons - cons an element onto a list, returning pointer to new head */ | |
| 13 | static element * cons(element *new, element *list) { | ||||
| bea5f65c » | jgm | 2008-06-12 | 14 | assert(new != NULL); | |
| 15 | new->next = list; | ||||
| 16 | return new; | ||||
| 17 | } | ||||
| 18 | |||||
| 19 | /* reverse - reverse a list, returning pointer to new list */ | ||||
| 20 | static element *reverse(element *list) { | ||||
| 21 | element *new = NULL; | ||||
| 22 | element *next = NULL; | ||||
| 23 | while (list != NULL) { | ||||
| 24 | next = list->next; | ||||
| 2a7ef09f » | jgm | 2008-06-12 | 25 | new = cons(list, new); | |
| bea5f65c » | jgm | 2008-06-12 | 26 | list = next; | |
| 27 | } | ||||
| 28 | return new; | ||||
| 29 | } | ||||
| 30 | |||||
| 31 | /* concat_string_list - concatenates string contents of list of STR elements. | ||||
| 32 | * Frees STR elements as they are added to the concatenation. */ | ||||
| 33 | static GString *concat_string_list(element *list) { | ||||
| 34 | GString *result; | ||||
| 35 | element *next; | ||||
| 36 | result = g_string_new(""); | ||||
| 37 | while (list != NULL) { | ||||
| 38 | assert(list->key == STR); | ||||
| 39 | assert(list->contents.str != NULL); | ||||
| 40 | g_string_append(result, list->contents.str); | ||||
| 41 | next = list->next; | ||||
| 42 | free_element(list); | ||||
| 43 | list = next; | ||||
| 44 | } | ||||
| 45 | return result; | ||||
| 46 | } | ||||
| 47 | |||||
| 48 | /********************************************************************** | ||||
| 49 | |||||
| 50 | Global variables used in parsing | ||||
| 51 | |||||
| 52 | ***********************************************************************/ | ||||
| 53 | |||||
| 54 | static char *charbuf = ""; /* Buffer of characters to be parsed. */ | ||||
| 55 | static element *references = NULL; /* List of link references found. */ | ||||
| 56 | static element *notes = NULL; /* List of footnotes found. */ | ||||
| 57 | static element *parse_result; /* Results of parse. */ | ||||
| 58 | static int syntax_extensions; /* Syntax extensions selected. */ | ||||
| 59 | |||||
| 60 | /********************************************************************** | ||||
| 61 | |||||
| 62 | Auxiliary functions for parsing actions. | ||||
| 63 | These make it easier to build up data structures (including lists) | ||||
| 64 | in the parsing actions. | ||||
| 65 | |||||
| 66 | ***********************************************************************/ | ||||
| 67 | |||||
| 68 | /* mk_element - generic constructor for element */ | ||||
| 69 | static element * mk_element(int key) { | ||||
| 70 | element *result = malloc(sizeof(element)); | ||||
| 71 | result->key = key; | ||||
| 72 | result->children = NULL; | ||||
| 73 | result->next = NULL; | ||||
| 74 | result->contents.str = NULL; | ||||
| 75 | return result; | ||||
| 76 | } | ||||
| 77 | |||||
| 78 | /* mk_str - constructor for STR element */ | ||||
| 79 | static element * mk_str(char *string) { | ||||
| 80 | element *result; | ||||
| 81 | assert(string != NULL); | ||||
| 82 | result = mk_element(STR); | ||||
| 83 | result->contents.str = strdup(string); | ||||
| 84 | return result; | ||||
| 85 | } | ||||
| 86 | |||||
| 87 | /* mk_str_from_list - makes STR element by concatenating a | ||||
| 88 | * reversed list of strings, adding optional extra newline */ | ||||
| 89 | static element * mk_str_from_list(element *list, bool extra_newline) { | ||||
| 90 | element *result; | ||||
| 91 | GString *c = concat_string_list(reverse(list)); | ||||
| 92 | if (extra_newline) | ||||
| 93 | g_string_append(c, "\n"); | ||||
| 94 | result = mk_element(STR); | ||||
| 95 | result->contents.str = c->str; | ||||
| 96 | g_string_free(c, false); | ||||
| 97 | return result; | ||||
| 98 | } | ||||
| 99 | |||||
| 100 | /* mk_list - makes new list with key 'key' and children the reverse of 'lst'. | ||||
| 2a7ef09f » | jgm | 2008-06-12 | 101 | * This is designed to be used with cons to build lists in a parser action. | |
| 102 | * The reversing is necessary because cons adds to the head of a list. */ | ||||
| bea5f65c » | jgm | 2008-06-12 | 103 | static element * mk_list(int key, element *lst) { | |
| 104 | element *result; | ||||
| 105 | result = mk_element(key); | ||||
| 106 | result->children = reverse(lst); | ||||
| 107 | return result; | ||||
| 108 | } | ||||
| 109 | |||||
| 110 | /* mk_link - constructor for LINK element */ | ||||
| 111 | static element * mk_link(element *label, char *url, char *title) { | ||||
| 112 | element *result; | ||||
| 113 | result = mk_element(LINK); | ||||
| 114 | result->contents.link = malloc(sizeof(link)); | ||||
| 115 | result->contents.link->label = label; | ||||
| 116 | result->contents.link->url = strdup(url); | ||||
| 117 | result->contents.link->title = strdup(title); | ||||
| 118 | return result; | ||||
| 119 | } | ||||
| 120 | |||||
| 121 | /* extension = returns true if extension is selected */ | ||||
| 122 | static bool extension(int ext) { | ||||
| 123 | return (syntax_extensions & ext); | ||||
| 124 | } | ||||
| 125 | |||||
| 126 | /* match_inlines - returns true if inline lists match (case-insensitive...) */ | ||||
| 127 | static bool match_inlines(element *l1, element *l2) { | ||||
| 128 | while (l1 != NULL && l2 != NULL) { | ||||
| c1d3ceca » | jgm | 2008-11-22 | 129 | if (l1->key != l2->key) | |
| bea5f65c » | jgm | 2008-06-12 | 130 | return false; | |
| 131 | switch (l1->key) { | ||||
| 132 | case SPACE: | ||||
| 133 | case LINEBREAK: | ||||
| c1d3ceca » | jgm | 2008-11-22 | 134 | case ELLIPSIS: | |
| 135 | case EMDASH: | ||||
| 136 | case ENDASH: | ||||
| 137 | case APOSTROPHE: | ||||
| bea5f65c » | jgm | 2008-06-12 | 138 | break; | |
| 139 | case CODE: | ||||
| 140 | case STR: | ||||
| 141 | case HTML: | ||||
| 142 | if (strcasecmp(l1->contents.str, l2->contents.str) == 0) | ||||
| 143 | break; | ||||
| 144 | else | ||||
| 145 | return false; | ||||
| 146 | case EMPH: | ||||
| 147 | case STRONG: | ||||
| 148 | case LIST: | ||||
| c1d3ceca » | jgm | 2008-11-22 | 149 | case SINGLEQUOTED: | |
| 150 | case DOUBLEQUOTED: | ||||
| bea5f65c » | jgm | 2008-06-12 | 151 | if (match_inlines(l1->children, l2->children)) | |
| 152 | break; | ||||
| 153 | else | ||||
| 154 | return false; | ||||
| 155 | case LINK: | ||||
| 156 | case IMAGE: | ||||
| 157 | return false; /* No links or images within links */ | ||||
| 158 | default: | ||||
| 159 | fprintf(stderr, "match_inlines encountered unknown key = %d\n", l1->key); | ||||
| 160 | exit(EXIT_FAILURE); | ||||
| 161 | break; | ||||
| 162 | } | ||||
| 163 | l1 = l1->next; | ||||
| 164 | l2 = l2->next; | ||||
| 165 | } | ||||
| 166 | return (l1 == NULL && l2 == NULL); /* return true if both lists exhausted */ | ||||
| 167 | } | ||||
| 168 | |||||
| 169 | /* find_reference - return true if link found in references matching label. | ||||
| 170 | * 'link' is modified with the matching url and title. */ | ||||
| 171 | static bool find_reference(link *result, element *label) { | ||||
| 172 | element *cur = references; /* pointer to walk up list of references */ | ||||
| 173 | link *curitem; | ||||
| 174 | while (cur != NULL) { | ||||
| 175 | curitem = cur->contents.link; | ||||
| 176 | if (match_inlines(label, curitem->label)) { | ||||
| 177 | *result = *curitem; | ||||
| 178 | return true; | ||||
| 179 | } | ||||
| 180 | else | ||||
| 181 | cur = cur->next; | ||||
| 182 | } | ||||
| 183 | return false; | ||||
| 184 | } | ||||
| 185 | |||||
| 186 | /* find_note - return true if note found in notes matching label. | ||||
| 187 | if found, 'result' is set to point to matched note. */ | ||||
| 188 | |||||
| 189 | static bool find_note(element **result, char *label) { | ||||
| 190 | element *cur = notes; /* pointer to walk up list of notes */ | ||||
| 191 | while (cur != NULL) { | ||||
| 192 | if (strcmp(label, cur->contents.str) == 0) { | ||||
| 193 | *result = cur; | ||||
| 194 | return true; | ||||
| 195 | } | ||||
| 196 | else | ||||
| 197 | cur = cur->next; | ||||
| 198 | } | ||||
| 199 | return false; | ||||
| 200 | } | ||||
| 201 | |||||
| 202 | /********************************************************************** | ||||
| 203 | |||||
| 204 | Definitions for leg parser generator. | ||||
| 205 | YY_INPUT is the function the parser calls to get new input. | ||||
| 206 | We take all new input from (static) charbuf. | ||||
| 207 | |||||
| 208 | ***********************************************************************/ | ||||
| 209 | |||||
| 210 | # define YYSTYPE element * | ||||
| 211 | #ifdef __DEBUG__ | ||||
| 212 | # define YY_DEBUG 1 | ||||
| 213 | #endif | ||||
| 214 | |||||
| 215 | #define YY_INPUT(buf, result, max_size) \ | ||||
| 216 | { \ | ||||
| 217 | int yyc; \ | ||||
| 218 | if (charbuf && *charbuf != '\0') { \ | ||||
| 219 | yyc= *charbuf++; \ | ||||
| 220 | } else { \ | ||||
| 221 | yyc= EOF; \ | ||||
| 222 | } \ | ||||
| 223 | result= (EOF == yyc) ? 0 : (*(buf)= yyc, 1); \ | ||||
| 224 | } | ||||
| 225 | |||||
| 226 | |||||
