Skip to content

Commit

Permalink
Merge pull request #43 from caridy/modules
Browse files Browse the repository at this point in the history
fixes #35: adds module import and export grammar
  • Loading branch information
nzakas committed Mar 5, 2015
2 parents 6448453 + bb258cb commit 7d4a819
Show file tree
Hide file tree
Showing 126 changed files with 5,107 additions and 6 deletions.
299 changes: 297 additions & 2 deletions espree.js
Original file line number Diff line number Diff line change
Expand Up @@ -4704,6 +4704,272 @@ function parseYieldExpression() {
return markerApply(marker, astNodeFactory.createYieldExpression(expr, delegateFlag));
}

// Modules grammar from:
// people.mozilla.org/~jorendorff/es6-draft.html

function parseModuleSpecifier() {
var marker = markerCreate(),
specifier;

if (lookahead.type !== Token.StringLiteral) {
throwError({}, Messages.InvalidModuleSpecifier);
}
specifier = astNodeFactory.createLiteralFromSource(lex(), source);
return markerApply(marker, specifier);
}

function parseExportSpecifier() {
var exported, local, marker = markerCreate();
if (matchKeyword("default")) {
lex();
local = markerApply(marker, astNodeFactory.createIdentifier("default"));
// export {default} from "something";
} else {
local = parseVariableIdentifier();
}
if (matchContextualKeyword("as")) {
lex();
exported = parseNonComputedProperty();
}
return markerApply(marker, astNodeFactory.createExportSpecifier(local, exported));
}

function parseExportNamedDeclaration() {
var declaration = null,
isExportFromIdentifier,
src = null, specifiers = [],
marker = markerCreate();

expectKeyword("export");

// non-default export
if (lookahead.type === Token.Keyword) {
// covers:
// export var f = 1;
switch (lookahead.value) {
case "let":
case "const":
case "var":
// TODO: enable this case when Class gets implemented
// case "class":
case "function":
declaration = parseSourceElement();
return markerApply(marker, astNodeFactory.createExportNamedDeclaration(declaration, specifiers, null));
default:
break;
}
}

expect("{");
if (!match("}")) {
do {
isExportFromIdentifier = isExportFromIdentifier || matchKeyword("default");
specifiers.push(parseExportSpecifier());
} while (match(",") && lex());
}
expect("}");

if (matchContextualKeyword("from")) {
// covering:
// export {default} from "foo";
// export {foo} from "foo";
lex();
src = parseModuleSpecifier();
consumeSemicolon();
} else if (isExportFromIdentifier) {
// covering:
// export {default}; // missing fromClause
throwError({}, lookahead.value ?
Messages.UnexpectedToken : Messages.MissingFromClause, lookahead.value);
} else {
// cover
// export {foo};
consumeSemicolon();
}
return markerApply(marker, astNodeFactory.createExportNamedDeclaration(declaration, specifiers, src));
}

function parseExportDefaultDeclaration() {
var declaration = null,
expression = null,
possibleIdentifierToken,
marker = markerCreate();

// covers:
// export default ...
expectKeyword("export");
expectKeyword("default");
// TODO: or matchKeyword("class") after implementing classes
if (matchKeyword("function")) {
possibleIdentifierToken = lookahead2();
if (isIdentifierName(possibleIdentifierToken)) {
// covers:
// export default function foo () {}
// export default class foo {}
declaration = parseSourceElement();
return markerApply(marker, astNodeFactory.createExportDefaultDeclaration(declaration));
}
// covers:
// export default function () {}
// export default class {}
if (lookahead.value === "function") {
// TODO: change this to declaration once we get FunctionDeclaration with nullable name
declaration = parseFunctionExpression();
return markerApply(marker, astNodeFactory.createExportDefaultDeclaration(declaration));
}
// TODO: enable after implementing classes
// else if (lookahead.value === "class") {
// // TODO: change this to declaration once we get ClassDeclaration with nullable name
// parseClassExpression()
// return markerApply(marker, astNodeFactory.createExportDefaultDeclaration(declaration));
// }
}

if (matchContextualKeyword("from")) {
throwError({}, Messages.UnexpectedToken, lookahead.value);
}

// covers:
// export default {};
// export default [];
// export default (1 + 2);
if (match("{")) {
expression = parseObjectInitialiser();
} else if (match("[")) {
expression = parseArrayInitialiser();
} else {
expression = parseAssignmentExpression();
}
consumeSemicolon();
return markerApply(marker, astNodeFactory.createExportDefaultDeclaration(expression));
}


function parseExportAllDeclaration() {
var src,
marker = markerCreate();

// covers:
// export * from "foo";
expectKeyword("export");
expect("*");
if (!matchContextualKeyword("from")) {
throwError({}, lookahead.value ?
Messages.UnexpectedToken : Messages.MissingFromClause, lookahead.value);
}
lex();
src = parseModuleSpecifier();
consumeSemicolon();

return markerApply(marker, astNodeFactory.createExportAllDeclaration(src));
}

function parseExportDeclaration() {
var declarationType = lookahead2().value;
if (declarationType === "default") {
return parseExportDefaultDeclaration();
} else if (declarationType === "*") {
return parseExportAllDeclaration();
} else {
return parseExportNamedDeclaration();
}
}

function parseImportSpecifier() {
// import {<foo as bar>} ...;
var local, imported, marker = markerCreate();

imported = parseNonComputedProperty();
if (matchContextualKeyword("as")) {
lex();
local = parseVariableIdentifier();
}

return markerApply(marker, astNodeFactory.createImportSpecifier(local, imported));
}

function parseNamedImports() {
var specifiers = [];
// {foo, bar as bas}
expect("{");
if (!match("}")) {
do {
specifiers.push(parseImportSpecifier());
} while (match(",") && lex());
}
expect("}");
return specifiers;
}

function parseImportDefaultSpecifier() {
// import <foo> ...;
var local, marker = markerCreate();

local = parseNonComputedProperty();

return markerApply(marker, astNodeFactory.createImportDefaultSpecifier(local));
}

function parseImportNamespaceSpecifier() {
// import <* as foo> ...;
var local, marker = markerCreate();

expect("*");
if (!matchContextualKeyword("as")) {
throwError({}, Messages.NoAsAfterImportNamespace);
}
lex();
local = parseNonComputedProperty();

return markerApply(marker, astNodeFactory.createImportNamespaceSpecifier(local));
}

function parseImportDeclaration() {
var specifiers, src, marker = markerCreate();

expectKeyword("import");
specifiers = [];

if (lookahead.type === Token.StringLiteral) {
// covers:
// import "foo";
src = parseModuleSpecifier();
consumeSemicolon();
return markerApply(marker, astNodeFactory.createImportDeclaration(specifiers, src));
}

if (!matchKeyword("default") && isIdentifierName(lookahead)) {
// covers:
// import foo
// import foo, ...
specifiers.push(parseImportDefaultSpecifier());
if (match(",")) {
lex();
}
}
if (match("*")) {
// covers:
// import foo, * as foo
// import * as foo
specifiers.push(parseImportNamespaceSpecifier());
} else if (match("{")) {
// covers:
// import foo, {bar}
// import {bar}
specifiers = specifiers.concat(parseNamedImports());
}

if (!matchContextualKeyword("from")) {
throwError({}, lookahead.value ?
Messages.UnexpectedToken : Messages.MissingFromClause, lookahead.value);
}
lex();
src = parseModuleSpecifier();
consumeSemicolon();

return markerApply(marker, astNodeFactory.createImportDeclaration(specifiers, src));
}

// 14 Functions and classes

// 14.1 Functions is defined above (13 in ES5)
Expand Down Expand Up @@ -4839,6 +5105,16 @@ function parseSourceElement() {

if (lookahead.type === Token.Keyword) {
switch (lookahead.value) {
case "export":
if (!extra.isModule) {
throwErrorTolerant({}, Messages.IllegalExportDeclaration);
}
return parseExportDeclaration();
case "import":
if (!extra.isModule) {
throwErrorTolerant({}, Messages.IllegalImportDeclaration);
}
return parseImportDeclaration();
case "function":
return parseFunctionDeclaration();
case "class":
Expand Down Expand Up @@ -4908,7 +5184,7 @@ function parseProgram() {
skipComment();
peek();
marker = markerCreate();
strict = false;
strict = extra.isModule;

body = parseSourceElements();
return markerApply(marker, astNodeFactory.createProgram(body));
Expand Down Expand Up @@ -5080,7 +5356,7 @@ function parse(code, options) {
};

extra = {
ecmaFeatures: defaultFeatures
ecmaFeatures: Object.create(defaultFeatures)
};

// for template strings
Expand Down Expand Up @@ -5116,6 +5392,25 @@ function parse(code, options) {
extra.trailingComments = [];
extra.leadingComments = [];
}
if (options.sourceType === "module") {
// TODO: enable all other ES6 features that are supported in modules
extra.ecmaFeatures.blockBindings = true;
extra.ecmaFeatures.regexUFlag = true;
extra.ecmaFeatures.regexYFlag = true;
extra.ecmaFeatures.templateStrings = true;
extra.ecmaFeatures.binaryLiterals = true;
extra.ecmaFeatures.octalLiterals = true;
extra.ecmaFeatures.unicodeCodePointEscapes = true;
extra.ecmaFeatures.defaultParams = true;
extra.ecmaFeatures.forOf = true;
extra.ecmaFeatures.objectLiteralComputedProperties = true;
extra.ecmaFeatures.objectLiteralShorthandMethods = true;
extra.ecmaFeatures.objectLiteralShorthandProperties = true;
extra.ecmaFeatures.objectLiteralDuplicateProperties = true;
extra.ecmaFeatures.generators = true;
extra.isModule = true;
// TODO: enable classes when it becomes available
}
}

try {
Expand Down
Loading

0 comments on commit 7d4a819

Please sign in to comment.