mirror of
https://github.com/mdgriffith/elm-optimize-level-2.git
synced 2024-12-01 19:35:08 +03:00
Merge pull request #6 from mdgriffith/modernize-js-and-fixes
added arrow func transformation and native object merge
This commit is contained in:
commit
3d73f2ffe9
@ -15,6 +15,11 @@ import {
|
|||||||
createInlineListFromArrayTransformer,
|
createInlineListFromArrayTransformer,
|
||||||
} from './experiments/inlineListFromArray';
|
} from './experiments/inlineListFromArray';
|
||||||
|
|
||||||
|
import {
|
||||||
|
replaceUtilsUpdateWithObjectSpread,
|
||||||
|
convertFunctionExpressionsToArrowFuncs,
|
||||||
|
} from './experiments/modernizeJS';
|
||||||
|
|
||||||
// Compile examples in `testcases/*` folder as js
|
// Compile examples in `testcases/*` folder as js
|
||||||
// Run whatever transformations we want on them, saving steps as `elm.{transformation}.js`
|
// Run whatever transformations we want on them, saving steps as `elm.{transformation}.js`
|
||||||
compile(['Main.elm'], {
|
compile(['Main.elm'], {
|
||||||
@ -54,9 +59,9 @@ const customTypeTransformer = createCustomTypesTransformer(
|
|||||||
Mode.Prod
|
Mode.Prod
|
||||||
);
|
);
|
||||||
|
|
||||||
const collectedSplits: FuncSplit[] = [];
|
const collectedSplits = new Map<string, FuncSplit>();
|
||||||
const splitTransformer = createSplitFunctionDeclarationsTransformer(split =>
|
const splitTransformer = createSplitFunctionDeclarationsTransformer(
|
||||||
collectedSplits.push(split)
|
collectedSplits
|
||||||
);
|
);
|
||||||
|
|
||||||
const funcInlineTransformer = createFuncInlineTransformer(collectedSplits);
|
const funcInlineTransformer = createFuncInlineTransformer(collectedSplits);
|
||||||
@ -70,6 +75,8 @@ const [result] = ts.transform(source, [
|
|||||||
splitTransformer,
|
splitTransformer,
|
||||||
funcInlineTransformer,
|
funcInlineTransformer,
|
||||||
inlineListFromArrayCalls,
|
inlineListFromArrayCalls,
|
||||||
|
replaceUtilsUpdateWithObjectSpread,
|
||||||
|
convertFunctionExpressionsToArrowFuncs,
|
||||||
]).transformed;
|
]).transformed;
|
||||||
|
|
||||||
const printer = ts.createPrinter();
|
const printer = ts.createPrinter();
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import ts from 'typescript';
|
import ts from 'typescript';
|
||||||
|
|
||||||
export type FuncSplit = {
|
export type FuncSplit = {
|
||||||
originalName: string;
|
|
||||||
rawLambdaName: string;
|
rawLambdaName: string;
|
||||||
arity: number;
|
arity: number;
|
||||||
};
|
};
|
||||||
@ -12,12 +11,18 @@ const deriveRawLambdaName = (wrappedName: string): string =>
|
|||||||
const wrapperRegex = /F(?<arity>[1-9]+[0-9]*)/;
|
const wrapperRegex = /F(?<arity>[1-9]+[0-9]*)/;
|
||||||
|
|
||||||
export const createSplitFunctionDeclarationsTransformer = (
|
export const createSplitFunctionDeclarationsTransformer = (
|
||||||
reportSplit: (split: FuncSplit) => void
|
splits: Map<string, FuncSplit>
|
||||||
): ts.TransformerFactory<ts.SourceFile> => context => {
|
): ts.TransformerFactory<ts.SourceFile> => context => {
|
||||||
return sourceFile => {
|
return sourceFile => {
|
||||||
const visitor = (node: ts.Node): ts.VisitResult<ts.Node> => {
|
const visitor = (node: ts.Node): ts.VisitResult<ts.Node> => {
|
||||||
// detects "var a"
|
// detects "var a"
|
||||||
if (ts.isVariableDeclaration(node) && ts.isIdentifier(node.name)) {
|
if (ts.isVariableDeclaration(node) && ts.isIdentifier(node.name)) {
|
||||||
|
if (node.initializer && ts.isIdentifier(node.initializer)) {
|
||||||
|
const existingSplit = splits.get(node.initializer.text);
|
||||||
|
if (existingSplit) {
|
||||||
|
splits.set(node.name.text, existingSplit);
|
||||||
|
}
|
||||||
|
}
|
||||||
// detects "var a = [exp](..)"
|
// detects "var a = [exp](..)"
|
||||||
if (node.initializer && ts.isCallExpression(node.initializer)) {
|
if (node.initializer && ts.isCallExpression(node.initializer)) {
|
||||||
const callExpression = node.initializer.expression;
|
const callExpression = node.initializer.expression;
|
||||||
@ -31,6 +36,16 @@ export const createSplitFunctionDeclarationsTransformer = (
|
|||||||
if (args.length === 1) {
|
if (args.length === 1) {
|
||||||
const [maybeFuncExpression] = args;
|
const [maybeFuncExpression] = args;
|
||||||
|
|
||||||
|
const arity = Number(maybeMatch.groups.arity);
|
||||||
|
const originalName = node.name.text;
|
||||||
|
|
||||||
|
if (ts.isIdentifier(maybeFuncExpression)) {
|
||||||
|
splits.set(originalName, {
|
||||||
|
arity,
|
||||||
|
rawLambdaName: maybeFuncExpression.text,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// and it is a function
|
// and it is a function
|
||||||
// detects "var a = F123( function (a) {return a})"
|
// detects "var a = F123( function (a) {return a})"
|
||||||
// or "var a = F123( a => a)"
|
// or "var a = F123( a => a)"
|
||||||
@ -39,11 +54,12 @@ export const createSplitFunctionDeclarationsTransformer = (
|
|||||||
ts.isFunctionExpression(maybeFuncExpression)
|
ts.isFunctionExpression(maybeFuncExpression)
|
||||||
) {
|
) {
|
||||||
// TODO typecheck?
|
// TODO typecheck?
|
||||||
const arity = Number(maybeMatch.groups.arity);
|
|
||||||
const originalName = node.name.text;
|
|
||||||
const rawLambdaName = deriveRawLambdaName(originalName);
|
const rawLambdaName = deriveRawLambdaName(originalName);
|
||||||
|
|
||||||
reportSplit({ arity, originalName, rawLambdaName });
|
splits.set(originalName, {
|
||||||
|
arity,
|
||||||
|
rawLambdaName,
|
||||||
|
});
|
||||||
|
|
||||||
const lambdaDeclaration = ts.createVariableDeclaration(
|
const lambdaDeclaration = ts.createVariableDeclaration(
|
||||||
rawLambdaName,
|
rawLambdaName,
|
||||||
@ -78,7 +94,7 @@ export const createSplitFunctionDeclarationsTransformer = (
|
|||||||
const invocationRegex = /A(?<arity>[1-9]+[0-9]*)/;
|
const invocationRegex = /A(?<arity>[1-9]+[0-9]*)/;
|
||||||
|
|
||||||
export const createFuncInlineTransformer = (
|
export const createFuncInlineTransformer = (
|
||||||
splits: FuncSplit[]
|
splits: Map<string, FuncSplit>
|
||||||
): ts.TransformerFactory<ts.SourceFile> => context => {
|
): ts.TransformerFactory<ts.SourceFile> => context => {
|
||||||
return sourceFile => {
|
return sourceFile => {
|
||||||
const visitor = (node: ts.Node): ts.VisitResult<ts.Node> => {
|
const visitor = (node: ts.Node): ts.VisitResult<ts.Node> => {
|
||||||
@ -103,17 +119,17 @@ export const createFuncInlineTransformer = (
|
|||||||
|
|
||||||
if (args.length !== arity) {
|
if (args.length !== arity) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`somerhing went wrong, expected number of arguments=${arity} but got ${args.length} for ${funcName.text}`
|
`something went wrong, expected number of arguments=${arity} but got ${args.length} for ${funcName.text}`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const split = splits.find(s => s.originalName === funcName.text);
|
const split = splits.get(funcName.text);
|
||||||
|
|
||||||
if (split && split.arity === arity) {
|
if (split && split.arity === arity) {
|
||||||
return ts.createCall(
|
return ts.createCall(
|
||||||
ts.createIdentifier(split.rawLambdaName),
|
ts.createIdentifier(split.rawLambdaName),
|
||||||
undefined,
|
undefined,
|
||||||
args
|
args.map(arg => ts.visitNode(arg, visitor))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
106
src/experiments/modernizeJS.ts
Normal file
106
src/experiments/modernizeJS.ts
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
import ts from 'typescript';
|
||||||
|
|
||||||
|
export const replaceUtilsUpdateWithObjectSpread: ts.TransformerFactory<ts.SourceFile> = context => {
|
||||||
|
return sourceFile => {
|
||||||
|
const visitor = (node: ts.Node): ts.VisitResult<ts.Node> => {
|
||||||
|
// detects function f(..){..}
|
||||||
|
if (
|
||||||
|
ts.isFunctionDeclaration(node) &&
|
||||||
|
node.name?.text === '_Utils_update'
|
||||||
|
) {
|
||||||
|
return ts.createVariableStatement(
|
||||||
|
undefined,
|
||||||
|
ts.createVariableDeclarationList(
|
||||||
|
[
|
||||||
|
ts.createVariableDeclaration(
|
||||||
|
ts.createIdentifier('_Utils_update'),
|
||||||
|
undefined,
|
||||||
|
ts.createArrowFunction(
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
[
|
||||||
|
ts.createParameter(
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
ts.createIdentifier('oldRecord'),
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
undefined
|
||||||
|
),
|
||||||
|
ts.createParameter(
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
ts.createIdentifier('updatedFields'),
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
undefined
|
||||||
|
),
|
||||||
|
],
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
|
||||||
|
ts.createObjectLiteral(
|
||||||
|
[
|
||||||
|
ts.createSpreadAssignment(
|
||||||
|
ts.createIdentifier('oldRecord')
|
||||||
|
),
|
||||||
|
ts.createSpreadAssignment(
|
||||||
|
ts.createIdentifier('updatedFields')
|
||||||
|
),
|
||||||
|
],
|
||||||
|
false
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
],
|
||||||
|
ts.NodeFlags.Const
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ts.visitEachChild(node, visitor, context);
|
||||||
|
};
|
||||||
|
|
||||||
|
return ts.visitNode(sourceFile, visitor);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const convertFunctionExpressionsToArrowFuncs: ts.TransformerFactory<ts.SourceFile> = context => {
|
||||||
|
return sourceFile => {
|
||||||
|
const visitor = (node: ts.Node): ts.VisitResult<ts.Node> => {
|
||||||
|
// console.log(
|
||||||
|
// `Visiting: ${ts.SyntaxKind[node.kind]} with name ${
|
||||||
|
// (node as any).name?.text
|
||||||
|
// }`
|
||||||
|
// );
|
||||||
|
if (
|
||||||
|
ts.isFunctionExpression(node) &&
|
||||||
|
node.name === undefined &&
|
||||||
|
node.body.statements.length === 1
|
||||||
|
) {
|
||||||
|
// console.log('$$body', node.body.getText());
|
||||||
|
const [returnStatement] = node.body.statements;
|
||||||
|
if (
|
||||||
|
ts.isReturnStatement(returnStatement) &&
|
||||||
|
returnStatement.expression !== undefined
|
||||||
|
) {
|
||||||
|
return ts.createArrowFunction(
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
node.parameters,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
ts.visitNode(returnStatement.expression, visitor)
|
||||||
|
// returnStatement.expression
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ts.visitEachChild(node, visitor, context);
|
||||||
|
};
|
||||||
|
|
||||||
|
return ts.visitNode(sourceFile, visitor);
|
||||||
|
};
|
||||||
|
};
|
36
src/index.ts
36
src/index.ts
@ -11,6 +11,7 @@ import {
|
|||||||
createInlineListFromArrayTransformer,
|
createInlineListFromArrayTransformer,
|
||||||
InlineMode,
|
InlineMode,
|
||||||
} from './experiments/inlineListFromArray';
|
} from './experiments/inlineListFromArray';
|
||||||
|
import { convertFunctionExpressionsToArrowFuncs } from './experiments/modernizeJS';
|
||||||
|
|
||||||
const elmOutput = `
|
const elmOutput = `
|
||||||
var $elm$core$Maybe$Nothing = {$: 'Nothing'};
|
var $elm$core$Maybe$Nothing = {$: 'Nothing'};
|
||||||
@ -27,6 +28,16 @@ var $author$project$Main$Three = F3(
|
|||||||
var _v1 = A3($author$project$Main$Three, a, b, c);
|
var _v1 = A3($author$project$Main$Three, a, b, c);
|
||||||
|
|
||||||
_List_fromArray(['a', 'b', 'c']);
|
_List_fromArray(['a', 'b', 'c']);
|
||||||
|
|
||||||
|
function _List_Cons(hd, tl) {
|
||||||
|
return { $: 1, a: hd, b: tl };
|
||||||
|
}
|
||||||
|
|
||||||
|
var _List_cons = F2(_List_Cons);
|
||||||
|
|
||||||
|
var $elm$core$List$cons = _List_cons;
|
||||||
|
A2($elm$core$List$cons, key, keyList);
|
||||||
|
$elm$core$String$join_raw("\n\n", A2($elm$core$List$cons, introduction, A2($elm$core$List$indexedMap, $elm$json$Json$Decode$errorOneOf, errors)));
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const source = ts.createSourceFile('elm.js', elmOutput, ts.ScriptTarget.ES2018);
|
const source = ts.createSourceFile('elm.js', elmOutput, ts.ScriptTarget.ES2018);
|
||||||
@ -71,9 +82,9 @@ console.log(printer.printFile(source));
|
|||||||
console.log(printer.printFile(newFile));
|
console.log(printer.printFile(newFile));
|
||||||
console.log('----------AFTER SPLIT TRANSFORM ----------------');
|
console.log('----------AFTER SPLIT TRANSFORM ----------------');
|
||||||
|
|
||||||
const collectedSplits: FuncSplit[] = [];
|
const collectedSplits = new Map<string, FuncSplit>();
|
||||||
const splitTransformer = createSplitFunctionDeclarationsTransformer(split =>
|
const splitTransformer = createSplitFunctionDeclarationsTransformer(
|
||||||
collectedSplits.push(split)
|
collectedSplits
|
||||||
);
|
);
|
||||||
const [sourceWithSplittedFunctions] = ts.transform(newFile, [
|
const [sourceWithSplittedFunctions] = ts.transform(newFile, [
|
||||||
splitTransformer,
|
splitTransformer,
|
||||||
@ -103,3 +114,22 @@ const [sourceWithInlinedListFromArr] = ts.transform(
|
|||||||
).transformed;
|
).transformed;
|
||||||
|
|
||||||
console.log(printer.printFile(sourceWithInlinedListFromArr));
|
console.log(printer.printFile(sourceWithInlinedListFromArr));
|
||||||
|
|
||||||
|
const funcSource = ts.createSourceFile(
|
||||||
|
'elm.js',
|
||||||
|
`
|
||||||
|
function F3(fun) {
|
||||||
|
return F(3, fun, function (a) {
|
||||||
|
return function (b) { return function (c) { return fun(a, b, c); }; };
|
||||||
|
});
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
ts.ScriptTarget.ES2018
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log('---------- Arrow Transformation ----------------');
|
||||||
|
const [fRes] = ts.transform(funcSource, [
|
||||||
|
convertFunctionExpressionsToArrowFuncs,
|
||||||
|
]).transformed;
|
||||||
|
|
||||||
|
console.log(printer.printFile(fRes));
|
||||||
|
@ -37,7 +37,13 @@ addMyType mine sum =
|
|||||||
|
|
||||||
main =
|
main =
|
||||||
let
|
let
|
||||||
|
f x =
|
||||||
|
addMyType x
|
||||||
|
|
||||||
|
g =
|
||||||
|
f
|
||||||
|
|
||||||
sum =
|
sum =
|
||||||
List.foldl addMyType 0 many
|
List.foldl g 0 many
|
||||||
in
|
in
|
||||||
Html.text (String.fromInt sum)
|
Html.text (String.fromInt sum)
|
||||||
|
@ -4522,7 +4522,11 @@ var $author$project$Main$many = $elm$core$List$concat(
|
|||||||
var $elm$virtual_dom$VirtualDom$text = _VirtualDom_text;
|
var $elm$virtual_dom$VirtualDom$text = _VirtualDom_text;
|
||||||
var $elm$html$Html$text = $elm$virtual_dom$VirtualDom$text;
|
var $elm$html$Html$text = $elm$virtual_dom$VirtualDom$text;
|
||||||
var $author$project$Main$main = function () {
|
var $author$project$Main$main = function () {
|
||||||
var sum = A3($elm$core$List$foldl, $author$project$Main$addMyType, 0, $author$project$Main$many);
|
var f = function (x) {
|
||||||
|
return $author$project$Main$addMyType(x);
|
||||||
|
};
|
||||||
|
var g = f;
|
||||||
|
var sum = A3($elm$core$List$foldl, g, 0, $author$project$Main$many);
|
||||||
return $elm$html$Html$text(
|
return $elm$html$Html$text(
|
||||||
$elm$core$String$fromInt(sum));
|
$elm$core$String$fromInt(sum));
|
||||||
}();
|
}();
|
||||||
|
@ -3741,12 +3741,11 @@
|
|||||||
var $elm$virtual_dom$VirtualDom$text = _VirtualDom_text;
|
var $elm$virtual_dom$VirtualDom$text = _VirtualDom_text;
|
||||||
var $elm$html$Html$text = $elm$virtual_dom$VirtualDom$text;
|
var $elm$html$Html$text = $elm$virtual_dom$VirtualDom$text;
|
||||||
var $author$project$Main$main = (function() {
|
var $author$project$Main$main = (function() {
|
||||||
var sum = A3(
|
var f = function(x) {
|
||||||
$elm$core$List$foldl,
|
return $author$project$Main$addMyType(x);
|
||||||
$author$project$Main$addMyType,
|
};
|
||||||
0,
|
var g = f;
|
||||||
$author$project$Main$many
|
var sum = A3($elm$core$List$foldl, g, 0, $author$project$Main$many);
|
||||||
);
|
|
||||||
return $elm$html$Html$text($elm$core$String$fromInt(sum));
|
return $elm$html$Html$text($elm$core$String$fromInt(sum));
|
||||||
})();
|
})();
|
||||||
_Platform_export({
|
_Platform_export({
|
||||||
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user