[njs] Introduced nullish coalescing operator.
Valentin Bartenev
vbart at nginx.com
Mon Dec 23 15:53:24 UTC 2019
details: https://hg.nginx.org/njs/rev/9c1ef5ab7a1f
branches:
changeset: 1284:9c1ef5ab7a1f
user: Valentin Bartenev <vbart at 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") },
More information about the nginx-devel
mailing list