From 2f6700415e0630cb37793bcf289399ea0960dfa8 Mon Sep 17 00:00:00 2001 From: John Alanbrook Date: Sat, 7 Feb 2026 23:38:39 -0600 Subject: [PATCH] add functinos --- source/quickjs.c | 752 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 723 insertions(+), 29 deletions(-) diff --git a/source/quickjs.c b/source/quickjs.c index d5c10b2c..fa834c3e 100644 --- a/source/quickjs.c +++ b/source/quickjs.c @@ -570,6 +570,9 @@ typedef enum MachOpcode { MACH_CALLMETHOD, /* Method call: R(A)=obj, B=nargs in R(A+2)..R(A+1+B), C=cpool key */ + MACH_EQ_TOL, /* R(A) = eq_tol(R(B), R(B+1), R(B+2)), C=3 */ + MACH_NEQ_TOL, /* R(A) = ne_tol(R(B), R(B+1), R(B+2)), C=3 */ + MACH_NOP, MACH_OP_COUNT @@ -632,6 +635,8 @@ static const char *mach_opcode_names[MACH_OP_COUNT] = { [MACH_HASPROP] = "hasprop", [MACH_REGEXP] = "regexp", [MACH_CALLMETHOD] = "callmethod", + [MACH_EQ_TOL] = "eq_tol", + [MACH_NEQ_TOL] = "neq_tol", [MACH_NOP] = "nop", }; @@ -3886,6 +3891,21 @@ static int js_string_compare_value (JSContext *ctx, JSValue op1, JSValue op2, BO return (len1 < len2) ? -1 : 1; } +static int js_string_compare_value_nocase (JSContext *ctx, JSValue op1, JSValue op2) { + (void)ctx; + int len1 = js_string_value_len (op1); + int len2 = js_string_value_len (op2); + if (len1 != len2) return 1; + for (int i = 0; i < len1; i++) { + uint32_t c1 = js_string_value_get (op1, i); + uint32_t c2 = js_string_value_get (op2, i); + if (c1 >= 'A' && c1 <= 'Z') c1 += 32; + if (c2 >= 'A' && c2 <= 'Z') c2 += 32; + if (c1 != c2) return 1; + } + return 0; +} + static JSValue JS_ConcatString (JSContext *ctx, JSValue op1, JSValue op2) { if (unlikely (!JS_IsText (op1))) { op1 = JS_ToString (ctx, op1); @@ -28582,6 +28602,13 @@ redo: } else if (p[1] == '=') { p += 2; s->token_val = TOK_DIV_ASSIGN; + } else if (p[1] == '!') { + s->token_u.ident.str = (const char *)p; + s->token_u.ident.len = 2; + s->token_u.ident.has_escape = FALSE; + s->token_u.ident.is_reserved = FALSE; + s->token_val = TOK_IDENT; + p += 2; } else { p++; s->token_val = c; @@ -28705,70 +28732,250 @@ redo: case '*': if (p[1] == '=') { p += 2; s->token_val = TOK_MUL_ASSIGN; } else if (p[1] == '*') { - if (p[2] == '=') { p += 3; s->token_val = TOK_POW_ASSIGN; } + if (p[2] == '!') { + s->token_u.ident.str = (const char *)p; + s->token_u.ident.len = 3; + s->token_u.ident.has_escape = FALSE; + s->token_u.ident.is_reserved = FALSE; + s->token_val = TOK_IDENT; + p += 3; + } + else if (p[2] == '=') { p += 3; s->token_val = TOK_POW_ASSIGN; } else { p += 2; s->token_val = TOK_POW; } - } else { goto def_token; } + } + else if (p[1] == '!') { + s->token_u.ident.str = (const char *)p; + s->token_u.ident.len = 2; + s->token_u.ident.has_escape = FALSE; + s->token_u.ident.is_reserved = FALSE; + s->token_val = TOK_IDENT; + p += 2; + } + else { goto def_token; } break; case '%': if (p[1] == '=') { p += 2; s->token_val = TOK_MOD_ASSIGN; } + else if (p[1] == '!') { + s->token_u.ident.str = (const char *)p; + s->token_u.ident.len = 2; + s->token_u.ident.has_escape = FALSE; + s->token_u.ident.is_reserved = FALSE; + s->token_val = TOK_IDENT; + p += 2; + } else { goto def_token; } break; case '+': if (p[1] == '=') { p += 2; s->token_val = TOK_PLUS_ASSIGN; } else if (p[1] == '+') { p += 2; s->token_val = TOK_INC; } + else if (p[1] == '!') { + s->token_u.ident.str = (const char *)p; + s->token_u.ident.len = 2; + s->token_u.ident.has_escape = FALSE; + s->token_u.ident.is_reserved = FALSE; + s->token_val = TOK_IDENT; + p += 2; + } else { goto def_token; } break; case '-': if (p[1] == '=') { p += 2; s->token_val = TOK_MINUS_ASSIGN; } else if (p[1] == '-') { p += 2; s->token_val = TOK_DEC; } + else if (p[1] == '!') { + s->token_u.ident.str = (const char *)p; + s->token_u.ident.len = 2; + s->token_u.ident.has_escape = FALSE; + s->token_u.ident.is_reserved = FALSE; + s->token_val = TOK_IDENT; + p += 2; + } else { goto def_token; } break; case '<': - if (p[1] == '=') { p += 2; s->token_val = TOK_LTE; } + if (p[1] == '=' && p[2] == '!') { + s->token_u.ident.str = (const char *)p; + s->token_u.ident.len = 3; + s->token_u.ident.has_escape = FALSE; + s->token_u.ident.is_reserved = FALSE; + s->token_val = TOK_IDENT; + p += 3; + } + else if (p[1] == '=') { p += 2; s->token_val = TOK_LTE; } else if (p[1] == '<') { - if (p[2] == '=') { p += 3; s->token_val = TOK_SHL_ASSIGN; } + if (p[2] == '!') { + s->token_u.ident.str = (const char *)p; + s->token_u.ident.len = 3; + s->token_u.ident.has_escape = FALSE; + s->token_u.ident.is_reserved = FALSE; + s->token_val = TOK_IDENT; + p += 3; + } + else if (p[2] == '=') { p += 3; s->token_val = TOK_SHL_ASSIGN; } else { p += 2; s->token_val = TOK_SHL; } - } else { goto def_token; } + } + else if (p[1] == '!') { + s->token_u.ident.str = (const char *)p; + s->token_u.ident.len = 2; + s->token_u.ident.has_escape = FALSE; + s->token_u.ident.is_reserved = FALSE; + s->token_val = TOK_IDENT; + p += 2; + } + else { goto def_token; } break; case '>': - if (p[1] == '=') { p += 2; s->token_val = TOK_GTE; } + if (p[1] == '=' && p[2] == '!') { + s->token_u.ident.str = (const char *)p; + s->token_u.ident.len = 3; + s->token_u.ident.has_escape = FALSE; + s->token_u.ident.is_reserved = FALSE; + s->token_val = TOK_IDENT; + p += 3; + } + else if (p[1] == '=') { p += 2; s->token_val = TOK_GTE; } else if (p[1] == '>') { if (p[2] == '>') { - if (p[3] == '=') { p += 4; s->token_val = TOK_SHR_ASSIGN; } + if (p[3] == '!') { + s->token_u.ident.str = (const char *)p; + s->token_u.ident.len = 4; + s->token_u.ident.has_escape = FALSE; + s->token_u.ident.is_reserved = FALSE; + s->token_val = TOK_IDENT; + p += 4; + } + else if (p[3] == '=') { p += 4; s->token_val = TOK_SHR_ASSIGN; } else { p += 3; s->token_val = TOK_SHR; } - } else if (p[2] == '=') { p += 3; s->token_val = TOK_SAR_ASSIGN; } + } + else if (p[2] == '!') { + s->token_u.ident.str = (const char *)p; + s->token_u.ident.len = 3; + s->token_u.ident.has_escape = FALSE; + s->token_u.ident.is_reserved = FALSE; + s->token_val = TOK_IDENT; + p += 3; + } + else if (p[2] == '=') { p += 3; s->token_val = TOK_SAR_ASSIGN; } else { p += 2; s->token_val = TOK_SAR; } - } else { goto def_token; } + } + else if (p[1] == '!') { + s->token_u.ident.str = (const char *)p; + s->token_u.ident.len = 2; + s->token_u.ident.has_escape = FALSE; + s->token_u.ident.is_reserved = FALSE; + s->token_val = TOK_IDENT; + p += 2; + } + else { goto def_token; } break; case '=': if (p[1] == '=') { if (p[2] == '=') { p += 3; s->token_val = TOK_STRICT_EQ; } else { p += 2; s->token_val = TOK_EQ; } } else if (p[1] == '>') { p += 2; s->token_val = TOK_ARROW; } + else if (p[1] == '!') { + s->token_u.ident.str = (const char *)p; + s->token_u.ident.len = 2; + s->token_u.ident.has_escape = FALSE; + s->token_u.ident.is_reserved = FALSE; + s->token_val = TOK_IDENT; + p += 2; + } else { goto def_token; } break; case '!': if (p[1] == '=') { - if (p[2] == '=') { p += 3; s->token_val = TOK_STRICT_NEQ; } + if (p[2] == '!') { + s->token_u.ident.str = (const char *)p; + s->token_u.ident.len = 3; + s->token_u.ident.has_escape = FALSE; + s->token_u.ident.is_reserved = FALSE; + s->token_val = TOK_IDENT; + p += 3; + } + else if (p[2] == '=') { p += 3; s->token_val = TOK_STRICT_NEQ; } else { p += 2; s->token_val = TOK_NEQ; } } else { goto def_token; } break; case '&': if (p[1] == '&') { - if (p[2] == '=') { p += 3; s->token_val = TOK_LAND_ASSIGN; } + if (p[2] == '!') { + s->token_u.ident.str = (const char *)p; + s->token_u.ident.len = 3; + s->token_u.ident.has_escape = FALSE; + s->token_u.ident.is_reserved = FALSE; + s->token_val = TOK_IDENT; + p += 3; + } + else if (p[2] == '=') { p += 3; s->token_val = TOK_LAND_ASSIGN; } else { p += 2; s->token_val = TOK_LAND; } - } else if (p[1] == '=') { p += 2; s->token_val = TOK_AND_ASSIGN; } + } + else if (p[1] == '=') { p += 2; s->token_val = TOK_AND_ASSIGN; } + else if (p[1] == '!') { + s->token_u.ident.str = (const char *)p; + s->token_u.ident.len = 2; + s->token_u.ident.has_escape = FALSE; + s->token_u.ident.is_reserved = FALSE; + s->token_val = TOK_IDENT; + p += 2; + } else { goto def_token; } break; case '|': if (p[1] == '|') { - if (p[2] == '=') { p += 3; s->token_val = TOK_LOR_ASSIGN; } + if (p[2] == '!') { + s->token_u.ident.str = (const char *)p; + s->token_u.ident.len = 3; + s->token_u.ident.has_escape = FALSE; + s->token_u.ident.is_reserved = FALSE; + s->token_val = TOK_IDENT; + p += 3; + } + else if (p[2] == '=') { p += 3; s->token_val = TOK_LOR_ASSIGN; } else { p += 2; s->token_val = TOK_LOR; } - } else if (p[1] == '=') { p += 2; s->token_val = TOK_OR_ASSIGN; } + } + else if (p[1] == '=') { p += 2; s->token_val = TOK_OR_ASSIGN; } + else if (p[1] == '!') { + s->token_u.ident.str = (const char *)p; + s->token_u.ident.len = 2; + s->token_u.ident.has_escape = FALSE; + s->token_u.ident.is_reserved = FALSE; + s->token_val = TOK_IDENT; + p += 2; + } else { goto def_token; } break; case '^': if (p[1] == '=') { p += 2; s->token_val = TOK_XOR_ASSIGN; } + else if (p[1] == '!') { + s->token_u.ident.str = (const char *)p; + s->token_u.ident.len = 2; + s->token_u.ident.has_escape = FALSE; + s->token_u.ident.is_reserved = FALSE; + s->token_val = TOK_IDENT; + p += 2; + } + else { goto def_token; } + break; + case '[': + if (p[1] == ']' && p[2] == '!') { + s->token_u.ident.str = (const char *)p; + s->token_u.ident.len = 3; + s->token_u.ident.has_escape = FALSE; + s->token_u.ident.is_reserved = FALSE; + s->token_val = TOK_IDENT; + p += 3; + } + else { goto def_token; } + break; + case '~': + if (p[1] == '!') { + s->token_u.ident.str = (const char *)p; + s->token_u.ident.len = 2; + s->token_u.ident.has_escape = FALSE; + s->token_u.ident.is_reserved = FALSE; + s->token_val = TOK_IDENT; + p += 2; + } else { goto def_token; } break; case '?': @@ -28905,6 +29112,13 @@ static int tokenize_next (ASTParseState *s) { } else if (p[1] == '=') { p += 2; s->token_val = TOK_DIV_ASSIGN; + } else if (p[1] == '!') { + s->token_u.ident.str = (const char *)p; + s->token_u.ident.len = 2; + s->token_u.ident.has_escape = FALSE; + s->token_u.ident.is_reserved = FALSE; + s->token_val = TOK_IDENT; + p += 2; } else { p++; s->token_val = c; @@ -29026,70 +29240,250 @@ static int tokenize_next (ASTParseState *s) { case '*': if (p[1] == '=') { p += 2; s->token_val = TOK_MUL_ASSIGN; } else if (p[1] == '*') { - if (p[2] == '=') { p += 3; s->token_val = TOK_POW_ASSIGN; } + if (p[2] == '!') { + s->token_u.ident.str = (const char *)p; + s->token_u.ident.len = 3; + s->token_u.ident.has_escape = FALSE; + s->token_u.ident.is_reserved = FALSE; + s->token_val = TOK_IDENT; + p += 3; + } + else if (p[2] == '=') { p += 3; s->token_val = TOK_POW_ASSIGN; } else { p += 2; s->token_val = TOK_POW; } - } else { goto def_token; } + } + else if (p[1] == '!') { + s->token_u.ident.str = (const char *)p; + s->token_u.ident.len = 2; + s->token_u.ident.has_escape = FALSE; + s->token_u.ident.is_reserved = FALSE; + s->token_val = TOK_IDENT; + p += 2; + } + else { goto def_token; } break; case '%': if (p[1] == '=') { p += 2; s->token_val = TOK_MOD_ASSIGN; } + else if (p[1] == '!') { + s->token_u.ident.str = (const char *)p; + s->token_u.ident.len = 2; + s->token_u.ident.has_escape = FALSE; + s->token_u.ident.is_reserved = FALSE; + s->token_val = TOK_IDENT; + p += 2; + } else { goto def_token; } break; case '+': if (p[1] == '=') { p += 2; s->token_val = TOK_PLUS_ASSIGN; } else if (p[1] == '+') { p += 2; s->token_val = TOK_INC; } + else if (p[1] == '!') { + s->token_u.ident.str = (const char *)p; + s->token_u.ident.len = 2; + s->token_u.ident.has_escape = FALSE; + s->token_u.ident.is_reserved = FALSE; + s->token_val = TOK_IDENT; + p += 2; + } else { goto def_token; } break; case '-': if (p[1] == '=') { p += 2; s->token_val = TOK_MINUS_ASSIGN; } else if (p[1] == '-') { p += 2; s->token_val = TOK_DEC; } + else if (p[1] == '!') { + s->token_u.ident.str = (const char *)p; + s->token_u.ident.len = 2; + s->token_u.ident.has_escape = FALSE; + s->token_u.ident.is_reserved = FALSE; + s->token_val = TOK_IDENT; + p += 2; + } else { goto def_token; } break; case '<': - if (p[1] == '=') { p += 2; s->token_val = TOK_LTE; } + if (p[1] == '=' && p[2] == '!') { + s->token_u.ident.str = (const char *)p; + s->token_u.ident.len = 3; + s->token_u.ident.has_escape = FALSE; + s->token_u.ident.is_reserved = FALSE; + s->token_val = TOK_IDENT; + p += 3; + } + else if (p[1] == '=') { p += 2; s->token_val = TOK_LTE; } else if (p[1] == '<') { - if (p[2] == '=') { p += 3; s->token_val = TOK_SHL_ASSIGN; } + if (p[2] == '!') { + s->token_u.ident.str = (const char *)p; + s->token_u.ident.len = 3; + s->token_u.ident.has_escape = FALSE; + s->token_u.ident.is_reserved = FALSE; + s->token_val = TOK_IDENT; + p += 3; + } + else if (p[2] == '=') { p += 3; s->token_val = TOK_SHL_ASSIGN; } else { p += 2; s->token_val = TOK_SHL; } - } else { goto def_token; } + } + else if (p[1] == '!') { + s->token_u.ident.str = (const char *)p; + s->token_u.ident.len = 2; + s->token_u.ident.has_escape = FALSE; + s->token_u.ident.is_reserved = FALSE; + s->token_val = TOK_IDENT; + p += 2; + } + else { goto def_token; } break; case '>': - if (p[1] == '=') { p += 2; s->token_val = TOK_GTE; } + if (p[1] == '=' && p[2] == '!') { + s->token_u.ident.str = (const char *)p; + s->token_u.ident.len = 3; + s->token_u.ident.has_escape = FALSE; + s->token_u.ident.is_reserved = FALSE; + s->token_val = TOK_IDENT; + p += 3; + } + else if (p[1] == '=') { p += 2; s->token_val = TOK_GTE; } else if (p[1] == '>') { if (p[2] == '>') { - if (p[3] == '=') { p += 4; s->token_val = TOK_SHR_ASSIGN; } + if (p[3] == '!') { + s->token_u.ident.str = (const char *)p; + s->token_u.ident.len = 4; + s->token_u.ident.has_escape = FALSE; + s->token_u.ident.is_reserved = FALSE; + s->token_val = TOK_IDENT; + p += 4; + } + else if (p[3] == '=') { p += 4; s->token_val = TOK_SHR_ASSIGN; } else { p += 3; s->token_val = TOK_SHR; } - } else if (p[2] == '=') { p += 3; s->token_val = TOK_SAR_ASSIGN; } + } + else if (p[2] == '!') { + s->token_u.ident.str = (const char *)p; + s->token_u.ident.len = 3; + s->token_u.ident.has_escape = FALSE; + s->token_u.ident.is_reserved = FALSE; + s->token_val = TOK_IDENT; + p += 3; + } + else if (p[2] == '=') { p += 3; s->token_val = TOK_SAR_ASSIGN; } else { p += 2; s->token_val = TOK_SAR; } - } else { goto def_token; } + } + else if (p[1] == '!') { + s->token_u.ident.str = (const char *)p; + s->token_u.ident.len = 2; + s->token_u.ident.has_escape = FALSE; + s->token_u.ident.is_reserved = FALSE; + s->token_val = TOK_IDENT; + p += 2; + } + else { goto def_token; } break; case '=': if (p[1] == '=') { if (p[2] == '=') { p += 3; s->token_val = TOK_STRICT_EQ; } else { p += 2; s->token_val = TOK_EQ; } } else if (p[1] == '>') { p += 2; s->token_val = TOK_ARROW; } + else if (p[1] == '!') { + s->token_u.ident.str = (const char *)p; + s->token_u.ident.len = 2; + s->token_u.ident.has_escape = FALSE; + s->token_u.ident.is_reserved = FALSE; + s->token_val = TOK_IDENT; + p += 2; + } else { goto def_token; } break; case '!': if (p[1] == '=') { - if (p[2] == '=') { p += 3; s->token_val = TOK_STRICT_NEQ; } + if (p[2] == '!') { + s->token_u.ident.str = (const char *)p; + s->token_u.ident.len = 3; + s->token_u.ident.has_escape = FALSE; + s->token_u.ident.is_reserved = FALSE; + s->token_val = TOK_IDENT; + p += 3; + } + else if (p[2] == '=') { p += 3; s->token_val = TOK_STRICT_NEQ; } else { p += 2; s->token_val = TOK_NEQ; } } else { goto def_token; } break; case '&': if (p[1] == '&') { - if (p[2] == '=') { p += 3; s->token_val = TOK_LAND_ASSIGN; } + if (p[2] == '!') { + s->token_u.ident.str = (const char *)p; + s->token_u.ident.len = 3; + s->token_u.ident.has_escape = FALSE; + s->token_u.ident.is_reserved = FALSE; + s->token_val = TOK_IDENT; + p += 3; + } + else if (p[2] == '=') { p += 3; s->token_val = TOK_LAND_ASSIGN; } else { p += 2; s->token_val = TOK_LAND; } - } else if (p[1] == '=') { p += 2; s->token_val = TOK_AND_ASSIGN; } + } + else if (p[1] == '=') { p += 2; s->token_val = TOK_AND_ASSIGN; } + else if (p[1] == '!') { + s->token_u.ident.str = (const char *)p; + s->token_u.ident.len = 2; + s->token_u.ident.has_escape = FALSE; + s->token_u.ident.is_reserved = FALSE; + s->token_val = TOK_IDENT; + p += 2; + } else { goto def_token; } break; case '|': if (p[1] == '|') { - if (p[2] == '=') { p += 3; s->token_val = TOK_LOR_ASSIGN; } + if (p[2] == '!') { + s->token_u.ident.str = (const char *)p; + s->token_u.ident.len = 3; + s->token_u.ident.has_escape = FALSE; + s->token_u.ident.is_reserved = FALSE; + s->token_val = TOK_IDENT; + p += 3; + } + else if (p[2] == '=') { p += 3; s->token_val = TOK_LOR_ASSIGN; } else { p += 2; s->token_val = TOK_LOR; } - } else if (p[1] == '=') { p += 2; s->token_val = TOK_OR_ASSIGN; } + } + else if (p[1] == '=') { p += 2; s->token_val = TOK_OR_ASSIGN; } + else if (p[1] == '!') { + s->token_u.ident.str = (const char *)p; + s->token_u.ident.len = 2; + s->token_u.ident.has_escape = FALSE; + s->token_u.ident.is_reserved = FALSE; + s->token_val = TOK_IDENT; + p += 2; + } else { goto def_token; } break; case '^': if (p[1] == '=') { p += 2; s->token_val = TOK_XOR_ASSIGN; } + else if (p[1] == '!') { + s->token_u.ident.str = (const char *)p; + s->token_u.ident.len = 2; + s->token_u.ident.has_escape = FALSE; + s->token_u.ident.is_reserved = FALSE; + s->token_val = TOK_IDENT; + p += 2; + } + else { goto def_token; } + break; + case '[': + if (p[1] == ']' && p[2] == '!') { + s->token_u.ident.str = (const char *)p; + s->token_u.ident.len = 3; + s->token_u.ident.has_escape = FALSE; + s->token_u.ident.is_reserved = FALSE; + s->token_val = TOK_IDENT; + p += 3; + } + else { goto def_token; } + break; + case '~': + if (p[1] == '!') { + s->token_u.ident.str = (const char *)p; + s->token_u.ident.len = 2; + s->token_u.ident.has_escape = FALSE; + s->token_u.ident.is_reserved = FALSE; + s->token_val = TOK_IDENT; + p += 2; + } else { goto def_token; } break; case '?': @@ -30491,6 +30885,18 @@ static int ast_sem_in_loop (ASTSemScope *scope) { return 0; } +static BOOL is_functino_name(const char *name) { + static const char *functinos[] = { + "+!", "-!", "*!", "/!", "%!", "**!", + "!", "<=!", ">=!", "=!", "!=!", + "&!", "|!", "^!", "<>!", ">>>!", + "&&!", "||!", "~!", "[]!", NULL + }; + for (int i = 0; functinos[i]; i++) + if (strcmp(name, functinos[i]) == 0) return TRUE; + return FALSE; +} + static void ast_sem_check_expr (ASTSemState *st, ASTSemScope *scope, cJSON *expr); static void ast_sem_check_stmt (ASTSemState *st, ASTSemScope *scope, cJSON *stmt); @@ -30743,6 +31149,11 @@ static void ast_sem_check_expr (ASTSemState *st, ASTSemScope *scope, cJSON *expr if (strcmp (kind, "name") == 0) { const char *name = cJSON_GetStringValue (cJSON_GetObjectItemCaseSensitive (expr, "name")); if (name) { + if (is_functino_name(name)) { + cJSON_AddStringToObject (expr, "make", "functino"); + cJSON_AddNumberToObject (expr, "level", -1); + return; + } ASTSemLookup r = ast_sem_lookup_var (scope, name); if (r.var) { cJSON_AddNumberToObject (expr, "level", r.level); @@ -31701,6 +32112,111 @@ static int mach_compile_expr(MachCompState *cs, cJSON *node, int dest) { if (fn_expr) fn_kind = cJSON_GetStringValue(cJSON_GetObjectItemCaseSensitive(fn_expr, "kind")); + /* Functino: inline operator call */ + if (fn_kind && strcmp(fn_kind, "name") == 0) { + const char *fn_make = cJSON_GetStringValue(cJSON_GetObjectItemCaseSensitive(fn_expr, "make")); + if (fn_make && strcmp(fn_make, "functino") == 0) { + const char *fname = cJSON_GetStringValue(cJSON_GetObjectItemCaseSensitive(fn_expr, "name")); + if (dest < 0) dest = mach_reserve_reg(cs); + + if (strcmp(fname, "~!") == 0) { + int save = cs->freereg; + int r = mach_compile_expr(cs, cJSON_GetArrayItem(args, 0), -1); + mach_emit(cs, MACH_ABC(MACH_BNOT, dest, r, 0)); + mach_free_reg_to(cs, save); + return dest; + } + if (strcmp(fname, "[]!") == 0) { + int save = cs->freereg; + int r0 = mach_compile_expr(cs, cJSON_GetArrayItem(args, 0), -1); + if (cs->freereg <= r0) cs->freereg = r0 + 1; + int r1 = mach_compile_expr(cs, cJSON_GetArrayItem(args, 1), -1); + mach_emit(cs, MACH_ABC(MACH_GETINDEX, dest, r0, r1)); + mach_free_reg_to(cs, save); + return dest; + } + if ((strcmp(fname, "=!") == 0 || strcmp(fname, "!=!") == 0) && nargs == 3) { + int save = cs->freereg; + int base = mach_reserve_reg(cs); + int r0 = mach_compile_expr(cs, cJSON_GetArrayItem(args, 0), base); + if (r0 != base) mach_emit(cs, MACH_ABC(MACH_MOVE, base, r0, 0)); + int r1_reg = mach_reserve_reg(cs); + int r1 = mach_compile_expr(cs, cJSON_GetArrayItem(args, 1), r1_reg); + if (r1 != r1_reg) mach_emit(cs, MACH_ABC(MACH_MOVE, r1_reg, r1, 0)); + int r2_reg = mach_reserve_reg(cs); + int r2 = mach_compile_expr(cs, cJSON_GetArrayItem(args, 2), r2_reg); + if (r2 != r2_reg) mach_emit(cs, MACH_ABC(MACH_MOVE, r2_reg, r2, 0)); + MachOpcode top = (strcmp(fname, "=!") == 0) ? MACH_EQ_TOL : MACH_NEQ_TOL; + mach_emit(cs, MACH_ABC(top, dest, base, 3)); + mach_free_reg_to(cs, save); + return dest; + } + if (strcmp(fname, "&&!") == 0) { + int save = cs->freereg; + int r0 = mach_compile_expr(cs, cJSON_GetArrayItem(args, 0), -1); + if (cs->freereg <= r0) cs->freereg = r0 + 1; + int r1 = mach_compile_expr(cs, cJSON_GetArrayItem(args, 1), -1); + /* Non-short-circuiting: if left is falsy, result=left, else result=right */ + mach_emit(cs, MACH_ABC(MACH_MOVE, dest, r0, 0)); + int jmp_pc = mach_current_pc(cs); + mach_emit(cs, MACH_AsBx(MACH_JMPFALSE, dest, 0)); + mach_emit(cs, MACH_ABC(MACH_MOVE, dest, r1, 0)); + { + int offset = mach_current_pc(cs) - (jmp_pc + 1); + cs->code[jmp_pc] = MACH_AsBx(MACH_GET_OP(cs->code[jmp_pc]), dest, (int16_t)offset); + } + mach_free_reg_to(cs, save); + return dest; + } + if (strcmp(fname, "||!") == 0) { + int save = cs->freereg; + int r0 = mach_compile_expr(cs, cJSON_GetArrayItem(args, 0), -1); + if (cs->freereg <= r0) cs->freereg = r0 + 1; + int r1 = mach_compile_expr(cs, cJSON_GetArrayItem(args, 1), -1); + /* Non-short-circuiting: if left is truthy, result=left, else result=right */ + mach_emit(cs, MACH_ABC(MACH_MOVE, dest, r0, 0)); + int jmp_pc = mach_current_pc(cs); + mach_emit(cs, MACH_AsBx(MACH_JMPTRUE, dest, 0)); + mach_emit(cs, MACH_ABC(MACH_MOVE, dest, r1, 0)); + { + int offset = mach_current_pc(cs) - (jmp_pc + 1); + cs->code[jmp_pc] = MACH_AsBx(MACH_GET_OP(cs->code[jmp_pc]), dest, (int16_t)offset); + } + mach_free_reg_to(cs, save); + return dest; + } + /* Standard 2-arg binary functino */ + { + int save = cs->freereg; + int r0 = mach_compile_expr(cs, cJSON_GetArrayItem(args, 0), -1); + if (cs->freereg <= r0) cs->freereg = r0 + 1; + int r1 = mach_compile_expr(cs, cJSON_GetArrayItem(args, 1), -1); + MachOpcode op; + if (strcmp(fname, "+!") == 0) op = MACH_ADD; + else if (strcmp(fname, "-!") == 0) op = MACH_SUB; + else if (strcmp(fname, "*!") == 0) op = MACH_MUL; + else if (strcmp(fname, "/!") == 0) op = MACH_DIV; + else if (strcmp(fname, "%!") == 0) op = MACH_MOD; + else if (strcmp(fname, "**!") == 0) op = MACH_POW; + else if (strcmp(fname, "!") == 0) op = MACH_GT; + else if (strcmp(fname, "<=!") == 0) op = MACH_LE; + else if (strcmp(fname, ">=!") == 0) op = MACH_GE; + else if (strcmp(fname, "=!") == 0) op = MACH_EQ; + else if (strcmp(fname, "!=!") == 0) op = MACH_NEQ; + else if (strcmp(fname, "&!") == 0) op = MACH_BAND; + else if (strcmp(fname, "|!") == 0) op = MACH_BOR; + else if (strcmp(fname, "^!") == 0) op = MACH_BXOR; + else if (strcmp(fname, "<>!") == 0) op = MACH_SHR; + else op = MACH_USHR; /* >>>! */ + mach_emit(cs, MACH_ABC(op, dest, r0, r1)); + mach_free_reg_to(cs, save); + return dest; + } + } + } + if (fn_kind && strcmp(fn_kind, ".") == 0) { /* Method call with dot notation: obj.method(args) */ int save_freereg = cs->freereg; @@ -33479,6 +33995,33 @@ static JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code, break; } + case MACH_EQ_TOL: + case MACH_NEQ_TOL: { + /* A=dest, B=base, C=3; args in R(B), R(B+1), R(B+2) */ + JSValue left = frame->slots[b]; + JSValue right = frame->slots[b + 1]; + JSValue tol = frame->slots[b + 2]; + BOOL is_eq_op = (op == MACH_EQ_TOL); + if (JS_IsNumber(left) && JS_IsNumber(right) && JS_IsNumber(tol)) { + double da, db, dt; + JS_ToFloat64(ctx, &da, left); + JS_ToFloat64(ctx, &db, right); + JS_ToFloat64(ctx, &dt, tol); + BOOL eq = fabs(da - db) <= dt; + frame->slots[a] = JS_NewBool(ctx, is_eq_op ? eq : !eq); + } else if (JS_IsText(left) && JS_IsText(right) && JS_VALUE_GET_TAG(tol) == JS_TAG_BOOL && JS_VALUE_GET_BOOL(tol)) { + BOOL eq = js_string_compare_value_nocase(ctx, left, right) == 0; + frame->slots[a] = JS_NewBool(ctx, is_eq_op ? eq : !eq); + } else { + /* Fall through to standard eq/neq */ + JSValue res = reg_vm_binop(ctx, is_eq_op ? MACH_EQ : MACH_NEQ, left, right); + frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); + if (JS_IsException(res)) { goto disrupt; } + frame->slots[a] = res; + } + break; + } + case MACH_NEG: { JSValue v = frame->slots[b]; if (JS_IsInt(v)) { @@ -34288,6 +34831,16 @@ static void mach_gen_emit_3 (MachGenState *s, const char *op, int a, int b, int mach_gen_add_instr (s, instr); } +static void mach_gen_emit_4 (MachGenState *s, const char *op, int a, int b, int c, int d) { + cJSON *instr = cJSON_CreateArray (); + cJSON_AddItemToArray (instr, cJSON_CreateString (op)); + cJSON_AddItemToArray (instr, cJSON_CreateNumber (a)); + cJSON_AddItemToArray (instr, cJSON_CreateNumber (b)); + cJSON_AddItemToArray (instr, cJSON_CreateNumber (c)); + cJSON_AddItemToArray (instr, cJSON_CreateNumber (d)); + mach_gen_add_instr (s, instr); +} + static void mach_gen_emit_const_num (MachGenState *s, int dest, double val) { cJSON *instr = cJSON_CreateArray (); cJSON_AddItemToArray (instr, cJSON_CreateString ("access")); @@ -34428,6 +34981,32 @@ static void mach_gen_emit_go_call_method (MachGenState *s, int obj, const char * mach_gen_emit_1 (s, "goinvoke", frame_slot); } +static const char *functino_to_mcode_op (const char *name) { + if (strcmp (name, "+!") == 0) return "add"; + if (strcmp (name, "-!") == 0) return "subtract"; + if (strcmp (name, "*!") == 0) return "multiply"; + if (strcmp (name, "/!") == 0) return "divide"; + if (strcmp (name, "%!") == 0) return "modulo"; + if (strcmp (name, "**!") == 0) return "pow"; + if (strcmp (name, "!") == 0) return "gt"; + if (strcmp (name, "<=!") == 0) return "le"; + if (strcmp (name, ">=!") == 0) return "ge"; + if (strcmp (name, "=!") == 0) return "eq"; + if (strcmp (name, "!=!") == 0) return "ne"; + if (strcmp (name, "&!") == 0) return "bitand"; + if (strcmp (name, "|!") == 0) return "bitor"; + if (strcmp (name, "^!") == 0) return "bitxor"; + if (strcmp (name, "<>!") == 0) return "shr"; + if (strcmp (name, ">>>!") == 0) return "ushr"; + if (strcmp (name, "&&!") == 0) return "and"; + if (strcmp (name, "||!") == 0) return "or"; + if (strcmp (name, "~!") == 0) return "bitnot"; + if (strcmp (name, "[]!") == 0) return "load"; + return NULL; +} + static const char *mach_gen_binop_to_string (const char *kind) { if (strcmp (kind, "+") == 0) return "add"; if (strcmp (kind, "-") == 0) return "subtract"; @@ -34828,13 +35407,69 @@ static int mach_gen_expr (MachGenState *s, cJSON *expr, int target) { if (strcmp (kind, "(") == 0) { cJSON *callee = cJSON_GetObjectItemCaseSensitive (expr, "expression"); cJSON *args_list = cJSON_GetObjectItemCaseSensitive (expr, "list"); + + /* Functino: inline operator call */ + const char *callee_kind = cJSON_GetStringValue (cJSON_GetObjectItemCaseSensitive (callee, "kind")); + if (callee_kind && strcmp (callee_kind, "name") == 0) { + const char *callee_make = cJSON_GetStringValue (cJSON_GetObjectItemCaseSensitive (callee, "make")); + if (callee_make && strcmp (callee_make, "functino") == 0) { + const char *fname = cJSON_GetStringValue (cJSON_GetObjectItemCaseSensitive (callee, "name")); + const char *mop = functino_to_mcode_op (fname); + int nargs = args_list ? cJSON_GetArraySize (args_list) : 0; + + if (strcmp (fname, "~!") == 0) { + int a0 = mach_gen_expr (s, cJSON_GetArrayItem (args_list, 0), -1); + int d = mach_gen_alloc_slot (s); + mach_gen_emit_2 (s, mop, d, a0); + return d; + } + if (strcmp (fname, "[]!") == 0) { + int a0 = mach_gen_expr (s, cJSON_GetArrayItem (args_list, 0), -1); + int a1 = mach_gen_expr (s, cJSON_GetArrayItem (args_list, 1), -1); + int d = mach_gen_alloc_slot (s); + mach_gen_emit_get_elem (s, d, a0, a1); + return d; + } + if ((strcmp (fname, "=!") == 0 || strcmp (fname, "!=!") == 0) && nargs == 3) { + int a0 = mach_gen_expr (s, cJSON_GetArrayItem (args_list, 0), -1); + int a1 = mach_gen_expr (s, cJSON_GetArrayItem (args_list, 1), -1); + int a2 = mach_gen_expr (s, cJSON_GetArrayItem (args_list, 2), -1); + int d = mach_gen_alloc_slot (s); + const char *top = (strcmp (fname, "=!") == 0) ? "eq_tol" : "ne_tol"; + mach_gen_emit_4 (s, top, d, a0, a1, a2); + return d; + } + if (strcmp (fname, "&&!") == 0) { + int a0 = mach_gen_expr (s, cJSON_GetArrayItem (args_list, 0), -1); + int a1 = mach_gen_expr (s, cJSON_GetArrayItem (args_list, 1), -1); + int d = mach_gen_alloc_slot (s); + mach_gen_emit_3 (s, "and", d, a0, a1); + return d; + } + if (strcmp (fname, "||!") == 0) { + int a0 = mach_gen_expr (s, cJSON_GetArrayItem (args_list, 0), -1); + int a1 = mach_gen_expr (s, cJSON_GetArrayItem (args_list, 1), -1); + int d = mach_gen_alloc_slot (s); + mach_gen_emit_3 (s, "or", d, a0, a1); + return d; + } + /* Standard 2-arg binary functino */ + { + int a0 = mach_gen_expr (s, cJSON_GetArrayItem (args_list, 0), -1); + int a1 = mach_gen_expr (s, cJSON_GetArrayItem (args_list, 1), -1); + int d = mach_gen_alloc_slot (s); + mach_gen_emit_3 (s, mop, d, a0, a1); + return d; + } + } + } + cJSON *arg_slots = cJSON_CreateArray (); cJSON *arg; cJSON_ArrayForEach (arg, args_list) { int arg_slot = mach_gen_expr (s, arg, -1); cJSON_AddItemToArray (arg_slots, cJSON_CreateNumber (arg_slot)); } - const char *callee_kind = cJSON_GetStringValue (cJSON_GetObjectItemCaseSensitive (callee, "kind")); int dest = mach_gen_alloc_slot (s); if (strcmp (callee_kind, ".") == 0) { cJSON *obj = cJSON_GetObjectItemCaseSensitive (callee, "left"); @@ -36317,6 +36952,61 @@ static JSValue mcode_exec(JSContext *ctx, JSMCode *code, JSValue this_obj, frame->slots[dest] = JS_TRUE; } } + else if (strcmp(op, "eq_tol") == 0) { + int dest = (int)a1->valuedouble; + JSValue left = frame->slots[(int)a2->valuedouble]; + JSValue right = frame->slots[(int)a3->valuedouble]; + cJSON *a4 = cJSON_GetArrayItem(instr, 4); + JSValue tol = frame->slots[(int)a4->valuedouble]; + if (JS_IsNumber(left) && JS_IsNumber(right) && JS_IsNumber(tol)) { + double a, b, t; + JS_ToFloat64(ctx, &a, left); + JS_ToFloat64(ctx, &b, right); + JS_ToFloat64(ctx, &t, tol); + frame->slots[dest] = JS_NewBool(ctx, fabs(a - b) <= t); + } else if (JS_IsText(left) && JS_IsText(right) && JS_VALUE_GET_TAG(tol) == JS_TAG_BOOL && JS_VALUE_GET_BOOL(tol)) { + frame->slots[dest] = JS_NewBool(ctx, js_string_compare_value_nocase(ctx, left, right) == 0); + } else { + /* Fall through to standard eq */ + if (left == right) frame->slots[dest] = JS_TRUE; + else if (JS_IsText(left) && JS_IsText(right)) + frame->slots[dest] = JS_NewBool(ctx, js_string_compare_value(ctx, left, right, TRUE) == 0); + else frame->slots[dest] = JS_FALSE; + } + } + else if (strcmp(op, "ne_tol") == 0) { + int dest = (int)a1->valuedouble; + JSValue left = frame->slots[(int)a2->valuedouble]; + JSValue right = frame->slots[(int)a3->valuedouble]; + cJSON *a4 = cJSON_GetArrayItem(instr, 4); + JSValue tol = frame->slots[(int)a4->valuedouble]; + if (JS_IsNumber(left) && JS_IsNumber(right) && JS_IsNumber(tol)) { + double a, b, t; + JS_ToFloat64(ctx, &a, left); + JS_ToFloat64(ctx, &b, right); + JS_ToFloat64(ctx, &t, tol); + frame->slots[dest] = JS_NewBool(ctx, fabs(a - b) > t); + } else if (JS_IsText(left) && JS_IsText(right) && JS_VALUE_GET_TAG(tol) == JS_TAG_BOOL && JS_VALUE_GET_BOOL(tol)) { + frame->slots[dest] = JS_NewBool(ctx, js_string_compare_value_nocase(ctx, left, right) != 0); + } else { + if (left == right) frame->slots[dest] = JS_FALSE; + else if (JS_IsText(left) && JS_IsText(right)) + frame->slots[dest] = JS_NewBool(ctx, js_string_compare_value(ctx, left, right, TRUE) != 0); + else frame->slots[dest] = JS_TRUE; + } + } + else if (strcmp(op, "and") == 0) { + int dest = (int)a1->valuedouble; + JSValue left = frame->slots[(int)a2->valuedouble]; + JSValue right = frame->slots[(int)a3->valuedouble]; + frame->slots[dest] = JS_ToBool(ctx, left) ? right : left; + } + else if (strcmp(op, "or") == 0) { + int dest = (int)a1->valuedouble; + JSValue left = frame->slots[(int)a2->valuedouble]; + JSValue right = frame->slots[(int)a3->valuedouble]; + frame->slots[dest] = JS_ToBool(ctx, left) ? left : right; + } else if (strcmp(op, "lt") == 0) { int dest = (int)a1->valuedouble; JSValue left = frame->slots[(int)a2->valuedouble]; @@ -37561,6 +38251,10 @@ static void dump_register_code(JSContext *ctx, JSCodeRegister *code, int indent) printf("r%d, r%d, r%d", a, b, c); break; + case MACH_EQ_TOL: case MACH_NEQ_TOL: + printf("r%d, r%d, %d", a, b, c); + break; + /* Property access */ case MACH_GETFIELD: printf("r%d, r%d, #%d", a, b, c);