home
readme
diff
tree
note
docs
0commit f32d05e89bbdcc6113f6075f12f0c9cd460e2972
1Author: lakefox <mason@lakefox.net>
2Date: Thu Jan 22 22:11:13 2026 -0700
3
4 Wrath vars working
5
6diff --git a/:w b/:w
7new file mode 100644
8index 0000000..de2bf61
9--- /dev/null
10+++ b/:w
11@@ -0,0 +1,1748 @@
12+#include "grim.h"
13+#include <iostream>
14+#include <fstream>
15+#include <sstream>
16+#include <unordered_map>
17+#include <cctype>
18+#include <algorithm>
19+#include <cmath>
20+#include <string_view>
21+
22+struct SymbolTable {
23+ std::unordered_map<std::string, int> table{};
24+ int next = 5;
25+
26+ int get(std::string_view name) {
27+ auto it = table.find(std::string(name));
28+ if (it != table.end()) return it->second;
29+
30+ int id = next++;
31+ table[std::string(name)] = id;
32+ return id;
33+ }
34+};
35+
36+std::unordered_map<std::string, std::string> parseAttributes(std::string_view token) {
37+ std::unordered_map<std::string, std::string> attrs;
38+
39+ bool inQuote = false;
40+ bool escaped = false;
41+ char quoteType = '"';
42+
43+ std::string word;
44+ for (unsigned short i = 0; i < token.length(); i++) {
45+ char current = token[i];
46+ if ((current == '"' || current == '\'') && !escaped) {
47+ if (inQuote && current == quoteType) {
48+ inQuote = false;
49+ } else if (!inQuote) {
50+ inQuote = true;
51+ quoteType = current;
52+ }
53+ }
54+
55+ if (current == '\\') { //'
56+ escaped = true;
57+ continue;
58+ }
59+
60+ if ((!std::isspace(current) || inQuote) && i != token.length()-1) {
61+ word += current;
62+ } else {
63+ if (i == token.length()-1) {
64+ word += current;
65+ }
66+
67+ std::string name = "";
68+ std::string value = "";
69+ bool setValue = false;
70+
71+ for (auto c : word) {
72+ if (c == '=') {
73+ setValue = true;
74+ continue;
75+ }
76+
77+ if (setValue) {
78+ value += c;
79+ } else {
80+ name += c;
81+ }
82+ }
83+
84+ word = "";
85+
86+ std::string trimmedName = "";
87+ for (auto c : name) {
88+ if (!std::isspace(c)) {
89+ trimmedName += c;
90+ }
91+ }
92+
93+ if (attrs.size() == 0) {
94+ value = '"'+trimmedName+'"';
95+ trimmedName = "tagName";
96+ }
97+
98+ std::string trimmedValue = "";
99+ int sliceStart = 0;
100+ int sliceEnd = value.length();
101+
102+ if (value.length() >= 2) {
103+ for (unsigned short t = 0; t < value.length(); t++) {
104+ if (!std::isspace(value[t])) {
105+ sliceStart = t;
106+ break;
107+ }
108+ }
109+
110+ for (int t = value.length()-1; t >= 0; t--) {
111+ // Trim the trailing / on self closing elements if there isn't a space in between
112+ if (!std::isspace(value[t]) && value[t] != '/') {
113+ sliceEnd = t;
114+ break;
115+ }
116+ }
117+
118+ // Add and subtract 1 from each side to remove quotes
119+ for (unsigned short t = sliceStart+1; t < sliceEnd; t++) {
120+ trimmedValue += value[t];
121+ }
122+ }
123+
124+ if (trimmedValue.length() == 0) {
125+ trimmedValue = "";
126+ }
127+
128+ attrs[trimmedName] = trimmedValue;
129+ }
130+ }
131+
132+ return attrs;
133+}
134+
135+static const std::unordered_map<std::string, KeyType> keySearch {
136+ {"accent-color", KeyType::ACCENT_COLOR},
137+ {"align-content", KeyType::ALIGN_CONTENT},
138+ {"align-items", KeyType::ALIGN_ITEMS},
139+ {"align-self", KeyType::ALIGN_SELF},
140+ {"alignment-baseline", KeyType::ALIGNMENT_BASELINE},
141+ {"all", KeyType::ALL},
142+ {"anchor-name", KeyType::ANCHOR_NAME},
143+ {"animation-composition", KeyType::ANIMATION_COMPOSITION},
144+ {"animation-delay", KeyType::ANIMATION_DELAY},
145+ {"animation-direction", KeyType::ANIMATION_DIRECTION},
146+ {"animation-duration", KeyType::ANIMATION_DURATION},
147+ {"animation-fill-mode", KeyType::ANIMATION_FILL_MODE},
148+ {"animation-iteration-count", KeyType::ANIMATION_ITERATION_COUNT},
149+ {"animation-name", KeyType::ANIMATION_NAME},
150+ {"animation-play-state", KeyType::ANIMATION_PLAY_STATE},
151+ {"animation-range-end", KeyType::ANIMATION_RANGE_END},
152+ {"animation-range-start", KeyType::ANIMATION_RANGE_START},
153+ {"animation-range", KeyType::ANIMATION_RANGE},
154+ {"animation-timeline", KeyType::ANIMATION_TIMELINE},
155+ {"animation-timing-function", KeyType::ANIMATION_TIMING_FUNCTION},
156+ {"animation", KeyType::ANIMATION},
157+ {"appearance", KeyType::APPEARANCE},
158+ {"aspect-ratio", KeyType::ASPECT_RATIO},
159+ {"backdrop-filter", KeyType::BACKDROP_FILTER},
160+ {"backface-visibility", KeyType::BACKFACE_VISIBILITY},
161+ {"background-attachment", KeyType::BACKGROUND_ATTACHMENT},
162+ {"background-blend-mode", KeyType::BACKGROUND_BLEND_MODE},
163+ {"background-clip", KeyType::BACKGROUND_CLIP},
164+ {"background-color", KeyType::BACKGROUND_COLOR},
165+ {"background-image", KeyType::BACKGROUND_IMAGE},
166+ {"background-origin", KeyType::BACKGROUND_ORIGIN},
167+ {"background-position-x", KeyType::BACKGROUND_POSITION_X},
168+ {"background-position-y", KeyType::BACKGROUND_POSITION_Y},
169+ {"background-position", KeyType::BACKGROUND_POSITION},
170+ {"background-repeat", KeyType::BACKGROUND_REPEAT},
171+ {"background-size", KeyType::BACKGROUND_SIZE},
172+ {"background", KeyType::BACKGROUND},
173+ {"block-size", KeyType::BLOCK_SIZE},
174+ {"border-block-color", KeyType::BORDER_BLOCK_COLOR},
175+ {"border-block-end-color", KeyType::BORDER_BLOCK_END_COLOR},
176+ {"border-block-end-style", KeyType::BORDER_BLOCK_END_STYLE},
177+ {"border-block-end-width", KeyType::BORDER_BLOCK_END_WIDTH},
178+ {"border-block-end", KeyType::BORDER_BLOCK_END},
179+ {"border-block-start-color", KeyType::BORDER_BLOCK_START_COLOR},
180+ {"border-block-start-style", KeyType::BORDER_BLOCK_START_STYLE},
181+ {"border-block-start-width", KeyType::BORDER_BLOCK_START_WIDTH},
182+ {"border-block-start", KeyType::BORDER_BLOCK_START},
183+ {"border-block-style", KeyType::BORDER_BLOCK_STYLE},
184+ {"border-block-width", KeyType::BORDER_BLOCK_WIDTH},
185+ {"border-block", KeyType::BORDER_BLOCK},
186+ {"border-bottom-color", KeyType::BORDER_BOTTOM_COLOR},
187+ {"border-bottom-left-radius", KeyType::BORDER_BOTTOM_LEFT_RADIUS},
188+ {"border-bottom-right-radius", KeyType::BORDER_BOTTOM_RIGHT_RADIUS},
189+ {"border-bottom-style", KeyType::BORDER_BOTTOM_STYLE},
190+ {"border-bottom-width", KeyType::BORDER_BOTTOM_WIDTH},
191+ {"border-bottom", KeyType::BORDER_BOTTOM},
192+ {"border-collapse", KeyType::BORDER_COLLAPSE},
193+ {"border-color", KeyType::BORDER_COLOR},
194+ {"border-end-end-radius", KeyType::BORDER_END_END_RADIUS},
195+ {"border-end-start-radius", KeyType::BORDER_END_START_RADIUS},
196+ {"border-image-outset", KeyType::BORDER_IMAGE_OUTSET},
197+ {"border-image-repeat", KeyType::BORDER_IMAGE_REPEAT},
198+ {"border-image-slice", KeyType::BORDER_IMAGE_SLICE},
199+ {"border-image-source", KeyType::BORDER_IMAGE_SOURCE},
200+ {"border-image-width", KeyType::BORDER_IMAGE_WIDTH},
201+ {"border-image", KeyType::BORDER_IMAGE},
202+ {"border-inline-color", KeyType::BORDER_INLINE_COLOR},
203+ {"border-inline-end-color", KeyType::BORDER_INLINE_END_COLOR},
204+ {"border-inline-end-style", KeyType::BORDER_INLINE_END_STYLE},
205+ {"border-inline-end-width", KeyType::BORDER_INLINE_END_WIDTH},
206+ {"border-inline-end", KeyType::BORDER_INLINE_END},
207+ {"border-inline-start-color", KeyType::BORDER_INLINE_START_COLOR},
208+ {"border-inline-start-style", KeyType::BORDER_INLINE_START_STYLE},
209+ {"border-inline-start-width", KeyType::BORDER_INLINE_START_WIDTH},
210+ {"border-inline-start", KeyType::BORDER_INLINE_START},
211+ {"border-inline-style", KeyType::BORDER_INLINE_STYLE},
212+ {"border-inline-width", KeyType::BORDER_INLINE_WIDTH},
213+ {"border-inline", KeyType::BORDER_INLINE},
214+ {"border-left-color", KeyType::BORDER_LEFT_COLOR},
215+ {"border-left-style", KeyType::BORDER_LEFT_STYLE},
216+ {"border-left-width", KeyType::BORDER_LEFT_WIDTH},
217+ {"border-left", KeyType::BORDER_LEFT},
218+ {"border-radius", KeyType::BORDER_RADIUS},
219+ {"border-right-color", KeyType::BORDER_RIGHT_COLOR},
220+ {"border-right-style", KeyType::BORDER_RIGHT_STYLE},
221+ {"border-right-width", KeyType::BORDER_RIGHT_WIDTH},
222+ {"border-right", KeyType::BORDER_RIGHT},
223+ {"border-spacing", KeyType::BORDER_SPACING},
224+ {"border-start-end-radius", KeyType::BORDER_START_END_RADIUS},
225+ {"border-start-start-radius", KeyType::BORDER_START_START_RADIUS},
226+ {"border-style", KeyType::BORDER_STYLE},
227+ {"border-top-color", KeyType::BORDER_TOP_COLOR},
228+ {"border-top-left-radius", KeyType::BORDER_TOP_LEFT_RADIUS},
229+ {"border-top-right-radius", KeyType::BORDER_TOP_RIGHT_RADIUS},
230+ {"border-top-style", KeyType::BORDER_TOP_STYLE},
231+ {"border-top-width", KeyType::BORDER_TOP_WIDTH},
232+ {"border-top", KeyType::BORDER_TOP},
233+ {"border-width", KeyType::BORDER_WIDTH},
234+ {"border", KeyType::BORDER},
235+ {"bottom", KeyType::BOTTOM},
236+ {"box-align", KeyType::BOX_ALIGN},
237+ {"box-decoration-break", KeyType::BOX_DECORATION_BREAK},
238+ {"box-direction", KeyType::BOX_DIRECTION},
239+ {"box-flex-group", KeyType::BOX_FLEX_GROUP},
240+ {"box-flex", KeyType::BOX_FLEX},
241+ {"box-lines", KeyType::BOX_LINES},
242+ {"box-ordinal-group", KeyType::BOX_ORDINAL_GROUP},
243+ {"box-orient", KeyType::BOX_ORIENT},
244+ {"box-pack", KeyType::BOX_PACK},
245+ {"box-shadow", KeyType::BOX_SHADOW},
246+ {"box-sizing", KeyType::BOX_SIZING},
247+ {"break-after", KeyType::BREAK_AFTER},
248+ {"break-before", KeyType::BREAK_BEFORE},
249+ {"break-inside", KeyType::BREAK_INSIDE},
250+ {"caption-side", KeyType::CAPTION_SIDE},
251+ {"caret-color", KeyType::CARET_COLOR},
252+ {"clear", KeyType::CLEAR},
253+ {"clip-path", KeyType::CLIP_PATH},
254+ {"clip-rule", KeyType::CLIP_RULE},
255+ {"clip", KeyType::CLIP},
256+ {"color-interpolation-filters", KeyType::COLOR_INTERPOLATION_FILTERS},
257+ {"color-interpolation", KeyType::COLOR_INTERPOLATION},
258+ {"color-scheme", KeyType::COLOR_SCHEME},
259+ {"color", KeyType::COLOR},
260+ {"column-count", KeyType::COLUMN_COUNT},
261+ {"column-fill", KeyType::COLUMN_FILL},
262+ {"column-gap", KeyType::COLUMN_GAP},
263+ {"column-rule-color", KeyType::COLUMN_RULE_COLOR},
264+ {"column-rule-style", KeyType::COLUMN_RULE_STYLE},
265+ {"column-rule-width", KeyType::COLUMN_RULE_WIDTH},
266+ {"column-rule", KeyType::COLUMN_RULE},
267+ {"column-span", KeyType::COLUMN_SPAN},
268+ {"column-width", KeyType::COLUMN_WIDTH},
269+ {"columns", KeyType::COLUMNS},
270+ {"contain-intrinsic-block-size", KeyType::CONTAIN_INTRINSIC_BLOCK_SIZE},
271+ {"contain-intrinsic-height", KeyType::CONTAIN_INTRINSIC_HEIGHT},
272+ {"contain-intrinsic-inline-size", KeyType::CONTAIN_INTRINSIC_INLINE_SIZE},
273+ {"contain-intrinsic-size", KeyType::CONTAIN_INTRINSIC_SIZE},
274+ {"contain-intrinsic-width", KeyType::CONTAIN_INTRINSIC_WIDTH},
275+ {"contain", KeyType::CONTAIN},
276+ {"container-name", KeyType::CONTAINER_NAME},
277+ {"container-type", KeyType::CONTAINER_TYPE},
278+ {"container", KeyType::CONTAINER},
279+ {"content-visibility", KeyType::CONTENT_VISIBILITY},
280+ {"content", KeyType::CONTENT},
281+ {"counter-increment", KeyType::COUNTER_INCREMENT},
282+ {"counter-reset", KeyType::COUNTER_RESET},
283+ {"counter-set", KeyType::COUNTER_SET},
284+ {"cursor", KeyType::CURSOR},
285+ {"cx", KeyType::CX},
286+ {"cy", KeyType::CY},
287+ {"d", KeyType::D},
288+ {"direction", KeyType::DIRECTION},
289+ {"display", KeyType::DISPLAY},
290+ {"dominant-baseline", KeyType::DOMINANT_BASELINE},
291+ {"empty-cells", KeyType::EMPTY_CELLS},
292+ {"field-sizing", KeyType::FIELD_SIZING},
293+ {"fill-opacity", KeyType::FILL_OPACITY},
294+ {"fill-rule", KeyType::FILL_RULE},
295+ {"fill", KeyType::FILL},
296+ {"filter", KeyType::FILTER},
297+ {"flex-basis", KeyType::FLEX_BASIS},
298+ {"flex-direction", KeyType::FLEX_DIRECTION},
299+ {"flex-flow", KeyType::FLEX_FLOW},
300+ {"flex-grow", KeyType::FLEX_GROW},
301+ {"flex-shrink", KeyType::FLEX_SHRINK},
302+ {"flex-wrap", KeyType::FLEX_WRAP},
303+ {"flex", KeyType::FLEX},
304+ {"float", KeyType::FLOAT},
305+ {"flood-color", KeyType::FLOOD_COLOR},
306+ {"flood-opacity", KeyType::FLOOD_OPACITY},
307+ {"font-family", KeyType::FONT_FAMILY},
308+ {"font-feature-settings", KeyType::FONT_FEATURE_SETTINGS},
309+ {"font-kerning", KeyType::FONT_KERNING},
310+ {"font-language-override", KeyType::FONT_LANGUAGE_OVERRIDE},
311+ {"font-optical-sizing", KeyType::FONT_OPTICAL_SIZING},
312+ {"font-palette", KeyType::FONT_PALETTE},
313+ {"font-size-adjust", KeyType::FONT_SIZE_ADJUST},
314+ {"font-size", KeyType::FONT_SIZE},
315+ {"font-smooth", KeyType::FONT_SMOOTH},
316+ {"font-stretch", KeyType::FONT_STRETCH},
317+ {"font-style", KeyType::FONT_STYLE},
318+ {"font-synthesis-position", KeyType::FONT_SYNTHESIS_POSITION},
319+ {"font-synthesis-small-caps", KeyType::FONT_SYNTHESIS_SMALL_CAPS},
320+ {"font-synthesis-style", KeyType::FONT_SYNTHESIS_STYLE},
321+ {"font-synthesis-weight", KeyType::FONT_SYNTHESIS_WEIGHT},
322+ {"font-synthesis", KeyType::FONT_SYNTHESIS},
323+ {"font-variant-alternates", KeyType::FONT_VARIANT_ALTERNATES},
324+ {"font-variant-caps", KeyType::FONT_VARIANT_CAPS},
325+ {"font-variant-east-asian", KeyType::FONT_VARIANT_EAST_ASIAN},
326+ {"font-variant-emoji", KeyType::FONT_VARIANT_EMOJI},
327+ {"font-variant-ligatures", KeyType::FONT_VARIANT_LIGATURES},
328+ {"font-variant-numeric", KeyType::FONT_VARIANT_NUMERIC},
329+ {"font-variant-position", KeyType::FONT_VARIANT_POSITION},
330+ {"font-variant", KeyType::FONT_VARIANT},
331+ {"font-variation-settings", KeyType::FONT_VARIATION_SETTINGS},
332+ {"font-weight", KeyType::FONT_WEIGHT},
333+ {"font", KeyType::FONT},
334+ {"forced-color-adjust", KeyType::FORCED_COLOR_ADJUST},
335+ {"gap", KeyType::GAP},
336+ {"grid-area", KeyType::GRID_AREA},
337+ {"grid-auto-columns", KeyType::GRID_AUTO_COLUMNS},
338+ {"grid-auto-flow", KeyType::GRID_AUTO_FLOW},
339+ {"grid-auto-rows", KeyType::GRID_AUTO_ROWS},
340+ {"grid-column-end", KeyType::GRID_COLUMN_END},
341+ {"grid-column-start", KeyType::GRID_COLUMN_START},
342+ {"grid-column", KeyType::GRID_COLUMN},
343+ {"grid-row-end", KeyType::GRID_ROW_END},
344+ {"grid-row-start", KeyType::GRID_ROW_START},
345+ {"grid-row", KeyType::GRID_ROW},
346+ {"grid-template-areas", KeyType::GRID_TEMPLATE_AREAS},
347+ {"grid-template-columns", KeyType::GRID_TEMPLATE_COLUMNS},
348+ {"grid-template-rows", KeyType::GRID_TEMPLATE_ROWS},
349+ {"grid-template", KeyType::GRID_TEMPLATE},
350+ {"grid", KeyType::GRID},
351+ {"hanging-punctuation", KeyType::HANGING_PUNCTUATION},
352+ {"height", KeyType::HEIGHT},
353+ {"hyphenate-character", KeyType::HYPHENATE_CHARACTER},
354+ {"hyphenate-limit-chars", KeyType::HYPHENATE_LIMIT_CHARS},
355+ {"hyphens", KeyType::HYPHENS},
356+ {"image-orientation", KeyType::IMAGE_ORIENTATION},
357+ {"image-rendering", KeyType::IMAGE_RENDERING},
358+ {"image-resolution", KeyType::IMAGE_RESOLUTION},
359+ {"initial-letter", KeyType::INITIAL_LETTER},
360+ {"inline-size", KeyType::INLINE_SIZE},
361+ {"inset-block-end", KeyType::INSET_BLOCK_END},
362+ {"inset-block-start", KeyType::INSET_BLOCK_START},
363+ {"inset-block", KeyType::INSET_BLOCK},
364+ {"inset-block", KeyType::INSET_BLOCK},
365+ {"inset-inline-end", KeyType::INSET_INLINE_END},
366+ {"inset-inline-start", KeyType::INSET_INLINE_START},
367+ {"inset-inline", KeyType::INSET_INLINE},
368+ {"inset-inline", KeyType::INSET_INLINE},
369+ {"inset", KeyType::INSET},
370+ {"inset", KeyType::INSET},
371+ {"interpolate-size", KeyType::INTERPOLATE_SIZE},
372+ {"isolation", KeyType::ISOLATION},
373+ {"justify-content", KeyType::JUSTIFY_CONTENT},
374+ {"justify-items", KeyType::JUSTIFY_ITEMS},
375+ {"justify-self", KeyType::JUSTIFY_SELF},
376+ {"left", KeyType::LEFT},
377+ {"letter-spacing", KeyType::LETTER_SPACING},
378+ {"lighting-color", KeyType::LIGHTING_COLOR},
379+ {"line-break", KeyType::LINE_BREAK},
380+ {"line-clamp", KeyType::LINE_CLAMP},
381+ {"line-height-step", KeyType::LINE_HEIGHT_STEP},
382+ {"line-height", KeyType::LINE_HEIGHT},
383+ {"list-style-image", KeyType::LIST_STYLE_IMAGE},
384+ {"list-style-position", KeyType::LIST_STYLE_POSITION},
385+ {"list-style-type", KeyType::LIST_STYLE_TYPE},
386+ {"list-style", KeyType::LIST_STYLE},
387+ {"list-style", KeyType::LIST_STYLE},
388+ {"margin-block-end", KeyType::MARGIN_BLOCK_END},
389+ {"margin-block-start", KeyType::MARGIN_BLOCK_START},
390+ {"margin-block", KeyType::MARGIN_BLOCK},
391+ {"margin-bottom", KeyType::MARGIN_BOTTOM},
392+ {"margin-inline-end", KeyType::MARGIN_INLINE_END},
393+ {"margin-inline-start", KeyType::MARGIN_INLINE_START},
394+ {"margin-inline", KeyType::MARGIN_INLINE},
395+ {"margin-left", KeyType::MARGIN_LEFT},
396+ {"margin-right", KeyType::MARGIN_RIGHT},
397+ {"margin-top", KeyType::MARGIN_TOP},
398+ {"margin-trim", KeyType::MARGIN_TRIM},
399+ {"margin", KeyType::MARGIN},
400+ {"marker-end", KeyType::MARKER_END},
401+ {"marker-mid", KeyType::MARKER_MID},
402+ {"marker-start", KeyType::MARKER_START},
403+ {"marker", KeyType::MARKER},
404+ {"mask-border-mode", KeyType::MASK_BORDER_MODE},
405+ {"mask-border-outset", KeyType::MASK_BORDER_OUTSET},
406+ {"mask-border-repeat", KeyType::MASK_BORDER_REPEAT},
407+ {"mask-border-slice", KeyType::MASK_BORDER_SLICE},
408+ {"mask-border-source", KeyType::MASK_BORDER_SOURCE},
409+ {"mask-border-width", KeyType::MASK_BORDER_WIDTH},
410+ {"mask-border", KeyType::MASK_BORDER},
411+ {"mask-clip", KeyType::MASK_CLIP},
412+ {"mask-composite", KeyType::MASK_COMPOSITE},
413+ {"mask-image", KeyType::MASK_IMAGE},
414+ {"mask-mode", KeyType::MASK_MODE},
415+ {"mask-origin", KeyType::MASK_ORIGIN},
416+ {"mask-position", KeyType::MASK_POSITION},
417+ {"mask-repeat", KeyType::MASK_REPEAT},
418+ {"mask-size", KeyType::MASK_SIZE},
419+ {"mask-type", KeyType::MASK_TYPE},
420+ {"mask", KeyType::MASK},
421+ {"math-depth", KeyType::MATH_DEPTH},
422+ {"math-shift", KeyType::MATH_SHIFT},
423+ {"math-style", KeyType::MATH_STYLE},
424+ {"max-block-size", KeyType::MAX_BLOCK_SIZE},
425+ {"max-height", KeyType::MAX_HEIGHT},
426+ {"max-inline-size", KeyType::MAX_INLINE_SIZE},
427+ {"max-width", KeyType::MAX_WIDTH},
428+ {"min-block-size", KeyType::MIN_BLOCK_SIZE},
429+ {"min-height", KeyType::MIN_HEIGHT},
430+ {"min-inline-size", KeyType::MIN_INLINE_SIZE},
431+ {"min-width", KeyType::MIN_WIDTH},
432+ {"mix-blend-mode", KeyType::MIX_BLEND_MODE},
433+ {"object-fit", KeyType::OBJECT_FIT},
434+ {"object-position", KeyType::OBJECT_POSITION},
435+ {"offset-anchor", KeyType::OFFSET_ANCHOR},
436+ {"offset-distance", KeyType::OFFSET_DISTANCE},
437+ {"offset-path", KeyType::OFFSET_PATH},
438+ {"offset-position", KeyType::OFFSET_POSITION},
439+ {"offset-rotate", KeyType::OFFSET_ROTATE},
440+ {"offset", KeyType::OFFSET},
441+ {"opacity", KeyType::OPACITY},
442+ {"order", KeyType::ORDER},
443+ {"orphans", KeyType::ORPHANS},
444+ {"outline-color", KeyType::OUTLINE_COLOR},
445+ {"outline-offset", KeyType::OUTLINE_OFFSET},
446+ {"outline-style", KeyType::OUTLINE_STYLE},
447+ {"outline-width", KeyType::OUTLINE_WIDTH},
448+ {"outline", KeyType::OUTLINE},
449+ {"overflow-anchor", KeyType::OVERFLOW_ANCHOR},
450+ {"overflow-block", KeyType::OVERFLOW_BLOCK},
451+ {"overflow-clip-margin", KeyType::OVERFLOW_CLIP_MARGIN},
452+ {"overflow-inline", KeyType::OVERFLOW_INLINE},
453+ {"overflow-wrap", KeyType::OVERFLOW_WRAP},
454+ {"overflow-x", KeyType::OVERFLOW_X},
455+ {"overflow-y", KeyType::OVERFLOW_Y},
456+ {"overflow", KeyType::OVERFLOW},
457+ {"overlay", KeyType::OVERLAY},
458+ {"overscroll-behavior-block", KeyType::OVERSCROLL_BEHAVIOR_BLOCK},
459+ {"overscroll-behavior-inline", KeyType::OVERSCROLL_BEHAVIOR_INLINE},
460+ {"overscroll-behavior-x", KeyType::OVERSCROLL_BEHAVIOR_X},
461+ {"overscroll-behavior-y", KeyType::OVERSCROLL_BEHAVIOR_Y},
462+ {"overscroll-behavior", KeyType::OVERSCROLL_BEHAVIOR},
463+ {"padding-block-end", KeyType::PADDING_BLOCK_END},
464+ {"padding-block-start", KeyType::PADDING_BLOCK_START},
465+ {"padding-block", KeyType::PADDING_BLOCK},
466+ {"padding-bottom", KeyType::PADDING_BOTTOM},
467+ {"padding-inline-end", KeyType::PADDING_INLINE_END},
468+ {"padding-inline-start", KeyType::PADDING_INLINE_START},
469+ {"padding-inline", KeyType::PADDING_INLINE},
470+ {"padding-left", KeyType::PADDING_LEFT},
471+ {"padding-right", KeyType::PADDING_RIGHT},
472+ {"padding-top", KeyType::PADDING_TOP},
473+ {"padding", KeyType::PADDING},
474+ {"page-break-after", KeyType::PAGE_BREAK_AFTER},
475+ {"page-break-before", KeyType::PAGE_BREAK_BEFORE},
476+ {"page-break-inside", KeyType::PAGE_BREAK_INSIDE},
477+ {"page", KeyType::PAGE},
478+ {"paint-order", KeyType::PAINT_ORDER},
479+ {"perspective-origin", KeyType::PERSPECTIVE_ORIGIN},
480+ {"perspective", KeyType::PERSPECTIVE},
481+ {"place-content", KeyType::PLACE_CONTENT},
482+ {"place-items", KeyType::PLACE_ITEMS},
483+ {"place-self", KeyType::PLACE_SELF},
484+ {"pointer-events", KeyType::POINTER_EVENTS},
485+ {"position-anchor", KeyType::POSITION_ANCHOR},
486+ {"position-area", KeyType::POSITION_AREA},
487+ {"position-try-fallbacks", KeyType::POSITION_TRY_FALLBACKS},
488+ {"position-try-order", KeyType::POSITION_TRY_ORDER},
489+ {"position-try", KeyType::POSITION_TRY},
490+ {"position-visibility", KeyType::POSITION_VISIBILITY},
491+ {"position", KeyType::POSITION},
492+ {"print-color-adjust", KeyType::PRINT_COLOR_ADJUST},
493+ {"quotes", KeyType::QUOTES},
494+ {"r", KeyType::R},
495+ {"reading-flow", KeyType::READING_FLOW},
496+ {"reading-order", KeyType::READING_ORDER},
497+ {"resize", KeyType::RESIZE},
498+ {"right", KeyType::RIGHT},
499+ {"rotate", KeyType::ROTATE},
500+ {"row-gap", KeyType::ROW_GAP},
501+ {"ruby-align", KeyType::RUBY_ALIGN},
502+ {"ruby-position", KeyType::RUBY_POSITION},
503+ {"rx", KeyType::RX},
504+ {"ry", KeyType::RY},
505+ {"scale", KeyType::SCALE},
506+ {"scroll-behavior", KeyType::SCROLL_BEHAVIOR},
507+ {"scroll-margin-block-end", KeyType::SCROLL_MARGIN_BLOCK_END},
508+ {"scroll-margin-block-start", KeyType::SCROLL_MARGIN_BLOCK_START},
509+ {"scroll-margin-block", KeyType::SCROLL_MARGIN_BLOCK},
510+ {"scroll-margin-bottom", KeyType::SCROLL_MARGIN_BOTTOM},
511+ {"scroll-margin-inline-end", KeyType::SCROLL_MARGIN_INLINE_END},
512+ {"scroll-margin-inline-start", KeyType::SCROLL_MARGIN_INLINE_START},
513+ {"scroll-margin-inline", KeyType::SCROLL_MARGIN_INLINE},
514+ {"scroll-margin-left", KeyType::SCROLL_MARGIN_LEFT},
515+ {"scroll-margin-right", KeyType::SCROLL_MARGIN_RIGHT},
516+ {"scroll-margin-top", KeyType::SCROLL_MARGIN_TOP},
517+ {"scroll-margin", KeyType::SCROLL_MARGIN},
518+ {"scroll-marker-group", KeyType::SCROLL_MARKER_GROUP},
519+ {"scroll-padding-block-end", KeyType::SCROLL_PADDING_BLOCK_END},
520+ {"scroll-padding-block-start", KeyType::SCROLL_PADDING_BLOCK_START},
521+ {"scroll-padding-block", KeyType::SCROLL_PADDING_BLOCK},
522+ {"scroll-padding-bottom", KeyType::SCROLL_PADDING_BOTTOM},
523+ {"scroll-padding-inline-end", KeyType::SCROLL_PADDING_INLINE_END},
524+ {"scroll-padding-inline-start", KeyType::SCROLL_PADDING_INLINE_START},
525+ {"scroll-padding-inline", KeyType::SCROLL_PADDING_INLINE},
526+ {"scroll-padding-left", KeyType::SCROLL_PADDING_LEFT},
527+ {"scroll-padding-right", KeyType::SCROLL_PADDING_RIGHT},
528+ {"scroll-padding-top", KeyType::SCROLL_PADDING_TOP},
529+ {"scroll-padding", KeyType::SCROLL_PADDING},
530+ {"scroll-snap-align", KeyType::SCROLL_SNAP_ALIGN},
531+ {"scroll-snap-stop", KeyType::SCROLL_SNAP_STOP},
532+ {"scroll-snap-type", KeyType::SCROLL_SNAP_TYPE},
533+ {"scroll-timeline-axis", KeyType::SCROLL_TIMELINE_AXIS},
534+ {"scroll-timeline-name", KeyType::SCROLL_TIMELINE_NAME},
535+ {"scroll-timeline", KeyType::SCROLL_TIMELINE},
536+ {"scrollbar-color", KeyType::SCROLLBAR_COLOR},
537+ {"scrollbar-gutter", KeyType::SCROLLBAR_GUTTER},
538+ {"scrollbar-width", KeyType::SCROLLBAR_WIDTH},
539+ {"shape-image-threshold", KeyType::SHAPE_IMAGE_THRESHOLD},
540+ {"shape-margin", KeyType::SHAPE_MARGIN},
541+ {"shape-outside", KeyType::SHAPE_OUTSIDE},
542+ {"shape-rendering", KeyType::SHAPE_RENDERING},
543+ {"speak-as", KeyType::SPEAK_AS},
544+ {"stop-color", KeyType::STOP_COLOR},
545+ {"stop-opacity", KeyType::STOP_OPACITY},
546+ {"stroke-dasharray", KeyType::STROKE_DASHARRAY},
547+ {"stroke-dashoffset", KeyType::STROKE_DASHOFFSET},
548+ {"stroke-linecap", KeyType::STROKE_LINECAP},
549+ {"stroke-linejoin", KeyType::STROKE_LINEJOIN},
550+ {"stroke-miterlimit", KeyType::STROKE_MITERLIMIT},
551+ {"stroke-opacity", KeyType::STROKE_OPACITY},
552+ {"stroke-width", KeyType::STROKE_WIDTH},
553+ {"stroke", KeyType::STROKE},
554+ {"tab-size", KeyType::TAB_SIZE},
555+ {"table-layout", KeyType::TABLE_LAYOUT},
556+ {"text-align-last", KeyType::TEXT_ALIGN_LAST},
557+ {"text-align", KeyType::TEXT_ALIGN},
558+ {"text-anchor", KeyType::TEXT_ANCHOR},
559+ {"text-box-edge", KeyType::TEXT_BOX_EDGE},
560+ {"text-box-trim", KeyType::TEXT_BOX_TRIM},
561+ {"text-box", KeyType::TEXT_BOX},
562+ {"text-combine-upright", KeyType::TEXT_COMBINE_UPRIGHT},
563+ {"text-decoration-color", KeyType::TEXT_DECORATION_COLOR},
564+ {"text-decoration-line", KeyType::TEXT_DECORATION_LINE},
565+ {"text-decoration-skip-ink", KeyType::TEXT_DECORATION_SKIP_INK},
566+ {"text-decoration-skip", KeyType::TEXT_DECORATION_SKIP},
567+ {"text-decoration-style", KeyType::TEXT_DECORATION_STYLE},
568+ {"text-decoration-thickness", KeyType::TEXT_DECORATION_THICKNESS},
569+ {"text-decoration", KeyType::TEXT_DECORATION},
570+ {"text-emphasis-color", KeyType::TEXT_EMPHASIS_COLOR},
571+ {"text-emphasis-position", KeyType::TEXT_EMPHASIS_POSITION},
572+ {"text-emphasis-style", KeyType::TEXT_EMPHASIS_STYLE},
573+ {"text-emphasis", KeyType::TEXT_EMPHASIS},
574+ {"text-indent", KeyType::TEXT_INDENT},
575+ {"text-justify", KeyType::TEXT_JUSTIFY},
576+ {"text-orientation", KeyType::TEXT_ORIENTATION},
577+ {"text-overflow", KeyType::TEXT_OVERFLOW},
578+ {"text-rendering", KeyType::TEXT_RENDERING},
579+ {"text-shadow", KeyType::TEXT_SHADOW},
580+ {"text-size-adjust", KeyType::TEXT_SIZE_ADJUST},
581+ {"text-spacing-trim", KeyType::TEXT_SPACING_TRIM},
582+ {"text-transform", KeyType::TEXT_TRANSFORM},
583+ {"text-underline-offset", KeyType::TEXT_UNDERLINE_OFFSET},
584+ {"text-underline-position", KeyType::TEXT_UNDERLINE_POSITION},
585+ {"text-wrap-mode", KeyType::TEXT_WRAP_MODE},
586+ {"text-wrap-style", KeyType::TEXT_WRAP_STYLE},
587+ {"text-wrap", KeyType::TEXT_WRAP},
588+ {"timeline-scope", KeyType::TIMELINE_SCOPE},
589+ {"top", KeyType::TOP},
590+ {"touch-action", KeyType::TOUCH_ACTION},
591+ {"transform-box", KeyType::TRANSFORM_BOX},
592+ {"transform-origin", KeyType::TRANSFORM_ORIGIN},
593+ {"transform-style", KeyType::TRANSFORM_STYLE},
594+ {"transform", KeyType::TRANSFORM},
595+ {"transition-behavior", KeyType::TRANSITION_BEHAVIOR},
596+ {"transition-delay", KeyType::TRANSITION_DELAY},
597+ {"transition-duration", KeyType::TRANSITION_DURATION},
598+ {"transition-property", KeyType::TRANSITION_PROPERTY},
599+ {"transition-timing-function", KeyType::TRANSITION_TIMING_FUNCTION},
600+ {"transition", KeyType::TRANSITION},
601+ {"translate", KeyType::TRANSLATE},
602+ {"unicode-bidi", KeyType::UNICODE_BIDI},
603+ {"user-modify", KeyType::USER_MODIFY},
604+ {"user-select", KeyType::USER_SELECT},
605+ {"vector-effect", KeyType::VECTOR_EFFECT},
606+ {"vertical-align", KeyType::VERTICAL_ALIGN},
607+ {"view-timeline-axis", KeyType::VIEW_TIMELINE_AXIS},
608+ {"view-timeline-inset", KeyType::VIEW_TIMELINE_INSET},
609+ {"view-timeline-name", KeyType::VIEW_TIMELINE_NAME},
610+ {"view-timeline", KeyType::VIEW_TIMELINE},
611+ {"view-transition-class", KeyType::VIEW_TRANSITION_CLASS},
612+ {"view-transition-name", KeyType::VIEW_TRANSITION_NAME},
613+ {"visibility", KeyType::VISIBILITY},
614+ {"white-space-collapse", KeyType::WHITE_SPACE_COLLAPSE},
615+ {"white-space", KeyType::WHITE_SPACE},
616+ {"widows", KeyType::WIDOWS},
617+ {"width", KeyType::WIDTH},
618+ {"will-change", KeyType::WILL_CHANGE},
619+ {"word-break", KeyType::WORD_BREAK},
620+ {"word-spacing", KeyType::WORD_SPACING},
621+ {"writing-mode", KeyType::WRITING_MODE},
622+ {"x", KeyType::X},
623+ {"y", KeyType::Y},
624+ {"z-index", KeyType::Z_INDEX},
625+ {"zoom", KeyType::ZOOM}
626+};
627+
628+static const std::unordered_map<std::string_view, unsigned int> kColors {
629+ {"black", 0x000000},
630+ {"silver", 0xc0c0c0},
631+ {"gray", 0x808080},
632+ {"white", 0xffffff},
633+ {"maroon", 0x800000},
634+ {"red", 0xff0000},
635+ {"purple", 0x800080},
636+ {"fuchsia", 0xff00ff},
637+ {"green", 0x008000},
638+ {"lime", 0x00ff00},
639+ {"olive", 0x808000},
640+ {"yellow", 0xffff00},
641+ {"navy", 0x000080},
642+ {"blue", 0x0000ff},
643+ {"teal", 0x008080},
644+ {"aqua", 0x00ffff},
645+ {"aliceblue", 0xf0f8ff},
646+ {"antiquewhite", 0xfaebd7},
647+ {"aqua", 0x00ffff},
648+ {"aquamarine", 0x7fffd4},
649+ {"azure", 0xf0ffff},
650+ {"beige", 0xf5f5dc},
651+ {"bisque", 0xffe4c4},
652+ {"black", 0x000000},
653+ {"blanchedalmond", 0xffebcd},
654+ {"blue", 0x0000ff},
655+ {"blueviolet", 0x8a2be2},
656+ {"brown", 0xa52a2a},
657+ {"burlywood", 0xdeb887},
658+ {"cadetblue", 0x5f9ea0},
659+ {"chartreuse", 0x7fff00},
660+ {"chocolate", 0xd2691e},
661+ {"coral", 0xff7f50},
662+ {"cornflowerblue", 0x6495ed},
663+ {"cornsilk", 0xfff8dc},
664+ {"crimson", 0xdc143c},
665+ {"cyan", 0x00ffff},
666+ {"darkblue", 0x00008b},
667+ {"darkcyan", 0x008b8b},
668+ {"darkgoldenrod", 0xb8860b},
669+ {"darkgray", 0xa9a9a9},
670+ {"darkgreen", 0x006400},
671+ {"darkgrey", 0xa9a9a9},
672+ {"darkkhaki", 0xbdb76b},
673+ {"darkmagenta", 0x8b008b},
674+ {"darkolivegreen", 0x556b2f},
675+ {"darkorange", 0xff8c00},
676+ {"darkorchid", 0x9932cc},
677+ {"darkred", 0x8b0000},
678+ {"darksalmon", 0xe9967a},
679+ {"darkseagreen", 0x8fbc8f},
680+ {"darkslateblue", 0x483d8b},
681+ {"darkslategray", 0x2f4f4f},
682+ {"darkslategrey", 0x2f4f4f},
683+ {"darkturquoise", 0x00ced1},
684+ {"darkviolet", 0x9400d3},
685+ {"deeppink", 0xff1493},
686+ {"deepskyblue", 0x00bfff},
687+ {"dimgray", 0x696969},
688+ {"dimgrey", 0x696969},
689+ {"dodgerblue", 0x1e90ff},
690+ {"firebrick", 0xb22222},
691+ {"floralwhite", 0xfffaf0},
692+ {"forestgreen", 0x228b22},
693+ {"fuchsia", 0xff00ff},
694+ {"gainsboro", 0xdcdcdc},
695+ {"ghostwhite", 0xf8f8ff},
696+ {"gold", 0xffd700},
697+ {"goldenrod", 0xdaa520},
698+ {"gray", 0x808080},
699+ {"green", 0x008000},
700+ {"greenyellow", 0xadff2f},
701+ {"grey", 0x808080},
702+ {"honeydew", 0xf0fff0},
703+ {"hotpink", 0xff69b4},
704+ {"indianred", 0xcd5c5c},
705+ {"indigo", 0x4b0082},
706+ {"ivory", 0xfffff0},
707+ {"khaki", 0xf0e68c},
708+ {"lavender", 0xe6e6fa},
709+ {"lavenderblush", 0xfff0f5},
710+ {"lawngreen", 0x7cfc00},
711+ {"lemonchiffon", 0xfffacd},
712+ {"lightblue", 0xadd8e6},
713+ {"lightcoral", 0xf08080},
714+ {"lightcyan", 0xe0ffff},
715+ {"lightgoldenrodyellow", 0xfafad2},
716+ {"lightgray", 0xd3d3d3},
717+ {"lightgreen", 0x90ee90},
718+ {"lightgrey", 0xd3d3d3},
719+ {"lightpink", 0xffb6c1},
720+ {"lightsalmon", 0xffa07a},
721+ {"lightseagreen", 0x20b2aa},
722+ {"lightskyblue", 0x87cefa},
723+ {"lightslategray", 0x778899},
724+ {"lightslategrey", 0x778899},
725+ {"lightsteelblue", 0xb0c4de},
726+ {"lightyellow", 0xffffe0},
727+ {"lime", 0x00ff00},
728+ {"limegreen", 0x32cd32},
729+ {"linen", 0xfaf0e6},
730+ {"magenta", 0xff00ff},
731+ {"maroon", 0x800000},
732+ {"mediumaquamarine", 0x66cdaa},
733+ {"mediumblue", 0x0000cd},
734+ {"mediumorchid", 0xba55d3},
735+ {"mediumpurple", 0x9370db},
736+ {"mediumseagreen", 0x3cb371},
737+ {"mediumslateblue", 0x7b68ee},
738+ {"mediumspringgreen", 0x00fa9a},
739+ {"mediumturquoise", 0x48d1cc},
740+ {"mediumvioletred", 0xc71585},
741+ {"midnightblue", 0x191970},
742+ {"mintcream", 0xf5fffa},
743+ {"mistyrose", 0xffe4e1},
744+ {"moccasin", 0xffe4b5},
745+ {"navajowhite", 0xffdead},
746+ {"navy", 0x000080},
747+ {"oldlace", 0xfdf5e6},
748+ {"olive", 0x808000},
749+ {"olivedrab", 0x6b8e23},
750+ {"orange", 0xffa500},
751+ {"orangered", 0xff4500},
752+ {"orchid", 0xda70d6},
753+ {"palegoldenrod", 0xeee8aa},
754+ {"palegreen", 0x98fb98},
755+ {"paleturquoise", 0xafeeee},
756+ {"palevioletred", 0xdb7093},
757+ {"papayawhip", 0xffefd5},
758+ {"peachpuff", 0xffdab9},
759+ {"peru", 0xcd853f},
760+ {"pink", 0xffc0cb},
761+ {"plum", 0xdda0dd},
762+ {"powderblue", 0xb0e0e6},
763+ {"purple", 0x800080},
764+ {"rebeccapurple", 0x663399},
765+ {"red", 0xff0000},
766+ {"rosybrown", 0xbc8f8f},
767+ {"royalblue", 0x4169e1},
768+ {"saddlebrown", 0x8b4513},
769+ {"salmon", 0xfa8072},
770+ {"sandybrown", 0xf4a460},
771+ {"seagreen", 0x2e8b57},
772+ {"seashell", 0xfff5ee},
773+ {"sienna", 0xa0522d},
774+ {"silver", 0xc0c0c0},
775+ {"skyblue", 0x87ceeb},
776+ {"slateblue", 0x6a5acd},
777+ {"slategray", 0x708090},
778+ {"slategrey", 0x708090},
779+ {"snow", 0xfffafa},
780+ {"springgreen", 0x00ff7f},
781+ {"steelblue", 0x4682b4},
782+ {"tan", 0xd2b48c},
783+ {"teal", 0x008080},
784+ {"thistle", 0xd8bfd8},
785+ {"tomato", 0xff6347},
786+ {"transparent", 0x00000000},
787+ {"turquoise", 0x40e0d0},
788+ {"violet", 0xee82ee},
789+ {"wheat", 0xf5deb3},
790+ {"white", 0xffffff},
791+ {"whitesmoke", 0xf5f5f5},
792+ {"yellow", 0xffff00},
793+ {"yellowgreen", 0x9acd32},
794+};
795+
796+struct SuffixParse {
797+ std::string_view suffix;
798+ UnitType type;
799+ int trim; // chars to strip from the right
800+};
801+
802+static constexpr SuffixParse sufMatch[] = {
803+ {"cqmin", UnitType::CQMIN, 5},
804+ {"cqmax", UnitType::CQMAX, 5},
805+ {"rcap", UnitType::RCAP, 4},
806+ {"vmax", UnitType::VMAX, 4},
807+ {"vmin", UnitType::VMIN, 4},
808+ {"grad", UnitType::GRAD, 4},
809+ {"turn", UnitType::TURN, 4},
810+ {"cap", UnitType::CAP, 3},
811+ {"rch", UnitType::RCH, 3},
812+ {"rem", UnitType::REM, 3},
813+ {"rex", UnitType::REX, 3},
814+ {"ric", UnitType::RIC, 3},
815+ {"rlh", UnitType::RLH, 3},
816+ {"cqw", UnitType::CQW, 3},
817+ {"cqh", UnitType::CQH, 3},
818+ {"cqi", UnitType::CQI, 3},
819+ {"cqb", UnitType::CQB, 3},
820+ {"deg", UnitType::DEG, 3},
821+ {"rad", UnitType::RAD, 3},
822+ {"khz", UnitType::KHZ, 3},
823+ {"kHz", UnitType::KHZ, 3},
824+ {"Khz", UnitType::KHZ, 3},
825+ {"KHz", UnitType::KHZ, 3},
826+ {"KHZ", UnitType::KHZ, 3},
827+ {"px", UnitType::PX, 2},
828+ {"hz", UnitType::HZ, 2},
829+ {"Hz", UnitType::HZ, 2},
830+ {"HZ", UnitType::HZ, 2},
831+ {"hZ", UnitType::HZ, 2},
832+ {"ms", UnitType::MS, 2},
833+ {"em", UnitType::EM, 2},
834+ {"cm", UnitType::CM, 2},
835+ {"ch", UnitType::CH, 2},
836+ {"ex", UnitType::EX, 2},
837+ {"ic", UnitType::IC, 2},
838+ {"lh", UnitType::LH, 2},
839+ {"vh", UnitType::VH, 2},
840+ {"vw", UnitType::VW, 2},
841+ {"vb", UnitType::VB, 2},
842+ {"vi", UnitType::VI, 2},
843+ {"mm", UnitType::MM, 2},
844+ {"in", UnitType::IN, 2},
845+ {"pc", UnitType::PC, 2},
846+ {"pt", UnitType::PT, 2},
847+ {"fr", UnitType::FRACTION,2},
848+ {"%", UnitType::PERCENT,1},
849+ {"q", UnitType::Q, 1},
850+ {"s", UnitType::SECOND,1},
851+};
852+
853+
854+static const std::unordered_map<std::string_view, UnitType> kMatch{
855+ {"+", UnitType::ADD},
856+ {"-", UnitType::SUB},
857+ {"*", UnitType::MULT},
858+ {"/", UnitType::DIV},
859+ {"==", UnitType::EQ},
860+ {">", UnitType::GT},
861+ {"<", UnitType::LT},
862+ {">=", UnitType::GE},
863+ {"<=", UnitType::LE},
864+ {"and", UnitType::AND},
865+ {"&&", UnitType::AND},
866+ {"or", UnitType::OR},
867+ {"||", UnitType::OR},
868+ {"not", UnitType::NOT},
869+ {"!=", UnitType::NOT},
870+ {"only", UnitType::ONLY},
871+ {"infinity", UnitType::INF},
872+ {"-infinity", UnitType::NEGINF},
873+ {"NaN", UnitType::NaN},
874+ {"e", UnitType::E},
875+ {"pi", UnitType::PI},
876+ {"content-box", UnitType::CONTENT},
877+ {"padding-box", UnitType::PADDING},
878+ {"border-box", UnitType::BORDER},
879+ {"margin-box", UnitType::MARGIN},
880+ {"fill-box", UnitType::FILL},
881+ {"stroke-box", UnitType::STROKE},
882+ {"view-box", UnitType::VIEW},
883+ {"space-between", UnitType::BETWEEN},
884+ {"space-around", UnitType::AROUND},
885+ {"space-evenly", UnitType::EVENLY},
886+ {"stretch", UnitType::STRETCH},
887+ {"normal", UnitType::NORMAL},
888+ {"contents", UnitType::CONTENTS},
889+ {"none", UnitType::NONE},
890+ {"flow", UnitType::FLOW},
891+ {"flow-root", UnitType::FLOW_ROOT},
892+ {"table", UnitType::TABLE},
893+ {"flex", UnitType::FLEX},
894+ {"grid", UnitType::GRID},
895+ {"ruby", UnitType::RUBY},
896+ {"table-row-group", UnitType::TABLE_ROW_GROUP},
897+ {"table-header-group", UnitType::TABLE_HEADER_GROUP},
898+ {"table-footer-group", UnitType::TABLE_FOOTER_GROUP},
899+ {"table-column-group", UnitType::TABLE_COLUMN_GROUP},
900+ {"table-row", UnitType::TABLE_ROW},
901+ {"table-cell", UnitType::TABLE_CELL},
902+ {"table-column", UnitType::TABLE_COLUMN},
903+ {"table-caption", UnitType::TABLE_CAPTION},
904+ {"ruby-base", UnitType::RUBY_BASE},
905+ {"ruby-text", UnitType::RUBY_TEXT},
906+ {"ruby-base-container", UnitType::RUBY_BASE_CONTAINER},
907+ {"ruby-text-container", UnitType::RUBY_TEXT_CONTAINER},
908+ {"inline-block", UnitType::INLINE_BLOCK},
909+ {"inline-table", UnitType::INLINE_TABLE},
910+ {"inline-flex", UnitType::INLINE_FLEX},
911+ {"inline-grid", UnitType::INLINE_GRID},
912+ {"list-item", UnitType::LISTITEM},
913+ {"block", UnitType::BLOCK},
914+ {"inline", UnitType::INLINE},
915+ {"run-in", UnitType::RUN_IN},
916+ {"linear", UnitType::LINEAR},
917+ {"ease", UnitType::EASE},
918+ {"ease-in", UnitType::EASE_IN},
919+ {"ease-out", UnitType::EASE_OUT},
920+ {"ease-in-out", UnitType::EASE_IN_OUT},
921+ {"step-start", UnitType::STEP_START},
922+ {"step-end", UnitType::STEP_END},
923+ {"jump-start", UnitType::JUMP_START},
924+ {"jump-end", UnitType::JUMP_END},
925+ {"jump-none", UnitType::JUMP_NONE},
926+ {"jump-both", UnitType::JUMP_BOTH},
927+ {"serif", UnitType::SERIF},
928+ {"sans-serif", UnitType::SANS_SERIF},
929+ {"monospace", UnitType::MONOSPACE},
930+ {"cursive", UnitType::CURSIVE},
931+ {"fantasy", UnitType::FANTASY},
932+ {"system-ui", UnitType::SYSTEM_UI},
933+ {"ui-serif", UnitType::UI_SERIF},
934+ {"ui-sans-serif", UnitType::UI_SANS_SERIF},
935+ {"ui-monospace", UnitType::UI_MONOSPACE},
936+ {"ui-rounded", UnitType::UI_ROUNDED},
937+ {"math", UnitType::MATH},
938+ {"fangsong", UnitType::FANGSONG},
939+ {"top", UnitType::TOP},
940+ {"left", UnitType::LEFT},
941+ {"bottom", UnitType::BOTTOM},
942+ {"right", UnitType::RIGHT},
943+ {"center", UnitType::CENTER},
944+ {"start", UnitType::START},
945+ {"end", UnitType::END},
946+ {"inline-start", UnitType::INLINE_START},
947+ {"inline-end", UnitType::INLINE_END},
948+ {"block-start", UnitType::BLOCK_START},
949+ {"block-end", UnitType::BLOCK_END},
950+ {"self-start", UnitType::SELF_START},
951+ {"self-end", UnitType::SELF_END},
952+ {"flex-start", UnitType::FLEX_START},
953+ {"flex-end", UnitType::FLEX_END},
954+ {"circle", UnitType::CIRCLE},
955+ {"ellipse", UnitType::ELLIPSE},
956+ {"shorterhue", UnitType::SHORTER_HUE},
957+ {"longerhue", UnitType::LONGER_HUE},
958+ {"increasinghue", UnitType::INCREASING_HUE},
959+ {"decreasinghue", UnitType::DECREASING_HUE},
960+ {"dotted", UnitType::DOTTED},
961+ {"dashed", UnitType::DASHED},
962+ {"solid", UnitType::SOLID},
963+ {"double", UnitType::DOUBLE},
964+ {"groove", UnitType::GROOVE},
965+ {"ridge", UnitType::RIDGE},
966+ {"inset", UnitType::INSET},
967+ {"outset", UnitType::OUTSET},
968+ {"baseline", UnitType::FIRST_BASELINE},
969+ {"firstbaseline", UnitType::FIRST_BASELINE},
970+ {"lastbaseline", UnitType::LAST_BASELINE},
971+ {"safe", UnitType::SAFE},
972+ {"unsafe", UnitType::UNSAFE},
973+ {"visible", UnitType::VISIBLE},
974+ {"clip", UnitType::CLIP},
975+ {"scroll", UnitType::SCROLL},
976+ {"inherit", UnitType::INHERIT},
977+ {"initial", UnitType::INITIAL},
978+ {"revert", UnitType::REVERT},
979+ {"revert-layer", UnitType::REVERT_LAYER},
980+ {"unset", UnitType::UNSET},
981+ {"nearest", UnitType::NEAREST},
982+ {"up", UnitType::UP},
983+ {"down", UnitType::DOWN},
984+ {"to-zero", UnitType::TO_ZERO},
985+};
986+
987+static const std::unordered_map<std::string_view, UnitType> kFunc{
988+ {"calc", UnitType::CALC},
989+ {"min", UnitType::MIN},
990+ {"max", UnitType::MAX},
991+ {"clamp", UnitType::CLAMP},
992+ {"round", UnitType::ROUND},
993+ {"mod", UnitType::MOD},
994+ {"progress", UnitType::PROGRESS},
995+ {"rem", UnitType::REMD},
996+ {"pow", UnitType::POW},
997+ {"sqrt", UnitType::SQRT},
998+ {"set", UnitType::SET},
999+ {"let", UnitType::LET},
1000+ {"hypot", UnitType::HYPOT},
1001+ {"log", UnitType::LOG},
1002+ {"exp", UnitType::EXP},
1003+ {"abs", UnitType::ABS},
1004+ {"sin", UnitType::SIN},
1005+ {"cos", UnitType::COS},
1006+ {"tan", UnitType::TAN},
1007+ {"asin", UnitType::ASIN},
1008+ {"acos", UnitType::ACOS},
1009+ {"atan", UnitType::ATAN},
1010+ {"atan2", UnitType::ATAN2},
1011+ {"sign", UnitType::SIGN},
1012+ {"var", UnitType::VAR_FUNC},
1013+ {"rgb", UnitType::RGB},
1014+ {"rgba", UnitType::RGBA},
1015+ {"hsl", UnitType::HSL},
1016+ {"linear", UnitType::LINEAR_EASING_FUNCTION},
1017+ {"cubic-bezier", UnitType::CUBIC_BEZIER_FUNCTION},
1018+ {"steps", UnitType::STEPS_FUNCTION},
1019+ {"blur", UnitType::BLUR},
1020+ {"brightness", UnitType::BRIGHTNESS},
1021+ {"contrast", UnitType::CONTRAST},
1022+ {"drop-shadow", UnitType::DROP_SHADOW},
1023+ {"grayscale", UnitType::GRAYSCALE},
1024+ {"hue-rotate", UnitType::HUE_ROTATE},
1025+ {"invert", UnitType::INVERT},
1026+ {"opacity", UnitType::OPACITY},
1027+ {"sepia", UnitType::SEPIA},
1028+ {"saturate", UnitType::SATURATE},
1029+ {"image", UnitType::IMAGE},
1030+ {"image-set", UnitType::IMAGE_SET},
1031+ {"cross-fade", UnitType::CROSS_FADE},
1032+ {"element", UnitType::ELEMENT},
1033+ {"gradient", UnitType::GRADIENT},
1034+ {"matrix", UnitType::MATRIX},
1035+ {"matrix3d", UnitType::MATRIX3D},
1036+ {"perspective", UnitType::PERSPECTIVE},
1037+ {"rotate", UnitType::ROTATE},
1038+ {"rotate3d", UnitType::ROTATE3D},
1039+ {"rotateX", UnitType::ROTATEX},
1040+ {"rotateY", UnitType::ROTATEY},
1041+ {"rotateZ", UnitType::ROTATEZ},
1042+ {"scale", UnitType::SCALE},
1043+ {"scale3d", UnitType::SCALE3D},
1044+ {"scaleX", UnitType::SCALEX},
1045+ {"scaleY", UnitType::SCALEY},
1046+ {"scaleZ", UnitType::SCALEZ},
1047+ {"translate", UnitType::TRANSLATE},
1048+ {"translate3d", UnitType::TRANSLATE3D},
1049+ {"translateX", UnitType::TRANSLATEX},
1050+ {"translateY", UnitType::TRANSLATEY},
1051+ {"translateZ", UnitType::TRANSLATEZ},
1052+ {"skew", UnitType::SKEW},
1053+ {"skewX", UnitType::SKEWX},
1054+ {"skewY", UnitType::SKEWY},
1055+ {"url", UnitType::URL},
1056+ {"attr", UnitType::ATTR},
1057+ {"type", UnitType::TYPE},
1058+ {"env", UnitType::ENV},
1059+ {"if", UnitType::IF},
1060+ {"var", UnitType::VAR},
1061+ {"", UnitType::GROUP}
1062+};
1063+
1064+static const std::unordered_map<std::string_view, float> kAbsSize{
1065+ {"xx-small",0.6f}, {"x-small",0.75f}, {"small",0.89f}, {"medium",1.0f},
1066+ {"large",1.2f}, {"x-large",1.5f}, {"xx-large",2.0f}, {"xxx-large",3.0f}
1067+};
1068+
1069+/*
1070+ Parses a string into Unit's and stores variables & strings into the Style object
1071+
1072+*/
1073+
1074+std::vector<Unit> getUnit(std::string value, SymbolTable* externalTable = nullptr) {
1075+ SymbolTable localTable;
1076+ SymbolTable& table = (externalTable != nullptr) ? *externalTable : localTable;
1077+
1078+ std::vector<Unit> units;
1079+
1080+ std::string buffer = "";
1081+ std::string str_buffer = "";
1082+ int parenDepth = 0;
1083+ bool inQuotes = false;
1084+ // Needs to not be empty but can't be a quote
1085+ char quoteType = 'a';
1086+ bool escaped = false;
1087+
1088+ for (size_t i = 0; i < value.length(); i++) {
1089+ char v = value[i];
1090+ if (v == '\\' && !escaped) escaped = true;
1091+ if (!escaped && (v == '"' || v == '\'') && parenDepth == 0) {
1092+ if (inQuotes && v == quoteType) {
1093+ for (size_t j = 0; j < str_buffer.length(); j++) {
1094+ Unit unit{};
1095+ unit.type = UnitType::STRING;
1096+ unit.value.CHAR = str_buffer[j];
1097+ units.push_back(unit);
1098+ }
1099+
1100+ str_buffer.clear();
1101+
1102+ Unit unit{};
1103+ unit.type = UnitType::STRING_TERM;
1104+ unit.value.INT = str_buffer.length();
1105+ units.push_back(unit);
1106+
1107+ inQuotes = false;
1108+ } else if (!inQuotes) {
1109+ quoteType = v;
1110+ inQuotes = true;
1111+ }
1112+ continue;
1113+ }
1114+
1115+ if (!inQuotes) {
1116+ if (v == '(') parenDepth++;
1117+ else if (v == ')') parenDepth--;
1118+ }
1119+
1120+ if (parenDepth > 0) {
1121+ buffer.push_back(v);
1122+ continue;
1123+ } else if (v == ')') {
1124+ /*
1125+ Function handling
1126+ */
1127+ // We are at the end of the parentheseses and we need to pass it for more parsing
1128+ std::string name;
1129+ bool nameset = false;
1130+ std::string value;
1131+
1132+ for (size_t j = 0; j < buffer.size(); j++) {
1133+ if (nameset) {
1134+ value.push_back(buffer[j]);
1135+ } else {
1136+ if (buffer[j] == '(') nameset = true;
1137+ else name.push_back(buffer[j]);
1138+ }
1139+ }
1140+
1141+ std::vector<Unit> children = getUnit(value, &table);
1142+
1143+ Unit unit;
1144+
1145+ if (auto it = kFunc.find(name); it != kFunc.end()) {
1146+ unit.type = it->second;
1147+ } else {
1148+ unit.type = UnitType::FUNC;
1149+ }
1150+
1151+ // The value of the function is the child count
1152+ unit.value.INT = children.size();
1153+
1154+ units.push_back(unit);
1155+ units.insert(units.end(), children.begin(), children.end());
1156+ // Add a end value for the function
1157+
1158+ buffer.clear();
1159+ } else if ((!inQuotes && (v == ',' || std::isspace(v))) || i >= value.length()-1) {
1160+ if (v != ',' && !std::isspace(v)) buffer.push_back(v);
1161+
1162+ Unit unit;
1163+
1164+ // Split by spaces into parts
1165+
1166+ if (auto it = kMatch.find(buffer); it != kMatch.end()) {
1167+ /*
1168+ Keyword Matching
1169+ */
1170+ unit.type = it->second;
1171+ // the value is not used here
1172+ } else if (auto it = kColors.find(buffer); it != kColors.end()) {
1173+ /*
1174+ Color Matching
1175+ */
1176+ unit.type = UnitType::HEX;
1177+ unit.value.UINT = it->second;
1178+ } else {
1179+ bool found = false;
1180+ for (const auto& [suffix, type, trim] : sufMatch) {
1181+ if (buffer.ends_with(suffix)) {
1182+ found = true;
1183+ try {
1184+ unit.value.FLOAT = std::stof(std::string(buffer.substr(0, buffer.size() - trim)));
1185+ } catch (...) {
1186+ unit.value.FLOAT = 0.0f;
1187+ }
1188+
1189+ unit.type = type;
1190+ break;
1191+ }
1192+ }
1193+
1194+ if (!found) {
1195+ if (buffer.starts_with("#") && buffer.length() > 1) {
1196+ /*
1197+ # Prefix for HEX codes
1198+ */
1199+ unit.type = UnitType::HEX;
1200+ unit.value.UINT = std::stoul(buffer.substr(1), nullptr, 16);
1201+ } else if (auto it = kAbsSize.find(buffer); it != kAbsSize.end()) {
1202+ /*
1203+ Absolute size matching
1204+ */
1205+ unit.type = UnitType::EM;
1206+ unit.value.FLOAT = it->second;
1207+ } else if (buffer == "true") {
1208+ unit.type = UnitType::BOOL;
1209+ unit.value.BOOL = true;
1210+ } else if (buffer == "true") {
1211+ unit.type = UnitType::BOOL;
1212+ unit.value.BOOL = false;
1213+ } else {
1214+ // Maybe its a number?
1215+ try {
1216+ unit.value.FLOAT = std::stof(buffer);
1217+ unit.type = UnitType::NUMBER;
1218+ } catch (...) {
1219+ // If we are here it is not a known parsable word, this could be a typo
1220+ // or a keyword like with the attr(size px) function. Here px would be
1221+ // parsed but not size.
1222+ if (!buffer.empty() && buffer.find_first_not_of(' ') != std::string::npos) {
1223+ unit.type = UnitType::VAR;
1224+ unit.value.INT = table.get(buffer);
1225+ } else {
1226+ continue;
1227+ }
1228+ }
1229+ }
1230+ }
1231+ }
1232+
1233+ units.push_back(unit);
1234+ buffer.clear();
1235+ } else if (inQuotes) {
1236+ str_buffer.push_back(v);
1237+ } else {
1238+ buffer.push_back(v);
1239+ }
1240+
1241+ escaped = false;
1242+ if (v == '\\') escaped = true;
1243+ }
1244+
1245+ return units;
1246+}
1247+
1248+Style parseCSS(std::istream& inputStream) {
1249+ // nowhitespace: value(can have whitespace); = property
1250+ // a selector name is anything before a { up to a ; or a }
1251+ std::vector<Style*> stack;
1252+ Style root{};
1253+ Style* current = &root;
1254+
1255+ // how to handle @import
1256+
1257+ std::string buffer;
1258+
1259+ bool incomment = false;
1260+
1261+
1262+ SymbolTable table;
1263+
1264+ char ch;
1265+ while (inputStream.get(ch)) {
1266+ if (ch == '*' && inputStream.peek() == '/') {
1267+ incomment = false;
1268+ // Skip ahead to the next letter so we don't capture the trailing /
1269+ inputStream.get(ch);
1270+ continue;
1271+ }
1272+ if (ch == '/' && inputStream.peek() == '*') {
1273+ incomment = true;
1274+ }
1275+
1276+ if (incomment) {
1277+ continue;
1278+ }
1279+
1280+ if (ch == '{') {
1281+ for (size_t i = buffer.length()-1; i >= 0; i--) {
1282+ if (std::isspace(buffer[i])) {
1283+ buffer.pop_back();
1284+ } else {
1285+ break;
1286+ }
1287+ }
1288+
1289+ // Parse the selector into its parts
1290+ std::vector<std::vector<std::string>> parts = parseSelectorParts(buffer);
1291+
1292+ if (current->selector.size() > 0) {
1293+ // To be able to handle a substitution with a parent that has h1, h2
1294+ // we need to rebuild the selector so we can add new copies
1295+ std::vector<std::vector<std::string>> subd;
1296+
1297+ // Look for any & if its a style and replace it with the parsed parent selector
1298+ for (size_t i = 0; i < parts.size(); i++) {
1299+ bool hasAmp = false;
1300+ for (size_t e = 0; e < parts[i].size(); e++) {
1301+ if (parts[i][e] == "&") {
1302+ hasAmp = true;
1303+ break;
1304+ }
1305+ }
1306+
1307+ if (hasAmp) {
1308+ // If it contains a amp then we need replace the amp with each parent selector
1309+ // Inject each part of the parent
1310+ // Parent [[h1],[h2]]
1311+ // Child: [[& .class, &, &]]
1312+ // Result: [[h1 .class, h1, h1]]
1313+ // [[h2 .class, h2, h2]]
1314+ for (size_t e = 0; e < current->selector.size(); e++) {
1315+ std::vector<std::string> temp;
1316+ for (size_t f = 0; f < parts[i].size(); f++) {
1317+ if (parts[i][f] == "&") {
1318+ for (auto p : current->selector[e]) {
1319+ temp.push_back(p);
1320+ }
1321+ } else {
1322+ temp.push_back(parts[i][f]);
1323+ }
1324+ }
1325+ subd.push_back(temp);
1326+ }
1327+ } else {
1328+ // If there's nothing to replace just add to back
1329+ subd.push_back(parts[i]);
1330+ }
1331+ }
1332+
1333+ parts = subd;
1334+ }
1335+
1336+ stack.push_back(current);
1337+
1338+ Style newSheet{};
1339+ newSheet.selector = parts;
1340+ current->addChild(newSheet);
1341+
1342+ current = ¤t->children.back();
1343+
1344+ buffer.clear();
1345+ } else if (ch == '}') {
1346+
1347+ if (!stack.empty()) {
1348+ current = stack.back();
1349+ stack.pop_back();
1350+ }
1351+
1352+ buffer.clear();
1353+ } else if (ch == ';') {
1354+ if (buffer[0] == '@') {
1355+ // Single line at-rule
1356+ Style inlineRule;
1357+ std::vector<std::vector<std::string>> parts = parseSelectorParts(buffer);
1358+ inlineRule.selector = parts;
1359+
1360+ if (parts[0][0] == "@import") {
1361+ inlineRule.selector = parts;
1362+
1363+ if (parts[0].size() > 3) {
1364+ // See if the last word ends in )
1365+ // if not then its the name of the layer
1366+ if (parts[0].back().back() != ')') {
1367+ inlineRule.name = parts[0].back();
1368+ }
1369+ }
1370+
1371+ } else if (parts[0][0] == "@layer") {
1372+ // ISSUE: need to add specificity
1373+
1374+ }
1375+
1376+ current->addChild(inlineRule);
1377+
1378+ } else {
1379+ // end of a style property
1380+ std::string key;
1381+ std::string value;
1382+ bool keyFound = false;
1383+ bool firstValue = false;
1384+ for (size_t i = 0; i < buffer.length(); i++) {
1385+ char c = buffer[i];
1386+ if (!keyFound && c == ':') {
1387+ keyFound = true;
1388+ } else if (!std::isspace(c) && !keyFound) {
1389+ key += c;
1390+ } else if (!firstValue && keyFound && !std::isspace(c)) {
1391+ firstValue = true;
1392+ value += c;
1393+ } else if (keyFound && firstValue && !(std::isspace(c) && i < buffer.length()-1 && std::isspace(buffer[i+1]))) {
1394+ // if the key is found and the character isn't a ':'
1395+ // also (if c is a space and the next c will be a space)
1396+ // this is to auto trim duplicate spaces from the value
1397+ value += c;
1398+ }
1399+ }
1400+
1401+ if (key.length() > 2 && key[0] == '-' && key[1] == '-') {
1402+ current->variables.set(key, getUnit(value, &table));
1403+ } else {
1404+ KeyType keyENUM;
1405+
1406+ if (auto it = keySearch.find(key); it != keySearch.end()) {
1407+ keyENUM = it->second;
1408+ } else {
1409+ std::cout << "Invalid property name: " << key << std::endl;
1410+ }
1411+ current->properties[keyENUM] = getUnit(value, &table);
1412+ }
1413+ }
1414+ buffer.clear();
1415+ } else {
1416+ buffer.push_back(ch);
1417+ }
1418+ }
1419+
1420+ return root;
1421+}
1422+
1423+std::string get_working_path() {
1424+ char temp [ PATH_MAX ];
1425+
1426+ if ( getcwd(temp, PATH_MAX) != 0)
1427+ return std::string ( temp );
1428+
1429+ int error = errno;
1430+
1431+ switch ( error ) {
1432+ // EINVAL can't happen - size argument > 0
1433+
1434+ // PATH_MAX includes the terminating nul,
1435+ // so ERANGE should not be returned
1436+
1437+ case EACCES:
1438+ throw std::runtime_error("Access denied");
1439+
1440+ case ENOMEM:
1441+ // I'm not sure whether this can happen or not
1442+ throw std::runtime_error("Insufficient storage");
1443+
1444+ default: {
1445+ std::ostringstream str;
1446+ str << "Unrecognised error" << error;
1447+ throw std::runtime_error(str.str());
1448+ }
1449+ }
1450+}
1451+
1452+std::string pathJoin(const std::string& p1, const std::string& p2) {
1453+ char sep = '/';
1454+ std::string tmp = p1;
1455+
1456+#ifdef _WIN32
1457+ sep = '\\';
1458+#endif
1459+
1460+ // Add separator if it is not included in the first path:
1461+ if (p1[p1.length() - 1] != sep) {
1462+ tmp += sep;
1463+ return tmp + p2;
1464+ } else {
1465+ return p1 + p2;
1466+ }
1467+}
1468+
1469+struct ParsedDocument {
1470+ Node document;
1471+ Style CSS;
1472+};
1473+
1474+ParsedDocument parseDocument(std::istream& inputStream) {
1475+ Node root;
1476+ root.setTagName("root");
1477+
1478+ Node* currentNode = &root;
1479+
1480+ bool inTag = false;
1481+ bool escaped = false;
1482+ bool inQuote = false;
1483+ bool inComment = false;
1484+ bool waitToClose = false;
1485+ char quoteType = '"';
1486+
1487+ std::string token;
1488+
1489+ // CSS stuff
1490+ std::vector<std::string> cssLinks;
1491+ std::vector<std::string> cssTags;
1492+
1493+ char current;
1494+ while (inputStream.get(current)) {
1495+ // Finds the --> and removes it then resets inComment
1496+ if (inComment) {
1497+ // added the peek to prevent hitting on every -
1498+ if (current == '-' && inputStream.peek() == '-') {
1499+ char a,b;
1500+ // load the next two
1501+ if (inputStream.get(a) && inputStream.get(b)) {
1502+ // We know a == -
1503+ if (b == '>') {
1504+ // Close the comment
1505+ inComment = false;
1506+ }
1507+ }
1508+
1509+ if (!inComment) {
1510+ // we don't put anything back and that puts us where we need to be
1511+ continue;
1512+ } else {
1513+ // Not the end
1514+ inputStream.putback(b);
1515+ inputStream.putback(a);
1516+ }
1517+ }
1518+ continue;
1519+ }
1520+
1521+ std::string tagName = currentNode->getTagName();
1522+ if (!waitToClose && (
1523+ tagName == "style" ||
1524+ tagName == "script" ||
1525+ tagName == "textarea"
1526+ ) && current == '<' && inputStream.peek() == '/' ) {
1527+ // If its the starting of a closing tag and we are inside of a special tag
1528+ // we need to see if the upcoming closing tag is the correct closing tag
1529+ // if it is then we put the read ahead back and waitToClose. This will let normal parsing
1530+ // continue
1531+ const size_t readTo = tagName.length()+2;
1532+ std::string buffer(readTo, '\0');
1533+
1534+ inputStream.read(&buffer[0], readTo);
1535+
1536+ if (buffer == "/"+tagName+">") {
1537+ waitToClose = true;
1538+ } else {
1539+ token += current;
1540+ }
1541+
1542+ for (int i = readTo-1; i>= 0; i--) {
1543+ inputStream.putback(buffer[i]);
1544+ }
1545+ if (!waitToClose) {
1546+ continue;
1547+ }
1548+ } else if (!waitToClose && (
1549+ tagName == "style" ||
1550+ tagName == "script" ||
1551+ tagName == "textarea"
1552+ )) {
1553+ token += current;
1554+ continue;
1555+ }
1556+
1557+ if (inTag) {
1558+ if ((current == '"' || current == '\'') && !escaped) {
1559+ if (inQuote && current == quoteType) {
1560+ inQuote = false;
1561+ } else if (!inQuote) {
1562+ inQuote = true;
1563+ quoteType = current;
1564+ }
1565+ }
1566+
1567+ if (current == '>' && !inQuote && !escaped) {
1568+ inTag = false;
1569+ waitToClose = false;
1570+ // Even if the next tag is next still add a text tag as we can just check if its empty and remove it
1571+ // this check would still have to be done either way so we aren't wasting anything
1572+ if (token.length() > 0) {
1573+ bool empty = true;
1574+ bool closingTag = false;
1575+ for (size_t i = 0; i < token.length(); i++) {
1576+ auto c = token[i];
1577+ if (std::isspace(c)) {
1578+ continue;
1579+ } else if (c == '/') {
1580+ closingTag = true;
1581+ } else {
1582+ empty = false;
1583+ break;
1584+ }
1585+ }
1586+
1587+ bool selfClosing = false;
1588+ int selfClosingPosition = token.length()-1;
1589+ if (!closingTag) {
1590+ for (int i = token.length()-1; i >= 0; i--) {
1591+ auto c = token[i];
1592+ if (std::isspace(c)) {
1593+ continue;
1594+ } else if (c == '/') {
1595+ selfClosingPosition = i;
1596+ selfClosing = true;
1597+ break;
1598+ } else {
1599+ break;
1600+ }
1601+ }
1602+ }
1603+
1604+ if (!empty) {
1605+ if (selfClosing) {
1606+ //Create element and don't jump inside
1607+ // Use node instead of currentNode because we do not need to jump into the created element
1608+ auto attrs = parseAttributes(token.substr(0, selfClosingPosition));
1609+ auto node = currentNode->createElement(attrs["tagName"]);
1610+ for (auto const& pair : attrs) {
1611+ // All attributes are stored as strings so we can just throw them in
1612+ node->setAttribute(pair.first, pair.second);
1613+ }
1614+ if (attrs["tagName"] == "link" && attrs["rel"] == "stylesheet") {
1615+ cssLinks.push_back(node->getAttribute("href"));
1616+ } else if (attrs["tagName"] == "style") {
1617+ cssTags.push_back(node->getAttribute("innerText"));
1618+ }
1619+ } else if (closingTag) {
1620+ // Checksum and tree move
1621+ std::string tagName = "";
1622+ for (auto t : token) {
1623+ if (!std::isspace(t) && t != '/') {
1624+ tagName += t;
1625+ } else if (t == '/') {
1626+ // For closing tags we just want the name
1627+ continue;
1628+ } else if (tagName.length() > 0) {
1629+ break;
1630+ }
1631+ }
1632+
1633+ if (currentNode->getTagName() == tagName) {
1634+ if (tagName == "link" && currentNode->getAttribute("rel") == "stylesheet") {
1635+ cssLinks.push_back(currentNode->getAttribute("href"));
1636+ } else if (tagName == "style") {
1637+ cssTags.push_back(currentNode->children[0].getAttribute("innerText"));
1638+ }
1639+
1640+ currentNode = currentNode->parent;
1641+ } else {
1642+ std::cerr << "malformed html: closing tag (</" << tagName << ">) found for <" << currentNode->getTagName() << ">" << std::endl;
1643+ }
1644+ } else {
1645+ // Create a element and jump inside
1646+ auto attrs = parseAttributes(token);
1647+ // Don't add elements like <!DOCTYPE>
1648+ if (attrs["tagName"][0] != '!') {
1649+ currentNode = currentNode->createElement(attrs["tagName"]);
1650+ for (auto const& pair : attrs) {
1651+ // All attributes are stored as strings so we can just throw them in
1652+ currentNode->setAttribute(pair.first, pair.second);
1653+ }
1654+ }
1655+
1656+ }
1657+ }
1658+ }
1659+
1660+ token = "";
1661+ continue;
1662+ }
1663+ } else if (!escaped && current == '<') {
1664+ // if the next character is a !
1665+ if (inputStream.peek() == '!') {
1666+ char a,b,c;
1667+ // load the next three characters (includes !)
1668+ if (inputStream.get(a) && inputStream.get(b) && inputStream.get(c)) {
1669+ // We know a == !
1670+ if (b == '-' && c == '-') {
1671+ inComment = true;
1672+ }
1673+ }
1674+
1675+ if (inComment) {
1676+ continue;
1677+ } else {
1678+ // Not a comment add all back
1679+ inputStream.putback(c);
1680+ inputStream.putback(b);
1681+ inputStream.putback(a);
1682+ }
1683+ }
1684+ inTag = true;
1685+
1686+ // Here's where you actually make the text node and above the real nodes
1687+ // can also prob make the vector of tokens a single variable
1688+ // it really just needs to be a data string bc we know the type based on inTag
1689+
1690+
1691+ bool hasText = false;
1692+ for (auto t : token) {
1693+ if (!std::isspace(t)) {
1694+ hasText = true;
1695+ break;
1696+ }
1697+ }
1698+
1699+ // TODO: Add a vector that stores inner text and when elements start to close out pop them but add all to their innerText
1700+ // | Will need something similar for innerHTML
1701+ if (hasText) {
1702+ auto node = currentNode->createElement("text");
1703+ node->setAttribute("innerText", token);
1704+ }
1705+
1706+
1707+ token = "";
1708+ continue;
1709+ }
1710+
1711+ escaped = false;
1712+
1713+ if (current == '\\') { //'
1714+ escaped = true;
1715+ continue;
1716+ }
1717+
1718+ token += current;
1719+ }
1720+
1721+ ParsedDocument doc;
1722+ doc.document = root;
1723+
1724+ Style style;
1725+
1726+ std::string workingPath = get_working_path();
1727+
1728+ for (size_t i = 0; i < cssLinks.size(); i++) {
1729+ std::string link = cssLinks[i];
1730+
1731+ std::ifstream file(pathJoin(workingPath, link));
1732+
1733+ if (!file.is_open()) {
1734+ std::cerr << "Failed to open file!" << std::endl;
1735+ } else {
1736+ Style s = parseCSS(file);
1737+
1738+ for (size_t j = 0; j < s.children.size(); j++) {
1739+ style.addChild(s.children[j]);
1740+ }
1741+ }
1742+ file.close();
1743+ }
1744+
1745+ for (size_t i = 0; i < cssTags.size(); i++) {
1746+ std::string tag = cssTags[i];
1747+
1748+ std::istringstream iss(tag);
1749+ Style s = parseCSS(iss);
1750+
1751+ for (size_t j = 0; j < s.children.size(); j++) {
1752+ style.addChild(s.children[j]);
1753+ }
1754+ }
1755+
1756+ doc.CSS = style;
1757+
1758+ return doc;
1759+}
1760diff --git a/include/grim.h b/include/grim.h
1761index e281f45..3f6f5db 100755
1762--- a/include/grim.h
1763+++ b/include/grim.h
1764@@ -2,6 +2,7 @@
1765 #ifndef GRIM_H
1766 #define GRIM_H
1767
1768+#include <iostream>
1769 #include <string>
1770 #include <string_view>
1771 #include <vector>
1772@@ -11,10 +12,13 @@
1773 #include <charconv>
1774 #include <optional>
1775 #include <stdexcept>
1776+#include <deque>
1777
1778 // TODO:
1779 // * templates
1780
1781+
1782+
1783 enum class UnitType : short {
1784 // Color
1785 TEST, HEX,
1786@@ -86,14 +90,14 @@ enum class UnitType : short {
1787 NEAREST, UP, DOWN, TO_ZERO,
1788
1789 // Keyword
1790- PRINT, SCREEN, TOKEN, NO_SUPPORT, IMAGE, STRING, BOOL,
1791+ PRINT, SCREEN, TOKEN, NO_SUPPORT, IMAGE, STRING, BOOL, VAR, STRING_TERM, NONOP,
1792
1793 FUNC_START,
1794 // General Functions
1795 CALC = FUNC_START, MIN, MAX, CLAMP, ROUND, MOD, PROGRESS, REMD, POW, SQRT,
1796- HYPOT, LOG, EXP, ABS, SIGN, SIN, COS, TAN, ASIN, ACOS, ATAN, ATAN2, VAR,
1797+ HYPOT, LOG, EXP, ABS, SIGN, SIN, COS, TAN, ASIN, ACOS, ATAN, ATAN2, VAR_FUNC,
1798 BLUR, BRIGHTNESS, CONTRAST, DROP_SHADOW, GRAYSCALE, HUE_ROTATE, INVERT,
1799- OPACITY, SEPIA, SATURATE, ATTR, TYPE, ENV, IF, FUNC,
1800+ OPACITY, SEPIA, SATURATE, ATTR, TYPE, ENV, IF, FUNC, LET, RETURN, SET,
1801
1802 // Easing Functions
1803 EASING_FUNCTION, LINEAR_EASING_FUNCTION, LINEAR, EASE, EASE_IN, EASE_OUT, EASE_IN_OUT, CUBIC_BEZIER_FUNCTION,
1804@@ -107,7 +111,8 @@ enum class UnitType : short {
1805 RGB, RGBA, HSL,
1806
1807 // Image & Grouping
1808- URL, IMAGE_SET, CROSS_FADE, ELEMENT, GRADIENT, GROUP,
1809+ URL, IMAGE_SET, CROSS_FADE, ELEMENT, GROUP, GRADIENT,
1810+ NON_FUNC_PADDING
1811 };
1812
1813
1814@@ -127,6 +132,68 @@ struct Unit {
1815 UnitValue value;
1816 };
1817
1818+struct Range {
1819+ size_t start;
1820+ size_t end;
1821+};
1822+
1823+struct Context {
1824+ int id;
1825+ size_t index;
1826+ size_t length;
1827+ Context* previous;
1828+};
1829+
1830+struct Snapshot {
1831+ Context* ctx;
1832+ size_t pointer;
1833+};
1834+
1835+class Memory {
1836+ private:
1837+ std::deque<Context> arena;
1838+ Context* current = nullptr;
1839+ size_t pointer = 0;
1840+ public:
1841+ std::vector<Unit> heap;
1842+
1843+ Memory() { heap.resize(1024); }
1844+
1845+ void push(int id, std::vector<Unit>& units, size_t start, size_t end) {
1846+ size_t len = end-start;
1847+ arena.push_back({id, pointer, len, current});
1848+ current = &arena.back();
1849+ pointer += len;
1850+ std::copy(units.begin() + start, units.begin() + end, heap.begin() + pointer);
1851+ }
1852+
1853+ // Used to prefill the heap
1854+ void push_back(int id, Unit unit) {
1855+ arena.push_back({id, pointer, 1, current});
1856+ current = &arena.back();
1857+ heap[pointer] = unit;
1858+ pointer += 1;
1859+ }
1860+
1861+ Range get(int id) {
1862+ Context* walker = current;
1863+ while (walker != nullptr) {
1864+ if (walker->id == id) return {walker->index, walker->index+walker->length};
1865+ walker = walker->previous;
1866+ }
1867+ return {0,0}; // Not found
1868+ }
1869+
1870+ Snapshot save() {
1871+ return {current, pointer};
1872+ }
1873+
1874+ void restore(Snapshot s) {
1875+ current = s.ctx;
1876+ pointer = s.pointer;
1877+ }
1878+};
1879+
1880 class Window;
1881
1882 struct ClassList {
1883@@ -329,6 +396,13 @@ class IndexedMap {
1884 map.push_back({key, value});
1885 return map.size()-1;
1886 }
1887+
1888+
1889+ // add
1890+
1891+ // function to build Context and heap
1892+
1893+ //
1894 };
1895
1896 class Style {
1897@@ -338,10 +412,11 @@ class Style {
1898 std::string name; // remove
1899 std::vector<std::vector<std::string>> selector{};
1900
1901- std::unordered_map<KeyType, std::vector<Unit>> properties{};
1902-
1903+ std::unordered_map<KeyType, std::vector<Unit>> properties{}; // When computing have a is rel function that if not full compute the value
1904+ // like if the value is calc(1px + 10px) then just store 11px but if its 10vw
1905+ // then you can't
1906+
1907 IndexedMap variables{};
1908- std::vector<std::string> strings{};
1909
1910 // Nesting
1911 std::vector<Style> children{};
1912@@ -521,6 +596,6 @@ For the adapter, it will take a window as a argument to set the adapters capabil
1913 Window createWindow();
1914
1915
1916-Unit UnitValue(Node*, Style*, const std::vector<Unit>&, size_t, size_t);
1917+Unit UnitValue(Memory&, std::vector<Unit>&, size_t, size_t);
1918
1919 #endif // NODE_H
1920diff --git a/include/parser.h b/include/parser.h
1921index f7b94c8..abcbdca 100755
1922--- a/include/parser.h
1923+++ b/include/parser.h
1924@@ -16,10 +16,17 @@ struct ParsedDocument {
1925 Style CSS;
1926 };
1927
1928+struct SymbolTable {
1929+ std::unordered_map<std::string, int> table;
1930+ int next;
1931+
1932+ int get(std::string_view name);
1933+};
1934+
1935 // Declare the parsing functions
1936 std::unordered_map<std::string, std::string> parseAttributes(std::string_view& token);
1937 ParsedDocument parseDocument(std::istream& inputStream);
1938 Style parseCSS(std::istream& inputStream);
1939-std::vector<Unit> getUnit(Style*, std::string);
1940+std::vector<Unit> getUnit(std::string, SymbolTable* external = nullptr);
1941
1942 #endif
1943diff --git a/playground/syntax.wr b/playground/syntax.wr
1944new file mode 100644
1945index 0000000..f0648a9
1946--- /dev/null
1947+++ b/playground/syntax.wr
1948@@ -0,0 +1,58 @@
1949+Context -> width -> screen -> etc
1950+
1951+(
1952+ let(x, (1 + 1)),
1953+ let(x, x + 1),
1954+ print(x),
1955+
1956+ func(uppercase, (str), (
1957+ map(str, func((e),
1958+ e + 20
1959+ )
1960+ )
1961+ )),
1962+
1963+ let(uppercase, func((str),
1964+ map(str, func((e),
1965+ e + 20
1966+ )
1967+ )
1968+ )
1969+ ),
1970+
1971+ let(string_test, "this is a string"),
1972+ let(string_test, uppercase(string_test)),
1973+ print(string_test),
1974+ print(width),
1975+
1976+ let(array, list(1,2,3)),
1977+ print(array(0))
1978+)
1979+
1980+
1981+(
1982+ let(0, (1 + 1)),
1983+ let(0, 0 + 1),
1984+ print(0),
1985+
1986+ func(1, (2), (
1987+ map(2, func((3),
1988+ 3 + 20
1989+ )
1990+ )
1991+ )),
1992+
1993+ let(4, "this is a string"),
1994+ let(4, uppercase(4)),
1995+ print(4),
1996+ print(5),
1997+
1998+ let(6, list(1,2,3)),
1999+ print(6(0))
2000+)
2001+
2002+let 0, (1 + 1) adds the computed value to the end
2003+let 0, 0 + 1 get the value of 0 then add 1 then adds that to the end and updates 0's point to there
2004+func adds a pointer to the function def to the end
2005+anon functions do not have to add the function pointer
2006+
2007diff --git a/playground/unitvalue.cc b/playground/unitvalue.cc
2008index fc38e68..b0a0909 100644
2009--- a/playground/unitvalue.cc
2010+++ b/playground/unitvalue.cc
2011@@ -6,7 +6,7 @@
2012 #include <iostream>
2013
2014 int main() {
2015- std::string html = "<!DOCTYPE html>"
2016+ /*std::string html = "<!DOCTYPE html>"
2017 "<html>"
2018 "<head>"
2019 "<title>This is a simple test</title>"
2020@@ -45,11 +45,22 @@ int main() {
2021
2022 document.cacheWidth(1000);
2023 document.cacheHeight(700);
2024- document.cacheEM(16);
2025+ document.cacheEM(16);*/
2026
2027- std::vector<Unit> units = getUnit(&window.CSS, "(3 >= (1 + 5))");
2028+ SymbolTable sym{};
2029
2030- Unit u = UnitValue(&document, &window.CSS, units, 0, units.size());
2031+ Memory m;
2032+ // Preset width, height, and em
2033+ m.push_back(sym.get("width"),{UnitType::NUMBER, {.FLOAT = 1000.0f}});
2034+ m.push_back(sym.get("height"),{UnitType::NUMBER, {.FLOAT = 700.0f}});
2035+ m.push_back(sym.get("em"),{UnitType::NUMBER, {.FLOAT= 16.0f}});
2036
2037- std::cout << u.value.BOOL << std::endl;
2038+ //std::vector<Unit> units = getUnit("(let(x, 10px), set(x, (x + 1)), min(5, x))", &sym);
2039+ std::vector<Unit> units = getUnit("let(x 10px) set(x (x + 100)) min(5 x)", &sym);
2040+ //std::vector<Unit> units = getUnit("true and false", &sym);
2041+
2042+
2043+ Unit u = UnitValue(m, units, 0, units.size());
2044+
2045+ std::cout << "Value: " << u.value.FLOAT << std::endl;
2046 }
2047diff --git a/src/grim.cc b/src/grim.cc
2048index 5895fe4..cc02744 100755
2049--- a/src/grim.cc
2050+++ b/src/grim.cc
2051@@ -872,44 +872,23 @@ std::unordered_map<std::string, Media> MediaLookup {
2052 {"fast", Media::Fast},
2053 {"slow", Media::Slow},
2054 };
2055-
2056+/*
2057+// Media queries like @media (hover: hover) are written like @media (hover)
2058 bool executeQuery(Window* w, std::string p) {
2059- bool matches = false;
2060- if (p != " ") {
2061- // in parenthesis
2062- if (p[0] == '(') {
2063- std::string key;
2064- std::string value;
2065- bool colonFound = false;
2066-
2067- // if colonFound is false then key has the parsed substr
2068- // and we know what type of query we are dealing with
2069- for (size_t j = 1; j < p.length()-1; j++) {
2070- if (!colonFound) {
2071- if (p[j] == ':') {
2072- colonFound = true;
2073- } else {
2074- key.push_back(p[j]);
2075- }
2076- } else {
2077- value.push_back(p[j]);
2078- }
2079- }
2080+ Context c;
2081+ c.id = window->symbol_table.get("all");
2082+ c.value.type = UnitType::BOOL;
2083+ c.value.BOOL = w->getMedia() == Media::All;
2084+ c.previous = nullptr;
2085
2086- if (colonFound) {
2087- auto k = w->MediaFeatures.find(key);
2088- if (k != w->MediaFeatures.end()) {
2089- auto v = MediaLookup.find(value);
2090-
2091- if (v != MediaLookup.end()) {
2092- matches = k->second == v->second;
2093- }
2094- }
2095- } else {
2096- std::vector<Unit> units = getUnit(&w->CSS, key);
2097- }
2098- } else {
2099- // Lookup keyword
2100+
2101+
2102+ std::vector<Unit> parsed = getUnit();
2103+ add the Units to the end and set the indexes
2104+
2105+
2106+
2107+ // Lookup keyword
2108 if (p == "all") {
2109 matches = w->getMedia() == Media::All;
2110 } else if (p == "print") {
2111@@ -921,11 +900,7 @@ bool executeQuery(Window* w, std::string p) {
2112 }
2113 return matches;
2114 }
2115-
2116-struct Range {
2117- size_t start;
2118- size_t end;
2119-};
2120+*/
2121
2122 Range getNextUnit(const std::vector<Unit>& units, size_t& offset, size_t end) {
2123 if (offset >= end) return {0, 0}; // Done
2124@@ -944,7 +919,9 @@ Range getNextUnit(const std::vector<Unit>& units, size_t& offset, size_t end) {
2125 }
2126 }
2127
2128-Unit UnitValue(Node* n, Style* style, const std::vector<Unit>& units, size_t start, size_t end) {
2129+// HEAP: [width, height, em, dpi]
2130+
2131+Unit UnitValue(Memory& m, std::vector<Unit>& units, size_t start, size_t end) {
2132 Unit result{};
2133 result.type = UnitType::NUMBER;
2134 result.value.FLOAT = 0.0f;
2135@@ -956,7 +933,6 @@ Unit UnitValue(Node* n, Style* style, const std::vector<Unit>& units, size_t sta
2136 Unit unit = units[i];
2137 UnitType type = unit.type;
2138
2139-
2140 switch (type) {
2141 case UnitType::ADD:
2142 case UnitType::SUB:
2143@@ -1026,19 +1002,19 @@ Unit UnitValue(Node* n, Style* style, const std::vector<Unit>& units, size_t sta
2144 case UnitType::RIC:
2145 case UnitType::RLH: {
2146 iv.type = UnitType::NUMBER;
2147- iv.value.FLOAT = unit.value.FLOAT*n->getEM();
2148+ iv.value.FLOAT = unit.value.FLOAT*m.heap[2].value.FLOAT;
2149 break;
2150 }
2151 case UnitType::EX:
2152 case UnitType::REX: {
2153 iv.type = UnitType::NUMBER;
2154- iv.value.FLOAT = unit.value.FLOAT*(n->getEM()*0.5f);
2155+ iv.value.FLOAT = unit.value.FLOAT*(m.heap[2].value.FLOAT*0.5f);
2156 break;
2157 }
2158 case UnitType::CQH:
2159 case UnitType::VH: {
2160 iv.type = UnitType::NUMBER;
2161- iv.value.FLOAT = n->getHeight()*(unit.value.FLOAT*0.01f);
2162+ iv.value.FLOAT = m.heap[1].value.FLOAT*(unit.value.FLOAT*0.01f);
2163 break;
2164 }
2165 case UnitType::CQW:
2166@@ -1047,47 +1023,47 @@ Unit UnitValue(Node* n, Style* style, const std::vector<Unit>& units, size_t sta
2167 case UnitType::PERCENT:
2168 case UnitType::VW: {
2169 iv.type = UnitType::NUMBER;
2170- iv.value.FLOAT = n->getWidth()*(unit.value.FLOAT*0.01f);
2171+ iv.value.FLOAT = m.heap[0].value.FLOAT*(unit.value.FLOAT*0.01f);
2172 break;
2173 }
2174 case UnitType::VMAX:
2175 case UnitType::CQMAX: {
2176 iv.type = UnitType::NUMBER;
2177- float w = n->getWidth();
2178- float h = n->getHeight();
2179- if (w > h) {
2180- iv.value.FLOAT = unit.value.FLOAT*w;
2181+ // 0: width
2182+ // 1: height
2183+ if (m.heap[0].value.FLOAT > m.heap[1].value.FLOAT) {
2184+ iv.value.FLOAT = unit.value.FLOAT*m.heap[0].value.FLOAT;
2185 } else {
2186- iv.value.FLOAT = unit.value.FLOAT*h;
2187+ iv.value.FLOAT = unit.value.FLOAT*m.heap[1].value.FLOAT;
2188 }
2189 break;
2190 }
2191 case UnitType::VMIN:
2192 case UnitType::CQMIN: {
2193 iv.type = UnitType::NUMBER;
2194- float w = n->getWidth();
2195- float h = n->getHeight();
2196- if (w < h) {
2197- iv.value.FLOAT = unit.value.FLOAT*w;
2198+ if (m.heap[0].value.FLOAT < m.heap[1].value.FLOAT) {
2199+ iv.value.FLOAT = unit.value.FLOAT*m.heap[0].value.FLOAT;
2200 } else {
2201- iv.value.FLOAT = unit.value.FLOAT*h;
2202+ iv.value.FLOAT = unit.value.FLOAT*m.heap[1].value.FLOAT;
2203 }
2204 break;
2205 }
2206 case UnitType::VB:
2207 case UnitType::VI: {
2208 iv.type = UnitType::NUMBER;
2209- iv.value.FLOAT = unit.value.FLOAT*n->getWidth();
2210+ iv.value.FLOAT = unit.value.FLOAT*m.heap[0].value.FLOAT;
2211 break;
2212 }
2213 case UnitType::GROUP: {
2214- iv = UnitValue(n, style, units, i+1, i+1+unit.value.INT);
2215+ Snapshot s = m.save();
2216+ iv = UnitValue(m, units, i+1, i+1+unit.value.INT);
2217 i += unit.value.INT;
2218+ m.restore(s);
2219 break;
2220 }
2221 case UnitType::CALC: {
2222 iv.type = UnitType::NUMBER;
2223- Unit u1 = UnitValue(n, style, units, i+1, i+1+unit.value.INT);
2224+ Unit u1 = UnitValue(m, units, i+1, i+1+unit.value.INT);
2225 iv.value.FLOAT = u1.value.FLOAT;
2226 i += unit.value.INT;
2227 break;
2228@@ -1099,11 +1075,12 @@ Unit UnitValue(Node* n, Style* style, const std::vector<Unit>& units, size_t sta
2229 size_t end = i + 1 + unit.value.INT;
2230
2231 Range r = getNextUnit(units, start, end);
2232- iv.value.FLOAT = UnitValue(n, style, units, r.start, r.end).value.FLOAT;
2233+ iv.value.FLOAT = UnitValue(m, units, r.start, r.end).value.FLOAT;
2234
2235 while (start < end) {
2236 r = getNextUnit(units, start, end);
2237- iv.value.FLOAT = std::min(iv.value.FLOAT, UnitValue(n, style, units, r.start, r.end).value.FLOAT);
2238+ float u = UnitValue(m, units, r.start, r.end).value.FLOAT;
2239+ iv.value.FLOAT = std::min(iv.value.FLOAT, u);
2240 }
2241
2242 i += unit.value.INT;
2243@@ -1116,11 +1093,11 @@ Unit UnitValue(Node* n, Style* style, const std::vector<Unit>& units, size_t sta
2244 size_t end = i + 1 + unit.value.INT;
2245
2246 Range r = getNextUnit(units, start, end);
2247- iv.value.FLOAT = UnitValue(n, style, units, r.start, r.end).value.FLOAT;
2248+ iv.value.FLOAT = UnitValue(m, units, r.start, r.end).value.FLOAT;
2249
2250 while (start < end) {
2251 r = getNextUnit(units, start, end);
2252- iv.value.FLOAT = std::max(iv.value.FLOAT, UnitValue(n, style, units, r.start, r.end).value.FLOAT);
2253+ iv.value.FLOAT = std::max(iv.value.FLOAT, UnitValue(m, units, r.start, r.end).value.FLOAT);
2254 }
2255
2256 i += unit.value.INT;
2257@@ -1136,7 +1113,7 @@ Unit UnitValue(Node* n, Style* style, const std::vector<Unit>& units, size_t sta
2258
2259 while (start < end) {
2260 Range r = getNextUnit(units, start, end);
2261- args[found++] = UnitValue(n, style, units, r.start, r.end).value.FLOAT;
2262+ args[found++] = UnitValue(m, units, r.start, r.end).value.FLOAT;
2263 }
2264
2265 if (found == 3) {
2266@@ -1160,7 +1137,7 @@ Unit UnitValue(Node* n, Style* style, const std::vector<Unit>& units, size_t sta
2267
2268 while (start < end) {
2269 Range r = getNextUnit(units, start, end);
2270- args[found++] = UnitValue(n, style, units, r.start, r.end);
2271+ args[found++] = UnitValue(m, units, r.start, r.end);
2272 }
2273
2274 if (found == 3) {
2275@@ -1202,7 +1179,7 @@ Unit UnitValue(Node* n, Style* style, const std::vector<Unit>& units, size_t sta
2276
2277 while (start < end) {
2278 Range r = getNextUnit(units, start, end);
2279- args[found++] = UnitValue(n, style, units, r.start, r.end).value.FLOAT;
2280+ args[found++] = UnitValue(m, units, r.start, r.end).value.FLOAT;
2281 }
2282
2283 if (found == 2) {
2284@@ -1224,7 +1201,7 @@ Unit UnitValue(Node* n, Style* style, const std::vector<Unit>& units, size_t sta
2285
2286 while (start < end) {
2287 Range r = getNextUnit(units, start, end);
2288- args[found++] = UnitValue(n, style, units, r.start, r.end).value.FLOAT;
2289+ args[found++] = UnitValue(m, units, r.start, r.end).value.FLOAT;
2290 }
2291
2292 if (found == 3) {
2293@@ -1248,7 +1225,7 @@ Unit UnitValue(Node* n, Style* style, const std::vector<Unit>& units, size_t sta
2294
2295 while (start < end) {
2296 Range r = getNextUnit(units, start, end);
2297- args[found++] = UnitValue(n, style, units, r.start, r.end).value.FLOAT;
2298+ args[found++] = UnitValue(m, units, r.start, r.end).value.FLOAT;
2299 }
2300
2301 if (found == 2) {
2302@@ -1262,42 +1239,42 @@ Unit UnitValue(Node* n, Style* style, const std::vector<Unit>& units, size_t sta
2303 }
2304 case UnitType::SIN: {
2305 iv.type = UnitType::NUMBER;
2306- Unit u1 = UnitValue(n, style, units, i+1, i+1+unit.value.INT);
2307+ Unit u1 = UnitValue(m, units, i+1, i+1+unit.value.INT);
2308 iv.value.FLOAT = std::sinf(u1.value.FLOAT);
2309 i += unit.value.INT;
2310 break;
2311 }
2312 case UnitType::COS: {
2313 iv.type = UnitType::NUMBER;
2314- Unit u1 = UnitValue(n, style, units, i+1, i+1+unit.value.INT);
2315+ Unit u1 = UnitValue(m, units, i+1, i+1+unit.value.INT);
2316 iv.value.FLOAT = std::cosf(u1.value.FLOAT);
2317 i += unit.value.INT;
2318 break;
2319 }
2320 case UnitType::TAN: {
2321 iv.type = UnitType::NUMBER;
2322- Unit u1 = UnitValue(n, style, units, i+1, i+1+unit.value.INT);
2323+ Unit u1 = UnitValue(m, units, i+1, i+1+unit.value.INT);
2324 iv.value.FLOAT = std::tanf(u1.value.FLOAT);
2325 i += unit.value.INT;
2326 break;
2327 }
2328 case UnitType::ASIN: {
2329 iv.type = UnitType::NUMBER;
2330- Unit u1 = UnitValue(n, style, units, i+1, i+1+unit.value.INT);
2331+ Unit u1 = UnitValue(m, units, i+1, i+1+unit.value.INT);
2332 iv.value.FLOAT = std::asinf(u1.value.FLOAT);
2333 i += unit.value.INT;
2334 break;
2335 }
2336 case UnitType::ACOS: {
2337 iv.type = UnitType::NUMBER;
2338- Unit u1 = UnitValue(n, style, units, i+1, i+1+unit.value.INT);
2339+ Unit u1 = UnitValue(m, units, i+1, i+1+unit.value.INT);
2340 iv.value.FLOAT = std::acosf(u1.value.FLOAT);
2341 i += unit.value.INT;
2342 break;
2343 }
2344 case UnitType::ATAN: {
2345 iv.type = UnitType::NUMBER;
2346- Unit u1 = UnitValue(n, style, units, i+1, i+1+unit.value.INT);
2347+ Unit u1 = UnitValue(m, units, i+1, i+1+unit.value.INT);
2348 iv.value.FLOAT = std::atanf(u1.value.FLOAT);
2349 i += unit.value.INT;
2350 break;
2351@@ -1312,7 +1289,7 @@ Unit UnitValue(Node* n, Style* style, const std::vector<Unit>& units, size_t sta
2352
2353 while (start < end) {
2354 Range r = getNextUnit(units, start, end);
2355- args[found++] = UnitValue(n, style, units, r.start, r.end).value.FLOAT;
2356+ args[found++] = UnitValue(m, units, r.start, r.end).value.FLOAT;
2357 }
2358
2359 if (found == 2) {
2360@@ -1334,7 +1311,7 @@ Unit UnitValue(Node* n, Style* style, const std::vector<Unit>& units, size_t sta
2361
2362 while (start < end) {
2363 Range r = getNextUnit(units, start, end);
2364- args[found++] = UnitValue(n, style, units, r.start, r.end).value.FLOAT;
2365+ args[found++] = UnitValue(m, units, r.start, r.end).value.FLOAT;
2366 }
2367
2368 if (found == 2) {
2369@@ -1348,7 +1325,7 @@ Unit UnitValue(Node* n, Style* style, const std::vector<Unit>& units, size_t sta
2370 }
2371 case UnitType::SQRT: {
2372 iv.type = UnitType::NUMBER;
2373- Unit u1 = UnitValue(n, style, units, i+1, i+1+unit.value.INT);
2374+ Unit u1 = UnitValue(m, units, i+1, i+1+unit.value.INT);
2375 iv.value.FLOAT = std::sqrtf(u1.value.FLOAT);
2376 i += unit.value.INT;
2377 break;
2378@@ -1363,7 +1340,7 @@ Unit UnitValue(Node* n, Style* style, const std::vector<Unit>& units, size_t sta
2379
2380 while (start < end) {
2381 Range r = getNextUnit(units, start, end);
2382- float t = UnitValue(n, style, units, r.start, r.end).value.FLOAT;
2383+ float t = UnitValue(m, units, r.start, r.end).value.FLOAT;
2384 v += t * t;
2385 }
2386
2387@@ -1382,7 +1359,7 @@ Unit UnitValue(Node* n, Style* style, const std::vector<Unit>& units, size_t sta
2388
2389 while (start < end) {
2390 Range r = getNextUnit(units, start, end);
2391- args[found++] = UnitValue(n, style, units, r.start, r.end).value.FLOAT;
2392+ args[found++] = UnitValue(m, units, r.start, r.end).value.FLOAT;
2393 }
2394
2395 if (found == 2) {
2396@@ -1396,21 +1373,21 @@ Unit UnitValue(Node* n, Style* style, const std::vector<Unit>& units, size_t sta
2397 }
2398 case UnitType::EXP: {
2399 iv.type = UnitType::NUMBER;
2400- Unit u1 = UnitValue(n, style, units, i+1, i+1+unit.value.INT);
2401+ Unit u1 = UnitValue(m, units, i+1, i+1+unit.value.INT);
2402 iv.value.FLOAT = std::expf(u1.value.FLOAT);
2403 i += unit.value.INT;
2404 break;
2405 }
2406 case UnitType::ABS: {
2407 iv.type = UnitType::NUMBER;
2408- Unit u1 = UnitValue(n, style, units, i+1, i+1+unit.value.INT);
2409+ Unit u1 = UnitValue(m, units, i+1, i+1+unit.value.INT);
2410 iv.value.FLOAT = std::fabsf(u1.value.FLOAT);
2411 i += unit.value.INT;
2412 break;
2413 }
2414 case UnitType::SIGN: {
2415 iv.type = UnitType::NUMBER;
2416- Unit u1 = UnitValue(n, style, units, i+1, i+1+unit.value.INT);
2417+ Unit u1 = UnitValue(m, units, i+1, i+1+unit.value.INT);
2418
2419 if (u1.value.FLOAT > 0) {
2420 iv.value.FLOAT = 1;
2421@@ -1433,40 +1410,37 @@ Unit UnitValue(Node* n, Style* style, const std::vector<Unit>& units, size_t sta
2422 args[found++] = getNextUnit(units, start, end);
2423 }
2424
2425- Unit u1 = UnitValue(n, style, units, args[0].start, args[0].end);
2426+ Unit u1 = units[args[0].start];
2427
2428 if (u1.type == UnitType::VAR) {
2429- bool has = n->hasAttribute(style->variables.key(u1.value.INT));
2430- std::string value = n->getAttribute(style->variables.key(u1.value.INT));
2431-
2432- if (!value.empty() && has) {
2433- // attr(size px, 10)
2434- // s = n->getAttribute(size) size="20"
2435- std::vector<Unit> us = getUnit(style, value);
2436- // us = {NUMBER, 20}
2437- // extracted[1] == {type: PX} || {type::TYPE}
2438- // Add the type of the next if the type is inside of type(px) or just add the type px
2439+
2440+ Range r = m.get(u1.value.INT); // Pull the marker off the ctx
2441+ Unit value = UnitValue(m, units, r.start, r.end);
2442+
2443+ if (r.end - r.start > 0) {
2444 if (found > 1) {
2445- Unit u2 = UnitValue(n, style, units, args[1].start, args[1].end);
2446+ // Get the passed type attr(0(value) 1(type), 2(default))
2447+ Unit u2 = units[args[1].start];
2448
2449+ // Cast the user defined type onto the value
2450 if (u2.type == UnitType::TYPE) {
2451- us[0].type = units[args[1].start+1].type;
2452+ value.type = units[args[1].start+1].type;
2453 } else {
2454- us[0].type = u2.type;
2455+ value.type = u2.type;
2456 }
2457 }
2458
2459- iv = UnitValue(n, style, us, 0, us.size());
2460+ iv = value;
2461 } else if (found == 3) {
2462 // Default value
2463- iv = UnitValue(n, style, units, args[2].start, args[2].end);
2464+ iv = UnitValue(m, units, args[2].start, args[2].end);
2465 }
2466 }
2467
2468 i += unit.value.INT;
2469 break;
2470 }
2471- case UnitType::VAR: {
2472+ case UnitType::VAR_FUNC: {
2473 Unit args[2];
2474 int found = 0;
2475 size_t start = i + 1;
2476@@ -1474,15 +1448,14 @@ Unit UnitValue(Node* n, Style* style, const std::vector<Unit>& units, size_t sta
2477
2478 while (start < end) {
2479 Range r = getNextUnit(units, start, end);
2480- args[found++] = UnitValue(n, style, units, r.start, r.end);
2481+ args[found++] = UnitValue(m, units, r.start, r.end);
2482 }
2483
2484 if (args[0].type == UnitType::VAR) {
2485- // Variables flow down during processing so theres no need to check above
2486- std::vector<Unit> us = style->variables.at(args[0].value.INT);
2487+ Range r = m.get(args[0].value.INT); // Pull the marker off the ctx
2488
2489- if (us.size() > 0) {
2490- iv = UnitValue(n, style, us, 0, us.size());
2491+ if (r.end > 0) {
2492+ iv = UnitValue(m, units, r.start, r.end);
2493 } else if (found == 2) {
2494 // Default value
2495 iv = args[1];
2496@@ -1492,61 +1465,105 @@ Unit UnitValue(Node* n, Style* style, const std::vector<Unit>& units, size_t sta
2497 i += unit.value.INT;
2498 break;
2499 }
2500- }
2501+ case UnitType::VAR: {
2502+ Range r = m.get(unit.value.INT);
2503
2504- if (iv.type == UnitType::NUMBER) {
2505- switch (op) {
2506- case UnitType::ADD:
2507- result.value.FLOAT += iv.value.FLOAT;
2508- break;
2509- case UnitType::SUB:
2510- result.value.FLOAT -= iv.value.FLOAT;
2511- break;
2512- case UnitType::DIV:
2513- result.value.FLOAT /= iv.value.FLOAT;
2514- break;
2515- case UnitType::MULT:
2516- result.value.FLOAT *= iv.value.FLOAT;
2517- break;
2518- case UnitType::LT:
2519- result.type = UnitType::BOOL;
2520- result.value.BOOL = result.value.FLOAT < iv.value.FLOAT;
2521- break;
2522- case UnitType::GT:
2523- result.type = UnitType::BOOL;
2524- result.value.BOOL = result.value.FLOAT > iv.value.FLOAT;
2525- break;
2526- case UnitType::LE:
2527- result.type = UnitType::BOOL;
2528- result.value.BOOL = result.value.FLOAT <= iv.value.FLOAT;
2529- break;
2530- case UnitType::GE:
2531- result.type = UnitType::BOOL;
2532- result.value.BOOL = result.value.FLOAT >= iv.value.FLOAT;
2533- break;
2534- case UnitType::EQ:
2535- result.type = UnitType::BOOL;
2536- result.value.BOOL = result.value.FLOAT == iv.value.FLOAT;
2537- break;
2538+ if (r.end > 0) {
2539+ iv = m.heap[r.start];
2540+ }
2541+
2542+ break;
2543 }
2544- } else if (iv.type == UnitType::BOOL) {
2545- result.type = UnitType::BOOL;
2546- switch (op) {
2547- case UnitType::AND:
2548- result.value.BOOL = result.value.BOOL && iv.value.BOOL;
2549- break;
2550- case UnitType::OR:
2551- result.value.BOOL = result.value.BOOL || iv.value.BOOL;
2552- break;
2553- case UnitType::NOT:
2554- result.value.BOOL = !iv.value.BOOL;
2555- break;
2556- case UnitType::EQ:
2557- result.value.BOOL = result.value.BOOL == iv.value.BOOL;
2558- default:
2559- result.value.BOOL = iv.value.BOOL;
2560+ case UnitType::LET: {
2561+ if (units[start + 1].type == UnitType::VAR) {
2562+ Unit u = UnitValue(m, units, i + 2, i + 1 + unit.value.INT);
2563+ m.push_back(units[i+1].value.INT, u);
2564+ }
2565+
2566+ i += unit.value.INT;
2567+ break;
2568+ }
2569+ case UnitType::SET: {
2570+ if (units[i + 1].type == UnitType::VAR) {
2571+ Range r = m.get(units[i + 1].value.INT);
2572+ Unit u = UnitValue(m, units, i + 2, i + 1 + unit.value.INT);
2573+
2574+ // r.end is the length
2575+ if (r.end > 0) { // need to write over
2576+ m.heap[r.start] = u;
2577+ } else {
2578+ m.push_back(units[i + 1].value.INT, u);
2579+ }
2580+ }
2581+
2582+ i += unit.value.INT;
2583 }
2584 }
2585+
2586+ switch (op) {
2587+ case UnitType::ADD:
2588+ result.type = UnitType::NUMBER;
2589+ result.value.FLOAT += iv.value.FLOAT;
2590+ op = UnitType::NONOP;
2591+ break;
2592+ case UnitType::SUB:
2593+ result.type = UnitType::NUMBER;
2594+ result.value.FLOAT -= iv.value.FLOAT;
2595+ op = UnitType::NONOP;
2596+ break;
2597+ case UnitType::DIV:
2598+ result.type = UnitType::NUMBER;
2599+ result.value.FLOAT /= iv.value.FLOAT;
2600+ op = UnitType::NONOP;
2601+ break;
2602+ case UnitType::MULT:
2603+ result.type = UnitType::NUMBER;
2604+ result.value.FLOAT *= iv.value.FLOAT;
2605+ op = UnitType::NONOP;
2606+ break;
2607+ case UnitType::LT:
2608+ result.type = UnitType::BOOL;
2609+ result.value.BOOL = result.value.FLOAT < iv.value.FLOAT;
2610+ op = UnitType::NONOP;
2611+ break;
2612+ case UnitType::GT:
2613+ result.type = UnitType::BOOL;
2614+ result.value.BOOL = result.value.FLOAT > iv.value.FLOAT;
2615+ op = UnitType::NONOP;
2616+ break;
2617+ case UnitType::LE:
2618+ result.type = UnitType::BOOL;
2619+ result.value.BOOL = result.value.FLOAT <= iv.value.FLOAT;
2620+ op = UnitType::NONOP;
2621+ break;
2622+ case UnitType::GE:
2623+ result.type = UnitType::BOOL;
2624+ result.value.BOOL = result.value.FLOAT >= iv.value.FLOAT;
2625+ op = UnitType::NONOP;
2626+ break;
2627+ case UnitType::EQ:
2628+ result.type = UnitType::BOOL;
2629+ result.value.BOOL = result.value.FLOAT == iv.value.FLOAT;
2630+ op = UnitType::NONOP;
2631+ break;
2632+ case UnitType::AND:
2633+ result.type = UnitType::BOOL;
2634+ result.value.BOOL = result.value.FLOAT && iv.value.FLOAT;
2635+ op = UnitType::NONOP;
2636+ break;
2637+ case UnitType::OR:
2638+ result.type = UnitType::BOOL;
2639+ result.value.BOOL = result.value.FLOAT || iv.value.FLOAT;
2640+ op = UnitType::NONOP;
2641+ break;
2642+ case UnitType::NOT:
2643+ result.type = UnitType::BOOL;
2644+ result.value.BOOL = !iv.value.FLOAT;
2645+ op = UnitType::NONOP;
2646+ break;
2647+ default:
2648+ result = iv;
2649+ }
2650 }
2651
2652 return result;
2653diff --git a/src/parser.cc b/src/parser.cc
2654index fca724a..c67be3a 100755
2655--- a/src/parser.cc
2656+++ b/src/parser.cc
2657@@ -8,6 +8,20 @@
2658 #include <cmath>
2659 #include <string_view>
2660
2661+struct SymbolTable {
2662+ std::unordered_map<std::string, int> table{};
2663+ int next = 5;
2664+
2665+ int get(std::string_view name) {
2666+ auto it = table.find(std::string(name));
2667+ if (it != table.end()) return it->second;
2668+
2669+ int id = next++;
2670+ table[std::string(name)] = id;
2671+ return id;
2672+ }
2673+};
2674+
2675 std::unordered_map<std::string, std::string> parseAttributes(std::string_view token) {
2676 std::unordered_map<std::string, std::string> attrs;
2677
2678@@ -774,7 +788,7 @@ struct SuffixParse {
2679 int trim; // chars to strip from the right
2680 };
2681
2682-static constexpr SuffixParse table[] = {
2683+static constexpr SuffixParse sufMatch[] = {
2684 {"cqmin", UnitType::CQMIN, 5},
2685 {"cqmax", UnitType::CQMAX, 5},
2686 {"rcap", UnitType::RCAP, 4},
2687@@ -970,6 +984,8 @@ static const std::unordered_map<std::string_view, UnitType> kFunc{
2688 {"rem", UnitType::REMD},
2689 {"pow", UnitType::POW},
2690 {"sqrt", UnitType::SQRT},
2691+ {"set", UnitType::SET},
2692+ {"let", UnitType::LET},
2693 {"hypot", UnitType::HYPOT},
2694 {"log", UnitType::LOG},
2695 {"exp", UnitType::EXP},
2696@@ -982,7 +998,7 @@ static const std::unordered_map<std::string_view, UnitType> kFunc{
2697 {"atan", UnitType::ATAN},
2698 {"atan2", UnitType::ATAN2},
2699 {"sign", UnitType::SIGN},
2700- {"var", UnitType::VAR},
2701+ {"var", UnitType::VAR_FUNC},
2702 {"rgb", UnitType::RGB},
2703 {"rgba", UnitType::RGBA},
2704 {"hsl", UnitType::HSL},
2705@@ -1044,8 +1060,11 @@ static const std::unordered_map<std::string_view, float> kAbsSize{
2706
2707 */
2708
2709-std::vector<Unit> getUnit(Style* s, std::string value) {
2710- std::vector<Unit> units;
2711+std::vector<Unit> getUnit(std::string value, SymbolTable* externalTable = nullptr) {
2712+ SymbolTable localTable;
2713+ SymbolTable& table = (externalTable != nullptr) ? *externalTable : localTable;
2714+
2715+ std::vector<Unit> units;
2716
2717 std::string buffer = "";
2718 std::string str_buffer = "";
2719@@ -1060,12 +1079,18 @@ std::vector<Unit> getUnit(Style* s, std::string value) {
2720 if (v == '\\' && !escaped) escaped = true;
2721 if (!escaped && (v == '"' || v == '\'') && parenDepth == 0) {
2722 if (inQuotes && v == quoteType) {
2723- s->strings.push_back(str_buffer);
2724+ for (size_t j = 0; j < str_buffer.length(); j++) {
2725+ Unit unit{};
2726+ unit.type = UnitType::STRING;
2727+ unit.value.CHAR = str_buffer[j];
2728+ units.push_back(unit);
2729+ }
2730+
2731 str_buffer.clear();
2732
2733 Unit unit{};
2734- unit.type = UnitType::STRING;
2735- unit.value.INT = s->strings.size()-1;
2736+ unit.type = UnitType::STRING_TERM;
2737+ unit.value.INT = str_buffer.length();
2738 units.push_back(unit);
2739
2740 inQuotes = false;
2741@@ -1102,7 +1127,7 @@ std::vector<Unit> getUnit(Style* s, std::string value) {
2742 }
2743 }
2744
2745- std::vector<Unit> children = getUnit(s, value);
2746+ std::vector<Unit> children = getUnit(value, &table);
2747
2748 Unit unit;
2749
2750@@ -1141,7 +1166,7 @@ std::vector<Unit> getUnit(Style* s, std::string value) {
2751 unit.value.UINT = it->second;
2752 } else {
2753 bool found = false;
2754- for (const auto& [suffix, type, trim] : table) {
2755+ for (const auto& [suffix, type, trim] : sufMatch) {
2756 if (buffer.ends_with(suffix)) {
2757 found = true;
2758 try {
2759@@ -1168,6 +1193,12 @@ std::vector<Unit> getUnit(Style* s, std::string value) {
2760 */
2761 unit.type = UnitType::EM;
2762 unit.value.FLOAT = it->second;
2763+ } else if (buffer == "true") {
2764+ unit.type = UnitType::BOOL;
2765+ unit.value.BOOL = true;
2766+ } else if (buffer == "false") {
2767+ unit.type = UnitType::BOOL;
2768+ unit.value.BOOL = false;
2769 } else {
2770 // Maybe its a number?
2771 try {
2772@@ -1179,7 +1210,7 @@ std::vector<Unit> getUnit(Style* s, std::string value) {
2773 // parsed but not size.
2774 if (!buffer.empty() && buffer.find_first_not_of(' ') != std::string::npos) {
2775 unit.type = UnitType::VAR;
2776- unit.value.INT = s->variables.set(buffer, {});
2777+ unit.value.INT = table.get(buffer);
2778 } else {
2779 continue;
2780 }
2781@@ -1216,6 +1247,9 @@ Style parseCSS(std::istream& inputStream) {
2782
2783 bool incomment = false;
2784
2785+
2786+ SymbolTable table;
2787+
2788 char ch;
2789 while (inputStream.get(ch)) {
2790 if (ch == '*' && inputStream.peek() == '/') {
2791@@ -1354,7 +1388,7 @@ Style parseCSS(std::istream& inputStream) {
2792 }
2793
2794 if (key.length() > 2 && key[0] == '-' && key[1] == '-') {
2795- current->variables.set(key, getUnit(current, value));
2796+ current->variables.set(key, getUnit(value, &table));
2797 } else {
2798 KeyType keyENUM;
2799
2800@@ -1363,7 +1397,7 @@ Style parseCSS(std::istream& inputStream) {
2801 } else {
2802 std::cout << "Invalid property name: " << key << std::endl;
2803 }
2804- current->properties[keyENUM] = getUnit(current, value);
2805+ current->properties[keyENUM] = getUnit(value, &table);
2806 }
2807 }
2808 buffer.clear();
2809diff --git a/tests/unitvalue.cc b/tests/unitvalue.cc
2810index aac413c..605943f 100644
2811--- a/tests/unitvalue.cc
2812+++ b/tests/unitvalue.cc
2813@@ -5,6 +5,9 @@
2814 #include <sstream>
2815 #include <vector>
2816
2817+
2818+
2819+/*
2820 TEST_CASE("Benchmark UnitValue evaluation", "[performance][grim]") {
2821 Window window = createWindow();
2822
2823@@ -14,9 +17,9 @@ TEST_CASE("Benchmark UnitValue evaluation", "[performance][grim]") {
2824 document.cacheEM(16);
2825
2826 SECTION("(3 >= min(1, 5)") {
2827- std::vector<Unit> units = getUnit(&window.CSS, "(3 >= min(1, 5))");
2828+ std::vector<Unit> units = getUnit("(3 >= min(1, 5))");
2829
2830- REQUIRE(UnitValue(&document, &window.CSS, units, 0, units.size()).value.BOOL);
2831+ REQUIRE(UnitValue(&document, units, 0, units.size()).value.BOOL);
2832 }
2833 }
2834
2835@@ -30,59 +33,81 @@ TEST_CASE("UnitValue Logic and Math", "[grim][logic]") {
2836 SECTION("Math Operators and Precedence (Group)") {
2837 // (10 + 2 * 5) -> In your linear engine, without GROUP this is 60.
2838 // With GROUP: (10 + (2 * 5)) = 20
2839- std::vector<Unit> units = getUnit(&window.CSS, "(10 + (2 * 5))");
2840- auto res = UnitValue(&document, &window.CSS, units, 0, units.size());
2841+ std::vector<Unit> units = getUnit("(10 + (2 * 5))");
2842+ auto res = UnitValue(&document, units, 0, units.size());
2843 REQUIRE(res.value.FLOAT == 20.0f);
2844 }
2845
2846 SECTION("CSS Units Conversion") {
2847 // 1in = 96px, 1cm = 37.8px
2848- std::vector<Unit> units = getUnit(&window.CSS, "1in + 4px");
2849- auto res = UnitValue(&document, &window.CSS, units, 0, units.size());
2850+ std::vector<Unit> units = getUnit("1in + 4px");
2851+ auto res = UnitValue(&document, units, 0, units.size());
2852 REQUIRE(res.value.FLOAT == 100.0f);
2853 }
2854
2855 SECTION("Viewport Units") {
2856 // 50vw (500px) + 10vh (50px) = 550px
2857- std::vector<Unit> units = getUnit(&window.CSS, "50vw + 10vh");
2858- auto res = UnitValue(&document, &window.CSS, units, 0, units.size());
2859+ std::vector<Unit> units = getUnit("50vw + 10vh");
2860+ auto res = UnitValue(&document, units, 0, units.size());
2861 REQUIRE(res.value.FLOAT == 550.0f);
2862 }
2863
2864 SECTION("Clamping and Min/Max") {
2865 // clamp(100, 50, 200) -> 100 (50 is below min)
2866- std::vector<Unit> units = getUnit(&window.CSS, "clamp(100, 50, 200)");
2867- auto res = UnitValue(&document, &window.CSS, units, 0, units.size());
2868+ std::vector<Unit> units = getUnit("clamp(100, 50, 200)");
2869+ auto res = UnitValue(&document, units, 0, units.size());
2870 REQUIRE(res.value.FLOAT == 100.0f);
2871
2872 // max(10, 20, 30, 5) -> 30
2873- units = getUnit(&window.CSS, "max(10, 20, 30, 5)");
2874- res = UnitValue(&document, &window.CSS, units, 0, units.size());
2875+ units = getUnit("max(10, 20, 30, 5)");
2876+ res = UnitValue(&document, units, 0, units.size());
2877 REQUIRE(res.value.FLOAT == 30.0f);
2878 }
2879
2880 SECTION("Logical Operators (Boolean)") {
2881 // (10 > 5) AND (2 <= 2) -> true
2882- std::vector<Unit> units = getUnit(&window.CSS, "(10 > 5) and (2 <= 2)");
2883- auto res1 = UnitValue(&document, &window.CSS, units, 0, units.size());
2884+ std::vector<Unit> units = getUnit("(10 > 5) and (2 <= 2)");
2885+ auto res1 = UnitValue(&document, units, 0, units.size());
2886 REQUIRE(res1.type == UnitType::BOOL);
2887 REQUIRE(res1.value.BOOL == true);
2888
2889 // (10 < 5) OR (1 == 1) -> true
2890- units = getUnit(&window.CSS, "(10 < 5) or (1 == 1)");
2891- auto res2 = UnitValue(&document, &window.CSS, units, 0, units.size());
2892+ units = getUnit("(10 < 5) or (1 == 1)");
2893+ auto res2 = UnitValue(&document, units, 0, units.size());
2894 REQUIRE(res2.value.BOOL == true);
2895 }
2896
2897 SECTION("Deep Recursion Stress") {
2898 // (((1 + 1) + 1) + 1) ...
2899 std::string deep = "(1 + (1 + (1 + (1 + (1 + (1 + 1))))))";
2900- std::vector<Unit> units = getUnit(&window.CSS, deep);
2901+ std::vector<Unit> units = getUnit(deep);
2902
2903 BENCHMARK("Deep Math Recursion") {
2904- return UnitValue(&document, &window.CSS, units, 0, units.size());
2905+ return UnitValue(&document, units, 0, units.size());
2906 };
2907 }
2908 }
2909
2910+*/
2911+
2912+TEST_CASE("BENCH") {
2913+ SECTION("BENCH") {
2914+ SymbolTable sym{};
2915+
2916+ Memory m;
2917+ // Preset width, height, and em
2918+ m.push_back(sym.get("width"),{UnitType::NUMBER, {.FLOAT = 1000.0f}});
2919+ m.push_back(sym.get("height"),{UnitType::NUMBER, {.FLOAT = 700.0f}});
2920+ m.push_back(sym.get("em"),{UnitType::NUMBER, {.FLOAT= 16.0f}});
2921
2922+ std::vector<Unit> units = getUnit("let(x 10px) set(x (x + 100)) min(5 x)", &sym);
2923+
2924+ Snapshot s = m.save();
2925+
2926+ BENCHMARK("(3 >= 1em))") {
2927+ Unit u = UnitValue(m, units, 0, units.size());
2928+ m.restore(s);
2929+ return u;
2930+ };
2931+ }
2932+}