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])) {