[njs] Exponentiation operators.

Valentin Bartenev vbart at nginx.com
Mon Dec 19 11:34:07 UTC 2016


details:   http://hg.nginx.org/njs/rev/f5225d9cc97b
branches:  
changeset: 288:f5225d9cc97b
user:      Valentin Bartenev <vbart at nginx.com>
date:      Mon Dec 19 14:19:59 2016 +0300
description:
Exponentiation operators.

diffstat:

 njs/njs_disassembler.c      |    2 +
 njs/njs_generator.c         |    2 +
 njs/njs_lexer.c             |    6 +
 njs/njs_parser.h            |    3 +
 njs/njs_parser_expression.c |   76 +++++++++++++++++++++-
 njs/njs_vm.c                |   34 +++++++++
 njs/njs_vm.h                |    2 +
 njs/test/njs_unit_test.c    |  157 ++++++++++++++++++++++++++++++++++++++++++++
 8 files changed, 281 insertions(+), 1 deletions(-)

diffs (406 lines):

diff -r a76d7066a117 -r f5225d9cc97b njs/njs_disassembler.c
--- a/njs/njs_disassembler.c	Mon Dec 19 14:19:43 2016 +0300
+++ b/njs/njs_disassembler.c	Mon Dec 19 14:19:59 2016 +0300
@@ -87,6 +87,8 @@ static njs_code_name_t  code_names[] = {
           nxt_string("SUBSTRACT       ") },
     { njs_vmcode_multiplication, sizeof(njs_vmcode_3addr_t),
           nxt_string("MULTIPLY        ") },
+    { njs_vmcode_exponentiation, sizeof(njs_vmcode_3addr_t),
+          nxt_string("POWER           ") },
     { njs_vmcode_division, sizeof(njs_vmcode_3addr_t),
           nxt_string("DIVIDE          ") },
     { njs_vmcode_remainder, sizeof(njs_vmcode_3addr_t),
diff -r a76d7066a117 -r f5225d9cc97b njs/njs_generator.c
--- a/njs/njs_generator.c	Mon Dec 19 14:19:43 2016 +0300
+++ b/njs/njs_generator.c	Mon Dec 19 14:19:59 2016 +0300
@@ -184,6 +184,7 @@ njs_generator(njs_vm_t *vm, njs_parser_t
     case NJS_TOKEN_ADDITION_ASSIGNMENT:
     case NJS_TOKEN_SUBSTRACTION_ASSIGNMENT:
     case NJS_TOKEN_MULTIPLICATION_ASSIGNMENT:
+    case NJS_TOKEN_EXPONENTIATION_ASSIGNMENT:
     case NJS_TOKEN_DIVISION_ASSIGNMENT:
     case NJS_TOKEN_REMAINDER_ASSIGNMENT:
         return njs_generate_operation_assignment(vm, parser, node);
@@ -219,6 +220,7 @@ njs_generator(njs_vm_t *vm, njs_parser_t
     case NJS_TOKEN_ADDITION:
     case NJS_TOKEN_SUBSTRACTION:
     case NJS_TOKEN_MULTIPLICATION:
+    case NJS_TOKEN_EXPONENTIATION:
     case NJS_TOKEN_DIVISION:
     case NJS_TOKEN_REMAINDER:
     case NJS_TOKEN_PROPERTY_DELETE:
diff -r a76d7066a117 -r f5225d9cc97b njs/njs_lexer.c
--- a/njs/njs_lexer.c	Mon Dec 19 14:19:43 2016 +0300
+++ b/njs/njs_lexer.c	Mon Dec 19 14:19:59 2016 +0300
@@ -203,8 +203,14 @@ static const njs_lexer_multi_t  njs_subs
 };
 
 
+static const njs_lexer_multi_t  njs_exponentiation_token[] = {
+    { '=', NJS_TOKEN_EXPONENTIATION_ASSIGNMENT, 0, NULL },
+};
+
+
 static const njs_lexer_multi_t  njs_multiplication_token[] = {
     { '=', NJS_TOKEN_MULTIPLICATION_ASSIGNMENT, 0, NULL },
+    { '*', NJS_TOKEN_EXPONENTIATION, 1, njs_exponentiation_token },
 };
 
 
diff -r a76d7066a117 -r f5225d9cc97b njs/njs_parser.h
--- a/njs/njs_parser.h	Mon Dec 19 14:19:43 2016 +0300
+++ b/njs/njs_parser.h	Mon Dec 19 14:19:59 2016 +0300
@@ -41,6 +41,7 @@ typedef enum {
     NJS_TOKEN_ADDITION_ASSIGNMENT,
     NJS_TOKEN_SUBSTRACTION_ASSIGNMENT,
     NJS_TOKEN_MULTIPLICATION_ASSIGNMENT,
+    NJS_TOKEN_EXPONENTIATION_ASSIGNMENT,
     NJS_TOKEN_DIVISION_ASSIGNMENT,
     NJS_TOKEN_REMAINDER_ASSIGNMENT,
     NJS_TOKEN_LEFT_SHIFT_ASSIGNMENT,
@@ -69,6 +70,8 @@ typedef enum {
 
     NJS_TOKEN_MULTIPLICATION,
 
+    NJS_TOKEN_EXPONENTIATION,
+
     NJS_TOKEN_DIVISION,
 
     NJS_TOKEN_REMAINDER,
diff -r a76d7066a117 -r f5225d9cc97b njs/njs_parser_expression.c
--- a/njs/njs_parser_expression.c	Mon Dec 19 14:19:43 2016 +0300
+++ b/njs/njs_parser_expression.c	Mon Dec 19 14:19:59 2016 +0300
@@ -63,6 +63,9 @@ static njs_token_t njs_parser_conditiona
 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);
+static njs_token_t njs_parser_exponential_expression(njs_vm_t *vm,
+    njs_parser_t *parser, const njs_parser_expression_t *expr,
+    njs_token_t token);
 static njs_token_t njs_parser_unary_expression(njs_vm_t *vm,
     njs_parser_t *parser, const njs_parser_expression_t *expr,
     njs_token_t token);
@@ -83,7 +86,7 @@ static njs_token_t njs_parser_property_b
 static const njs_parser_expression_t
     njs_parser_factor_expression =
 {
-    njs_parser_unary_expression,
+    njs_parser_exponential_expression,
     NULL,
     3, {
         { NJS_TOKEN_MULTIPLICATION, njs_vmcode_multiplication,
@@ -391,6 +394,11 @@ njs_parser_assignment_expression(njs_vm_
             operation = njs_vmcode_multiplication;
             break;
 
+        case NJS_TOKEN_EXPONENTIATION_ASSIGNMENT:
+            nxt_thread_log_debug("JS: **=");
+            operation = njs_vmcode_exponentiation;
+            break;
+
         case NJS_TOKEN_DIVISION_ASSIGNMENT:
             nxt_thread_log_debug("JS: /=");
             operation = njs_vmcode_division;
@@ -685,6 +693,64 @@ njs_parser_binary_expression(njs_vm_t *v
 
 
 static njs_token_t
+njs_parser_exponential_expression(njs_vm_t *vm, njs_parser_t *parser,
+    const njs_parser_expression_t *expr, njs_token_t token)
+{
+    njs_parser_node_t  *node;
+
+    token = njs_parser_unary_expression(vm, parser, NULL, token);
+    if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+        return token;
+    }
+
+    for ( ;; ) {
+        if (token == NJS_TOKEN_EXPONENTIATION) {
+
+            node = njs_parser_node_alloc(vm);
+            if (nxt_slow_path(node == NULL)) {
+                return NJS_TOKEN_ERROR;
+            }
+
+            node->token = token;
+            node->u.operation = njs_vmcode_exponentiation;
+            node->left = parser->node;
+            node->left->dest = node;
+
+            parser->code_size += sizeof(njs_vmcode_3addr_t);
+
+            token = njs_parser_token(parser);
+            if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+                return token;
+            }
+
+            token = njs_parser_exponential_expression(vm, parser, NULL, token);
+            if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+                return token;
+            }
+
+            node->right = parser->node;
+            node->right->dest = node;
+            parser->node = node;
+        }
+
+        if (token == NJS_TOKEN_LINE_END) {
+
+            token = njs_lexer_token(parser->lexer);
+            if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+                return token;
+            }
+
+            if (njs_parser_expression_operator(token)) {
+                continue;
+            }
+        }
+
+        return token;
+    }
+}
+
+
+static njs_token_t
 njs_parser_unary_expression(njs_vm_t *vm, njs_parser_t *parser,
     const njs_parser_expression_t *expr, njs_token_t token)
 {
@@ -739,6 +805,14 @@ njs_parser_unary_expression(njs_vm_t *vm
         return next;
     }
 
+    if (next == NJS_TOKEN_EXPONENTIATION) {
+        nxt_alert(&vm->trace, NXT_LEVEL_ERROR,
+                  "SyntaxError: Either left-hand side or entire exponentiation "
+                  "must be parenthesized");
+
+        return NJS_TOKEN_ILLEGAL;
+    }
+
     if (token == NJS_TOKEN_UNARY_PLUS
         && parser->node->token == NJS_TOKEN_NUMBER)
     {
diff -r a76d7066a117 -r f5225d9cc97b njs/njs_vm.c
--- a/njs/njs_vm.c	Mon Dec 19 14:19:43 2016 +0300
+++ b/njs/njs_vm.c	Mon Dec 19 14:19:59 2016 +0300
@@ -1598,6 +1598,40 @@ njs_vmcode_multiplication(njs_vm_t *vm, 
 
 
 njs_ret_t
+njs_vmcode_exponentiation(njs_vm_t *vm, njs_value_t *val1, njs_value_t *val2)
+{
+    double      num, base, exponent;
+    nxt_bool_t  valid;
+
+    if (nxt_fast_path(njs_is_numeric(val1) && njs_is_numeric(val2))) {
+        base = val1->data.u.number;
+        exponent = val2->data.u.number;
+
+        /*
+         * According to ES7:
+         *  1. If exponent is NaN, the result should be NaN;
+         *  2. The result of +/-1 ** +/-Infinity should be NaN.
+         */
+        valid = nxt_expect(1, fabs(base) != 1
+                              || (!isnan(exponent) && !isinf(exponent)));
+
+        if (valid) {
+            num = pow(base, exponent);
+
+        } else {
+            num = NAN;
+        }
+
+        njs_number_set(&vm->retval, num);
+
+        return sizeof(njs_vmcode_3addr_t);
+    }
+
+    return NJS_TRAP_NUMBERS;
+}
+
+
+njs_ret_t
 njs_vmcode_division(njs_vm_t *vm, njs_value_t *val1, njs_value_t *val2)
 {
     double  num;
diff -r a76d7066a117 -r f5225d9cc97b njs/njs_vm.h
--- a/njs/njs_vm.h	Mon Dec 19 14:19:43 2016 +0300
+++ b/njs/njs_vm.h	Mon Dec 19 14:19:59 2016 +0300
@@ -927,6 +927,8 @@ njs_ret_t njs_vmcode_substraction(njs_vm
     njs_value_t *val2);
 njs_ret_t njs_vmcode_multiplication(njs_vm_t *vm, njs_value_t *val1,
     njs_value_t *val2);
+njs_ret_t njs_vmcode_exponentiation(njs_vm_t *vm, njs_value_t *val1,
+    njs_value_t *val2);
 njs_ret_t njs_vmcode_division(njs_vm_t *vm, njs_value_t *val1,
     njs_value_t *val2);
 njs_ret_t njs_vmcode_remainder(njs_vm_t *vm, njs_value_t *val1,
diff -r a76d7066a117 -r f5225d9cc97b njs/test/njs_unit_test.c
--- a/njs/test/njs_unit_test.c	Mon Dec 19 14:19:43 2016 +0300
+++ b/njs/test/njs_unit_test.c	Mon Dec 19 14:19:59 2016 +0300
@@ -224,6 +224,163 @@ static njs_unit_test_t  njs_test[] =
     { nxt_string("var a = 1; function f(x) { a = x; return 2 }; a += f(5)"),
       nxt_string("3") },
 
+    /* Exponentiation. */
+
+    { nxt_string("2 ** (3 ** 2)"),
+      nxt_string("512") },
+
+    { nxt_string("(2 ** 3) ** 2"),
+      nxt_string("64") },
+
+    { nxt_string("3 ** 2 - 9"),
+      nxt_string("0") },
+
+    { nxt_string("-9 + 3 ** 2"),
+      nxt_string("0") },
+
+    { nxt_string("-3 ** 2"),
+      nxt_string("SyntaxError: Either left-hand side or entire exponentiation "
+                 "must be parenthesized in 1") },
+
+    { nxt_string("-(3) ** 2"),
+      nxt_string("SyntaxError: Either left-hand side or entire exponentiation "
+                 "must be parenthesized in 1") },
+
+    { nxt_string("-(3 ** 2)"),
+      nxt_string("-9") },
+
+    { nxt_string("(-3) ** 2"),
+      nxt_string("9") },
+
+    { nxt_string("1 ** NaN"),
+      nxt_string("NaN") },
+
+    { nxt_string("'a' ** -0"),
+      nxt_string("1") },
+
+    { nxt_string("1.1 ** Infinity"),
+      nxt_string("Infinity") },
+
+    { nxt_string("(-1.1) ** -Infinity"),
+      nxt_string("0") },
+
+    { nxt_string("(-1) ** Infinity"),
+      nxt_string("NaN") },
+
+    { nxt_string("1 ** -Infinity"),
+      nxt_string("NaN") },
+
+    { nxt_string("(-0.9) ** Infinity"),
+      nxt_string("0") },
+
+    { nxt_string("0.9 ** -Infinity"),
+      nxt_string("Infinity") },
+
+    { nxt_string("'Infinity' ** 0.1"),
+      nxt_string("Infinity") },
+
+    { nxt_string("Infinity ** '-0.1'"),
+      nxt_string("0") },
+
+    { nxt_string("(-Infinity) ** 3"),
+      nxt_string("-Infinity") },
+
+    { nxt_string("'-Infinity' ** '3.1'"),
+      nxt_string("Infinity") },
+
+    { nxt_string("(-Infinity) ** '-3'"),
+      nxt_string("-0") },
+
+    { nxt_string("'-Infinity' ** -2"),
+      nxt_string("0") },
+
+    { nxt_string("'0' ** 0.1"),
+      nxt_string("0") },
+
+    { nxt_string("0 ** '-0.1'"),
+      nxt_string("Infinity") },
+
+    { nxt_string("(-0) ** 3"),
+      nxt_string("-0") },
+
+    { nxt_string("'-0' ** '3.1'"),
+      nxt_string("0") },
+
+    { nxt_string("(-0) ** '-3'"),
+      nxt_string("-Infinity") },
+
+    { nxt_string("'-0' ** -2"),
+      nxt_string("Infinity") },
+
+    { nxt_string("(-3) ** 0.1"),
+      nxt_string("NaN") },
+
+    { nxt_string("var a = 0.1; a **= -2"),
+      nxt_string("100") },
+
+    { nxt_string("var a = 1; a **= NaN"),
+      nxt_string("NaN") },
+
+    { nxt_string("var a = 'a'; a **= -0"),
+      nxt_string("1") },
+
+    { nxt_string("var a = 1.1; a **= Infinity"),
+      nxt_string("Infinity") },
+
+    { nxt_string("var a = -1.1; a **= -Infinity"),
+      nxt_string("0") },
+
+    { nxt_string("var a = -1; a **= Infinity"),
+      nxt_string("NaN") },
+
+    { nxt_string("var a = 1; a **= -Infinity"),
+      nxt_string("NaN") },
+
+    { nxt_string("var a = -0.9; a **= Infinity"),
+      nxt_string("0") },
+
+    { nxt_string("var a = 0.9; a **= -Infinity"),
+      nxt_string("Infinity") },
+
+    { nxt_string("var a = 'Infinity'; a **= 0.1"),
+      nxt_string("Infinity") },
+
+    { nxt_string("var a = Infinity; a **= '-0.1'"),
+      nxt_string("0") },
+
+    { nxt_string("var a = -Infinity; a **= 3"),
+      nxt_string("-Infinity") },
+
+    { nxt_string("var a = '-Infinity'; a **= '3.1'"),
+      nxt_string("Infinity") },
+
+    { nxt_string("var a = -Infinity; a **= '-3'"),
+      nxt_string("-0") },
+
+    { nxt_string("var a = '-Infinity'; a **= -2"),
+      nxt_string("0") },
+
+    { nxt_string("var a = '0'; a **= 0.1"),
+      nxt_string("0") },
+
+    { nxt_string("var a = 0; a **= '-0.1'"),
+      nxt_string("Infinity") },
+
+    { nxt_string("var a = -0; a **= 3"),
+      nxt_string("-0") },
+
+    { nxt_string("var a = '-0'; a **= '3.1'"),
+      nxt_string("0") },
+
+    { nxt_string("var a = -0; a **= '-3'"),
+      nxt_string("-Infinity") },
+
+    { nxt_string("var a = '-0'; a **= -2"),
+      nxt_string("Infinity") },
+
+    { nxt_string("var a = -3; a **= 0.1"),
+      nxt_string("NaN") },
+
     /**/
 
     { nxt_string("12 | 6"),


More information about the nginx-devel mailing list