on master help sign in
More work
commitf5e23f6c769e54d49719ae17aefccc479d730a6f authorNeil Mitchell <ndmitchell@gmail.com> dateSat 26 Nov 2016, 12:55 (10 years ago) parentbbf756c
4 files changed+218−83
go.bat+1 hexml.c+200−82 hexml.h+7 main.c+10−1
go.bat+1
@@ -1 +1,2 @@
1+rem sos -p .*.c -c "go 2>&1 | head -n12"
12gcc -c hexml.c && gcc -c main.c && gcc -o hexml hexml.o main.o && hexml
hexml.c+200−82
@@ -5,17 +5,27 @@
55#include <ctype.h>
66#include <stdio.h>
77
8+typedef int bool;
9+
810/////////////////////////////////////////////////////////////////////
911// TYPES
1012
11−str mkStr(int32_t start, int32_t length)
13+int static inline end(str s){return s.start + s.length;}
14+
15+str static inline start_length(int32_t start, int32_t length)
1216{
17+ if (start < 0 || length < 0) assert(0);
1318 str res;
1419 res.start = start;
1520 res.length = length;
1621 return res;
1722}
1823
24+str static inline start_end(int32_t start, int32_t end)
25+{
26+ return start_length(start, end - start);
27+}
28+
1929typedef struct
2030{
2131 int size;
@@ -51,21 +61,111 @@ struct document
5161{
5262 char* body; // pointer to initial argument, not owned by us
5363
54− int cursor; // things only used while parsing
55− int length;
64+ // things only used while parsing
65+ int cursor; // pointer to where we are in body
66+ int length; // total length of body
67+ // if cursor is >= length we have gone past the end
5668
5769 char* error_message;
58− node* node;
5970 node_buffer nodes;
6071 attr_buffer attrs;
6172};
6273
6374
75+/////////////////////////////////////////////////////////////////////
76+// RENDER CODE
77+
78+typedef struct
79+{
80+ document* d;
81+ char* buffer;
82+ int length;
83+ int cursor;
84+} render;
85+
86+void static inline render_char(render* r, char c)
87+{
88+ if (r->cursor < r->length)
89+ r->buffer[r->cursor] = c;
90+ r->cursor++;
91+}
92+
93+void static inline bound(char* msg, int index, int mn, int mx)
94+{
95+ if (index < mn || index > mx)
96+ {
97+ printf("Bounds checking failed %s, got %i, which should be in %i:%i\n", msg, index, mn, mx);
98+ assert(0);
99+ }
100+}
101+
102+void static inline bound_str(char* msg, str s, int mn, int mx)
103+{
104+ if (s.length < 0) assert(0);
105+ bound(msg, s.start, mn, mx);
106+ bound(msg, end(s), mn, mx);
107+}
108+
109+void render_str(render* r, str s)
110+{
111+ bound_str("render_str", s, 0, r->d->length);
112+ render_char(r, '[');
113+ for (int i = 0; i < s.length; i++)
114+ render_char(r, r->d->body[s.start + i]);
115+ render_char(r, ']');
116+}
117+
118+void render_tag(render* r, node* n);
119+
120+void render_content(render* r, node* n)
121+{
122+ bound_str("render_conent inner", n->inner, 0, r->d->length);
123+ bound_str("render_conent nodes", n->nodes, 0, r->d->nodes.used_front);
124+ bound_str("render_conent attrs", n->attrs, 0, r->d->attrs.used);
125+
126+ int done = n->inner.start;
127+ printf("%i\n", n->nodes.length);
128+ for (int i = 0; i < n->nodes.length; i++)
129+ {
130+ node* x = &r->d->nodes.nodes[n->nodes.start + i];
131+ render_str(r, start_end(done, x->outer.start));
132+ done = end(x->outer);
133+ render_tag(r, x);
134+ }
135+ render_str(r, start_end(done, end(n->inner)));
136+}
137+
138+void render_tag(render* r, node* n)
139+{
140+ render_char(r, '<');
141+ render_str(r, n->name);
142+ render_char(r, '>');
143+ render_content(r, n);
144+ render_char(r, '<');
145+ render_char(r, '/');
146+ render_str(r, n->name);
147+ render_char(r, '>');
148+}
149+
150+int document_render(document* d, char* buffer, int length)
151+{
152+ render r;
153+ r.d = d;
154+ r.buffer = buffer;
155+ r.length = length;
156+ r.cursor = 0;
157+ node* me = document_node(d);
158+ assert(me->nodes.start == 1 && me->nodes.length == 1);
159+ render_content(&r, document_node(d));
160+ return r.cursor;
161+}
162+
163+
64164/////////////////////////////////////////////////////////////////////
65165// NOT THE PARSER
66166
67167char* document_error(document* d){return d->error_message;}
68−node* document_node(document* d){return d->node;}
168+node* document_node(document* d){return &d->nodes.nodes[0];}
69169str node_inner(node* n){return n->inner;}
70170str node_outer(node* n){return n->outer;}
71171
@@ -117,17 +217,45 @@ node* node_nextChildBy(document* d, node* parent, node* prev, char* s, int slen)
117217
118218
119219/////////////////////////////////////////////////////////////////////
120−// PARSER PRIMITIVES
220+// PARSER COMBINATORS
121221
122−int has(document* d, int ask){return (d->length - d->cursor) >= ask;}
222+char static inline peekAt(document* d, int i)
223+{
224+ int j = d->cursor + i;
225+ return j >= d->length ? 0 : d->body[j];
226+}
123227
228+void static inline skip(document* d, int i){d->cursor += i;}
229+char static inline peek(document* d){return peekAt(d, 0);}
230+char static inline get(document* d){char c = peek(d); skip(d, 1); return c;}
231+
232+// Remove whitespace characters from the cursor while they are still whitespace
124233void trim(document* d)
125234{
126− while (has(d, 1) && isspace(grab(d->body[*p]))
127− *p += 1;
235+ while (isspace(peek(d)))
236+ skip(d, 1);
237+}
238+
239+// Find this character form the cursor onwards, if true adjust the cursor to that char, otherwise leave it at the end
240+bool find(document* d, char c)
241+{
242+ char* start = &d->body[d->cursor];
243+ char* end = memchr(start, c, d->length - d->cursor);
244+ if (end == NULL)
245+ {
246+ d->cursor = d->length;
247+ return 0;
248+ }
249+ else
250+ {
251+ d->cursor += end - start;
252+ return 1;
253+ }
128254}
129255
130256
257+/////////////////////////////////////////////////////////////////////
258+// PARSING CODE
131259
132260void node_alloc(node_buffer* b, int ask)
133261{
@@ -145,7 +273,8 @@ void attr_alloc(attr_buffer* b, int ask)
145273
146274void lexeme(char* msg, document* d, str s)
147275{
148− printf("Lexeme: %s, %i:%i, %.*s\n", msg, s.start, s.length, s.length, &d->body[s.start]);
276+ if (1)
277+ printf("Lexeme: %s, %i:%i, %.*s\n", msg, s.start, s.length, s.length, &d->body[s.start]);
149278}
150279
151280int isName(char c)
@@ -155,82 +284,72 @@ int isName(char c)
155284
156285// you now expect a name, perhaps preceeded by whitespace
157286// the name may be empty
158−str parse_name(document* d, int* p)
287+str parse_name(document* d)
159288{
160− printf("Before trim: %i\n", *p);
161− trim(d, p);
162− printf("After trim: %i %c\n", *p, d->body[*p]);
289+ trim(d);
163290 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;
291+ res.start = d->cursor;
292+ while (isName(peek(d)))
293+ get(d);
294+ res.length = d->cursor - res.start;
169295 lexeme("parse_name", d, res);
170296 return res;
171297}
172298
173−str parse_attrval(document* d, int* p)
299+str parse_attrval(document* d)
174300{
175− trim(d, p);
301+ trim(d);
176302 str res;
177− if (*p < d->bodylen || d->body[*p] != '=') return mkStr(0,0);
178− trim(d, p);
179− return parse_name(d, p);
303+ if (peek(d) != '=') return start_length(0,0);
304+ trim(d);
305+ return parse_name(d);
180306}
181307
182308
183309// seen a tag name, now looking for attributes terminated by >
184310// puts the attributes it finds in the attribute buffer
185−void parse_attributes(document* s, int* p)
311+str parse_attributes(document* d)
186312{
187−
188−
313+ str res;
314+ res.start = d->attrs.used;
315+ // do attribute parsing here
316+ res.length = d->attrs.used - res.start;
317+ return res;
189318}
190319
191−void parse_content(document* d, int* p);
320+str parse_content(document* d);
192321
193322
194323// Add a new entry into tag, am at a '<'
195−void parse_tag(document* d, int* p)
324+void parse_tag(document* d)
196325{
197− printf("Before parse_tag %i\n", *p);
198− assert(d->body[*p] == '<');
326+ char c = get(d);
327+ assert(c == '<');
199328
200329 node_alloc(&d->nodes, 1);
201330 int me = d->nodes.used_back;
202331 d->nodes.used_back++;
203332
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;
333+ d->nodes.nodes[me].name = parse_name(d);
334+ d->nodes.nodes[me].attrs = parse_attributes(d);
211335
212− if (d->bodylen - *p >= 2 &&
213− (d->body[*p] == '/' || d->body[*p] == '?') &&
214− d->body[*p + 1] == '>')
336+ c = peek(d);
337+ if ((c == '/' || c == '?') && peekAt(d, 1) == '>')
215338 {
216− *p += 2;
339+ skip(d, 2);
340+ d->nodes.nodes[me].nodes = start_length(0, 0);
217341 return;
218342 }
219−
220− d->nodes.nodes[me].nodes.start = 42;
221− parse_content(d, p);
222− d->nodes.nodes[me].nodes.length = 42;
343+ d->nodes.nodes[me].nodes = parse_content(d);
223344
224345 if (d->error_message != NULL) return;
225− if (d->bodylen - *p >= 2 &&
226− d->body[*p] == '<' &&
227− d->body[*p + 1] == '/')
346+ if (peek(d) == '<' && peekAt(d, 1) == '/')
228347 {
229− *p += 2;
230− str close = parse_name(d, p);
348+ skip(d, 2);
349+ str close = parse_name(d);
231350 lexeme("openning tag", d, d->nodes.nodes[me].name);
232351 lexeme("closing tag", d, close);
233− trim(d, p);
352+ trim(d);
234353 if (close.length == d->nodes.nodes[me].name.length &&
235354 memcmp(&d->body[close.start], &d->body[d->nodes.nodes[me].name.start], close.length) == 0)
236355 return;
@@ -240,47 +359,38 @@ void parse_tag(document* d, int* p)
240359 d->error_message = strdup("Weirdness when trying to close tags");
241360}
242361
243−// Parser until </
244−void parse_content(document* d, int* p)
362+// Parser until </, return the index of your node children
363+str parse_content(document* d)
245364{
246365 int before = d->nodes.used_back;
247366 while (d->error_message == NULL)
248367 {
249368 // 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)
369+ if (!find(d, '<'))
260370 {
261− // final character of string is <
262− printf("break 1\n");
263371 break;
264372 }
265− else if (d->body[*p + 1] == '/')
373+ if (peekAt(d, 1) == '/')
266374 {
267375 // have found a </
268− printf("break 2\n");
269376 break;
270377 }
271378 else
272379 {
273− printf("calling parse_tag\n");
274− parse_tag(d, p);
380+ printf("parsing a tag\n");
381+ parse_tag(d);
275382 }
276383 }
277384 int diff = d->nodes.used_back - before;
278385 node_alloc(&d->nodes, diff);
386+ str res = start_length(d->nodes.used_front, diff);
279387
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];
388+ printf("res %i %i\n", res.start, res.length);
389+ for (int i = 0; i < diff; i++)
390+ d->nodes.nodes[d->nodes.used_front + i] = d->nodes.nodes[d->nodes.size - d->nodes.used_back - i];
282391 d->nodes.used_front += diff;
283392 d->nodes.used_back -= diff;
393+ return res;
284394}
285395
286396document* document_parse(char* s, int slen)
@@ -289,9 +399,9 @@ document* document_parse(char* s, int slen)
289399
290400 document* d = malloc(sizeof(document));
291401 d->body = s;
292− d->bodylen = slen;
402+ d->cursor = 0;
403+ d->length = slen;
293404 d->error_message = NULL;
294− d->node = NULL;
295405 d->attrs.size = 0;
296406 d->attrs.used = 0;
297407 d->attrs.attrs = NULL;
@@ -300,15 +410,23 @@ document* document_parse(char* s, int slen)
300410 d->nodes.used_front = 1;
301411 d->nodes.nodes = malloc(sizeof(node) * 1000);
302412
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)
413+ d->nodes.nodes[0].name = start_length(0, 0);
414+ d->nodes.nodes[0].outer = start_length(0, slen);
415+ d->nodes.nodes[0].inner = start_length(0, slen);
416+ d->nodes.nodes[0].attrs = start_length(0, 0);
417+ d->nodes.nodes[0].nodes = parse_content(d);
418+
419+ node* me = &d->nodes.nodes[0];
420+// printf("Got %i %i\n", me->nodes.start, me->nodes.length);
421+ assert(me->nodes.start == 1 && me->nodes.length == 1);
422+
423+ me = &d->nodes.nodes[1];
424+// printf("Got %i %i\n", me->nodes.start, me->nodes.length);
425+ assert(me->nodes.length == 0);
426+
427+ if (d->cursor < d->length && d->error_message == NULL)
310428 {
311− printf("%i vs %i\n", p, slen);
429+ printf("%i vs %i\n", d->cursor, d->length);
312430 d->error_message = strdup("Trailing junk at the end of the document");
313431 }
314432 return d;
hexml.h+7
@@ -23,6 +23,13 @@ typedef struct
2323// Must use document_free to release the memory.
2424document* document_parse(char* s, int slen);
2525
26+// Free the memory returned from document_parse.
27+void document_free(document* d);
28+
29+// generate a fresh document with the same semantics
30+// requires an input buffer, returns the size of the rendered document
31+int document_render(document* d, char* buffer, int length);
32+
2633// return either NULL (successful parse) or the error message
2734char* document_error(document* d);
2835
main.c+10−1
@@ -1,13 +1,22 @@
11#include "hexml.h"
22#include <stdio.h>
3+#include <malloc.h>
34
45void main()
56{
7+ setbuf(stdout, NULL);
68 char* body = "<?xml version=\"1.0\" lang=\"ja\"?><foo><bar baz=\"qux\">quux</bar><bar baz=\"bar\">hoge</bar><piyo>&gt;piyopiyo&lt;</piyo></foo><foo2 />";
9+ body = "<test>X</test> ";
710 document* doc = document_parse(body, -1);
811 char* err = document_error(doc);
912 if (err == NULL)
10− printf("Parse successful\n");
13+ {
14+ int len = document_render(doc, NULL, 0);
15+ char* s = malloc(len + 1);
16+ document_render(doc, s, len);
17+ s[len] = 0;
18+ printf("Parse successful, %s\n", s);
19+ }
1120 else
1221 printf("Parse failed, %s\n", err);
1322}