home readme diff tree note docs

0commit 8dd2d7fea3a642b9b4482abc53415b3919e0ea56
1Author: lakefox <mason@lakefox.net>
2Date:   Tue Feb 17 18:16:06 2026 -0700
3
4    get_unit tested and working
5
6diff --git a/Makefile b/Makefile
7index 93c0468..551455a 100755
8--- a/Makefile
9+++ b/Makefile
10@@ -21,13 +21,13 @@ build/parser.o: src/parser.c include/parser.h build/colors.o build/match.o build
11 
12 # --- Gperf ---
13 build/colors.c: maps/colors.gperf
14-	gperf maps/colors.gperf > build/colors.c
15+	gperf --constants-prefix=COLORS_ maps/colors.gperf > build/colors.c
16 
17 build/match.c: maps/match.gperf
18-	gperf maps/match.gperf > build/match.c
19+	gperf --constants-prefix=MATCH_ maps/match.gperf > build/match.c
20 
21 build/functions.c: maps/functions.gperf
22-	gperf maps/functions.gperf > build/functions.c
23+	gperf --constants-prefix=MAPS_ maps/functions.gperf > build/functions.c
24 
25 build/colors.o: build/colors.c include/grim.h | build
26 	${CC} ${CCFLAGS} -Iinclude/ -c build/colors.c -o build/colors.o
27@@ -68,6 +68,9 @@ build/parseselectorparts_playground: playground/parseselectorparts.c build/grim.
28 build/html_parser_playground: playground/html_parser.c build/grim.o build/parser.o | build
29 	${CC} ${CCFLAGS} -Iinclude/ playground/html_parser.c build/grim.o build/parser.o -o build/html_parser_playground
30 
31+build/css_unit_playground: playground/css_unit.c build/grim.o build/parser.o | build
32+	${CC} ${CCFLAGS} -Iinclude/ playground/css_unit.c build/grim.o build/parser.o -o build/css_unit_playground
33+
34 
35 
36 # --- Prebuild ---
37diff --git a/include/grim.h b/include/grim.h
38index 35fc1cf..4534c11 100755
39--- a/include/grim.h
40+++ b/include/grim.h
41@@ -242,6 +242,8 @@ char* INNER_HTML(SymbolTable* sym, Node* node, char** buffer, size_t* size, int
42 
43 void ADD_CLASS(SymbolTable* sym, Node* n, char* value);
44 
45+void get_unit(SymbolTable* sym, struct Unit** units, int* cap, int* unit_len, char* value);
46+
47 /*
48 bool testSelector(Node*, std::vector<std::vector<std::string>>);
49 
50diff --git a/playground/css_unit.c b/playground/css_unit.c
51index b7ea223..b60a590 100644
52--- a/playground/css_unit.c
53+++ b/playground/css_unit.c
54@@ -1,9 +1,93 @@
55+#define _POSIX_C_SOURCE 199309L
56+#include <stdio.h>
57+#include <stdlib.h>
58+#include <time.h>
59+#include <string.h>
60+#include <assert.h>
61+
62 #include "../include/grim.h"
63 #include "../include/parser.h"
64 
65+void benchmark_get_unit(SymbolTable* sym, char* value) {
66+	struct timespec start, end;
67+
68+	int iterations = 10000;
69+	
70+	// Get start time
71+	clock_gettime(CLOCK_MONOTONIC, &start);
72+
73+	int cap = 10;
74+	struct Unit *units = malloc(sizeof(struct Unit) * cap);
75+	int units_len = 0;
76+
77+	for (int i = 0; i < iterations; i++) {
78+		units_len = 0;
79+		get_unit(sym, &units, &cap, &units_len, value);
80+	}
81+
82+	free(units);
83+
84+	// Get end time
85+	clock_gettime(CLOCK_MONOTONIC, &end);
86+
87+	// Calculate time difference in seconds
88+	double total_time = (end.tv_sec - start.tv_sec) + (end.tv_nsec - start.tv_nsec) / 1e9;
89+	printf("Average execution time: %.9f seconds (%f us)\n", 
90+		total_time / iterations, 
91+	       (total_time / iterations) * 1000000);
92+}
93+
94+void test_get_unit(char* ustr, struct Unit* known_units, int known_units_len) {
95+	SymbolTable* sym = create_symbol_table(4096);
96+	int cap = 10;
97+	struct Unit *units = malloc(sizeof(struct Unit) * cap);
98+	int units_len = 0;
99+
100+	get_unit(sym, &units, &cap, &units_len, ustr);
101+
102+	
103+	assert(units_len == known_units_len);
104+
105+	bool error = false;
106+
107+	for (int i = 0; i < units_len; i++) {
108+		if (units[i].type != known_units[i].type) {
109+			fprintf(stderr, "TYPE assertion failed on unit %d of test case \"%s\": %d != %d\n", i, ustr, units[i].type, known_units[i].type);
110+			error = true;
111+		}
112+		if (units[i].value != known_units[i].value) {
113+			fprintf(stderr, "VALUE assertion failed on unit %d of test case \"%s\": %f != %f\n", i, ustr, units[i].value, known_units[i].value);
114+			error = true;
115+		}
116+	}
117+	
118+	if (!error) {
119+		printf("\"%s\" parsed correctly\n", ustr);
120+	}
121+
122+	free(units);
123+
124+	FREE_SYMBOL_TABLE(sym);
125+}
126+
127 int main() {
128-	Style style{};
129-	std::vector<Unit> test = getUnit(&style, "attr(size type(px), 10)");
130 
131-	std::cout << "Done" << std::endl;
132+	SymbolTable* sym = create_symbol_table(4096);
133+	int cap = 10;
134+	struct Unit *units = malloc(sizeof(struct Unit) * cap);
135+	int units_len = 0;
136+
137+	char* ustr = "1px + 200%";
138+
139+	get_unit(sym, &units, &cap, &units_len, ustr);
140+
141+	free(units);
142+
143+	benchmark_get_unit(sym, ustr);
144+
145+	FREE_SYMBOL_TABLE(sym);
146+
147+	test_get_unit("1 + 1", (struct Unit[]){{NUMBER, 1.0f}, {ADD, 0.0f}, {NUMBER, 1.0f}}, 3);
148+	test_get_unit("23px * 5678.7%", (struct Unit[]){{PX, 23.0f}, {MULT, 0.0f}, {PERCENT, 5678.7f}}, 3);
149+	test_get_unit("calc(23px * size)", (struct Unit[]){{CALC, 3}, {PX, 23.0f}, {MULT, 0.0f}, {VAR, 0.0f}}, 4);
150 }
151diff --git a/src/parser.c b/src/parser.c
152index 8d3d941..eb14712 100755
153--- a/src/parser.c
154+++ b/src/parser.c
155@@ -74,10 +74,23 @@ static const struct AbsSize kAbsSize[] = {
156 	{"large",1.2f}, {"x-large",1.5f}, {"xx-large",2.0f}, {"xxx-large",3.0f}
157 };
158 
159-/*
160-// Parses a string into Unit's and stores variables & strings into the Style object
161+void write_unit(struct Unit** units_ptr, int* len, int* cap, struct Unit val) {
162+	if (*len >= *cap) {
163+		int new_cap = (*cap <= 0) ? 8 : (*cap * 2);
164+		struct Unit* next = realloc(*units_ptr, sizeof(struct Unit) * new_cap);
165+		if (!next) {
166+			fprintf(stderr, "Out of memory\n");
167+			exit(1);
168+		}
169+		*units_ptr = next;
170+		*cap = new_cap;
171+	}
172 
173-void getUnit(SymbolTable* sym, Unit* units, int* unit_len, char* value, int size) {
174+	(*units_ptr)[*len] = val;
175+	(*len)++;
176+}
177+
178+void get_unit(SymbolTable* sym, struct Unit** units, int* cap, int* units_len, char* value) {
179 	char buffer[1024];
180 	int buffer_len = 0;
181 
182@@ -92,21 +105,23 @@ void getUnit(SymbolTable* sym, Unit* units, int* unit_len, char* value, int size
183 	bool inlineComment = false;
184 	bool multilineComment = false;
185 
186-	for (size_t i = 0; i < size; i++) {
187+	int size = strlen(value);
188+
189+	for (int i = 0; i < size; i++) {
190 		char v = value[i];
191 		if (v == '\\' && !escaped) escaped = true;
192 		if (!escaped && (v == '"' || v == '\'') && parenDepth == 0) {
193 			if (inQuotes && v == quoteType) {
194-				Unit unit;
195+				struct Unit unit;
196 				unit.type = STRING;
197 				unit.value = str_buffer_len;
198-				units[units_len++] = unit;
199+				write_unit(units, units_len, cap, unit);
200 
201-				for (size_t j = 0; j < str_buffer_len; j++) {
202-					Unit unit;
203+				for (int j = 0; j < str_buffer_len; j++) {
204+					struct Unit unit;
205 					unit.type = CHAR;
206 					unit.value = str_buffer[j];
207-					units[units_len++] = unit;
208+					write_unit(units, units_len, cap, unit);
209 				}
210 
211 				str_buffer[0] = '\0';
212@@ -165,12 +180,12 @@ void getUnit(SymbolTable* sym, Unit* units, int* unit_len, char* value, int size
213 
214 			bool nameset = false;
215 
216-			char args[MAX_STR_LEN];
217+			char* args = malloc(MAX_STR_LEN);
218 			int args_len = 0;
219 
220-			for (size_t j = 0; j < buffer_len; j++) {
221+			for (int j = 0; j < buffer_len; j++) {
222 				if (nameset) {
223-					args[arg_len++] = buffer[j];
224+					args[args_len++] = buffer[j];
225 				} else {
226 					if (buffer[j] == '(') {
227 						nameset = true;
228@@ -180,28 +195,25 @@ void getUnit(SymbolTable* sym, Unit* units, int* unit_len, char* value, int size
229 				}
230 			}
231 
232-			Unit* children;	
233-			int children_len = 0;
234-			getUnit(sym, children, &children_len, &args, args_len);
235+			args[args_len++] = '\0';
236 
237-			Unit unit;
238+			int function_idx = *units_len;
239 
240-			const struct FuncMap* fm = find_func(name, name_len);
241+			struct Unit placeholder = {0};
242+			write_unit(units, units_len, cap, placeholder);
243 
244-			if (fm) { 
245-				unit.type = fm->type;
246-			} else {
247-				unit.type = FUNC;
248-			}
249+			int before_count = *units_len;
250+			get_unit(sym, units, cap, units_len, args);
251+			int child_count = *units_len - before_count;
252 
253-			// The value of the function is the child count
254-			unit.value = children_len;
255+			const struct FuncMap* fm = find_func(name, name_len);
256+			struct Unit final_unit;
257+			final_unit.type = fm ? fm->type : FUNC;
258+			final_unit.value = (float)child_count;
259 
260-			units[units_len++] = unit;
261+			(*units)[function_idx] = final_unit;
262 
263-			for (int j = 0; j < children_len; j++) {
264-				units[units_len++] = children[j];
265-			}
266+			free(args);
267 			buffer[0] = '\0';
268 			buffer_len = 0;
269 		} else if ((!inQuotes && (v == ',' ||  isspace(v))) || i >= size - 1) {
270@@ -209,24 +221,26 @@ void getUnit(SymbolTable* sym, Unit* units, int* unit_len, char* value, int size
271 				buffer[buffer_len++] = v;
272 			}
273 
274-			Unit unit;
275+			struct Unit unit;
276 
277 			// Split by spaces into parts
278 
279 			const struct MatchMap* mm = find_match(buffer, buffer_len);
280 			const struct ColorMap* cm = find_color(buffer, buffer_len);
281 
282+			buffer[buffer_len++] = '\0';
283+
284 			if (mm) { 
285 				//	Keyword Matching
286-				
287 				unit.type = mm->type;
288+				unit.value = 0.0f;
289 				// the value is not used here
290 				buffer[0] = '\0';
291 				buffer_len = 0;
292 			} else if (cm) { 
293 				//	Color Matching
294 				
295-				unit.type = UnitType::HEX;
296+				unit.type = HEX;
297 				unit.value = cm->hex;
298 				buffer[0] = '\0';
299 				buffer_len = 0;
300@@ -234,21 +248,20 @@ void getUnit(SymbolTable* sym, Unit* units, int* unit_len, char* value, int size
301 			} else {
302 				bool found = false;
303 				size_t suf_match_length = sizeof(sufMatch) / sizeof(sufMatch[0]);
304+				
305+				for (size_t j = 0; j < suf_match_length; j++) {
306+					if (buffer_len >= sufMatch[j].trim+1) {
307+						if (strcmp(buffer + (buffer_len-sufMatch[j].trim-1), sufMatch[j].suffix) == 0) {
308+							found = true;
309 
310-				for (int j = 0; j < suf_match_length; j++) {
311-					if (strcmp(buffer + (buffer_len-sufMatch[j].trim), sufMatch[j].suffix)) {
312-						found = true;
313-						try {
314 							buffer[buffer_len-sufMatch[j].trim] = '\0';
315 							unit.value = (float)atof(buffer);
316-						} catch (...) {
317-							unit.value = 0.0f;
318-						}
319 
320-						unit.type = sufMatch[j].type;
321-						buffer[0] = '\0';
322-						buffer_len = 0;
323-						break;
324+							unit.type = sufMatch[j].type;
325+							buffer[0] = '\0';
326+							buffer_len = 0;
327+							break;
328+						}
329 					}
330 				}
331 
332@@ -266,7 +279,7 @@ void getUnit(SymbolTable* sym, Unit* units, int* unit_len, char* value, int size
333 
334 						//	Absolute size matching
335 						for (int j = 0; j < abs_length; j++) {
336-							if (strmp(buffer, kAbsSize[j].key)) {
337+							if (strcmp(buffer, kAbsSize[j].key) == 0) {
338 								unit.type = EM;
339 								unit.value = kAbsSize[j].value;
340 								found = true;
341@@ -275,18 +288,21 @@ void getUnit(SymbolTable* sym, Unit* units, int* unit_len, char* value, int size
342 						}
343 
344 						if (!found) { 
345-							if (strcmp(buffer, "true")) {
346+							if (strcmp(buffer, "true") == 0) {
347 								unit.type = BOOL;
348 								unit.value = true;
349-							} else if (strcmp(buffer, "false")) {
350+							} else if (strcmp(buffer, "false") == 0) {
351 								unit.type = BOOL;
352 								unit.value = false;
353 							} else {
354 								// Maybe its a number?
355-								try {
356+								char* endptr;
357+								strtof(buffer, &endptr);
358+
359+								if (buffer != endptr) {
360 									unit.value = (float)atof(buffer);
361-									unit.type = UnitType::NUMBER;
362-								} catch (...) {
363+									unit.type = NUMBER;
364+								} else {
365 									// If we are here it is not a known parsable word, this could be a typo 
366 									// or a keyword like with the attr(size px) function. Here px would be
367 									// parsed but not size.
368@@ -309,7 +325,7 @@ void getUnit(SymbolTable* sym, Unit* units, int* unit_len, char* value, int size
369 				}
370 			}
371 
372-			units[units_len++] = unit;
373+			write_unit(units, units_len, cap, unit);
374 			buffer[0] = '\0';
375 			buffer_len = 0;
376 		} else if (inQuotes) {
377@@ -322,7 +338,7 @@ void getUnit(SymbolTable* sym, Unit* units, int* unit_len, char* value, int size
378 		if (v == '\\') escaped = true;
379 	}
380 }
381-*/
382+
383 
384 // Basemap maps sym tokens to arrays of indexes to the selector list
385 // @container (width > 200px)			| getUnit
386@@ -407,8 +423,6 @@ void html_parser(SymbolTable* sym, Node* root, const char* input, int size) {
387 	char attr_name[MAX_STR_LEN];
388 	bool attr_name_set = false;
389 
390-	char attr_value[MAX_STR_LEN];
391-
392 	bool inRawMode = false;
393 	char rawTagName[MAX_STR_LEN] = "";
394 
395@@ -485,7 +499,7 @@ void html_parser(SymbolTable* sym, Node* root, const char* input, int size) {
396 						if (strcmp(attr_name, "id") == 0) {
397 							SET_ID(sym, currentNode, token);
398 						} else if (strcmp(attr_name, "class") == 0) {
399-							char* buffer;
400+							char* buffer = "\0";
401 							int buffer_length = 0;
402 							for (int j = 0; j < token_length; j++) {
403 								if (isspace(token[j])) {