MIRROR: javascript for ๐Ÿœ's, a tiny runtime with big ambitions
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

async server tests

+155 -36
+109 -21
src/ant.c
··· 1390 1390 1391 1391 while (next(js) != TOK_RBRACE) { 1392 1392 jsval_t key = 0; 1393 + jsoff_t id_off = 0, id_len = 0; 1394 + 1393 1395 if (js->tok == TOK_IDENTIFIER) { 1396 + id_off = js->toff; 1397 + id_len = js->tlen; 1394 1398 if (exe) key = js_mkstr(js, js->code + js->toff, js->tlen); 1395 1399 } else if (js->tok == TOK_STRING) { 1396 1400 if (exe) key = js_str_literal(js); ··· 1398 1402 return js_mkerr(js, "parse error"); 1399 1403 } 1400 1404 js->consumed = 1; 1401 - EXPECT(TOK_COLON, ); 1402 - jsval_t val = js_expr(js); 1403 - if (exe) { 1404 - if (is_err(val)) return val; 1405 - if (is_err(key)) return key; 1406 - jsval_t res = setprop(js, obj, key, resolveprop(js, val)); 1407 - if (is_err(res)) return res; 1405 + 1406 + // Check for shorthand property syntax 1407 + if (id_len > 0 && (next(js) == TOK_COMMA || next(js) == TOK_RBRACE)) { 1408 + // Shorthand: { handler, params } means { handler: handler, params: params } 1409 + jsval_t val = lookup(js, js->code + id_off, id_len); 1410 + if (exe) { 1411 + if (is_err(val)) return val; 1412 + if (is_err(key)) return key; 1413 + jsval_t res = setprop(js, obj, key, resolveprop(js, val)); 1414 + if (is_err(res)) return res; 1415 + } 1416 + } else { 1417 + // Normal syntax: { key: value } 1418 + EXPECT(TOK_COLON, ); 1419 + jsval_t val = js_expr(js); 1420 + if (exe) { 1421 + if (is_err(val)) return val; 1422 + if (is_err(key)) return key; 1423 + jsval_t res = setprop(js, obj, key, resolveprop(js, val)); 1424 + if (is_err(res)) return res; 1425 + } 1408 1426 } 1427 + 1409 1428 if (next(js) == TOK_RBRACE) break; 1410 1429 EXPECT(TOK_COMMA, ); 1411 1430 } ··· 2122 2141 uint8_t exe = !(js->flags & F_NOEXEC); 2123 2142 js->consumed = 1; 2124 2143 for (;;) { 2125 - EXPECT(TOK_IDENTIFIER, ); 2126 - js->consumed = 0; 2127 - jsoff_t noff = js->toff, nlen = js->tlen; 2128 - char *name = (char *) &js->code[noff]; 2129 - jsval_t v = js_mkundef(); 2130 - js->consumed = 1; 2131 - if (next(js) == TOK_ASSIGN) { 2144 + // Check for object destructuring pattern { a, b } or { handler, params } 2145 + if (next(js) == TOK_LBRACE) { 2146 + js->consumed = 1; 2147 + 2148 + // Parse destructuring pattern 2149 + typedef struct { jsoff_t name_off; jsoff_t name_len; } PropName; 2150 + PropName props[32]; 2151 + int prop_count = 0; 2152 + 2153 + while (next(js) != TOK_RBRACE && next(js) != TOK_EOF && prop_count < 32) { 2154 + EXPECT(TOK_IDENTIFIER, ); 2155 + props[prop_count].name_off = js->toff; 2156 + props[prop_count].name_len = js->tlen; 2157 + prop_count++; 2158 + js->consumed = 1; 2159 + 2160 + // Skip colon and alias if present (e.g., { handler: h }) 2161 + if (next(js) == TOK_COLON) { 2162 + js->consumed = 1; 2163 + EXPECT(TOK_IDENTIFIER, ); 2164 + js->consumed = 1; 2165 + } 2166 + 2167 + if (next(js) == TOK_RBRACE) break; 2168 + EXPECT(TOK_COMMA, ); 2169 + } 2170 + 2171 + EXPECT(TOK_RBRACE, ); 2172 + 2173 + // Expect assignment 2174 + jsval_t v = js_mkundef(); 2175 + if (next(js) == TOK_ASSIGN) { 2176 + js->consumed = 1; 2177 + v = js_expr(js); 2178 + if (is_err(v)) return v; 2179 + } else { 2180 + return js_mkerr(js, "destructuring requires assignment"); 2181 + } 2182 + 2183 + // Extract properties from the object 2184 + if (exe) { 2185 + jsval_t obj = resolveprop(js, v); 2186 + if (vtype(obj) != T_OBJ && vtype(obj) != T_ARR) { 2187 + return js_mkerr(js, "cannot destructure non-object"); 2188 + } 2189 + 2190 + for (int i = 0; i < prop_count; i++) { 2191 + const char *prop_name = &js->code[props[i].name_off]; 2192 + jsoff_t prop_len = props[i].name_len; 2193 + 2194 + if (lkp(js, js->scope, prop_name, prop_len) > 0) { 2195 + return js_mkerr(js, "'%.*s' already declared", (int) prop_len, prop_name); 2196 + } 2197 + 2198 + jsoff_t prop_off = lkp(js, obj, prop_name, prop_len); 2199 + jsval_t prop_val = js_mkundef(); 2200 + 2201 + if (prop_off > 0) { 2202 + prop_val = resolveprop(js, mkval(T_PROP, prop_off)); 2203 + } 2204 + 2205 + jsval_t x = mkprop(js, js->scope, js_mkstr(js, prop_name, prop_len), prop_val, is_const); 2206 + if (is_err(x)) return x; 2207 + } 2208 + } 2209 + } else { 2210 + // Regular identifier declaration 2211 + EXPECT(TOK_IDENTIFIER, ); 2212 + js->consumed = 0; 2213 + jsoff_t noff = js->toff, nlen = js->tlen; 2214 + char *name = (char *) &js->code[noff]; 2215 + jsval_t v = js_mkundef(); 2132 2216 js->consumed = 1; 2133 - v = js_expr(js); 2134 - if (is_err(v)) return v; 2135 - } 2136 - if (exe) { 2137 - if (lkp(js, js->scope, name, nlen) > 0) return js_mkerr(js, "'%.*s' already declared", (int) nlen, name); 2138 - jsval_t x = mkprop(js, js->scope, js_mkstr(js, name, nlen), resolveprop(js, v), is_const); 2139 - if (is_err(x)) return x; 2217 + if (next(js) == TOK_ASSIGN) { 2218 + js->consumed = 1; 2219 + v = js_expr(js); 2220 + if (is_err(v)) return v; 2221 + } 2222 + if (exe) { 2223 + if (lkp(js, js->scope, name, nlen) > 0) return js_mkerr(js, "'%.*s' already declared", (int) nlen, name); 2224 + jsval_t x = mkprop(js, js->scope, js_mkstr(js, name, nlen), resolveprop(js, v), is_const); 2225 + if (is_err(x)) return x; 2226 + } 2140 2227 } 2228 + 2141 2229 if (next(js) == TOK_SEMICOLON || next(js) == TOK_EOF) break; 2142 2230 EXPECT(TOK_COMMA, ); 2143 2231 }
+33
src/modules/server.c
··· 5 5 6 6 #include "mongoose.h" 7 7 #include "modules/server.h" 8 + #include "modules/timer.h" 8 9 9 10 typedef struct { 10 11 struct js *js; ··· 27 28 exit(0); 28 29 } 29 30 31 + static jsval_t wait_for_promise(struct js *js, jsval_t promise_val) { 32 + if (js_type(promise_val) != JS_PRIV) return promise_val; 33 + 34 + jsval_t state_check = js_get(js, promise_val, "__state"); 35 + if (js_type(state_check) == JS_UNDEF) return promise_val; 36 + 37 + while (has_pending_microtasks()) { 38 + process_microtasks(js); 39 + 40 + jsval_t state_val = js_get(js, promise_val, "__state"); 41 + if (js_type(state_val) == JS_NUM) { 42 + int state = (int)js_getnum(state_val); 43 + if (state != 0) { 44 + jsval_t value_val = js_get(js, promise_val, "__value"); 45 + return value_val; 46 + } 47 + } 48 + } 49 + 50 + jsval_t state_val = js_get(js, promise_val, "__state"); 51 + if (js_type(state_val) == JS_NUM) { 52 + int state = (int)js_getnum(state_val); 53 + if (state != 0) { 54 + jsval_t value_val = js_get(js, promise_val, "__value"); 55 + return value_val; 56 + } 57 + } 58 + 59 + return promise_val; 60 + } 61 + 30 62 static void http_handler(struct mg_connection *c, int ev, void *ev_data) { 31 63 if (ev == MG_EV_HTTP_MSG) { 32 64 http_server_t *server = (http_server_t *)c->fn_data; ··· 50 82 jsval_t args[2] = {req, res_obj}; 51 83 52 84 result = js_call(server->js, server->handler, args, 2); 85 + result = wait_for_promise(server->js, result); 53 86 } 54 87 55 88 if (js_type(result) == JS_ERR) {
+2 -4
tests/server/radix3.cjs
··· 118 118 const params = {}; 119 119 const handler = this.matchPath(this.root, path, 0, params); 120 120 121 - if (handler !== undefined) { 122 - return handler(params); 123 - } 124 - return undefined; 121 + if (!handler) return {}; 122 + return { handler, params }; 125 123 } 126 124 127 125 matchPath(node, path, depth, params) {
+11 -11
tests/server/server.cjs
··· 19 19 }; 20 20 }); 21 21 22 - router.insert('/hello', () => { 22 + router.insert('/hello', async () => { 23 23 return { status: 200, body: 'Hello, World!' }; 24 24 }); 25 25 26 - router.insert('/status', () => { 26 + router.insert('/status', async () => { 27 27 return { status: 200, body: 'Server is running with Radix3 router!' }; 28 28 }); 29 29 30 - router.insert('/echo', () => { 30 + router.insert('/echo', async () => { 31 31 return { status: 200, body: 'Echo endpoint - returns request details' }; 32 32 }); 33 33 34 - router.insert('/users/:id', p => { 34 + router.insert('/users/:id', async p => { 35 35 return { status: 200, body: 'User ID: ' + p.id }; 36 36 }); 37 37 38 - router.insert('/users/:id/posts', () => { 38 + router.insert('/users/:id/posts', async () => { 39 39 return { status: 200, body: 'Posts for user: ' + p.id }; 40 40 }); 41 41 42 - router.insert('/api/v1/users', () => { 42 + router.insert('/api/v1/users', async () => { 43 43 return { status: 200, body: 'API v1 users endpoint' }; 44 44 }); 45 45 46 - router.insert('/api/v2/users', () => { 46 + router.insert('/api/v2/users', async () => { 47 47 return { status: 200, body: 'API v2 users endpoint' }; 48 48 }); 49 49 50 - router.insert('/files/*path', () => { 50 + router.insert('/files/*path', async () => { 51 51 return { status: 200, body: 'File path: ' + p.path }; 52 52 }); 53 53 54 54 router.printTree(); 55 55 Ant.println(''); 56 56 57 - function handleRequest(req, _res) { 57 + async function handleRequest(req, _res) { 58 58 Ant.println('request:', req.method, req.uri); 59 59 60 - let result = router.lookup(req.uri); 61 - if (result !== undefined) return result; 60 + const { handler, params } = router.lookup(req.uri); 61 + handler(params); 62 62 63 63 return { status: 404, body: 'Not Found: ' + req.uri }; 64 64 }