traceur
Version:
ES6 to ES5 compiler
846 lines (754 loc) • 21.5 kB
JavaScript
// Copyright 2012 Traceur Authors.
//
// Licensed under the Apache License, Version 2.0 (the 'License');
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an 'AS IS' BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import {IdentifierToken} from '../syntax/IdentifierToken.js';
import {LiteralToken} from '../syntax/LiteralToken.js';
import {
ParseTree,
ParseTreeType
} from '../syntax/trees/ParseTree.js';
import {
CALL,
CREATE,
DEFINE_PROPERTY,
FREEZE,
OBJECT,
UNDEFINED
} from '../syntax/PredefinedName.js';
import {Token} from '../syntax/Token.js';
import {
EQUAL,
FALSE,
NULL,
NUMBER,
STRING,
TRUE,
VOID
} from '../syntax/TokenType.js';
import {assert} from '../util/assert.js';
import {
ArgumentList,
ArrayLiteral,
BindingElement,
BinaryExpression,
BindingIdentifier,
Block,
BreakStatement,
CallExpression,
CaseClause,
Catch,
ClassDeclaration,
CommaExpression,
ConditionalExpression,
ContinueStatement,
DefaultClause,
DoWhileStatement,
EmptyStatement,
ExpressionStatement,
Finally,
ForInStatement,
ForOfStatement,
ForStatement,
FormalParameter,
FormalParameterList,
FunctionBody,
FunctionExpression,
IdentifierExpression,
IfStatement,
ImportedBinding,
LiteralExpression,
LiteralPropertyName,
MemberExpression,
MemberLookupExpression,
NewExpression,
ObjectLiteral,
ParenExpression,
PostfixExpression,
Script,
PropertyNameAssignment,
RestParameter,
ReturnStatement,
SpreadExpression,
SwitchStatement,
ThisExpression,
ThrowStatement,
TryStatement,
UnaryExpression,
VariableDeclaration,
VariableDeclarationList,
VariableStatement,
WhileStatement,
WithStatement
} from '../syntax/trees/ParseTrees.js';
// Helpers so we can use these on Arguments objects.
let slice = Array.prototype.slice.call.bind(Array.prototype.slice);
let map = Array.prototype.map.call.bind(Array.prototype.map);
// Tokens
/**
* @param {TokenType} operator
* @return {Token}
*/
export function createOperatorToken(operator) {
return new Token(operator, null);
}
/**
* @param {string} identifier
* @return {IdentifierToken}
*/
export function createIdentifierToken(identifier) {
return new IdentifierToken(null, identifier);
}
export function createStringLiteralToken(value) {
return new LiteralToken(STRING, JSON.stringify(value), null);
}
export function createBooleanLiteralToken(value) {
return new Token(value ? TRUE : FALSE, null);
}
export function createNullLiteralToken() {
return new LiteralToken(NULL, 'null', null);
}
export function createNumberLiteralToken(value) {
return new LiteralToken(NUMBER, String(value), null);
}
// Trees
/**
* @return {FormalParameterList}
*/
export function createEmptyParameterList() {
return new FormalParameterList(null, []);
}
export function createFormalParameter(name) {
let bindingIdentifier = createBindingIdentifier(name);
return new FormalParameter(
null,
new BindingElement(null, bindingIdentifier, null),
null,
[]);
}
/**
* @param {Array.<ParseTree>} list
* @return {ArgumentList}
*/
export function createArgumentList(list) {
return new ArgumentList(null, list);
}
/**
* @return {ArgumentList}
*/
export function createEmptyArgumentList() {
return createArgumentList([]);
}
/**
* @param {Array.<ParseTree>} list
* @return {ArrayLiteral}
*/
export function createArrayLiteral(list) {
return new ArrayLiteral(null, list);
}
/**
* @return {ArrayLiteral}
*/
export function createEmptyArrayLiteral() {
return createArrayLiteral([]);
}
/**
* @param {ParseTree} lhs
* @param {ParseTree} rhs
* @return {BinaryExpression}
*/
export function createAssignmentExpression(lhs, rhs) {
return new BinaryExpression(null, lhs,
createOperatorToken(EQUAL), rhs);
}
/**
* @return {BinaryExpression}
*/
export function createBinaryExpression(left, operator, right) {
return new BinaryExpression(null, left, operator, right);
}
/**
* @param {string|IdentifierToken|IdentifierExpression|BindingIdentifier}
* identifier
* @return {BindingIdentifier}
*/
export function createBindingIdentifier(identifier) {
if (typeof identifier === 'string')
identifier = createIdentifierToken(identifier);
else if (identifier.type === ParseTreeType.BINDING_IDENTIFIER)
return identifier;
else if (identifier.type === ParseTreeType.IDENTIFIER_EXPRESSION)
return new BindingIdentifier(identifier.location,
identifier.identifierToken);
return new BindingIdentifier(null, identifier);
}
export function createImportedBinding(name) {
let bindingIdentifier = createBindingIdentifier(name);
return new ImportedBinding(bindingIdentifier.location, bindingIdentifier);
}
/**
* @return {EmptyStatement}
*/
export function createEmptyStatement() {
return new EmptyStatement(null);
}
/**
* @return {Block}
*/
export function createEmptyBlock() {
return createBlock([]);
}
/**
* @param {Array.<ParseTree>} statements
* @return {Block}
*/
export function createBlock(statements) {
return new Block(null, statements);
}
/**
* @param {Array.<ParseTree>} statements
* @return {FunctionBody}
*/
export function createFunctionBody(statements) {
return new FunctionBody(null, statements);
}
/**
* @param {FunctionBody} body
* @return {CallExpression}
*/
export function createScopedExpression(body, scope) {
assert(body.type === 'FUNCTION_BODY');
return createCallCall(
createParenExpression(
createFunctionExpression(createEmptyParameterList(), body)),
scope);
}
/**
* @param {FunctionBody} body
* @return {CallExpression}
*/
export function createImmediatelyInvokedFunctionExpression(body) {
assert(body.type === 'FUNCTION_BODY');
return createCallExpression(createParenExpression(
createFunctionExpression(createEmptyParameterList(), body)));
}
/**
* @param {ParseTree} operand
* @param {ArgumentList=} args
* @return {CallExpression}
*/
export function createCallExpression(operand,
args = createEmptyArgumentList()) {
return new CallExpression(null, operand, args);
}
/**
* @return {BreakStatement}
*/
export function createBreakStatement(name = null) {
return new BreakStatement(null, name);
}
/**
* @param {ParseTree} func
* @param {ParseTree} thisExpression
* @return {CallExpression}
*/
function createCallCall(func, thisExpression) {
return createCallExpression(
createMemberExpression(func, CALL),
createArgumentList([thisExpression]));
}
/**
* @param {ParseTree} expression
* @param {Array.<ParseTree>} statements
* @return {CaseClause}
*/
export function createCaseClause(expression, statements) {
return new CaseClause(null, expression, statements);
}
/**
* @param {BindingIdentifier|IdentifierToken} identifier
* @param {ParseTree} catchBody
* @return {Catch}
*/
export function createCatch(identifier, catchBody) {
identifier = createBindingIdentifier(identifier);
return new Catch(null, identifier, catchBody);
}
/**
* @param {IdentifierToken} name
* @param {ParseTree} superClass
* @param {Array.<ParseTree>} elements
* @return {ClassDeclaration}
*/
export function createClassDeclaration(name, superClass, elements) {
return new ClassDeclaration(null, name, superClass, elements, []);
}
/**
* @param {Array.<ParseTree>} expressions
* @return {CommaExpression}
*/
export function createCommaExpression(expressions) {
return new CommaExpression(null, expressions);
}
/**
* @param {ParseTree} condition
* @param {ParseTree} left
* @param {ParseTree} right
* @return {ConditionalExpression}
*/
export function createConditionalExpression(condition, left, right) {
return new ConditionalExpression(null, condition, left, right);
}
/**
* @return {ContinueStatement}
*/
export function createContinueStatement(name = null) {
return new ContinueStatement(null, name);
}
/**
* @param {Array.<ParseTree>} statements
* @return {DefaultClause}
*/
export function createDefaultClause(statements) {
return new DefaultClause(null, statements);
}
/**
* @param {ParseTree} body
* @param {ParseTree} condition
* @return {DoWhileStatement}
*/
export function createDoWhileStatement(body, condition) {
return new DoWhileStatement(null, body, condition);
}
/**
* @param {ParseTree} lhs
* @param {ParseTree} rhs
* @return {ExpressionStatement}
*/
export function createAssignmentStatement(lhs, rhs) {
return createExpressionStatement(createAssignmentExpression(lhs, rhs));
}
/**
* @param {ParseTree} operand
* @param {ArgumentList=} args
* @return {ExpressionStatement}
*/
export function createCallStatement(operand, args = undefined) {
return createExpressionStatement(createCallExpression(operand, args));
}
/**
* @param {ParseTree} expression
* @return {ExpressionStatement}
*/
export function createExpressionStatement(expression) {
return new ExpressionStatement(null, expression);
}
/**
* @param {ParseTree} block
* @return {Finally}
*/
export function createFinally(block) {
return new Finally(null, block);
}
/**
* @param {VariableDeclarationList} initializer
* @param {ParseTree} collection
* @param {ParseTree} body
* @return {ForOfStatement}
*/
export function createForOfStatement(initializer, collection, body) {
return new ForOfStatement(null, initializer, collection, body);
}
/**
* @param {ParseTree} initializer
* @param {ParseTree} collection
* @param {ParseTree} body
* @return {ForInStatement}
*/
export function createForInStatement(initializer, collection, body) {
return new ForInStatement(null, initializer, collection, body);
}
/**
* @param {ParseTree} variables
* @param {ParseTree} condition
* @param {ParseTree} increment
* @param {ParseTree} body
* @return {ForStatement}
*/
export function createForStatement(variables, condition, increment, body) {
return new ForStatement(null, variables, condition, increment, body);
}
/**
* @param {FormalParameterList} parameterList
* @param {FunctionBody} body
* @return {FunctionExpression}
*/
export function createFunctionExpression(parameterList, body) {
assert(body.type === 'FUNCTION_BODY');
return new FunctionExpression(null, null, false,
parameterList, null, [], body);
}
/**
* @param {string|IdentifierToken} identifier
* @return {IdentifierExpression}
*/
export function createIdentifierExpression(identifier) {
if (typeof identifier === 'string')
identifier = createIdentifierToken(identifier);
else if (identifier instanceof BindingIdentifier)
identifier = identifier.identifierToken;
return new IdentifierExpression(null, identifier);
}
/**
* @return {IdentifierExpression}
*/
export function createUndefinedExpression() {
return createIdentifierExpression(UNDEFINED);
}
/**
* @param {ParseTree} condition
* @param {ParseTree} ifClause
* @param {ParseTree=} elseClause
* @return {IfStatement}
*/
export function createIfStatement(condition, ifClause, elseClause = null) {
return new IfStatement(null, condition, ifClause, elseClause);
}
/**
* @param {string} value
* @return {ParseTree}
*/
export function createStringLiteral(value) {
return new LiteralExpression(null, createStringLiteralToken(value));
}
/**
* @param {boolean} value
* @return {ParseTree}
*/
export function createBooleanLiteral(value) {
return new LiteralExpression(null, createBooleanLiteralToken(value));
}
/**
* @return {ParseTree}
*/
export function createTrueLiteral() {
return createBooleanLiteral(true);
}
/**
* @return {ParseTree}
*/
export function createFalseLiteral() {
return createBooleanLiteral(false);
}
/**
* @return {ParseTree}
*/
export function createNullLiteral() {
return new LiteralExpression(null, createNullLiteralToken());
}
/**
* @param {number} value
* @return {ParseTree}
*/
export function createNumberLiteral(value) {
return new LiteralExpression(null, createNumberLiteralToken(value));
}
/**
* Creates 'operand.memberName' or 'operand[memberName]' if memberName
* is a LiteralToken or LiteralExpression.
*
* @param {string|IdentifierToken|ParseTree} operand
* @param {string|IdentifierToken|LiteralToken|LiteralExpression} memberName
* @param {...string|IdentifierToken} memberNames
* @return {MemberExpression|MemberLookupExpression}
*/
export function createMemberExpression(operand, memberName, ...memberNames) {
if (typeof operand === 'string' || operand instanceof IdentifierToken)
operand = createIdentifierExpression(operand);
if (typeof memberName === 'string')
memberName = createIdentifierToken(memberName);
if (memberName instanceof LiteralToken)
memberName = new LiteralExpression(null, memberName);
let tree = memberName instanceof LiteralExpression ?
new MemberLookupExpression(null, operand, memberName) :
new MemberExpression(null, operand, memberName);
for (let i = 0; i < memberNames.length; i++) {
tree = createMemberExpression(tree, memberNames[i]);
}
return tree;
}
/**
* @return {MemberLookupExpression}
*/
export function createMemberLookupExpression(operand, memberExpression) {
return new MemberLookupExpression(null, operand, memberExpression);
}
/**
* @return {ParseTree}
*/
export function createThisExpression() {
return new ThisExpression(null);
}
/**
* @param {ParseTree} operand
* @param {ArgumentList} args
* @return {NewExpression}
*/
export function createNewExpression(operand, args) {
return new NewExpression(null, operand, args);
}
/**
* @param {ParseTree} value
* @return {ParseTree}
*/
export function createObjectFreeze(value) {
// Object.freeze(value)
return createCallExpression(
createMemberExpression(OBJECT, FREEZE),
createArgumentList([value]));
}
/**
* @param {ParseTree} protoExpression
* @param {ObjectLiteral=} descriptors
* @return {ParseTree}
*/
export function createObjectCreate(protoExpression, descriptors = undefined) {
let argumentList = [protoExpression];
if (descriptors)
argumentList.push(descriptors);
return createCallExpression(
createMemberExpression(OBJECT,
CREATE),
createArgumentList(argumentList));
}
/**
* Creates an object literal tree representing a property descriptor.
* @param {Object} descr This is a normal js object. The values in the descr
* may be true, false or a ParseTree.
* @return {ObjectLiteral}
*/
export function createObjectLiteralForDescriptor(descr) {
let propertyNameAndValues = Object.keys(descr).map(function(name) {
let value = descr[name];
if (!(value instanceof ParseTree))
value = createBooleanLiteral(!!value);
return createPropertyNameAssignment(name, value);
});
return createObjectLiteral(propertyNameAndValues);
}
/**
* Creates a call expression to Object.defineProperty(tree, name, descr).
*
* @param {ParseTree} tree
* @param {string|ParseTree} name
* @param {Object} descr This is a normal js object. The values in the descr
* may be true, false or a ParseTree.
* @return {ParseTree}
*/
export function createDefineProperty(tree, name, descr) {
if (typeof name === 'string')
name = createStringLiteral(name);
return createCallExpression(
createMemberExpression(OBJECT, DEFINE_PROPERTY),
createArgumentList([
tree,
name,
createObjectLiteralForDescriptor(descr)
]));
}
/**
* @param {Array.<ParseTree>} propertyNameAndValues
* @return {ObjectLiteral}
*/
export function createObjectLiteral(propertyNameAndValues) {
return new ObjectLiteral(null, propertyNameAndValues);
}
/**
* @param {ParseTree} expression
* @return {ParenExpression}
*/
export function createParenExpression(expression) {
return new ParenExpression(null, expression);
}
/**
* @param {ParseTree} operand
* @param {ParseTree} operator
* @return {PostfixExpression}
*/
export function createPostfixExpression(operand, operator) {
return new PostfixExpression(null, operand, operator);
}
/**
* @param {Array.<ParseTree>} scriptItemList
* @return {Script}
*/
export function createScript(scriptItemList) {
return new Script(null, scriptItemList, null);
}
/**
* @param {string|ParseTree} identifier
* @param {ParseTree} value
* @return {PropertyNameAssignment}
*/
export function createPropertyNameAssignment(identifier, value) {
if (typeof identifier === 'string')
identifier = createLiteralPropertyName(identifier);
return new PropertyNameAssignment(null, identifier, value);
}
/**
* @param {string} name
* @return {LiteralPropertyName}
*/
export function createLiteralPropertyName(name) {
return new LiteralPropertyName(null, createIdentifierToken(name));
}
/**
* @param {string|IdentifierToken|BindingIdentifier} identifier
* @return {RestParameter}
*/
export function createRestParameter(identifier) {
let rest = new RestParameter(null, createBindingIdentifier(identifier));
return new FormalParameter(null, rest, null, []);
}
/**
* @param {ParseTree} expression
* @return {ReturnStatement}
*/
export function createReturnStatement(expression) {
return new ReturnStatement(null, expression);
}
/**
* @param {ParseTree} expression
* @return {SpreadExpression}
*/
function createSpreadExpression(expression) {
return new SpreadExpression(null, expression);
}
/**
* @param {ParseTree} expression
* @param {Array.<ParseTree>} caseClauses
* @return {SwitchStatement}
*/
export function createSwitchStatement(expression, caseClauses) {
return new SwitchStatement(null, expression, caseClauses);
}
/**
* @param {ParseTree} value
* @return {ThrowStatement}
*/
export function createThrowStatement(value) {
return new ThrowStatement(null, value);
}
/**
* @param {ParseTree} body
* @param {ParseTree} catchBlock
* @param {ParseTree=} finallyBlock
* @return {TryStatement}
*/
export function createTryStatement(body, catchBlock, finallyBlock = null) {
return new TryStatement(null, body, catchBlock, finallyBlock);
}
/**
* @param {Token} operator
* @param {ParseTree} operand
* @return {UnaryExpression}
*/
export function createUnaryExpression(operator, operand) {
return new UnaryExpression(null, operator, operand);
}
/**
* @return {ParseTree}
*/
export function createUseStrictDirective() {
return createExpressionStatement(createStringLiteral('use strict'));
}
/**
* @param {TokenType} binding
* @param {IdentifierToken|Array.<VariableDeclaration>} identifierOrDeclarations
* @param {ParseTree=} initializer
* @return {VariableDeclarationList}
*/
export function createVariableDeclarationList(binding,
identifierOrDeclarations,
initializer = undefined) {
if (identifierOrDeclarations instanceof Array) {
let declarations = identifierOrDeclarations;
return new VariableDeclarationList(null, binding, declarations);
}
let identifier = identifierOrDeclarations;
return createVariableDeclarationList(
binding, [createVariableDeclaration(identifier, initializer)]);
}
/**
* @param {string|IdentifierToken|ParseTree} identifier
* @param {ParseTree} initializer
* @return {VariableDeclaration}
*/
export function createVariableDeclaration(identifier, initializer) {
if (!(identifier instanceof ParseTree) ||
identifier.type !== ParseTreeType.BINDING_IDENTIFIER &&
identifier.type !== ParseTreeType.OBJECT_PATTERN &&
identifier.type !== ParseTreeType.ARRAY_PATTERN) {
identifier = createBindingIdentifier(identifier);
}
return new VariableDeclaration(null, identifier, null, initializer);
}
/**
* @param {VariableDeclarationList|TokenType} listOrBinding
* @param {string|IdentifierToken=} identifier
* @param {ParseTree=} initializer
* @return {VariableStatement}
*/
export function createVariableStatement(listOrBinding,
identifier = undefined,
initializer = undefined) {
if (listOrBinding instanceof VariableDeclarationList)
return new VariableStatement(null, listOrBinding);
let binding = listOrBinding;
let list = createVariableDeclarationList(binding, identifier, initializer);
return createVariableStatement(list);
}
/**
* Creates a (void 0) expression.
* @return {ParenExpression}
*/
export function createVoid0() {
return createParenExpression(
createUnaryExpression(
createOperatorToken(VOID),
createNumberLiteral(0)));
}
/**
* @param {ParseTree} condition
* @param {ParseTree} body
* @return {WhileStatement}
*/
export function createWhileStatement(condition, body) {
return new WhileStatement(null, condition, body);
}
/**
* @param {ParseTree} expression
* @param {ParseTree} body
* @return {WithStatement}
*/
export function createWithStatement(expression, body) {
return new WithStatement(null, expression, body);
}
/**
* @param {number} state
* @return {ExpressionStatement}
*/
export function createAssignStateStatement(state) {
return createAssignmentStatement(
createMemberExpression('$ctx', 'state'),
createNumberLiteral(state));
}