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.

...rest params

+164 -5
+83 -5
src/ant.c
··· 64 64 TOK_YIELD, TOK_UNDEF, TOK_NULL, TOK_TRUE, TOK_FALSE, 65 65 TOK_DOT = 100, TOK_CALL, TOK_BRACKET, TOK_POSTINC, TOK_POSTDEC, TOK_NOT, TOK_TILDA, 66 66 TOK_TYPEOF, TOK_UPLUS, TOK_UMINUS, TOK_EXP, TOK_MUL, TOK_DIV, TOK_REM, 67 - TOK_OPTIONAL_CHAIN, 67 + TOK_OPTIONAL_CHAIN, TOK_REST, 68 68 TOK_PLUS, TOK_MINUS, TOK_SHL, TOK_SHR, TOK_ZSHR, TOK_LT, TOK_LE, TOK_GT, 69 69 TOK_GE, TOK_EQ, TOK_NE, TOK_AND, TOK_XOR, TOK_OR, TOK_LAND, TOK_LOR, 70 70 TOK_COLON, TOK_Q, TOK_ASSIGN, TOK_PLUS_ASSIGN, TOK_MINUS_ASSIGN, ··· 639 639 case ';': TOK(TOK_SEMICOLON, 1); 640 640 case ',': TOK(TOK_COMMA, 1); 641 641 case '!': if (LOOK(1, '=') && LOOK(2, '=')) TOK(TOK_NE, 3); if (LOOK(1, '=')) TOK(TOK_NE, 2); TOK(TOK_NOT, 1); 642 - case '.': TOK(TOK_DOT, 1); 642 + case '.': if (LOOK(1, '.') && LOOK(2, '.')) TOK(TOK_REST, 3); TOK(TOK_DOT, 1); 643 643 case '~': TOK(TOK_TILDA, 1); 644 644 case '-': if (LOOK(1, '-')) TOK(TOK_POSTDEC, 2); if (LOOK(1, '=')) TOK(TOK_MINUS_ASSIGN, 2); TOK(TOK_MINUS, 1); 645 645 case '+': if (LOOK(1, '+')) TOK(TOK_POSTINC, 2); if (LOOK(1, '=')) TOK(TOK_PLUS_ASSIGN, 2); TOK(TOK_PLUS, 1); ··· 1063 1063 } 1064 1064 1065 1065 jsval_t function_scope = mkobj(js, parent_scope_offset); 1066 + bool has_rest = false; 1067 + jsoff_t rest_param_start = 0, rest_param_len = 0; 1068 + 1066 1069 while (fnpos < fnlen) { 1067 1070 fnpos = skiptonext(fn, fnlen, fnpos); 1068 1071 if (fnpos < fnlen && fn[fnpos] == ')') break; 1072 + 1073 + bool is_rest = false; 1074 + if (fnpos + 3 < fnlen && fn[fnpos] == '.' && fn[fnpos + 1] == '.' && fn[fnpos + 2] == '.') { 1075 + is_rest = true; 1076 + has_rest = true; 1077 + fnpos += 3; 1078 + fnpos = skiptonext(fn, fnlen, fnpos); 1079 + } 1080 + 1069 1081 jsoff_t identlen = 0; 1070 1082 uint8_t tok = parseident(&fn[fnpos], fnlen - fnpos, &identlen); 1071 1083 if (tok != TOK_IDENTIFIER) break; 1084 + 1085 + if (is_rest) { 1086 + rest_param_start = fnpos; 1087 + rest_param_len = identlen; 1088 + fnpos = skiptonext(fn, fnlen, fnpos + identlen); 1089 + break; 1090 + } 1091 + 1072 1092 js->pos = skiptonext(js->code, js->clen, js->pos); 1073 1093 js->consumed = 1; 1074 1094 jsval_t v = js->code[js->pos] == ')' ? js_mkundef() : js_expr(js); ··· 1079 1099 if (fnpos < fnlen && fn[fnpos] == ',') fnpos++; 1080 1100 } 1081 1101 1102 + if (has_rest && rest_param_len > 0) { 1103 + jsval_t rest_array = mkarr(js); 1104 + if (!is_err(rest_array)) { 1105 + jsoff_t idx = 0; 1106 + js->pos = skiptonext(js->code, js->clen, js->pos); 1107 + 1108 + while (js->pos < js->clen && js->code[js->pos] != ')') { 1109 + js->consumed = 1; 1110 + jsval_t arg = js_expr(js); 1111 + if (!is_err(arg)) { 1112 + char idxstr[16]; 1113 + snprintf(idxstr, sizeof(idxstr), "%u", (unsigned) idx); 1114 + jsval_t key = js_mkstr(js, idxstr, strlen(idxstr)); 1115 + setprop(js, rest_array, key, resolveprop(js, arg)); 1116 + idx++; 1117 + } 1118 + js->pos = skiptonext(js->code, js->clen, js->pos); 1119 + if (js->pos < js->clen && js->code[js->pos] == ',') js->pos++; 1120 + } 1121 + 1122 + jsval_t len_key = js_mkstr(js, "length", 6); 1123 + setprop(js, rest_array, len_key, tov((double) idx)); 1124 + rest_array = mkval(T_ARR, vdata(rest_array)); 1125 + 1126 + setprop(js, function_scope, js_mkstr(js, &fn[rest_param_start], rest_param_len), rest_array); 1127 + } 1128 + } 1129 + 1082 1130 js->scope = function_scope; 1083 1131 if (fnpos < fnlen && fn[fnpos] == ')') fnpos++; 1084 1132 fnpos = skiptonext(fn, fnlen, fnpos); ··· 1485 1533 jsoff_t pos = js->pos - 1; 1486 1534 for (bool comma = false; next(js) != TOK_EOF; comma = true) { 1487 1535 if (!comma && next(js) == TOK_RPAREN) break; 1488 - EXPECT(TOK_IDENTIFIER, js->flags = flags); 1536 + 1537 + bool is_rest = false; 1538 + if (next(js) == TOK_REST) { 1539 + is_rest = true; 1540 + js->consumed = 1; 1541 + next(js); 1542 + } 1543 + 1544 + if (next(js) != TOK_IDENTIFIER) { 1545 + js->flags = flags; 1546 + return js_mkerr(js, "identifier expected"); 1547 + } 1548 + js->consumed = 1; 1549 + 1550 + if (is_rest && next(js) != TOK_RPAREN) { 1551 + js->flags = flags; 1552 + return js_mkerr(js, "rest parameter must be last"); 1553 + } 1489 1554 if (next(js) == TOK_RPAREN) break; 1490 1555 EXPECT(TOK_COMMA, js->flags = flags); 1491 1556 } ··· 1811 1876 else if (js->tok == TOK_RPAREN) paren_depth--; 1812 1877 1813 1878 if (paren_depth > 0) { 1814 - if (js->tok != TOK_IDENTIFIER && js->tok != TOK_COMMA) could_be_arrow = false; 1879 + if (js->tok != TOK_IDENTIFIER && js->tok != TOK_COMMA && js->tok != TOK_REST) could_be_arrow = false; 1815 1880 } 1816 1881 js->consumed = 1; 1817 1882 } ··· 2296 2361 jsoff_t pos = js->pos - 1; 2297 2362 for (bool comma = false; next(js) != TOK_EOF; comma = true) { 2298 2363 if (!comma && next(js) == TOK_RPAREN) break; 2299 - EXPECT(TOK_IDENTIFIER, ); 2364 + 2365 + bool is_rest = false; 2366 + if (next(js) == TOK_REST) { 2367 + is_rest = true; 2368 + js->consumed = 1; 2369 + next(js); 2370 + } 2371 + 2372 + if (next(js) != TOK_IDENTIFIER) return js_mkerr(js, "identifier expected"); 2373 + js->consumed = 1; 2374 + 2375 + if (is_rest && next(js) != TOK_RPAREN) { 2376 + return js_mkerr(js, "rest parameter must be last"); 2377 + } 2300 2378 if (next(js) == TOK_RPAREN) break; 2301 2379 EXPECT(TOK_COMMA, ); 2302 2380 }
+81
tests/test_rest_params.cjs
··· 1 + // Test rest parameters in function definitions 2 + 3 + // Test 1: Basic rest parameter 4 + function sum(...numbers) { 5 + let total = 0; 6 + for (let i = 0; i < numbers.length; i++) { 7 + total = total + numbers[i]; 8 + } 9 + return total; 10 + } 11 + 12 + Ant.println("Test 1 - Basic rest params:"); 13 + Ant.println(sum(1, 2, 3, 4, 5)); // Should print 15 14 + 15 + // Test 2: Rest parameter with regular parameters 16 + function greet(greeting, ...names) { 17 + let result = greeting; 18 + for (let i = 0; i < names.length; i++) { 19 + result = result + " " + names[i]; 20 + } 21 + return result; 22 + } 23 + 24 + Ant.println("\nTest 2 - Rest params with regular params:"); 25 + Ant.println(greet("Hello", "Alice", "Bob", "Charlie")); // Should print "Hello Alice Bob Charlie" 26 + 27 + // Test 3: Rest parameter with no arguments passed 28 + function noArgs(...args) { 29 + return args.length; 30 + } 31 + 32 + Ant.println("\nTest 3 - Rest params with no args:"); 33 + Ant.println(noArgs()); // Should print 0 34 + 35 + // Test 4: Rest parameter with single argument 36 + function singleArg(...args) { 37 + return args[0]; 38 + } 39 + 40 + Ant.println("\nTest 4 - Rest params with single arg:"); 41 + Ant.println(singleArg(42)); // Should print 42 42 + 43 + // Test 5: Arrow function with rest parameters 44 + const multiply = (...factors) => { 45 + let result = 1; 46 + for (let i = 0; i < factors.length; i++) { 47 + result = result * factors[i]; 48 + } 49 + return result; 50 + }; 51 + 52 + Ant.println("\nTest 5 - Arrow function with rest params:"); 53 + Ant.println(multiply(2, 3, 4)); // Should print 24 54 + 55 + // Test 6: Multiple regular params with rest 56 + function compute(operation, initial, ...values) { 57 + let result = initial; 58 + if (operation === "add") { 59 + for (let i = 0; i < values.length; i++) { 60 + result = result + values[i]; 61 + } 62 + } 63 + if (operation === "multiply") { 64 + for (let i = 0; i < values.length; i++) { 65 + result = result * values[i]; 66 + } 67 + } 68 + return result; 69 + } 70 + 71 + Ant.println("\nTest 6 - Multiple params with rest:"); 72 + Ant.println(compute("add", 10, 5, 3, 2)); // Should print 20 73 + Ant.println(compute("multiply", 2, 3, 4)); // Should print 24 74 + 75 + // Test 7: Rest parameter is an actual array 76 + function checkArray(...items) { 77 + return items.length; 78 + } 79 + 80 + Ant.println("\nTest 7 - Rest param is array:"); 81 + Ant.println(checkArray("a", "b", "c", "d")); // Should print 4