home readme diff tree note docs

0commit 8d8a7243f03d1d683d9235f4a183e8a2c0594bb6
1Author: lakefox <mason@lakefox.net>
2Date:   Fri Jan 23 16:24:22 2026 -0700
3
4    If added
5
6diff --git a/:w b/:w
7deleted file mode 100644
8index de2bf61..0000000
9--- a/:w
10+++ /dev/null
11@@ -1,1748 +0,0 @@
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 3f6f5db..45d25bb 100755
1762--- a/include/grim.h
1763+++ b/include/grim.h
1764@@ -90,7 +90,7 @@ enum class UnitType : short {
1765 		NEAREST, UP, DOWN, TO_ZERO,	
1766 
1767 	// Keyword
1768-		PRINT, SCREEN, TOKEN, NO_SUPPORT, IMAGE, STRING, BOOL, VAR, STRING_TERM, NONOP,
1769+		PRINT, SCREEN, TOKEN, NO_SUPPORT, IMAGE, STRING, BOOL, VAR, STRING_TERM, NONOP, ELSE,
1770 
1771 	FUNC_START,
1772 		// General Functions
1773@@ -98,6 +98,7 @@ enum class UnitType : short {
1774 		HYPOT, LOG, EXP, ABS, SIGN, SIN, COS, TAN, ASIN, ACOS, ATAN, ATAN2, VAR_FUNC,
1775 		BLUR, BRIGHTNESS, CONTRAST, DROP_SHADOW, GRAYSCALE, HUE_ROTATE, INVERT, 
1776 		OPACITY, SEPIA, SATURATE, ATTR, TYPE, ENV, IF, FUNC, LET, RETURN, SET,
1777+		LIST,
1778 		
1779 		// Easing Functions
1780 		EASING_FUNCTION, LINEAR_EASING_FUNCTION, LINEAR, EASE, EASE_IN, EASE_OUT, EASE_IN_OUT, CUBIC_BEZIER_FUNCTION, 
1781diff --git a/playground/syntax.wr b/playground/syntax.wr
1782index f0648a9..7c6a2fe 100644
1783--- a/playground/syntax.wr
1784+++ b/playground/syntax.wr
1785@@ -1,9 +1,9 @@
1786 Context -> width -> screen -> etc
1787 
1788 (
1789-	let(x, (1 + 1)),
1790-	let(x, x + 1),
1791-	print(x),
1792+	let(x (1 + 1))
1793+	set(x (x + 1))
1794+	print(x)
1795 
1796 	func(uppercase, (str), (
1797 		map(str, func((e),
1798@@ -12,9 +12,10 @@ Context -> width -> screen -> etc
1799 		)
1800 	)),
1801 
1802-	let(uppercase, func((str),
1803-			map(str, func((e),
1804-				e + 20
1805+	let(uppercase, func((str) (
1806+				map(str, func((e)
1807+					set(e (e + 20))
1808+					)
1809 				)
1810 			)
1811 		)
1812diff --git a/playground/unitvalue.cc b/playground/unitvalue.cc
1813index b0a0909..bab6d4a 100644
1814--- a/playground/unitvalue.cc
1815+++ b/playground/unitvalue.cc
1816@@ -55,10 +55,7 @@ int main() {
1817 	m.push_back(sym.get("height"),{UnitType::NUMBER, {.FLOAT = 700.0f}});
1818 	m.push_back(sym.get("em"),{UnitType::NUMBER, {.FLOAT= 16.0f}});
1819 
1820-	//std::vector<Unit> units = getUnit("(let(x, 10px), set(x, (x + 1)), min(5, x))", &sym);
1821-	std::vector<Unit> units = getUnit("let(x 10px) set(x (x + 100)) min(5 x)", &sym);
1822-	//std::vector<Unit> units = getUnit("true and false", &sym);
1823-
1824+	std::vector<Unit> units = getUnit("if((1 > 1): 10px; else: 11vw;)", &sym);
1825 	
1826 	Unit u = UnitValue(m, units, 0, units.size());
1827 
1828diff --git a/src/grim.cc b/src/grim.cc
1829index cc02744..0ea2d69 100755
1830--- a/src/grim.cc
1831+++ b/src/grim.cc
1832@@ -877,7 +877,7 @@ std::unordered_map<std::string, Media> MediaLookup {
1833 bool executeQuery(Window* w, std::string p) {
1834 	Context c;
1835 	c.id = window->symbol_table.get("all");
1836-	c.value.type = UnitType::BOOL;
1837+	c.type = UnitType::BOOL;
1838 	c.value.BOOL = w->getMedia() == Media::All;
1839 	c.previous = nullptr;
1840 
1841@@ -1497,6 +1497,44 @@ Unit UnitValue(Memory& m, std::vector<Unit>& units, size_t start, size_t end) {
1842 				}
1843 
1844 				i += unit.value.INT;
1845+				break;
1846+			}
1847+			case UnitType::PRINT: {
1848+				Unit u = UnitValue(m, units, i+1, i+1+unit.value.INT);
1849+				std::cout << "> " << u.value.FLOAT << std::endl;
1850+				iv = u;
1851+				i += unit.value.INT;
1852+				break;
1853+			}
1854+			case UnitType::IF: {
1855+				Range args[4]; // condition trueval else falseval
1856+				int found = 0;
1857+				size_t start = i + 1;
1858+				size_t end = i + 1 + unit.value.INT;
1859+
1860+				while (start < end) {
1861+					args[found++] = getNextUnit(units, start, end);
1862+				}
1863+
1864+				bool condition = UnitValue(m, units, args[0].start, args[0].end).value.BOOL;
1865+
1866+				if (condition) {
1867+					iv = UnitValue(m, units, args[1].start, args[1].end);
1868+				} else {
1869+					UnitType u = units[args[2].start].type;
1870+					if (u == UnitType::ELSE) {
1871+						iv = UnitValue(m, units, args[3].start, args[3].end);
1872+					} else {
1873+						iv = UnitValue(m, units, args[2].start, args[2].end);
1874+					}
1875+				}
1876+
1877+
1878+			   	i += unit.value.INT;
1879+				break;
1880+			}
1881+			case UnitType::LIST: {
1882+				
1883 			}
1884 		}
1885 
1886diff --git a/src/parser.cc b/src/parser.cc
1887index c67be3a..b92c0b3 100755
1888--- a/src/parser.cc
1889+++ b/src/parser.cc
1890@@ -857,6 +857,7 @@ static const std::unordered_map<std::string_view, UnitType> kMatch{
1891 	{"not", UnitType::NOT},
1892 	{"!=", UnitType::NOT},
1893 	{"only", UnitType::ONLY},
1894+	{"else", UnitType::ELSE},
1895 	{"infinity", UnitType::INF},
1896 	{"-infinity", UnitType::NEGINF},
1897 	{"NaN", UnitType::NaN},
1898@@ -1047,6 +1048,8 @@ static const std::unordered_map<std::string_view, UnitType> kFunc{
1899 	{"env", UnitType::ENV},
1900 	{"if", UnitType::IF},
1901 	{"var", UnitType::VAR},
1902+	{"print", UnitType::PRINT},
1903+	{"list", UnitType::LIST},
1904 	{"", UnitType::GROUP}
1905 };
1906 
1907@@ -1104,6 +1107,8 @@ std::vector<Unit> getUnit(std::string value, SymbolTable* externalTable = nullpt
1908 		if (!inQuotes) {
1909 			if (v == '(') parenDepth++;
1910 			else if (v == ')') parenDepth--;
1911+
1912+			if (v == ':' || v == ';') continue;
1913 		}
1914 
1915 		if (parenDepth > 0) {
1916diff --git a/tests/unitvalue.cc b/tests/unitvalue.cc
1917index 605943f..6bd824d 100644
1918--- a/tests/unitvalue.cc
1919+++ b/tests/unitvalue.cc
1920@@ -100,7 +100,7 @@ TEST_CASE("BENCH") {
1921 		m.push_back(sym.get("height"),{UnitType::NUMBER, {.FLOAT = 700.0f}});
1922 		m.push_back(sym.get("em"),{UnitType::NUMBER, {.FLOAT= 16.0f}});
1923 
1924-		std::vector<Unit> units = getUnit("let(x 10px) set(x (x + 100)) min(5 x)", &sym);
1925+		std::vector<Unit> units = getUnit("let(x 10px) set(x (x + 100)) (min(5 x) * 2)", &sym);
1926 
1927 		Snapshot s = m.save();
1928