Author: Mason Wright
Email:
[email protected]
Date: Sun, 15 Jun 2025 16:07:44 -0600
element.cc
d9eef16adaf292f3748db5fb5aa98463de10d712
Creates elements, figuring out how to implement attributes
Clone
-
adapter.cc
-
build.sh
M
element.cc
-
events.cc
M
index.html
M
main
-
main.cc
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/element.cc b/element.cc index ae1f1b4..0593b32 100755 --- a/element.cc +++ b/element.cc @@ -1,0 +2 @@ +#include
@@ -8,2 +9,4 @@ -#include
-#include
+ +#define GETTER_SETTER(type, name) \ + type get##name() const { return name; } \ + void set##name(type value) { name = value; } @@ -35,28 +37,0 @@ struct State { -class ClassList { - private: - std::vector
values; - public: - std::string value() const { - if (values.empty()) { - return ""; - } - std::string collection = values[0]; - for (size_t i = 1; i < values.size(); ++i) { - collection += " " + values[i]; - } - return collection; - } - - void add(std::string value) { - values.push_back(value); - } - - void remove(std::string value) { - auto it_prev = std::find(values.begin(), values.end(), value); - if (it_prev != values.end()) { - *it_prev = values.back(); // Overwrite prevous position with last element - values.pop_back(); // Remove the last element - } - } -}; - @@ -66 +41,2 @@ class Node { - // !NOTE: ContentEditable only supports plaintext + std::string Id; + std::string InnerText; @@ -68,40 +43,0 @@ class Node { - - template
- T stringToType(const std::string& s) const { - // This should theoretically not be called if we have std::string overload - // but acts as a fallback for unsupported types or as a reminder. - static_assert(!std::is_same
::value, "stringToType not implemented for this type"); - return T(); // Should not reach here - } - - int stringToType(const std::string& s) const { - try { - return std::stoi(s); - } catch (const std::invalid_argument& e) { - std::cerr << "Warning: Invalid integer attribute value '" << s << "'. Defaulting to 0." << std::endl; - return 0; - } catch (const std::out_of_range& e) { - std::cerr << "Warning: Integer attribute value '" << s << "' out of range. Defaulting to 0." << std::endl; - return 0; - } - } - - bool stringToType(const std::string& s) const { - // For boolean attributes, presence means true, value might be "true" or "" - // You decide the logic. Common HTML: attribute presence means true. - // If your parser puts "false" in the map for contenteditable="false", then check "false" - // If it puts "" for contenteditable, check for non-empty string. - // My setAttribute for bool puts "" for true, and removes for false. - // So, if the attribute is present, it's true. - return true; // If attribute exists, it's considered true - // If you want to strictly parse "true"/"false" strings: - // return (s == "" || s == "true"); // Assuming empty string means true for boolean attribute - // return (s != "false" && !s.empty()); // If "false" explicitly means false - } - - // Add more overloads for other types like double, float, long, etc. - double stringToType(const std::string& s) const { - try { - return std::stod(s); - } catch (...) { /* error handling */ return 0.0; } - } @@ -111 +46,0 @@ class Node { - ClassList classList; @@ -122,0 +58,4 @@ class Node { + // Define the getters and setters for each private property + GETTER_SETTER(std::string, TagName) + GETTER_SETTER(std::string, Id) + GETTER_SETTER(std::string, InnerText) @@ -124,9 +63 @@ class Node { - // --- Templated setAttribute --- - // This will convert the given value (of any type T) to std::string - template
- void setAttribute(const std::string& name, const T& value) { - Attributes[name] = std::to_string(value); // Default for numbers, bools need custom - } - - // Overload for std::string to avoid unnecessary conversion - void setAttribute(const std::string& name, const std::string& value) { + void setAttribute(std::string name, std::string value) { @@ -136,28 +67 @@ class Node { - // Overload for bool (custom serialization) - void setAttribute(const std::string& name, bool value) { - Attributes[name] = value ? "" : "false"; - if (value) { - Attributes[name] = ""; // Attribute present with empty value typically means 'true' - } else { - // If value doesn't exist then remove it - Attributes.erase(name); - } - } - - - // --- Templated getAttribute --- - // This will convert the string from the map to the desired type T - template
- T getAttribute(const std::string& name) const { - auto it = Attributes.find(name); - if (it != Attributes.end()) { - // Need to convert it->second (std::string) to T - // This requires a helper or std::from_chars/stoi/etc. - return stringToType
(it->second); - } - // Fail return default constructor for T - return T(); - } - - // Overload for std::string directly - std::string getAttribute(const std::string& name) const { + std::string getAttribute(std::string name) const { @@ -168 +72 @@ class Node { - return ""; // Default for string attributes if not found + return ""; // Return empty string if attribute not found @@ -233 +137 @@ std::unordered_map
parseAttributes(std::vector
parserdocument(std::string file) { - bool inClosing = false; + @@ -258,0 +163,2 @@ std::unique_ptr
parserdocument(std::string file) { + println("{}", line); + @@ -262,10 +168 @@ std::unique_ptr
parserdocument(std::string file) { - - if (current == '/' && inTag) { - currentNode = currentNode->parent; - inClosing = true; - } - - // Hijack the closing skipping to skipp over comments - if (current == '!' && inTag) { - inClosing = true; - } + @@ -277,5 +173,0 @@ std::unique_ptr
parserdocument(std::string file) { - inClosing = false; - } - - if (inClosing) { - continue; @@ -320,41 +212 @@ std::unique_ptr
parserdocument(std::string file) { - auto v = pair.second; - // !TODO: Add all global attributes - // + Src: https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Global_attributes - if(pair.first == "id") { - currentNode->setId(v); - } else if (pair.first == "contentEditable") { - if (v != "false") { - } - currentNode->setContentEditable(true); - } else if (pair.first == "href") { - currentNode->setHref(v); - } else if (pair.first == "src") { - currentNode->setSrc(v); - } else if (pair.first == "title") { - currentNode->setTitle(v); - } else if (pair.first == "value") { - currentNode->setValue(v); - } else if (pair.first == "tabindex") { - try { - int tabindex = std::stoi(v); - currentNode->setTabIndex(tabindex); - } catch (const std::invalid_argument& e) { - std::cerr << "Error converting \"" << v << "\": Invalid argument: \"tabindex\"=" << e.what() << std::endl; - } catch (const std::out_of_range& e) { - std::cerr << "Error converting \"" << v << "\": Out of range: \"tabindex\"=" << e.what() << std::endl; - } - } else if (pair.first == "disabled") { - if (v != "false") { - currentNode->setDisabled(true); - } - } else if (pair.first == "checked") { - if (v != "false") { - currentNode->setChecked(true); - } - } else if (pair.first == "required") { - if (v != "false") { - currentNode->setRequired(true); - } - } else { - currentNode->setAttribute(pair.first, v); - } + std::cout << "Key: " << pair.first << ", Value: " << pair.second << std::endl; @@ -369,5 +221,3 @@ std::unique_ptr
parserdocument(std::string file) { - inputFile.close(); - - - std::cout << root->children.size() << std::endl; - std::cout << root->children[0].get()->children[1].get()->getTagName() << std::endl; + for (auto t : tokens) { + std::cout << t << std::endl; + } @@ -374,0 +225 @@ std::unique_ptr
parserdocument(std::string file) { + inputFile.close();
#include
#include
#include
#include
#include
#include
#include
#include
#include
struct Styles { std::vector
> stylesheets; std::unordered_map
inlineStyles; std::unordered_map
> psuedoStyles; }; struct Bounds { int top; int right; int bottom; int left; }; struct State { // Bounds offset; // Border border; // std::vector
background; int width; int height; int z; bool hidden; int tabIndex; }; class ClassList { private: std::vector
values; public: std::string value() const { if (values.empty()) { return ""; } std::string collection = values[0]; for (size_t i = 1; i < values.size(); ++i) { collection += " " + values[i]; } return collection; } void add(std::string value) { values.push_back(value); } void remove(std::string value) { auto it_prev = std::find(values.begin(), values.end(), value); if (it_prev != values.end()) { *it_prev = values.back(); // Overwrite prevous position with last element values.pop_back(); // Remove the last element } } }; class Node { private: std::string TagName; // !NOTE: ContentEditable only supports plaintext std::unordered_map
Attributes; template
T stringToType(const std::string& s) const { // This should theoretically not be called if we have std::string overload // but acts as a fallback for unsupported types or as a reminder. static_assert(!std::is_same
::value, "stringToType not implemented for this type"); return T(); // Should not reach here } int stringToType(const std::string& s) const { try { return std::stoi(s); } catch (const std::invalid_argument& e) { std::cerr << "Warning: Invalid integer attribute value '" << s << "'. Defaulting to 0." << std::endl; return 0; } catch (const std::out_of_range& e) { std::cerr << "Warning: Integer attribute value '" << s << "' out of range. Defaulting to 0." << std::endl; return 0; } } bool stringToType(const std::string& s) const { // For boolean attributes, presence means true, value might be "true" or "" // You decide the logic. Common HTML: attribute presence means true. // If your parser puts "false" in the map for contenteditable="false", then check "false" // If it puts "" for contenteditable, check for non-empty string. // My setAttribute for bool puts "" for true, and removes for false. // So, if the attribute is present, it's true. return true; // If attribute exists, it's considered true // If you want to strictly parse "true"/"false" strings: // return (s == "" || s == "true"); // Assuming empty string means true for boolean attribute // return (s != "false" && !s.empty()); // If "false" explicitly means false } // Add more overloads for other types like double, float, long, etc. double stringToType(const std::string& s) const { try { return std::stod(s); } catch (...) { /* error handling */ return 0.0; } } public: Node* parent; std::vector
> children; ClassList classList; Node() : parent(nullptr) {} 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(); } // --- Templated setAttribute --- // This will convert the given value (of any type T) to std::string template
void setAttribute(const std::string& name, const T& value) { Attributes[name] = std::to_string(value); // Default for numbers, bools need custom } // Overload for std::string to avoid unnecessary conversion void setAttribute(const std::string& name, const std::string& value) { Attributes[name] = value; } // Overload for bool (custom serialization) void setAttribute(const std::string& name, bool value) { Attributes[name] = value ? "" : "false"; if (value) { Attributes[name] = ""; // Attribute present with empty value typically means 'true' } else { // If value doesn't exist then remove it Attributes.erase(name); } } // --- Templated getAttribute --- // This will convert the string from the map to the desired type T template
T getAttribute(const std::string& name) const { auto it = Attributes.find(name); if (it != Attributes.end()) { // Need to convert it->second (std::string) to T // This requires a helper or std::from_chars/stoi/etc. return stringToType
(it->second); } // Fail return default constructor for T return T(); } // Overload for std::string directly std::string getAttribute(const std::string& name) const { auto it = Attributes.find(name); if (it != Attributes.end()) { return it->second; } return ""; // Default for string attributes if not found } }; std::unordered_map
parseAttributes(std::vector
tokens) { std::unordered_map
attrs; for (unsigned short i = 0; i < tokens.size(); i++) { if (i != 0) { std::string name = ""; std::string value = ""; bool setValue = false; for (auto c : tokens[i]) { if (c == '=') { setValue = true; continue; } if (setValue) { value += c; } else { name += c; } } std::string trimmedName = ""; for (auto c : name) { if (!std::isspace(c)) { trimmedName += c; } } std::string trimmedValue = ""; int sliceStart = 0; int sliceEnd = value.length(); if (value.length() >= 2) { for (unsigned short t = 0; t < value.length(); t++) { if (!std::isspace(value[t])) { sliceStart = t; break; } } for (int t = value.length()-1; t >= 0; t--) { if (!std::isspace(value[t])) { sliceEnd = t; break; } } // Add and subtract 1 from each side to remove quotes for (unsigned short t = sliceStart+1; t < sliceEnd; t++) { trimmedValue += value[t]; } } attrs[trimmedName] = trimmedValue; } } return attrs; } // !TODO: Make a html string parser as well std::unique_ptr
parserdocument(std::string file) { std::ifstream inputFile(file); if (!inputFile.is_open()) { std::cerr << "Unable to open: " << file << std::endl; return nullptr; } std::unique_ptr
root = std::make_unique
(); root->setTagName("root"); std::string line; Node* currentNode = root.get(); // !ISSUE: test // bool inTag = false; bool escaped = false; bool inQuote = false; char quoteType = '"'; bool inClosing = false; std::vector
tokens; std::string word = ""; while (std::getline(inputFile, line)) { int ll = line.length(); for (unsigned short i = 0; i < ll; i++) { char current = line[i]; if (current == '/' && inTag) { currentNode = currentNode->parent; inClosing = true; } // Hijack the closing skipping to skipp over comments if (current == '!' && inTag) { inClosing = true; } if (current == '>') { tokens.push_back(word); word = ""; inTag = false; inClosing = false; } if (inClosing) { continue; } if (inTag) { if ((current == '"' || current == '\'') && !escaped) { if (inQuote && current == quoteType) { inQuote = false; } else if (!inQuote) { inQuote = true; quoteType = current; } } if (current == ' ' && !inQuote) { tokens.push_back(word); word = ""; } else { word += current; } } else if (current != '<' || current != '>') { // Outside tag add to innerText currentNode->setInnerText(currentNode->getInnerText() + current); } if (current == '<') { inTag = true; } escaped = false; if (current == '\\') { //' escaped = true; } if (!inTag && tokens.size() > 0) { // Build the element currentNode = currentNode->createElement(tokens[0]); auto attrs = parseAttributes(tokens); for (auto const& pair : attrs) { auto v = pair.second; // !TODO: Add all global attributes // + Src: https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Global_attributes if(pair.first == "id") { currentNode->setId(v); } else if (pair.first == "contentEditable") { if (v != "false") { } currentNode->setContentEditable(true); } else if (pair.first == "href") { currentNode->setHref(v); } else if (pair.first == "src") { currentNode->setSrc(v); } else if (pair.first == "title") { currentNode->setTitle(v); } else if (pair.first == "value") { currentNode->setValue(v); } else if (pair.first == "tabindex") { try { int tabindex = std::stoi(v); currentNode->setTabIndex(tabindex); } catch (const std::invalid_argument& e) { std::cerr << "Error converting \"" << v << "\": Invalid argument: \"tabindex\"=" << e.what() << std::endl; } catch (const std::out_of_range& e) { std::cerr << "Error converting \"" << v << "\": Out of range: \"tabindex\"=" << e.what() << std::endl; } } else if (pair.first == "disabled") { if (v != "false") { currentNode->setDisabled(true); } } else if (pair.first == "checked") { if (v != "false") { currentNode->setChecked(true); } } else if (pair.first == "required") { if (v != "false") { currentNode->setRequired(true); } } else { currentNode->setAttribute(pair.first, v); } } tokens.clear(); word = ""; } } } inputFile.close(); std::cout << root->children.size() << std::endl; std::cout << root->children[0].get()->children[1].get()->getTagName() << std::endl; return root; } int main() { parserdocument("./index.html"); return 0; }