home
readme
diff
tree
note
docs
0commit 56074a6bfe4498d092f3a227297c8c20e2bb962c
1Author: Mason Wright <mason@lakefox.net>
2Date: Sun Jun 22 19:42:58 2025 -0600
3
4 Working on testSelector
5
6diff --git a/build/grim.o b/build/grim.o
7index d30b88a..a655a9c 100644
8Binary files a/build/grim.o and b/build/grim.o differ
9diff --git a/include/grim.h b/include/grim.h
10index 0919017..05f148b 100644
11--- a/include/grim.h
12+++ b/include/grim.h
13@@ -30,9 +30,10 @@ struct State {
14
15 class ClassList {
16 private:
17- std::vector<std::string> values;
18+ std::vector<std::string> classes;
19 public:
20 std::string value() const;
21+ std::vector<std::string> values();
22 void add(std::string value);
23 void remove(std::string value);
24 };
25@@ -83,6 +84,8 @@ public:
26 void setAttribute(const std::string& name, const std::string& value);
27 std::string getAttribute(const std::string& name) const;
28
29+ std::vector<std::string> getAttributeKeys();
30+
31 void print(int indent = 0) const;
32 };
33
34diff --git a/main b/main
35index 29af90c..f079efc 100755
36Binary files a/main and b/main differ
37diff --git a/src/grim.cc b/src/grim.cc
38index 6d356b9..9950d0c 100755
39--- a/src/grim.cc
40+++ b/src/grim.cc
41@@ -6,25 +6,29 @@
42 #include <cctype>
43
44 std::string ClassList::value() const {
45- if (values.empty()) {
46+ if (classes.empty()) {
47 return "";
48 }
49- std::string collection = values[0];
50- for (size_t i = 1; i < values.size(); ++i) {
51- collection += " " + values[i];
52+ std::string collection = classes[0];
53+ for (size_t i = 1; i < classes.size(); ++i) {
54+ collection += " " + classes[i];
55 }
56 return collection;
57 }
58
59+std::vector<std::string> ClassList::values() {
60+ return classes;
61+}
62+
63 void ClassList::add(std::string value) {
64- values.push_back(value);
65+ classes.push_back(value);
66 }
67
68 void ClassList::remove(std::string value) {
69- auto it_prev = std::find(values.begin(), values.end(), value);
70- if (it_prev != values.end()) {
71- *it_prev = values.back();
72- values.pop_back();
73+ auto it_prev = std::find(classes.begin(), classes.end(), value);
74+ if (it_prev != classes.end()) {
75+ *it_prev = classes.back();
76+ classes.pop_back();
77 }
78 }
79
80@@ -132,6 +136,15 @@ std::string Node::getAttribute(const std::string& name) const {
81 return "";
82 }
83
84+std::vector<std::string> Node::getAttributeKeys() {
85+ std::vector<std::string> keys;
86+
87+ for(auto p : Attributes) {
88+ keys.push_back(p.first);
89+ }
90+ return keys;
91+}
92+
93 void Node::print(int indent) const {
94 for (int i = 0; i < indent; ++i) {
95 std::cout << " ";
96@@ -169,7 +182,7 @@ struct Style {
97 std::unordered_map<std::string, std::string> properties;
98 std::string selector;
99 // Index of when it was added (for cascading)
100- int index;
101+ size_t index;
102 };
103
104 // struct BaseParts {
105@@ -184,15 +197,13 @@ class StyleHandler {
106 // basemap: Maps baseparts to a index of the styles vector pointing to a Style object
107 // because processing a CSS selector isn't trivial we want to make a short list of
108 // the styles that can possibly be a match so we aren't spending a lot of compute
109- // on a selector that applys to the wrong element. Todo this we take the right most part
110+ // on a selector that applies to the wrong element. Todo this we take the right most part
111 // of a selector (that is the part that targets the current element) and we do a few checks
112 // like is the tagname the same, does it have that class, and do the id's match. If so that
113- // is a canidate and we run the full selector on it. That includes checking parents,children,
114+ // is a candidate and we run the full selector on it. That includes checking parents,children,
115 // or what ever else the selector needs to match.
116 std::unordered_map<std::string, std::vector<size_t>> basemap;
117 std::vector<Style> styles;
118- int currentIndex = 0;
119-
120
121 std::vector<std::string> parseSelectorParts(std::string selector) {
122 // we want to find the right most (right of a " " > ~ + ) selector
123@@ -227,7 +238,7 @@ class StyleHandler {
124 // so we can build the basemap
125 std::string part;
126 for (auto w : word) {
127- // if the current letter is a : that is either a psuedo element
128+ // if the current letter is a : that is either a pseudo element
129 // or selector. We don't care about that part for this part
130 if (w != ':') {
131 // We don't care if the thing we are splitting is a id or class we just
132@@ -284,18 +295,257 @@ class StyleHandler {
133 }
134
135
136- // std::vector<std::string> parseNodeParts(Node* node) {
137+ std::vector<std::string> parseNodeParts(Node* node) {
138+ std::vector<std::string> parts;
139+
140+ parts.push_back(node->getTagName());
141+ parts.push_back("#"+node->getId());
142+
143+ // classes and attributes
144+ auto keys = node->getAttributeKeys();
145+
146+ for (auto k : keys) {
147+ if (k != "tagName" || k != "id" || k != "innerText" || k != "class") {
148+ std::string v;
149+ std::string av = node->getAttribute(k);
150+ if (av != "") {
151+ v = "=\""+av+"\"";
152+
153+ }
154+ parts.push_back(k+v);
155+ }
156+ }
157+
158+ auto classes = node->classList.values();
159+
160+ for (auto c : classes) {
161+ parts.push_back("."+c);
162+ }
163+
164+ return parts;
165+ }
166+
167+ std::string cleanSelector(std::string selector) {
168+ std::string cleaned;
169+ for (size_t i = 0; i < selector.length(); i++) {
170+ if (selector[i] == ' ' && i > 0 && i < selector.length()-1) {
171+ char prev = selector[i-1];
172+ char next = selector[i+1];
173+
174+ if (prev != '>' && prev != '+' && prev != '~' && next != '>' && next != '+' && next != '~' && !std::isspace(prev) && !std::isspace(next)) {
175+ cleaned += selector[i];
176+ }
177+ } else if (selector[i] == ' ' && (i == 0 || i == selector.length()-1)) {
178+ continue;
179+ } else {
180+ cleaned += selector[i];
181+ }
182+ }
183+ return cleaned;
184+ }
185 public:
186 void add(Style style) {
187+ std::string selector = cleanSelector(style.selector);
188+ style.selector = selector;
189+ size_t index = styles.size();
190+
191+ style.index = index;
192 styles.push_back(std::move(style));
193- if (basemap.find(styles.back().selector) != basemap.end()) {
194- basemap[styles.back().selector].push_back(styles.size()-1);
195- } else {
196- std::vector<size_t> bm = {styles.size() - 1};
197- basemap[styles.back().selector] = bm;
198+
199+ std::vector<std::string> parts = parseSelectorParts(selector);
200+
201+ for (auto p : parts) {
202+ if (basemap.find(p) != basemap.end()) {
203+ basemap[p].push_back(index);
204+ } else {
205+ std::vector<size_t> bm = {index};
206+ basemap[p] = bm;
207+ }
208 }
209- // Give the properties a cascade index
210- styles.back().index = currentIndex++;
211 }
212+
213+ std::unordered_map<std::string, std::string> getStyles(Node* node) {
214+ std::vector<std::string> parts = parseNodeParts(node);
215+
216+ std::vector<size_t> canidates;
217+
218+ for (auto p : parts) {
219+ if (auto sty = basemap.find(p); sty != basemap.end()) {
220+ for (auto s : sty->second) {
221+ bool found = false;
222+ // This makes sure we aren't adding the same styles to the candidates list
223+ for (auto c : canidates) {
224+ if (c == s) {
225+ found = true;
226+ break;
227+ }
228+ }
229+ if (!found) {
230+ canidates.push_back(s);
231+ }
232+ }
233+ }
234+ }
235
236+ // Now we have the list of candidates next we will refined our selection
237+ // we need to pull the selectors from each candidate then run testSelector on it
238+ }
239 };
240+
241+std::vector<std::string> splitSelector(std::string selector, char key) {
242+ size_t nesting = 0;
243+
244+ std::vector<std::string> selectors;
245+ std::string current = "";
246+
247+ for (auto s : selector) {
248+ if (s == '(') {
249+ nesting++;
250+ } else if (s == ')') {
251+ nesting--;
252+ } else if (s == '[') {
253+ nesting++;
254+ } else if (s == ']') {
255+ nesting--;
256+ } else if (s == '{') {
257+ nesting++;
258+ } else if (s == '}') {
259+ nesting--;
260+ } else if (s == key && nesting == 0) {
261+ selectors.push_back(current);
262+ current = "";
263+ continue;
264+ }
265+ current += s;
266+ }
267+
268+ selectors.push_back(current);
269+
270+ // Trim the space
271+ for (size_t i = 0; i < selectors.size(); i++) {
272+ size_t start = 0;
273+ size_t end = selectors[i].length()-1;
274+ for (size_t s = 0; s < selectors[i].length(); s++) {
275+ if (!std::isspace(selectors[i][s])) {
276+ start = s;
277+ break;
278+ }
279+ }
280+ for (size_t e = selectors[i].length()-1; e > start; e--) {
281+ if (!std::isspace(selectors[i][e])) {
282+ end = e;
283+ break;
284+ }
285+ }
286+ selectors[i] = selectors[i].substr(start,end - start +1);
287+ }
288+
289+ return selectors;
290+}
291+
292+void popSelector(std::string selector, std::string& trimmed, std::string& popped, char& symbol) {
293+ for (size_t i = selector.length()-1; i >= 0; i--) {
294+ if (
295+ selector[i] == ' ' ||
296+ selector[i] == '>' ||
297+ selector[i] == '~' ||
298+ selector[i] == '+'
299+ ) {
300+ symbol = selector[i];
301+ trimmed = selector.substr(0,i);
302+ break;
303+ } else {
304+ popped = selector[i]+popped;
305+ }
306+ }
307+}
308+
309+bool testSelector(Node* node, std::string selector) {
310+ // By the time we get here, we know the right most selector some what matches
311+ // so now if it is a compound selector we should see if the parent('s) some what match
312+ if (selector == '*') {
313+ return true;
314+ }
315+
316+ std::vector<std::string> selectors = splitSelector(selector, ',');
317+
318+ if (selectors.size() > 1) {
319+ for (auto s : selectors) {
320+ bool match = testSelector(node, s);
321+
322+ if (match) {
323+ return true;
324+ }
325+ }
326+ }
327+
328+ std::string trimmed, popped;
329+ char symbol;
330+ popSelector(selector, trimmed, popped, symbol);
331+
332+
333+ // Check if popped matches this element
334+ if () {
335+ } else {
336+ return false;
337+ }
338+
339+ if (symbol == '' || node->parent != nullptr) {
340+ return false;
341+ }
342+
343+ bool matched = false;
344+ if (symbol == '>') {
345+ // We are just testing the parent node here
346+ matched = testSelector(node->parent, trimmed);
347+ } else if (symbol == '+') {
348+ auto children = node->parent.children;
349+ if (children.size() > 1) {
350+ for (size_t i = 0; i < children.size(); i++) {
351+ Node* child_ptr = children[i].get();
352+
353+ if (child_ptr == node) {
354+ if (i == 0) {
355+ return false;
356+ } else {
357+ // In this selector we just need to get the direct sibling and test it
358+ matched = testSelector(children[i-1].get(), trimmed);
359+ }
360+ break;
361+ }
362+ }
363+ }
364+ } else if (symbol == '~') {
365+ auto children = node->parent.children;
366+ if (children.size() > 1) {
367+ for (size_t i = 0; i < children.size(); i++) {
368+ Node* child_ptr = children[i].get();
369+
370+ if (child_ptr == node) {
371+ break;
372+ } else {
373+ // We have to test every selector before the current element,
374+ // if any match then it matches
375+ matched = testSelector(children[i].get(), trimmed);
376+ if (matched) {
377+ break;
378+ }
379+ }
380+ }
381+ }
382+ } else if (symbol == ' ') {
383+ // For the descendant combinator we have to check every parent until we match or nullptr out
384+ Node* current = node->parent.get();
385+ while (current != nullptr) {
386+ if (testSelector(current, trimmed)) {
387+ matched = true;
388+ break;
389+ }
390+ current = current->parent.get();
391+ }
392+ }
393+
394+ return matched;
395+}
396+
397+
398diff --git a/src/parser.cc b/src/parser.cc
399index 2bfc103..32ca96f 100644
400--- a/src/parser.cc
401+++ b/src/parser.cc
402@@ -272,6 +272,9 @@ std::unique_ptr<Node> parseStream(std::istream& inputStream) {
403 break;
404 }
405 }
406+
407+ // !TODO: Add a vector that stores inner text and when elements start to close out pop them but add all to their innerText
408+ // + Will need something simular for innerHTML
409 if (hasText) {
410 auto node = currentNode->createElement("text");
411 node->setInnerText(token);