home
readme
diff
tree
note
docs
0commit f2b5c8202fbc904e2ed78260e3fdbd55164799d2
1Author: Mason Wright <mason@lakefox.net>
2Date: Wed Jun 18 16:02:43 2025 -0600
3
4 Parser supports everything, may add some more grim specific stuff later
5
6diff --git a/element.cc b/element.cc
7index 08f9888..4994d06 100755
8--- a/element.cc
9+++ b/element.cc
10@@ -220,7 +220,7 @@ class Node {
11
12 // Print attributes
13 for (const auto& attr_pair : getAttributes()) {
14- if (attr_pair.first == "innerText") {
15+ if (attr_pair.first == "innerText" || attr_pair.first == "tagName") {
16 continue;
17 }
18 std::cout << " " << attr_pair.first;
19@@ -244,26 +244,50 @@ class Node {
20 }
21
22 // Print closing tag if it's not a self-closing/root tag
23- // (Assuming you'll refine logic for self-closing tags eventually)
24- if (!children.empty() || !getInnerText().empty()) {
25- for (int i = 0; i < indent; ++i) {
26- std::cout << " ";
27- }
28- std::cout << "</" << getTagName() << ">" << std::endl;
29+ for (int i = 0; i < indent; ++i) {
30+ std::cout << " ";
31 }
32+ std::cout << "</" << getTagName() << ">" << std::endl;
33 }
34 };
35
36-std::unordered_map<std::string, std::string> parseAttributes(std::vector<std::string> tokens) {
37+std::unordered_map<std::string, std::string> parseAttributes(std::string token) {
38 std::unordered_map<std::string, std::string> attrs;
39+
40+ bool inQuote = false;
41+ bool escaped = false;
42+ char quoteType = '"';
43+
44+
45+ std::string word;
46+ for (unsigned short i = 0; i < token.length(); i++) {
47+ char current = token[i];
48+ if ((current == '"' || current == '\'') && !escaped) {
49+ if (inQuote && current == quoteType) {
50+ inQuote = false;
51+ } else if (!inQuote) {
52+ inQuote = true;
53+ quoteType = current;
54+ }
55+ }
56+
57+ if (current == '\\') { //'
58+ escaped = true;
59+ continue;
60+ }
61+
62+ if ((!std::isspace(current) || inQuote) && i != token.length()-1) {
63+ word += current;
64+ } else {
65+ if (i == token.length()-1) {
66+ word += current;
67+ }
68
69- for (unsigned short i = 0; i < tokens.size(); i++) {
70- if (i != 0) {
71 std::string name = "";
72 std::string value = "";
73 bool setValue = false;
74
75- for (auto c : tokens[i]) {
76+ for (auto c : word) {
77 if (c == '=') {
78 setValue = true;
79 continue;
80@@ -276,6 +300,8 @@ std::unordered_map<std::string, std::string> parseAttributes(std::vector<std::st
81 }
82 }
83
84+ word = "";
85+
86 std::string trimmedName = "";
87 for (auto c : name) {
88 if (!std::isspace(c)) {
89@@ -283,6 +309,11 @@ std::unordered_map<std::string, std::string> parseAttributes(std::vector<std::st
90 }
91 }
92
93+ if (attrs.size() == 0) {
94+ value = '"'+trimmedName+'"';
95+ trimmedName = "tagName";
96+ }
97+
98 std::string trimmedValue = "";
99 int sliceStart = 0;
100 int sliceEnd = value.length();
101@@ -296,7 +327,8 @@ std::unordered_map<std::string, std::string> parseAttributes(std::vector<std::st
102 }
103
104 for (int t = value.length()-1; t >= 0; t--) {
105- if (!std::isspace(value[t])) {
106+ // Trim the trailing / on self closing elements if there isn't a space inbetween
107+ if (!std::isspace(value[t]) && value[t] != '/') {
108 sliceEnd = t;
109 break;
110 }
111@@ -315,17 +347,6 @@ std::unordered_map<std::string, std::string> parseAttributes(std::vector<std::st
112 return attrs;
113 }
114
115-enum Type {
116- TEXT,
117- NODE
118-};
119-
120-struct Token {
121- Type type;
122- std::string data;
123-};
124-
125-// !TODO: Make a html string parser as well
126 std::unique_ptr<Node> parseStream(std::istream& inputStream) {
127 std::unique_ptr<Node> root = std::make_unique<Node>();
128 root->setTagName("root");
129@@ -335,12 +356,40 @@ std::unique_ptr<Node> parseStream(std::istream& inputStream) {
130 bool inTag = false;
131 bool escaped = false;
132 bool inQuote = false;
133+ bool inComment = false;
134 char quoteType = '"';
135
136- std::vector<Token> tokens;
137+ std::string token;
138
139 char current;
140 while (inputStream.get(current)) {
141+
142+ // Finds the --> and removes it then resets inComment
143+ if (inComment) {
144+ // added the peek to prevent hitting on every -
145+ if (current == '-' && inputStream.peek() == '-') {
146+ char a,b;
147+ // load the next two
148+ if (inputStream.get(a) && inputStream.get(b)) {
149+ // We know b == -
150+ if (b == '>') {
151+ // Close the comment
152+ inComment = false;
153+ }
154+ }
155+
156+ if (!inComment) {
157+ // we don't put anything back and that puts us where we need to be
158+ continue;
159+ } else {
160+ // Not the end
161+ inputStream.putback(b);
162+ inputStream.putback(a);
163+ }
164+ }
165+ continue;
166+ }
167+
168 if (inTag) {
169 if ((current == '"' || current == '\'') && !escaped) {
170 if (inQuote && current == quoteType) {
171@@ -350,23 +399,129 @@ std::unique_ptr<Node> parseStream(std::istream& inputStream) {
172 quoteType = current;
173 }
174 }
175+
176 if (current == '>' && !inQuote && !escaped) {
177 inTag = false;
178 // Even if the next tag is next still add a text tag as we can just check if its empty and remove it
179 // this check would still have to be done either way so we aren't wasting anything
180- std::string data = "";
181- Token newToken = Token{TEXT, data};
182- tokens.push_back(newToken);
183- // for closing tags, use them as a checksum and to manage the position in the tree
184+ if (token.length() > 0) {
185+ bool empty = true;
186+ bool closingTag = false;
187+ for (int i = 0; i < token.length(); i++) {
188+ auto c = token[i];
189+ if (std::isspace(c)) {
190+ continue;
191+ } else if (c == '/') {
192+ closingTag = true;
193+ } else {
194+ empty = false;
195+ break;
196+ }
197+ }
198+
199+ bool selfClosing = false;
200+ int selfClosingPosition = token.length()-1;
201+ if (!closingTag) {
202+ for (int i = token.length()-1; i >= 0; i--) {
203+ auto c = token[i];
204+ if (std::isspace(c)) {
205+ continue;
206+ } else if (c == '/') {
207+ selfClosingPosition = i;
208+ selfClosing = true;
209+ break;
210+ } else {
211+ break;
212+ }
213+ }
214+ }
215+
216+ if (!empty) {
217+ if (selfClosing) {
218+ //Create element and don't jump inside
219+ // Use node instead of currentNode because we do not need to jump into the created element
220+ auto attrs = parseAttributes(token.substr(0, selfClosingPosition));
221+ auto node = currentNode->createElement(attrs["tagName"]);
222+ for (auto const& pair : attrs) {
223+ // All attributes are stored as strings so we can just throw them in
224+ node->setAttribute(pair.first, pair.second);
225+ }
226+ } else if (closingTag) {
227+ // Checksum and tree move
228+ std::string tagName = "";
229+ for (auto t : token) {
230+ if (!std::isspace(t) && t != '/') {
231+ tagName += t;
232+ } else if (t == '/') {
233+ // For closing tags we just want the name
234+ continue;
235+ } else if (tagName.length() > 0) {
236+ break;
237+ }
238+ }
239+ if (currentNode->getTagName() == tagName) {
240+ currentNode = currentNode->parent;
241+ } else {
242+ std::cerr << "malformed html: closing tag (</" << tagName << ">) found for <" << currentNode->getTagName() << ">" << std::endl;
243+ }
244+ } else {
245+ // Create a element and jump inside
246+ auto attrs = parseAttributes(token);
247+ currentNode = currentNode->createElement(attrs["tagName"]);
248+ for (auto const& pair : attrs) {
249+ // All attributes are stored as strings so we can just throw them in
250+ currentNode->setAttribute(pair.first, pair.second);
251+ }
252+
253+ }
254+ }
255+ }
256+
257+ token = "";
258 continue;
259 }
260 } else if (!escaped && current == '<') {
261+ // if the next charector is a !
262+ if (inputStream.peek() == '!') {
263+ char a,b,c;
264+ // load the next three charecters (includes !)
265+ if (inputStream.get(a) && inputStream.get(b) && inputStream.get(c)) {
266+ // We know a == !
267+ if (b == '-' && c == '-') {
268+ inComment = true;
269+ }
270+ }
271+
272+ if (inComment) {
273+ continue;
274+ } else {
275+ // Not a comment add all back
276+ inputStream.putback(c);
277+ inputStream.putback(b);
278+ inputStream.putback(a);
279+ }
280+ }
281 inTag = true;
282- std::string data = "";
283- Token newToken = Token{NODE, data};
284- tokens.push_back(newToken);
285+
286 // Heres where you actually make the text node and above the real nodes
287 // can also prob make the vector of tokens a single variable
288+ // it really just needs to be a data string bc we know the type based on inTag
289+
290+
291+ bool hasText = false;
292+ for (auto t : token) {
293+ if (!std::isspace(t)) {
294+ hasText = true;
295+ break;
296+ }
297+ }
298+ if (hasText) {
299+ auto node = currentNode->createElement("text");
300+ node->setInnerText(token);
301+ }
302+
303+
304+ token = "";
305 continue;
306 }
307
308@@ -377,13 +532,7 @@ std::unique_ptr<Node> parseStream(std::istream& inputStream) {
309 continue;
310 }
311
312- if (!tokens.empty()) {
313- tokens.back().data += current;
314- }
315- }
316-
317- for (auto t : tokens) {
318- std::cout << t.type << t.data << std::endl;
319+ token += current;
320 }
321
322 return root;
323@@ -396,7 +545,7 @@ int main() {
324
325 std::ifstream inputFile("./index.html");
326 auto document = parseStream(inputFile);
327-
328+
329 document->print();
330 return 0;
331 }
332diff --git a/index.html b/index.html
333index 47a71eb..e4e370d 100644
334--- a/index.html
335+++ b/index.html
336@@ -1,5 +1,15 @@
337 <html>
338- <h1 id="<"></h1>
339+ <h1 id="<">
340+
341+<!-- asdjkasdhjklsahdjklsa -->
342+<!--
343+ <b></b>
344+-->
345+<!-- -->
346+<!---->
347+ </h1>
348+ <br />
349+ <input type="text"/>
350 <p id="test">
351 this is a test
352 </p>
353diff --git a/main b/main
354index 445d49b..688e6e3 100755
355Binary files a/main and b/main differ