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 = &current->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+}