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),
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);
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:
};
+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 },
};
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,
NJS_TOKEN_MULTIPLICATION,
+ NJS_TOKEN_EXPONENTIATION,
+
NJS_TOKEN_DIVISION,
NJS_TOKEN_REMAINDER,
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);
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,
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;
}
+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)
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)
{
}
+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)
{
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,
{ 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"),