rm ?? and .?
This commit is contained in:
126
source/quickjs.c
126
source/quickjs.c
@@ -8616,7 +8616,6 @@ enum {
|
||||
TOK_POW_ASSIGN,
|
||||
TOK_LAND_ASSIGN,
|
||||
TOK_LOR_ASSIGN,
|
||||
TOK_DOUBLE_QUESTION_MARK_ASSIGN,
|
||||
TOK_DEC,
|
||||
TOK_INC,
|
||||
TOK_SHL,
|
||||
@@ -8634,8 +8633,6 @@ enum {
|
||||
TOK_LOR,
|
||||
TOK_POW,
|
||||
TOK_ARROW,
|
||||
TOK_DOUBLE_QUESTION_MARK,
|
||||
TOK_QUESTION_MARK_DOT,
|
||||
TOK_ERROR,
|
||||
TOK_PRIVATE_NAME,
|
||||
TOK_EOF,
|
||||
@@ -8727,7 +8724,6 @@ static const char *ast_token_kind_str(int token_val) {
|
||||
case TOK_POW_ASSIGN: return "**=";
|
||||
case TOK_LAND_ASSIGN: return "&&=";
|
||||
case TOK_LOR_ASSIGN: return "||=";
|
||||
case TOK_DOUBLE_QUESTION_MARK_ASSIGN: return "?\?=";
|
||||
case TOK_DEC: return "--";
|
||||
case TOK_INC: return "++";
|
||||
case TOK_SHL: return "<<";
|
||||
@@ -8745,8 +8741,6 @@ static const char *ast_token_kind_str(int token_val) {
|
||||
case TOK_LOR: return "||";
|
||||
case TOK_POW: return "**";
|
||||
case TOK_ARROW: return "=>";
|
||||
case TOK_DOUBLE_QUESTION_MARK: return "??";
|
||||
case TOK_QUESTION_MARK_DOT: return "?.";
|
||||
/* keywords */
|
||||
case TOK_NULL: return "null";
|
||||
case TOK_FALSE: return "false";
|
||||
@@ -10060,7 +10054,7 @@ static JSValue parse_ident (JSParseState *s, const uint8_t **pp, BOOL *pident_ha
|
||||
} else if (c >= 128) {
|
||||
c = unicode_from_utf8 (p, UTF8_CHAR_LEN_MAX, &p1);
|
||||
}
|
||||
if (!lre_js_is_ident_next (c)) break;
|
||||
if (!lre_js_is_ident_next (c) && c != '?' && c != '!') break;
|
||||
p = p1;
|
||||
if (unlikely (ident_pos >= ident_size - UTF8_CHAR_LEN_MAX)) {
|
||||
if (ident_realloc (s->ctx, &buf, &ident_size, ident_buf)) {
|
||||
@@ -10451,21 +10445,7 @@ redo:
|
||||
}
|
||||
break;
|
||||
case '?':
|
||||
if (p[1] == '?') {
|
||||
if (p[2] == '=') {
|
||||
p += 3;
|
||||
s->token.val = TOK_DOUBLE_QUESTION_MARK_ASSIGN;
|
||||
} else {
|
||||
p += 2;
|
||||
s->token.val = TOK_DOUBLE_QUESTION_MARK;
|
||||
}
|
||||
} else if (p[1] == '.' && !(p[2] >= '0' && p[2] <= '9')) {
|
||||
p += 2;
|
||||
s->token.val = TOK_QUESTION_MARK_DOT;
|
||||
} else {
|
||||
goto def_token;
|
||||
}
|
||||
break;
|
||||
goto def_token;
|
||||
default:
|
||||
if (c >= 128) {
|
||||
/* unicode value */
|
||||
@@ -12558,22 +12538,7 @@ static __exception int js_parse_postfix_expr (JSParseState *s,
|
||||
JSFunctionDef *fd = s->cur_func;
|
||||
BOOL has_optional_chain = FALSE;
|
||||
|
||||
if (s->token.val == TOK_QUESTION_MARK_DOT) {
|
||||
if ((parse_flags & PF_POSTFIX_CALL) == 0)
|
||||
return js_parse_error (
|
||||
s, "new keyword cannot be used with an optional chain");
|
||||
op_token_ptr = s->token.ptr;
|
||||
/* optional chaining */
|
||||
if (next_token (s)) return -1;
|
||||
has_optional_chain = TRUE;
|
||||
if (s->token.val == '(' && accept_lparen) {
|
||||
goto parse_func_call;
|
||||
} else if (s->token.val == '[') {
|
||||
goto parse_array_access;
|
||||
} else {
|
||||
goto parse_property;
|
||||
}
|
||||
} else if (s->token.val == '(' && accept_lparen) {
|
||||
if (s->token.val == '(' && accept_lparen) {
|
||||
int opcode, arg_count, drop_count;
|
||||
|
||||
/* function call */
|
||||
@@ -13040,8 +13005,6 @@ static __exception int js_parse_logical_and_or (JSParseState *s, int op, int par
|
||||
if (js_parse_logical_and_or (s, TOK_LAND, parse_flags)) return -1;
|
||||
}
|
||||
if (s->token.val != op) {
|
||||
if (s->token.val == TOK_DOUBLE_QUESTION_MARK)
|
||||
return js_parse_error (s, "cannot mix ?? with && or ||");
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -13053,25 +13016,7 @@ static __exception int js_parse_logical_and_or (JSParseState *s, int op, int par
|
||||
|
||||
static __exception int js_parse_coalesce_expr (JSParseState *s,
|
||||
int parse_flags) {
|
||||
int label1;
|
||||
|
||||
if (js_parse_logical_and_or (s, TOK_LOR, parse_flags)) return -1;
|
||||
if (s->token.val == TOK_DOUBLE_QUESTION_MARK) {
|
||||
label1 = new_label (s);
|
||||
for (;;) {
|
||||
if (next_token (s)) return -1;
|
||||
|
||||
emit_op (s, OP_dup);
|
||||
emit_op (s, OP_is_null);
|
||||
emit_goto (s, OP_if_false, label1);
|
||||
emit_op (s, OP_drop);
|
||||
|
||||
if (js_parse_expr_binary (s, 8, parse_flags)) return -1;
|
||||
if (s->token.val != TOK_DOUBLE_QUESTION_MARK) break;
|
||||
}
|
||||
emit_label (s, label1);
|
||||
}
|
||||
return 0;
|
||||
return js_parse_logical_and_or (s, TOK_LOR, parse_flags);
|
||||
}
|
||||
|
||||
/* allowed parse_flags: PF_IN_ACCEPTED */
|
||||
@@ -13159,7 +13104,7 @@ static __exception int js_parse_assign_expr2 (JSParseState *s,
|
||||
emit_op (s, op);
|
||||
}
|
||||
put_lvalue (s, opcode, scope, name, label, PUT_LVALUE_KEEP_TOP, FALSE);
|
||||
} else if (op >= TOK_LAND_ASSIGN && op <= TOK_DOUBLE_QUESTION_MARK_ASSIGN) {
|
||||
} else if (op >= TOK_LAND_ASSIGN && op <= TOK_LOR_ASSIGN) {
|
||||
int label, label1, depth_lvalue, label2;
|
||||
|
||||
if (next_token (s)) return -1;
|
||||
@@ -13168,7 +13113,6 @@ static __exception int js_parse_assign_expr2 (JSParseState *s,
|
||||
return -1;
|
||||
|
||||
emit_op (s, OP_dup);
|
||||
if (op == TOK_DOUBLE_QUESTION_MARK_ASSIGN) emit_op (s, OP_is_null);
|
||||
label1
|
||||
= emit_goto (s, op == TOK_LOR_ASSIGN ? OP_if_true : OP_if_false, -1);
|
||||
emit_op (s, OP_drop);
|
||||
@@ -28660,7 +28604,8 @@ redo:
|
||||
while (p < s->buf_end) {
|
||||
c = *p;
|
||||
if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
|
||||
(c >= '0' && c <= '9') || c == '_' || c == '$') {
|
||||
(c >= '0' && c <= '9') || c == '_' || c == '$' ||
|
||||
c == '?' || c == '!') {
|
||||
p++;
|
||||
} else if (c >= 0x80) {
|
||||
/* unicode identifier */
|
||||
@@ -28827,12 +28772,7 @@ redo:
|
||||
else { goto def_token; }
|
||||
break;
|
||||
case '?':
|
||||
if (p[1] == '?') {
|
||||
if (p[2] == '=') { p += 3; s->token_val = TOK_DOUBLE_QUESTION_MARK_ASSIGN; }
|
||||
else { p += 2; s->token_val = TOK_DOUBLE_QUESTION_MARK; }
|
||||
} else if (p[1] == '.') { p += 2; s->token_val = TOK_QUESTION_MARK_DOT; }
|
||||
else { goto def_token; }
|
||||
break;
|
||||
goto def_token;
|
||||
default:
|
||||
def_token:
|
||||
p++;
|
||||
@@ -28987,7 +28927,8 @@ static int tokenize_next (ASTParseState *s) {
|
||||
while (p < s->buf_end) {
|
||||
c = *p;
|
||||
if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
|
||||
(c >= '0' && c <= '9') || c == '_' || c == '$') {
|
||||
(c >= '0' && c <= '9') || c == '_' || c == '$' ||
|
||||
c == '?' || c == '!') {
|
||||
p++;
|
||||
} else if (c >= 0x80) {
|
||||
p++;
|
||||
@@ -29152,12 +29093,7 @@ static int tokenize_next (ASTParseState *s) {
|
||||
else { goto def_token; }
|
||||
break;
|
||||
case '?':
|
||||
if (p[1] == '?') {
|
||||
if (p[2] == '=') { p += 3; s->token_val = TOK_DOUBLE_QUESTION_MARK_ASSIGN; }
|
||||
else { p += 2; s->token_val = TOK_DOUBLE_QUESTION_MARK; }
|
||||
} else if (p[1] == '.') { p += 2; s->token_val = TOK_QUESTION_MARK_DOT; }
|
||||
else { goto def_token; }
|
||||
break;
|
||||
goto def_token;
|
||||
default:
|
||||
def_token:
|
||||
p++;
|
||||
@@ -29590,44 +29526,6 @@ static cJSON *ast_parse_postfix (ASTParseState *s) {
|
||||
ast_next_token (s);
|
||||
ast_node_end (s, new_node, s->buf_ptr);
|
||||
node = new_node;
|
||||
} else if (s->token_val == TOK_QUESTION_MARK_DOT) {
|
||||
ast_next_token (s);
|
||||
if (s->token_val == '[') {
|
||||
/* Optional bracket access: o?.["a"] */
|
||||
ast_next_token (s);
|
||||
cJSON *new_node = ast_node (s, "?.[", start);
|
||||
cJSON_AddItemToObject (new_node, "left", node);
|
||||
cJSON *index = ast_parse_assign_expr (s);
|
||||
cJSON_AddItemToObject (new_node, "right", index);
|
||||
if (s->token_val == ']') ast_next_token (s);
|
||||
else ast_error (s, s->token_ptr, "expected ']'");
|
||||
ast_node_end (s, new_node, s->buf_ptr);
|
||||
node = new_node;
|
||||
} else if (s->token_val == '(') {
|
||||
/* Optional call: o.f?.() */
|
||||
ast_next_token (s);
|
||||
cJSON *new_node = ast_node (s, "?.(", start);
|
||||
cJSON_AddItemToObject (new_node, "expression", node);
|
||||
cJSON *list = cJSON_AddArrayToObject (new_node, "list");
|
||||
while (s->token_val != ')' && s->token_val != TOK_EOF) {
|
||||
cJSON *arg = ast_parse_assign_expr (s);
|
||||
if (arg) cJSON_AddItemToArray (list, arg);
|
||||
if (s->token_val == ',') ast_next_token (s);
|
||||
else break;
|
||||
}
|
||||
if (s->token_val == ')') ast_next_token (s);
|
||||
else ast_error (s, s->token_ptr, "unterminated argument list, expected ')'");
|
||||
ast_node_end (s, new_node, s->buf_ptr);
|
||||
node = new_node;
|
||||
} else if (s->token_val == TOK_IDENT) {
|
||||
/* Optional property access: o?.a */
|
||||
cJSON *new_node = ast_node (s, "?.", start);
|
||||
cJSON_AddItemToObject (new_node, "left", node);
|
||||
cjson_add_strn (new_node, "right", s->token_u.ident.str, s->token_u.ident.len);
|
||||
ast_next_token (s);
|
||||
ast_node_end (s, new_node, s->buf_ptr);
|
||||
node = new_node;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
@@ -29733,7 +29631,6 @@ static const ASTBinOp ast_binops[] = {
|
||||
{ '|', "|", 6 },
|
||||
{ TOK_LAND, "&&", 5 },
|
||||
{ TOK_LOR, "||", 4 },
|
||||
{ TOK_DOUBLE_QUESTION_MARK, "??", 3 },
|
||||
{ 0, NULL, 0 }
|
||||
};
|
||||
|
||||
@@ -29813,7 +29710,6 @@ static cJSON *ast_parse_assign (ASTParseState *s) {
|
||||
case TOK_POW_ASSIGN: kind = "**="; break;
|
||||
case TOK_LAND_ASSIGN: kind = "&&="; break;
|
||||
case TOK_LOR_ASSIGN: kind = "||="; break;
|
||||
case TOK_DOUBLE_QUESTION_MARK_ASSIGN: kind = "??="; break;
|
||||
default:
|
||||
return left;
|
||||
}
|
||||
|
||||
@@ -604,6 +604,48 @@ run("self-referencing object", function() {
|
||||
assert_eq(o.self.self.name, "root", "cycle access")
|
||||
})
|
||||
|
||||
// === IDENTIFIER ? AND ! ===
|
||||
|
||||
run("question mark in identifier", function() {
|
||||
var nil? = (x) => x == null
|
||||
assert_eq(nil?(null), true, "nil? null")
|
||||
assert_eq(nil?(42), false, "nil? 42")
|
||||
})
|
||||
|
||||
run("bang in identifier", function() {
|
||||
var set! = (x) => x + 1
|
||||
assert_eq(set!(5), 6, "set! call")
|
||||
})
|
||||
|
||||
run("question mark mid identifier", function() {
|
||||
var is?valid = (x) => x > 0
|
||||
assert_eq(is?valid(3), true, "is?valid true")
|
||||
assert_eq(is?valid(-1), false, "is?valid false")
|
||||
})
|
||||
|
||||
run("bang mid identifier", function() {
|
||||
var do!stuff = () => 42
|
||||
assert_eq(do!stuff(), 42, "do!stuff call")
|
||||
})
|
||||
|
||||
run("ternary after question ident", function() {
|
||||
var nil? = (x) => x == null
|
||||
var a = nil?(null) ? "yes" : "no"
|
||||
assert_eq(a, "yes", "ternary true branch")
|
||||
var b = nil?(42) ? "yes" : "no"
|
||||
assert_eq(b, "no", "ternary false branch")
|
||||
})
|
||||
|
||||
run("bang not confused with logical not", function() {
|
||||
assert_eq(!true, false, "logical not true")
|
||||
assert_eq(!false, true, "logical not false")
|
||||
})
|
||||
|
||||
run("inequality not confused with bang ident", function() {
|
||||
assert_eq(1 != 2, true, "inequality true")
|
||||
assert_eq(1 != 1, false, "inequality false")
|
||||
})
|
||||
|
||||
// === SUMMARY ===
|
||||
|
||||
print(text(passed) + " passed, " + text(failed) + " failed out of " + text(passed + failed))
|
||||
|
||||
Reference in New Issue
Block a user