home readme diff tree note docs

0commit 36bbb7f78ec07095031aafe80cdfb9aaef7bc023
1Author: Mason Wright <mason@lakefox.net>
2Date:   Mon Oct 6 21:30:05 2025 -0600
3
4    Generic Unit Parsing Function
5
6diff --git a/include/grim.h b/include/grim.h
7index 9c8162c..2322226 100755
8--- a/include/grim.h
9+++ b/include/grim.h
10@@ -157,14 +157,16 @@ enum class UnitType {
11 		VW, VMAX, VMIN, VB, VI, CQW, CQH, CQI, CQB, CQMIN, CQMAX, MM, Q, IN, PC, PT,
12 	// Angle
13 		DEG, RAD, GRAD, TURN,
14+	// Non-unit numbers
15+		NUMBER, E, PI, INF, NEGINF, NaN,
16 	// Baseline Position
17 		FIRST, LAST,
18 	// Box Edge
19 		BOX_EDGE, CONTENT, PADDING, BORDER, MARGIN, FILL, STROKE, VIEW,
20 	// Function
21-		CALC,
22+		CALC, MIN,
23 	// Keyword
24-		PRINT, SCREEN, TOKEN, FUNC,
25+		PRINT, SCREEN, TOKEN, FUNC, OPERATOR, GROUP,
26 	// Property
27 		BACKGROUND_COLOR, BASELINE
28 };
29diff --git a/include/parser.h b/include/parser.h
30index e2970ba..8b71a76 100755
31--- a/include/parser.h
32+++ b/include/parser.h
33@@ -16,6 +16,6 @@ std::unordered_map<std::string, std::string> parseAttributes(std::string_view& t
34 std::unique_ptr<Node> parseHTML(std::istream& inputStream);
35 std::unique_ptr<StyleHandler> parseCSS(std::istream& inputStream);
36 std::unordered_map<std::string, std::string> parseCSSInline(std::string inlineStyles);
37-Unit getUnit(std::string, std::string);
38+std::vector<Unit> getUnit(std::string);
39 
40 #endif
41diff --git a/src/parser.cc b/src/parser.cc
42index 9f2001d..3d514b6 100755
43--- a/src/parser.cc
44+++ b/src/parser.cc
45@@ -6,6 +6,7 @@
46 #include <cctype>
47 #include <algorithm>
48 #include <cmath>
49+#include <string_view>
50 
51 std::unordered_map<std::string, std::string> parseAttributes(std::string_view token) {
52 	std::unordered_map<std::string, std::string> attrs;
53@@ -82,7 +83,7 @@ std::unordered_map<std::string, std::string> parseAttributes(std::string_view to
54 				}
55 
56 				for (int t = value.length()-1; t >= 0; t--) {
57-					// Trim the trailing / on self closing elements if there isn't a space inbetween
58+					// Trim the trailing / on self closing elements if there isn't a space in between
59 					if (!std::isspace(value[t]) && value[t] != '/') {
60 						sliceEnd = t;
61 						break;
62@@ -275,10 +276,10 @@ std::unique_ptr<Node> parseHTML(std::istream& inputStream) {
63 				continue;
64 			}
65 		} else if (!escaped && current == '<') {
66-			// if the next charector is a !
67+			// if the next character is a !
68 			if (inputStream.peek() == '!') {
69 				char a,b,c;
70-				// load the next three charecters (includes !)
71+				// load the next three characters (includes !)
72 				if (inputStream.get(a) && inputStream.get(b) && inputStream.get(c)) {
73 					// We know a == !
74 					if (b == '-' && c == '-') {
75@@ -297,7 +298,7 @@ std::unique_ptr<Node> parseHTML(std::istream& inputStream) {
76 			}
77 			inTag = true;
78 			
79-			// Heres where you actually make the text node and above the real nodes
80+			// Here's where you actually make the text node and above the real nodes
81 			// can also prob make the vector of tokens a single variable
82 			// it really just needs to be a data string bc we know the type based on inTag
83 
84@@ -311,7 +312,7 @@ std::unique_ptr<Node> parseHTML(std::istream& inputStream) {
85 			}
86 
87 			// TODO: Add a vector that stores inner text and when elements start to close out pop them but add all to their innerText
88-			// | Will need something simular for innerHTML
89+			// | Will need something similar for innerHTML
90 			if (hasText) {
91 				auto node = currentNode->createElement("text");
92 				node->setAttribute("innerText", token);
93@@ -340,7 +341,7 @@ void typeCheck(std::string key, std::string value, Unit* property, std::vector<U
94 	for (size_t i = 0; i < property->children.size(); i++) {
95 		bool allowed = false;
96 		for (size_t j = 0; j < allowedTypes.size(); j++) {
97-			if (property->children[i]->type == allowedTypes[j]) {
98+			if (property->children[i].type == allowedTypes[j]) {
99 				allowed = true;
100 				break;
101 			}
102@@ -348,259 +349,206 @@ void typeCheck(std::string key, std::string value, Unit* property, std::vector<U
103 
104 		if (!allowed) {
105 			std::cerr << "Invalid value of property: " << key << " Value: " << value << std::endl;
106-			exit();
107+			std::exit(10);
108 		}
109 	}	
110 }
111 
112-/*
113-	| Parses CSS color strings:
114-	|	rgba(255 125 245 / 10)
115-	|	rgba(255,125,245, 0.10)
116-	|	rgb(255,125,245)
117-	| 	etc..
118-	| value: color string
119-	| max: the value you want to wrap around
120-	|	if max was 255 and you had -10 then it would now
121-	| 	be 245. It also clamps the value to the max
122-*/
123-std::vector<double> parseColor(std::string value) {
124-	std::vector<double> colors;
125-
126-	bool set = false;
127-	int collector = 0;
128-
129-	bool decimal = false;
130-	int decimalLevel = 1;
131-	bool negitive = false;
132-
133-	for (size_t i = 0; i < value.length(); i++) {
134-		int v = value[i]-'0';
135-		if (v >= 0 && v <= 9) {
136-			collector = (collector*10) + v;
137-			set = true;
138-			if (decimal) {
139-				decimalLevel *= 10;
140-			}
141-		} else if (value[i] != '.' && set) {
142-			if (negitive) {
143-				collector *= -1;
144-			}
145-
146-			negitive = false;
147-			colors.push_back(collector/decimalLevel);
148-
149-			set = false;
150-			decimalLevel = 1;
151-			decimal = false;
152-			collector = 0;
153-		} else if (value[i] == '.') {
154-			decimal = true;
155-		} else if (value[i] == '-') {
156-			negitive = true;
157-		}
158-	}
159-
160-	if (colors.size() < 4) {
161-		colors.push_back(0);
162-	}
163-	return colors;
164-}
165-
166-double hueToRGB(double p, double q, double t) {
167-    if (t < 0) t += 1;
168-    if (t > 1) t -= 1;
169-    if (t < 1.0 / 6.0) return p + (q - p) * 6.0 * t;
170-    if (t < 1.0 / 2.0) return q;
171-    if (t < 2.0 / 3.0) return p + (q - p) * (2.0 / 3.0 - t) * 6.0;
172-    return p;
173-}
174-
175-std::vector<Unit> getUnit(std::string key, std::string value) {
176-	// NOTES:
177-	// | All colors are stored as rgba in hex format
178-
179+struct SuffixParse {
180+	std::string_view suffix;
181+	UnitType type;
182+	int trim;   // chars to strip from the right
183+};
184+
185+static constexpr SuffixParse table[] = {
186+	{"cqmin", UnitType::CQMIN, 5},
187+	{"cqmax", UnitType::CQMAX, 5},
188+	{"rcap",  UnitType::RCAP,  4},
189+	{"vmax",  UnitType::VMAX,  4},
190+	{"vmin",  UnitType::VMIN,  4},
191+	{"grad",  UnitType::GRAD,  4},
192+	{"turn",  UnitType::TURN,  4},
193+	{"cap",   UnitType::CAP,   3},
194+	{"rch",   UnitType::RCH,   3},
195+	{"rem",   UnitType::REM,   3},
196+	{"rex",   UnitType::REX,   3},
197+	{"ric",   UnitType::RIC,   3},
198+	{"rlh",   UnitType::RLH,   3},
199+	{"cqw",   UnitType::CQW,   3},
200+	{"cqh",   UnitType::CQH,   3},
201+	{"cqi",   UnitType::CQI,   3},
202+	{"cqb",   UnitType::CQB,   3},
203+	{"deg",   UnitType::DEG,   3},
204+	{"rad",   UnitType::RAD,   3},
205+	{"px",    UnitType::PX,    2},
206+	{"em",    UnitType::EM,    2},
207+	{"cm",    UnitType::CM,    2},
208+	{"ch",    UnitType::CH,    2},
209+	{"ex",    UnitType::EX,    2},
210+	{"ic",    UnitType::IC,    2},
211+	{"lh",    UnitType::LH,    2},
212+	{"vh",    UnitType::VH,    2},
213+	{"vw",    UnitType::VW,    2},
214+	{"vb",    UnitType::VB,    2},
215+	{"vi",    UnitType::VI,    2},
216+	{"mm",    UnitType::MM,    2},
217+	{"in",    UnitType::IN,    2},
218+	{"pc",    UnitType::PC,    2},
219+	{"pt",    UnitType::PT,    2},
220+	{"%",     UnitType::PERCENT,1},
221+	{"q",     UnitType::Q,     1},
222+};
223+
224+static const std::unordered_map<std::string_view, UnitType> kOp{
225+	{"+", UnitType::ADD}, {"-", UnitType::SUB}, {"*", UnitType::MULT},
226+	{"/", UnitType::DIV}, {"==",UnitType::EQ}, {">", UnitType::GT},
227+	{"<", UnitType::LT}, {">=",UnitType::GE}, {"<=",UnitType::LE},
228+	{"e", UnitType::E}, {"pi", UnitType::PI}, {"infinity", UnitType::INF},
229+	{"-infinity", UnitType::NEGINF}, {"NaN", UnitType::NaN}
230+};
231+
232+static const std::unordered_map<std::string_view, float> kAbsSize{
233+	{"xx-small",0.6f}, {"x-small",0.75f}, {"small",0.89f}, {"medium",1.0f},
234+	{"large",1.2f}, {"x-large",1.5f}, {"xx-large",2.0f}, {"xxx-large",3.0f}
235+};
236+
237+std::vector<Unit> getUnit(std::string value) {
238 	std::vector<Unit> units;
239 	
240-	std::vector<std::string> parts;
241 	std::string buffer = "";
242-	bool inParentheses = false;
243+	int parenDepth = 0;
244 	bool inQuotes = false;
245-	char quoteType = '';
246+	// Needs to not be empty but can't be a quote
247+	char quoteType = 'a';
248 	bool escaped = false;
249 
250 	for (size_t i = 0; i < value.length(); i++) {
251 		char v = value[i];
252 		if (v == '\\' && !escaped) escaped = true;
253-
254-		if (v == '(' && !inParentheses && !inQuotes) inParentheses = true;
255-		else if (v == ')' && inParentheses && !inQuotes) inParentheses = false;
256-		else if (v == '"' || v == '\'' && !inParentheses) {
257-			if (inQuote && v == quoteType) {
258+		if ((v == '"' || v == '\'') && parenDepth == 0) {
259+			if (inQuotes && v == quoteType) {
260 				inQuotes = false;
261-			} else if (!inQuote) {
262+			} else if (!inQuotes) {
263 				quoteType = v;
264 				inQuotes = true;
265 			}
266 
267-		} else if (!inQuotes && !inParentheses && v == ' ') {
268-			// Split by spaces into parts
269-			parts.push_back(buffer);
270-			buffer.clear();
271-		} else if (!inQuotes && !inParentheses && v == ',' || i >= value.length()-1) {
272-			// EOL or a ','
273-			
274-			// add the last one
275-			parts.push_back(buffer);
276-			buffer.clear();
277+		}
278 
279-			// Main logic
280-			// Loop through each part and convert it into its type
281-			// the code around this point is breaking the css string 
282-			// that may look like this:
283-			// 1px 3% 67vw 2px, rgba(123,456,789,0%)
284-			// into:
285-			// parts = [["1px", "3%", "67vw", "2px"],["rgba(123,456,789,0%)"]]
286-			//
287-			// the first for loop (j) will go through its parts and once its
288-			// done it will figure out what type of collection it is
289-			// in the example above it is a set of units for either margin or padding
290-			// and the second one is a color value
291-			// once it knows the type of collection it will add it to the units vector
292-
293-			for (size_t j = 0; j < parts.size(); j++) {
294-				Unit property;
295-				for (size_t k = 0; k < parts[j].size(); k++) {
296-					std::string part = parts[j][k];
297-					UnitType ut;
298-					UnitValue uv;
299-
300-					if (value.starts_with("#")) {
301-						ut = UnitType::HEX;
302-						uv.UINT = std::stoul(value.substr(1), nullptr, 16);
303-					} else if (value.starts_with("rgb")) {
304-						ut = UnitType::RGBA;
305-						std::vector<double> colors = parseColor(value);
306-						uv.UINT = (static_cast<int>(colors[0]) << 24) | (static_cast<int>(colors[1]) << 16) | (static_cast<int>(colors[2]) << 8) | static_cast<int>(255*colors[3]/100);
307-					} else if (value.starts_with("hsl")) {
308-						ut = UnitType::HSL;
309-						std::vector<double> colors = parseColor(value);
310-						// Convert the parsed values to rgba
311-						int r,g,b;
312-						double h = colors[0]/360.0;
313-						double s = colors[1]/100.0;
314-						double l = colors[2]/100.0;
315-						if (colors[1] == 0) {
316-							// Gray scale
317-							int lInt = l*255;
318-							r = lInt;
319-							g = lInt;
320-							b = lInt;
321-						} else {
322-							double q = (l < 0.5) ? l * (1.0 + s) : l + s - (l * s);
323-							double p = 2.0 * l - q;
324+		if (!inQuotes) {
325+			if (v == '(') parenDepth++;
326+			else if (v == ')') parenDepth--;
327+		}
328 
329-							r = static_cast<int>(hueToRGB(p, q, h + 1.0 / 3.0) * 255.0);
330-							g = static_cast<int>(hueToRGB(p, q, h) * 255.0);
331-							b = static_cast<int>(hueToRGB(p, q, h - 1.0 / 3.0) * 255.0);
332-						}
333+		if (parenDepth > 0) {
334+			buffer.push_back(v);
335+			continue;
336+		} else if (v == ')') {
337+			// We are at the end of the parentheseses and we need to pass it for more parsing
338+			std::string name;
339+			bool nameset = false;
340+			std::string value;
341 
342-						// convert hsl to rgb/hex
343-						uv.UINT = (r << 24) | (g << 16) | (b << 8) | static_cast<int>(255*colors[3]/100);
344-					} else if (value.starts_with("hwb")) {
345-						ut = UnitType::HWB;
346-						std::vector<double> colors = parseColor(value);
347-						// if gray scale or close
348-						int r,g,b;
349-						colors[1] /= 100;
350-						colors[2] /= 100;
351-						if (colors[1] + colors[2] > 1.0 - 1e-6) {
352-							r = static_cast<int>(colors[1] * 255.0);
353-							g = r;
354-							b = r;
355-						} else {
356-							double mod = std::fmod(colors[0], 360.0);
357-							if (mod < 0) mod += 360.0;
358-
359-							double i = std::floor(mod / 60.0);
360-							double f = (mod / 60.0) - i;
361-
362-							double rp = 0.0;
363-							double gp = 0.0;
364-							double bp = 0.0;
365-
366-							switch (static_cast<int>(i)) {
367-								case 0: rp = 1.0;     gp = f;       bp = 0.0;      break;
368-								case 1: rp = 1.0 - f; gp = 1.0;     bp = 0.0;      break;
369-								case 2: rp = 0.0;     gp = 1.0;     bp = f;        break;
370-								case 3: rp = 0.0;     gp = 1.0 - f; bp = 1.0;      break;
371-								case 4: rp = f;       gp = 0.0;     bp = 1.0;      break;
372-								case 5: rp = 1.0;     gp = 0.0;     bp = 1.0 - f;  break;
373-								default: // Should not happen for valid H
374-									rp = gp = bp = 0.0; break; 
375-							}
376+			for (size_t j = 0; j < buffer.size(); j++) {
377+				if (nameset) {
378+					value.push_back(buffer[j]);
379+				} else {
380+					if (buffer[j] == '(') nameset = true;
381+					else name.push_back(buffer[j]);
382+				}
383+			}
384 
385-							double v = 1.0 - colors[1] - colors[2];
386+			std::vector<Unit> children = getUnit(value);
387+			Unit unit;
388+			unit.type = UnitType::FUNC;
389 
390-							r = static_cast<int>(((rp * v) + colors[1])*255.0);
391-							g = static_cast<int>(((gp * v) + colors[1])*255.0);
392-							b = static_cast<int>(((bp * v) + colors[1])*255.0);
393-						}
394+			if (name == "calc") {
395+				unit.value.ENUM = UnitType::CALC;
396+			} else if (name == "min") {
397+				unit.value.ENUM = UnitType::MIN;
398+			} else if (name == "") {
399+				unit.value.ENUM = UnitType::GROUP;
400+			}
401 
402-						uv.UINT = (r << 24) | (g << 16) | (b << 8) | static_cast<int>(255*colors[3]/100);
403-					} else if (value == "xx-small" && value == "x-small" && value == "small" && value == "medium" && 
404-							value == "large" && value == "x-large" && value == "xx-large" && value == "xxx-large" ) {
405-						// <absolute-size> read as a EM
406-						ut = UnitType::EM;
407-
408-						if (value == "xx-small") uv.FLOAT = 0.6;
409-						else if (value == "x-small") uv.FLOAT = 0.75;
410-						else if (value == "small") uv.FLOAT = 0.89;
411-						else if (value == "medium") uv.FLOAT = 1;
412-						else if (value == "large") uv.FLOAT = 1.2;
413-						else if (value == "x-large") uv.FLOAT = 1.5;
414-						else if (value == "xx-large") uv.FLOAT = 2;
415-						else if (value == "xxx-large") uv.FLOAT = 3;
416-					} else if (value.ends_with("baseline")) {
417-						ut = UnitType::BASELINE;
418-						if (value.starts_with("first")) uv.ENUM = UnitType::FIRST;
419-						else if (value.starts_with("last")) uv.ENUM = UnitType::LAST;
420-						else uv.ENUM = UnitType::FIRST;
421-					} else if (value.ends_with("-box")) {
422-						ut = UnitType::BOX_EDGE;
423-
424-						if (value.starts_with("content")) uv.ENUM = UnitType::CONTENT;
425-						else if (value.starts_with("padding")) uv.ENUM = UnitType::PADDING;
426-						else if (value.starts_with("border")) uv.ENUM = UnitType::BORDER;
427-						else if (value.starts_with("margin")) uv.ENUM = UnitType::MARGIN;
428-						else if (value.starts_with("fill")) uv.ENUM = UnitType::FILL;
429-						else if (value.starts_with("stroke")) uv.ENUM = UnitType::STROKE;
430-						else if (value.starts_with("view")) uv.ENUM = UnitType::VIEW;
431-					}
432+			if (children.size() > 0) {
433+				unit.children = children;
434+				children.clear();
435+			}
436+
437+			units.push_back(unit);
438+			buffer.clear();
439+		} else if ((!inQuotes && (v == ',' ||  std::isspace(v))) || i >= value.length()-1) {
440+			if (v != ',' && !std::isspace(v)) buffer.push_back(v);
441 
442-					// What about cases like rotate(calc(10deg)) or even rgb should parse sub values
443+			Unit unit;
444 
445-					Unit unit;
446-					unit.type = ut;
447-					unit.value = uv;
448+			// Split by spaces into parts
449+
450+			bool found = false;
451 
452-					property.children.push_back(unit);
453+			for (const auto& [suffix, type, trim] : table) {
454+				if (buffer.ends_with(suffix)) {
455+					unit.value.FLOAT = std::stof(std::string(buffer.substr(0, buffer.size() - trim)));
456+					unit.type = type;
457+					found = true;
458+					break;
459 				}
460+			}
461 
462-				// ISSUE: need to find the property type before adding it
463-				// probally cut back on the super intensive parsing like the colors
464-				// and move to a lightweight recursive (lol) approach
465-				// the typeChecking of key values could also be moved to a different function that calls this one
466+			if (!found) {
467+				if (buffer.starts_with("#")) {
468+					unit.type = UnitType::HEX;
469+					unit.value.UINT = std::stoul(buffer.substr(1), nullptr, 16);
470+					found = true;
471+				} else if (auto it = kOp.find(buffer); it != kOp.end()) { 
472+					unit.type = UnitType::OPERATOR;
473+					unit.value.ENUM = it->second;
474+					found = true;
475+				} else if (auto it = kAbsSize.find(buffer); it != kAbsSize.end()) { 
476+					unit.type = UnitType::EM;
477+					unit.value.FLOAT = it->second;
478+					found = true;
479+				} else if (buffer.ends_with("baseline")) {
480+					unit.type = UnitType::BASELINE;
481+					if (buffer.starts_with("first")) unit.value.ENUM = UnitType::FIRST;
482+					else if (buffer.starts_with("last")) unit.value.ENUM = UnitType::LAST;
483+					else unit.value.ENUM = UnitType::FIRST;
484+					found = true;
485+				} else if (buffer.ends_with("-box")) {
486+					unit.type = UnitType::BOX_EDGE;
487+
488+					if (buffer.starts_with("content")) unit.value.ENUM = UnitType::CONTENT;
489+					else if (buffer.starts_with("padding")) unit.value.ENUM = UnitType::PADDING;
490+					else if (buffer.starts_with("border")) unit.value.ENUM = UnitType::BORDER;
491+					else if (buffer.starts_with("margin")) unit.value.ENUM = UnitType::MARGIN;
492+					else if (buffer.starts_with("fill")) unit.value.ENUM = UnitType::FILL;
493+					else if (buffer.starts_with("stroke")) unit.value.ENUM = UnitType::STROKE;
494+					else if (buffer.starts_with("view")) unit.value.ENUM = UnitType::VIEW;
495+					found = true;
496+				} else {
497+					// Maybe its a number?
498+					try {
499+						unit.value.FLOAT = std::stof(buffer);
500+						unit.type = UnitType::NUMBER;
501+						found = true;
502+					} catch (...) {
503+						found = false;
504+					}
505+				}
506+			}
507 
508-				if (key == "background-color") {
509-					typeCheck(key, value, property, {UnitType::RGB, UnitType::RGBA, UnitType::HSL, UnitType::HSLA, UnitType::HWB, UnitType::HEX})
510-					property.type == UnitType::BACKGROUND_COLOR;
511-				} 
512+			if (found) {
513+				// Only process if its found
514+				units.push_back(unit);
515 
516-				units.push_back(property);
517+				buffer.clear();
518+			} else if (v == ',') {
519+				// if we hit a comma before we match we can dump the
520+				// buffer and try again
521+				std::cerr << "Unable to parse buffer: " << buffer << std::endl;
522+				buffer.clear();
523 			}
524 
525-			parts.clear();
526 		} else {
527 			buffer.push_back(v);
528 		}
529@@ -629,7 +577,7 @@ void StyleHandler::parseStream(std::istream& inputStream) {
530 	while (inputStream.get(ch)) {
531 		if (ch == '*' && inputStream.peek() == '/') {
532 			incomment = false;
533-			// Skip ahead to the next letter so we don't caputure the trailing /
534+			// Skip ahead to the next letter so we don't capture the trailing /
535 			inputStream.get(ch);
536 			continue;
537 		}
538@@ -654,7 +602,7 @@ void StyleHandler::parseStream(std::istream& inputStream) {
539 			std::vector<std::vector<std::string>> parts = parseSelectorParts(buffer);
540 
541 			if (current->selector.size() > 0) {
542-				// To be able to handle a subsutution with a parent that has h1, h2
543+				// To be able to handle a substitution with a parent that has h1, h2
544 				// we need to rebuild the selector so we can add new copies
545 				std::vector<std::vector<std::string>> subd;
546 
547@@ -689,7 +637,7 @@ void StyleHandler::parseStream(std::istream& inputStream) {
548 							subd.push_back(temp);
549 						}
550 					} else {
551-						// If theres nothing to replace just add to back
552+						// If there's nothing to replace just add to back
553 						subd.push_back(parts[i]);
554 					}
555 				}
556@@ -809,7 +757,7 @@ void StyleHandler::parseStream(std::istream& inputStream) {
557 					}
558 
559 				} else if (parts[0][0] == "@layer") {
560-					// ISSUE: need to add speficity
561+					// ISSUE: need to add specificity
562 
563 				}
564 
565@@ -831,7 +779,7 @@ void StyleHandler::parseStream(std::istream& inputStream) {
566 						firstValue = true;
567 						value += c;
568 					} else if (keyFound && firstValue && !(std::isspace(c) && i < buffer.length()-1 && std::isspace(buffer[i+1]))) {
569-						// if the key is found and the charactor isn't a ':'
570+						// if the key is found and the character isn't a ':'
571 						// 	also (if c is a space and the next c will be a space)
572 						// 	this is to auto trim duplicate spaces from the value
573 						value += c;
574@@ -875,7 +823,7 @@ std::unordered_map<std::string, std::string> parseCSSInline(std::string inlineSt
575 						!(std::isspace(c) && i < buffer.length()-1 && std::isspace(buffer[i+1])) &&
576 						c != ';'
577 					  ) {
578-					// if the key is found and the charactor isn't a ':'
579+					// if the key is found and the character isn't a ':'
580 					// 	also (if c is a space and the next c will be a space)
581 					// 	this is to auto trim duplicate spaces from the value
582 					value += c;
583diff --git a/src/parser.cc.bak b/src/parser.cc.bak
584new file mode 100755
585index 0000000..2578022
586--- /dev/null
587+++ b/src/parser.cc.bak
588@@ -0,0 +1,804 @@
589+#include "grim.h" 
590+#include <iostream>
591+#include <fstream>
592+#include <sstream>
593+#include <unordered_map>
594+#include <cctype>
595+#include <algorithm>
596+#include <cmath>
597+#include <string_view>
598+
599+std::unordered_map<std::string, std::string> parseAttributes(std::string_view token) {
600+	std::unordered_map<std::string, std::string> attrs;
601+	
602+	bool inQuote = false;
603+	bool escaped = false;
604+	char quoteType = '"';
605+
606+	std::string word;
607+	for (unsigned short i = 0; i < token.length(); i++) {
608+		char current = token[i];
609+		if ((current == '"' || current == '\'') && !escaped) {
610+			if (inQuote && current == quoteType) {
611+				inQuote = false;
612+			} else if (!inQuote) {
613+				inQuote = true;
614+				quoteType = current;
615+			}
616+		}
617+
618+		if (current == '\\') { //'
619+			escaped = true;
620+			continue;
621+		}
622+
623+		if ((!std::isspace(current) || inQuote) && i != token.length()-1) {
624+			word += current;
625+		} else {
626+			if (i == token.length()-1) {
627+				word += current;
628+			}
629+
630+			std::string name = "";
631+			std::string value = "";
632+			bool setValue = false;
633+			
634+			for (auto c : word) {
635+				if (c == '=') {
636+					setValue = true;
637+					continue;
638+				}
639+
640+				if (setValue) {
641+					value += c;
642+				} else {
643+					name += c;
644+				}
645+			}
646+
647+			word = "";
648+
649+			std::string trimmedName = "";
650+			for (auto c : name) {
651+				if (!std::isspace(c)) {
652+					trimmedName += c;
653+				}
654+			}
655+
656+			if (attrs.size() == 0) {
657+				value = '"'+trimmedName+'"';
658+				trimmedName = "tagName";
659+			}
660+
661+			std::string trimmedValue = "";
662+			int sliceStart = 0;
663+			int sliceEnd = value.length();
664+
665+			if (value.length() >= 2) {
666+				for (unsigned short t = 0; t < value.length(); t++) {
667+					if (!std::isspace(value[t])) {
668+						sliceStart = t;
669+						break;
670+					}
671+				}
672+
673+				for (int t = value.length()-1; t >= 0; t--) {
674+					// Trim the trailing / on self closing elements if there isn't a space inbetween
675+					if (!std::isspace(value[t]) && value[t] != '/') {
676+						sliceEnd = t;
677+						break;
678+					}
679+				}
680+
681+				// Add and subtract 1 from each side to remove quotes
682+				for (unsigned short t = sliceStart+1; t < sliceEnd; t++) {
683+					trimmedValue += value[t];
684+				}
685+			}
686+
687+			attrs[trimmedName] = trimmedValue;
688+		}
689+	}
690+
691+	return attrs;
692+}
693+
694+std::unique_ptr<Node> parseHTML(std::istream& inputStream) {
695+	std::unique_ptr<Node> root = std::make_unique<Node>();
696+	root->setTagName("root");
697+
698+	Node* currentNode = root.get();
699+
700+	bool inTag = false;
701+	bool escaped = false;
702+	bool inQuote = false;
703+	bool inComment = false;
704+	bool waitToClose = false;
705+	char quoteType = '"';
706+
707+	std::string token;
708+
709+	char current;
710+	while (inputStream.get(current)) {
711+		// Finds the --> and removes it then resets inComment
712+		if (inComment) {
713+			// added the peek to prevent hitting on every -
714+			if (current == '-' && inputStream.peek() == '-') {
715+				char a,b;
716+				// load the next two
717+				if (inputStream.get(a) && inputStream.get(b)) {
718+					// We know a == -
719+					if (b == '>') {
720+						// Close the comment
721+						inComment = false;
722+					}
723+				}
724+
725+				if (!inComment) {
726+					// we don't put anything back and that puts us where we need to be
727+					continue;
728+				} else {
729+					// Not the end 
730+					inputStream.putback(b);
731+					inputStream.putback(a);
732+				}
733+			}
734+			continue;
735+		}
736+
737+		std::string tagName = currentNode->getTagName();
738+		if (!waitToClose && (
739+					tagName == "style" || 
740+					tagName == "script" ||
741+					tagName == "textarea" 
742+					) && current == '<' && inputStream.peek() == '/' ) {
743+			// If its the starting of a closing tag and we are inside of a special tag
744+			// we need to see if the upcoming closing tag is the correct closing tag
745+			// if it is then we put the read ahead back and waitToClose. This will let normal parsing
746+			// continue
747+			const size_t readTo = tagName.length()+2;
748+			std::string buffer(readTo, '\0');
749+
750+			inputStream.read(&buffer[0], readTo);
751+
752+			if (buffer == "/"+tagName+">") {
753+				waitToClose = true;
754+			} else {
755+				token += current;
756+			}
757+
758+			for (int i = readTo-1; i>= 0; i--) {
759+				inputStream.putback(buffer[i]);
760+			}
761+			if (!waitToClose) {
762+				continue;
763+			}
764+		} else if (!waitToClose && (
765+					tagName == "style" || 
766+					tagName == "script" ||
767+					tagName == "textarea" 
768+					)) {
769+			token += current;
770+			continue;
771+		}
772+
773+		if (inTag) {
774+			 if ((current == '"' || current == '\'') && !escaped) {
775+				if (inQuote && current == quoteType) {
776+					inQuote = false;
777+				} else if (!inQuote) {
778+					inQuote = true;
779+					quoteType = current;
780+				}
781+			}
782+
783+			if (current == '>' && !inQuote && !escaped) {
784+				inTag = false;
785+				waitToClose = false;
786+				// Even if the next tag is next still add a text tag as we can just check if its empty and remove it
787+				// this check would still have to be done either way so we aren't wasting anything
788+				if (token.length() > 0) {
789+					bool empty = true;
790+					bool closingTag = false;
791+					for (size_t i = 0; i < token.length(); i++) {
792+						auto c = token[i];
793+						if (std::isspace(c)) {
794+							continue;
795+						} else if (c == '/') {
796+							closingTag = true;
797+						} else {
798+							empty = false;
799+							break;
800+						}
801+					}
802+
803+					bool selfClosing = false;
804+					int selfClosingPosition = token.length()-1;
805+					if (!closingTag) {
806+						for (int i = token.length()-1; i >= 0; i--) {
807+							auto c = token[i];
808+							if (std::isspace(c)) {
809+								continue;
810+							} else if (c == '/') {
811+								selfClosingPosition = i;
812+								selfClosing = true;
813+								break;
814+							} else {
815+								break;
816+							}
817+						}
818+					}
819+
820+					if (!empty) {
821+						if (selfClosing) {
822+							//Create element and don't jump inside
823+							// Use node instead of currentNode because we do not need to jump into the created element
824+							auto attrs = parseAttributes(token.substr(0, selfClosingPosition));
825+							auto node = currentNode->createElement(attrs["tagName"]);
826+							for (auto const& pair : attrs) {
827+								// All attributes are stored as strings so we can just throw them in
828+								node->setAttribute(pair.first, pair.second);
829+							}
830+						} else if (closingTag) {
831+							// Checksum and tree move
832+							std::string tagName = "";
833+							for (auto t : token) {
834+								if (!std::isspace(t) && t != '/') {
835+									tagName += t;
836+								} else if (t == '/') {
837+									// For closing tags we just want the name
838+									continue;
839+								} else if (tagName.length() > 0) {
840+									break;
841+								}
842+							}
843+							if (currentNode->getTagName() == tagName) {
844+								currentNode = currentNode->parent;
845+							} else {
846+								std::cerr << "malformed html: closing tag (</" << tagName << ">) found for <" << currentNode->getTagName() << ">" << std::endl;
847+							}
848+						} else {
849+							// Create a element and jump inside
850+							auto attrs = parseAttributes(token);
851+							// Don't add elements like <!DOCTYPE>
852+							if (attrs["tagName"][0] != '!') {
853+								currentNode = currentNode->createElement(attrs["tagName"]);
854+								for (auto const& pair : attrs) {
855+									// All attributes are stored as strings so we can just throw them in
856+									currentNode->setAttribute(pair.first, pair.second);
857+								}
858+							}
859+						}
860+					}
861+				}
862+
863+				token = "";	
864+				continue;
865+			}
866+		} else if (!escaped && current == '<') {
867+			// if the next charector is a !
868+			if (inputStream.peek() == '!') {
869+				char a,b,c;
870+				// load the next three charecters (includes !)
871+				if (inputStream.get(a) && inputStream.get(b) && inputStream.get(c)) {
872+					// We know a == !
873+					if (b == '-' && c == '-') {
874+						inComment = true;
875+					}
876+				}
877+
878+				if (inComment) {
879+					continue;
880+				} else {
881+					// Not a comment add all back
882+					inputStream.putback(c);
883+					inputStream.putback(b);
884+					inputStream.putback(a);
885+				}
886+			}
887+			inTag = true;
888+			
889+			// Heres where you actually make the text node and above the real nodes
890+			// can also prob make the vector of tokens a single variable
891+			// it really just needs to be a data string bc we know the type based on inTag
892+
893+
894+			bool hasText = false;
895+			for (auto t : token) {
896+				if (!std::isspace(t)) {
897+					hasText = true;
898+					break;
899+				}
900+			}
901+
902+			// TODO: Add a vector that stores inner text and when elements start to close out pop them but add all to their innerText
903+			// | Will need something simular for innerHTML
904+			if (hasText) {
905+				auto node = currentNode->createElement("text");
906+				node->setAttribute("innerText", token);
907+			}
908+
909+
910+			token = "";
911+			continue;
912+		}
913+		
914+		escaped = false;
915+
916+		if (current == '\\') { //'
917+			escaped = true;
918+			continue;
919+		}
920+
921+		token += current;
922+	}
923+
924+	return root;
925+}
926+
927+
928+void typeCheck(std::string key, std::string value, Unit* property, std::vector<UnitType> allowedTypes) {
929+	for (size_t i = 0; i < property->children.size(); i++) {
930+		bool allowed = false;
931+		for (size_t j = 0; j < allowedTypes.size(); j++) {
932+			if (property->children[i].type == allowedTypes[j]) {
933+				allowed = true;
934+				break;
935+			}
936+		}
937+
938+		if (!allowed) {
939+			std::cerr << "Invalid value of property: " << key << " Value: " << value << std::endl;
940+			std::exit(10);
941+		}
942+	}	
943+}
944+
945+struct SuffixParse {
946+    std::string_view suffix;
947+    UnitType type;
948+    int trim;   // chars to strip from the right
949+};
950+
951+static constexpr SuffixParse table[] = {
952+    {"cqmin", UnitType::CQMIN, 5},
953+    {"cqmax", UnitType::CQMAX, 5},
954+    {"rcap",  UnitType::RCAP,  4},
955+    {"vmax",  UnitType::VMAX,  4},
956+    {"vmin",  UnitType::VMIN,  4},
957+    {"cap",   UnitType::CAP,   3},
958+    {"rch",   UnitType::RCH,   3},
959+    {"rem",   UnitType::REM,   3},
960+    {"rex",   UnitType::REX,   3},
961+    {"ric",   UnitType::RIC,   3},
962+    {"rlh",   UnitType::RLH,   3},
963+    {"cqw",   UnitType::CQW,   3},
964+    {"cqh",   UnitType::CQH,   3},
965+    {"cqi",   UnitType::CQI,   3},
966+    {"cqb",   UnitType::CQB,   3},
967+    {"px",    UnitType::PX,    2},
968+    {"em",    UnitType::EM,    2},
969+    {"cm",    UnitType::CM,    2},
970+    {"ch",    UnitType::CH,    2},
971+    {"ex",    UnitType::EX,    2},
972+    {"ic",    UnitType::IC,    2},
973+    {"lh",    UnitType::LH,    2},
974+    {"vh",    UnitType::VH,    2},
975+    {"vw",    UnitType::VW,    2},
976+    {"vb",    UnitType::VB,    2},
977+    {"vi",    UnitType::VI,    2},
978+    {"mm",    UnitType::MM,    2},
979+    {"in",    UnitType::IN,    2},
980+    {"pc",    UnitType::PC,    2},
981+    {"pt",    UnitType::PT,    2},
982+    {"%",     UnitType::PERCENT,1},
983+    {"q",     UnitType::Q,     1},
984+};
985+
986+std::vector<Unit> getUnit(std::string value) {
987+	std::vector<Unit> units;
988+	std::vector<Unit> children;
989+
990+	
991+	std::string buffer = "";
992+	std::string parenthesesesBuffer = "";
993+	bool inParentheses = false;
994+	bool inQuotes = false;
995+	// Needs to not be empty but can't be a quote
996+	char quoteType = 'a';
997+	bool escaped = false;
998+
999+	for (size_t i = 0; i < value.length(); i++) {
1000+		char v = value[i];
1001+		if (v == '\\' && !escaped) escaped = true;
1002+
1003+		if (inParentheses) {
1004+			parenthesesesBuffer.push_back(v);
1005+		}
1006+
1007+		// The way this is setup to parse func() calls is like so
1008+		// | calc(1px, 2px);
1009+		// | -> value of 1px, 2px get's put into getUnit()
1010+		// | once those are pulled into children
1011+		// | the buffer only contains calc
1012+		// | add the children to the parsed keyword
1013+
1014+		
1015+		if (v == '(' && !inParentheses && !inQuotes) inParentheses = true;
1016+		else if (v == ')' && inParentheses && !inQuotes) {
1017+			children = getUnit(parenthesesesBuffer);
1018+			inParentheses = false;
1019+			parenthesesesBuffer.clear();
1020+		} else if ((v == '"' || v == '\'') && !inParentheses) {
1021+			if (inQuotes && v == quoteType) {
1022+				inQuotes = false;
1023+			} else if (!inQuotes) {
1024+				quoteType = v;
1025+				inQuotes = true;
1026+			}
1027+
1028+		} else if ((!inQuotes && !inParentheses && (v == ',' ||  std::isspace(v))) || i >= value.length()-1) {
1029+			if (buffer.length() == 0) continue;
1030+			if (v != ',' && !std::isspace(v)) buffer.push_back(v);
1031+
1032+			Unit unit;
1033+
1034+			// Split by spaces into parts
1035+
1036+			bool found = false;
1037+
1038+			for (const auto& [suffix, type, trim] : table) {
1039+				if (buffer.ends_with(suffix)) {
1040+					unit.value.FLOAT = std::stof(std::string(buffer.substr(0, buffer.size() - trim)));
1041+					unit.type = type;
1042+					found = true;
1043+					break;
1044+				}
1045+			}
1046+
1047+			if (!found) {
1048+				if (buffer.starts_with("#")) {
1049+					unit.type = UnitType::HEX;
1050+					unit.value.UINT = std::stoul(buffer.substr(1), nullptr, 16);
1051+					found = true;
1052+				} else if (buffer == "xx-small" || buffer == "x-small" || buffer == "small" || buffer == "medium" || 
1053+						buffer == "large" || buffer == "x-large" || buffer == "xx-large" || buffer == "xxx-large" ) {
1054+					// <absolute-size> read as a EM
1055+					unit.type = UnitType::EM;
1056+
1057+					if (buffer == "xx-small") unit.value.FLOAT = 0.6;
1058+					else if (buffer == "x-small") unit.value.FLOAT = 0.75;
1059+					else if (buffer == "small") unit.value.FLOAT = 0.89;
1060+					else if (buffer == "medium") unit.value.FLOAT = 1;
1061+					else if (buffer == "large") unit.value.FLOAT = 1.2;
1062+					else if (buffer == "x-large") unit.value.FLOAT = 1.5;
1063+					else if (buffer == "xx-large") unit.value.FLOAT = 2;
1064+					else if (buffer == "xxx-large") unit.value.FLOAT = 3;
1065+					found = true;
1066+				} else if (buffer.ends_with("baseline")) {
1067+					unit.type = UnitType::BASELINE;
1068+					std::cout << buffer << std::endl;
1069+					if (buffer.starts_with("first")) unit.value.ENUM = UnitType::FIRST;
1070+					else if (buffer.starts_with("last")) unit.value.ENUM = UnitType::LAST;
1071+					else unit.value.ENUM = UnitType::FIRST;
1072+					found = true;
1073+				} else if (buffer.ends_with("-box")) {
1074+					unit.type = UnitType::BOX_EDGE;
1075+
1076+					if (buffer.starts_with("content")) unit.value.ENUM = UnitType::CONTENT;
1077+					else if (buffer.starts_with("padding")) unit.value.ENUM = UnitType::PADDING;
1078+					else if (buffer.starts_with("border")) unit.value.ENUM = UnitType::BORDER;
1079+					else if (buffer.starts_with("margin")) unit.value.ENUM = UnitType::MARGIN;
1080+					else if (buffer.starts_with("fill")) unit.value.ENUM = UnitType::FILL;
1081+					else if (buffer.starts_with("stroke")) unit.value.ENUM = UnitType::STROKE;
1082+					else if (buffer.starts_with("view")) unit.value.ENUM = UnitType::VIEW;
1083+					found = true;
1084+				}
1085+			}
1086+
1087+			if (found) {
1088+				// Only process if its found
1089+
1090+				if (children.size() > 0) {
1091+					unit.children = children;
1092+					children.clear();
1093+				}
1094+
1095+				units.push_back(unit);
1096+
1097+				buffer.clear();
1098+			}
1099+		} else {
1100+			buffer.push_back(v);
1101+		}
1102+		
1103+		escaped = false;
1104+		if (v == '\\') escaped = true;
1105+		
1106+	}
1107+
1108+	return units;
1109+}
1110+
1111+void StyleHandler::parseStream(std::istream& inputStream) {
1112+	// nowhitespace: value(can have whitespace); = property
1113+	// a selector name is anything before a { up to a ; or a }
1114+	Sheet* root = &window->CSS.root;
1115+	
1116+	Sheet* current = root;
1117+	// how to handle @import
1118+
1119+	std::string buffer;
1120+
1121+	bool incomment = false;
1122+
1123+	char ch;
1124+	while (inputStream.get(ch)) {
1125+		if (ch == '*' && inputStream.peek() == '/') {
1126+			incomment = false;
1127+			// Skip ahead to the next letter so we don't caputure the trailing /
1128+			inputStream.get(ch);
1129+			continue;
1130+		}
1131+		if (ch == '/' && inputStream.peek() == '*') {
1132+			incomment = true;
1133+		}
1134+		
1135+		if (incomment) {
1136+			continue;
1137+		}
1138+
1139+		if (ch == '{') {
1140+			for (size_t i = buffer.length()-1; i >= 0; i--) {
1141+				if (std::isspace(buffer[i])) {
1142+					buffer.pop_back();
1143+				} else {
1144+					break;
1145+				}
1146+			}
1147+
1148+			// Parse the selector into its parts
1149+			std::vector<std::vector<std::string>> parts = parseSelectorParts(buffer);
1150+
1151+			if (current->selector.size() > 0) {
1152+				// To be able to handle a subsutution with a parent that has h1, h2
1153+				// we need to rebuild the selector so we can add new copies
1154+				std::vector<std::vector<std::string>> subd;
1155+
1156+				// Look for any & if its a style and replace it with the parsed parent selector
1157+				for (size_t i = 0; i < parts.size(); i++) {
1158+					bool hasAmp = false;
1159+					for (size_t e = 0; e < parts[i].size(); e++) {
1160+						if (parts[i][e] == "&") {
1161+							hasAmp = true;
1162+							break;
1163+						}
1164+					}
1165+
1166+					if (hasAmp) {
1167+						// If it contains a amp then we need replace the amp with each parent selector
1168+						// Inject each part of the parent
1169+						// Parent [[h1],[h2]]
1170+						// Child: [[& .class, &, &]]
1171+						// Result: [[h1 .class, h1, h1]]
1172+						// 	   [[h2 .class, h2, h2]]
1173+						for (size_t e = 0; e < current->selector.size(); e++) {
1174+							std::vector<std::string> temp;
1175+							for (size_t f = 0; f < parts[i].size(); f++) {
1176+								if (parts[i][f] == "&") {
1177+									for (auto p : current->selector[e]) {
1178+										temp.push_back(p);
1179+									}
1180+								} else {
1181+									temp.push_back(parts[i][f]);
1182+								}
1183+							}
1184+							subd.push_back(temp);
1185+						}
1186+					} else {
1187+						// If theres nothing to replace just add to back
1188+						subd.push_back(parts[i]);
1189+					}
1190+				}
1191+
1192+				parts = subd;
1193+			}
1194+
1195+			// Find who the parent is
1196+			Sheet newSheet;
1197+			newSheet.parent = current;
1198+			newSheet.selector = parts;
1199+			current->children.push_back(newSheet);
1200+
1201+			current = &current->children.back();
1202+
1203+			// here we need to take the selector (buffer)
1204+			// and find out what type it is. Also need to manage the nesting
1205+			if (parts[0][0][0] == '@') {
1206+				if (parts[0][0] == "@container") {
1207+					current->type = SheetType::CONTAINER;
1208+					bool hasName = false;
1209+					if (parts[0].size() >= 3) {
1210+						// @container not (width < 400px)
1211+						if (parts[0][2] != "not") {
1212+							// @container (width > 400px) or|and (height > 400px)
1213+							if (parts[0].size() >= 5) {
1214+								if (parts[0][5] != "and" || parts[0][5] != "or") {
1215+									current->name = parts[0][2];
1216+									hasName = true;
1217+								}
1218+							}
1219+						}
1220+					}
1221+					// If a name is present then remove the name and tag from the selector
1222+					// else just remove the @container
1223+					if (hasName) {
1224+						current->selector[0] = std::vector<std::string>(current->selector[0].begin() + 3, current->selector[0].end());
1225+					} else {
1226+						current->selector[0] = std::vector<std::string>(current->selector[0].begin() + 1, current->selector[0].end());
1227+					}
1228+				} else if (parts[0][0] == "@counter-style") {
1229+					current->name = parts[0][2];
1230+					current->type = SheetType::COUNTER;
1231+				} else if (parts[0][0] == "@font-face") {
1232+					current->type = SheetType::FONTFACE;
1233+				} else if (parts[0][0] == "@font-feature") {
1234+					current->type = SheetType::FONTFEATURE;
1235+				} else if (parts[0][0] == "@swash") {
1236+					current->type = SheetType::SWASH;
1237+				} else if (parts[0][0] == "@annotation") {
1238+					current->type = SheetType::ANNOTATION;
1239+				} else if (parts[0][0] == "@ornaments") {
1240+					current->type = SheetType::ORNAMENTS;
1241+				} else if (parts[0][0] == "@stylistic") {
1242+					current->type = SheetType::STYLISTIC;
1243+				} else if (parts[0][0] == "@styleset") {
1244+					current->type = SheetType::STYLESET;
1245+				} else if (parts[0][0] == "@character-varient") {
1246+					current->type = SheetType::CHARACTERVARIANT;
1247+				} else if (parts[0][0] == "@font-palette-values") {
1248+					current->type = SheetType::FONTPALETTEVALUES;
1249+					current->name = parts[0][2];
1250+				} else if (parts[0][0] == "@keyframes") {
1251+					current->type = SheetType::KEYFRAMES;
1252+					current->name = parts[0][2];
1253+				} else if (parts[0][0] == "@layer") {
1254+					current->type = SheetType::LAYER;
1255+				} else if (parts[0][0] == "@media") {
1256+					current->type = SheetType::MEDIA;
1257+				} else if (parts[0][0] == "@property") {
1258+					current->type = SheetType::PROPERTY;
1259+					current->name = parts[0][2];
1260+				} else if (parts[0][0] == "@starting-style") {
1261+					current->type = SheetType::STARTINGSTYLE;
1262+				}
1263+			} else {
1264+				current->type = SheetType::STYLE;
1265+			}
1266+			
1267+
1268+			buffer.clear();
1269+		} else if (ch == '}') {
1270+			// Calculate the basemap (more info in grim.h)
1271+			std::vector<std::string> targetParts = current->selector[current->selector.size()-1];
1272+			for (auto p : targetParts) {
1273+				if (current->parent->basemap.find(p) != current->parent->basemap.end()) {
1274+					current->parent->basemap[p].push_back(window->CSS.index);
1275+				} else {
1276+					std::vector<size_t> bm = {window->CSS.index};
1277+					current->parent->basemap[p] = bm;
1278+				}
1279+			}
1280+
1281+			window->CSS.index++;
1282+
1283+			if (current->parent != nullptr) {
1284+				current = current->parent;
1285+			}
1286+
1287+                        buffer.clear();
1288+		} else if (ch == ';') {
1289+			if (buffer[0] == '@') {
1290+				// Single line at-rule
1291+				Sheet inlineRule;
1292+				std::vector<std::vector<std::string>> parts = parseSelectorParts(buffer);
1293+				inlineRule.selector = parts;
1294+				
1295+				if (parts[0][0] == "@import") {
1296+					inlineRule.path = parts[0][2];
1297+
1298+					if (parts[0].size() > 3) {
1299+						// See if the last word ends in )
1300+						// if not then its the name of the layer
1301+						if (parts[0].back().back() != ')') {
1302+							inlineRule.name = parts[0].back();
1303+						}
1304+					}
1305+
1306+				} else if (parts[0][0] == "@layer") {
1307+					// ISSUE: need to add speficity
1308+
1309+				}
1310+
1311+				current->children.push_back(inlineRule);
1312+
1313+			} else {
1314+				// end of a style property
1315+				std::string key;
1316+				std::string value;
1317+				bool keyFound = false;
1318+				bool firstValue = false;
1319+				for (size_t i = 0; i < buffer.length(); i++) {
1320+					char c = buffer[i];
1321+					if (!keyFound && c == ':') {
1322+						keyFound = true;
1323+					} else if (!std::isspace(c) && !keyFound) {
1324+						key += c;
1325+					} else if (!firstValue && keyFound && !std::isspace(c)) {
1326+						firstValue = true;
1327+						value += c;
1328+					} else if (keyFound && firstValue && !(std::isspace(c) && i < buffer.length()-1 && std::isspace(buffer[i+1]))) {
1329+						// if the key is found and the charactor isn't a ':'
1330+						// 	also (if c is a space and the next c will be a space)
1331+						// 	this is to auto trim duplicate spaces from the value
1332+						value += c;
1333+					}
1334+				}
1335+
1336+				if (key.length() > 2 && key[0] == '-' && key[1] == '-') {
1337+					current->variables[key] = value;
1338+				} else {
1339+					current->properties[key] = value;
1340+				}
1341+			}
1342+			buffer.clear();
1343+		} else {
1344+			buffer.push_back(ch);
1345+		}
1346+	}
1347+}
1348+
1349+std::unordered_map<std::string, std::string> parseCSSInline(std::string inlineStyles) {
1350+	std::unordered_map<std::string, std::string> props;
1351+
1352+	std::string buffer;
1353+
1354+	for (size_t i = 0; i < inlineStyles.length(); i++) {
1355+		char ch = inlineStyles[i];
1356+
1357+		if (ch == ';' || i >= inlineStyles.length()-1) {
1358+			// If this is a ';' then it will be removed in the last else if statement
1359+			buffer.push_back(ch);
1360+			std::string key;
1361+			std::string value;
1362+			bool keyFound = false;
1363+			for (size_t i = 0; i < buffer.length(); i++) {
1364+				char c = buffer[i];
1365+				if (!keyFound && c == ':') {
1366+					keyFound = true;
1367+				} else if (!std::isspace(c) && !keyFound) {
1368+					key += c;
1369+				} else if (keyFound &&
1370+						!(std::isspace(c) && i < buffer.length()-1 && std::isspace(buffer[i+1])) &&
1371+						c != ';'
1372+					  ) {
1373+					// if the key is found and the charactor isn't a ':'
1374+					// 	also (if c is a space and the next c will be a space)
1375+					// 	this is to auto trim duplicate spaces from the value
1376+					value += c;
1377+					
1378+				}
1379+			}
1380+
1381+			props[key] = value;
1382+
1383+			buffer.clear();
1384+		} else {
1385+			buffer.push_back(ch);
1386+		}
1387+	}
1388+
1389+	return props;
1390+}
1391+
1392+
1393diff --git a/tests/css_unit.cc b/tests/css_unit.cc
1394index 239e7fa..0198810 100644
1395--- a/tests/css_unit.cc
1396+++ b/tests/css_unit.cc
1397@@ -6,47 +6,630 @@
1398 
1399 
1400 TEST_CASE("getUnit can parse color values") {
1401-
1402+/*
1403 	unsigned int base = 0xff7dff1a;
1404 
1405 	SECTION("getUnit can parse hex") {
1406-		Unit test = getUnit("background-color", "#ff7dff1a");
1407+		Unit test = getUnit("#ff7dff1a");
1408 		INFO(test.value.UINT);
1409 		REQUIRE(base == test.value.UINT);
1410 	};
1411 
1412 	SECTION("getUnit can parse rgba") {
1413-		Unit test = getUnit("background-color", "rgba(255 125 255 / 10%)");
1414+		Unit test = getUnit("rgba(255 125 255 / 10%)");
1415 		INFO(test.value.UINT);
1416 		// Due to type casting 0.1 will return 25 not 26 or 19 vs 1a
1417 		REQUIRE(0xff7dff19 == test.value.UINT);
1418 	}
1419 
1420 	SECTION("getUnit can parse rgb") {
1421-		Unit test = getUnit("background-color", "rgb(255 125 255)");
1422+		Unit test = getUnit("rgb(255 125 255)");
1423 		INFO(test.value.UINT);
1424 		// Due to type casting 0.1 will return 25 not 26 or 19 vs 1a
1425 		REQUIRE(0xff7dff00 == test.value.UINT);
1426 	}
1427 
1428 	SECTION("getUnit can parse hsl") {
1429-		Unit test = getUnit("background-color", "hsl(300deg 100% 74.51%)");
1430+		Unit test = getUnit("hsl(300deg 100% 74.51%)");
1431 		INFO(test.value.UINT);
1432 		// Due to type casting 0.1 will return 25 not 26 or 19 vs 1a
1433 		REQUIRE(0xff7afe00 == test.value.UINT);
1434 	}
1435 
1436 	SECTION("getUnit can parse hsla") {
1437-		Unit test = getUnit("background-color", "hsl(300deg 100% 74.51% / 10%)");
1438+		Unit test = getUnit("hsl(300deg 100% 74.51% / 10%)");
1439 		INFO(test.value.UINT);
1440 		// Due to type casting 0.1 will return 25 not 26 or 19 vs 1a
1441 		REQUIRE(0xff7afe19 == test.value.UINT);
1442 	}
1443 
1444 	SECTION("getUnit can parse hwb") {
1445-		Unit test = getUnit("background-color", "hwb(80deg 20 50 / 50%)");
1446+		Unit test = getUnit("hwb(80deg 20 50 / 50%)");
1447 		INFO(test.value.UINT);
1448 		// Due to type casting 0.1 will return 25 not 26 or 19 vs 1a
1449 		REQUIRE(0x667f337f == test.value.UINT);
1450+	}*/
1451+	// px
1452+	SECTION("getUnit can parse px") {
1453+		std::vector<Unit> test = getUnit("10px 10.5px");
1454+		REQUIRE(10.0f == test[0].value.FLOAT);
1455+		REQUIRE(UnitType::PX == test[0].type);
1456+		REQUIRE(10.5f == test[1].value.FLOAT);
1457+		REQUIRE(UnitType::PX == test[1].type);
1458+	}
1459+
1460+	// em
1461+	SECTION("getUnit can parse em") {
1462+		std::vector<Unit> test = getUnit("1.2em 0em");
1463+		REQUIRE(1.2f == test[0].value.FLOAT);
1464+		REQUIRE(UnitType::EM == test[0].type);
1465+		REQUIRE(0.0f == test[1].value.FLOAT);
1466+		REQUIRE(UnitType::EM == test[1].type);
1467+	}
1468+
1469+	// %
1470+	SECTION("getUnit can parse percent") {
1471+		std::vector<Unit> test = getUnit("50% 100.5%");
1472+		REQUIRE(50.0f == test[0].value.FLOAT);
1473+		REQUIRE(UnitType::PERCENT == test[0].type);
1474+		REQUIRE(100.5f == test[1].value.FLOAT);
1475+		REQUIRE(UnitType::PERCENT == test[1].type);
1476+	}
1477+
1478+	// cm
1479+	SECTION("getUnit can parse cm") {
1480+		std::vector<Unit> test = getUnit("2.54cm 0.5cm");
1481+		REQUIRE(2.54f == test[0].value.FLOAT);
1482+		REQUIRE(UnitType::CM == test[0].type);
1483+		REQUIRE(0.5f == test[1].value.FLOAT);
1484+		REQUIRE(UnitType::CM == test[1].type);
1485+	}
1486+
1487+	// cap
1488+	SECTION("getUnit can parse cap") {
1489+		std::vector<Unit> test = getUnit("3cap 0cap");
1490+		REQUIRE(3.0f == test[0].value.FLOAT);
1491+		REQUIRE(UnitType::CAP == test[0].type);
1492+		REQUIRE(0.0f == test[1].value.FLOAT);
1493+		REQUIRE(UnitType::CAP == test[1].type);
1494+	}
1495+
1496+	// ch
1497+	SECTION("getUnit can parse ch") {
1498+		std::vector<Unit> test = getUnit("10ch 0.5ch");
1499+		REQUIRE(10.0f == test[0].value.FLOAT);
1500+		REQUIRE(UnitType::CH == test[0].type);
1501+		REQUIRE(0.5f == test[1].value.FLOAT);
1502+		REQUIRE(UnitType::CH == test[1].type);
1503+	}
1504+
1505+	// ex
1506+	SECTION("getUnit can parse ex") {
1507+		std::vector<Unit> test = getUnit("2ex 1.5ex");
1508+		REQUIRE(2.0f == test[0].value.FLOAT);
1509+		REQUIRE(UnitType::EX == test[0].type);
1510+		REQUIRE(1.5f == test[1].value.FLOAT);
1511+		REQUIRE(UnitType::EX == test[1].type);
1512+	}
1513+
1514+	// ic
1515+	SECTION("getUnit can parse ic") {
1516+		std::vector<Unit> test = getUnit("5ic 0ic");
1517+		REQUIRE(5.0f == test[0].value.FLOAT);
1518+		REQUIRE(UnitType::IC == test[0].type);
1519+		REQUIRE(0.0f == test[1].value.FLOAT);
1520+		REQUIRE(UnitType::IC == test[1].type);
1521+	}
1522+
1523+	// lh
1524+	SECTION("getUnit can parse lh") {
1525+		std::vector<Unit> test = getUnit("1.2lh 0lh");
1526+		REQUIRE(1.2f == test[0].value.FLOAT);
1527+		REQUIRE(UnitType::LH == test[0].type);
1528+		REQUIRE(0.0f == test[1].value.FLOAT);
1529+		REQUIRE(UnitType::LH == test[1].type);
1530+	}
1531+
1532+	// rcap
1533+	SECTION("getUnit can parse rcap") {
1534+		std::vector<Unit> test = getUnit("2rcap 0.5rcap");
1535+		REQUIRE(2.0f == test[0].value.FLOAT);
1536+		REQUIRE(UnitType::RCAP == test[0].type);
1537+		REQUIRE(0.5f == test[1].value.FLOAT);
1538+		REQUIRE(UnitType::RCAP == test[1].type);
1539+	}
1540+
1541+	// rch
1542+	SECTION("getUnit can parse rch") {
1543+		std::vector<Unit> test = getUnit("3rch 0rch");
1544+		REQUIRE(3.0f == test[0].value.FLOAT);
1545+		REQUIRE(UnitType::RCH == test[0].type);
1546+		REQUIRE(0.0f == test[1].value.FLOAT);
1547+		REQUIRE(UnitType::RCH == test[1].type);
1548+	}
1549+
1550+	// rem
1551+	SECTION("getUnit can parse rem") {
1552+		std::vector<Unit> test = getUnit("1rem 0.5rem");
1553+		REQUIRE(1.0f == test[0].value.FLOAT);
1554+		REQUIRE(UnitType::REM == test[0].type);
1555+		REQUIRE(0.5f == test[1].value.FLOAT);
1556+		REQUIRE(UnitType::REM == test[1].type);
1557+	}
1558+
1559+	// rex
1560+	SECTION("getUnit can parse rex") {
1561+		std::vector<Unit> test = getUnit("1.5rex 0rex");
1562+		REQUIRE(1.5f == test[0].value.FLOAT);
1563+		REQUIRE(UnitType::REX == test[0].type);
1564+		REQUIRE(0.0f == test[1].value.FLOAT);
1565+		REQUIRE(UnitType::REX == test[1].type);
1566+	}
1567+
1568+	// ric
1569+	SECTION("getUnit can parse ric") {
1570+		std::vector<Unit> test = getUnit("2ric 0.25ric");
1571+		REQUIRE(2.0f == test[0].value.FLOAT);
1572+		REQUIRE(UnitType::RIC == test[0].type);
1573+		REQUIRE(0.25f == test[1].value.FLOAT);
1574+		REQUIRE(UnitType::RIC == test[1].type);
1575+	}
1576+
1577+	// rlh
1578+	SECTION("getUnit can parse rlh") {
1579+		std::vector<Unit> test = getUnit("1.1rlh 0rlh");
1580+		REQUIRE(1.1f == test[0].value.FLOAT);
1581+		REQUIRE(UnitType::RLH == test[0].type);
1582+		REQUIRE(0.0f == test[1].value.FLOAT);
1583+		REQUIRE(UnitType::RLH == test[1].type);
1584+	}
1585+
1586+	// vh
1587+	SECTION("getUnit can parse vh") {
1588+		std::vector<Unit> test = getUnit("50vh 100vh");
1589+		REQUIRE(50.0f == test[0].value.FLOAT);
1590+		REQUIRE(UnitType::VH == test[0].type);
1591+		REQUIRE(100.0f == test[1].value.FLOAT);
1592+		REQUIRE(UnitType::VH == test[1].type);
1593+	}
1594+
1595+	// vw
1596+	SECTION("getUnit can parse vw") {
1597+		std::vector<Unit> test = getUnit("25vw 0vw");
1598+		REQUIRE(25.0f == test[0].value.FLOAT);
1599+		REQUIRE(UnitType::VW == test[0].type);
1600+		REQUIRE(0.0f == test[1].value.FLOAT);
1601+		REQUIRE(UnitType::VW == test[1].type);
1602+	}
1603+
1604+	// vmax
1605+	SECTION("getUnit can parse vmax") {
1606+		std::vector<Unit> test = getUnit("75vmax 0vmax");
1607+		REQUIRE(75.0f == test[0].value.FLOAT);
1608+		REQUIRE(UnitType::VMAX == test[0].type);
1609+		REQUIRE(0.0f == test[1].value.FLOAT);
1610+		REQUIRE(UnitType::VMAX == test[1].type);
1611+	}
1612+
1613+	// vmin
1614+	SECTION("getUnit can parse vmin") {
1615+		std::vector<Unit> test = getUnit("10vmin 99.9vmin");
1616+		REQUIRE(10.0f == test[0].value.FLOAT);
1617+		REQUIRE(UnitType::VMIN == test[0].type);
1618+		REQUIRE(99.9f == test[1].value.FLOAT);
1619+		REQUIRE(UnitType::VMIN == test[1].type);
1620 	}
1621+
1622+	// vb
1623+	SECTION("getUnit can parse vb") {
1624+		std::vector<Unit> test = getUnit("20vb 0vb");
1625+		REQUIRE(20.0f == test[0].value.FLOAT);
1626+		REQUIRE(UnitType::VB == test[0].type);
1627+		REQUIRE(0.0f == test[1].value.FLOAT);
1628+		REQUIRE(UnitType::VB == test[1].type);
1629+	}
1630+
1631+	// vi
1632+	SECTION("getUnit can parse vi") {
1633+		std::vector<Unit> test = getUnit("5vi 1vi");
1634+		REQUIRE(5.0f == test[0].value.FLOAT);
1635+		REQUIRE(UnitType::VI == test[0].type);
1636+		REQUIRE(1.0f == test[1].value.FLOAT);
1637+		REQUIRE(UnitType::VI == test[1].type);
1638+	}
1639+
1640+	// cqw
1641+	SECTION("getUnit can parse cqw") {
1642+		std::vector<Unit> test = getUnit("30cqw 0cqw");
1643+		REQUIRE(30.0f == test[0].value.FLOAT);
1644+		REQUIRE(UnitType::CQW == test[0].type);
1645+		REQUIRE(0.0f == test[1].value.FLOAT);
1646+		REQUIRE(UnitType::CQW == test[1].type);
1647+	}
1648+
1649+	// cqh
1650+	SECTION("getUnit can parse cqh") {
1651+		std::vector<Unit> test = getUnit("40cqh 10.5cqh");
1652+		REQUIRE(40.0f == test[0].value.FLOAT);
1653+		REQUIRE(UnitType::CQH == test[0].type);
1654+		REQUIRE(10.5f == test[1].value.FLOAT);
1655+		REQUIRE(UnitType::CQH == test[1].type);
1656+	}
1657+
1658+	// cqi
1659+	SECTION("getUnit can parse cqi") {
1660+		std::vector<Unit> test = getUnit("15cqi 0cqi");
1661+		REQUIRE(15.0f == test[0].value.FLOAT);
1662+		REQUIRE(UnitType::CQI == test[0].type);
1663+		REQUIRE(0.0f == test[1].value.FLOAT);
1664+		REQUIRE(UnitType::CQI == test[1].type);
1665+	}
1666+
1667+	// cqb
1668+	SECTION("getUnit can parse cqb") {
1669+		std::vector<Unit> test = getUnit("25cqb 1cqb");
1670+		REQUIRE(25.0f == test[0].value.FLOAT);
1671+		REQUIRE(UnitType::CQB == test[0].type);
1672+		REQUIRE(1.0f == test[1].value.FLOAT);
1673+		REQUIRE(UnitType::CQB == test[1].type);
1674+	}
1675+
1676+	// cqmin
1677+	SECTION("getUnit can parse cqmin") {
1678+		std::vector<Unit> test = getUnit("10cqmin 0cqmin");
1679+		REQUIRE(10.0f == test[0].value.FLOAT);
1680+		REQUIRE(UnitType::CQMIN == test[0].type);
1681+		REQUIRE(0.0f == test[1].value.FLOAT);
1682+		REQUIRE(UnitType::CQMIN == test[1].type);
1683+	}
1684+
1685+	// cqmax
1686+	SECTION("getUnit can parse cqmax") {
1687+		std::vector<Unit> test = getUnit("90cqmax \n 50.5cqmax");
1688+		REQUIRE(90.0f == test[0].value.FLOAT);
1689+		REQUIRE(UnitType::CQMAX == test[0].type);
1690+		REQUIRE(50.5f == test[1].value.FLOAT);
1691+		REQUIRE(UnitType::CQMAX == test[1].type);
1692+	}
1693+
1694+	// mm
1695+	SECTION("getUnit can parse mm") {
1696+		std::vector<Unit> test = getUnit("10mm		 0.1mm");
1697+		REQUIRE(10.0f == test[0].value.FLOAT);
1698+		REQUIRE(UnitType::MM == test[0].type);
1699+		REQUIRE(0.1f == test[1].value.FLOAT);
1700+		REQUIRE(UnitType::MM == test[1].type);
1701+	}
1702+
1703+	// q
1704+	SECTION("getUnit can parse q") {
1705+		std::vector<Unit> test = getUnit("40q,          1q");
1706+		REQUIRE(40.0f == test[0].value.FLOAT);
1707+		REQUIRE(UnitType::Q == test[0].type);
1708+		REQUIRE(1.0f == test[1].value.FLOAT);
1709+		REQUIRE(UnitType::Q == test[1].type);
1710+	}
1711+
1712+	// in
1713+	SECTION("getUnit can parse in") {
1714+		std::vector<Unit> test = getUnit("1in          0.5in");
1715+		REQUIRE(1.0f == test[0].value.FLOAT);
1716+		REQUIRE(UnitType::IN == test[0].type);
1717+		REQUIRE(0.5f == test[1].value.FLOAT);
1718+		REQUIRE(UnitType::IN == test[1].type);
1719+	}
1720+
1721+	// pc
1722+	SECTION("getUnit can parse pc") {
1723+		std::vector<Unit> test = getUnit("6pc, 0pc");
1724+		REQUIRE(6.0f == test[0].value.FLOAT);
1725+		REQUIRE(UnitType::PC == test[0].type);
1726+		REQUIRE(0.0f == test[1].value.FLOAT);
1727+		REQUIRE(UnitType::PC == test[1].type);
1728+	}
1729+
1730+	// pt
1731+	SECTION("getUnit can parse pt") {
1732+		std::vector<Unit> test = getUnit("12pt");
1733+		REQUIRE(12.0f == test[0].value.FLOAT);
1734+		REQUIRE(UnitType::PT == test[0].type);
1735+	}
1736+
1737+	// HEX Color
1738+	SECTION("getUnit can parse a hex color") {
1739+		std::vector<Unit> test = getUnit("#000000");
1740+		REQUIRE(0x000000 == test[0].value.UINT);
1741+		REQUIRE(UnitType::HEX == test[0].type);
1742+	}
1743+
1744+	// xx-small abs unit
1745+	SECTION("getUnit can parse a xx-small abs unit") {
1746+		std::vector<Unit> test = getUnit("xx-small");
1747+		REQUIRE(0.6f == test[0].value.FLOAT);
1748+		REQUIRE(UnitType::EM == test[0].type);
1749+	}
1750+
1751+	// x-small abs unit
1752+	SECTION("getUnit can parse an x-small abs unit") {
1753+		std::vector<Unit> test = getUnit("x-small");
1754+		REQUIRE(0.75f == test[0].value.FLOAT);
1755+		REQUIRE(UnitType::EM == test[0].type);
1756+	}
1757+
1758+	// small abs unit
1759+	SECTION("getUnit can parse a small abs unit") {
1760+		std::vector<Unit> test = getUnit("small");
1761+		REQUIRE(0.89f == test[0].value.FLOAT);
1762+		REQUIRE(UnitType::EM == test[0].type);
1763+	}
1764+
1765+	// medium abs unit
1766+	SECTION("getUnit can parse a medium abs unit") {
1767+		std::vector<Unit> test = getUnit("medium");
1768+		REQUIRE(1.0f == test[0].value.FLOAT);
1769+		REQUIRE(UnitType::EM == test[0].type);
1770+	}
1771+
1772+	// large abs unit
1773+	SECTION("getUnit can parse a large abs unit") {
1774+		std::vector<Unit> test = getUnit("large");
1775+		REQUIRE(1.2f == test[0].value.FLOAT);
1776+		REQUIRE(UnitType::EM == test[0].type);
1777+	}
1778+
1779+	// x-large abs unit
1780+	SECTION("getUnit can parse an x-large abs unit") {
1781+		std::vector<Unit> test = getUnit("x-large");
1782+		REQUIRE(1.5f == test[0].value.FLOAT);
1783+		REQUIRE(UnitType::EM == test[0].type);
1784+	}
1785+
1786+	// xx-large abs unit
1787+	SECTION("getUnit can parse an xx-large abs unit") {
1788+		std::vector<Unit> test = getUnit("xx-large");
1789+		REQUIRE(2.0f == test[0].value.FLOAT);
1790+		REQUIRE(UnitType::EM == test[0].type);
1791+	}
1792+
1793+	// xxx-large abs unit
1794+	SECTION("getUnit can parse an xxx-large abs unit") {
1795+		std::vector<Unit> test = getUnit("xxx-large");
1796+		REQUIRE(3.0f == test[0].value.FLOAT);
1797+		REQUIRE(UnitType::EM == test[0].type);
1798+	}
1799+
1800+	// small and large abs unit
1801+	SECTION("getUnit can parse two abs units with a space") {
1802+		std::vector<Unit> test = getUnit("small large");
1803+		REQUIRE(0.89f == test[0].value.FLOAT);
1804+		REQUIRE(1.2f == test[1].value.FLOAT);
1805+		REQUIRE(UnitType::EM == test[0].type);
1806+		REQUIRE(UnitType::EM == test[1].type);
1807+	}
1808+
1809+	// small and large abs unit
1810+	SECTION("getUnit can parse two abs units with a comma") {
1811+		std::vector<Unit> test = getUnit("small, large");
1812+		REQUIRE(0.89f == test[0].value.FLOAT);
1813+		REQUIRE(1.2f == test[1].value.FLOAT);
1814+		REQUIRE(UnitType::EM == test[0].type);
1815+		REQUIRE(UnitType::EM == test[1].type);
1816+	}
1817+
1818+	// baseline
1819+	SECTION("getUnit can parse baseline into first baseline") {
1820+		std::vector<Unit> test = getUnit("baseline");
1821+		REQUIRE(UnitType::FIRST == test[0].value.ENUM);
1822+		REQUIRE(UnitType::BASELINE == test[0].type);
1823+	}
1824+
1825+	// first baseline
1826+	SECTION("getUnit can parse first baseline") {
1827+		std::vector<Unit> test = getUnit("first baseline");
1828+		REQUIRE(UnitType::FIRST == test[0].value.ENUM);
1829+		REQUIRE(UnitType::BASELINE == test[0].type);
1830+	}
1831+
1832+	// last baseline
1833+	SECTION("getUnit can parse last baseline") {
1834+		std::vector<Unit> test = getUnit("last baseline");
1835+		REQUIRE(UnitType::LAST == test[0].value.ENUM);
1836+		REQUIRE(UnitType::BASELINE == test[0].type);
1837+	}
1838+
1839+	// last baseline with another space seperated value
1840+	SECTION("getUnit can parse last baseline with a space seperated value") {
1841+		std::vector<Unit> test = getUnit("last baseline 10px");
1842+		REQUIRE(UnitType::LAST == test[0].value.ENUM);
1843+		REQUIRE(UnitType::BASELINE == test[0].type);
1844+		REQUIRE(10.0f == test[1].value.FLOAT);
1845+		REQUIRE(UnitType::PX == test[1].type);
1846+	}
1847+
1848+	// content-box
1849+	SECTION("getUnit can parse content-box") {
1850+		std::vector<Unit> test = getUnit("content-box");
1851+		REQUIRE(UnitType::CONTENT == test[0].value.ENUM);
1852+		REQUIRE(UnitType::BOX_EDGE == test[0].type);
1853+	}
1854+
1855+	// padding-box
1856+	SECTION("getUnit can parse padding-box") {
1857+		std::vector<Unit> test = getUnit("padding-box");
1858+		REQUIRE(UnitType::PADDING == test[0].value.ENUM);
1859+		REQUIRE(UnitType::BOX_EDGE == test[0].type);
1860+	}
1861+
1862+	// border-box
1863+	SECTION("getUnit can parse border-box") {
1864+		std::vector<Unit> test = getUnit("border-box");
1865+		REQUIRE(UnitType::BORDER == test[0].value.ENUM);
1866+		REQUIRE(UnitType::BOX_EDGE == test[0].type);
1867+	}
1868+
1869+	// margin-box
1870+	SECTION("getUnit can parse margin-box") {
1871+		std::vector<Unit> test = getUnit("margin-box");
1872+		REQUIRE(UnitType::MARGIN == test[0].value.ENUM);
1873+		REQUIRE(UnitType::BOX_EDGE == test[0].type);
1874+	}
1875+
1876+	// fill-box
1877+	SECTION("getUnit can parse fill-box") {
1878+		std::vector<Unit> test = getUnit("fill-box");
1879+		REQUIRE(UnitType::FILL == test[0].value.ENUM);
1880+		REQUIRE(UnitType::BOX_EDGE == test[0].type);
1881+	}
1882+
1883+	// stroke-box
1884+	SECTION("getUnit can parse stroke-box") {
1885+		std::vector<Unit> test = getUnit("stroke-box");
1886+		REQUIRE(UnitType::STROKE == test[0].value.ENUM);
1887+		REQUIRE(UnitType::BOX_EDGE == test[0].type);
1888+	}
1889+
1890+	// view-box
1891+	SECTION("getUnit can parse view-box") {
1892+		std::vector<Unit> test = getUnit("view-box");
1893+		REQUIRE(UnitType::VIEW == test[0].value.ENUM);
1894+		REQUIRE(UnitType::BOX_EDGE == test[0].type);
1895+	}
1896+
1897+	// calc
1898+	SECTION("getUnit can parse calc") {
1899+		std::vector<Unit> test = getUnit("calc(10px)");
1900+		REQUIRE(UnitType::CALC == test[0].value.ENUM);
1901+		REQUIRE(UnitType::FUNC == test[0].type);
1902+
1903+		REQUIRE(10.0f == test[0].children[0].value.FLOAT);
1904+		REQUIRE(UnitType::PX == test[0].children[0].type);
1905+	}
1906+
1907+	SECTION("getUnit can parse calc with add") {
1908+		std::vector<Unit> test = getUnit("calc(10px + 14%)");
1909+		REQUIRE(UnitType::CALC == test[0].value.ENUM);
1910+		REQUIRE(UnitType::FUNC == test[0].type);
1911+
1912+		REQUIRE(10.0f == test[0].children[0].value.FLOAT);
1913+		REQUIRE(UnitType::PX == test[0].children[0].type);
1914+
1915+		REQUIRE(UnitType::ADD == test[0].children[1].value.ENUM);
1916+		REQUIRE(UnitType::OPERATOR == test[0].children[1].type);
1917+
1918+		REQUIRE(14.0f == test[0].children[2].value.FLOAT);
1919+		REQUIRE(UnitType::PERCENT == test[0].children[2].type);
1920+	}
1921+
1922+
1923+	// ---------- 1.  Whitespace handling ----------
1924+	SECTION("calc ignores leading / trailing spaces and tabs") {
1925+		std::vector<Unit> t = getUnit("  calc(  50vmin  )  ");
1926+		REQUIRE(t[0].type == UnitType::FUNC);
1927+		REQUIRE(t[0].value.ENUM == UnitType::CALC);
1928+		REQUIRE(t[0].children[0].value.FLOAT == 50.0f);
1929+		REQUIRE(t[0].children[0].type == UnitType::VMIN);
1930+	}
1931+
1932+	// ---------- 2.  Subtraction ----------
1933+	SECTION("calc with subtraction") {
1934+		std::vector<Unit> t = getUnit("calc(100% - 2em)");
1935+		REQUIRE(t[0].value.ENUM == UnitType::CALC);
1936+		REQUIRE(t[0].children[1].value.ENUM == UnitType::SUB);
1937+		REQUIRE(t[0].children[1].type == UnitType::OPERATOR);
1938+		REQUIRE(t[0].children[2].value.FLOAT == 2.0f);
1939+		REQUIRE(t[0].children[2].type == UnitType::EM);
1940+	}
1941+
1942+	// ---------- 3.  Multiplication ----------
1943+	SECTION("calc with multiply") {
1944+		std::vector<Unit> t = getUnit("calc(3 * 4.5rem)");
1945+		REQUIRE(t[0].children[1].value.ENUM == UnitType::MULT);
1946+		REQUIRE(t[0].children[2].value.FLOAT == 4.5f);
1947+		REQUIRE(t[0].children[2].type == UnitType::REM);
1948+	}
1949+
1950+	// ---------- 4.  Division ----------
1951+	SECTION("calc with divide") {
1952+		std::vector<Unit> t = getUnit("calc(90deg / 3)");
1953+		REQUIRE(t[0].children[1].value.ENUM == UnitType::DIV);
1954+		REQUIRE(t[0].children[2].value.FLOAT == 3.0f);
1955+		REQUIRE(t[0].children[0].type == UnitType::DEG);
1956+	}
1957+
1958+	// ---------- 5.  Chained operators ----------
1959+	SECTION("calc with three terms and two ops") {
1960+		std::vector<Unit> t = getUnit("calc(10px + 20px - 5px)");
1961+		REQUIRE(t[0].children.size() == 5);          // px  +  px  -  px
1962+		REQUIRE(t[0].children[1].value.ENUM == UnitType::ADD);
1963+		REQUIRE(t[0].children[3].value.ENUM == UnitType::SUB);
1964+	}
1965+
1966+	// ---------- 6.  Parentheses ----------
1967+	SECTION("calc nested parentheses") {
1968+		std::vector<Unit> t = getUnit("calc((2 * 3em) + 4px)");
1969+		// The first child should be the subtree for the parens
1970+		REQUIRE(t[0].type == UnitType::FUNC);
1971+		REQUIRE(t[0].children[0].value.ENUM == UnitType::GROUP);
1972+		REQUIRE(t[0].children[2].type == UnitType::PX);
1973+	}
1974+
1975+	// ---------- 7.  Constants ----------
1976+	SECTION("calc with pi") {
1977+		std::vector<Unit> t = getUnit("calc(pi * 30px)");
1978+		REQUIRE(t[0].children[0].value.ENUM == UnitType::PI);
1979+		REQUIRE(t[0].children[1].value.ENUM == UnitType::MULT);
1980+		REQUIRE(t[0].children[2].value.FLOAT == 30.0f);
1981+	}
1982+
1983+	// ---------- 8.  Negative numbers ----------
1984+	SECTION("calc with negative value") {
1985+		std::vector<Unit> t = getUnit("calc(-15vw + 10vw)");
1986+		REQUIRE(t[0].children[0].value.FLOAT == -15.0f);
1987+		REQUIRE(t[0].children[0].type == UnitType::VW);
1988+	}
1989+
1990+	// ---------- 9.  Zero unit ----------
1991+	SECTION("calc with zero px") {
1992+		std::vector<Unit> t = getUnit("calc(0px + 5px)");
1993+		REQUIRE(t[0].children[0].value.FLOAT == 0.0f);
1994+		REQUIRE(t[0].children[0].type == UnitType::PX);
1995+	}
1996+
1997+	// ---------- 10.  Scientific notation ----------
1998+	SECTION("calc with scientific notation") {
1999+		std::vector<Unit> t = getUnit("calc(1.2e3px)");
2000+		REQUIRE(t[0].children[0].value.FLOAT == 1200.0f);
2001+		REQUIRE(t[0].children[0].type == UnitType::PX);
2002+	}
2003+
2004+	// ---------- 11.  Many spaces around operator ----------
2005+	SECTION("calc with huge whitespace gap") {
2006+		std::vector<Unit> t = getUnit("calc(50px     *     2)");
2007+		REQUIRE(t[0].children[1].value.ENUM == UnitType::MULT);
2008+	}
2009+
2010+	// ---------- 12.  Double negation ----------
2011+	SECTION("calc with double negation") {
2012+		std::vector<Unit> t = getUnit("calc(10px - -5px)");
2013+		REQUIRE(t[0].children[2].value.FLOAT == -5.0f);
2014+		REQUIRE(t[0].children[1].value.ENUM == UnitType::SUB);
2015+	}
2016+
2017+	// ---------- 13.  Mixed units ----------
2018+	SECTION("calc mixed vw and px") {
2019+		std::vector<Unit> t = getUnit("calc(100vw - 20px)");
2020+		REQUIRE(t[0].children[0].type == UnitType::VW);
2021+		REQUIRE(t[0].children[2].type == UnitType::PX);
2022+	}
2023+
2024+	// ---------- 14.  Functions inside calc ----------
2025+	SECTION("calc with nested function") {
2026+		std::vector<Unit> t = getUnit("calc(min(10px, 20px) + 5px)");
2027+		// first child should be the min() subtree
2028+		REQUIRE(t[0].children[0].type == UnitType::FUNC);
2029+		REQUIRE(t[0].children[2].value.FLOAT == 5.0f);
2030+	}
2031+
2032+
2033+
2034 }