[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