Browse Source

LibJS: Parse comma operator into SequenceExpression

Linus Groh 5 years ago
parent
commit
8ffdcce0d0
2 changed files with 48 additions and 13 deletions
  1. 22 13
      Libraries/LibJS/Parser.cpp
  2. 26 0
      Libraries/LibJS/Tests/comma-operator.js

+ 22 - 13
Libraries/LibJS/Parser.cpp

@@ -299,7 +299,7 @@ RefPtr<FunctionExpression> Parser::try_parse_arrow_function_expression(bool expe
             if (expect_parens && match(TokenType::Equals)) {
                 consume(TokenType::Equals);
                 function_length = parameters.size();
-                default_value = parse_expression(0);
+                default_value = parse_expression(2);
             }
             parameters.append({ parameter_name, default_value });
         } else if (match(TokenType::TripleDot)) {
@@ -340,7 +340,7 @@ RefPtr<FunctionExpression> Parser::try_parse_arrow_function_expression(bool expe
     if (parse_failed)
         return nullptr;
 
-    if (function_length == -1) 
+    if (function_length == -1)
         function_length = parameters.size();
 
     auto function_body_result = [this]() -> RefPtr<BlockStatement> {
@@ -504,7 +504,7 @@ NonnullRefPtr<ObjectExpression> Parser::parse_object_expression()
             consume(TokenType::BracketClose);
         } else if (match(TokenType::TripleDot)) {
             consume(TokenType::TripleDot);
-            property_key = create_ast_node<SpreadExpression>(parse_expression(0));
+            property_key = create_ast_node<SpreadExpression>(parse_expression(2));
             property_value = property_key;
             need_colon = false;
             is_spread = true;
@@ -518,7 +518,7 @@ NonnullRefPtr<ObjectExpression> Parser::parse_object_expression()
             property_value = parse_function_node<FunctionExpression>(false);
         } else if (need_colon || match(TokenType::Colon)) {
             consume(TokenType::Colon);
-            property_value = parse_expression(0);
+            property_value = parse_expression(2);
         }
         auto property = create_ast_node<ObjectProperty>(*property_key, *property_value);
         properties.append(property);
@@ -545,9 +545,9 @@ NonnullRefPtr<ArrayExpression> Parser::parse_array_expression()
 
         if (match(TokenType::TripleDot)) {
             consume(TokenType::TripleDot);
-            expression = create_ast_node<SpreadExpression>(parse_expression(0));
+            expression = create_ast_node<SpreadExpression>(parse_expression(2));
         } else if (match_expression()) {
-            expression = parse_expression(0);
+            expression = parse_expression(2);
         }
 
         elements.append(expression);
@@ -634,6 +634,15 @@ NonnullRefPtr<Expression> Parser::parse_expression(int min_precedence, Associati
             expression = create_ast_node<TaggedTemplateLiteral>(move(expression), move(template_literal));
         }
     }
+    if (match(TokenType::Comma) && min_precedence <= 1) {
+        NonnullRefPtrVector<Expression> expressions;
+        expressions.append(expression);
+        while (match(TokenType::Comma)) {
+            consume();
+            expressions.append(parse_expression(2));
+        }
+        expression = create_ast_node<SequenceExpression>(move(expressions));
+    }
     return expression;
 }
 
@@ -795,9 +804,9 @@ NonnullRefPtr<CallExpression> Parser::parse_call_expression(NonnullRefPtr<Expres
     while (match_expression() || match(TokenType::TripleDot)) {
         if (match(TokenType::TripleDot)) {
             consume();
-            arguments.append({ parse_expression(0), true });
+            arguments.append({ parse_expression(2), true });
         } else {
-            arguments.append({ parse_expression(0), false });
+            arguments.append({ parse_expression(2), false });
         }
         if (!match(TokenType::Comma))
             break;
@@ -823,9 +832,9 @@ NonnullRefPtr<NewExpression> Parser::parse_new_expression()
         while (match_expression() || match(TokenType::TripleDot)) {
             if (match(TokenType::TripleDot)) {
                 consume();
-                arguments.append({ parse_expression(0), true });
+                arguments.append({ parse_expression(2), true });
             } else {
-                arguments.append({ parse_expression(0), false });
+                arguments.append({ parse_expression(2), false });
             }
             if (!match(TokenType::Comma))
                 break;
@@ -906,7 +915,7 @@ NonnullRefPtr<FunctionNodeType> Parser::parse_function_node(bool needs_function_
         if (match(TokenType::Equals)) {
             consume(TokenType::Equals);
             function_length = parameters.size();
-            default_value = parse_expression(0);
+            default_value = parse_expression(2);
         }
         parameters.append({ parameter_name, default_value });
         if (match(TokenType::ParenClose))
@@ -917,7 +926,7 @@ NonnullRefPtr<FunctionNodeType> Parser::parse_function_node(bool needs_function_
 
     if (function_length == -1)
         function_length = parameters.size();
-        
+
     auto body = parse_block_statement();
     body->add_variables(m_parser_state.m_var_scopes.last());
     return create_ast_node<FunctionNodeType>(name, move(body), move(parameters), function_length, NonnullRefPtrVector<VariableDeclaration>());
@@ -950,7 +959,7 @@ NonnullRefPtr<VariableDeclaration> Parser::parse_variable_declaration()
         RefPtr<Expression> init;
         if (match(TokenType::Equals)) {
             consume();
-            init = parse_expression(0);
+            init = parse_expression(2);
         }
         declarations.append(create_ast_node<VariableDeclarator>(create_ast_node<Identifier>(move(id)), move(init)));
         if (match(TokenType::Comma)) {

+ 26 - 0
Libraries/LibJS/Tests/comma-operator.js

@@ -0,0 +1,26 @@
+load("test-common.js");
+
+try {
+    assert((1, 2, 3) === 3);
+    assert((1, 2 + 3, 4) === 4);
+
+    var foo = 0;
+    foo = (foo++, foo);
+    assert(foo === 1);
+
+    var a, b, c;
+    assert((a = b = 3, c = 4) === 4);
+    assert(a === 3);
+    assert(b === 3);
+    assert(c === 4);
+
+    var x, y, z;
+    assert((x = (y = 5, z = 6)) === 6);
+    assert(x === 6)
+    assert(y === 5)
+    assert(z === 6)
+
+    console.log("PASS");
+} catch (e) {
+    console.log("FAIL: " + e);
+}