@@ -0,0 +1,315 @@
1+#include "hexml.h"
2+#include <malloc.h>
3+#include <string.h>
4+#include <assert.h>
5+#include <ctype.h>
6+#include <stdio.h>
7+
8+/////////////////////////////////////////////////////////////////////
9+// TYPES
10+
11+str mkStr(int32_t start, int32_t length)
12+{
13+ str res;
14+ res.start = start;
15+ res.length = length;
16+ return res;
17+}
18+
19+typedef struct
20+{
21+ int size;
22+ int used;
23+ attr* attrs; // dynamically allocated buffer
24+} attr_buffer;
25+
26+
27+struct node
28+{
29+ str name; // tag name, e.g. <[foo]>
30+ str outer; // outer text, [<foo>bar</foo>]
31+ str inner; // inner text, <foo>[bar]</foo>
32+ str attrs; // all the attributes, in the attribute buffer
33+ str nodes; // all the nodes, in the node buffer
34+};
35+
36+typedef struct
37+{
38+ // have a cursor at the front, which is all the stuff I have written out, final
39+ // have a cursor at the end, which is stack scoped, so children write out, then I do
40+ // when you commit, you copy over from end to front
41+
42+ // nodes
43+ int size;
44+ int used_front; // front entries, stored for good
45+ int used_back; // back entries, stack based, copied into front
46+ node* nodes; // dynamically allocated buffer
47+} node_buffer;
48+
49+
50+struct document
51+{
52+ char* body; // pointer to initial argument, not owned by us
53+
54+ int cursor; // things only used while parsing
55+ int length;
56+
57+ char* error_message;
58+ node* node;
59+ node_buffer nodes;
60+ attr_buffer attrs;
61+};
62+
63+
64+/////////////////////////////////////////////////////////////////////
65+// NOT THE PARSER
66+
67+char* document_error(document* d){return d->error_message;}
68+node* document_node(document* d){return d->node;}
69+str node_inner(node* n){return n->inner;}
70+str node_outer(node* n){return n->outer;}
71+
72+void document_free(document* d)
73+{
74+ free(d->error_message);
75+ free(d->nodes.nodes);
76+ free(d->attrs.attrs);
77+ free(d);
78+}
79+
80+node* node_children(document* d, node* n, int* res)
81+{
82+ *res = n->nodes.length;
83+ return &d->nodes.nodes[n->nodes.start];
84+}
85+
86+attr* node_attributes(document* d, node* n, int* res)
87+{
88+ *res = n->attrs.length;
89+ return &d->attrs.attrs[n->attrs.start];
90+}
91+
92+
93+attr* node_attributeBy(document* d, node* n, char* s, int slen)
94+{
95+ assert(0);
96+}
97+
98+// Search for given strings within a node
99+node* node_firstChildBy(document* d, node* n, char* s, int slen)
100+{
101+ if (slen == -1) slen = strlen(s);
102+ int i = n->nodes.start;
103+ int limit = i + n->nodes.length;
104+ for (; i < limit; i++)
105+ {
106+ if (d->nodes.nodes[i].name.length == slen &&
107+ memcmp(s, &d->body[d->nodes.nodes[i].name.start], slen) == 0)
108+ return &d->nodes.nodes[i];
109+ }
110+ return NULL;
111+}
112+
113+node* node_nextChildBy(document* d, node* parent, node* prev, char* s, int slen)
114+{
115+ assert(0);
116+}
117+
118+
119+/////////////////////////////////////////////////////////////////////
120+// PARSER PRIMITIVES
121+
122+int has(document* d, int ask){return (d->length - d->cursor) >= ask;}
123+
124+void trim(document* d)
125+{
126+ while (has(d, 1) && isspace(grab(d->body[*p]))
127+ *p += 1;
128+}
129+
130+
131+
132+void node_alloc(node_buffer* b, int ask)
133+{
134+ int space = b->size - b->used_back - b->used_front;
135+ if (space >= ask) return;
136+ assert(0);
137+}
138+
139+void attr_alloc(attr_buffer* b, int ask)
140+{
141+ int space = b->size - b->used;
142+ if (space >= ask) return;
143+ assert(0);
144+}
145+
146+void lexeme(char* msg, document* d, str s)
147+{
148+ printf("Lexeme: %s, %i:%i, %.*s\n", msg, s.start, s.length, s.length, &d->body[s.start]);
149+}
150+
151+int isName(char c)
152+{
153+ return isalnum(c) || c == '?';
154+}
155+
156+// you now expect a name, perhaps preceeded by whitespace
157+// the name may be empty
158+str parse_name(document* d, int* p)
159+{
160+ printf("Before trim: %i\n", *p);
161+ trim(d, p);
162+ printf("After trim: %i %c\n", *p, d->body[*p]);
163+ str res;
164+ res.start = *p;
165+ while (*p < d->bodylen && isName(d->body[*p]))
166+ *p += 1;
167+ printf("After trim: %i\n", *p);
168+ res.length = *p - res.start;
169+ lexeme("parse_name", d, res);
170+ return res;
171+}
172+
173+str parse_attrval(document* d, int* p)
174+{
175+ trim(d, p);
176+ str res;
177+ if (*p < d->bodylen || d->body[*p] != '=') return mkStr(0,0);
178+ trim(d, p);
179+ return parse_name(d, p);
180+}
181+
182+
183+// seen a tag name, now looking for attributes terminated by >
184+// puts the attributes it finds in the attribute buffer
185+void parse_attributes(document* s, int* p)
186+{
187+
188+
189+}
190+
191+void parse_content(document* d, int* p);
192+
193+
194+// Add a new entry into tag, am at a '<'
195+void parse_tag(document* d, int* p)
196+{
197+ printf("Before parse_tag %i\n", *p);
198+ assert(d->body[*p] == '<');
199+
200+ node_alloc(&d->nodes, 1);
201+ int me = d->nodes.used_back;
202+ d->nodes.used_back++;
203+
204+ *p += 1;
205+ printf("After increment %i\n", *p);
206+ d->nodes.nodes[me].name = parse_name(d, p);
207+
208+ d->nodes.nodes[me].attrs.start = d->attrs.used;
209+ parse_attributes(d, p);
210+ d->nodes.nodes[me].attrs.length = d->attrs.used;
211+
212+ if (d->bodylen - *p >= 2 &&
213+ (d->body[*p] == '/' || d->body[*p] == '?') &&
214+ d->body[*p + 1] == '>')
215+ {
216+ *p += 2;
217+ return;
218+ }
219+
220+ d->nodes.nodes[me].nodes.start = 42;
221+ parse_content(d, p);
222+ d->nodes.nodes[me].nodes.length = 42;
223+
224+ if (d->error_message != NULL) return;
225+ if (d->bodylen - *p >= 2 &&
226+ d->body[*p] == '<' &&
227+ d->body[*p + 1] == '/')
228+ {
229+ *p += 2;
230+ str close = parse_name(d, p);
231+ lexeme("openning tag", d, d->nodes.nodes[me].name);
232+ lexeme("closing tag", d, close);
233+ trim(d, p);
234+ if (close.length == d->nodes.nodes[me].name.length &&
235+ memcmp(&d->body[close.start], &d->body[d->nodes.nodes[me].name.start], close.length) == 0)
236+ return;
237+ d->error_message = strdup("Mismatch in closing tags");
238+ return;
239+ }
240+ d->error_message = strdup("Weirdness when trying to close tags");
241+}
242+
243+// Parser until </
244+void parse_content(document* d, int* p)
245+{
246+ int before = d->nodes.used_back;
247+ while (d->error_message == NULL)
248+ {
249+ // only < can have any impact
250+ char* res = memchr(&d->body[*p], '<', d->bodylen - *p);
251+ printf("%i - %i = %i\n", d->bodylen, *p, d->bodylen - *p);
252+ if (res == NULL)
253+ {
254+ *p = d->bodylen;
255+ printf("break 0\n");
256+ break;
257+ }
258+ *p = res - d->body;
259+ if (*p + 1 == d->bodylen)
260+ {
261+ // final character of string is <
262+ printf("break 1\n");
263+ break;
264+ }
265+ else if (d->body[*p + 1] == '/')
266+ {
267+ // have found a </
268+ printf("break 2\n");
269+ break;
270+ }
271+ else
272+ {
273+ printf("calling parse_tag\n");
274+ parse_tag(d, p);
275+ }
276+ }
277+ int diff = d->nodes.used_back - before;
278+ node_alloc(&d->nodes, diff);
279+
280+// for (int i = 0; i < diff; i++)
281+// d->nodes.nodes[d->nodes.used_front + i] = d->nodes.nodes[d->nodes.size - d->nodes.used_back - i];
282+ d->nodes.used_front += diff;
283+ d->nodes.used_back -= diff;
284+}
285+
286+document* document_parse(char* s, int slen)
287+{
288+ if (slen == -1) slen = strlen(s);
289+
290+ document* d = malloc(sizeof(document));
291+ d->body = s;
292+ d->bodylen = slen;
293+ d->error_message = NULL;
294+ d->node = NULL;
295+ d->attrs.size = 0;
296+ d->attrs.used = 0;
297+ d->attrs.attrs = NULL;
298+ d->nodes.size = 1000;
299+ d->nodes.used_back = 0;
300+ d->nodes.used_front = 1;
301+ d->nodes.nodes = malloc(sizeof(node) * 1000);
302+
303+ d->nodes.nodes[0].name = mkStr(0, 0);
304+ d->nodes.nodes[0].outer = mkStr(0, slen);
305+ d->nodes.nodes[0].inner = mkStr(0, slen);
306+ d->nodes.nodes[0].attrs = mkStr(0, 0);
307+ int p = 0;
308+ parse_content(d, &p);
309+ if (p != slen && d->error_message == NULL)
310+ {
311+ printf("%i vs %i\n", p, slen);
312+ d->error_message = strdup("Trailing junk at the end of the document");
313+ }
314+ return d;
315+}