home readme diff tree note docs

0commit 09c195df02536b6a796bd648fce9669397b96109
1Author: Mason Wright <mason@lakefox.net>
2Date:   Wed Jun 18 16:42:10 2025 -0600
3
4    Moved things around and made .h files
5
6diff --git a/build.sh b/build.sh
7deleted file mode 100755
8index 4439e39..0000000
9--- a/build.sh
10+++ /dev/null
11@@ -1 +0,0 @@
12-clang++ element.cc -std=c++23 -o main
13diff --git a/element.cc b/element.cc
14deleted file mode 100755
15index 4994d06..0000000
16--- a/element.cc
17+++ /dev/null
18@@ -1,551 +0,0 @@
19-#include <string>
20-#include <vector>
21-#include <fstream>
22-#include <sstream>
23-#include <unordered_map>
24-#include <iostream>
25-#include <memory>
26-#include <cctype>
27-#include <algorithm>
28-#include <stdexcept>
29-
30-
31-// --- THE MACRO DEFINITION ---
32-// This macro generates a getter and a setter for a specific attribute.
33-// _Type: The C++ data type (e.g., std::string, int, bool)
34-// _FuncNameSuffix: The PascalCase suffix for the getter/setter (e.g., Id, TabIndex)
35-// _AttrKeyString: The exact string literal key used in the Attributes map (e.g., "id", "tabindex")
36-#define GENERATE_ATTRIBUTE_ACCESSORS(_Type, _FuncNameSuffix, _AttrKeyString) \
37-    _Type get##_FuncNameSuffix() const { \
38-        return getAttribute<_Type>(_AttrKeyString); \
39-    } \
40-    void set##_FuncNameSuffix(_Type value) { \
41-        setAttribute(_AttrKeyString, value); \
42-    }
43-
44-
45-struct Styles {
46-	std::vector<std::unordered_map<std::string, std::string>> stylesheets;
47-	std::unordered_map<std::string, std::string> inlineStyles;
48-	std::unordered_map<std::string, std::unordered_map<std::string, std::string>> psuedoStyles;
49-};
50-
51-struct Bounds {
52-	int top;
53-	int right;
54-	int bottom;
55-	int left;
56-};
57-
58-struct State {
59-	// Bounds offset;
60-	// Border border;
61-	// std::vector<Background> background;
62-	int width;
63-	int height;
64-	int z;
65-	bool hidden;
66-	int tabIndex;
67-};
68-
69-class ClassList {
70-	private:
71-		std::vector<std::string> values;
72-	public:
73-		std::string value() const {
74-			if (values.empty()) {
75-				return "";
76-			}
77-			std::string collection = values[0];
78-			for (size_t i = 1; i < values.size(); ++i) {
79-				collection += " " + values[i];
80-			}
81-			return collection;
82-		}
83-
84-		void add(std::string value) {
85-			values.push_back(value);
86-		}
87-
88-		void remove(std::string value) {
89-			auto it_prev = std::find(values.begin(), values.end(), value);
90-			if (it_prev != values.end()) {
91-				*it_prev = values.back(); // Overwrite prevous position with last element
92-				values.pop_back();          // Remove the last element
93-			}
94-		}
95-};
96-
97-// !TODO: Cascade the styles during the parsing of the document and add the styles during the css parsing
98-// + all other styles should cascade when added
99-class Node {
100-	private:
101-		std::string TagName;
102-		// !NOTE: ContentEditable only supports plaintext
103-		std::unordered_map<std::string, std::string> Attributes;
104-	public:
105-		Node* parent;
106-		std::vector<std::unique_ptr<Node>> children;
107-		ClassList classList;
108-
109-		Node() : parent(nullptr) {}
110-		// Can't use macro on tag name
111-		std::string getTagName() const { return TagName; }
112-		void setTagName(const std::string& name) { TagName = name; }
113-
114-		const std::unordered_map<std::string, std::string>& getAttributes() const {
115-			return Attributes;
116-		}
117-
118-
119-		// --- Define Getters and Setters
120-		// !TODO: Add all global attributes
121-		// + Src: https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Global_attributes
122-		GENERATE_ATTRIBUTE_ACCESSORS(std::string, Id, "id")
123-		GENERATE_ATTRIBUTE_ACCESSORS(std::string, InnerText, "innerText") // If you want innerText as an attribute
124-		GENERATE_ATTRIBUTE_ACCESSORS(bool, ContentEditable, "contenteditable")
125-		GENERATE_ATTRIBUTE_ACCESSORS(std::string, Href, "href")
126-		GENERATE_ATTRIBUTE_ACCESSORS(std::string, Src, "src")
127-		GENERATE_ATTRIBUTE_ACCESSORS(std::string, Title, "title")
128-		GENERATE_ATTRIBUTE_ACCESSORS(std::string, Value, "value")
129-		GENERATE_ATTRIBUTE_ACCESSORS(int, TabIndex, "tabindex")
130-		GENERATE_ATTRIBUTE_ACCESSORS(bool, Disabled, "disabled")
131-		GENERATE_ATTRIBUTE_ACCESSORS(bool, Required, "required")
132-		GENERATE_ATTRIBUTE_ACCESSORS(bool, Checked, "checked")
133-
134-
135-		Node* createElement(std::string name) {
136-			std::unique_ptr<Node> newNode = std::make_unique<Node>();
137-			newNode->setTagName(name);
138-			newNode->parent = this;
139-			children.push_back(std::move(newNode));
140-			return children.back().get();
141-		}
142-
143-
144-		// --- Generic setAttribute (Type to string conversion) ---
145-		template<typename T>
146-			void setAttribute(const std::string& name, const T& value) {
147-				if constexpr (std::is_same_v<T, bool>) {
148-					if (value) {
149-						Attributes[name] = "";
150-					} else {
151-						Attributes.erase(name);
152-					}
153-				} else if constexpr (std::is_arithmetic_v<T>) { // Handles int, double, etc.
154-					Attributes[name] = std::to_string(value);
155-				} else {
156-					// This static_assert will fire if you try to use setAttribute with a type
157-					// that doesn't have a specific handling or a std::string overload.
158-					static_assert(std::is_convertible_v<T, std::string>,
159-							"setAttribute: Type cannot be converted to std::string automatically.");
160-					// If it's convertible, let's try to convert it.
161-					Attributes[name] = static_cast<std::string>(value);
162-				}
163-			}
164-
165-		// Overload for std::string to avoid unnecessary conversion (and explicit to_string)
166-		void setAttribute(const std::string& name, const std::string& value) {
167-			Attributes[name] = value;
168-		}
169-
170-
171-		// --- Generic getAttribute (string to Type conversion) ---
172-		template<typename T>
173-			T getAttribute(const std::string& name) const {
174-				auto it = Attributes.find(name);
175-				if (it != Attributes.end()) {
176-					const std::string& s = it->second; // Get the string value from the map
177-
178-					// Use if constexpr to convert the string to the requested type T
179-					if constexpr (std::is_same_v<T, int>) {
180-						try {
181-							return std::stoi(s);
182-						} catch (const std::invalid_argument& e) {
183-							std::cerr << "Warning: Invalid integer attribute value '" << s << "' for '" << name << "'. Defaulting to 0. " << e.what() << std::endl;
184-							return 0;
185-						} catch (const std::out_of_range& e) {
186-							std::cerr << "Warning: Integer attribute value '" << s << "' for '" << name << "' out of range. Defaulting to 0. " << e.what() << std::endl;
187-							return 0;
188-						}
189-					} else if constexpr (std::is_same_v<T, bool>) {
190-						// Return true if string is not empty and not "false" (case-insensitive)
191-						// Adjust this logic if you have different boolean attribute parsing rules
192-						std::string lower_s = s;
193-						std::transform(lower_s.begin(), lower_s.end(), lower_s.begin(),
194-								[](unsigned char c){ return std::tolower(c); });
195-						return (!lower_s.empty() && lower_s != "false");
196-					} else if constexpr (std::is_same_v<T, double>) {
197-						try {
198-							return std::stod(s);
199-						} catch (const std::invalid_argument& e) {
200-							std::cerr << "Warning: Invalid double attribute value '" << s << "' for '" << name << "'. Defaulting to 0.0. " << e.what() << std::endl;
201-							return 0.0;
202-						} catch (const std::out_of_range& e) {
203-							std::cerr << "Warning: Double attribute value '" << s << "' for '" << name << "' out of range. Defaulting to 0.0. " << e.what() << std::endl;
204-							return 0.0;
205-						}
206-					} else {
207-						// If a type is requested that isn't explicitly handled,
208-						// this static_assert will cause a compile error.
209-						static_assert(std::is_convertible_v<std::string, T>,
210-								"getAttribute: Type conversion from std::string not implemented for this type.");
211-						// If it's convertible, attempt a static_cast (might not be what you want for all types)
212-						return static_cast<T>(s);
213-					}
214-				}
215-				// Return a default-constructed T if attribute not found
216-				// This relies on T having a default constructor (e.g., int() is 0, bool() is false, std::string() is empty)
217-				return T();
218-			}
219-
220-		// Overload for std::string directly (avoids template instantiation and conversion overhead)
221-		// This function will be preferred by the compiler when T is std::string.
222-		std::string getAttribute(const std::string& name) const {
223-			auto it = Attributes.find(name);
224-			if (it != Attributes.end()) {
225-				return it->second;
226-			}
227-			return ""; // Default for string attributes if not found
228-		}
229-
230-		void print(int indent = 0) const {
231-			// Print indentation
232-			for (int i = 0; i < indent; ++i) {
233-				std::cout << "  ";
234-			}
235-
236-			// Print node information
237-			std::cout << "<" << getTagName();
238-
239-			// Print attributes
240-			for (const auto& attr_pair : getAttributes()) {
241-				if (attr_pair.first == "innerText" || attr_pair.first == "tagName") {
242-					continue;
243-				}
244-				std::cout << " " << attr_pair.first;
245-				if (!attr_pair.second.empty()) { // Only print value if it's not empty (for boolean attributes)
246-					std::cout << "=\"" << attr_pair.second << "\"";
247-				}
248-			}
249-			std::cout << ">";
250-
251-			// Print inner text if any
252-			// Note: HTML whitespace rules are complex; this just prints it raw
253-			if (!getInnerText().empty()) {
254-				std::cout << "\n" << getInnerText();
255-			}
256-
257-			std::cout << std::endl;
258-
259-			// Recursively call print for children
260-			for (const auto& child : children) {
261-				child->print(indent + 1); // Increase indent for children
262-			}
263-
264-			// Print closing tag if it's not a self-closing/root tag
265-			for (int i = 0; i < indent; ++i) {
266-				std::cout << "  ";
267-			}
268-			std::cout << "</" << getTagName() << ">" << std::endl;
269-		}
270-};
271-
272-std::unordered_map<std::string, std::string> parseAttributes(std::string token) {
273-	std::unordered_map<std::string, std::string> attrs;
274-	
275-	bool inQuote = false;
276-	bool escaped = false;
277-	char quoteType = '"';
278-
279-
280-	std::string word;
281-	for (unsigned short i = 0; i < token.length(); i++) {
282-		char current = token[i];
283-		if ((current == '"' || current == '\'') && !escaped) {
284-			if (inQuote && current == quoteType) {
285-				inQuote = false;
286-			} else if (!inQuote) {
287-				inQuote = true;
288-				quoteType = current;
289-			}
290-		}
291-
292-		if (current == '\\') { //'
293-			escaped = true;
294-			continue;
295-		}
296-
297-		if ((!std::isspace(current) || inQuote) && i != token.length()-1) {
298-			word += current;
299-		} else {
300-			if (i == token.length()-1) {
301-				word += current;
302-			}
303-
304-			std::string name = "";
305-			std::string value = "";
306-			bool setValue = false;
307-			
308-			for (auto c : word) {
309-				if (c == '=') {
310-					setValue = true;
311-					continue;
312-				}
313-
314-				if (setValue) {
315-					value += c;
316-				} else {
317-					name += c;
318-				}
319-			}
320-
321-			word = "";
322-
323-			std::string trimmedName = "";
324-			for (auto c : name) {
325-				if (!std::isspace(c)) {
326-					trimmedName += c;
327-				}
328-			}
329-
330-			if (attrs.size() == 0) {
331-				value = '"'+trimmedName+'"';
332-				trimmedName = "tagName";
333-			}
334-
335-			std::string trimmedValue = "";
336-			int sliceStart = 0;
337-			int sliceEnd = value.length();
338-
339-			if (value.length() >= 2) {
340-				for (unsigned short t = 0; t < value.length(); t++) {
341-					if (!std::isspace(value[t])) {
342-						sliceStart = t;
343-						break;
344-					}
345-				}
346-
347-				for (int t = value.length()-1; t >= 0; t--) {
348-					// Trim the trailing / on self closing elements if there isn't a space inbetween
349-					if (!std::isspace(value[t]) && value[t] != '/') {
350-						sliceEnd = t;
351-						break;
352-					}
353-				}
354-
355-				// Add and subtract 1 from each side to remove quotes
356-				for (unsigned short t = sliceStart+1; t < sliceEnd; t++) {
357-					trimmedValue += value[t];
358-				}
359-			}
360-
361-			attrs[trimmedName] = trimmedValue;
362-		}
363-	}
364-
365-	return attrs;
366-}
367-
368-std::unique_ptr<Node> parseStream(std::istream& inputStream) {
369-	std::unique_ptr<Node> root = std::make_unique<Node>();
370-	root->setTagName("root");
371-
372-	Node* currentNode = root.get();
373-
374-	bool inTag = false;
375-	bool escaped = false;
376-	bool inQuote = false;
377-	bool inComment = false;
378-	char quoteType = '"';
379-
380-	std::string token;
381-
382-	char current;
383-	while (inputStream.get(current)) {
384-
385-		// Finds the --> and removes it then resets inComment
386-		if (inComment) {
387-			// added the peek to prevent hitting on every -
388-			if (current == '-' && inputStream.peek() == '-') {
389-				char a,b;
390-				// load the next two
391-				if (inputStream.get(a) && inputStream.get(b)) {
392-					// We know b == -
393-					if (b == '>') {
394-						// Close the comment
395-						inComment = false;
396-					}
397-				}
398-
399-				if (!inComment) {
400-					// we don't put anything back and that puts us where we need to be
401-					continue;
402-				} else {
403-					// Not the end 
404-					inputStream.putback(b);
405-					inputStream.putback(a);
406-				}
407-			}
408-			continue;
409-		}
410-
411-		if (inTag) {
412-			 if ((current == '"' || current == '\'') && !escaped) {
413-				if (inQuote && current == quoteType) {
414-					inQuote = false;
415-				} else if (!inQuote) {
416-					inQuote = true;
417-					quoteType = current;
418-				}
419-			}
420-
421-			if (current == '>' && !inQuote && !escaped) {
422-				inTag = false;
423-				// Even if the next tag is next still add a text tag as we can just check if its empty and remove it
424-				// this check would still have to be done either way so we aren't wasting anything
425-				if (token.length() > 0) {
426-					bool empty = true;
427-					bool closingTag = false;
428-					for (int i = 0; i < token.length(); i++) {
429-						auto c = token[i];
430-						if (std::isspace(c)) {
431-							continue;
432-						} else if (c == '/') {
433-							closingTag = true;
434-						} else {
435-							empty = false;
436-							break;
437-						}
438-					}
439-
440-					bool selfClosing = false;
441-					int selfClosingPosition = token.length()-1;
442-					if (!closingTag) {
443-						for (int i = token.length()-1; i >= 0; i--) {
444-							auto c = token[i];
445-							if (std::isspace(c)) {
446-								continue;
447-							} else if (c == '/') {
448-								selfClosingPosition = i;
449-								selfClosing = true;
450-								break;
451-							} else {
452-								break;
453-							}
454-						}
455-					}
456-
457-					if (!empty) {
458-						if (selfClosing) {
459-							//Create element and don't jump inside
460-							// Use node instead of currentNode because we do not need to jump into the created element
461-							auto attrs = parseAttributes(token.substr(0, selfClosingPosition));
462-							auto node = currentNode->createElement(attrs["tagName"]);
463-							for (auto const& pair : attrs) {
464-								// All attributes are stored as strings so we can just throw them in
465-								node->setAttribute(pair.first, pair.second);
466-							}
467-						} else if (closingTag) {
468-							// Checksum and tree move
469-							std::string tagName = "";
470-							for (auto t : token) {
471-								if (!std::isspace(t) && t != '/') {
472-									tagName += t;
473-								} else if (t == '/') {
474-									// For closing tags we just want the name
475-									continue;
476-								} else if (tagName.length() > 0) {
477-									break;
478-								}
479-							}
480-							if (currentNode->getTagName() == tagName) {
481-								currentNode = currentNode->parent;
482-							} else {
483-								std::cerr << "malformed html: closing tag (</" << tagName << ">) found for <" << currentNode->getTagName() << ">" << std::endl;
484-							}
485-						} else {
486-							// Create a element and jump inside
487-							auto attrs = parseAttributes(token);
488-							currentNode = currentNode->createElement(attrs["tagName"]);
489-							for (auto const& pair : attrs) {
490-								// All attributes are stored as strings so we can just throw them in
491-								currentNode->setAttribute(pair.first, pair.second);
492-							}
493-
494-						}
495-					}
496-				}
497-
498-				token = "";	
499-				continue;
500-			}
501-		} else if (!escaped && current == '<') {
502-			// if the next charector is a !
503-			if (inputStream.peek() == '!') {
504-				char a,b,c;
505-				// load the next three charecters (includes !)
506-				if (inputStream.get(a) && inputStream.get(b) && inputStream.get(c)) {
507-					// We know a == !
508-					if (b == '-' && c == '-') {
509-						inComment = true;
510-					}
511-				}
512-
513-				if (inComment) {
514-					continue;
515-				} else {
516-					// Not a comment add all back
517-					inputStream.putback(c);
518-					inputStream.putback(b);
519-					inputStream.putback(a);
520-				}
521-			}
522-			inTag = true;
523-			
524-			// Heres where you actually make the text node and above the real nodes
525-			// can also prob make the vector of tokens a single variable
526-			// it really just needs to be a data string bc we know the type based on inTag
527-
528-
529-			bool hasText = false;
530-			for (auto t : token) {
531-				if (!std::isspace(t)) {
532-					hasText = true;
533-					break;
534-				}
535-			}
536-			if (hasText) {
537-				auto node = currentNode->createElement("text");
538-				node->setInnerText(token);
539-			}
540-
541-
542-			token = "";
543-			continue;
544-		}
545-		
546-		escaped = false;
547-
548-		if (current == '\\') { //'
549-			escaped = true;
550-			continue;
551-		}
552-
553-		token += current;
554-	}
555-
556-	return root;
557-}
558-
559-
560-int main() {
561-	// std::string html1 = "<h1></h1>";
562-	// std::stringstream ss1(html1);
563-
564-	std::ifstream inputFile("./index.html");
565-	auto document = parseStream(inputFile);
566-	
567-	document->print();
568-	return 0;
569-}
570diff --git a/include/element.h b/include/element.h
571new file mode 100644
572index 0000000..83d5155
573--- /dev/null
574+++ b/include/element.h
575@@ -0,0 +1,89 @@
576+// Node.h
577+#ifndef ELEMENT_H
578+#define ELEMENT_H
579+
580+#include <string>
581+#include <vector>
582+#include <unordered_map>
583+#include <memory>
584+
585+struct Styles {
586+    std::vector<std::unordered_map<std::string, std::string>> stylesheets;
587+    std::unordered_map<std::string, std::string> inlineStyles;
588+    std::unordered_map<std::string, std::unordered_map<std::string, std::string>> psuedoStyles;
589+};
590+
591+struct Bounds {
592+    int top;
593+    int right;
594+    int bottom;
595+    int left;
596+};
597+
598+struct State {
599+    int width;
600+    int height;
601+    int z;
602+    bool hidden;
603+    int tabIndex;
604+};
605+
606+class ClassList {
607+private:
608+    std::vector<std::string> values;
609+public:
610+    std::string value() const;
611+    void add(std::string value);
612+    void remove(std::string value);
613+};
614+
615+class Node {
616+private:
617+    std::string TagName;
618+    std::unordered_map<std::string, std::string> Attributes;
619+
620+    template<typename T>
621+    void setAttribute(const std::string& name, const T& value);
622+
623+    template<typename T>
624+    T getAttribute(const std::string& name) const;
625+
626+public:
627+    Node* parent;
628+    std::vector<std::unique_ptr<Node>> children;
629+    ClassList classList;
630+
631+    Node();
632+    std::string getTagName() const;
633+    void setTagName(const std::string& name);
634+    const std::unordered_map<std::string, std::string>& getAttributes() const;
635+
636+    // --- Define Getters and Setters
637+    // !TODO: Add all global attributes
638+    // + Src: https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Global_attributes
639+    // The macro should stay here as it's part of the class definition
640+    #define GENERATE_ATTRIBUTE_ACCESSORS(_Type, _FuncNameSuffix, _AttrKeyString) \
641+        _Type get##_FuncNameSuffix() const; \
642+        void set##_FuncNameSuffix(_Type value);
643+
644+    GENERATE_ATTRIBUTE_ACCESSORS(std::string, Id, "id")
645+    GENERATE_ATTRIBUTE_ACCESSORS(std::string, InnerText, "innerText")
646+    GENERATE_ATTRIBUTE_ACCESSORS(bool, ContentEditable, "contenteditable")
647+    GENERATE_ATTRIBUTE_ACCESSORS(std::string, Href, "href")
648+    GENERATE_ATTRIBUTE_ACCESSORS(std::string, Src, "src")
649+    GENERATE_ATTRIBUTE_ACCESSORS(std::string, Title, "title")
650+    GENERATE_ATTRIBUTE_ACCESSORS(std::string, Value, "value")
651+    GENERATE_ATTRIBUTE_ACCESSORS(int, TabIndex, "tabindex")
652+    GENERATE_ATTRIBUTE_ACCESSORS(bool, Disabled, "disabled")
653+    GENERATE_ATTRIBUTE_ACCESSORS(bool, Required, "required")
654+    GENERATE_ATTRIBUTE_ACCESSORS(bool, Checked, "checked")
655+
656+    Node* createElement(std::string name);
657+
658+    void setAttribute(const std::string& name, const std::string& value);
659+    std::string getAttribute(const std::string& name) const;
660+
661+    void print(int indent = 0) const;
662+};
663+
664+#endif // NODE_H
665diff --git a/include/parser.h b/include/parser.h
666new file mode 100644
667index 0000000..68f0246
668--- /dev/null
669+++ b/include/parser.h
670@@ -0,0 +1,16 @@
671+// parser.h
672+#ifndef PARSER_H
673+#define PARSER_H
674+
675+#include <memory>
676+#include <istream>
677+#include <unordered_map>
678+#include <string>
679+
680+class Node;
681+
682+// Declare the parsing functions
683+std::unordered_map<std::string, std::string> parseAttributes(std::string token);
684+std::unique_ptr<Node> parseStream(std::istream& inputStream);
685+
686+#endif
687diff --git a/main b/main
688index 688e6e3..06056be 100755
689Binary files a/main and b/main differ
690diff --git a/main.cc b/main.cc
691index 4a6554a..135fb3a 100755
692--- a/main.cc
693+++ b/main.cc
694@@ -1,35 +1,31 @@
695-#include <print>   // For print and std::println
696-#include <string>
697-using namespace std;
698+#include <iostream>
699+#include <fstream>
700+#include <memory>
701+#include "element.h"
702+#include "parser.h" 
703 
704 int main() {
705-    int age = 30;
706-    string name = "Alice";
707-    double pi = 3.1415926535;
708-    bool is_cpp_fun = true;
709-
710-    // 1. Basic usage - similar to cout without endl
711-    print("Hello, world!");
712-    print("This is awesome.\n"); // Manually add newline for print
713-
714-    // 2. Basic usage with arguments - placeholder {}
715-    println("Name: {}, Age: {}", name, age);
716-
717-    // 3. Positional arguments (optional, but useful for reordering)
718-    println("Age: {1}, Name: {0}", name, age);
719-
720-    // 4. Formatting numbers (precision, width, scientific notation)
721-    println("Pi (default): {}", pi);
722-    println("Pi (2 decimal places): {:.2f}", pi);
723-    println("Pi (scientific): {:e}", pi);
724-    println("Number with width and fill: {:*^10}", 42); // Center 42 in 10 chars, fill with *
725-
726-    // 5. Formatting boolean values
727-    println("Is C++ fun? {}", is_cpp_fun); // Prints 1 or 0 by default
728-    println("Is C++ fun? {:L}", is_cpp_fun); // Prints true or false (locale-dependent)
729-
730-    // 6. Printing multiple values with spaces in between
731-    println("{} {} {} {}", "One", 2, 3.0, true);
732+    // Option 1: Parse from a file
733+    std::ifstream inputFile("./index.html");
734+    if (!inputFile.is_open()) {
735+        std::cerr << "Error: Could not open index.html" << std::endl;
736+        return 1;
737+    }
738+
739+    std::unique_ptr<Node> document = parseStream(inputFile);
740+    inputFile.close(); // Close the file after parsing
741+
742+    // Option 2: Parse from a string (for testing)
743+    // std::string html_content = "<div>Hello <b>world</b>!</div><p>Another paragraph.</p>";
744+    // std::stringstream ss(html_content);
745+    // std::unique_ptr<Node> document = parseStream(ss);
746+
747+    // Print the parsed document tree
748+    if (document) {
749+        document->print();
750+    } else {
751+        std::cout << "Document parsing failed or resulted in an empty document." << std::endl;
752+    }
753 
754     return 0;
755 }
756diff --git a/make.sh b/make.sh
757new file mode 100755
758index 0000000..5498ebc
759--- /dev/null
760+++ b/make.sh
761@@ -0,0 +1 @@
762+clang++ -I./include main.cc src/element.cc src/parser.cc -std=c++23 -o main
763diff --git a/adapter.cc b/src/adapter.cc
764similarity index 100%
765rename from adapter.cc
766rename to src/adapter.cc
767diff --git a/src/element.cc b/src/element.cc
768new file mode 100755
769index 0000000..157533f
770--- /dev/null
771+++ b/src/element.cc
772@@ -0,0 +1,166 @@
773+#include "element.h"
774+#include <iostream>
775+#include <sstream>
776+#include <algorithm>
777+#include <stdexcept>
778+#include <cctype>
779+
780+std::string ClassList::value() const {
781+    if (values.empty()) {
782+        return "";
783+    }
784+    std::string collection = values[0];
785+    for (size_t i = 1; i < values.size(); ++i) {
786+        collection += " " + values[i];
787+    }
788+    return collection;
789+}
790+
791+void ClassList::add(std::string value) {
792+    values.push_back(value);
793+}
794+
795+void ClassList::remove(std::string value) {
796+    auto it_prev = std::find(values.begin(), values.end(), value);
797+    if (it_prev != values.end()) {
798+        *it_prev = values.back();
799+        values.pop_back();
800+    }
801+}
802+
803+// Constructor
804+Node::Node() : parent(nullptr) {}
805+
806+std::string Node::getTagName() const { return TagName; }
807+void Node::setTagName(const std::string& name) { TagName = name; }
808+
809+const std::unordered_map<std::string, std::string>& Node::getAttributes() const {
810+    return Attributes;
811+}
812+
813+// Implement the getter/setter methods declared using the macro
814+#define IMPLEMENT_ATTRIBUTE_ACCESSORS(_Type, _FuncNameSuffix, _AttrKeyString) \
815+    _Type Node::get##_FuncNameSuffix() const { \
816+        return getAttribute<_Type>(_AttrKeyString); \
817+    } \
818+    void Node::set##_FuncNameSuffix(_Type value) { \
819+        setAttribute(_AttrKeyString, value); \
820+    }
821+
822+IMPLEMENT_ATTRIBUTE_ACCESSORS(std::string, Id, "id")
823+IMPLEMENT_ATTRIBUTE_ACCESSORS(std::string, InnerText, "innerText")
824+IMPLEMENT_ATTRIBUTE_ACCESSORS(bool, ContentEditable, "contenteditable")
825+IMPLEMENT_ATTRIBUTE_ACCESSORS(std::string, Href, "href")
826+IMPLEMENT_ATTRIBUTE_ACCESSORS(std::string, Src, "src")
827+IMPLEMENT_ATTRIBUTE_ACCESSORS(std::string, Title, "title")
828+IMPLEMENT_ATTRIBUTE_ACCESSORS(std::string, Value, "value")
829+IMPLEMENT_ATTRIBUTE_ACCESSORS(int, TabIndex, "tabindex")
830+IMPLEMENT_ATTRIBUTE_ACCESSORS(bool, Disabled, "disabled")
831+IMPLEMENT_ATTRIBUTE_ACCESSORS(bool, Required, "required")
832+IMPLEMENT_ATTRIBUTE_ACCESSORS(bool, Checked, "checked")
833+
834+Node* Node::createElement(std::string name) {
835+    std::unique_ptr<Node> newNode = std::make_unique<Node>();
836+    newNode->setTagName(name);
837+    newNode->parent = this;
838+    children.push_back(std::move(newNode));
839+    return children.back().get();
840+}
841+
842+template<typename T>
843+void Node::setAttribute(const std::string& name, const T& value) {
844+    if constexpr (std::is_same_v<T, bool>) {
845+        if (value) {
846+            Attributes[name] = "";
847+        } else {
848+            Attributes.erase(name);
849+        }
850+    } else if constexpr (std::is_arithmetic_v<T>) {
851+        Attributes[name] = std::to_string(value);
852+    } else {
853+        static_assert(std::is_convertible_v<T, std::string>,
854+            "setAttribute: Type cannot be converted to std::string automatically.");
855+        Attributes[name] = static_cast<std::string>(value);
856+    }
857+}
858+
859+void Node::setAttribute(const std::string& name, const std::string& value) {
860+    Attributes[name] = value;
861+}
862+
863+template<typename T>
864+T Node::getAttribute(const std::string& name) const {
865+    auto it = Attributes.find(name);
866+    if (it != Attributes.end()) {
867+        const std::string& s = it->second;
868+
869+        if constexpr (std::is_same_v<T, int>) {
870+            try {
871+                return std::stoi(s);
872+            } catch (const std::invalid_argument& e) {
873+                return 0;
874+            } catch (const std::out_of_range& e) {
875+                return 0;
876+            }
877+        } else if constexpr (std::is_same_v<T, bool>) {
878+            std::string lower_s = s;
879+            std::transform(lower_s.begin(), lower_s.end(), lower_s.begin(),
880+                [](unsigned char c){ return std::tolower(c); });
881+            return (!lower_s.empty() && lower_s != "false");
882+        } else if constexpr (std::is_same_v<T, double>) {
883+            try {
884+                return std::stod(s);
885+            } catch (const std::invalid_argument& e) {
886+                return 0.0;
887+            } catch (const std::out_of_range& e) {
888+                return 0.0;
889+            }
890+        } else {
891+            static_assert(std::is_convertible_v<std::string, T>,
892+                "getAttribute: Type conversion from std::string not implemented for this type.");
893+            return static_cast<T>(s);
894+        }
895+    }
896+    return T();
897+}
898+
899+std::string Node::getAttribute(const std::string& name) const {
900+    auto it = Attributes.find(name);
901+    if (it != Attributes.end()) {
902+        return it->second;
903+    }
904+    return "";
905+}
906+
907+void Node::print(int indent) const {
908+    for (int i = 0; i < indent; ++i) {
909+        std::cout << "  ";
910+    }
911+
912+    std::cout << "<" << getTagName();
913+    for (const auto& attr_pair : getAttributes()) {
914+        if (attr_pair.first == "innerText" || attr_pair.first == "tagName") {
915+            continue;
916+        }
917+        std::cout << " " << attr_pair.first;
918+        if (!attr_pair.second.empty()) {
919+            std::cout << "=\"" << attr_pair.second << "\"";
920+        }
921+    }
922+    std::cout << ">";
923+
924+    if (!getAttribute("innerText").empty()) {
925+        std::cout << "\n" << getAttribute("innerText");
926+    }
927+
928+    std::cout << std::endl;
929+
930+    for (const auto& child : children) {
931+        child->print(indent + 1);
932+    }
933+
934+    for (int i = 0; i < indent; ++i) {
935+        std::cout << "  ";
936+    }
937+    std::cout << "</" << getTagName() << ">" << std::endl;
938+}
939diff --git a/events.cc b/src/events.cc
940similarity index 100%
941rename from events.cc
942rename to src/events.cc
943diff --git a/src/parser.cc b/src/parser.cc
944new file mode 100644
945index 0000000..784fbdd
946--- /dev/null
947+++ b/src/parser.cc
948@@ -0,0 +1,294 @@
949+#include "element.h" 
950+#include <iostream>
951+#include <fstream>
952+#include <sstream>
953+#include <unordered_map>
954+#include <cctype>
955+#include <algorithm>
956+
957+std::unordered_map<std::string, std::string> parseAttributes(std::string token) {
958+	std::unordered_map<std::string, std::string> attrs;
959+	
960+	bool inQuote = false;
961+	bool escaped = false;
962+	char quoteType = '"';
963+
964+
965+	std::string word;
966+	for (unsigned short i = 0; i < token.length(); i++) {
967+		char current = token[i];
968+		if ((current == '"' || current == '\'') && !escaped) {
969+			if (inQuote && current == quoteType) {
970+				inQuote = false;
971+			} else if (!inQuote) {
972+				inQuote = true;
973+				quoteType = current;
974+			}
975+		}
976+
977+		if (current == '\\') { //'
978+			escaped = true;
979+			continue;
980+		}
981+
982+		if ((!std::isspace(current) || inQuote) && i != token.length()-1) {
983+			word += current;
984+		} else {
985+			if (i == token.length()-1) {
986+				word += current;
987+			}
988+
989+			std::string name = "";
990+			std::string value = "";
991+			bool setValue = false;
992+			
993+			for (auto c : word) {
994+				if (c == '=') {
995+					setValue = true;
996+					continue;
997+				}
998+
999+				if (setValue) {
1000+					value += c;
1001+				} else {
1002+					name += c;
1003+				}
1004+			}
1005+
1006+			word = "";
1007+
1008+			std::string trimmedName = "";
1009+			for (auto c : name) {
1010+				if (!std::isspace(c)) {
1011+					trimmedName += c;
1012+				}
1013+			}
1014+
1015+			if (attrs.size() == 0) {
1016+				value = '"'+trimmedName+'"';
1017+				trimmedName = "tagName";
1018+			}
1019+
1020+			std::string trimmedValue = "";
1021+			int sliceStart = 0;
1022+			int sliceEnd = value.length();
1023+
1024+			if (value.length() >= 2) {
1025+				for (unsigned short t = 0; t < value.length(); t++) {
1026+					if (!std::isspace(value[t])) {
1027+						sliceStart = t;
1028+						break;
1029+					}
1030+				}
1031+
1032+				for (int t = value.length()-1; t >= 0; t--) {
1033+					// Trim the trailing / on self closing elements if there isn't a space inbetween
1034+					if (!std::isspace(value[t]) && value[t] != '/') {
1035+						sliceEnd = t;
1036+						break;
1037+					}
1038+				}
1039+
1040+				// Add and subtract 1 from each side to remove quotes
1041+				for (unsigned short t = sliceStart+1; t < sliceEnd; t++) {
1042+					trimmedValue += value[t];
1043+				}
1044+			}
1045+
1046+			attrs[trimmedName] = trimmedValue;
1047+		}
1048+	}
1049+
1050+	return attrs;
1051+}
1052+
1053+std::unique_ptr<Node> parseStream(std::istream& inputStream) {
1054+	std::unique_ptr<Node> root = std::make_unique<Node>();
1055+	root->setTagName("root");
1056+
1057+	Node* currentNode = root.get();
1058+
1059+	bool inTag = false;
1060+	bool escaped = false;
1061+	bool inQuote = false;
1062+	bool inComment = false;
1063+	char quoteType = '"';
1064+
1065+	std::string token;
1066+
1067+	char current;
1068+	while (inputStream.get(current)) {
1069+
1070+		// Finds the --> and removes it then resets inComment
1071+		if (inComment) {
1072+			// added the peek to prevent hitting on every -
1073+			if (current == '-' && inputStream.peek() == '-') {
1074+				char a,b;
1075+				// load the next two
1076+				if (inputStream.get(a) && inputStream.get(b)) {
1077+					// We know b == -
1078+					if (b == '>') {
1079+						// Close the comment
1080+						inComment = false;
1081+					}
1082+				}
1083+
1084+				if (!inComment) {
1085+					// we don't put anything back and that puts us where we need to be
1086+					continue;
1087+				} else {
1088+					// Not the end 
1089+					inputStream.putback(b);
1090+					inputStream.putback(a);
1091+				}
1092+			}
1093+			continue;
1094+		}
1095+
1096+		if (inTag) {
1097+			 if ((current == '"' || current == '\'') && !escaped) {
1098+				if (inQuote && current == quoteType) {
1099+					inQuote = false;
1100+				} else if (!inQuote) {
1101+					inQuote = true;
1102+					quoteType = current;
1103+				}
1104+			}
1105+
1106+			if (current == '>' && !inQuote && !escaped) {
1107+				inTag = false;
1108+				// Even if the next tag is next still add a text tag as we can just check if its empty and remove it
1109+				// this check would still have to be done either way so we aren't wasting anything
1110+				if (token.length() > 0) {
1111+					bool empty = true;
1112+					bool closingTag = false;
1113+					for (int i = 0; i < token.length(); i++) {
1114+						auto c = token[i];
1115+						if (std::isspace(c)) {
1116+							continue;
1117+						} else if (c == '/') {
1118+							closingTag = true;
1119+						} else {
1120+							empty = false;
1121+							break;
1122+						}
1123+					}
1124+
1125+					bool selfClosing = false;
1126+					int selfClosingPosition = token.length()-1;
1127+					if (!closingTag) {
1128+						for (int i = token.length()-1; i >= 0; i--) {
1129+							auto c = token[i];
1130+							if (std::isspace(c)) {
1131+								continue;
1132+							} else if (c == '/') {
1133+								selfClosingPosition = i;
1134+								selfClosing = true;
1135+								break;
1136+							} else {
1137+								break;
1138+							}
1139+						}
1140+					}
1141+
1142+					if (!empty) {
1143+						if (selfClosing) {
1144+							//Create element and don't jump inside
1145+							// Use node instead of currentNode because we do not need to jump into the created element
1146+							auto attrs = parseAttributes(token.substr(0, selfClosingPosition));
1147+							auto node = currentNode->createElement(attrs["tagName"]);
1148+							for (auto const& pair : attrs) {
1149+								// All attributes are stored as strings so we can just throw them in
1150+								node->setAttribute(pair.first, pair.second);
1151+							}
1152+						} else if (closingTag) {
1153+							// Checksum and tree move
1154+							std::string tagName = "";
1155+							for (auto t : token) {
1156+								if (!std::isspace(t) && t != '/') {
1157+									tagName += t;
1158+								} else if (t == '/') {
1159+									// For closing tags we just want the name
1160+									continue;
1161+								} else if (tagName.length() > 0) {
1162+									break;
1163+								}
1164+							}
1165+							if (currentNode->getTagName() == tagName) {
1166+								currentNode = currentNode->parent;
1167+							} else {
1168+								std::cerr << "malformed html: closing tag (</" << tagName << ">) found for <" << currentNode->getTagName() << ">" << std::endl;
1169+							}
1170+						} else {
1171+							// Create a element and jump inside
1172+							auto attrs = parseAttributes(token);
1173+							currentNode = currentNode->createElement(attrs["tagName"]);
1174+							for (auto const& pair : attrs) {
1175+								// All attributes are stored as strings so we can just throw them in
1176+								currentNode->setAttribute(pair.first, pair.second);
1177+							}
1178+
1179+						}
1180+					}
1181+				}
1182+
1183+				token = "";	
1184+				continue;
1185+			}
1186+		} else if (!escaped && current == '<') {
1187+			// if the next charector is a !
1188+			if (inputStream.peek() == '!') {
1189+				char a,b,c;
1190+				// load the next three charecters (includes !)
1191+				if (inputStream.get(a) && inputStream.get(b) && inputStream.get(c)) {
1192+					// We know a == !
1193+					if (b == '-' && c == '-') {
1194+						inComment = true;
1195+					}
1196+				}
1197+
1198+				if (inComment) {
1199+					continue;
1200+				} else {
1201+					// Not a comment add all back
1202+					inputStream.putback(c);
1203+					inputStream.putback(b);
1204+					inputStream.putback(a);
1205+				}
1206+			}
1207+			inTag = true;
1208+			
1209+			// Heres where you actually make the text node and above the real nodes
1210+			// can also prob make the vector of tokens a single variable
1211+			// it really just needs to be a data string bc we know the type based on inTag
1212+
1213+
1214+			bool hasText = false;
1215+			for (auto t : token) {
1216+				if (!std::isspace(t)) {
1217+					hasText = true;
1218+					break;
1219+				}
1220+			}
1221+			if (hasText) {
1222+				auto node = currentNode->createElement("text");
1223+				node->setInnerText(token);
1224+			}
1225+
1226+
1227+			token = "";
1228+			continue;
1229+		}
1230+		
1231+		escaped = false;
1232+
1233+		if (current == '\\') { //'
1234+			escaped = true;
1235+			continue;
1236+		}
1237+
1238+		token += current;
1239+	}
1240+
1241+	return root;
1242+}