home
readme
diff
tree
note
docs
0commit 3aeecff454c8d32c0165e8db4f3804b5c4c24cad
1Author: lakefox <mason@lakefox.net>
2Date: Sat Feb 7 17:11:07 2026 -0700
3
4 html_parser converted
5
6diff --git a/include/parser.h b/include/parser.h
7index 2f5397e..a26ba1f 100755
8--- a/include/parser.h
9+++ b/include/parser.h
10@@ -3,6 +3,6 @@
11 #define PARSER_H
12
13 // Declare the parsing functions
14-void parseDocument(SymbolTable*, Node*, char*, int);
15+void html_parser(SymbolTable*, Node*, char*, int);
16
17 #endif
18diff --git a/playground/html_parser.c b/playground/html_parser.c
19index ec73536..3c91e6f 100644
20--- a/playground/html_parser.c
21+++ b/playground/html_parser.c
22@@ -32,13 +32,39 @@ char* read_file_to_string(const char* filename) {
23 return buffer;
24 }
25
26+
27+void benchmark_html_parser(SymbolTable* sym, const char* file) {
28+ struct timespec start, end;
29+
30+ int iterations = 10000;
31+
32+ // Get start time
33+ clock_gettime(CLOCK_MONOTONIC, &start);
34+
35+ for (int i = 0; i < iterations; i++) {
36+ Node* root = CREATE_ELEMENT(sym, "root");
37+ html_parser(sym, root, file, strlen(file));
38+
39+ FREE_NODE(root);
40+ }
41+
42+ // Get end time
43+ clock_gettime(CLOCK_MONOTONIC, &end);
44+
45+ // Calculate time difference in seconds
46+ double total_time = (end.tv_sec - start.tv_sec) + (end.tv_nsec - start.tv_nsec) / 1e9;
47+ printf("Average execution time: %.9f seconds (%f us)\n",
48+ total_time / iterations,
49+ (total_time / iterations) * 1000000);
50+}
51+
52 int main() {
53 SymbolTable* sym = create_symbol_table(4096);
54 Node* root = CREATE_ELEMENT(sym, "root");
55
56- char* file = read_file_to_string("./tests/easy.html");
57+ char* file = read_file_to_string("./tests/index.html");
58
59- parseDocument(sym, root, file, strlen(file));
60+ html_parser(sym, root, file, strlen(file));
61
62 size_t size = 10000;
63 char* html = calloc(size, sizeof(char));
64@@ -47,6 +73,9 @@ int main() {
65
66 printf("%s\n", html);
67
68+
69+ benchmark_html_parser(sym, file);
70+
71 free(html);
72 free(file);
73 FREE_SYMBOL_TABLE(sym);
74diff --git a/src/grim.c b/src/grim.c
75index 28d751d..bc22ce7 100755
76--- a/src/grim.c
77+++ b/src/grim.c
78@@ -82,7 +82,7 @@ Node* CREATE_ELEMENT(SymbolTable* sym, const char* value) {
79 Node* el = malloc(sizeof(Node));
80 memset(el, 0, sizeof(Node));
81 el->tag_name = intern_string(sym, value);
82- el->innertext = NULL;
83+ el->innertext = '\0';
84 el->id = -1;
85 return el;
86 }
87@@ -168,8 +168,19 @@ char* INNER_HTML(SymbolTable* sym, Node* node, char** buffer, size_t* size, int
88 append_to_buffer(buffer, size, temp);
89 }
90
91+ if (node->attr_count > 0) {
92+ for (int i = 0; i < node->attr_count; i++) {
93+ sprintf(temp, " %s=\"%s\"", node->attributes[i].key, node->attributes[i].value);
94+ append_to_buffer(buffer, size, temp);
95+ }
96+ }
97+
98 append_to_buffer(buffer, size, ">\n");
99
100+ if (node->innertext && strlen(node->innertext)) {
101+ append_to_buffer(buffer, size, node->innertext);
102+ }
103+
104 Node* child = node->first_child;
105
106 while (child) {
107diff --git a/src/parser.c b/src/parser.c
108index 40e88f4..8c85c43 100755
109--- a/src/parser.c
110+++ b/src/parser.c
111@@ -3,104 +3,6 @@
112 #include <ctype.h>
113
114 /*
115-std::unordered_map<std::string, std::string> parseAttributes(std::string_view token) {
116- std::unordered_map<std::string, std::string> attrs;
117-
118- bool inQuote = false;
119- bool escaped = false;
120- char quoteType = '"';
121-
122- std::string word;
123- for (unsigned short i = 0; i < token.length(); i++) {
124- char current = token[i];
125- if ((current == '"' || current == '\'') && !escaped) {
126- if (inQuote && current == quoteType) {
127- inQuote = false;
128- } else if (!inQuote) {
129- inQuote = true;
130- quoteType = current;
131- }
132- }
133-
134- if (current == '\\') { //'
135- escaped = true;
136- continue;
137- }
138-
139- if ((!std::isspace(current) || inQuote) && i != token.length()-1) {
140- word += current;
141- } else {
142- if (i == token.length()-1) {
143- word += current;
144- }
145-
146- std::string name = "";
147- std::string value = "";
148- bool setValue = false;
149-
150- for (auto c : word) {
151- if (c == '=') {
152- setValue = true;
153- continue;
154- }
155-
156- if (setValue) {
157- value += c;
158- } else {
159- name += c;
160- }
161- }
162-
163- word = "";
164-
165- std::string trimmedName = "";
166- for (auto c : name) {
167- if (!std::isspace(c)) {
168- trimmedName += c;
169- }
170- }
171-
172- if (attrs.size() == 0) {
173- value = '"'+trimmedName+'"';
174- trimmedName = "tagName";
175- }
176-
177- std::string trimmedValue = "";
178- int sliceStart = 0;
179- int sliceEnd = value.length();
180-
181- if (value.length() >= 2) {
182- for (unsigned short t = 0; t < value.length(); t++) {
183- if (!std::isspace(value[t])) {
184- sliceStart = t;
185- break;
186- }
187- }
188-
189- for (int t = value.length()-1; t >= 0; t--) {
190- // Trim the trailing / on self closing elements if there isn't a space in between
191- if (!std::isspace(value[t]) && value[t] != '/') {
192- sliceEnd = t;
193- break;
194- }
195- }
196-
197- // Add and subtract 1 from each side to remove quotes
198- for (unsigned short t = sliceStart+1; t < sliceEnd; t++) {
199- trimmedValue += value[t];
200- }
201- }
202-
203- if (trimmedValue.length() == 0) {
204- trimmedValue = "";
205- }
206-
207- attrs[trimmedName] = trimmedValue;
208- }
209- }
210-
211- return attrs;
212-}
213
214 static const std::unordered_map<std::string, KeyType> keySearch {
215 {"accent-color", KeyType::ACCENT_COLOR},
216@@ -1254,7 +1156,7 @@ std::vector<Unit> getUnit(std::string value, SymbolTable* externalTable = nullpt
217
218 return units;
219 }
220-
221+*/
222 Style parseCSS(std::istream& inputStream) {
223 // nowhitespace: value(can have whitespace); = property
224 // a selector name is anything before a { up to a ; or a }
225@@ -1430,6 +1332,7 @@ Style parseCSS(std::istream& inputStream) {
226 return root;
227 }
228
229+/*
230 std::string get_working_path() {
231 char temp [ PATH_MAX ];
232
233@@ -1477,8 +1380,7 @@ std::string pathJoin(const std::string& p1, const std::string& p2) {
234 }
235 */
236
237-void parseDocument(SymbolTable* sym, Node* root, const char* input, int size) {
238- printf("ROOT TAGNAME: %s, %i\n", symbol_lookup_string(sym, root->tag_name), root->tag_name);
239+void html_parser(SymbolTable* sym, Node* root, const char* input, int size) {
240 Node* currentNode = root;
241
242 bool inTag = false;
243@@ -1498,9 +1400,42 @@ void parseDocument(SymbolTable* sym, Node* root, const char* input, int size) {
244
245 char attr_value[MAX_STR_LEN];
246
247+ bool inRawMode = false;
248+ char rawTagName[MAX_STR_LEN] = "";
249+
250 for (int i = 0; i < size; i++) {
251 char current = input[i];
252-
253+
254+ if (inRawMode) {
255+ if (current == '<' && i + 2 < size && input[i+1] == '/') {
256+ int tagLen = strlen(rawTagName);
257+ if (strncmp(&input[i+2], rawTagName, tagLen) == 0) {
258+
259+ token[token_length] = '\0';
260+ if (token_length > 0) {
261+ Node* textNode = CREATE_ELEMENT(sym, "text");
262+ SET_INNERTEXT(textNode, token);
263+ APPEND_CHILD(currentNode, textNode);
264+ }
265+
266+ if (currentNode->parent != NULL) {
267+ currentNode = currentNode->parent;
268+ }
269+
270+ inRawMode = false;
271+ token_length = 0;
272+
273+ while (i < size && input[i] != '>') i++;
274+ continue;
275+ }
276+ }
277+ if (token_length < 1023) {
278+ token[token_length++] = current;
279+ }
280+ continue;
281+ }
282+
283+
284 // Finds the --> and removes it then resets inComment
285 if (inComment) {
286 // added the peek to prevent hitting on every -
287@@ -1522,18 +1457,29 @@ void parseDocument(SymbolTable* sym, Node* root, const char* input, int size) {
288
289
290 if (inTag) {
291+ if (token_length == 0 && current == '/') {
292+ if (currentNode->parent != NULL) {
293+ currentNode = currentNode->parent;
294+ }
295+
296+ while (i < size && input[i] != '>') i++;
297+ inTag = false;
298+ tagname_set = false;
299+ token_length = 0;
300+ continue;
301+ }
302+
303 if ((current == '"' || current == '\'') && !escaped) {
304 if (inQuote && current == quoteType) {
305 inQuote = false;
306 if (attr_name[0] != '\0' && attr_name_set) {
307 if (strcmp(attr_name, "id") == 0) {
308- SET_ID(sym, currentNode, token+1);
309+ SET_ID(sym, currentNode, token);
310 } else if (strcmp(attr_name, "class") == 0) {
311
312 } else {
313- SET_ATTRIBUTE(currentNode, attr_name, token+1);
314+ SET_ATTRIBUTE(currentNode, attr_name, token);
315 }
316- token[0] = '\0';
317 token_length = 0;
318 attr_name[0] = '\0';
319 attr_name_set = false;
320@@ -1546,48 +1492,72 @@ void parseDocument(SymbolTable* sym, Node* root, const char* input, int size) {
321 continue;
322 }
323
324- printf("%c %i %i\n", current, inQuote, tagname_set);
325+ if (!tagname_set && !inQuote && (isspace(current) || current == '>')) {
326+ token[token_length] = '\0';
327+ if (token_length > 0) {
328+ bool self_closing = false;
329
330- if ((isspace(current) || current == '>') && !inQuote && !tagname_set) {
331- if (token[0] == '/') {
332- if (currentNode->parent != NULL) {
333- currentNode = currentNode->parent;
334+ if (token[token_length - 1] == '/') {
335+ self_closing = true;
336+ token[token_length - 1] = '\0';
337 }
338
339- token[0] = '\0';
340- token_length = 0;
341+ strcpy(tagname, token);
342+
343+ if (tagname[0] != '!') {
344+ Node* node = CREATE_ELEMENT(sym, tagname);
345+ APPEND_CHILD(currentNode, node);
346+ if (!self_closing) {
347+ currentNode = node;
348+ if (strcmp(tagname, "script") == 0 ||
349+ strcmp(tagname, "style") == 0 ||
350+ strcmp(tagname, "textarea") == 0) {
351+ inRawMode = true;
352+ strcpy(rawTagName, tagname);
353+ }
354+ }
355+ tagname_set = true;
356+ } else {
357+ while (i < size && input[i] != '>') i++;
358+ inTag = false;
359+ tagname_set = false;
360+ token_length = 0;
361+ continue;
362+ }
363+ }
364
365- continue;
366+ token_length = 0;
367+ if (current == '>') {
368+ inTag = false;
369+ tagname_set = false;
370 }
371+ continue;
372+ }
373
374+ if (!inQuote && current == '=' && !attr_name_set) {
375 token[token_length] = '\0';
376- strcpy(tagname, token);
377
378- printf("TAGNAME %s\n", tagname);
379+ char* name_ptr = token;
380+ while(isspace(*name_ptr)) name_ptr++;
381+ strcpy(attr_name, name_ptr);
382
383- token[0] = '\0';
384+ attr_name_set = true;
385 token_length = 0;
386+ continue;
387+ }
388
389- if (tagname[0] == '!') continue;
390-
391- tagname_set = true;
392-
393- Node* node = CREATE_ELEMENT(sym, tagname);
394- printf("CREATING ELEMENT: %s, PARENT: %s\n", tagname, symbol_lookup_string(sym, currentNode->tag_name));
395- APPEND_CHILD(currentNode, node);
396-
397- currentNode = node;
398- } else if (current == '>' && !inQuote && !escaped) {
399+ if (!inQuote && current == '>') {
400+ if (token_length > 0 && token[token_length-1] == '/') {
401+ if (currentNode->parent != NULL) {
402+ currentNode = currentNode->parent;
403+ }
404+ }
405 inTag = false;
406 tagname_set = false;
407- tagname[0] = '\0';
408- } else if (current == '=' && !attr_name_set && !inQuote) {
409- token[token_length] = '\0';
410- strcpy(attr_name, token+1);
411- token[0] = '\0';
412 token_length = 0;
413- attr_name_set = true;
414+ continue;
415 }
416+
417 } else if (!escaped && current == '<') {
418 // if the next character is a !
419 if (i+3 < size && input[i+1] == '!') {
420@@ -1599,28 +1569,22 @@ void parseDocument(SymbolTable* sym, Node* root, const char* input, int size) {
421 }
422 }
423 inTag = true;
424-
425- // Here's where you actually make the text node and above the real nodes
426- // can also prob make the vector of tokens a single variable
427- // it really just needs to be a data string bc we know the type based on inTag
428
429+ bool has_text = false;
430
431- bool hasText = false;
432 for (int j = 0; j < token_length; j++) {
433- if (!isspace(token[j])) {
434- hasText = true;
435+ if (!isspace(token[j]) && token[j] != '\n') {
436+ has_text = true;
437 break;
438 }
439 }
440-/*
441- if (hasText) {
442+
443+ if (has_text) {
444 Node* node = CREATE_ELEMENT(sym, "text");
445- token[token_length++] = '\0';
446 SET_INNERTEXT(node, token);
447 APPEND_CHILD(currentNode, node);
448- }*/
449-
450-
451+ }
452+
453 token[0] = '\0';
454 token_length = 0;
455 continue;
456diff --git a/tests/easy.html b/tests/easy.html
457index 54c0e15..1863b8e 100644
458--- a/tests/easy.html
459+++ b/tests/easy.html
460@@ -12,7 +12,7 @@
461 Hello world
462 </h1>
463 <br/>
464- <br />
465+ <br/>
466 <input type="text"/>
467 <p id="test2">
468 this is a test