[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