home
readme
diff
tree
note
docs
0commit 608704c468e4adf8e052f1695033bc225383485f
1Author: lakefox <mason@lakefox.net>
2Date: Mon Jan 19 10:54:23 2026 -0700
3
4 Wrath VM working
5
6diff --git a/Makefile b/Makefile
7index 32afc3f..5197f09 100755
8--- a/Makefile
9+++ b/Makefile
10@@ -46,6 +46,11 @@ run_%: build/%
11 @echo "--- Running $* ---"
12 @./build/$* --skip-benchmarks
13
14+bench_%: build/%
15+ @clear
16+ @echo "--- Running $* ---"
17+ @./build/$*
18+
19 build/css_unit_test: tests/css_unit.cc build/grim.o build/catch.o build/parser.o | build
20 ${CC} ${CCFLAGS} -Iinclude/ tests/css_unit.cc build/grim.o build/catch.o build/parser.o -o build/css_unit_test
21
22@@ -67,6 +72,9 @@ build/query_select_test: tests/query_select.cc build/grim.o build/parser.o build
23 build/windowunitlength_test: tests/windowunitlength.cc build/grim.o build/parser.o build/catch.o | build
24 ${CC} ${CCFLAGS} -Iinclude/ tests/windowunitlength.cc build/grim.o build/parser.o build/catch.o -o build/windowunitlength_test
25
26+build/unitvalue_test: tests/unitvalue.cc build/grim.o build/parser.o build/catch.o | build
27+ ${CC} ${CCFLAGS} -Iinclude/ tests/unitvalue.cc build/grim.o build/parser.o build/catch.o -o build/unitvalue_test
28+
29
30 build/catch.o: ./include/catch_amalgamated.cpp ./include/catch_amalgamated.hpp | build
31 ${CC} ${CCFLAGS} -Iinclude/ -c include/catch_amalgamated.cpp -o build/catch.o
32@@ -87,6 +95,10 @@ build/css_unit_playground: playground/css_unit.cc build/grim.o build/parser.o |
33 build/window_playground: playground/window.cc build/grim.o build/parser.o | build
34 ${CC} ${CCFLAGS} -Iinclude/ playground/window.cc build/grim.o build/parser.o -o build/window_playground
35
36+build/unitvalue_playground: playground/unitvalue.cc build/grim.o build/parser.o | build
37+ ${CC} ${CCFLAGS} -Iinclude/ playground/unitvalue.cc build/grim.o build/parser.o -o build/unitvalue_playground
38+
39+
40 # --- Prebuild ---
41
42 # use to prebuild the master.css
43diff --git a/include/grim.h b/include/grim.h
44index 19b86a2..e281f45 100755
45--- a/include/grim.h
46+++ b/include/grim.h
47@@ -17,7 +17,7 @@
48
49 enum class UnitType : short {
50 // Color
51- TEST, HEX, RGB, RGBA, HSL,
52+ TEST, HEX,
53 // Logic
54 AND, OR, NOT, ONLY, COMMA,
55 // Length
56@@ -72,8 +72,6 @@ enum class UnitType : short {
57 SHORTER_HUE, LONGER_HUE, INCREASING_HUE, DECREASING_HUE, HUE,
58 // Line Style
59 DOTTED, DASHED, SOLID, DOUBLE, GROOVE, RIDGE, INSET, OUTSET,
60- // Image
61- URL, IMAGE_SET, CROSS_FADE, ELEMENT, GRADIENT,
62 // Appearance *Not added*
63 MENU_LIST, TEXTFIELD, BASE_SELECT, BUTTON, CHECKBOX,
64 // Space
65@@ -84,24 +82,32 @@ enum class UnitType : short {
66 MULTIPLY, OVERLAY, DARKEN, LIGHTEN, COLOR_DODGE, COLOR_BURN, HARD_LIGHT, SOFT_LIGHT, DIFFERENCE, EXCLUSION, SATURATION, COLOR, LUMINOSITY,
67 // Background Clip
68 BORDER_BOX, PADDING_BOX, CONTENT_BOX, TEXT, BORDER_AREA,
69-
70-
71- // General Function
72- CALC, MIN, MAX, CLAMP, ROUND, MOD, PROGRESS, REMD, POW, SQRT, HYPOT, LOG, EXP, ABS,
73- SIGN, SIN, COS, TAN, ASIN, ACOS, ATAN, ATAN2, VAR,
74- BLUR, BRIGHTNESS, CONTRAST, DROP_SHADOW, GRAYSCALE, HUE_ROTATE, INVERT, OPACITY, SEPIA, SATURATE,
75- ATTR, TYPE, ENV, IF,
76- // Easing Functions
77- EASING_FUNCTION, LINEAR_EASING_FUNCTION, LINEAR, EASE, EASE_IN, EASE_OUT, EASE_IN_OUT, CUBIC_BEZIER_FUNCTION,
78- STEP_START, STEP_END, STEPS_FUNCTION, JUMP_START, JUMP_END, JUMP_NONE, JUMP_BOTH,
79- // Transform Functions
80- MATRIX, MATRIX3D, PERSPECTIVE, ROTATE, ROTATE3D, ROTATEX, ROTATEY, ROTATEZ, SCALE, SCALE3D, SCALEX, SCALEY, SCALEZ, TRANSLATE, TRANSLATE3D, TRANSLATEX, TRANSLATEY, TRANSLATEZ,
81- SKEW, SKEWX, SKEWY,
82 // Rounding
83 NEAREST, UP, DOWN, TO_ZERO,
84
85 // Keyword
86- PRINT, SCREEN, TOKEN, FUNC, GROUP, NO_SUPPORT, IMAGE, STRING
87+ PRINT, SCREEN, TOKEN, NO_SUPPORT, IMAGE, STRING, BOOL,
88+
89+ FUNC_START,
90+ // General Functions
91+ CALC = FUNC_START, MIN, MAX, CLAMP, ROUND, MOD, PROGRESS, REMD, POW, SQRT,
92+ HYPOT, LOG, EXP, ABS, SIGN, SIN, COS, TAN, ASIN, ACOS, ATAN, ATAN2, VAR,
93+ BLUR, BRIGHTNESS, CONTRAST, DROP_SHADOW, GRAYSCALE, HUE_ROTATE, INVERT,
94+ OPACITY, SEPIA, SATURATE, ATTR, TYPE, ENV, IF, FUNC,
95+
96+ // Easing Functions
97+ EASING_FUNCTION, LINEAR_EASING_FUNCTION, LINEAR, EASE, EASE_IN, EASE_OUT, EASE_IN_OUT, CUBIC_BEZIER_FUNCTION,
98+ STEP_START, STEP_END, STEPS_FUNCTION, JUMP_START, JUMP_END, JUMP_NONE, JUMP_BOTH,
99+
100+ // Transform Functions
101+ MATRIX, MATRIX3D, PERSPECTIVE, ROTATE, ROTATE3D, ROTATEX, ROTATEY, ROTATEZ,
102+ SCALE, SCALE3D, SCALEX, SCALEY, SCALEZ, TRANSLATE, TRANSLATE3D, TRANSLATEX,
103+ TRANSLATEY, TRANSLATEZ, SKEW, SKEWX, SKEWY,
104+
105+ RGB, RGBA, HSL,
106+
107+ // Image & Grouping
108+ URL, IMAGE_SET, CROSS_FADE, ELEMENT, GRADIENT, GROUP,
109 };
110
111
112@@ -115,11 +121,10 @@ union UnitValue {
113 char CHAR;
114 };
115
116-// 7 bytes
117+// 8 bytes
118 struct Unit {
119 UnitType type; // short
120 UnitValue value;
121- bool isFunc;
122 };
123
124 class Window;
125@@ -516,6 +521,6 @@ For the adapter, it will take a window as a argument to set the adapters capabil
126 Window createWindow();
127
128
129-float UnitLengthValue(Node*, Style*, const std::vector<Unit>&, size_t, size_t);
130+Unit UnitValue(Node*, Style*, const std::vector<Unit>&, size_t, size_t);
131
132 #endif // NODE_H
133diff --git a/playground/unitlengthvalue.cc b/playground/unitvalue.cc
134similarity index 87%
135rename from playground/unitlengthvalue.cc
136rename to playground/unitvalue.cc
137index d8a79a5..fc38e68 100644
138--- a/playground/unitlengthvalue.cc
139+++ b/playground/unitvalue.cc
140@@ -47,7 +47,9 @@ int main() {
141 document.cacheHeight(700);
142 document.cacheEM(16);
143
144- std::vector<Unit> units = getUnit("mod(13px, 5px)");
145+ std::vector<Unit> units = getUnit(&window.CSS, "(3 >= (1 + 5))");
146
147- std::cout << UnitLengthValue(&document, units, 0, units.size()) << std::endl;
148+ Unit u = UnitValue(&document, &window.CSS, units, 0, units.size());
149+
150+ std::cout << u.value.BOOL << std::endl;
151 }
152diff --git a/src/grim.cc b/src/grim.cc
153index bbd3df0..5895fe4 100755
154--- a/src/grim.cc
155+++ b/src/grim.cc
156@@ -904,12 +904,9 @@ bool executeQuery(Window* w, std::string p) {
157 if (v != MediaLookup.end()) {
158 matches = k->second == v->second;
159 }
160- }/* else {
161- if (k == "min-width") {
162-
163- matches = w->getWidth() >
164- }
165- }*/
166+ }
167+ } else {
168+ std::vector<Unit> units = getUnit(&w->CSS, key);
169 }
170 } else {
171 // Lookup keyword
172@@ -930,77 +927,93 @@ struct Range {
173 size_t end;
174 };
175
176-std::vector<Range> extractUnits(const std::vector<Unit>& units, size_t start, size_t end) {
177- std::vector<Range> u;
178- for (size_t i = start; i < end; i++) {
179- Unit unit = units[i];
180+Range getNextUnit(const std::vector<Unit>& units, size_t& offset, size_t end) {
181+ if (offset >= end) return {0, 0}; // Done
182
183- if (unit.isFunc) {
184- u.push_back({i,i+unit.value.INT});
185- i += unit.value.INT;
186- } else {
187- std::vector<Unit> s{unit};
188- u.push_back({i, i+1});
189- }
190- }
191- return u;
192+ size_t start = offset;
193+ const Unit& unit = units[offset];
194+
195+ if (unit.type > UnitType::FUNC_START) {
196+ // Range is the function unit + its body
197+ size_t argEnd = offset + unit.value.INT + 1;
198+ offset = argEnd; // Move offset past this function for next time
199+ return {start, argEnd};
200+ } else {
201+ offset += 1; // Move offset to next unit
202+ return {start, start + 1};
203+ }
204 }
205
206-/*
207- CAUTION: These values are only correct for top level media queries
208-*/
209-float UnitLengthValue(Node* n, Style* style, const std::vector<Unit>& units, size_t start, size_t end) {
210- float sum = 0;
211+Unit UnitValue(Node* n, Style* style, const std::vector<Unit>& units, size_t start, size_t end) {
212+ Unit result{};
213+ result.type = UnitType::NUMBER;
214+ result.value.FLOAT = 0.0f;
215 UnitType op = UnitType::ADD;
216
217+ Unit iv;
218+
219 for (size_t i = start; i < end; i++) {
220 Unit unit = units[i];
221 UnitType type = unit.type;
222
223- float value = 0;
224- float w = 0;
225- float h = 0;
226- float v1 = 0;
227- float v2 = 0;
228- float v3 = 0;
229- std::vector<Range> extracted;
230- UnitType ut;
231- std::string s = "";
232- bool b = false;
233
234 switch (type) {
235 case UnitType::ADD:
236 case UnitType::SUB:
237 case UnitType::DIV:
238 case UnitType::MULT:
239+ case UnitType::LT:
240+ case UnitType::LE:
241+ case UnitType::GT:
242+ case UnitType::GE:
243+ case UnitType::EQ:
244+ case UnitType::AND:
245+ case UnitType::OR:
246+ case UnitType::NOT:
247+ case UnitType::ONLY: {
248 op = type;
249- break;
250+ continue;
251+ }
252 // DEG, TURN are not length values but are treated as generic units (PX)
253 case UnitType::DEG:
254 case UnitType::TURN:
255 case UnitType::NUMBER:
256 // NUMBER is any untyped numbers "2". Treat as a pixel
257- case UnitType::PX:
258- value = unit.value.FLOAT;
259+ case UnitType::PX: {
260+ iv.type = UnitType::NUMBER;
261+ iv.value.FLOAT = unit.value.FLOAT;
262 break;
263- case UnitType::CM:
264- value = unit.value.FLOAT*37.8f;
265+ }
266+ case UnitType::CM: {
267+ iv.type = UnitType::NUMBER;
268+ iv.value.FLOAT = unit.value.FLOAT*37.8f;
269 break;
270- case UnitType::MM:
271- value = unit.value.FLOAT*3.78f;
272+ }
273+ case UnitType::MM: {
274+ iv.type = UnitType::NUMBER;
275+ iv.value.FLOAT = unit.value.FLOAT*3.78f;
276 break;
277- case UnitType::Q:
278- value = unit.value.FLOAT*0.94;
279+ }
280+ case UnitType::Q: {
281+ iv.type = UnitType::NUMBER;
282+ iv.value.FLOAT = unit.value.FLOAT*0.94f;
283 break;
284- case UnitType::IN:
285- value = unit.value.FLOAT*96;
286+ }
287+ case UnitType::IN: {
288+ iv.type = UnitType::NUMBER;
289+ iv.value.FLOAT = unit.value.FLOAT*96.0f;
290 break;
291- case UnitType::PC:
292- value = unit.value.FLOAT*16;
293+ }
294+ case UnitType::PC: {
295+ iv.type = UnitType::NUMBER;
296+ iv.value.FLOAT = unit.value.FLOAT*16.0f;
297 break;
298- case UnitType::PT:
299- value = unit.value.FLOAT*1.3;
300+ }
301+ case UnitType::PT: {
302+ iv.type = UnitType::NUMBER;
303+ iv.value.FLOAT = unit.value.FLOAT*1.3f;
304 break;
305+ }
306 case UnitType::EM:
307 case UnitType::REM:
308 case UnitType::CAP:
309@@ -1011,379 +1024,532 @@ float UnitLengthValue(Node* n, Style* style, const std::vector<Unit>& units, siz
310 case UnitType::RCAP:
311 case UnitType::RCH:
312 case UnitType::RIC:
313- case UnitType::RLH:
314- value = unit.value.FLOAT*n->getEM();
315+ case UnitType::RLH: {
316+ iv.type = UnitType::NUMBER;
317+ iv.value.FLOAT = unit.value.FLOAT*n->getEM();
318 break;
319+ }
320 case UnitType::EX:
321- case UnitType::REX:
322- value = unit.value.FLOAT*(n->getEM()/2);
323+ case UnitType::REX: {
324+ iv.type = UnitType::NUMBER;
325+ iv.value.FLOAT = unit.value.FLOAT*(n->getEM()*0.5f);
326 break;
327+ }
328 case UnitType::CQH:
329- case UnitType::VH:
330- value = n->getHeight()*(unit.value.FLOAT/100);
331+ case UnitType::VH: {
332+ iv.type = UnitType::NUMBER;
333+ iv.value.FLOAT = n->getHeight()*(unit.value.FLOAT*0.01f);
334 break;
335+ }
336 case UnitType::CQW:
337 case UnitType::CQI:
338 case UnitType::CQB:
339 case UnitType::PERCENT:
340- case UnitType::VW:
341- value = n->getWidth()*(unit.value.FLOAT/100);
342+ case UnitType::VW: {
343+ iv.type = UnitType::NUMBER;
344+ iv.value.FLOAT = n->getWidth()*(unit.value.FLOAT*0.01f);
345 break;
346+ }
347 case UnitType::VMAX:
348- case UnitType::CQMAX:
349- w = n->getWidth();
350- h = n->getHeight();
351+ case UnitType::CQMAX: {
352+ iv.type = UnitType::NUMBER;
353+ float w = n->getWidth();
354+ float h = n->getHeight();
355 if (w > h) {
356- value = unit.value.FLOAT*w;
357+ iv.value.FLOAT = unit.value.FLOAT*w;
358 } else {
359- value = unit.value.FLOAT*h;
360+ iv.value.FLOAT = unit.value.FLOAT*h;
361 }
362 break;
363+ }
364 case UnitType::VMIN:
365- case UnitType::CQMIN:
366- w = n->getWidth();
367- h = n->getHeight();
368+ case UnitType::CQMIN: {
369+ iv.type = UnitType::NUMBER;
370+ float w = n->getWidth();
371+ float h = n->getHeight();
372 if (w < h) {
373- value = unit.value.FLOAT*w;
374+ iv.value.FLOAT = unit.value.FLOAT*w;
375 } else {
376- value = unit.value.FLOAT*h;
377+ iv.value.FLOAT = unit.value.FLOAT*h;
378 }
379 break;
380+ }
381 case UnitType::VB:
382- case UnitType::VI:
383- value = unit.value.FLOAT*n->getWidth();
384+ case UnitType::VI: {
385+ iv.type = UnitType::NUMBER;
386+ iv.value.FLOAT = unit.value.FLOAT*n->getWidth();
387+ break;
388+ }
389+ case UnitType::GROUP: {
390+ iv = UnitValue(n, style, units, i+1, i+1+unit.value.INT);
391+ i += unit.value.INT;
392 break;
393- case UnitType::CALC:
394- value = UnitLengthValue(n, style, units, i+1, i+1+unit.value.INT);
395+ }
396+ case UnitType::CALC: {
397+ iv.type = UnitType::NUMBER;
398+ Unit u1 = UnitValue(n, style, units, i+1, i+1+unit.value.INT);
399+ iv.value.FLOAT = u1.value.FLOAT;
400 i += unit.value.INT;
401 break;
402- case UnitType::MIN:
403- extracted = extractUnits(units, i+1, i+1+unit.value.INT);
404+ }
405+ case UnitType::MIN: {
406+ iv.type = UnitType::NUMBER;
407
408- v1 = std::numeric_limits<float>::max(); // Store of the min value
409+ size_t start = i + 1;
410+ size_t end = i + 1 + unit.value.INT;
411
412- for (size_t j = 0; j < extracted.size(); j++) {
413- v2 = UnitLengthValue(n, style, units, extracted[j].start, extracted[j].end);
414+ Range r = getNextUnit(units, start, end);
415+ iv.value.FLOAT = UnitValue(n, style, units, r.start, r.end).value.FLOAT;
416
417- if (v2 < v1) {
418- v1 = v2;
419- }
420+ while (start < end) {
421+ r = getNextUnit(units, start, end);
422+ iv.value.FLOAT = std::min(iv.value.FLOAT, UnitValue(n, style, units, r.start, r.end).value.FLOAT);
423 }
424
425- value = v1;
426-
427 i += unit.value.INT;
428 break;
429- case UnitType::MAX:
430- extracted = extractUnits(units, i+1, i+1+unit.value.INT);
431+ }
432+ case UnitType::MAX: {
433+ iv.type = UnitType::NUMBER;
434
435- v1 = std::numeric_limits<float>::min(); // Store of the max value
436+ size_t start = i + 1;
437+ size_t end = i + 1 + unit.value.INT;
438
439- for (size_t j = 0; j < extracted.size(); j++) {
440- v2 = UnitLengthValue(n, style, units, extracted[j].start, extracted[j].end);
441+ Range r = getNextUnit(units, start, end);
442+ iv.value.FLOAT = UnitValue(n, style, units, r.start, r.end).value.FLOAT;
443
444- if (v2 > v1) {
445- v1 = v2;
446- }
447+ while (start < end) {
448+ r = getNextUnit(units, start, end);
449+ iv.value.FLOAT = std::max(iv.value.FLOAT, UnitValue(n, style, units, r.start, r.end).value.FLOAT);
450 }
451
452- value = v1;
453-
454 i += unit.value.INT;
455- break;
456- case UnitType::CLAMP:
457- extracted = extractUnits(units, i+1, i+1+unit.value.INT);
458+ break;
459+ }
460+ case UnitType::CLAMP: {
461+ iv.type = UnitType::NUMBER;
462
463- if (extracted.size() >= 3) {
464- v1 = UnitLengthValue(n, style, units, extracted[0].start, extracted[0].end);
465- v2 = UnitLengthValue(n, style, units, extracted[1].start, extracted[1].end);
466- v3 = UnitLengthValue(n, style, units, extracted[2].start, extracted[2].end);
467+ float args[3];
468+ int found = 0;
469+ size_t start = i + 1;
470+ size_t end = i + 1 + unit.value.INT;
471
472- if (v2 < v1) {
473- v2 = v1;
474- }
475- if (v2 > v3) {
476- v2 = v3;
477- }
478+ while (start < end) {
479+ Range r = getNextUnit(units, start, end);
480+ args[found++] = UnitValue(n, style, units, r.start, r.end).value.FLOAT;
481+ }
482
483- value = v2;
484+ if (found == 3) {
485+ iv.value.FLOAT = std::clamp(args[1], args[0], args[2]);
486+ } else {
487+ iv.value.FLOAT = args[0];
488 }
489
490 i += unit.value.INT;
491 break;
492- case UnitType::ROUND:
493- extracted = extractUnits(units, i+1, i+1+unit.value.INT);
494+ }
495+ case UnitType::ROUND: {
496+ iv.type = UnitType::NUMBER;
497
498- ut = UnitType::NEAREST;
499+ UnitType ut = UnitType::NEAREST;
500
501- if (extracted.size() == 3) {
502- ut = units[extracted[0].start].type;
503+ Unit args[3];
504+ int found = 0;
505+ size_t start = i + 1;
506+ size_t end = i + 1 + unit.value.INT;
507
508- v1 = UnitLengthValue(n, style, units, extracted[1].start, extracted[1].end);
509- v2 = UnitLengthValue(n, style, units, extracted[2].start, extracted[2].end);
510- } else if (extracted.size() == 2) {
511+ while (start < end) {
512+ Range r = getNextUnit(units, start, end);
513+ args[found++] = UnitValue(n, style, units, r.start, r.end);
514+ }
515+
516+ if (found == 3) {
517+ ut = args[0].type;
518+
519+ } else if (found == 2) {
520 ut = UnitType::NEAREST;
521
522- v1 = UnitLengthValue(n, style, units, extracted[0].start, extracted[0].end);
523- v2 = UnitLengthValue(n, style, units, extracted[1].start, extracted[1].end);
524- } else if (extracted.size() == 1) {
525+ args[2].value.FLOAT = args[1].value.FLOAT;
526+ args[1].value.FLOAT = args[0].value.FLOAT;
527+ } else if (found == 1) {
528 ut = UnitType::NEAREST;
529
530- v1 = UnitLengthValue(n, style, units, extracted[0].start, extracted[0].end);
531- v2 = 1.0f;
532+ args[1].value.FLOAT = args[0].value.FLOAT;
533+ args[2].value.FLOAT = 1.0f;
534 }
535
536
537 if (ut == UnitType::NEAREST) {
538- value = std::round(v1/v2)*v2;
539+ iv.value.FLOAT = std::round(args[1].value.FLOAT/args[2].value.FLOAT)*args[2].value.FLOAT;
540 } else if (ut == UnitType::UP) {
541- value = std::ceil(v1/v2)*v2;
542+ iv.value.FLOAT = std::ceil(args[1].value.FLOAT/args[2].value.FLOAT)*args[2].value.FLOAT;
543 } else if (ut == UnitType::DOWN) {
544- value = std::floor(v1/v2)*v2;
545+ iv.value.FLOAT = std::floor(args[1].value.FLOAT/args[2].value.FLOAT)*args[2].value.FLOAT;
546 } else if (ut == UnitType::TO_ZERO) {
547- value = std::trunc(v1/v2)*v2;
548+ iv.value.FLOAT = std::trunc(args[1].value.FLOAT/args[2].value.FLOAT)*args[2].value.FLOAT;
549 }
550
551 i += unit.value.INT;
552 break;
553- case UnitType::MOD:
554- extracted = extractUnits(units, i+1, i+1+unit.value.INT);
555+ }
556+ case UnitType::MOD: {
557+ iv.type = UnitType::NUMBER;
558
559- if (extracted.size() == 2) {
560- v1 = UnitLengthValue(n, style, units, extracted[0].start, extracted[0].end);
561- v2 = UnitLengthValue(n, style, units, extracted[1].start, extracted[1].end);
562+ float args[2];
563+ int found = 0;
564+ size_t start = i + 1;
565+ size_t end = i + 1 + unit.value.INT;
566
567- value = std::fmod(v1, v2);
568+ while (start < end) {
569+ Range r = getNextUnit(units, start, end);
570+ args[found++] = UnitValue(n, style, units, r.start, r.end).value.FLOAT;
571+ }
572+
573+ if (found == 2) {
574+ iv.value.FLOAT = std::fmod(args[0], args[1]);
575+ } else {
576+ iv.value.FLOAT = args[0];
577 }
578
579 i += unit.value.INT;
580 break;
581- case UnitType::PROGRESS:
582- extracted = extractUnits(units, i+1, i+1+unit.value.INT);
583+ }
584+ case UnitType::PROGRESS: {
585+ iv.type = UnitType::NUMBER;
586
587- if (extracted.size() >= 3) {
588- v1 = UnitLengthValue(n, style, units, extracted[0].start, extracted[0].end);
589- v2 = UnitLengthValue(n, style, units, extracted[1].start, extracted[1].end);
590- v3 = UnitLengthValue(n, style, units, extracted[2].start, extracted[2].end);
591+ float args[3];
592+ int found = 0;
593+ size_t start = i + 1;
594+ size_t end = i + 1 + unit.value.INT;
595
596+ while (start < end) {
597+ Range r = getNextUnit(units, start, end);
598+ args[found++] = UnitValue(n, style, units, r.start, r.end).value.FLOAT;
599+ }
600+
601+ if (found == 3) {
602 // (progress - progress start) / (progress end - progress start)
603- value = (v1-v2)/(v3-v2);
604+ iv.value.FLOAT = (args[0]-args[1])/(args[2]-args[1]);
605+ } else {
606+ iv.value.FLOAT = args[0];
607 }
608
609
610 i += unit.value.INT;
611 break;
612- case UnitType::REMD:
613- extracted = extractUnits(units, i+1, i+1+unit.value.INT);
614+ }
615+ case UnitType::REMD: {
616+ iv.type = UnitType::NUMBER;
617
618- if (extracted.size() == 2) {
619- v1 = UnitLengthValue(n, style, units, extracted[0].start, extracted[0].end);
620- v2 = UnitLengthValue(n, style, units, extracted[1].start, extracted[1].end);
621+ float args[2];
622+ int found = 0;
623+ size_t start = i + 1;
624+ size_t end = i + 1 + unit.value.INT;
625
626- value = std::fmodf(v1, v2);
627+ while (start < end) {
628+ Range r = getNextUnit(units, start, end);
629+ args[found++] = UnitValue(n, style, units, r.start, r.end).value.FLOAT;
630+ }
631+
632+ if (found == 2) {
633+ iv.value.FLOAT = std::fmodf(args[0], args[1]);
634+ } else {
635+ iv.value.FLOAT = args[0];
636 }
637
638 i += unit.value.INT;
639 break;
640- case UnitType::SIN:
641- v1 = UnitLengthValue(n, style, units, i+1, i+1+unit.value.INT);
642- value = std::sinf(v1);
643+ }
644+ case UnitType::SIN: {
645+ iv.type = UnitType::NUMBER;
646+ Unit u1 = UnitValue(n, style, units, i+1, i+1+unit.value.INT);
647+ iv.value.FLOAT = std::sinf(u1.value.FLOAT);
648 i += unit.value.INT;
649 break;
650- case UnitType::COS:
651- v1 = UnitLengthValue(n, style, units, i+1, i+1+unit.value.INT);
652- value = std::cosf(v1);
653+ }
654+ case UnitType::COS: {
655+ iv.type = UnitType::NUMBER;
656+ Unit u1 = UnitValue(n, style, units, i+1, i+1+unit.value.INT);
657+ iv.value.FLOAT = std::cosf(u1.value.FLOAT);
658 i += unit.value.INT;
659 break;
660- case UnitType::TAN:
661- v1 = UnitLengthValue(n, style, units, i+1, i+1+unit.value.INT);
662- value = std::tanf(v1);
663+ }
664+ case UnitType::TAN: {
665+ iv.type = UnitType::NUMBER;
666+ Unit u1 = UnitValue(n, style, units, i+1, i+1+unit.value.INT);
667+ iv.value.FLOAT = std::tanf(u1.value.FLOAT);
668 i += unit.value.INT;
669 break;
670- case UnitType::ASIN:
671- v1 = UnitLengthValue(n, style, units, i+1, i+1+unit.value.INT);
672- value = std::asinf(v1);
673+ }
674+ case UnitType::ASIN: {
675+ iv.type = UnitType::NUMBER;
676+ Unit u1 = UnitValue(n, style, units, i+1, i+1+unit.value.INT);
677+ iv.value.FLOAT = std::asinf(u1.value.FLOAT);
678 i += unit.value.INT;
679 break;
680- case UnitType::ACOS:
681- v1 = UnitLengthValue(n, style, units, i+1, i+1+unit.value.INT);
682- value = std::acosf(v1);
683+ }
684+ case UnitType::ACOS: {
685+ iv.type = UnitType::NUMBER;
686+ Unit u1 = UnitValue(n, style, units, i+1, i+1+unit.value.INT);
687+ iv.value.FLOAT = std::acosf(u1.value.FLOAT);
688 i += unit.value.INT;
689 break;
690- case UnitType::ATAN:
691- v1 = UnitLengthValue(n, style, units, i+1, i+1+unit.value.INT);
692- value = std::atanf(v1);
693+ }
694+ case UnitType::ATAN: {
695+ iv.type = UnitType::NUMBER;
696+ Unit u1 = UnitValue(n, style, units, i+1, i+1+unit.value.INT);
697+ iv.value.FLOAT = std::atanf(u1.value.FLOAT);
698 i += unit.value.INT;
699 break;
700- case UnitType::ATAN2:
701- extracted = extractUnits(units, i+1, i+1+unit.value.INT);
702+ }
703+ case UnitType::ATAN2: {
704+ iv.type = UnitType::NUMBER;
705+
706+ float args[2];
707+ int found = 0;
708+ size_t start = i + 1;
709+ size_t end = i + 1 + unit.value.INT;
710
711- if (extracted.size() == 2) {
712- v1 = UnitLengthValue(n, style, units, extracted[0].start, extracted[0].end);
713- v2 = UnitLengthValue(n, style, units, extracted[1].start, extracted[1].end);
714+ while (start < end) {
715+ Range r = getNextUnit(units, start, end);
716+ args[found++] = UnitValue(n, style, units, r.start, r.end).value.FLOAT;
717+ }
718
719- value = std::atan2f(v1, v2);
720+ if (found == 2) {
721+ iv.value.FLOAT = std::atan2f(args[0], args[1]);
722+ } else {
723+ iv.value.FLOAT = args[0];
724 }
725+
726 i += unit.value.INT;
727 break;
728- case UnitType::POW:
729- extracted = extractUnits(units, i+1, i+1+unit.value.INT);
730+ }
731+ case UnitType::POW: {
732+ iv.type = UnitType::NUMBER;
733
734- if (extracted.size() == 2) {
735- v1 = UnitLengthValue(n, style, units, extracted[0].start, extracted[0].end);
736- v2 = UnitLengthValue(n, style, units, extracted[1].start, extracted[1].end);
737+ float args[2];
738+ int found = 0;
739+ size_t start = i + 1;
740+ size_t end = i + 1 + unit.value.INT;
741
742- value = std::powf(v1, v2);
743+ while (start < end) {
744+ Range r = getNextUnit(units, start, end);
745+ args[found++] = UnitValue(n, style, units, r.start, r.end).value.FLOAT;
746 }
747+
748+ if (found == 2) {
749+ iv.value.FLOAT = std::powf(args[0], args[1]);
750+ } else {
751+ iv.value.FLOAT = args[0];
752+ }
753+
754 i += unit.value.INT;
755 break;
756- case UnitType::SQRT:
757- v1 = UnitLengthValue(n, style, units, i+1, i+1+unit.value.INT);
758- value = std::sqrtf(v1);
759+ }
760+ case UnitType::SQRT: {
761+ iv.type = UnitType::NUMBER;
762+ Unit u1 = UnitValue(n, style, units, i+1, i+1+unit.value.INT);
763+ iv.value.FLOAT = std::sqrtf(u1.value.FLOAT);
764 i += unit.value.INT;
765 break;
766- case UnitType::HYPOT:
767- extracted = extractUnits(units, i+1, i+1+unit.value.INT);
768+ }
769+ case UnitType::HYPOT: {
770+ iv.type = UnitType::NUMBER;
771
772- v2 = 0;
773- for (size_t j = 0; j < extracted.size(); j++) {
774- v1 = UnitLengthValue(n, style, units, extracted[j].start, extracted[j].end);
775
776- v2 += std::powf(v1, 2);
777+ float v = 0;
778+ size_t start = i + 1;
779+ size_t end = i + 1 + unit.value.INT;
780+
781+ while (start < end) {
782+ Range r = getNextUnit(units, start, end);
783+ float t = UnitValue(n, style, units, r.start, r.end).value.FLOAT;
784+ v += t * t;
785 }
786
787- value = std::sqrtf(v2);
788+ iv.value.FLOAT = std::sqrtf(v);
789
790 i += unit.value.INT;
791 break;
792- case UnitType::LOG:
793- extracted = extractUnits(units, i+1, i+1+unit.value.INT);
794+ }
795+ case UnitType::LOG: {
796+ iv.type = UnitType::NUMBER;
797
798- if (extracted.size() == 2) {
799- v1 = UnitLengthValue(n, style, units, extracted[0].start, extracted[0].end);
800- v2 = UnitLengthValue(n, style, units, extracted[1].start, extracted[1].end);
801+ float args[2];
802+ int found = 0;
803+ size_t start = i + 1;
804+ size_t end = i + 1 + unit.value.INT;
805
806- value = std::logf(v1) / std::logf(v2);
807- } else if (extracted.size() == 1) {
808- v1 = UnitLengthValue(n, style, units, extracted[0].start, extracted[0].end);
809+ while (start < end) {
810+ Range r = getNextUnit(units, start, end);
811+ args[found++] = UnitValue(n, style, units, r.start, r.end).value.FLOAT;
812+ }
813
814- value = std::logf(v1);
815+ if (found == 2) {
816+ iv.value.FLOAT = std::logf(args[0]) / std::logf(args[1]);
817+ } else if (found == 1) {
818+ iv.value.FLOAT = std::logf(args[0]);
819 }
820
821 i += unit.value.INT;
822 break;
823- case UnitType::EXP:
824- v1 = UnitLengthValue(n, style, units, i+1, i+1+unit.value.INT);
825- value = std::expf(v1);
826+ }
827+ case UnitType::EXP: {
828+ iv.type = UnitType::NUMBER;
829+ Unit u1 = UnitValue(n, style, units, i+1, i+1+unit.value.INT);
830+ iv.value.FLOAT = std::expf(u1.value.FLOAT);
831 i += unit.value.INT;
832 break;
833- case UnitType::ABS:
834- v1 = UnitLengthValue(n, style, units, i+1, i+1+unit.value.INT);
835-
836- if (v1 < 0) v1 *= -1;
837- value = v1;
838-
839+ }
840+ case UnitType::ABS: {
841+ iv.type = UnitType::NUMBER;
842+ Unit u1 = UnitValue(n, style, units, i+1, i+1+unit.value.INT);
843+ iv.value.FLOAT = std::fabsf(u1.value.FLOAT);
844 i += unit.value.INT;
845 break;
846- case UnitType::SIGN:
847- v1 = UnitLengthValue(n, style, units, i+1, i+1+unit.value.INT);
848-
849- if (v1 > 0) {
850- value = 1;
851- } else if (v1 < 0) {
852- value = -1;
853+ }
854+ case UnitType::SIGN: {
855+ iv.type = UnitType::NUMBER;
856+ Unit u1 = UnitValue(n, style, units, i+1, i+1+unit.value.INT);
857+
858+ if (u1.value.FLOAT > 0) {
859+ iv.value.FLOAT = 1;
860+ } else if (u1.value.FLOAT < 0) {
861+ iv.value.FLOAT = -1;
862 } else {
863- value = 0;
864+ iv.value.FLOAT = 0;
865 }
866
867 i += unit.value.INT;
868 break;
869- case UnitType::ATTR:
870- extracted = extractUnits(units, i+1, i+1+unit.value.INT);
871+ }
872+ case UnitType::ATTR: {
873+ Range args[3];
874+ int found = 0;
875+ size_t start = i + 1;
876+ size_t end = i + 1 + unit.value.INT;
877+
878+ while (start < end) {
879+ args[found++] = getNextUnit(units, start, end);
880+ }
881+
882+ Unit u1 = UnitValue(n, style, units, args[0].start, args[0].end);
883
884- if (units[extracted[0].start].type == UnitType::VAR) {
885- b = n->hasAttribute(style->variables.key(units[extracted[0].start].value.INT));
886- s = n->getAttribute(style->variables.key(units[extracted[0].start].value.INT));
887+ if (u1.type == UnitType::VAR) {
888+ bool has = n->hasAttribute(style->variables.key(u1.value.INT));
889+ std::string value = n->getAttribute(style->variables.key(u1.value.INT));
890
891- if (!s.empty() && b) {
892+ if (!value.empty() && has) {
893 // attr(size px, 10)
894 // s = n->getAttribute(size) size="20"
895- std::vector<Unit> us = getUnit(style, s);
896+ std::vector<Unit> us = getUnit(style, value);
897 // us = {NUMBER, 20}
898 // extracted[1] == {type: PX} || {type::TYPE}
899 // Add the type of the next if the type is inside of type(px) or just add the type px
900- if (extracted.size() > 1) {
901- if (units[extracted[1].start].type == UnitType::TYPE) {
902- us[0].type = units[extracted[1].start+1].type;
903+ if (found > 1) {
904+ Unit u2 = UnitValue(n, style, units, args[1].start, args[1].end);
905+
906+ if (u2.type == UnitType::TYPE) {
907+ us[0].type = units[args[1].start+1].type;
908 } else {
909- us[0].type = units[extracted[1].start].type;
910+ us[0].type = u2.type;
911 }
912 }
913
914- value = UnitLengthValue(n, style, us, 0, us.size());
915- } else if (extracted.size() == 3) {
916+ iv = UnitValue(n, style, us, 0, us.size());
917+ } else if (found == 3) {
918 // Default value
919- value = UnitLengthValue(n, style, units, extracted[6].start, extracted[2].end);
920+ iv = UnitValue(n, style, units, args[2].start, args[2].end);
921 }
922 }
923
924 i += unit.value.INT;
925 break;
926- case UnitType::VAR:
927- extracted = extractUnits(units, i+1, i+1+unit.value.INT);
928+ }
929+ case UnitType::VAR: {
930+ Unit args[2];
931+ int found = 0;
932+ size_t start = i + 1;
933+ size_t end = i + 1 + unit.value.INT;
934+
935+ while (start < end) {
936+ Range r = getNextUnit(units, start, end);
937+ args[found++] = UnitValue(n, style, units, r.start, r.end);
938+ }
939
940- if (units[extracted[0].start].type == UnitType::VAR) {
941+ if (args[0].type == UnitType::VAR) {
942 // Variables flow down during processing so theres no need to check above
943- std::vector<Unit> us = style->variables.at(units[extracted[0].start].value.INT);
944+ std::vector<Unit> us = style->variables.at(args[0].value.INT);
945
946 if (us.size() > 0) {
947- value = UnitLengthValue(n, style, us, 0, us.size());
948- } else if (extracted.size() == 2) {
949+ iv = UnitValue(n, style, us, 0, us.size());
950+ } else if (found == 2) {
951 // Default value
952- value = UnitLengthValue(n, style, units, extracted[1].start, extracted[1].end);
953+ iv = args[1];
954 }
955 }
956
957 i += unit.value.INT;
958 break;
959-
960-
961-
962-
963-
964-
965-
966-
967- // Why not make this just get unit value and return a unit?
968-
969-
970-
971-
972-
973-
974-
975- // Need to add variable injection and group handling
976+ }
977 }
978
979- if (op != type) {
980+ if (iv.type == UnitType::NUMBER) {
981 switch (op) {
982 case UnitType::ADD:
983- sum += value;
984+ result.value.FLOAT += iv.value.FLOAT;
985 break;
986 case UnitType::SUB:
987- sum -= value;
988+ result.value.FLOAT -= iv.value.FLOAT;
989 break;
990 case UnitType::DIV:
991- sum /= value;
992+ result.value.FLOAT /= iv.value.FLOAT;
993 break;
994 case UnitType::MULT:
995- sum *= value;
996+ result.value.FLOAT *= iv.value.FLOAT;
997+ break;
998+ case UnitType::LT:
999+ result.type = UnitType::BOOL;
1000+ result.value.BOOL = result.value.FLOAT < iv.value.FLOAT;
1001+ break;
1002+ case UnitType::GT:
1003+ result.type = UnitType::BOOL;
1004+ result.value.BOOL = result.value.FLOAT > iv.value.FLOAT;
1005+ break;
1006+ case UnitType::LE:
1007+ result.type = UnitType::BOOL;
1008+ result.value.BOOL = result.value.FLOAT <= iv.value.FLOAT;
1009+ break;
1010+ case UnitType::GE:
1011+ result.type = UnitType::BOOL;
1012+ result.value.BOOL = result.value.FLOAT >= iv.value.FLOAT;
1013+ break;
1014+ case UnitType::EQ:
1015+ result.type = UnitType::BOOL;
1016+ result.value.BOOL = result.value.FLOAT == iv.value.FLOAT;
1017 break;
1018 }
1019+ } else if (iv.type == UnitType::BOOL) {
1020+ result.type = UnitType::BOOL;
1021+ switch (op) {
1022+ case UnitType::AND:
1023+ result.value.BOOL = result.value.BOOL && iv.value.BOOL;
1024+ break;
1025+ case UnitType::OR:
1026+ result.value.BOOL = result.value.BOOL || iv.value.BOOL;
1027+ break;
1028+ case UnitType::NOT:
1029+ result.value.BOOL = !iv.value.BOOL;
1030+ break;
1031+ case UnitType::EQ:
1032+ result.value.BOOL = result.value.BOOL == iv.value.BOOL;
1033+ default:
1034+ result.value.BOOL = iv.value.BOOL;
1035+ }
1036 }
1037 }
1038-
1039- return sum;
1040+
1041+ return result;
1042 }
1043
1044 // Used for root node without a parent
1045diff --git a/src/parser.cc b/src/parser.cc
1046index e5f027a..fca724a 100755
1047--- a/src/parser.cc
1048+++ b/src/parser.cc
1049@@ -836,6 +836,13 @@ static const std::unordered_map<std::string_view, UnitType> kMatch{
1050 {"<", UnitType::LT},
1051 {">=", UnitType::GE},
1052 {"<=", UnitType::LE},
1053+ {"and", UnitType::AND},
1054+ {"&&", UnitType::AND},
1055+ {"or", UnitType::OR},
1056+ {"||", UnitType::OR},
1057+ {"not", UnitType::NOT},
1058+ {"!=", UnitType::NOT},
1059+ {"only", UnitType::ONLY},
1060 {"infinity", UnitType::INF},
1061 {"-infinity", UnitType::NEGINF},
1062 {"NaN", UnitType::NaN},
1063@@ -1058,7 +1065,6 @@ std::vector<Unit> getUnit(Style* s, std::string value) {
1064
1065 Unit unit{};
1066 unit.type = UnitType::STRING;
1067- unit.isFunc = false;
1068 unit.value.INT = s->strings.size()-1;
1069 units.push_back(unit);
1070
1071@@ -1099,10 +1105,11 @@ std::vector<Unit> getUnit(Style* s, std::string value) {
1072 std::vector<Unit> children = getUnit(s, value);
1073
1074 Unit unit;
1075- unit.isFunc = true;
1076
1077 if (auto it = kFunc.find(name); it != kFunc.end()) {
1078 unit.type = it->second;
1079+ } else {
1080+ unit.type = UnitType::FUNC;
1081 }
1082
1083 // The value of the function is the child count
1084@@ -1117,7 +1124,6 @@ std::vector<Unit> getUnit(Style* s, std::string value) {
1085 if (v != ',' && !std::isspace(v)) buffer.push_back(v);
1086
1087 Unit unit;
1088- unit.isFunc = false;
1089
1090 // Split by spaces into parts
1091
1092diff --git a/tests/unitvalue.cc b/tests/unitvalue.cc
1093new file mode 100644
1094index 0000000..aac413c
1095--- /dev/null
1096+++ b/tests/unitvalue.cc
1097@@ -0,0 +1,88 @@
1098+#include <catch_amalgamated.hpp>
1099+#include "../include/grim.h"
1100+#include "../include/parser.h"
1101+#include <string>
1102+#include <sstream>
1103+#include <vector>
1104+
1105+TEST_CASE("Benchmark UnitValue evaluation", "[performance][grim]") {
1106+ Window window = createWindow();
1107+
1108+ Node document = window.document;
1109+ document.cacheWidth(1000);
1110+ document.cacheHeight(700);
1111+ document.cacheEM(16);
1112+
1113+ SECTION("(3 >= min(1, 5)") {
1114+ std::vector<Unit> units = getUnit(&window.CSS, "(3 >= min(1, 5))");
1115+
1116+ REQUIRE(UnitValue(&document, &window.CSS, units, 0, units.size()).value.BOOL);
1117+ }
1118+}
1119+
1120+TEST_CASE("UnitValue Logic and Math", "[grim][logic]") {
1121+ Window window = createWindow();
1122+ Node document = window.document;
1123+ document.cacheWidth(1000); // 1vw = 10px
1124+ document.cacheHeight(500); // 1vh = 5px
1125+ document.cacheEM(16); // 1em = 16px
1126+
1127+ SECTION("Math Operators and Precedence (Group)") {
1128+ // (10 + 2 * 5) -> In your linear engine, without GROUP this is 60.
1129+ // With GROUP: (10 + (2 * 5)) = 20
1130+ std::vector<Unit> units = getUnit(&window.CSS, "(10 + (2 * 5))");
1131+ auto res = UnitValue(&document, &window.CSS, units, 0, units.size());
1132+ REQUIRE(res.value.FLOAT == 20.0f);
1133+ }
1134+
1135+ SECTION("CSS Units Conversion") {
1136+ // 1in = 96px, 1cm = 37.8px
1137+ std::vector<Unit> units = getUnit(&window.CSS, "1in + 4px");
1138+ auto res = UnitValue(&document, &window.CSS, units, 0, units.size());
1139+ REQUIRE(res.value.FLOAT == 100.0f);
1140+ }
1141+
1142+ SECTION("Viewport Units") {
1143+ // 50vw (500px) + 10vh (50px) = 550px
1144+ std::vector<Unit> units = getUnit(&window.CSS, "50vw + 10vh");
1145+ auto res = UnitValue(&document, &window.CSS, units, 0, units.size());
1146+ REQUIRE(res.value.FLOAT == 550.0f);
1147+ }
1148+
1149+ SECTION("Clamping and Min/Max") {
1150+ // clamp(100, 50, 200) -> 100 (50 is below min)
1151+ std::vector<Unit> units = getUnit(&window.CSS, "clamp(100, 50, 200)");
1152+ auto res = UnitValue(&document, &window.CSS, units, 0, units.size());
1153+ REQUIRE(res.value.FLOAT == 100.0f);
1154+
1155+ // max(10, 20, 30, 5) -> 30
1156+ units = getUnit(&window.CSS, "max(10, 20, 30, 5)");
1157+ res = UnitValue(&document, &window.CSS, units, 0, units.size());
1158+ REQUIRE(res.value.FLOAT == 30.0f);
1159+ }
1160+
1161+ SECTION("Logical Operators (Boolean)") {
1162+ // (10 > 5) AND (2 <= 2) -> true
1163+ std::vector<Unit> units = getUnit(&window.CSS, "(10 > 5) and (2 <= 2)");
1164+ auto res1 = UnitValue(&document, &window.CSS, units, 0, units.size());
1165+ REQUIRE(res1.type == UnitType::BOOL);
1166+ REQUIRE(res1.value.BOOL == true);
1167+
1168+ // (10 < 5) OR (1 == 1) -> true
1169+ units = getUnit(&window.CSS, "(10 < 5) or (1 == 1)");
1170+ auto res2 = UnitValue(&document, &window.CSS, units, 0, units.size());
1171+ REQUIRE(res2.value.BOOL == true);
1172+ }
1173+
1174+ SECTION("Deep Recursion Stress") {
1175+ // (((1 + 1) + 1) + 1) ...
1176+ std::string deep = "(1 + (1 + (1 + (1 + (1 + (1 + 1))))))";
1177+ std::vector<Unit> units = getUnit(&window.CSS, deep);
1178+
1179+ BENCHMARK("Deep Math Recursion") {
1180+ return UnitValue(&document, &window.CSS, units, 0, units.size());
1181+ };
1182+ }
1183+}
1184+
1185+