AST extensions and parsing for import & export declarations.
authorrossberg@chromium.org <rossberg@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Wed, 29 Feb 2012 12:12:52 +0000 (12:12 +0000)
committerrossberg@chromium.org <rossberg@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Wed, 29 Feb 2012 12:12:52 +0000 (12:12 +0000)
R=jkummerow@chromium.org
BUG=
TEST=

Review URL: https://chromiumcodereview.appspot.com/9496003

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@10866 ce2b1a6d-e550-0410-aec6-3dcde31c8c00

src/ast.cc
src/ast.h
src/full-codegen.cc
src/hydrogen.cc
src/parser.cc
src/parser.h
src/prettyprinter.cc
src/rewriter.cc
test/mjsunit/harmony/module-parsing.js

index 9810c34..b19fc35 100644 (file)
@@ -1008,6 +1008,8 @@ CaseClause::CaseClause(Isolate* isolate,
 INCREASE_NODE_COUNT(VariableDeclaration)
 INCREASE_NODE_COUNT(FunctionDeclaration)
 INCREASE_NODE_COUNT(ModuleDeclaration)
+INCREASE_NODE_COUNT(ImportDeclaration)
+INCREASE_NODE_COUNT(ExportDeclaration)
 INCREASE_NODE_COUNT(ModuleLiteral)
 INCREASE_NODE_COUNT(ModuleVariable)
 INCREASE_NODE_COUNT(ModulePath)
index d7819c3..5398775 100644 (file)
--- a/src/ast.h
+++ b/src/ast.h
@@ -63,6 +63,8 @@ namespace internal {
   V(VariableDeclaration)                        \
   V(FunctionDeclaration)                        \
   V(ModuleDeclaration)                          \
+  V(ImportDeclaration)                          \
+  V(ExportDeclaration)                          \
 
 #define MODULE_NODE_LIST(V)                     \
   V(ModuleLiteral)                              \
@@ -544,6 +546,48 @@ class ModuleDeclaration: public Declaration {
 };
 
 
+class ImportDeclaration: public Declaration {
+ public:
+  DECLARE_NODE_TYPE(ImportDeclaration)
+
+  Module* module() const { return module_; }
+  virtual InitializationFlag initialization() const {
+    return kCreatedInitialized;
+  }
+
+ protected:
+  template<class> friend class AstNodeFactory;
+
+  ImportDeclaration(VariableProxy* proxy,
+                    Module* module,
+                    Scope* scope)
+      : Declaration(proxy, LET, scope),
+        module_(module) {
+  }
+
+ private:
+  Module* module_;
+};
+
+
+class ExportDeclaration: public Declaration {
+ public:
+  DECLARE_NODE_TYPE(ExportDeclaration)
+
+  virtual InitializationFlag initialization() const {
+    return kCreatedInitialized;
+  }
+
+ protected:
+  template<class> friend class AstNodeFactory;
+
+  ExportDeclaration(VariableProxy* proxy,
+                    Scope* scope)
+      : Declaration(proxy, LET, scope) {
+  }
+};
+
+
 class Module: public AstNode {
   // TODO(rossberg): stuff to come...
  protected:
@@ -2580,6 +2624,21 @@ class AstNodeFactory BASE_EMBEDDED {
     VISIT_AND_RETURN(ModuleDeclaration, decl)
   }
 
+  ImportDeclaration* NewImportDeclaration(VariableProxy* proxy,
+                                          Module* module,
+                                          Scope* scope) {
+    ImportDeclaration* decl =
+        new(zone_) ImportDeclaration(proxy, module, scope);
+    VISIT_AND_RETURN(ImportDeclaration, decl)
+  }
+
+  ExportDeclaration* NewExportDeclaration(VariableProxy* proxy,
+                                          Scope* scope) {
+    ExportDeclaration* decl =
+        new(zone_) ExportDeclaration(proxy, scope);
+    VISIT_AND_RETURN(ExportDeclaration, decl)
+  }
+
   ModuleLiteral* NewModuleLiteral(Block* body) {
     ModuleLiteral* module = new(zone_) ModuleLiteral(body);
     VISIT_AND_RETURN(ModuleLiteral, module)
index a37b3e4..5f3c1d2 100644 (file)
@@ -63,6 +63,14 @@ void BreakableStatementChecker::VisitModuleDeclaration(
     ModuleDeclaration* decl) {
 }
 
+void BreakableStatementChecker::VisitImportDeclaration(
+    ImportDeclaration* decl) {
+}
+
+void BreakableStatementChecker::VisitExportDeclaration(
+    ExportDeclaration* decl) {
+}
+
 
 void BreakableStatementChecker::VisitModuleLiteral(ModuleLiteral* module) {
 }
@@ -622,6 +630,16 @@ void FullCodeGenerator::VisitModuleDeclaration(ModuleDeclaration* decl) {
 }
 
 
+void FullCodeGenerator::VisitImportDeclaration(ImportDeclaration* decl) {
+  EmitDeclaration(decl->proxy(), decl->mode(), NULL);
+}
+
+
+void FullCodeGenerator::VisitExportDeclaration(ExportDeclaration* decl) {
+  // TODO(rossberg)
+}
+
+
 void FullCodeGenerator::VisitModuleLiteral(ModuleLiteral* module) {
   // TODO(rossberg)
 }
index 6413fad..bc048b3 100644 (file)
@@ -6932,7 +6932,17 @@ void HGraphBuilder::VisitFunctionDeclaration(FunctionDeclaration* decl) {
 
 
 void HGraphBuilder::VisitModuleDeclaration(ModuleDeclaration* decl) {
-  // TODO(rossberg)
+  UNREACHABLE();
+}
+
+
+void HGraphBuilder::VisitImportDeclaration(ImportDeclaration* decl) {
+  UNREACHABLE();
+}
+
+
+void HGraphBuilder::VisitExportDeclaration(ExportDeclaration* decl) {
+  UNREACHABLE();
 }
 
 
index 4c342f8..5f04cab 100644 (file)
@@ -1184,10 +1184,10 @@ Statement* Parser::ParseModuleElement(ZoneStringList* labels,
 
   switch (peek()) {
     case Token::FUNCTION:
-      return ParseFunctionDeclaration(ok);
+      return ParseFunctionDeclaration(NULL, ok);
     case Token::LET:
     case Token::CONST:
-      return ParseVariableStatement(kModuleElement, ok);
+      return ParseVariableStatement(kModuleElement, NULL, ok);
     case Token::IMPORT:
       return ParseImportDeclaration(ok);
     case Token::EXPORT:
@@ -1205,7 +1205,7 @@ Statement* Parser::ParseModuleElement(ZoneStringList* labels,
             estmt->expression()->AsVariableProxy()->name()->Equals(
                 isolate()->heap()->module_symbol()) &&
             !scanner().literal_contains_escapes()) {
-          return ParseModuleDeclaration(ok);
+          return ParseModuleDeclaration(NULL, ok);
         }
       }
       return stmt;
@@ -1214,7 +1214,7 @@ Statement* Parser::ParseModuleElement(ZoneStringList* labels,
 }
 
 
-Block* Parser::ParseModuleDeclaration(bool* ok) {
+Block* Parser::ParseModuleDeclaration(ZoneStringList* names, bool* ok) {
   // ModuleDeclaration:
   //    'module' Identifier Module
 
@@ -1229,6 +1229,7 @@ Block* Parser::ParseModuleDeclaration(bool* ok) {
 
   // TODO(rossberg): Add initialization statement to block.
 
+  if (names) names->Add(name);
   return block;
 }
 
@@ -1236,19 +1237,26 @@ Block* Parser::ParseModuleDeclaration(bool* ok) {
 Module* Parser::ParseModule(bool* ok) {
   // Module:
   //    '{' ModuleElement '}'
-  //    '=' ModulePath
-  //    'at' String
+  //    '=' ModulePath ';'
+  //    'at' String ';'
 
   switch (peek()) {
     case Token::LBRACE:
       return ParseModuleLiteral(ok);
 
-    case Token::ASSIGN:
+    case Token::ASSIGN: {
       Expect(Token::ASSIGN, CHECK_OK);
-      return ParseModulePath(ok);
+      Module* result = ParseModulePath(CHECK_OK);
+      ExpectSemicolon(CHECK_OK);
+      return result;
+    }
 
-    default:
-      return ParseModuleUrl(ok);
+    default: {
+      ExpectContextualKeyword("at", CHECK_OK);
+      Module* result = ParseModuleUrl(CHECK_OK);
+      ExpectSemicolon(CHECK_OK);
+      return result;
+    }
   }
 }
 
@@ -1317,31 +1325,122 @@ Module* Parser::ParseModuleVariable(bool* ok) {
 
 Module* Parser::ParseModuleUrl(bool* ok) {
   // Module:
-  //    'at' String
+  //    String
 
-  Expect(Token::IDENTIFIER, CHECK_OK);
-  Handle<String> symbol = GetSymbol(CHECK_OK);
-  if (!symbol->IsEqualTo(CStrVector("at"))) {
-    *ok = false;
-    ReportUnexpectedToken(scanner().current_token());
-    return NULL;
-  }
   Expect(Token::STRING, CHECK_OK);
-  symbol = GetSymbol(CHECK_OK);
+  Handle<String> symbol = GetSymbol(CHECK_OK);
 
   return factory()->NewModuleUrl(symbol);
 }
 
 
+Module* Parser::ParseModuleSpecifier(bool* ok) {
+  // ModuleSpecifier:
+  //    String
+  //    ModulePath
+
+  if (peek() == Token::STRING) {
+    return ParseModuleUrl(ok);
+  } else {
+    return ParseModulePath(ok);
+  }
+}
+
+
 Block* Parser::ParseImportDeclaration(bool* ok) {
-  // TODO(rossberg)
-  return NULL;
+  // ImportDeclaration:
+  //    'import' IdentifierName (',' IdentifierName)* 'from' ModuleSpecifier ';'
+  //
+  // TODO(ES6): implement destructuring ImportSpecifiers
+
+  Expect(Token::IMPORT, CHECK_OK);
+  ZoneStringList names(1);
+
+  Handle<String> name = ParseIdentifierName(CHECK_OK);
+  while (peek() == Token::COMMA) {
+    Consume(Token::COMMA);
+    name = ParseIdentifierName(CHECK_OK);
+    names.Add(name);
+  }
+
+  ExpectContextualKeyword("from", CHECK_OK);
+  Module* module = ParseModuleSpecifier(CHECK_OK);
+  ExpectSemicolon(CHECK_OK);
+
+  // Generate a separate declaration for each identifier.
+  // TODO(ES6): once we implement destructuring, make that one declaration.
+  Block* block = factory()->NewBlock(NULL, 1, true);
+  for (int i = 0; i < names.length(); ++i) {
+    VariableProxy* proxy = NewUnresolved(names[i], LET);
+    Declaration* declaration =
+        factory()->NewImportDeclaration(proxy, module, top_scope_);
+    Declare(declaration, true, CHECK_OK);
+    // TODO(rossberg): Add initialization statement to block.
+  }
+
+
+  return block;
 }
 
 
-Block* Parser::ParseExportDeclaration(bool* ok) {
-  // TODO(rossberg)
-  return NULL;
+Statement* Parser::ParseExportDeclaration(bool* ok) {
+  // ExportDeclaration:
+  //    'export' Identifier (',' Identifier)* ';'
+  //    'export' VariableDeclaration
+  //    'export' FunctionDeclaration
+  //    'export' ModuleDeclaration
+  //
+  // TODO(ES6): implement structuring ExportSpecifiers
+
+  Expect(Token::EXPORT, CHECK_OK);
+
+  Statement* result = NULL;
+  ZoneStringList names(1);
+  switch (peek()) {
+    case Token::IDENTIFIER: {
+      Handle<String> name = ParseIdentifier(CHECK_OK);
+      // Handle 'module' as a context-sensitive keyword.
+      if (!name->IsEqualTo(CStrVector("module"))) {
+        names.Add(name);
+        while (peek() == Token::COMMA) {
+          Consume(Token::COMMA);
+          name = ParseIdentifier(CHECK_OK);
+          names.Add(name);
+        }
+        ExpectSemicolon(CHECK_OK);
+        result = factory()->NewEmptyStatement();
+      } else {
+        result = ParseModuleDeclaration(&names, CHECK_OK);
+      }
+      break;
+    }
+
+    case Token::FUNCTION:
+      result = ParseFunctionDeclaration(&names, CHECK_OK);
+      break;
+
+    case Token::VAR:
+    case Token::LET:
+    case Token::CONST:
+      result = ParseVariableStatement(kModuleElement, &names, CHECK_OK);
+      break;
+
+    default:
+      *ok = false;
+      ReportUnexpectedToken(scanner().current_token());
+      return NULL;
+  }
+
+  // Extract declared names into export declarations.
+  for (int i = 0; i < names.length(); ++i) {
+    VariableProxy* proxy = NewUnresolved(names[i], LET);
+    Declaration* declaration =
+        factory()->NewExportDeclaration(proxy, top_scope_);
+    top_scope_->AddDeclaration(declaration);
+  }
+
+  ASSERT(result != NULL);
+  return result;
 }
 
 
@@ -1359,10 +1458,10 @@ Statement* Parser::ParseBlockElement(ZoneStringList* labels,
 
   switch (peek()) {
     case Token::FUNCTION:
-      return ParseFunctionDeclaration(ok);
+      return ParseFunctionDeclaration(NULL, ok);
     case Token::LET:
     case Token::CONST:
-      return ParseVariableStatement(kModuleElement, ok);
+      return ParseVariableStatement(kModuleElement, NULL, ok);
     default:
       return ParseStatement(labels, ok);
   }
@@ -1404,7 +1503,7 @@ Statement* Parser::ParseStatement(ZoneStringList* labels, bool* ok) {
     case Token::CONST:  // fall through
     case Token::LET:
     case Token::VAR:
-      stmt = ParseVariableStatement(kStatement, ok);
+      stmt = ParseVariableStatement(kStatement, NULL, ok);
       break;
 
     case Token::SEMICOLON:
@@ -1481,7 +1580,7 @@ Statement* Parser::ParseStatement(ZoneStringList* labels, bool* ok) {
         *ok = false;
         return NULL;
       }
-      return ParseFunctionDeclaration(ok);
+      return ParseFunctionDeclaration(NULL, ok);
     }
 
     case Token::DEBUGGER:
@@ -1708,7 +1807,7 @@ Statement* Parser::ParseNativeDeclaration(bool* ok) {
 }
 
 
-Statement* Parser::ParseFunctionDeclaration(bool* ok) {
+Statement* Parser::ParseFunctionDeclaration(ZoneStringList* names, bool* ok) {
   // FunctionDeclaration ::
   //   'function' Identifier '(' FormalParameterListopt ')' '{' FunctionBody '}'
   Expect(Token::FUNCTION, CHECK_OK);
@@ -1729,6 +1828,7 @@ Statement* Parser::ParseFunctionDeclaration(bool* ok) {
   Declaration* declaration =
       factory()->NewFunctionDeclaration(proxy, mode, fun, top_scope_);
   Declare(declaration, true, CHECK_OK);
+  if (names) names->Add(name);
   return factory()->NewEmptyStatement();
 }
 
@@ -1795,13 +1895,14 @@ Block* Parser::ParseScopedBlock(ZoneStringList* labels, bool* ok) {
 
 
 Block* Parser::ParseVariableStatement(VariableDeclarationContext var_context,
+                                      ZoneStringList* names,
                                       bool* ok) {
   // VariableStatement ::
   //   VariableDeclarations ';'
 
   Handle<String> ignore;
   Block* result =
-      ParseVariableDeclarations(var_context, NULL, &ignore, CHECK_OK);
+      ParseVariableDeclarations(var_context, NULL, names, &ignore, CHECK_OK);
   ExpectSemicolon(CHECK_OK);
   return result;
 }
@@ -1821,6 +1922,7 @@ bool Parser::IsEvalOrArguments(Handle<String> string) {
 Block* Parser::ParseVariableDeclarations(
     VariableDeclarationContext var_context,
     VariableDeclarationProperties* decl_props,
+    ZoneStringList* names,
     Handle<String>* out,
     bool* ok) {
   // VariableDeclarations ::
@@ -1965,6 +2067,7 @@ Block* Parser::ParseVariableDeclarations(
       *ok = false;
       return NULL;
     }
+    if (names) names->Add(name);
 
     // Parse initialization expression if present and/or needed. A
     // declaration of the form:
@@ -2617,7 +2720,7 @@ Statement* Parser::ParseForStatement(ZoneStringList* labels, bool* ok) {
     if (peek() == Token::VAR || peek() == Token::CONST) {
       Handle<String> name;
       Block* variable_statement =
-          ParseVariableDeclarations(kForStatement, NULL, &name, CHECK_OK);
+          ParseVariableDeclarations(kForStatement, NULL, NULL, &name, CHECK_OK);
 
       if (peek() == Token::IN && !name.is_null()) {
         VariableProxy* each = top_scope_->NewUnresolved(factory(), name);
@@ -2646,7 +2749,8 @@ Statement* Parser::ParseForStatement(ZoneStringList* labels, bool* ok) {
       Handle<String> name;
       VariableDeclarationProperties decl_props = kHasNoInitializers;
       Block* variable_statement =
-         ParseVariableDeclarations(kForStatement, &decl_props, &name, CHECK_OK);
+         ParseVariableDeclarations(kForStatement, &decl_props, NULL, &name,
+                                   CHECK_OK);
       bool accept_IN = !name.is_null() && decl_props != kHasInitializers;
       if (peek() == Token::IN && accept_IN) {
         // Rewrite a for-in statement of the form
@@ -4576,6 +4680,18 @@ void Parser::ExpectSemicolon(bool* ok) {
 }
 
 
+void Parser::ExpectContextualKeyword(const char* keyword, bool* ok) {
+  Expect(Token::IDENTIFIER, ok);
+  if (!*ok) return;
+  Handle<String> symbol = GetSymbol(ok);
+  if (!*ok) return;
+  if (!symbol->IsEqualTo(CStrVector(keyword))) {
+    *ok = false;
+    ReportUnexpectedToken(scanner().current_token());
+  }
+}
+
+
 Literal* Parser::GetLiteralUndefined() {
   return factory()->NewLiteral(isolate()->factory()->undefined_value());
 }
index 4c18fe9..dea73a1 100644 (file)
@@ -581,23 +581,26 @@ class Parser {
   void* ParseSourceElements(ZoneList<Statement*>* processor,
                             int end_token, bool* ok);
   Statement* ParseModuleElement(ZoneStringList* labels, bool* ok);
-  Block* ParseModuleDeclaration(bool* ok);
+  Block* ParseModuleDeclaration(ZoneStringList* names, bool* ok);
   Module* ParseModule(bool* ok);
   Module* ParseModuleLiteral(bool* ok);
   Module* ParseModulePath(bool* ok);
   Module* ParseModuleVariable(bool* ok);
   Module* ParseModuleUrl(bool* ok);
+  Module* ParseModuleSpecifier(bool* ok);
   Block* ParseImportDeclaration(bool* ok);
-  Block* ParseExportDeclaration(bool* ok);
+  Statement* ParseExportDeclaration(bool* ok);
   Statement* ParseBlockElement(ZoneStringList* labels, bool* ok);
   Statement* ParseStatement(ZoneStringList* labels, bool* ok);
-  Statement* ParseFunctionDeclaration(bool* ok);
+  Statement* ParseFunctionDeclaration(ZoneStringList* names, bool* ok);
   Statement* ParseNativeDeclaration(bool* ok);
   Block* ParseBlock(ZoneStringList* labels, bool* ok);
   Block* ParseVariableStatement(VariableDeclarationContext var_context,
+                                ZoneStringList* names,
                                 bool* ok);
   Block* ParseVariableDeclarations(VariableDeclarationContext var_context,
                                    VariableDeclarationProperties* decl_props,
+                                   ZoneStringList* names,
                                    Handle<String>* out,
                                    bool* ok);
   Statement* ParseExpressionOrLabelledStatement(ZoneStringList* labels,
@@ -700,6 +703,7 @@ class Parser {
   void Expect(Token::Value token, bool* ok);
   bool Check(Token::Value token);
   void ExpectSemicolon(bool* ok);
+  void ExpectContextualKeyword(const char* keyword, bool* ok);
 
   Handle<String> LiteralString(PretenureFlag tenured) {
     if (scanner().is_literal_ascii()) {
index d879da1..0d8dadc 100644 (file)
@@ -83,6 +83,22 @@ void PrettyPrinter::VisitModuleDeclaration(ModuleDeclaration* node) {
 }
 
 
+void PrettyPrinter::VisitImportDeclaration(ImportDeclaration* node) {
+  Print("import ");
+  PrintLiteral(node->proxy()->name(), false);
+  Print(" from ");
+  Visit(node->module());
+  Print(";");
+}
+
+
+void PrettyPrinter::VisitExportDeclaration(ExportDeclaration* node) {
+  Print("export ");
+  PrintLiteral(node->proxy()->name(), false);
+  Print(";");
+}
+
+
 void PrettyPrinter::VisitModuleLiteral(ModuleLiteral* node) {
   VisitBlock(node->body());
 }
@@ -771,6 +787,19 @@ void AstPrinter::VisitModuleDeclaration(ModuleDeclaration* node) {
 }
 
 
+void AstPrinter::VisitImportDeclaration(ImportDeclaration* node) {
+  IndentedScope indent(this, "IMPORT");
+  PrintLiteralIndented("NAME", node->proxy()->name(), true);
+  Visit(node->module());
+}
+
+
+void AstPrinter::VisitExportDeclaration(ExportDeclaration* node) {
+  IndentedScope indent(this, "EXPORT ");
+  PrintLiteral(node->proxy()->name(), true);
+}
+
+
 void AstPrinter::VisitModuleLiteral(ModuleLiteral* node) {
   VisitBlock(node->body());
 }
index a92c3c3..55f93ee 100644 (file)
@@ -212,6 +212,8 @@ void Processor::VisitWithStatement(WithStatement* node) {
 void Processor::VisitVariableDeclaration(VariableDeclaration* node) {}
 void Processor::VisitFunctionDeclaration(FunctionDeclaration* node) {}
 void Processor::VisitModuleDeclaration(ModuleDeclaration* node) {}
+void Processor::VisitImportDeclaration(ImportDeclaration* node) {}
+void Processor::VisitExportDeclaration(ExportDeclaration* node) {}
 void Processor::VisitModuleLiteral(ModuleLiteral* node) {}
 void Processor::VisitModuleVariable(ModuleVariable* node) {}
 void Processor::VisitModulePath(ModulePath* node) {}
index 5a5e82f..cf56502 100644 (file)
@@ -36,19 +36,39 @@ module A2 = A;
 module A3 = A2
 
 module B {
-  var x
-  var x, y;
-  var x = 0, y
-  let x, y
-  let z = 1
+  export x
+  export y, z, c, f
+
+  var vx
+  var vx, vy;
+  var vx = 0, vy
+  let lx, ly
+  let lz = 1
   const c = 9
   function f() {}
+
   module C {
     let x
     module D {}
     let y
   }
+
   let zz = ""
+
+  export var x0
+  export var x1, x2 = 6, x3
+  export let y0
+  export let y1 = 0, y2
+  export const z0 = 0
+  export const z1 = 2, z2 = 3
+  export function f0() {}
+  export module M1 {}
+  export module M2 = C.D
+  export module M3 at "http://where"
+
+  import i0 from I
+  import i1, i2, i3 from I
+  import i4, i5 from "http://where"
 }
 
 module C1 = B.C;
@@ -76,6 +96,45 @@ module Z
 at
 "file://local"
 
+import
+x
+,
+y
+from
+"file://local"
+
+module Wrap {
+export
+x
+,
+y
+
+export
+var
+v1 = 1
+
+export
+let
+v2 = 2
+
+export
+const
+v3 = 3
+
+export
+function
+f
+(
+)
+{
+}
+
+export
+module V
+{
+}
+}
+
 
 // Check that 'module' still works as an identifier.