home readme diff tree note docs

0commit 27e9805b71324a7f0fe6b676884272db5afa2def
1Author: Mason Wright <mason@lakefox.net>
2Date:   Wed Sep 24 16:52:27 2025 -0600
3
4    Value parsing
5
6diff --git a/Makefile b/Makefile
7index b766151..095ad48 100755
8--- a/Makefile
9+++ b/Makefile
10@@ -27,6 +27,10 @@ all_tests: build/html_node_test build/css_selector_test build/html_parser_test b
11 	build/css_selector_test --skip-benchmarks
12 	build/html_parser_test --skip-benchmarks
13 	build/query_select_test --skip-benchmarks
14+	build/css_unit_test --skip-benchmarks
15+
16+build/css_unit_test: tests/css_unit.cc build/grim.o build/catch.o build/parser.o | build
17+	${CC} ${CCFLAGS} -Iinclude/ tests/css_unit.cc build/grim.o build/catch.o build/parser.o -o build/css_unit_test 
18 
19 build/html_node_test: tests/html_node.cc build/grim.o build/catch.o build/parser.o | build
20 	${CC} ${CCFLAGS} -Iinclude/ tests/html_node.cc build/grim.o build/catch.o build/parser.o -o build/html_node_test 
21diff --git a/docs/content/features/supported-css-selectors.md b/docs/content/features/supported-css-selectors.md
22index 9f8886d..26be4d8 100755
23--- a/docs/content/features/supported-css-selectors.md
24+++ b/docs/content/features/supported-css-selectors.md
25@@ -16,11 +16,11 @@
26 | Nesting | X | parseCSS can parse a CSS sheet, parseCSS can parse multiple selectors nested | parseCSS can parse a nested CSS rule without a &, parseCSS can parse a the parent of a nested CSS rule, parseCSS can parse a nested CSS rule with a pseudo element, parseCSS can parse a parent CSS rule with a property in between sub-classes, multi selector test | tests/css_parser.cc |
27 | & Nesting | X | parseCSS can parse a CSS sheet, parseCSS can parse multiple selectors nested | parseCSS can parse a nested CSS rule without a &, parseCSS can parse a the parent of a nested CSS rule, parseCSS can parse a nested CSS rule with a pseudo element, parseCSS can parse a double nested CSS rule with a pseudo element and &, parseCSS can parse a nested CSS rule with a &, parseCSS can parse a parent CSS rule with a property in between sub-classes, multi selector test | tests/css_parser.cc |
28 
29-## Combined
30+## Combinator
31 | Name | Support | Test Case | Section | Test File |
32 |-|-|-|-| - |
33-| Child Combined | X | testSelector | testSelector can match a child combined | tests/css_selector.cc |
34-| Next-Sibling Combined | X | testSelector | testSelector can match a next sibling combined | tests/css_selector.cc |
35+| Child Combinator | X | testSelector | testSelector can match a child combinator | tests/css_selector.cc |
36+| Next-Sibling Combinator | X | testSelector | testSelector can match a next sibling combinator | tests/css_selector.cc |
37 | Descendant Combinator | X | testSelector | testSelector can match a descendant combinator | tests/css_selector.cc |
38 | Subsequent-Sibling Combinator | X | testSelector | testSelector can match a subsequent-sibling combinator | tests/css_selector.cc |
39 
40@@ -147,7 +147,7 @@
41 | scroll-state | @container | | |
42 | container | | @container | | Computed using the overall container via window properties (orientation: landscape) and the content-box (min-width: 300px). Supports aspect-ratio, height, orientation, width |
43 
44-### Media Queries
45+## Media Queries
46 | Name | Support | Usage | Note |
47 |-|-|-|-|
48 | any-hover | X | window.setMouse(AnyPointer::Fine) | Will be set to AnyHover::Hover if AnyPointer is not AnyPointer::None, default AnyHover::Hover |
49@@ -178,3 +178,17 @@
50 | update | X | window.setUpdate(Update::Slow) | Defaults to Update::Fast |
51 | video-dynamic-range | * | | Only matches on "standard" |
52 | width | X | window.setWidth(int) | Supports width, min-width, max-width |
53+
54+## Colors
55+| Name | Support | Usage | Note |
56+|-|-|-|-|
57+| rgb | X | rgb(0-255, 0-255, 0-255) | You may use spaces instead of commas |
58+| rgba | X | rgba(0-255, 0-255, 0-255 / 0-100%) | The alpha value MUST be a perent ranging from 0-100. You may also use spaces instead of commas |
59+| hsl | X | hsl(0-360deg, 0-100%, 0-100%) | You may use spaces instead of commas |
60+| hsla | X | hsla(0-360deg, 0-100%, 0-100% / 0-100%) | The alpha value MUST be a perent ranging from 0-100. You may also use spaces instead of commas |
61+
62+
63+## Other
64+| Name | Support | Usage | Note |
65+|-|-|-|-|
66+| Variables | X | --<name> | |
67diff --git a/docs/content/index.md b/docs/content/index.md
68index cab4a27..06e37bb 100755
69--- a/docs/content/index.md
70+++ b/docs/content/index.md
71@@ -3,7 +3,7 @@
72 ## Cloning
73 
74 ```sh
75-git clone https://remote.grimui.com/grim.git
76+git clone https://grimui.com/grim.git
77 ```
78 
79 
80@@ -12,7 +12,7 @@ git clone https://remote.grimui.com/grim.git
81 ### Adding to a project
82 ```sh
83 cd my_project/
84-git submodule add https://remote.grimui.com/grim.git
85+git submodule add https://grimui.com/grim.git
86 ```
87 
88 ### Including
89diff --git a/include/grim.h b/include/grim.h
90index 173de14..3bbc557 100755
91--- a/include/grim.h
92+++ b/include/grim.h
93@@ -146,103 +146,42 @@ std::vector<std::vector<std::string>> parseSelectorParts(std::string_view);
94 bool testSelector(Node*, std::vector<std::vector<std::string>>);
95 
96 enum class UnitType {
97-	EQ, // :
98-	LT, // <
99-	GT, // >
100-	LE, // <=
101-	GE, // >=
102-	ADD, // +
103-	SUB, // -
104-	MULT, // *
105-	DIV, // /
106-	HEX,
107-	RGB,
108-	RGBA,
109-	HSL,
110-	HSLA,
111-	HWB,
112-	COLOR,
113-	AND,
114-	OR,
115-	NOT,
116-	ONLY,
117-	COMMA,
118-	PRINT,
119-	SCREEN,
120-	PX,
121-	EM,
122-	PERCENT, // %
123-	CM,
124-	CAP,
125-	CH,
126-	EX,
127-	IC,
128-	LH,
129-	RCAP,
130-	RCH,
131-	REM,
132-	REX,
133-	RIC,
134-	RLH,
135-	VH,
136-	VW,
137-	VMAX,
138-	VMIN,
139-	VB,
140-	VI,
141-	CQW,
142-	CQH,
143-	CQI,
144-	CQB,
145-	CQMIN,
146-	CQMAX,
147-	MM,
148-	Q,
149-	IN,
150-	PC,
151-	PT,
152-	FUNC,
153-	CALC, // need to add all
154-
155-}
156+	// Operator 
157+		EQ, LT, GT, LE, GE, ADD, SUB, MULT, DIV,
158+	// Color
159+		HEX, RGB, RGBA, HSL, HSLA, HWB, COLOR,
160+	// Logic
161+		AND, OR, NOT, ONLY, COMMA,
162+	// Length 
163+		PX, EM, PERCENT, CM, CAP, CH, EX, IC, LH, RCAP, RCH, REM, REX, RIC, RLH, VH, 
164+		VW, VMAX, VMIN, VB, VI, CQW, CQH, CQI, CQB, CQMIN, CQMAX, MM, Q, IN, PC, PT,
165+	// Function
166+		CALC,
167+	// Keyword
168+		PRINT, SCREEN, TOKEN, FUNC,
169+	// Property
170+		BACKGROUND_COLOR
171+};
172 
173+// 4 bytes
174 union UnitValue {
175 	UnitType ENUM;
176 	int INT;
177-	double DOUBLE;
178+	unsigned int UINT;
179+	float DOUBLE;
180 	bool BOOL;
181-}
182+};
183 
184+// 8 bytes + vector
185 struct Unit {
186 	UnitType type;
187 	UnitValue value;
188 	std::vector<Unit> children;
189-
190-}
191+};
192 
193 enum class SheetType {
194-	CONTAINER,
195-	COUNTER,
196-	FONTFACE,
197-	FONTFEATURE,
198-	SWASH,
199-	ANNOTATION,
200-	ORNAMENTS,
201-	STYLISTIC,
202-	STYLESET,
203-	CHARACTERVARIANT,
204-	FONTPALETTEVALUES,
205-	IMPORT,
206-	KEYFRAMES,
207-	LAYER,
208-	NAMESPACE,
209-	PAGE,
210-	PROPERTY,
211-	ROOT,
212-	SHEET,
213-	MEDIA,
214-	STARTINGSTYLE,
215-	STYLE
216+	CONTAINER,  COUNTER,    FONTFACE,  FONTFEATURE,  SWASH,  ANNOTATION,  ORNAMENTS,  STYLISTIC,  STYLESET,  CHARACTERVARIANT,  FONTPALETTEVALUES,
217+	IMPORT,     KEYFRAMES,  LAYER,     NAMESPACE,    PAGE,   PROPERTY,    ROOT,       SHEET,      MEDIA,     STARTINGSTYLE,     STYLE
218 };
219 
220 // A sheet is a group of styles, layers can be nested for specifity and the order can be set via
221@@ -431,9 +370,6 @@ class Window {
222 			if (height == 0) return 0.0;
223 			return static_cast<double>(width) / height;
224 		}
225-
226-		
227-
228 };
229 
230 #endif // NODE_H
231diff --git a/include/parser.h b/include/parser.h
232index afb935d..e2970ba 100755
233--- a/include/parser.h
234+++ b/include/parser.h
235@@ -16,5 +16,6 @@ std::unordered_map<std::string, std::string> parseAttributes(std::string_view& t
236 std::unique_ptr<Node> parseHTML(std::istream& inputStream);
237 std::unique_ptr<StyleHandler> parseCSS(std::istream& inputStream);
238 std::unordered_map<std::string, std::string> parseCSSInline(std::string inlineStyles);
239+Unit getUnit(std::string, std::string);
240 
241 #endif
242diff --git a/src/grim.cc b/src/grim.cc
243index 0fd1b6e..7e952ee 100755
244--- a/src/grim.cc
245+++ b/src/grim.cc
246@@ -786,11 +786,10 @@ std::unordered_map<std::string, std::string> StyleHandler::getStyles(Node* node)
247 const std::vector<std::unique_ptr<Node>> EMPTY_NODE_CHILDREN_VECTOR;
248 
249 /*
250-add: & subs
251-add: testing from inheritance 
252 at-rules need to be calculated at getstyles
253 make a function to testAtRules
254 */
255+
256 // node: The node you are checking the selector against
257 // selector: output of parseSelector
258 bool testSelector(Node* node, std::vector<std::vector<std::string>> selector) {
259diff --git a/src/parser.cc b/src/parser.cc
260index e3c68ca..b8fbf08 100755
261--- a/src/parser.cc
262+++ b/src/parser.cc
263@@ -13,7 +13,6 @@ std::unordered_map<std::string, std::string> parseAttributes(std::string_view to
264 	bool escaped = false;
265 	char quoteType = '"';
266 
267-
268 	std::string word;
269 	for (unsigned short i = 0; i < token.length(); i++) {
270 		char current = token[i];
271@@ -335,6 +334,120 @@ std::unique_ptr<Node> parseHTML(std::istream& inputStream) {
272 	return root;
273 }
274 
275+/*
276+	| Parses CSS color strings:
277+	|	rgba(255 125 245 / 10)
278+	|	rgba(255,125,245, 0.10)
279+	|	rgb(255,125,245)
280+	| 	etc..
281+	| value: color string
282+	| max: the value you want to wrap around
283+	|	if max was 255 and you had -10 then it would now
284+	| 	be 245. It also clamps the value to the max
285+*/
286+std::vector<double> parseColor(std::string value) {
287+	std::vector<double> colors;
288+
289+	bool set = false;
290+	int collector = 0;
291+
292+	bool decimal = false;
293+	int decimalLevel = 1;
294+	bool negitive = false;
295+
296+	for (size_t i = 0; i < value.length(); i++) {
297+		int v = value[i]-'0';
298+		if (v >= 0 && v <= 9) {
299+			collector = (collector*10) + v;
300+			set = true;
301+			if (decimal) {
302+				decimalLevel *= 10;
303+			}
304+		} else if (value[i] != '.' && set) {
305+			if (negitive) {
306+				collector *= -1;
307+			}
308+
309+			negitive = false;
310+			colors.push_back(collector/decimalLevel);
311+
312+			set = false;
313+			decimalLevel = 1;
314+			decimal = false;
315+			collector = 0;
316+		} else if (value[i] == '.') {
317+			decimal = true;
318+		} else if (value[i] == '-') {
319+			negitive = true;
320+		}
321+	}
322+
323+	if (colors.size() < 4) {
324+		colors.push_back(0);
325+	}
326+	return colors;
327+}
328+
329+double hueToRGB(double p, double q, double t) {
330+    if (t < 0) t += 1;
331+    if (t > 1) t -= 1;
332+    if (t < 1.0 / 6.0) return p + (q - p) * 6.0 * t;
333+    if (t < 1.0 / 2.0) return q;
334+    if (t < 2.0 / 3.0) return p + (q - p) * (2.0 / 3.0 - t) * 6.0;
335+    return p;
336+}
337+
338+Unit getUnit(std::string key, std::string value) {
339+	// NOTES:
340+	// | All colors are stored as rgba in hex format
341+
342+	Unit unit;
343+	UnitValue uv;
344+	std::string prefix = value.substr(0,4);
345+
346+	if (value[0] == '#') {
347+		uv.UINT = std::stoul(value.substr(1), nullptr, 16);
348+	} else if (prefix == "rgba" || prefix == "rgb(") {
349+		std::vector<double> colors = parseColor(value);
350+		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);
351+	} else if (prefix == "hsl(" || prefix == "hsla") {
352+		std::vector<double> colors = parseColor(value);
353+		// Convert the parsed values to rgba
354+		int r,g,b;
355+		double h = colors[0]/360.0;
356+		double s = colors[1]/100.0;
357+		double l = colors[2]/100.0;
358+		if (colors[1] == 0) {
359+			// Gray scale
360+			int lInt = l*255;
361+			r = lInt;
362+			g = lInt;
363+			b = lInt;
364+		} else {
365+			double q = (l < 0.5) ? l * (1.0 + s) : l + s - (l * s);
366+			double p = 2.0 * l - q;
367+			std::cout << q << std::endl;
368+			std::cout << p << std::endl;
369+			std::cout << h << std::endl;
370+			std::cout << s << std::endl;
371+			std::cout << l << std::endl;
372+			r = static_cast<int>(hueToRGB(p, q, h + 1.0 / 3.0) * 255.0);
373+			g = static_cast<int>(hueToRGB(p, q, h) * 255.0);
374+			b = static_cast<int>(hueToRGB(p, q, h - 1.0 / 3.0) * 255.0);
375+		}
376+
377+		// convert hsl to rgb/hex
378+		uv.UINT = (r << 24) | (g << 16) | (b << 8) | static_cast<int>(255*colors[3]/100);
379+	}
380+
381+	unit.value = uv;
382+	
383+	if (key == "background-color") {
384+		unit.type = UnitType::BACKGROUND_COLOR;
385+	}
386+	return unit;
387+}
388+
389 void StyleHandler::parseStream(std::istream& inputStream) {
390 	// nowhitespace: value(can have whitespace); = property
391 	// a selector name is anything before a { up to a ; or a }
392@@ -489,8 +602,6 @@ void StyleHandler::parseStream(std::istream& inputStream) {
393 				}
394 			} else {
395 				current->type = SheetType::STYLE;
396-
397-
398 			}
399 			
400 
401@@ -533,7 +644,7 @@ void StyleHandler::parseStream(std::istream& inputStream) {
402 					}
403 
404 				} else if (parts[0][0] == "@layer") {
405-					// need to add speficity
406+					// ISSUE: need to add speficity
407 
408 				}
409 
410@@ -544,13 +655,17 @@ void StyleHandler::parseStream(std::istream& inputStream) {
411 				std::string key;
412 				std::string value;
413 				bool keyFound = false;
414+				bool firstValue = false;
415 				for (size_t i = 0; i < buffer.length(); i++) {
416 					char c = buffer[i];
417 					if (!keyFound && c == ':') {
418 						keyFound = true;
419 					} else if (!std::isspace(c) && !keyFound) {
420 						key += c;
421-					} else if (keyFound  && !(std::isspace(c) && i < buffer.length()-1 && std::isspace(buffer[i+1]))) {
422+					} else if (!firstValue && keyFound && !std::isspace(c)) {
423+						firstValue = true;
424+						value += c;
425+					} else if (keyFound && firstValue && !(std::isspace(c) && i < buffer.length()-1 && std::isspace(buffer[i+1]))) {
426 						// if the key is found and the charactor isn't a ':'
427 						// 	also (if c is a space and the next c will be a space)
428 						// 	this is to auto trim duplicate spaces from the value