Skip to content

Commit

Permalink
feat: support array destructing, close #34
Browse files Browse the repository at this point in the history
  • Loading branch information
cxtom committed May 7, 2019
1 parent 1b3ac62 commit f94f8a8
Show file tree
Hide file tree
Showing 6 changed files with 114 additions and 88 deletions.
98 changes: 33 additions & 65 deletions src/emitter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -290,10 +290,10 @@ export function emitFile(
// // Binding patterns
// case SyntaxKind.ObjectBindingPattern:
// return emitObjectBindingPattern(<ts.ObjectBindingPattern>node);
// case SyntaxKind.ArrayBindingPattern:
// return emitArrayBindingPattern(<ArrayBindingPattern>node);
// case SyntaxKind.BindingElement:
// return emitBindingElement(<BindingElement>node);
case SyntaxKind.ArrayBindingPattern:
return emitArrayBindingPattern(<ts.ArrayBindingPattern>node);
case SyntaxKind.BindingElement:
return emitBindingElement(<ts.BindingElement>node);

// Misc
case SyntaxKind.TemplateSpan:
Expand Down Expand Up @@ -544,8 +544,8 @@ export function emitFile(
// return emitSpreadExpression(<ts.SpreadElement>node);
case SyntaxKind.ClassExpression:
return emitClassExpression(<ts.ClassExpression>node);
// case SyntaxKind.OmittedExpression:
// return;
case SyntaxKind.OmittedExpression:
return;
case SyntaxKind.AsExpression:
return emitAsExpression(<ts.AsExpression>node);
// case SyntaxKind.NonNullExpression:
Expand Down Expand Up @@ -1097,22 +1097,22 @@ export function emitFile(
// writePunctuation("}");
// }

// function emitArrayBindingPattern(node: ArrayBindingPattern) {
// writePunctuation("[");
// emitList(node, node.elements, ListFormat.ArrayBindingPatternElements);
// writePunctuation("]");
// }
function emitArrayBindingPattern(node: ts.ArrayBindingPattern) {
writePunctuation("list(");
emitList(node, node.elements, ListFormat.ArrayBindingPatternElements);
writePunctuation(")");
}

// function emitBindingElement(node: BindingElement) {
// emit(node.dotDotDotToken);
// if (node.propertyName) {
// emit(node.propertyName);
// writePunctuation(":");
// writeSpace();
// }
// emit(node.name);
// emitInitializer(node.initializer, node.name.end, node);
// }
function emitBindingElement(node: ts.BindingElement) {
// emit(node.dotDotDotToken);
// if (node.propertyName) {
// emit(node.propertyName);
// writePunctuation(":");
// writeSpace();
// }
emit(node.name);
// emitInitializer(node.initializer, node.name.end, node);
}

// //
// // Expressions
Expand Down Expand Up @@ -1645,47 +1645,9 @@ export function emitFile(
// //

function emitVariableDeclaration(node: ts.VariableDeclaration) {
if (ts.isObjectBindingPattern(node.name)) {
const count = node.name.elements.length;
let initializer = node.initializer;
forEach(node.name.elements, (element, index) => {

if (ts.isIdentifier(element.name)) {

const nameNode = element.name;
const access = ts.createPropertyAccess(
initializer,
element.propertyName ? getTextOfNode(element.propertyName) : nameNode
);

writeBase("$");
emit(nameNode);
writeBase(" = ");

if (element.initializer) {
writeBase('isset(');
}

emitExpression(access);

if (element.initializer && ts.isLiteralExpression(element.initializer)) {
writeBase(') ? ');
emitExpression(access);
writeBase(' : ');
emitLiteral(element.initializer);
}
}
if (index < count - 1) {
writeSemicolon();
writeLine();
}
});
}
else {
emit(node.name);
// emitTypeAnnotation(node.type);
emitInitializer(node.initializer, node.type ? node.type.end : node.name.end, node);
}
emit(node.name);
// emitTypeAnnotation(node.type);
emitInitializer(node.initializer, node.type ? node.type.end : node.name.end, node);
}

function emitVariableDeclarationList(node: ts.VariableDeclarationList) {
Expand Down Expand Up @@ -2822,6 +2784,12 @@ export function emitFile(
}

function writeDelimiter(format: ts.ListFormat) {

if (format === ts.ListFormat.VariableDeclarationList) {
writePunctuation(";");
return;
}

switch (format & ts.ListFormat.DelimitersMask) {
case ts.ListFormat.None:
break;
Expand Down Expand Up @@ -2871,7 +2839,7 @@ export function emitFile(
else {
// Write the opening line terminator or leading whitespace.
const mayEmitInterveningComments = (format & ts.ListFormat.NoInterveningComments) === 0;
let shouldEmitInterveningComments = mayEmitInterveningComments;
// let shouldEmitInterveningComments = mayEmitInterveningComments;
if (shouldWriteLeadingLineTerminator(parentNode, children!, format)) { // TODO: GH#18217
writeLine();
// shouldEmitInterveningComments = false;
Expand All @@ -2887,7 +2855,7 @@ export function emitFile(

// Emit each child.
let previousSibling: ts.Node | undefined;
let shouldDecreaseIndentAfterEmit = false;
// let shouldDecreaseIndentAfterEmit = false;
for (let i = 0; i < count; i++) {
const child = children![start + i];

Expand All @@ -2912,7 +2880,7 @@ export function emitFile(
// Write either a line terminator or whitespace to separate the elements.
if (shouldWriteSeparatingLineTerminator(previousSibling, child, format)) {
writeLine();
shouldEmitInterveningComments = false;
// shouldEmitInterveningComments = false;
}
else if (previousSibling && format & ts.ListFormat.SpaceBetweenSiblings) {
writeSpace();
Expand Down
87 changes: 71 additions & 16 deletions src/transformer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,21 +107,7 @@ export function transform(context: ts.TransformationContext) {
* @param node The node to visit.
*/
function sourceElementVisitor(node: ts.Node): ts.VisitResult<ts.Node> {
return saveStateAndInvoke(node, visitorWorker);
}

/**
* Visits and possibly transforms any node.
*
* @param node The node to visit.
*/
function visitorWorker(node: ts.Node): ts.VisitResult<ts.Node> {
if (node.transformFlags & ts.TransformFlags.TypeScript) {
// This node is explicitly marked as TypeScript, so we should transform the node.
return visitTypeScript(node);
}

return node;
return saveStateAndInvoke(node, visitTypeScript);
}

/**
Expand Down Expand Up @@ -150,12 +136,81 @@ export function transform(context: ts.TransformationContext) {
case ts.SyntaxKind.CallExpression:
return visitCallExpression(<ts.CallExpression>node);

case ts.SyntaxKind.VariableDeclarationList:
return visitVariableDeclarationList(<ts.VariableDeclarationList>node);

default:
return node;
}

}

function visitVariableDeclarationList(node: ts.VariableDeclarationList) {

const pendingMap = new Map();

node.declarations.forEach((declaration, index) => {

const nameNode = declaration.name;

if (!ts.isObjectBindingPattern(nameNode) && !ts.isArrayBindingPattern(nameNode)) {
return;
}

const initializer = declaration.initializer;
const pendingList = [] as ts.VariableDeclaration[];
const isArray = ts.isArrayBindingPattern(nameNode);

nameNode.elements.forEach((child, index) => {

if (!ts.isBindingElement(child)) {
return;
}

const accessName = child.propertyName || child.name;

const access = ts.createElementAccess(
initializer,
isArray ? index : ts.createStringLiteral(accessName.getText())
);

const variable = ts.createVariableDeclaration(
child.name, undefined,
child.initializer
? ts.createConditional(
ts.createCall(ts.createIdentifier('isset'), [], [access]),
access,
child.initializer
)
: access
);

pendingList.push(variable);
});

pendingMap.set(index, pendingList);

});

if (!pendingMap.size) {
return node;
}

let newDeclarations = [];
let start = 0

pendingMap.forEach((declarations, index) => {
newDeclarations = [
...newDeclarations,
...node.declarations.slice(start, index - start),
...declarations
];
start = index + 1;
});

return ts.updateVariableDeclarationList(node, newDeclarations);
}

function visitCallExpression(node: ts.CallExpression) {

const index = node.arguments.findIndex(node => ts.isSpreadElement(node))
Expand Down Expand Up @@ -406,7 +461,7 @@ export function transform(context: ts.TransformationContext) {
* @param node The node to visit.
*/
function visitor(node: ts.Node): ts.VisitResult<ts.Node> {
return saveStateAndInvoke(node, visitorWorker);
return saveStateAndInvoke(node, visitTypeScript);
}

}
Expand Down
6 changes: 4 additions & 2 deletions src/utilities/nodeTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ const shouldAddDollerParentList = new Set([
SyntaxKind.ImportSpecifier,
SyntaxKind.PropertyDeclaration,
SyntaxKind.EnumDeclaration,
SyntaxKind.ArrowFunction
SyntaxKind.ArrowFunction,
SyntaxKind.BindingElement
]);

/**
Expand Down Expand Up @@ -99,7 +100,8 @@ const newLineArrayBrackets = ["array(", ")"];
const arrayBracketsMap = {
[ts.ListFormat.ObjectLiteralExpressionProperties]: baseArrayBrackets,
[ts.ListFormat.ObjectLiteralExpressionProperties | ts.ListFormat.PreferNewLine]: newLineArrayBrackets,
[ts.ListFormat.ArrayLiteralExpressionElements]: baseArrayBrackets
[ts.ListFormat.ArrayLiteralExpressionElements]: baseArrayBrackets,
[ts.ListFormat.ArrayLiteralExpressionElements | ts.ListFormat.PreferNewLine]: newLineArrayBrackets
};
/**
* 判断是否需要转换成 array()
Expand Down
5 changes: 3 additions & 2 deletions test/features/ArrayLiteralExpression.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@
namespace test\case_ArrayLiteralExpression;
$b = 4;
$a = array(1, 2, 3, $b);
$c = [
$c = array(
1,
2,
3,
$b
];
);
$e = $a[1]; $f = $a[2];
2 changes: 2 additions & 0 deletions test/features/ArrayLiteralExpression.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,5 @@ let c = [
3,
b
];

const [, e, f] = a;
4 changes: 1 addition & 3 deletions test/features/Destructuring.php
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
<?php
namespace test\case_Destructuring;
$tplData = array( "a" => 1 );
$difftime = isset($tplData["difftime"]) ? $tplData["difftime"] : 8;
$a = $tplData["a"];
$y = isset($tplData["c"]) ? $tplData["c"] : 1;
$difftime = isset($tplData["difftime"]) ? $tplData["difftime"] : 8; $a = $tplData["a"]; $y = isset($tplData["c"]) ? $tplData["c"] : 1;
$c = $tplData["a"];

0 comments on commit f94f8a8

Please sign in to comment.