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>