Author: Mason Wright
Email:
[email protected]
Date: Sun, 22 Jun 2025 19:42:58 -0600
src/grim.cc
56074a6bfe4498d092f3a227297c8c20e2bb962c
Working on testSelector
Clone
-
Makefile
M
build/grim.o
-
build/parser.o
M
include/grim.h
-
include/parser.h
-
index.html
M
main
-
main.cc
-
src/adapter.cc
-
src/events.cc
M
src/grim.cc
M
src/parser.cc
-
style.css
Commits
b966b2a517365074e5c381dbdea05b3221dc0198
e840f1eeb0ae26af69e1ae146ea9938e28e9f1af
e4e05418a640eaed08cd1ec7cd8644eb1dbcca50
4e01ba8ad2c3361fa4be3d896288020948b58b5e
aae562ac1350480e4889aabb35899f776c5b59e9
6c3ae0e31eb0893f20e3872117f92cc6b9a942af
350e7d88bb2feb9db00c6e032cc6623f215b7adf
95e6c70d23e99ffcf70e5bbe12503496e5d8f232
e188783659b9bc3b9993a647e93ed110e7f41db6
5e4c38ff3c212cdd9881427ef3f8c2706539a190
e50ea9e1356a74af18fdd171337ef9dc931e1f4e
8f2e83556d12aaebe8e8597ea6923804b0eb7a43
1627c585128af263181053ab2cf1a4cdcd14ee21
def3513f75b325464ad88a33c741c4ca80572b77
a21501590980a905fa9b902897d700a42a08b7f0
56074a6bfe4498d092f3a227297c8c20e2bb962c
d9cf1485b7ae0614130494f0e73237921323b9a1
80f04b134ae32ad8a9d526007b33dd02f6600f05
23d6c65f9368d3c622a55a3068a6b2f1efa0c8d4
09c195df02536b6a796bd648fce9669397b96109
f2b5c8202fbc904e2ed78260e3fdbd55164799d2
4bfba076120f389994fc46a98e8b7a2622314400
e36ac5417e10ee9b9f94f340e1ccf28afc5705ea
d00dc89a86dd7e2fcfd4618bc3a1c8cfba9e3c3d
d9eef16adaf292f3748db5fb5aa98463de10d712
18ff2ec1bfc1cf9fcd17c1acb05c3b41f8f0ed83
9e7fd2980d723437ea621b78d395fa72ca3f4922
Diff
diff --git a/src/grim.cc b/src/grim.cc index 9950d0c..6d356b9 100755 --- a/src/grim.cc +++ b/src/grim.cc @@ -9 +9 @@ std::string ClassList::value() const { - if (classes.empty()) { + if (values.empty()) { @@ -12,3 +12,3 @@ std::string ClassList::value() const { - std::string collection = classes[0]; - for (size_t i = 1; i < classes.size(); ++i) { - collection += " " + classes[i]; + std::string collection = values[0]; + for (size_t i = 1; i < values.size(); ++i) { + collection += " " + values[i]; @@ -19,4 +18,0 @@ std::string ClassList::value() const { -std::vector
ClassList::values() { - return classes; -} - @@ -24 +20 @@ void ClassList::add(std::string value) { - classes.push_back(value); + values.push_back(value); @@ -28,4 +24,4 @@ void ClassList::remove(std::string value) { - auto it_prev = std::find(classes.begin(), classes.end(), value); - if (it_prev != classes.end()) { - *it_prev = classes.back(); - classes.pop_back(); + auto it_prev = std::find(values.begin(), values.end(), value); + if (it_prev != values.end()) { + *it_prev = values.back(); + values.pop_back(); @@ -139,9 +134,0 @@ std::string Node::getAttribute(const std::string& name) const { -std::vector
Node::getAttributeKeys() { - std::vector
keys; - - for(auto p : Attributes) { - keys.push_back(p.first); - } - return keys; -} - @@ -185 +172 @@ struct Style { - size_t index; + int index; @@ -200 +187 @@ class StyleHandler { - // on a selector that applies to the wrong element. Todo this we take the right most part + // on a selector that applys to the wrong element. Todo this we take the right most part @@ -203 +190 @@ class StyleHandler { - // is a candidate and we run the full selector on it. That includes checking parents,children, + // is a canidate and we run the full selector on it. That includes checking parents,children, @@ -206,0 +194,2 @@ class StyleHandler { + int currentIndex = 0; + @@ -241 +230 @@ class StyleHandler { - // if the current letter is a : that is either a pseudo element + // if the current letter is a : that is either a psuedo element @@ -298,48 +287 @@ class StyleHandler { - std::vector
parseNodeParts(Node* node) { - std::vector
parts; - - parts.push_back(node->getTagName()); - parts.push_back("#"+node->getId()); - - // classes and attributes - auto keys = node->getAttributeKeys(); - - for (auto k : keys) { - if (k != "tagName" || k != "id" || k != "innerText" || k != "class") { - std::string v; - std::string av = node->getAttribute(k); - if (av != "") { - v = "=\""+av+"\""; - - } - parts.push_back(k+v); - } - } - - auto classes = node->classList.values(); - - for (auto c : classes) { - parts.push_back("."+c); - } - - return parts; - } - - std::string cleanSelector(std::string selector) { - std::string cleaned; - for (size_t i = 0; i < selector.length(); i++) { - if (selector[i] == ' ' && i > 0 && i < selector.length()-1) { - char prev = selector[i-1]; - char next = selector[i+1]; - - if (prev != '>' && prev != '+' && prev != '~' && next != '>' && next != '+' && next != '~' && !std::isspace(prev) && !std::isspace(next)) { - cleaned += selector[i]; - } - } else if (selector[i] == ' ' && (i == 0 || i == selector.length()-1)) { - continue; - } else { - cleaned += selector[i]; - } - } - return cleaned; - } + // std::vector
parseNodeParts(Node* node) { @@ -348,5 +289,0 @@ class StyleHandler { - std::string selector = cleanSelector(style.selector); - style.selector = selector; - size_t index = styles.size(); - - style.index = index; @@ -354,10 +291,5 @@ class StyleHandler { - - std::vector
parts = parseSelectorParts(selector); - - for (auto p : parts) { - if (basemap.find(p) != basemap.end()) { - basemap[p].push_back(index); - } else { - std::vector
bm = {index}; - basemap[p] = bm; - } + if (basemap.find(styles.back().selector) != basemap.end()) { + basemap[styles.back().selector].push_back(styles.size()-1); + } else { + std::vector
bm = {styles.size() - 1}; + basemap[styles.back().selector] = bm; @@ -364,0 +297,2 @@ class StyleHandler { + // Give the properties a cascade index + styles.back().index = currentIndex++; @@ -366,23 +299,0 @@ class StyleHandler { - - std::unordered_map
getStyles(Node* node) { - std::vector
parts = parseNodeParts(node); - - std::vector
canidates; - - for (auto p : parts) { - if (auto sty = basemap.find(p); sty != basemap.end()) { - for (auto s : sty->second) { - bool found = false; - // This makes sure we aren't adding the same styles to the candidates list - for (auto c : canidates) { - if (c == s) { - found = true; - break; - } - } - if (!found) { - canidates.push_back(s); - } - } - } - } @@ -390,3 +300,0 @@ class StyleHandler { - // Now we have the list of candidates next we will refined our selection - // we need to pull the selectors from each candidate then run testSelector on it - } @@ -394,158 +301,0 @@ class StyleHandler { - -std::vector
splitSelector(std::string selector, char key) { - size_t nesting = 0; - - std::vector
selectors; - std::string current = ""; - - for (auto s : selector) { - if (s == '(') { - nesting++; - } else if (s == ')') { - nesting--; - } else if (s == '[') { - nesting++; - } else if (s == ']') { - nesting--; - } else if (s == '{') { - nesting++; - } else if (s == '}') { - nesting--; - } else if (s == key && nesting == 0) { - selectors.push_back(current); - current = ""; - continue; - } - current += s; - } - - selectors.push_back(current); - - // Trim the space - for (size_t i = 0; i < selectors.size(); i++) { - size_t start = 0; - size_t end = selectors[i].length()-1; - for (size_t s = 0; s < selectors[i].length(); s++) { - if (!std::isspace(selectors[i][s])) { - start = s; - break; - } - } - for (size_t e = selectors[i].length()-1; e > start; e--) { - if (!std::isspace(selectors[i][e])) { - end = e; - break; - } - } - selectors[i] = selectors[i].substr(start,end - start +1); - } - - return selectors; -} - -void popSelector(std::string selector, std::string& trimmed, std::string& popped, char& symbol) { - for (size_t i = selector.length()-1; i >= 0; i--) { - if ( - selector[i] == ' ' || - selector[i] == '>' || - selector[i] == '~' || - selector[i] == '+' - ) { - symbol = selector[i]; - trimmed = selector.substr(0,i); - break; - } else { - popped = selector[i]+popped; - } - } -} - -bool testSelector(Node* node, std::string selector) { - // By the time we get here, we know the right most selector some what matches - // so now if it is a compound selector we should see if the parent('s) some what match - if (selector == '*') { - return true; - } - - std::vector
selectors = splitSelector(selector, ','); - - if (selectors.size() > 1) { - for (auto s : selectors) { - bool match = testSelector(node, s); - - if (match) { - return true; - } - } - } - - std::string trimmed, popped; - char symbol; - popSelector(selector, trimmed, popped, symbol); - - - // Check if popped matches this element - if () { - } else { - return false; - } - - if (symbol == '' || node->parent != nullptr) { - return false; - } - - bool matched = false; - if (symbol == '>') { - // We are just testing the parent node here - matched = testSelector(node->parent, trimmed); - } else if (symbol == '+') { - auto children = node->parent.children; - if (children.size() > 1) { - for (size_t i = 0; i < children.size(); i++) { - Node* child_ptr = children[i].get(); - - if (child_ptr == node) { - if (i == 0) { - return false; - } else { - // In this selector we just need to get the direct sibling and test it - matched = testSelector(children[i-1].get(), trimmed); - } - break; - } - } - } - } else if (symbol == '~') { - auto children = node->parent.children; - if (children.size() > 1) { - for (size_t i = 0; i < children.size(); i++) { - Node* child_ptr = children[i].get(); - - if (child_ptr == node) { - break; - } else { - // We have to test every selector before the current element, - // if any match then it matches - matched = testSelector(children[i].get(), trimmed); - if (matched) { - break; - } - } - } - } - } else if (symbol == ' ') { - // For the descendant combinator we have to check every parent until we match or nullptr out - Node* current = node->parent.get(); - while (current != nullptr) { - if (testSelector(current, trimmed)) { - matched = true; - break; - } - current = current->parent.get(); - } - } - - return matched; -} - -
#include "grim.h" #include
#include
#include
#include
#include
std::string ClassList::value() const { if (classes.empty()) { return ""; } std::string collection = classes[0]; for (size_t i = 1; i < classes.size(); ++i) { collection += " " + classes[i]; } return collection; } std::vector
ClassList::values() { return classes; } void ClassList::add(std::string value) { classes.push_back(value); } void ClassList::remove(std::string value) { auto it_prev = std::find(classes.begin(), classes.end(), value); if (it_prev != classes.end()) { *it_prev = classes.back(); classes.pop_back(); } } // Constructor Node::Node() : parent(nullptr) {} std::string Node::getTagName() const { return TagName; } void Node::setTagName(const std::string& name) { TagName = name; } const std::unordered_map
& Node::getAttributes() const { return Attributes; } // Implement the getter/setter methods declared using the macro #define IMPLEMENT_ATTRIBUTE_ACCESSORS(_Type, _FuncNameSuffix, _AttrKeyString) \ _Type Node::get##_FuncNameSuffix() const { \ return getAttribute<_Type>(_AttrKeyString); \ } \ void Node::set##_FuncNameSuffix(_Type value) { \ setAttribute(_AttrKeyString, value); \ } IMPLEMENT_ATTRIBUTE_ACCESSORS(std::string, Id, "id") IMPLEMENT_ATTRIBUTE_ACCESSORS(std::string, InnerText, "innerText") IMPLEMENT_ATTRIBUTE_ACCESSORS(bool, ContentEditable, "contenteditable") IMPLEMENT_ATTRIBUTE_ACCESSORS(std::string, Href, "href") IMPLEMENT_ATTRIBUTE_ACCESSORS(std::string, Src, "src") IMPLEMENT_ATTRIBUTE_ACCESSORS(std::string, Title, "title") IMPLEMENT_ATTRIBUTE_ACCESSORS(std::string, Value, "value") IMPLEMENT_ATTRIBUTE_ACCESSORS(int, TabIndex, "tabindex") IMPLEMENT_ATTRIBUTE_ACCESSORS(bool, Disabled, "disabled") IMPLEMENT_ATTRIBUTE_ACCESSORS(bool, Required, "required") IMPLEMENT_ATTRIBUTE_ACCESSORS(bool, Checked, "checked") Node* Node::createElement(std::string name) { std::unique_ptr
newNode = std::make_unique
(); newNode->setTagName(name); newNode->parent = this; children.push_back(std::move(newNode)); return children.back().get(); } template
void Node::setAttribute(const std::string& name, const T& value) { if constexpr (std::is_same_v
) { if (value) { Attributes[name] = ""; } else { Attributes.erase(name); } } else if constexpr (std::is_arithmetic_v
) { Attributes[name] = std::to_string(value); } else { static_assert(std::is_convertible_v
, "setAttribute: Type cannot be converted to std::string automatically."); Attributes[name] = static_cast
(value); } } void Node::setAttribute(const std::string& name, const std::string& value) { Attributes[name] = value; } template
T Node::getAttribute(const std::string& name) const { auto it = Attributes.find(name); if (it != Attributes.end()) { const std::string& s = it->second; if constexpr (std::is_same_v
) { try { return std::stoi(s); } catch (const std::invalid_argument& e) { return 0; } catch (const std::out_of_range& e) { return 0; } } else if constexpr (std::is_same_v
) { std::string lower_s = s; std::transform(lower_s.begin(), lower_s.end(), lower_s.begin(), [](unsigned char c){ return std::tolower(c); }); return (!lower_s.empty() && lower_s != "false"); } else if constexpr (std::is_same_v
) { try { return std::stod(s); } catch (const std::invalid_argument& e) { return 0.0; } catch (const std::out_of_range& e) { return 0.0; } } else { static_assert(std::is_convertible_v
, "getAttribute: Type conversion from std::string not implemented for this type."); return static_cast
(s); } } return T(); } std::string Node::getAttribute(const std::string& name) const { auto it = Attributes.find(name); if (it != Attributes.end()) { return it->second; } return ""; } std::vector
Node::getAttributeKeys() { std::vector
keys; for(auto p : Attributes) { keys.push_back(p.first); } return keys; } void Node::print(int indent) const { for (int i = 0; i < indent; ++i) { std::cout << " "; } std::cout << "<" << getTagName(); for (const auto& attr_pair : getAttributes()) { if (attr_pair.first == "innerText" || attr_pair.first == "tagName") { continue; } std::cout << " " << attr_pair.first; if (!attr_pair.second.empty()) { std::cout << "=\"" << attr_pair.second << "\""; } } std::cout << ">"; if (!getAttribute("innerText").empty()) { std::cout << "\n" << getAttribute("innerText"); } std::cout << std::endl; for (const auto& child : children) { child->print(indent + 1); } for (int i = 0; i < indent; ++i) { std::cout << " "; } std::cout << "" << getTagName() << ">" << std::endl; } struct Style { std::unordered_map
properties; std::string selector; // Index of when it was added (for cascading) size_t index; }; // struct BaseParts { // std::string tagName; // std::string id; // std::vector
classes; // std::unordered_map
attributes; // } class StyleHandler { private: // basemap: Maps baseparts to a index of the styles vector pointing to a Style object // because processing a CSS selector isn't trivial we want to make a short list of // the styles that can possibly be a match so we aren't spending a lot of compute // on a selector that applies to the wrong element. Todo this we take the right most part // of a selector (that is the part that targets the current element) and we do a few checks // like is the tagname the same, does it have that class, and do the id's match. If so that // is a candidate and we run the full selector on it. That includes checking parents,children, // or what ever else the selector needs to match. std::unordered_map
> basemap; std::vector