home
readme
diff
tree
note
docs
0commit f0a858ee9f6a16e02b06f916c25cb79f7f1447eb
1Author: lakefox <mason@lakefox.net>
2Date: Sat Feb 7 15:42:21 2026 -0700
3
4 Saving
5
6diff --git a/Makefile b/Makefile
7index 7ff62e0..64a7691 100755
8--- a/Makefile
9+++ b/Makefile
10@@ -16,8 +16,8 @@ main: main.c build/grim.o build/parser.o
11 build/grim.o: src/grim.c include/grim.h | build
12 ${CC} ${CCFLAGS} -Iinclude/ -c src/grim.c -o build/grim.o
13
14-build/parser.o: src/parser.cc include/parser.h | build
15- ${CC} ${CCFLAGS} -Iinclude/ -c src/parser.cc -o build/parser.o
16+build/parser.o: src/parser.c include/parser.h | build
17+ ${CC} ${CCFLAGS} -Iinclude/ -c src/parser.c -o build/parser.o
18
19 # --- Tests ---
20
21@@ -46,6 +46,9 @@ bench_%: build/%
22 build/parseselectorparts_playground: playground/parseselectorparts.c build/grim.o | build
23 ${CC} ${CCFLAGS} -Iinclude/ playground/parseselectorparts.c build/grim.o -o build/parseselectorparts_playground
24
25+build/html_parser_playground: playground/html_parser.c build/grim.o build/parser.o | build
26+ ${CC} ${CCFLAGS} -Iinclude/ playground/html_parser.c build/grim.o build/parser.o -o build/html_parser_playground
27+
28
29
30 # --- Prebuild ---
31diff --git a/include/grim.h b/include/grim.h
32index 53ee278..78b577b 100755
33--- a/include/grim.h
34+++ b/include/grim.h
35@@ -1,17 +1,15 @@
36 // Grim.h
37 #ifndef GRIM_H
38 #define GRIM_H
39-
40 #include <stdio.h>
41 #include <stddef.h>
42-
43+#include <stdint.h>
44
45 #define MAX_TOKENS 32
46 #define MAX_GROUPS 16
47 #define MAX_STR_LEN 128
48-
49-
50-
51+#define MAX_ATTRS 16
52+#define MAX_SYMBOLS 4096
53
54 /*
55 typedef enum {
56@@ -227,32 +225,97 @@ struct Snapshot {
57 Context* ctx;
58 size_t pointer;
59 };
60+*/
61
62-struct dom_list {
63- std::vector<Node> elements;
64-}
65+typedef struct {
66+ char* string;
67+ uint32_t hash;
68+ int id;
69+} SymbolEntry;
70
71-struct Node {
72- std::vector<Pair> attributes;
73- std::vector<Pair> computed_style;
74-}
75+typedef struct {
76+ SymbolEntry* entries;
77+ const char** strings;
78+
79+ int capacity;
80+ int count;
81+} SymbolTable;
82
83-struct Pair {
84- std::string key;
85- std::string value;
86-}
87-*/
88 typedef struct {
89- char items[MAX_TOKENS][MAX_STR_LEN];
90- int size;
91+ char key[MAX_STR_LEN];
92+ char value[MAX_STR_LEN];
93+} Attribute;
94+
95+typedef int Symbol;
96+
97+typedef struct Node {
98+ Symbol tag_name;
99+ Symbol id;
100+ Symbol* class_list;
101+ int classes_count;
102+
103+ // Attributes
104+ Attribute attributes[MAX_ATTRS];
105+ int attr_count;
106+
107+ // Tree Navigation
108+ struct Node* parent;
109+ struct Node* first_child;
110+ struct Node* next_sibling;
111+ struct Node* prev_sibling;
112+
113+ // Content
114+ char* innertext;
115+} Node;
116+
117+typedef struct {
118+ char items[MAX_TOKENS][MAX_STR_LEN];
119+ int size;
120 } SelectorParts;
121
122 typedef struct {
123- SelectorParts items[MAX_GROUPS];
124- int size;
125+ SelectorParts items[MAX_GROUPS];
126+ int size;
127 } ParsedSelector;
128
129 ParsedSelector parseSelectorParts(const char*);
130+
131+void SET_TAG_NAME(SymbolTable*, Node*, char*);
132+
133+int intern_string(SymbolTable* st, const char* str);
134+
135+const char* symbol_lookup_string(SymbolTable* st, int id);
136+
137+
138+Node* CREATE_ELEMENT(SymbolTable* sym, const char* value);
139+
140+void APPEND_CHILD(Node* parent, Node* child);
141+
142+void SET_ATTRIBUTE(Node* n, char* key, char* value);
143+
144+const char* GET_ATTRIBUTE(Node* n, char* key);
145+
146+void SET_TAG_NAME(SymbolTable* sym, Node* n, char* value);
147+
148+const char* GET_TAG_NAME(SymbolTable* sym, Node* n);
149+
150+void SET_ID(SymbolTable* sym, Node* n, char* value);
151+
152+const char* GET_ID(SymbolTable* sym, Node* n);
153+
154+void SET_INNERTEXT(Node* n, char* value);
155+
156+char* GET_INNERTEXT(Node* n);
157+
158+SymbolTable* create_symbol_table(int capacity);
159+
160+void FREE_SYMBOL_TABLE(SymbolTable* st);
161+
162+void FREE_NODE(Node* n);
163+
164+
165+char* INNER_HTML(SymbolTable* sym, Node* node, char** buffer, size_t* size, int depth);
166+
167 /*
168 bool testSelector(Node*, std::vector<std::vector<std::string>>);
169
170diff --git a/include/parser.h b/include/parser.h
171index 7f5b328..2f5397e 100755
172--- a/include/parser.h
173+++ b/include/parser.h
174@@ -2,17 +2,7 @@
175 #ifndef PARSER_H
176 #define PARSER_H
177
178-struct SymbolTable {
179- std::unordered_map<std::string, int> table;
180- int next;
181-
182- int get(std::string_view name);
183-};
184-
185 // Declare the parsing functions
186-std::unordered_map<std::string, std::string> parseAttributes(std::string_view& token);
187-ParsedDocument parseDocument(std::istream& inputStream);
188-Style parseCSS(std::istream& inputStream);
189-std::vector<Unit> getUnit(std::string, SymbolTable* external = nullptr);
190+void parseDocument(SymbolTable*, Node*, char*, int);
191
192 #endif
193diff --git a/playground/html_parser.c b/playground/html_parser.c
194new file mode 100644
195index 0000000..ec73536
196--- /dev/null
197+++ b/playground/html_parser.c
198@@ -0,0 +1,54 @@
199+#define _POSIX_C_SOURCE 199309L
200+#include <stdio.h>
201+#include <stdlib.h>
202+#include <time.h>
203+#include <string.h>
204+
205+#include "../include/grim.h"
206+#include "../include/parser.h"
207+
208+#include <stdbool.h>
209+
210+char* read_file_to_string(const char* filename) {
211+ FILE* file = fopen(filename, "rb");
212+ if (!file) {
213+ perror("Failed to open file");
214+ return NULL;
215+ }
216+
217+ // Seek to the end to find the file size
218+ fseek(file, 0, SEEK_END);
219+ long length = ftell(file);
220+ fseek(file, 0, SEEK_SET);
221+
222+ // Allocate memory (+1 for null terminator)
223+ char* buffer = malloc(length + 1);
224+ if (buffer) {
225+ fread(buffer, 1, length, file);
226+ buffer[length] = '\0'; // Always null-terminate strings in C!
227+ }
228+
229+ fclose(file);
230+ return buffer;
231+}
232+
233+int main() {
234+ SymbolTable* sym = create_symbol_table(4096);
235+ Node* root = CREATE_ELEMENT(sym, "root");
236+
237+ char* file = read_file_to_string("./tests/easy.html");
238+
239+ parseDocument(sym, root, file, strlen(file));
240+
241+ size_t size = 10000;
242+ char* html = calloc(size, sizeof(char));
243+
244+ INNER_HTML(sym, root, &html, &size, 0);
245+
246+ printf("%s\n", html);
247+
248+ free(html);
249+ free(file);
250+ FREE_SYMBOL_TABLE(sym);
251+ FREE_NODE(root);
252+}
253diff --git a/src/grim.c b/src/grim.c
254index 0c6051e..28d751d 100755
255--- a/src/grim.c
256+++ b/src/grim.c
257@@ -2,8 +2,10 @@
258 //#include "parser.h"
259 #include <stdio.h>
260 #include <stddef.h>
261+#include <stdint.h>
262 #include <string.h>
263 #include <ctype.h>
264+#include <malloc.h>
265
266 // +------------------------------------------------------+
267 // |(TOC) Table of contents |
268@@ -25,6 +27,159 @@
269 // | (p98ym) Utils |
270 // +---------------------------------------------------+
271
272+#define FNV_OFFSET_BASIS 2166136261U
273+#define FNV_PRIME 16777619U
274+
275+uint32_t hash_string(const char* str) {
276+ uint32_t hash = FNV_OFFSET_BASIS;
277+ while (*str) {
278+ hash ^= (uint8_t)*str++;
279+ hash *= FNV_PRIME;
280+ }
281+ return hash;
282+}
283+
284+SymbolTable* create_symbol_table(int capacity) {
285+ SymbolTable* st = malloc(sizeof(SymbolTable));
286+ st->capacity = capacity;
287+ st->count = 0;
288+ st->entries = calloc(capacity, sizeof(SymbolEntry));
289+ st->strings = calloc(capacity, sizeof(char*));
290+ return st;
291+}
292+
293+int intern_string(SymbolTable* st, const char* str) {
294+ uint32_t hash = hash_string(str);
295+ int index = hash % st->capacity;
296+
297+ // Linear Probing: Find the string or an empty slot
298+ while (st->entries[index].string != NULL) {
299+ if (st->entries[index].hash == hash && strcmp(st->entries[index].string, str) == 0) {
300+ return st->entries[index].id; // Found existing
301+ }
302+ index = (index + 1) % st->capacity; // Move to next slot
303+ }
304+
305+ char* duplicated_str = strdup(str);
306+
307+ int new_id = st->count++;
308+
309+ // Not found, add new entry
310+ st->entries[index].string = duplicated_str;
311+ st->entries[index].hash = hash;
312+ st->entries[index].id = new_id;
313+
314+ st->strings[new_id] = duplicated_str;
315+ return st->entries[index].id;
316+}
317+
318+const char* symbol_lookup_string(SymbolTable* st, int id) {
319+ if (id < 0 || id >= st->count) return "";
320+ return st->strings[id];
321+}
322+
323+Node* CREATE_ELEMENT(SymbolTable* sym, const char* value) {
324+ Node* el = malloc(sizeof(Node));
325+ memset(el, 0, sizeof(Node));
326+ el->tag_name = intern_string(sym, value);
327+ el->innertext = NULL;
328+ el->id = -1;
329+ return el;
330+}
331+
332+void FREE_NODE(Node* n) {
333+ if (!n) return;
334+
335+ Node* child = n->first_child;
336+ while (child) {
337+ Node* next = child->next_sibling;
338+ FREE_NODE(child);
339+ child = next;
340+ }
341+
342+ if (n->innertext) {
343+ free(n->innertext);
344+ }
345+
346+ free(n);
347+}
348+
349+void FREE_SYMBOL_TABLE(SymbolTable* st) {
350+ if (!st) return;
351+
352+ for (int i = 0; i < st->count; i++) {
353+ free((void*)st->strings[i]);
354+ }
355+
356+ free(st->entries);
357+ free(st->strings);
358+ free(st);
359+}
360+
361+void APPEND_CHILD(Node* parent, Node* child) {
362+ child->parent = parent;
363+ if (!parent->first_child) {
364+ parent->first_child = child;
365+ } else {
366+ Node* last = parent->first_child;
367+ if (last->next_sibling) {
368+ while (last->next_sibling) {
369+ last = last->next_sibling;
370+ }
371+ last->next_sibling = child;
372+ child->prev_sibling = last;
373+
374+ } else {
375+ last->next_sibling = child;
376+ child->prev_sibling = last;
377+ }
378+ }
379+}
380+
381+void append_to_buffer(char** buffer, size_t* size, const char* text) {
382+ size_t current_len = strlen(*buffer);
383+ size_t text_len = strlen(text);
384+
385+ if (current_len + text_len + 1 > *size) {
386+ *size *= 2; // Double the capacity
387+ *buffer = realloc(*buffer, *size);
388+ if (!*buffer) {
389+ perror("Failed to reallocate buffer");
390+ }
391+ }
392+ strcat(*buffer, text);
393+}
394+
395+char* INNER_HTML(SymbolTable* sym, Node* node, char** buffer, size_t* size, int depth) {
396+ char temp[1024];
397+
398+ char tabs[depth+1];
399+
400+ for (int i = 0; i < depth; i++) {
401+ tabs[i] = '\t';
402+ }
403+ tabs[depth] = '\0';
404+
405+ sprintf(temp, "%s<%s", tabs, symbol_lookup_string(sym, node->tag_name));
406+ append_to_buffer(buffer, size, temp);
407+
408+ if (node->id > -1) {
409+ sprintf(temp, " id=\"%s\"", symbol_lookup_string(sym, node->id));
410+ append_to_buffer(buffer, size, temp);
411+ }
412+
413+ append_to_buffer(buffer, size, ">\n");
414+
415+ Node* child = node->first_child;
416+
417+ while (child) {
418+ INNER_HTML(sym, child, buffer, size, depth+1);
419+ child = child->next_sibling;
420+ }
421+
422+ sprintf(temp, "%s</%s>\n", tabs, symbol_lookup_string(sym, node->tag_name));
423+ append_to_buffer(buffer, size, temp);
424+}
425
426 // +---------------------------------------------------+
427 // | (tigfn) Attribute Handling |
428@@ -38,15 +193,52 @@
429 // handlers like ClassList (.classList) and StyleList
430 // (.style) for more information on each view
431 // f98y0 and lhujn respectively.
432-/*
433+
434 void SET_ATTRIBUTE(Node* n, char* key, char* value) {
435+ Attribute a;
436+ strcpy(a.key, key);
437+ strcpy(a.value, value);
438+ n->attributes[n->attr_count++] = a;
439+}
440
441+const char* GET_ATTRIBUTE(Node* n, char* key) {
442+ for (int i = 0; i < n->attr_count; i++) {
443+ if (strcmp(n->attributes[i].key, key)) {
444+ return n->attributes[i].value;
445+ }
446+ }
447+ return "";
448 }
449
450-void GET_ATTRIBUTE(Node* n, char* key) {
451+void SET_TAG_NAME(SymbolTable* sym, Node* n, char* value) {
452+ n->tag_name = intern_string(sym, value);
453+}
454
455+const char* GET_TAG_NAME(SymbolTable* sym, Node* n) {
456+ return symbol_lookup_string(sym, n->tag_name);
457 }
458-*/
459+
460+void SET_ID(SymbolTable* sym, Node* n, char* value) {
461+ n->id = intern_string(sym, value);
462+}
463+
464+const char* GET_ID(SymbolTable* sym, Node* n) {
465+ return symbol_lookup_string(sym, n->id);
466+}
467+
468+void SET_INNERTEXT(Node* n, char* value) {
469+ if (n->innertext != NULL) {
470+ free(n->innertext);
471+ }
472+ if (value != NULL) {
473+ n->innertext = strdup(value);
474+ }
475+}
476+
477+char* GET_INNERTEXT(Node* n) {
478+ return n->innertext;
479+}
480+
481 // +---------------------------------------------------+
482 // | (mnjki) CSS Style Handling |
483 // +---------------------------------------------------+
484diff --git a/src/parser.cc b/src/parser.c
485similarity index 87%
486rename from src/parser.cc
487rename to src/parser.c
488index 20957a7..40e88f4 100755
489--- a/src/parser.cc
490+++ b/src/parser.c
491@@ -1,29 +1,8 @@
492 #include "grim.h"
493-#include <iostream>
494-#include <fstream>
495-#include <sstream>
496-#include <unordered_map>
497-#include <cctype>
498-#include <algorithm>
499-#include <cmath>
500-#include <string_view>
501-#include <bit>
502-#include <cstdint>
503-
504-struct SymbolTable {
505- std::unordered_map<std::string, int> table{};
506- int next = 5;
507-
508- int get(std::string_view name) {
509- auto it = table.find(std::string(name));
510- if (it != table.end()) return it->second;
511-
512- int id = next++;
513- table[std::string(name)] = id;
514- return id;
515- }
516-};
517+#include <string.h>
518+#include <ctype.h>
519
520+/*
521 std::unordered_map<std::string, std::string> parseAttributes(std::string_view token) {
522 std::unordered_map<std::string, std::string> attrs;
523
524@@ -1064,10 +1043,7 @@ static const std::unordered_map<std::string_view, float> kAbsSize{
525 {"large",1.2f}, {"x-large",1.5f}, {"xx-large",2.0f}, {"xxx-large",3.0f}
526 };
527
528-/*
529- Parses a string into Unit's and stores variables & strings into the Style object
530-
531-*/
532+// Parses a string into Unit's and stores variables & strings into the Style object
533
534 std::vector<Unit> getUnit(std::string value, SymbolTable* externalTable = nullptr) {
535 SymbolTable localTable;
536@@ -1149,9 +1125,9 @@ std::vector<Unit> getUnit(std::string value, SymbolTable* externalTable = nullpt
537 buffer.push_back(v);
538 continue;
539 } else if (v == ')') {
540- /*
541- Function handling
542- */
543+
544+ // Function handling
545+
546 // We are at the end of the parentheseses and we need to pass it for more parsing
547 std::string name;
548 bool nameset = false;
549@@ -1192,15 +1168,15 @@ std::vector<Unit> getUnit(std::string value, SymbolTable* externalTable = nullpt
550 // Split by spaces into parts
551
552 if (auto it = kMatch.find(buffer); it != kMatch.end()) {
553- /*
554- Keyword Matching
555- */
556+
557+ // Keyword Matching
558+
559 unit.type = it->second;
560 // the value is not used here
561 } else if (auto it = kColors.find(buffer); it != kColors.end()) {
562- /*
563- Color Matching
564- */
565+
566+ // Color Matching
567+
568 unit.type = UnitType::HEX;
569 unit.value = it->second;
570 } else {
571@@ -1221,15 +1197,15 @@ std::vector<Unit> getUnit(std::string value, SymbolTable* externalTable = nullpt
572
573 if (!found) {
574 if (buffer.starts_with("#") && buffer.length() > 1) {
575- /*
576- # Prefix for HEX codes
577- */
578+
579+ // # Prefix for HEX codes
580+
581 unit.type = UnitType::HEX;
582 unit.value = std::bit_cast<float>(static_cast<uint32_t>(std::stoul(buffer.substr(1), nullptr, 16)));
583 } else if (auto it = kAbsSize.find(buffer); it != kAbsSize.end()) {
584- /*
585- Absolute size matching
586- */
587+
588+ // Absolute size matching
589+
590 unit.type = UnitType::EM;
591 unit.value = it->second;
592 } else if (buffer == "true") {
593@@ -1499,220 +1475,127 @@ std::string pathJoin(const std::string& p1, const std::string& p2) {
594 return p1 + p2;
595 }
596 }
597+*/
598
599-struct ParsedDocument {
600- Node document;
601- Style CSS;
602-};
603-
604-ParsedDocument parseDocument(std::istream& inputStream) {
605- Node root;
606- root.setTagName("root");
607-
608- Node* currentNode = &root;
609+void parseDocument(SymbolTable* sym, Node* root, const char* input, int size) {
610+ printf("ROOT TAGNAME: %s, %i\n", symbol_lookup_string(sym, root->tag_name), root->tag_name);
611+ Node* currentNode = root;
612
613 bool inTag = false;
614 bool escaped = false;
615 bool inQuote = false;
616 bool inComment = false;
617- bool waitToClose = false;
618 char quoteType = '"';
619
620- std::string token;
621+ char token[1024];
622+ int token_length = 0;
623+
624+ bool tagname_set = false;
625+ char tagname[MAX_STR_LEN];
626
627- // CSS stuff
628- std::vector<std::string> cssLinks;
629- std::vector<std::string> cssTags;
630+ char attr_name[MAX_STR_LEN];
631+ bool attr_name_set = false;
632
633- char current;
634- while (inputStream.get(current)) {
635+ char attr_value[MAX_STR_LEN];
636+
637+ for (int i = 0; i < size; i++) {
638+ char current = input[i];
639+
640 // Finds the --> and removes it then resets inComment
641 if (inComment) {
642 // added the peek to prevent hitting on every -
643- if (current == '-' && inputStream.peek() == '-') {
644- char a,b;
645- // load the next two
646- if (inputStream.get(a) && inputStream.get(b)) {
647- // We know a == -
648- if (b == '>') {
649- // Close the comment
650- inComment = false;
651- }
652+ if (current == '-' && i+2 < size && input[i+1] == '-') {
653+ char a = input[i+2];
654+ if (a == '>') {
655+ // Close the comment
656+ inComment = false;
657 }
658
659 if (!inComment) {
660 // we don't put anything back and that puts us where we need to be
661+ i += 2;
662 continue;
663- } else {
664- // Not the end
665- inputStream.putback(b);
666- inputStream.putback(a);
667 }
668 }
669 continue;
670 }
671
672- std::string tagName = currentNode->getTagName();
673- if (!waitToClose && (
674- tagName == "style" ||
675- tagName == "script" ||
676- tagName == "textarea"
677- ) && current == '<' && inputStream.peek() == '/' ) {
678- // If its the starting of a closing tag and we are inside of a special tag
679- // we need to see if the upcoming closing tag is the correct closing tag
680- // if it is then we put the read ahead back and waitToClose. This will let normal parsing
681- // continue
682- const size_t readTo = tagName.length()+2;
683- std::string buffer(readTo, '\0');
684-
685- inputStream.read(&buffer[0], readTo);
686-
687- if (buffer == "/"+tagName+">") {
688- waitToClose = true;
689- } else {
690- token += current;
691- }
692-
693- for (int i = readTo-1; i>= 0; i--) {
694- inputStream.putback(buffer[i]);
695- }
696- if (!waitToClose) {
697- continue;
698- }
699- } else if (!waitToClose && (
700- tagName == "style" ||
701- tagName == "script" ||
702- tagName == "textarea"
703- )) {
704- token += current;
705- continue;
706- }
707
708 if (inTag) {
709 if ((current == '"' || current == '\'') && !escaped) {
710 if (inQuote && current == quoteType) {
711 inQuote = false;
712+ if (attr_name[0] != '\0' && attr_name_set) {
713+ if (strcmp(attr_name, "id") == 0) {
714+ SET_ID(sym, currentNode, token+1);
715+ } else if (strcmp(attr_name, "class") == 0) {
716+
717+ } else {
718+ SET_ATTRIBUTE(currentNode, attr_name, token+1);
719+ }
720+ token[0] = '\0';
721+ token_length = 0;
722+ attr_name[0] = '\0';
723+ attr_name_set = false;
724+ }
725+
726 } else if (!inQuote) {
727 inQuote = true;
728 quoteType = current;
729 }
730+ continue;
731 }
732
733- if (current == '>' && !inQuote && !escaped) {
734- inTag = false;
735- waitToClose = false;
736- // Even if the next tag is next still add a text tag as we can just check if its empty and remove it
737- // this check would still have to be done either way so we aren't wasting anything
738- if (token.length() > 0) {
739- bool empty = true;
740- bool closingTag = false;
741- for (size_t i = 0; i < token.length(); i++) {
742- auto c = token[i];
743- if (std::isspace(c)) {
744- continue;
745- } else if (c == '/') {
746- closingTag = true;
747- } else {
748- empty = false;
749- break;
750- }
751- }
752+ printf("%c %i %i\n", current, inQuote, tagname_set);
753
754- bool selfClosing = false;
755- int selfClosingPosition = token.length()-1;
756- if (!closingTag) {
757- for (int i = token.length()-1; i >= 0; i--) {
758- auto c = token[i];
759- if (std::isspace(c)) {
760- continue;
761- } else if (c == '/') {
762- selfClosingPosition = i;
763- selfClosing = true;
764- break;
765- } else {
766- break;
767- }
768- }
769+ if ((isspace(current) || current == '>') && !inQuote && !tagname_set) {
770+ if (token[0] == '/') {
771+ if (currentNode->parent != NULL) {
772+ currentNode = currentNode->parent;
773 }
774
775- if (!empty) {
776- if (selfClosing) {
777- //Create element and don't jump inside
778- // Use node instead of currentNode because we do not need to jump into the created element
779- auto attrs = parseAttributes(token.substr(0, selfClosingPosition));
780- auto node = currentNode->createElement(attrs["tagName"]);
781- for (auto const& pair : attrs) {
782- // All attributes are stored as strings so we can just throw them in
783- node->setAttribute(pair.first, pair.second);
784- }
785- if (attrs["tagName"] == "link" && attrs["rel"] == "stylesheet") {
786- cssLinks.push_back(node->getAttribute("href"));
787- } else if (attrs["tagName"] == "style") {
788- cssTags.push_back(node->getAttribute("innerText"));
789- }
790- } else if (closingTag) {
791- // Checksum and tree move
792- std::string tagName = "";
793- for (auto t : token) {
794- if (!std::isspace(t) && t != '/') {
795- tagName += t;
796- } else if (t == '/') {
797- // For closing tags we just want the name
798- continue;
799- } else if (tagName.length() > 0) {
800- break;
801- }
802- }
803+ token[0] = '\0';
804+ token_length = 0;
805
806- if (currentNode->getTagName() == tagName) {
807- if (tagName == "link" && currentNode->getAttribute("rel") == "stylesheet") {
808- cssLinks.push_back(currentNode->getAttribute("href"));
809- } else if (tagName == "style") {
810- cssTags.push_back(currentNode->children[0].getAttribute("innerText"));
811- }
812+ continue;
813+ }
814
815- currentNode = currentNode->parent;
816- } else {
817- std::cerr << "malformed html: closing tag (</" << tagName << ">) found for <" << currentNode->getTagName() << ">" << std::endl;
818- }
819- } else {
820- // Create a element and jump inside
821- auto attrs = parseAttributes(token);
822- // Don't add elements like <!DOCTYPE>
823- if (attrs["tagName"][0] != '!') {
824- currentNode = currentNode->createElement(attrs["tagName"]);
825- for (auto const& pair : attrs) {
826- // All attributes are stored as strings so we can just throw them in
827- currentNode->setAttribute(pair.first, pair.second);
828- }
829- }
830+ token[token_length] = '\0';
831+ strcpy(tagname, token);
832
833- }
834- }
835- }
836+ printf("TAGNAME %s\n", tagname);
837
838- token = "";
839- continue;
840+ token[0] = '\0';
841+ token_length = 0;
842+
843+ if (tagname[0] == '!') continue;
844+
845+ tagname_set = true;
846+
847+ Node* node = CREATE_ELEMENT(sym, tagname);
848+ printf("CREATING ELEMENT: %s, PARENT: %s\n", tagname, symbol_lookup_string(sym, currentNode->tag_name));
849+ APPEND_CHILD(currentNode, node);
850+
851+ currentNode = node;
852+ } else if (current == '>' && !inQuote && !escaped) {
853+ inTag = false;
854+ tagname_set = false;
855+ tagname[0] = '\0';
856+ } else if (current == '=' && !attr_name_set && !inQuote) {
857+ token[token_length] = '\0';
858+ strcpy(attr_name, token+1);
859+ token[0] = '\0';
860+ token_length = 0;
861+ attr_name_set = true;
862 }
863 } else if (!escaped && current == '<') {
864 // if the next character is a !
865- if (inputStream.peek() == '!') {
866- char a,b,c;
867+ if (i+3 < size && input[i+1] == '!') {
868 // load the next three characters (includes !)
869- if (inputStream.get(a) && inputStream.get(b) && inputStream.get(c)) {
870- // We know a == !
871- if (b == '-' && c == '-') {
872- inComment = true;
873- }
874- }
875-
876- if (inComment) {
877+ if (input[i+2] == '-' && input[i+3] == '-') {
878+ inComment = true;
879+ i += 3;
880 continue;
881- } else {
882- // Not a comment add all back
883- inputStream.putback(c);
884- inputStream.putback(b);
885- inputStream.putback(a);
886 }
887 }
888 inTag = true;
889@@ -1723,71 +1606,37 @@ ParsedDocument parseDocument(std::istream& inputStream) {
890
891
892 bool hasText = false;
893- for (auto t : token) {
894- if (!std::isspace(t)) {
895+ for (int j = 0; j < token_length; j++) {
896+ if (!isspace(token[j])) {
897 hasText = true;
898 break;
899 }
900 }
901-
902- // TODO: Add a vector that stores inner text and when elements start to close out pop them but add all to their innerText
903- // | Will need something similar for innerHTML
904+/*
905 if (hasText) {
906- auto node = currentNode->createElement("text");
907- node->setAttribute("innerText", token);
908- }
909+ Node* node = CREATE_ELEMENT(sym, "text");
910+ token[token_length++] = '\0';
911+ SET_INNERTEXT(node, token);
912+ APPEND_CHILD(currentNode, node);
913+ }*/
914
915
916- token = "";
917+ token[0] = '\0';
918+ token_length = 0;
919 continue;
920 }
921
922 escaped = false;
923
924- if (current == '\\') { //'
925+ if (current == '\\') {
926 escaped = true;
927 continue;
928 }
929
930- token += current;
931- }
932-
933- ParsedDocument doc;
934- doc.document = root;
935-
936- Style style;
937-
938- std::string workingPath = get_working_path();
939-
940- for (size_t i = 0; i < cssLinks.size(); i++) {
941- std::string link = cssLinks[i];
942-
943- std::ifstream file(pathJoin(workingPath, link));
944-
945- if (!file.is_open()) {
946- std::cerr << "Failed to open file!" << std::endl;
947- } else {
948- Style s = parseCSS(file);
949-
950- for (size_t j = 0; j < s.children.size(); j++) {
951- style.addChild(s.children[j]);
952- }
953+ if (token_length < MAX_STR_LEN) {
954+ token[token_length++] = current;
955+ token[token_length] = '\0';
956 }
957- file.close();
958- }
959
960- for (size_t i = 0; i < cssTags.size(); i++) {
961- std::string tag = cssTags[i];
962-
963- std::istringstream iss(tag);
964- Style s = parseCSS(iss);
965-
966- for (size_t j = 0; j < s.children.size(); j++) {
967- style.addChild(s.children[j]);
968- }
969 }
970-
971- doc.CSS = style;
972-
973- return doc;
974 }
975diff --git a/tests/easy.html b/tests/easy.html
976new file mode 100644
977index 0000000..54c0e15
978--- /dev/null
979+++ b/tests/easy.html
980@@ -0,0 +1,21 @@
981+<!DOCTYPE html>
982+<html>
983+ <head>
984+<style>
985+ h1 {
986+ color: red;
987+ }
988+</style>
989+ </head>
990+ <body>
991+ <h1 id="test1">
992+ Hello world
993+ </h1>
994+ <br/>
995+ <br />
996+ <input type="text"/>
997+ <p id="test2">
998+ this is a test
999+ </p>
1000+ </body>
1001+</html>
1002diff --git a/tests/index.html b/tests/index.html
1003index 289b7f3..28a9c20 100755
1004--- a/tests/index.html
1005+++ b/tests/index.html
1006@@ -9,17 +9,17 @@
1007 </style>
1008 </head>
1009 <body>
1010- <h1 id="<">
1011+ <h1 id="test1">
1012 Hello world
1013 </h1>
1014 <br />
1015 <input type="text"/>
1016- <p id="test">
1017+ <p id="test2">
1018 this is a test
1019 </p>
1020+ <textarea><script>console.log(\"</pre>\");</script></textarea>
1021 <pre>
1022 <script> console.log("</pre>");</script>
1023 </pre>
1024- <textarea><script>console.log(\"</pre>\");</script></textarea>
1025 </body>
1026 </html>