From: Valentin Bartenev Date: Mon, 19 Dec 2016 11:19:59 +0000 (+0300) Subject: Exponentiation operators. X-Git-Tag: 0.1.7~2 X-Git-Url: http://www.kaiwu.me/postgresql/commit/?a=commitdiff_plain;h=e513acfe291f598d013ea679085a5660132bac89;p=njs.git Exponentiation operators. --- diff --git a/njs/njs_disassembler.c b/njs/njs_disassembler.c index a8e9405e..4b2c488e 100644 --- a/njs/njs_disassembler.c +++ b/njs/njs_disassembler.c @@ -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 --git a/njs/njs_generator.c b/njs/njs_generator.c index 5da68750..caa9bae6 100644 --- a/njs/njs_generator.c +++ b/njs/njs_generator.c @@ -184,6 +184,7 @@ njs_generator(njs_vm_t *vm, njs_parser_t *parser, njs_parser_node_t *node) 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 *parser, njs_parser_node_t *node) 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 --git a/njs/njs_lexer.c b/njs/njs_lexer.c index 1d289ecd..1c8c62ff 100644 --- a/njs/njs_lexer.c +++ b/njs/njs_lexer.c @@ -203,8 +203,14 @@ static const njs_lexer_multi_t njs_substraction_token[] = { }; +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 --git a/njs/njs_parser.h b/njs/njs_parser.h index 327d6c6b..2f142bb2 100644 --- a/njs/njs_parser.h +++ b/njs/njs_parser.h @@ -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 --git a/njs/njs_parser_expression.c b/njs/njs_parser_expression.c index 9100784c..64b1fe77 100644 --- a/njs/njs_parser_expression.c +++ b/njs/njs_parser_expression.c @@ -63,6 +63,9 @@ static njs_token_t njs_parser_conditional_expression(njs_vm_t *vm, 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_brackets(njs_vm_t *vm, 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_t *vm, njs_parser_t *parser, 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; @@ -684,6 +692,64 @@ njs_parser_binary_expression(njs_vm_t *vm, njs_parser_t *parser, } +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, njs_parser_t *parser, 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 --git a/njs/njs_vm.c b/njs/njs_vm.c index 68211728..72394c00 100644 --- a/njs/njs_vm.c +++ b/njs/njs_vm.c @@ -1597,6 +1597,40 @@ 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) +{ + 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) { diff --git a/njs/njs_vm.h b/njs/njs_vm.h index 29cfd75a..f7bf38c4 100644 --- a/njs/njs_vm.h +++ b/njs/njs_vm.h @@ -927,6 +927,8 @@ njs_ret_t njs_vmcode_substraction(njs_vm_t *vm, njs_value_t *val1, 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 --git a/njs/test/njs_unit_test.c b/njs/test/njs_unit_test.c index 16d79778..b702f355 100644 --- a/njs/test/njs_unit_test.c +++ b/njs/test/njs_unit_test.c @@ -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"),