home readme diff tree note docs

0commit 8cb68b3a6e02329a09a5c4065b6722c85b4b4b7a
1Author: lakefox <mason@lakefox.net>
2Date:   Tue Dec 30 12:14:30 2025 -0700
3
4    WindowUnitLengthValue calculates correctly needs to be finished
5
6diff --git a/include/grim.h b/include/grim.h
7index 77af12c..9496863 100755
8--- a/include/grim.h
9+++ b/include/grim.h
10@@ -87,7 +87,9 @@ enum class UnitType : short {
11 
12 
13 	// General Function
14-		CALC, MIN, BLUR, BRIGHTNESS, CONTRAST, DROP_SHADOW, GRAYSCALE, HUE_ROTATE, INVERT, OPACITY, SEPIA, SATURATE,
15+		CALC, MIN, MAX, CLAMP, ROUND, MOD, PROGRESS, REMD, POW, SQRT, HYPOT, LOG, EXP, ABS,
16+		SIGN, SIN, COS, TAN, ASIN, ACOS, ATAN, ATAN2, VAR,
17+		BLUR, BRIGHTNESS, CONTRAST, DROP_SHADOW, GRAYSCALE, HUE_ROTATE, INVERT, OPACITY, SEPIA, SATURATE,
18 	// Easing Functions
19 		EASING_FUNCTION, LINEAR_EASING_FUNCTION, LINEAR, EASE, EASE_IN, EASE_OUT, EASE_IN_OUT, CUBIC_BEZIER_FUNCTION, 
20 		STEP_START, STEP_END, STEPS_FUNCTION, JUMP_START, JUMP_END, JUMP_NONE, JUMP_BOTH, 
21@@ -110,10 +112,11 @@ union UnitValue {
22 	char CHAR;
23 };
24 
25-// 6 bytes 
26+// 7 bytes 
27 struct Unit {
28 	UnitType type; // short
29 	UnitValue value;
30+	bool isFunc;
31 };
32 
33 class Window;
34@@ -168,8 +171,7 @@ class Node {
35 
36 		StyleList style;
37 
38-		Node() : parent(nullptr), classList(this), style(this) {
39-		}
40+		Node() : parent(nullptr), classList(this), style(this) {}
41 
42 		std::string getTagName() const;
43 		void setTagName(const std::string& name);
44@@ -267,7 +269,6 @@ enum class KeyType : short {
45 
46 class Style {
47 	public:
48-		Window* window;
49 		std::unordered_map<std::string, std::vector<size_t>> basemap{};
50 
51 		std::string name;
52@@ -284,65 +285,47 @@ class Style {
53 		std::unordered_map<KeyType, UnitType> getStyles(Node* n);
54 };
55 
56-enum class AnyHover : char {
57-	None,
58-	Hover
59-};
60-
61-enum class AnyPointer : char {
62+enum class Media : char {
63+	All,
64+	Print,
65+	Screen,
66 	None,
67+	Hover,
68 	Fine,
69-	Coarse
70-};
71-
72-enum class Orientation : char {
73+	Coarse,
74 	Landscape,
75-	Portrait
76-};
77-
78-enum class ColorScheme : char {
79+	Portrait,
80 	Light,
81-	Dark
82-};
83-
84-enum class Contrast : char {
85+	Dark,
86 	NoPreference,
87 	More,
88-	Less
89-};
90-
91-enum class ReducedMotion : char {
92-	NoPreference,
93-	Reduce
94-};
95-
96-enum class ReducedTransparency : char {
97-	NoPreference,
98-	Reduce
99-};
100-
101-enum class Update : char {
102+	Less,
103+	Reduce,
104 	Fast,
105 	Slow
106 };
107 
108 class Window {
109 	private:
110-		int width = 0;
111-		int height = 0;
112-		int resolution = 0;
113-		int em = 16;
114-		AnyHover anyHover = AnyHover::Hover;
115-		AnyPointer anyPointer = AnyPointer::Fine;
116-		Orientation orientation = Orientation::Landscape;
117-		ColorScheme colorScheme = ColorScheme::Light;
118-		Contrast contrast = Contrast::NoPreference;
119-		ReducedMotion reducedMotion = ReducedMotion::NoPreference;
120-		ReducedTransparency reducedTransparency = ReducedTransparency::NoPreference;
121-		Update update = Update::Fast;
122-
123+		float width = 0;
124+		float height = 0;
125+		float resolution = 0;
126+		float em = 16;
127 	public:
128 		
129+		std::unordered_map<std::string, Media> MediaFeatures {
130+			{"any-hover", Media::Hover},
131+			{"any-pointer", Media::Fine},
132+			{"orientation", Media::Landscape},
133+			{"pefers-color-scheme", Media::Light},
134+			{"contrast", Media::NoPreference},
135+			{"prefers-reduced-motion", Media::NoPreference},
136+			{"prefers-reduced-transparency", Media::NoPreference},
137+			{"update", Media::Fast},
138+			{"media", Media::All},
139+		};
140+
141+		
142 		void parseHTML(std::istream& inputStream);
143 		
144 		Node document;
145@@ -352,70 +335,115 @@ class Window {
146 		// | (hldfk) Window Properties                         |
147 		// +---------------------------------------------------+
148 
149-		void setWidth(int value) {
150+		void setWidth(float value) {
151 			width = value;
152 		}
153-		void setHeight(int value) {
154+		void setHeight(float value) {
155 			height = value;
156 		}
157-		void setResolution(int value) {
158+		void setResolution(float value) {
159 			resolution = value;
160 		}
161 
162-		void setEM(int value) {
163+		void setEM(float value) {
164 			em = value;
165 		}
166 
167-		void setMouse(AnyPointer pointer) {
168-			if (pointer == AnyPointer::None) {
169-				anyHover = AnyHover::None;
170-			} else {
171-				anyHover = AnyHover::Hover;
172-			}
173-			anyPointer = pointer;
174+		void setMouse(Media value) {
175+			// Accepts: Media::Hover && Media::None
176+			MediaFeatures.at("any-hover") = value;
177+			MediaFeatures.at("any-pointer") = value;
178+		}
179+
180+		void setOrientation(Media value) {
181+			MediaFeatures.at("orientation") = value;
182 		}
183 
184-		void setOrientation(Orientation value) {
185-			orientation = value;
186+		void setColorScheme(Media value) {
187+			MediaFeatures.at("prefers-color-scheme") = value;
188 		}
189 
190-		void setColorScheme(ColorScheme value) {
191-			colorScheme = value;
192+		void setContrast(Media value) {
193+			MediaFeatures.at("contrast") = value;
194 		}
195 
196-		void setContrast(Contrast value) {
197-			contrast = value;
198+		void setReducedMotion(Media value) {
199+			MediaFeatures.at("prefers-reduced-motion") = value;
200 		}
201 
202-		void setReducedMotion(ReducedMotion value) {
203-			reducedMotion = value;
204+		void setReducedTransparency(Media value) {
205+			MediaFeatures.at("prefers-reduced-transparency") = value;
206 		}
207 
208-		void setReducedTransparency(ReducedTransparency value) {
209-			reducedTransparency = value;
210+		void setUpdate(Media value) {
211+			MediaFeatures.at("update") = value;
212 		}
213 
214-		void setUpdate(Update value) {
215-			update = value;
216+		void setMedia(Media value) {
217+			MediaFeatures.at("media") = value;
218 		}
219 
220 		// Getters
221-		int getWidth() const {
222+		float getWidth() const {
223 			return width;
224 		}
225-		int getHeight() const {
226+		float getHeight() const {
227 			return height;
228 		}
229-		int getResolution() const {
230+		float getResolution() const {
231 			return resolution;
232 		}
233-		int getEM() const {
234+		float getEM() const {
235 			return em;
236 		}
237-		double getAspectRatio() const {
238+		float getAspectRatio() const {
239 			if (height == 0) return 0.0;
240 			return static_cast<double>(width) / height;
241 		}
242+		Media getMedia() const {
243+			auto it = MediaFeatures.find("media");
244+			return it->second;
245+		}
246+
247+		Media getAnyHover() const {
248+			auto it = MediaFeatures.find("any-hover");
249+			return it->second;
250+		}
251+
252+		Media getAnyPointer() const {
253+			auto it = MediaFeatures.find("any-pointer");
254+			return it->second;
255+		}
256+
257+		Media getOrientation() const {
258+			auto it = MediaFeatures.find("orientation");
259+			return it->second;
260+		}
261+
262+		Media getColorScheme() const {
263+			auto it = MediaFeatures.find("prefers-color-scheme");
264+			return it->second;
265+		}
266+
267+		Media getContrast() const {
268+			auto it = MediaFeatures.find("contrast");
269+			return it->second;
270+		}
271+
272+		Media getReducedMotion() const {
273+			auto it = MediaFeatures.find("prefers-reduced-motion");
274+			return it->second;
275+		}
276+
277+		Media getReducedTransparency() const {
278+			auto it = MediaFeatures.find("prefers-reduced-motion");
279+			return it->second;
280+		}
281+
282+		Media getUpdate() const {
283+			auto it = MediaFeatures.find("update");
284+			return it->second;
285+		}
286 };
287 
288 // Will need to require an adapter later
289@@ -426,4 +454,7 @@ For the adapter, it will take a window as a argument to set the adapters capabil
290 */
291 Window createWindow();
292 
293+
294+float WindowUnitLengthValue(Window*, const std::vector<Unit>&, size_t, size_t);
295+
296 #endif // NODE_H
297diff --git a/playground/css_unit.cc b/playground/css_unit.cc
298index 48da6ac..148320a 100644
299--- a/playground/css_unit.cc
300+++ b/playground/css_unit.cc
301@@ -4,11 +4,7 @@
302 #include <fstream>
303 
304 int main() {
305-	std::vector<Unit> test = getUnit("first baseline");
306+	std::vector<Unit> test = getUnit("min(100px, 12rem)");
307 
308-	UnitType t = test[0].type;
309-
310-	bool et = t == UnitType::FIRST_BASELINE;
311-
312-	std::cout << et << std::endl;
313+	std::cout << "Done" << std::endl;
314 }
315diff --git a/playground/parseselectorparts.cc b/playground/parseselectorparts.cc
316index e0e9299..ca566fe 100644
317--- a/playground/parseselectorparts.cc
318+++ b/playground/parseselectorparts.cc
319@@ -5,8 +5,17 @@
320 #include <iostream>
321 
322 int main() {
323-	std::vector<std::vector<std::string>> parts = parseSelectorParts("h1:test, h1[name='asd']");
324+	std::vector<std::vector<std::string>> parts = parseSelectorParts("@media (min-width: 60px)");
325 
326-	std::cout << parts[1][1] << std::endl;
327+
328+	std::cout << "[" << std::endl;
329+	for (size_t i = 0; i < parts.size(); i++) {
330+		std::cout << "[";
331+		for (size_t j = 0; j < parts[i].size(); j++) {
332+			std::cout << "\"" << parts[i][j] << "\", ";
333+		}
334+		std::cout << "]," << std::endl;
335+	}
336+	std::cout << "]" << std::endl;
337 }
338 
339diff --git a/playground/window.cc b/playground/window.cc
340index bbf274a..a76ef6e 100644
341--- a/playground/window.cc
342+++ b/playground/window.cc
343@@ -10,7 +10,7 @@ int main() {
344 			"<html>"
345 				"<head>"
346 					"<title>This is a simple test</title>"
347-					"<!--<link rel=\"stylesheet\" href=\'/style.css\'/>-->"
348+					"<link rel=\"stylesheet\" href=\'tests/style.css\'/>"
349 					"<style>h1 {background: red;}</style>"
350 				"</head>"
351 				"<body>"
352@@ -41,5 +41,14 @@ int main() {
353 	Window window = createWindow();
354 	window.parseHTML(ss);
355 
356-	Node document = window.document;
357+//	Node document = window.document;
358+
359+	window.setWidth(1000);
360+	window.setHeight(700);
361+	window.setEM(16);
362+
363+	// std::vector<Unit> units = getUnit("clamp(100px, calc(30% / 2rem + 10px), 900px)");
364+	std::vector<Unit> units = getUnit("clamp(100px, calc(30% / 2rem + 10px), 900px)");
365+
366+	std::cout << WindowUnitLengthValue(&window, units, 0, units.size()) << std::endl;
367 }
368diff --git a/src/grim.cc b/src/grim.cc
369index fd316a0..6cde02b 100755
370--- a/src/grim.cc
371+++ b/src/grim.cc
372@@ -11,6 +11,7 @@
373 #include <tuple>
374 #include <optional>
375 #include <stdexcept>
376+#include <limits>
377 
378 // +------------------------------------------------------+
379 // |(TOC) Table of contents                               |
380@@ -727,9 +728,6 @@ std::vector<std::string> parseNodeParts(Node* node) {
381 void Style::addChild(Style s) {	
382 	size_t index = children.size();
383 
384-	// Add the window
385-	s.window = window;
386-
387 	children.push_back(s);
388 
389 	// Get the last selector group from the parsed selector parts
390@@ -803,15 +801,351 @@ std::unordered_map<KeyType, UnitType> Style::getStyles(Node* node) {
391 // testQuery tries to calculate if the query (ie a media query or 
392 // container query) applies to the node passed
393 
394-bool testQuery(Node* node, std::vector<std::string> parts) {
395+bool testQuery(Node* node, std::vector<std::vector<std::string>> parts) {
396+	// Takes a fully parsed selector. If it is a at-rule even multiple,
397+	// the at-rule tag must but the first.
398+
399+/*
400+Just a ref
401+bool result = false;
402+for (auto& condition : conditions) {
403+    result = result || evaluateSingleQuery(condition, windowProps);
404+}
405+
406+
407+also not can only be at the start
408+*/
409+	
410+	Window* w = node->window;
411+
412+	std::string tag = parts[0][0];
413+	parts[0].erase(parts[0].begin());
414+
415+	if (tag == "@media") {
416+		for (size_t i = 0; i < parts.size(); i++) {
417+			bool prev = false;
418+			bool match = false;
419+			for (size_t j = 0; j < parts[i].size(); j++) {
420+				if (parts[i][j] == "and") {
421+
422+				}
423+			}
424+		}
425+	}
426+
427 	return true;	
428 }
429 
430+/*
431+	at-rules consist of "query's" the are tre or false given the stat of the window.
432+	executeQuery runs the non-logic (and, or etc..) and returns the value of the 
433+	executed query.
434+*/
435+
436+std::unordered_map<std::string, Media> MediaLookup {
437+	{"all", Media::All},
438+	{"print", Media::Print},
439+	{"screen", Media::Screen},
440+	{"none", Media::None},
441+	{"hover", Media::Hover},
442+	{"fine", Media::Fine},
443+	{"coarse", Media::Coarse},
444+	{"landscape", Media::Landscape},
445+	{"portrait", Media::Portrait},
446+	{"light", Media::Light},
447+	{"dark", Media::Dark},
448+	{"no-preference", Media::NoPreference},
449+	{"more", Media::More},
450+	{"less", Media::Less},
451+	{"reduce", Media::Reduce},
452+	{"fast", Media::Fast},
453+	{"slow", Media::Slow},
454+};
455+
456+bool executeQuery(Window* w, std::string p) {
457+	bool matches = false;
458+	if (p != " ") {
459+		// in parenthesis
460+		if (p[0] == '(') {
461+			std::string key;
462+			std::string value;
463+			bool colonFound = false;
464+
465+			// if colonFound is false then key has the parsed substr
466+			// and we know what type of query we are dealing with
467+			for (size_t j = 1; j < p.length()-1; j++) {
468+				if (!colonFound) {
469+					if (p[j] == ':') {
470+						colonFound = true;
471+					} else {
472+						key.push_back(p[j]);
473+					}
474+				} else {
475+					value.push_back(p[j]);
476+				}
477+			}
478+
479+			if (colonFound) {
480+				auto k = w->MediaFeatures.find(key);
481+				if (k != w->MediaFeatures.end()) {
482+					auto v = MediaLookup.find(value);
483+					
484+					if (v != MediaLookup.end()) {
485+						matches = k->second == v->second;
486+					}
487+				}/* else {
488+					if (k == "min-width") {
489+
490+						matches = w->getWidth() > 
491+					}
492+				}*/
493+			}
494+		} else {
495+			// Lookup keyword
496+			if (p == "all") {
497+				matches = w->getMedia() == Media::All;
498+			} else if (p == "print") {
499+				matches = w->getMedia() == Media::Print;
500+			} else if (p == "screen") {
501+				matches = w->getMedia() == Media::Screen;
502+			} 
503+		}
504+	}
505+	return matches;
506+}
507+
508+struct Range {
509+	size_t start;
510+	size_t end;
511+};
512+
513+std::vector<Range> extractUnits(const std::vector<Unit>& units, size_t start, size_t end) {
514+	std::vector<Range> u;
515+	for (size_t i = start; i < end; i++) {
516+		Unit unit = units[i];
517+
518+		if (unit.isFunc) {
519+			u.push_back({i,i+unit.value.INT});
520+			i += unit.value.INT;
521+		} else {
522+			std::vector<Unit> s{unit};
523+			u.push_back({i, i+1});
524+		}
525+	}
526+	return u;
527+}
528+
529+/*
530+	CAUTION: These values are only correct for top level media queries
531+*/
532+float WindowUnitLengthValue(Window* window, const std::vector<Unit>& units, size_t start, size_t end) {
533+	float sum = 0;
534+	UnitType op = UnitType::ADD;
535+
536+	for (size_t i = start; i < end; i++) {
537+		Unit unit = units[i];
538+		UnitType type = unit.type;
539+
540+		float value = 0;
541+		float w = 0;
542+		float h = 0;
543+		float v1 = 0;
544+		float v2 = 0;
545+		float v3 = 0;
546+		std::vector<Range> extracted;
547+
548+		switch (type) {
549+			case UnitType::ADD:
550+			case UnitType::SUB:
551+			case UnitType::DIV:
552+			case UnitType::MULT:
553+				op = type;
554+				break;
555+			case UnitType::PX:
556+				std::cout << "PX: " << unit.value.FLOAT << std::endl;
557+				value = unit.value.FLOAT;
558+				break;
559+			case UnitType::CM:
560+				value = unit.value.FLOAT*37.8f;
561+				break;
562+			case UnitType::MM:
563+				value = unit.value.FLOAT*3.78f;
564+				break;
565+			case UnitType::Q:
566+				value = unit.value.FLOAT*0.94;
567+				break;
568+			case UnitType::IN:
569+				value = unit.value.FLOAT*96;
570+				break;
571+			case UnitType::PC:
572+				value = unit.value.FLOAT*16;
573+				break;
574+			case UnitType::PT:
575+				value = unit.value.FLOAT*1.3;
576+				break;
577+			case UnitType::EM:
578+			case UnitType::REM:
579+			case UnitType::CAP:
580+				// Technically wrong but fonts aren't loaded before this is ran
581+			case UnitType::CH:
582+			case UnitType::IC:
583+			case UnitType::LH:
584+			case UnitType::RCAP:
585+			case UnitType::RCH:
586+			case UnitType::RIC:
587+			case UnitType::RLH:
588+				value = unit.value.FLOAT*window->getEM();
589+				break;
590+			case UnitType::EX:
591+			case UnitType::REX:
592+				value = unit.value.FLOAT*(window->getEM()/2);
593+				break;
594+			case UnitType::CQH:
595+			case UnitType::VH:
596+				value = window->getHeight()*(unit.value.FLOAT/100);
597+				break;
598+			case UnitType::CQW:
599+			case UnitType::CQI:
600+			case UnitType::CQB:
601+			case UnitType::PERCENT:
602+			case UnitType::VW:	
603+				value = window->getWidth()*(unit.value.FLOAT/100);
604+				break;
605+			case UnitType::VMAX:
606+			case UnitType::CQMAX:
607+				w = window->getWidth();
608+				h = window->getHeight();
609+				if (w > h) {
610+					value = unit.value.FLOAT*w;
611+				} else {
612+					value = unit.value.FLOAT*h;
613+				}
614+				break;
615+			case UnitType::VMIN:
616+			case UnitType::CQMIN:
617+				w = window->getWidth();
618+				h = window->getHeight();
619+				if (w < h) {
620+					value = unit.value.FLOAT*w;
621+				} else {
622+					value = unit.value.FLOAT*h;
623+				}	
624+				break;
625+			case UnitType::VB:
626+			case UnitType::VI:
627+				value = unit.value.FLOAT*window->getWidth();
628+				break;
629+			case UnitType::CALC:
630+				value = WindowUnitLengthValue(window, units, i+1, i+1+unit.value.INT);
631+				i += unit.value.INT;
632+				break;
633+			case UnitType::MIN:
634+				extracted = extractUnits(units, i+1, i+1+unit.value.INT);
635+
636+				v1 = std::numeric_limits<float>::max(); // Store of the min value
637+
638+				for (size_t j = 0; j < extracted.size(); j++) {
639+					v2 = WindowUnitLengthValue(window, units, extracted[j].start, extracted[j].end);
640+
641+					if (v2 < v1) {
642+						v1 = v2;
643+					}
644+				}
645+
646+				value = v1;
647+
648+				i += unit.value.INT;
649+				break;
650+			case UnitType::MAX:
651+				extracted = extractUnits(units, i+1, i+1+unit.value.INT);
652+
653+				v1 = std::numeric_limits<float>::min(); // Store of the max value
654+
655+				for (size_t j = 0; j < extracted.size(); j++) {
656+					v2 = WindowUnitLengthValue(window, units, extracted[j].start, extracted[j].end);
657+
658+					if (v2 > v1) {
659+						v1 = v2;
660+					}
661+				}
662+
663+				value = v1;
664+
665+				i += unit.value.INT;
666+				break;		
667+			case UnitType::CLAMP:
668+				extracted = extractUnits(units, i+1, i+1+unit.value.INT);
669+
670+				if (extracted.size() >= 3) {
671+					v1 = WindowUnitLengthValue(window, units, extracted[0].start, extracted[0].end);
672+					v2 = WindowUnitLengthValue(window, units, extracted[1].start, extracted[1].end);
673+					v3 = WindowUnitLengthValue(window, units, extracted[2].start, extracted[2].end);
674+
675+					std::cout << "V1: " << v1 << std::endl;
676+					std::cout << "V2: " << v2 << std::endl;
677+					std::cout << "V3: " << v3 << std::endl;
678+
679+					if (v2 < v1) {
680+						v2 = v1;
681+					}
682+					if (v2 > v3) {
683+						v2 = v3;
684+					}
685+				}
686+
687+				value = v2;
688+			
689+				
690+				i += unit.value.INT;
691+				break;
692+			case UnitType::ROUND:
693+				
694+			// Need to add variable injection
695+		}
696+
697+		if (op != type) {
698+			switch (op) {
699+				case UnitType::ADD:
700+					sum += value;
701+					break;
702+				case UnitType::SUB:
703+					sum -= value;
704+					break;
705+				case UnitType::DIV:
706+					sum /= value;
707+					break;
708+				case UnitType::MULT:
709+					sum *= value;
710+					break;
711+			}
712+		}
713+	}
714+	
715+	return sum;
716+}
717+
718 // Used for root node without a parent
719 std::vector<Node> EMPTY_NODE_CHILDREN_VECTOR;
720 
721 // node: The node you are checking the selector against
722 // selector: output of parseSelector
723+
724+
725+
726+
727+
728+
729+
730+// todo: make testSelector use indices
731+
732+
733+
734+
735+
736+
737+
738+
739+
740 bool testSelector(Node* node, std::vector<std::vector<std::string>> selector) {
741 	// By the time we get here, we know the right most selector some what matches
742 	// so now if it is a compound selector we should see if the parent('s) some what match
743@@ -979,9 +1313,6 @@ bool testSelector(Node* node, std::vector<std::vector<std::string>> selector) {
744 			matched = true;
745 		} else if (i == 0 && p[0] != '#' && p[0] != '.' &&  p[0] != '[' && p[0] != ':') {
746 			matched = tagName == p;
747-		} else if (p[0] == '@') {
748-			// This is a at-rule we will process it in a different function
749-			return testQuery(node, parts);
750 		} else if (p[0] == '#') {
751 			matched = "#"+attributes["id"] == p;
752 		} else if (p[0] == '.') {
753diff --git a/src/parser.cc b/src/parser.cc
754index c1dbd83..2c156df 100755
755--- a/src/parser.cc
756+++ b/src/parser.cc
757@@ -951,6 +951,27 @@ static const std::unordered_map<std::string_view, UnitType> kMatch{
758 static const std::unordered_map<std::string_view, UnitType> kFunc{
759 	{"calc", UnitType::CALC},
760 	{"min", UnitType::MIN},
761+	{"max", UnitType::MAX},
762+	{"clamp", UnitType::CLAMP},
763+	{"round", UnitType::ROUND},
764+	{"mod", UnitType::MOD},
765+	{"progress", UnitType::PROGRESS},
766+	{"rem", UnitType::REMD},
767+	{"pow", UnitType::POW},
768+	{"sqrt", UnitType::SQRT},
769+	{"hypot", UnitType::HYPOT},
770+	{"log", UnitType::LOG},
771+	{"exp", UnitType::EXP},
772+	{"ABS", UnitType::ABS},
773+	{"sin", UnitType::SIN},
774+	{"cos", UnitType::COS},
775+	{"tan", UnitType::TAN},
776+	{"asin", UnitType::ASIN},
777+	{"acos", UnitType::ACOS},
778+	{"atan", UnitType::ATAN},
779+	{"atan2", UnitType::ATAN2},
780+	{"sign", UnitType::SIGN},
781+	{"var", UnitType::VAR},
782 	{"rgb", UnitType::RGB},
783 	{"rgba", UnitType::RGBA},
784 	{"hsl", UnitType::HSL},
785@@ -1054,6 +1075,7 @@ std::vector<Unit> getUnit(std::string value) {
786 			std::vector<Unit> children = getUnit(value);
787 			
788 			Unit unit;
789+			unit.isFunc = true;
790 
791 			if (auto it = kFunc.find(name); it != kFunc.end()) { 
792 				unit.type = it->second;
793@@ -1071,6 +1093,7 @@ std::vector<Unit> getUnit(std::string value) {
794 			if (v != ',' && !std::isspace(v)) buffer.push_back(v);
795 
796 			Unit unit;
797+			unit.isFunc = false;
798 
799 			// Split by spaces into parts
800 
801@@ -1145,6 +1168,7 @@ std::vector<Unit> getUnit(std::string value) {
802 		} else if (inQuotes) {
803 			if (v == '"') continue;
804 			Unit u;
805+			u.isFunc = false;
806 			u.type = UnitType::STRING;
807 			UnitValue uv;
808 			uv.CHAR = v;
809@@ -1334,6 +1358,52 @@ Style parseCSS(std::istream& inputStream) {
810 	return root;
811 }
812 
813+std::string get_working_path() {
814+    char temp [ PATH_MAX ];
815+
816+    if ( getcwd(temp, PATH_MAX) != 0) 
817+        return std::string ( temp );
818+
819+    int error = errno;
820+
821+    switch ( error ) {
822+        // EINVAL can't happen - size argument > 0
823+
824+        // PATH_MAX includes the terminating nul, 
825+        // so ERANGE should not be returned
826+
827+        case EACCES:
828+            throw std::runtime_error("Access denied");
829+
830+        case ENOMEM:
831+            // I'm not sure whether this can happen or not 
832+            throw std::runtime_error("Insufficient storage");
833+
834+        default: {
835+            std::ostringstream str;
836+            str << "Unrecognised error" << error;
837+            throw std::runtime_error(str.str());
838+        }
839+    }
840+}
841+
842+std::string pathJoin(const std::string& p1, const std::string& p2) {
843+    char sep = '/';
844+    std::string tmp = p1;
845+
846+#ifdef _WIN32
847+    sep = '\\';
848+#endif
849+
850+    // Add separator if it is not included in the first path:
851+    if (p1[p1.length() - 1] != sep) {
852+        tmp += sep;
853+        return tmp + p2;
854+    } else {
855+        return p1 + p2;
856+    }
857+}
858+
859 struct ParsedDocument {
860 	Node document;
861 	Style CSS;
862@@ -1355,7 +1425,8 @@ ParsedDocument parseDocument(std::istream& inputStream) {
863 	std::string token;
864 
865 	// CSS stuff
866-	std::vector<Node*> cssElements;
867+	std::vector<std::string> cssLinks;
868+	std::vector<std::string> cssTags;
869 
870 	char current;
871 	while (inputStream.get(current)) {
872@@ -1478,8 +1549,10 @@ ParsedDocument parseDocument(std::istream& inputStream) {
873 								// All attributes are stored as strings so we can just throw them in
874 								node->setAttribute(pair.first, pair.second);
875 							}
876-							if (attrs["tagName"] == "link" || attrs["tagName"] == "style") {
877-								cssElements.push_back(node);
878+							if (attrs["tagName"] == "link" && attrs["rel"] == "stylesheet") {
879+								cssLinks.push_back(node->getAttribute("href"));
880+							} else if (attrs["tagName"] == "style") {
881+								cssTags.push_back(node->getAttribute("innerText"));
882 							}
883 						} else if (closingTag) {
884 							// Checksum and tree move
885@@ -1494,7 +1567,14 @@ ParsedDocument parseDocument(std::istream& inputStream) {
886 									break;
887 								}
888 							}
889+
890 							if (currentNode->getTagName() == tagName) {
891+								if (tagName == "link" && currentNode->getAttribute("rel") == "stylesheet") {
892+									cssLinks.push_back(currentNode->getAttribute("href"));
893+								} else if (tagName == "style") {
894+									cssTags.push_back(currentNode->children[0].getAttribute("innerText"));
895+								}
896+
897 								currentNode = currentNode->parent;
898 							} else {
899 								std::cerr << "malformed html: closing tag (</" << tagName << ">) found for <" << currentNode->getTagName() << ">" << std::endl;
900@@ -1508,10 +1588,7 @@ ParsedDocument parseDocument(std::istream& inputStream) {
901 								for (auto const& pair : attrs) {
902 									// All attributes are stored as strings so we can just throw them in
903 									currentNode->setAttribute(pair.first, pair.second);
904-								}
905-								if (attrs["tagName"] == "link" || attrs["tagName"] == "style") {
906-									cssElements.push_back(currentNode);
907-								}
908+								}	
909 							}
910 
911 						}
912@@ -1584,22 +1661,33 @@ ParsedDocument parseDocument(std::istream& inputStream) {
913 
914 	Style style;
915 
916-	for (size_t i = 0; i < cssElements.size(); i++) {
917-		Node* el = cssElements[i];
918+	std::string workingPath = get_working_path();
919 
920-		if (el->getTagName() == "link" && el->getAttribute("rel") == "stylesheet") {
921-			std::ifstream file(el->getAttribute("href"));
922-			if (!file.is_open()) {
923-				std::cerr << "Failed to open file!" << std::endl;
924-			} else {
925-				Style s = parseCSS(file);
926-				style.addChild(s);
927+	for (size_t i = 0; i < cssLinks.size(); i++) {
928+		std::string link = cssLinks[i];
929+
930+		std::ifstream file(pathJoin(workingPath, link));
931+
932+		if (!file.is_open()) {
933+			std::cerr << "Failed to open file!" << std::endl;
934+		} else {
935+			Style s = parseCSS(file);
936+
937+			for (size_t j = 0; j < s.children.size(); j++) {
938+				style.addChild(s.children[j]);
939 			}
940-			file.close();
941-		} else if (el->getTagName() == "style") {
942-			std::istringstream iss(el->children[0].getAttribute("innerText"));
943-			Style s = parseCSS(iss);
944-			style.addChild(s);
945+		}
946+		file.close();
947+	}
948+
949+	for (size_t i = 0; i < cssTags.size(); i++) {
950+		std::string tag = cssTags[i];
951+
952+		std::istringstream iss(tag);
953+		Style s = parseCSS(iss);
954+		
955+		for (size_t j = 0; j < s.children.size(); j++) {
956+			style.addChild(s.children[j]);
957 		}
958 	}
959