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