details: https://hg.nginx.org/njs/rev/9c1ef5ab7a1f branches: changeset: 1284:9c1ef5ab7a1f user: Valentin Bartenev <vb...@nginx.com> date: Thu Dec 12 18:50:27 2019 +0300 description: Introduced nullish coalescing operator.
diffstat: src/njs_disassembler.c | 12 ++++++ src/njs_generator.c | 1 + src/njs_lexer.c | 11 ++++++ src/njs_lexer.h | 2 + src/njs_parser_expression.c | 81 +++++++++++++++++++++++++++++++++++++++++++- src/njs_vmcode.c | 9 +++- src/njs_vmcode.h | 26 +++++++------ src/test/njs_unit_test.c | 17 +++++++++ 8 files changed, 142 insertions(+), 17 deletions(-) diffs (262 lines): diff -r 5bf71dfc052f -r 9c1ef5ab7a1f src/njs_disassembler.c --- a/src/njs_disassembler.c Wed Dec 11 15:58:05 2019 +0300 +++ b/src/njs_disassembler.c Thu Dec 12 18:50:27 2019 +0300 @@ -282,6 +282,18 @@ njs_disassemble(u_char *start, u_char *e continue; } + if (operation == NJS_VMCODE_COALESCE) { + test_jump = (njs_vmcode_test_jump_t *) p; + + njs_printf("%05uz COALESCE %04Xz %04Xz +%uz\n", + p - start, (size_t) test_jump->retval, + (size_t) test_jump->value, (size_t) test_jump->offset); + + p += sizeof(njs_vmcode_test_jump_t); + + continue; + } + if (operation == NJS_VMCODE_FUNCTION_FRAME) { function = (njs_vmcode_function_frame_t *) p; diff -r 5bf71dfc052f -r 9c1ef5ab7a1f src/njs_generator.c --- a/src/njs_generator.c Wed Dec 11 15:58:05 2019 +0300 +++ b/src/njs_generator.c Thu Dec 12 18:50:27 2019 +0300 @@ -361,6 +361,7 @@ njs_generate(njs_vm_t *vm, njs_generator case NJS_TOKEN_LOGICAL_AND: case NJS_TOKEN_LOGICAL_OR: + case NJS_TOKEN_COALESCE: return njs_generate_test_jump_expression(vm, generator, node); case NJS_TOKEN_DELETE: diff -r 5bf71dfc052f -r 9c1ef5ab7a1f src/njs_lexer.c --- a/src/njs_lexer.c Wed Dec 11 15:58:05 2019 +0300 +++ b/src/njs_lexer.c Thu Dec 12 18:50:27 2019 +0300 @@ -276,6 +276,11 @@ static const njs_lexer_multi_t njs_grea }; +static const njs_lexer_multi_t njs_conditional_token[] = { + { '?', NJS_TOKEN_COALESCE, 0, NULL }, +}; + + static const njs_lexer_multi_t njs_assignment_token[] = { { '=', NJS_TOKEN_EQUAL, 1, njs_strict_equal_token }, { '>', NJS_TOKEN_ARROW, 0, NULL }, @@ -551,6 +556,12 @@ njs_lexer_next_token(njs_lexer_t *lexer, goto multi; + case NJS_TOKEN_CONDITIONAL: + n = njs_nitems(njs_conditional_token), + multi = njs_conditional_token; + + goto multi; + case NJS_TOKEN_LINE_END: lexer->line++; diff -r 5bf71dfc052f -r 9c1ef5ab7a1f src/njs_lexer.h --- a/src/njs_lexer.h Wed Dec 11 15:58:05 2019 +0300 +++ b/src/njs_lexer.h Thu Dec 12 18:50:27 2019 +0300 @@ -95,6 +95,8 @@ typedef enum { NJS_TOKEN_BITWISE_NOT, NJS_TOKEN_LOGICAL_NOT, + NJS_TOKEN_COALESCE, + NJS_TOKEN_IN, NJS_TOKEN_INSTANCEOF, NJS_TOKEN_TYPEOF, diff -r 5bf71dfc052f -r 9c1ef5ab7a1f src/njs_parser_expression.c --- a/src/njs_parser_expression.c Wed Dec 11 15:58:05 2019 +0300 +++ b/src/njs_parser_expression.c Thu Dec 12 18:50:27 2019 +0300 @@ -41,6 +41,8 @@ static njs_token_t njs_parser_any_expres njs_token_t token); static njs_token_t njs_parser_conditional_expression(njs_vm_t *vm, njs_parser_t *parser, njs_token_t token); +static njs_token_t njs_parser_coalesce_expression(njs_vm_t *vm, + njs_parser_t *parser, njs_token_t token); static njs_token_t njs_parser_binary_expression(njs_vm_t *vm, njs_parser_t *parser, const njs_parser_expression_t *expr, njs_token_t token); @@ -359,9 +361,7 @@ njs_parser_conditional_expression(njs_vm { njs_parser_node_t *node, *cond; - token = njs_parser_binary_expression(vm, parser, - &njs_parser_logical_or_expression, - token); + token = njs_parser_coalesce_expression(vm, parser, token); if (njs_slow_path(token <= NJS_TOKEN_ILLEGAL)) { return token; } @@ -421,6 +421,81 @@ njs_parser_conditional_expression(njs_vm static njs_token_t +njs_parser_coalesce_expression(njs_vm_t *vm, njs_parser_t *parser, + njs_token_t token) +{ + njs_token_t prev_token, next_token; + njs_parser_node_t *node; + + token = njs_parser_binary_expression(vm, parser, + &njs_parser_logical_or_expression, + token); + if (njs_slow_path(token <= NJS_TOKEN_ILLEGAL)) { + return token; + } + + for ( ;; ) { + if (token != NJS_TOKEN_COALESCE) { + return token; + } + + prev_token = parser->lexer->prev_token; + + node = njs_parser_node_new(vm, parser, NJS_TOKEN_COALESCE); + if (njs_slow_path(node == NULL)) { + return NJS_TOKEN_ERROR; + } + + node->u.operation = NJS_VMCODE_COALESCE; + node->left = parser->node; + node->left->dest = node; + + token = njs_parser_token(vm, parser); + if (njs_slow_path(token <= NJS_TOKEN_ILLEGAL)) { + return token; + } + + next_token = token; + + token = njs_parser_binary_expression(vm, parser, + &njs_parser_logical_or_expression, + token); + if (njs_slow_path(token <= NJS_TOKEN_ILLEGAL)) { + return token; + } + + node->right = parser->node; + node->right->dest = node; + parser->node = node; + + if (prev_token != NJS_TOKEN_CLOSE_PARENTHESIS + && njs_slow_path(node->left->token == NJS_TOKEN_LOGICAL_OR + || node->left->token == NJS_TOKEN_LOGICAL_AND)) + { + token = node->left->token; + goto require_parentheses; + } + + if (next_token != NJS_TOKEN_OPEN_PARENTHESIS + && njs_slow_path(node->right->token == NJS_TOKEN_LOGICAL_OR + || node->right->token == NJS_TOKEN_LOGICAL_AND)) + { + token = node->right->token; + goto require_parentheses; + } + } + +require_parentheses: + + njs_parser_syntax_error(vm, parser, "Either \"??\" or \"%s\" expression " + "must be parenthesized", + (token == NJS_TOKEN_LOGICAL_OR) ? "||" + : "&&"); + return NJS_TOKEN_ILLEGAL; +} + + +static njs_token_t njs_parser_binary_expression(njs_vm_t *vm, njs_parser_t *parser, const njs_parser_expression_t *expr, njs_token_t token) { diff -r 5bf71dfc052f -r 9c1ef5ab7a1f src/njs_vmcode.c --- a/src/njs_vmcode.c Wed Dec 11 15:58:05 2019 +0300 +++ b/src/njs_vmcode.c Thu Dec 12 18:50:27 2019 +0300 @@ -503,9 +503,14 @@ next: case NJS_VMCODE_TEST_IF_TRUE: case NJS_VMCODE_TEST_IF_FALSE: - ret = njs_is_true(value1); + case NJS_VMCODE_COALESCE: + if (op == NJS_VMCODE_COALESCE) { + ret = !njs_is_null_or_undefined(value1); - ret ^= op - NJS_VMCODE_TEST_IF_TRUE; + } else { + ret = njs_is_true(value1); + ret ^= op - NJS_VMCODE_TEST_IF_TRUE; + } if (ret) { test_jump = (njs_vmcode_test_jump_t *) pc; diff -r 5bf71dfc052f -r 9c1ef5ab7a1f src/njs_vmcode.h --- a/src/njs_vmcode.h Wed Dec 11 15:58:05 2019 +0300 +++ b/src/njs_vmcode.h Thu Dec 12 18:50:27 2019 +0300 @@ -105,19 +105,21 @@ typedef uint8_t #define NJS_VMCODE_TEST_IF_TRUE VMCODE1(34) #define NJS_VMCODE_TEST_IF_FALSE VMCODE1(35) -#define NJS_VMCODE_UNARY_PLUS VMCODE1(36) -#define NJS_VMCODE_UNARY_NEGATION VMCODE1(37) -#define NJS_VMCODE_BITWISE_NOT VMCODE1(38) -#define NJS_VMCODE_LOGICAL_NOT VMCODE1(39) -#define NJS_VMCODE_OBJECT VMCODE1(40) -#define NJS_VMCODE_ARRAY VMCODE1(41) -#define NJS_VMCODE_FUNCTION VMCODE1(42) -#define NJS_VMCODE_REGEXP VMCODE1(43) +#define NJS_VMCODE_COALESCE VMCODE1(36) -#define NJS_VMCODE_INSTANCE_OF VMCODE1(44) -#define NJS_VMCODE_TYPEOF VMCODE1(45) -#define NJS_VMCODE_VOID VMCODE1(46) -#define NJS_VMCODE_DELETE VMCODE1(47) +#define NJS_VMCODE_UNARY_PLUS VMCODE1(37) +#define NJS_VMCODE_UNARY_NEGATION VMCODE1(38) +#define NJS_VMCODE_BITWISE_NOT VMCODE1(39) +#define NJS_VMCODE_LOGICAL_NOT VMCODE1(40) +#define NJS_VMCODE_OBJECT VMCODE1(41) +#define NJS_VMCODE_ARRAY VMCODE1(42) +#define NJS_VMCODE_FUNCTION VMCODE1(43) +#define NJS_VMCODE_REGEXP VMCODE1(44) + +#define NJS_VMCODE_INSTANCE_OF VMCODE1(45) +#define NJS_VMCODE_TYPEOF VMCODE1(46) +#define NJS_VMCODE_VOID VMCODE1(47) +#define NJS_VMCODE_DELETE VMCODE1(48) #define NJS_VMCODE_NOP 255 diff -r 5bf71dfc052f -r 9c1ef5ab7a1f src/test/njs_unit_test.c --- a/src/test/njs_unit_test.c Wed Dec 11 15:58:05 2019 +0300 +++ b/src/test/njs_unit_test.c Thu Dec 12 18:50:27 2019 +0300 @@ -1295,6 +1295,23 @@ static njs_unit_test_t njs_test[] = { njs_str("false && (true || true)"), njs_str("false") }, + { njs_str("true && (null ?? true)"), + njs_str("true") }, + + { njs_str("(null || undefined) ?? (true && true)"), + njs_str("true") }, + + { njs_str("undefined ?? null ?? false ?? true"), + njs_str("false") }, + + { njs_str("1 && 1 ?? true"), + njs_str("SyntaxError: Either \"??\" or \"&&\" expression " + "must be parenthesized in 1") }, + + { njs_str("null ?? 0 || 1"), + njs_str("SyntaxError: Either \"??\" or \"||\" expression " + "must be parenthesized in 1") }, + { njs_str("var a = true; a = -~!a"), njs_str("1") }, _______________________________________________ nginx-devel mailing list nginx-devel@nginx.org http://mailman.nginx.org/mailman/listinfo/nginx-devel