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.

'in' operator

+284 -9
+150 -9
src/ant.c
··· 133 133 static jsval_t js_block_or_stmt(struct js *js); 134 134 static jsval_t do_op(struct js *, uint8_t op, jsval_t l, jsval_t r); 135 135 static jsval_t do_instanceof(struct js *js, jsval_t l, jsval_t r); 136 + static jsval_t do_in(struct js *js, jsval_t l, jsval_t r); 136 137 static jsval_t resolveprop(struct js *js, jsval_t v); 137 138 static jsoff_t lkp(struct js *js, jsval_t obj, const char *buf, size_t len); 138 139 static jsval_t builtin_array_push(struct js *js, jsval_t *args, int nargs); ··· 1335 1336 case TOK_TYPEOF: return js_mkstr(js, typestr(vtype(r)), strlen(typestr(vtype(r)))); 1336 1337 case TOK_VOID: return js_mkundef(); 1337 1338 case TOK_INSTANCEOF: return do_instanceof(js, l, r); 1339 + case TOK_IN: return do_in(js, l, r); 1338 1340 case TOK_CALL: return do_call_op(js, l, r); 1339 1341 case TOK_BRACKET: return do_bracket_op(js, l, rhs); 1340 1342 case TOK_ASSIGN: return assign(js, lhs, r); ··· 2338 2340 LTR_BINOP(js_shifts, 2339 2341 (next(js) == TOK_LT || next(js) == TOK_LE || 2340 2342 next(js) == TOK_GT || next(js) == TOK_GE || 2341 - next(js) == TOK_INSTANCEOF)); 2343 + next(js) == TOK_INSTANCEOF || next(js) == TOK_IN)); 2342 2344 } 2343 2345 2344 2346 static jsval_t js_equality(struct js *js) { ··· 2805 2807 if (exe) mkscope(js); 2806 2808 if (!expect(js, TOK_FOR, &res)) goto done; 2807 2809 if (!expect(js, TOK_LPAREN, &res)) goto done; 2808 - if (next(js) == TOK_SEMICOLON) { 2809 - } else if (next(js) == TOK_LET) { 2810 - v = js_let(js); 2811 - if (is_err2(&v, &res)) goto done; 2812 - } else if (next(js) == TOK_CONST) { 2813 - v = js_const(js); 2814 - if (is_err2(&v, &res)) goto done; 2810 + 2811 + // Check for for-in loop: for (let/const/var x in obj) 2812 + bool is_for_in = false; 2813 + jsoff_t var_name_off = 0, var_name_len = 0; 2814 + bool is_const_var = false; 2815 + 2816 + if (next(js) == TOK_LET || next(js) == TOK_CONST) { 2817 + is_const_var = (js->tok == TOK_CONST); 2818 + js->consumed = 1; 2819 + if (next(js) == TOK_IDENTIFIER) { 2820 + var_name_off = js->toff; 2821 + var_name_len = js->tlen; 2822 + js->consumed = 1; 2823 + if (next(js) == TOK_IN) { 2824 + is_for_in = true; 2825 + js->consumed = 1; 2826 + } else { 2827 + // Not for-in, backtrack and handle as regular for loop 2828 + js->pos = var_name_off; 2829 + js->consumed = 1; 2830 + if (is_const_var) { 2831 + v = js_const(js); 2832 + } else { 2833 + v = js_let(js); 2834 + } 2835 + if (is_err2(&v, &res)) goto done; 2836 + } 2837 + } 2838 + } else if (next(js) == TOK_IDENTIFIER) { 2839 + var_name_off = js->toff; 2840 + var_name_len = js->tlen; 2841 + js->consumed = 1; 2842 + if (next(js) == TOK_IN) { 2843 + is_for_in = true; 2844 + js->consumed = 1; 2845 + } else { 2846 + // Not for-in, parse as expression 2847 + js->pos = var_name_off; 2848 + js->consumed = 1; 2849 + v = js_expr(js); 2850 + if (is_err2(&v, &res)) goto done; 2851 + } 2852 + } else if (next(js) == TOK_SEMICOLON) { 2853 + // Empty init in regular for loop 2815 2854 } else { 2816 2855 v = js_expr(js); 2817 2856 if (is_err2(&v, &res)) goto done; 2818 2857 } 2858 + 2859 + if (is_for_in) { 2860 + // Handle for-in loop 2861 + jsval_t obj_expr = js_expr(js); 2862 + if (is_err2(&obj_expr, &res)) goto done; 2863 + if (!expect(js, TOK_RPAREN, &res)) goto done; 2864 + 2865 + jsoff_t body_start = js->pos; 2866 + js->flags |= F_NOEXEC; 2867 + v = js_block_or_stmt(js); 2868 + if (is_err2(&v, &res)) goto done; 2869 + jsoff_t body_end = js->pos; 2870 + 2871 + if (exe) { 2872 + jsval_t obj = resolveprop(js, obj_expr); 2873 + if (vtype(obj) != T_OBJ && vtype(obj) != T_ARR && vtype(obj) != T_FUNC) { 2874 + res = js_mkerr(js, "for-in requires object"); 2875 + goto done; 2876 + } 2877 + 2878 + jsval_t iter_obj = obj; 2879 + if (vtype(obj) == T_FUNC) { 2880 + iter_obj = mkval(T_OBJ, vdata(obj)); 2881 + } 2882 + 2883 + // Iterate over object properties 2884 + jsoff_t prop_off = loadoff(js, (jsoff_t) vdata(iter_obj)) & ~(3U | CONSTMASK); 2885 + 2886 + while (prop_off < js->brk && prop_off != 0) { 2887 + // Get property key 2888 + jsoff_t koff = loadoff(js, prop_off + (jsoff_t) sizeof(prop_off)); 2889 + jsoff_t klen = offtolen(loadoff(js, koff)); 2890 + jsval_t key_str = js_mkstr(js, (char *) &js->mem[koff + sizeof(koff)], klen); 2891 + 2892 + // Set loop variable to the key 2893 + const char *var_name = &js->code[var_name_off]; 2894 + jsoff_t existing = lkp(js, js->scope, var_name, var_name_len); 2895 + if (existing > 0) { 2896 + saveval(js, existing + sizeof(jsoff_t) * 2, key_str); 2897 + } else { 2898 + jsval_t x = mkprop(js, js->scope, js_mkstr(js, var_name, var_name_len), key_str, is_const_var); 2899 + if (is_err(x)) { 2900 + res = x; 2901 + goto done; 2902 + } 2903 + } 2904 + 2905 + // Execute loop body 2906 + js->pos = body_start; 2907 + js->consumed = 1; 2908 + js->flags = (flags & ~F_NOEXEC) | F_LOOP; 2909 + v = js_block_or_stmt(js); 2910 + if (is_err(v)) { 2911 + res = v; 2912 + goto done; 2913 + } 2914 + 2915 + if (js->flags & F_BREAK) break; 2916 + if (js->flags & F_RETURN) { 2917 + res = v; 2918 + goto done; 2919 + } 2920 + 2921 + // Move to next property 2922 + prop_off = loadoff(js, prop_off) & ~(3U | CONSTMASK); 2923 + } 2924 + } 2925 + 2926 + js->pos = body_end; 2927 + js->tok = TOK_SEMICOLON; 2928 + js->consumed = 0; 2929 + goto done; 2930 + } 2931 + 2932 + // Regular for loop 2819 2933 if (!expect(js, TOK_SEMICOLON, &res)) goto done; 2820 2934 js->flags |= F_NOEXEC; 2821 2935 pos1 = js->pos; ··· 3214 3328 switch (next(js)) { 3215 3329 case TOK_CASE: case TOK_CATCH: 3216 3330 case TOK_DEFAULT: case TOK_FINALLY: 3217 - case TOK_IN: case TOK_SWITCH: 3331 + case TOK_SWITCH: 3218 3332 case TOK_THROW: case TOK_TRY: case TOK_VAR: 3219 3333 case TOK_WITH: case TOK_YIELD: 3220 3334 res = js_mkerr(js, "'%.*s' not implemented", (int) js->tlen, js->code + js->toff); ··· 4210 4324 } 4211 4325 } 4212 4326 return mkval(T_BOOL, 0); 4327 + } 4328 + 4329 + static jsval_t do_in(struct js *js, jsval_t l, jsval_t r) { 4330 + // l should be a string (property name), r should be an object 4331 + if (vtype(l) != T_STR) { 4332 + return js_mkerr(js, "left operand of 'in' must be string"); 4333 + } 4334 + 4335 + if (vtype(r) != T_OBJ && vtype(r) != T_ARR && vtype(r) != T_FUNC) { 4336 + return js_mkerr(js, "right operand of 'in' must be object"); 4337 + } 4338 + 4339 + // Get the property name 4340 + jsoff_t prop_len; 4341 + jsoff_t prop_off = vstr(js, l, &prop_len); 4342 + const char *prop_name = (char *) &js->mem[prop_off]; 4343 + 4344 + // For functions, check the function object 4345 + jsval_t check_obj = r; 4346 + if (vtype(r) == T_FUNC) { 4347 + check_obj = mkval(T_OBJ, vdata(r)); 4348 + } 4349 + 4350 + // Look up the property 4351 + jsoff_t found = lkp(js, check_obj, prop_name, prop_len); 4352 + 4353 + return mkval(T_BOOL, found != 0 ? 1 : 0); 4213 4354 } 4214 4355 4215 4356 struct js *js_create(void *buf, size_t len) {
+134
tests/test_in_operator.cjs
··· 1 + // Test 'in' operator and for-in loops 2 + Ant.println('=== In Operator and For-In Tests ==='); 3 + 4 + // Test 1: Basic 'in' operator with object 5 + Ant.println('\nTest 1: Basic in operator'); 6 + const obj1 = { name: 'test', value: 42, flag: true }; 7 + Ant.println("'name' in obj1: " + ('name' in obj1)); 8 + Ant.println("'value' in obj1: " + ('value' in obj1)); 9 + Ant.println("'missing' in obj1: " + ('missing' in obj1)); 10 + 11 + // Test 2: 'in' operator with arrays 12 + Ant.println('\nTest 2: In operator with arrays'); 13 + const arr = [10, 20, 30]; 14 + Ant.println("'0' in arr: " + ('0' in arr)); 15 + Ant.println("'1' in arr: " + ('1' in arr)); 16 + Ant.println("'5' in arr: " + ('5' in arr)); 17 + Ant.println("'length' in arr: " + ('length' in arr)); 18 + 19 + // Test 3: Basic for-in loop with object 20 + Ant.println('\nTest 3: For-in loop with object'); 21 + const obj2 = { a: 1, b: 2, c: 3 }; 22 + let keys = ''; 23 + for (let key in obj2) { 24 + keys = keys + key + ','; 25 + } 26 + Ant.println('Keys: ' + keys); 27 + 28 + // Test 4: For-in loop with const 29 + Ant.println('\nTest 4: For-in with const'); 30 + const obj3 = { x: 10, y: 20 }; 31 + for (const prop in obj3) { 32 + Ant.println('Property: ' + prop + ' = ' + obj3[prop]); 33 + } 34 + 35 + // Test 5: For-in loop accumulating values 36 + Ant.println('\nTest 5: For-in accumulating values'); 37 + const obj4 = { first: 5, second: 10, third: 15 }; 38 + let sum = 0; 39 + for (let k in obj4) { 40 + sum = sum + obj4[k]; 41 + } 42 + Ant.println('Sum of values: ' + sum); 43 + 44 + // Test 6: For-in with break 45 + Ant.println('\nTest 6: For-in with break'); 46 + const obj5 = { a: 1, b: 2, c: 3, d: 4 }; 47 + let count = 0; 48 + for (let key in obj5) { 49 + count = count + 1; 50 + if (count === 2) { 51 + break; 52 + } 53 + Ant.println('Key: ' + key); 54 + } 55 + Ant.println('Stopped at count: ' + count); 56 + 57 + // Test 7: For-in with continue 58 + Ant.println('\nTest 7: For-in with continue'); 59 + const obj6 = { a: 1, b: 2, c: 3, d: 4 }; 60 + for (let key in obj6) { 61 + if (key === 'b' || key === 'd') { 62 + continue; 63 + } 64 + Ant.println('Processing: ' + key); 65 + } 66 + 67 + // Test 8: For-in with array 68 + Ant.println('\nTest 8: For-in with array'); 69 + const arr2 = ['first', 'second', 'third']; 70 + for (let idx in arr2) { 71 + Ant.println('Index ' + idx + ': ' + arr2[idx]); 72 + } 73 + 74 + // Test 9: Nested for-in loops 75 + Ant.println('\nTest 9: Nested for-in loops'); 76 + const outer = { a: { x: 1, y: 2 }, b: { x: 3, y: 4 } }; 77 + for (let key1 in outer) { 78 + for (let key2 in outer[key1]) { 79 + Ant.println(key1 + '.' + key2 + ' = ' + outer[key1][key2]); 80 + } 81 + } 82 + 83 + // Test 10: In operator with nested properties 84 + Ant.println('\nTest 10: In operator with nested object'); 85 + const nested = { outer: { inner: 'value' } }; 86 + Ant.println("'outer' in nested: " + ('outer' in nested)); 87 + Ant.println("'inner' in nested: " + ('inner' in nested)); 88 + 89 + // Test 11: For-in counting properties 90 + Ant.println('\nTest 11: Counting properties with for-in'); 91 + const obj7 = { prop1: 'a', prop2: 'b', prop3: 'c', prop4: 'd' }; 92 + let propCount = 0; 93 + for (let p in obj7) { 94 + propCount = propCount + 1; 95 + } 96 + Ant.println('Property count: ' + propCount); 97 + 98 + // Test 12: For-in with empty object 99 + Ant.println('\nTest 12: For-in with empty object'); 100 + const empty = {}; 101 + let ranOnce = false; 102 + for (let k in empty) { 103 + ranOnce = true; 104 + } 105 + Ant.println('Loop ran: ' + ranOnce); 106 + 107 + // Test 13: In operator with different types 108 + Ant.println('\nTest 13: In operator checks'); 109 + const testObj = { num: 42, str: 'hello', bool: true }; 110 + Ant.println("'num' in testObj: " + ('num' in testObj)); 111 + Ant.println("'str' in testObj: " + ('str' in testObj)); 112 + Ant.println("'bool' in testObj: " + ('bool' in testObj)); 113 + 114 + // Test 14: For-in modifying external variable 115 + Ant.println('\nTest 14: For-in with external variable'); 116 + const data = { a: 10, b: 20, c: 30 }; 117 + let total = 0; 118 + for (let key in data) { 119 + total = total + data[key]; 120 + } 121 + Ant.println('Total: ' + total); 122 + 123 + // Test 15: For-in with conditional logic 124 + Ant.println('\nTest 15: For-in with conditional'); 125 + const items = { item1: 5, item2: 15, item3: 25, item4: 35 }; 126 + let filtered = 0; 127 + for (let name in items) { 128 + if (items[name] > 10) { 129 + filtered = filtered + items[name]; 130 + } 131 + } 132 + Ant.println('Filtered sum (>10): ' + filtered); 133 + 134 + Ant.println('\n=== All in operator tests completed ===');