home
readme
diff
tree
note
docs
0commit 23d6c65f9368d3c622a55a3068a6b2f1efa0c8d4
1Author: Mason Wright <mason@lakefox.net>
2Date: Thu Jun 19 20:40:16 2025 -0600
3
4 Base parts added
5
6diff --git a/index.html b/index.html
7index e4e370d..3284123 100644
8--- a/index.html
9+++ b/index.html
10@@ -1,16 +1,20 @@
11+<!DOCTYPE html>
12 <html>
13+ <head>
14+<style>
15+ h1 {
16+ color: red;
17+ }
18+</style>
19+ </head>
20+ <body>
21 <h1 id="<">
22-
23-<!-- asdjkasdhjklsahdjklsa -->
24-<!--
25- <b></b>
26--->
27-<!-- -->
28-<!---->
29+ Hello world
30 </h1>
31 <br />
32 <input type="text"/>
33 <p id="test">
34 this is a test
35 </p>
36+ </body>
37 </html>
38diff --git a/main b/main
39index 06056be..6f01d7a 100755
40Binary files a/main and b/main differ
41diff --git a/main.cc b/main.cc
42index 135fb3a..f51d773 100755
43--- a/main.cc
44+++ b/main.cc
45@@ -5,27 +5,34 @@
46 #include "parser.h"
47
48 int main() {
49- // Option 1: Parse from a file
50- std::ifstream inputFile("./index.html");
51- if (!inputFile.is_open()) {
52- std::cerr << "Error: Could not open index.html" << std::endl;
53- return 1;
54- }
55+ // // Option 1: Parse from a file
56+ // std::ifstream inputFile("./index.html");
57+ // if (!inputFile.is_open()) {
58+ // std::cerr << "Error: Could not open index.html" << std::endl;
59+ // return 1;
60+ // }
61+ //
62+ // std::unique_ptr<Node> document = parseStream(inputFile);
63+ // inputFile.close(); // Close the file after parsing
64+ //
65+ // // Option 2: Parse from a string (for testing)
66+ // // std::string html_content = "<div>Hello <b>world</b>!</div><p>Another paragraph.</p>";
67+ // // std::stringstream ss(html_content);
68+ // // std::unique_ptr<Node> document = parseStream(ss);
69+ //
70+ // // Print the parsed document tree
71+ // if (document) {
72+ // document->print();
73+ // } else {
74+ // std::cout << "Document parsing failed or resulted in an empty document." << std::endl;
75+ // }
76
77- std::unique_ptr<Node> document = parseStream(inputFile);
78- inputFile.close(); // Close the file after parsing
79+
80+ std::ifstream inputFile("./style.css");
81
82- // Option 2: Parse from a string (for testing)
83- // std::string html_content = "<div>Hello <b>world</b>!</div><p>Another paragraph.</p>";
84- // std::stringstream ss(html_content);
85- // std::unique_ptr<Node> document = parseStream(ss);
86+ parseCSS(inputFile);
87
88- // Print the parsed document tree
89- if (document) {
90- document->print();
91- } else {
92- std::cout << "Document parsing failed or resulted in an empty document." << std::endl;
93- }
94+ inputFile.close();
95
96- return 0;
97+ return 0;
98 }
99diff --git a/src/element.cc b/src/element.cc
100index 157533f..5c7f76a 100755
101--- a/src/element.cc
102+++ b/src/element.cc
103@@ -164,3 +164,127 @@ void Node::print(int indent) const {
104 }
105 std::cout << "</" << getTagName() << ">" << std::endl;
106 }
107+
108+struct Style {
109+ std::unordered_map<std::string, std::string> properties;
110+ std::string selector;
111+ // Index of when it was added (for cascading)
112+ int index;
113+}
114+
115+// struct BaseParts {
116+// std::string tagName;
117+// std::string id;
118+// std::vector<std::string> classes;
119+// std::unordered_map<std::string, std::string> attributes;
120+// }
121+
122+class StyleHandler {
123+ private:
124+ // basemap: Maps baseparts to a index of the styles vector pointing to a Style object
125+ // because processing a CSS selector isn't trivial we want to make a short list of
126+ // the styles that can possibly be a match so we aren't spending a lot of compute
127+ // on a selector that applys to the wrong element. Todo this we take the right most part
128+ // of a selector (that is the part that targets the current element) and we do a few checks
129+ // like is the tagname the same, does it have that class, and do the id's match. If so that
130+ // is a canidate and we run the full selector on it. That includes checking parents,children,
131+ // or what ever else the selector needs to match.
132+ std::unordered_map<std::string, std::vector<size_t>> basemap;
133+ std::vector<Style> styles;
134+ int currentIndex = 0;
135+
136+
137+ std::vector<std::string> parseSelectorParts(std::string selector) {
138+ // we want to find the right most (right of a " " > ~ + ) selector
139+ // account for * for commas split the selector then for each parse right
140+
141+ std::vector<std::string> parts;
142+ std::string word;
143+
144+ for (int s = 0; s < selector.length(); s++) {
145+ // Start parsing if we see a comma or the end of the selector
146+ if (selector[s] == ',' || s == selector.length()-1) {
147+ // Break the tag and add the right most parts to parts
148+ for (int i = word.length()-1; i > -1; i--) {
149+ if (
150+ word[i] == ' ' ||
151+ word[i] == '>' ||
152+ word[i] == '~' ||
153+ word[i] == '+'
154+ ) {
155+ word = word.substr(i+1);
156+ break;
157+ }
158+ }
159+
160+ // Now word contains the right most selection
161+ // we will extract the quick to verify parts from it
162+ // so we can build the basemap
163+ std::string part;
164+ for (auto w : word) {
165+ // if the current letter is a : that is either a psuedo element
166+ // or selector. We don't care about that part for this part
167+ if (w != ':') {
168+ // We don't care if the thing we are splitting is a id or class we just
169+ // need them split up so we can use them as map keys
170+
171+ if (w == '#') {
172+ if (part != "") {
173+ parts.push_back(part);
174+ part = "";
175+ }
176+
177+ } else if (w == '.') {
178+ if (part != "") {
179+ parts.push_back(part);
180+ part = "";
181+ }
182+ } else if (w == '[' || w == ']') {
183+ if (part != "") {
184+ parts.push_back(part);
185+ part = "";
186+ }
187+ continue;
188+ }
189+
190+ part += w;
191+ } else {
192+ break;
193+ }
194+ }
195+
196+ if (part != "") {
197+ parts.push_back(part);
198+ }
199+ } else {
200+ word += selector[s];
201+ }
202+ }
203+
204+ std::vector<std::string> deduplicated;
205+
206+ for (int p1 = 0; p1 < parts.size(); p1++) {
207+ bool matches = false;
208+
209+ for (int p2 = p1+1; p2 < parts.size(); p2++) {
210+ if (parts[p1] == parts[p2]) {
211+ matches = true;
212+ }
213+ }
214+
215+ if (!matches) {
216+ deduplicated.push_back(parts[p1]);
217+ }
218+ }
219+
220+ return deduplicated;
221+ }
222+ public:
223+ void add(Style style) {
224+ styles.push_back(std::move(style));
225+ basemap[styles.back().selector] = styles.size() - 1; // Store the index
226+ // Give the properties a cascade index
227+ styles.back().index = currentIndex++;
228+ }
229+
230+}
231diff --git a/src/parser.cc b/src/parser.cc
232index 784fbdd..9017b6c 100644
233--- a/src/parser.cc
234+++ b/src/parser.cc
235@@ -222,12 +222,14 @@ std::unique_ptr<Node> parseStream(std::istream& inputStream) {
236 } else {
237 // Create a element and jump inside
238 auto attrs = parseAttributes(token);
239- currentNode = currentNode->createElement(attrs["tagName"]);
240- for (auto const& pair : attrs) {
241- // All attributes are stored as strings so we can just throw them in
242- currentNode->setAttribute(pair.first, pair.second);
243+ // Don't add elements like <!DOCTYPE>
244+ if (attrs["tagName"][0] != '!') {
245+ currentNode = currentNode->createElement(attrs["tagName"]);
246+ for (auto const& pair : attrs) {
247+ // All attributes are stored as strings so we can just throw them in
248+ currentNode->setAttribute(pair.first, pair.second);
249+ }
250 }
251-
252 }
253 }
254 }
255diff --git a/style.css b/style.css
256new file mode 100644
257index 0000000..2e59320
258--- /dev/null
259+++ b/style.css
260@@ -0,0 +1,7 @@
261+h1 {
262+ color: red;
263+}
264+
265+h1 > #id {
266+ background: purple;
267+}