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,
|
||||
} from './experiments/inlineListFromArray';
|
||||
|
||||
import {
|
||||
replaceUtilsUpdateWithObjectSpread,
|
||||
convertFunctionExpressionsToArrowFuncs,
|
||||
} from './experiments/modernizeJS';
|
||||
|
||||
// Compile examples in `testcases/*` folder as js
|
||||
// Run whatever transformations we want on them, saving steps as `elm.{transformation}.js`
|
||||
compile(['Main.elm'], {
|
||||
@ -54,9 +59,9 @@ const customTypeTransformer = createCustomTypesTransformer(
|
||||
Mode.Prod
|
||||
);
|
||||
|
||||
const collectedSplits: FuncSplit[] = [];
|
||||
const splitTransformer = createSplitFunctionDeclarationsTransformer(split =>
|
||||
collectedSplits.push(split)
|
||||
const collectedSplits = new Map<string, FuncSplit>();
|
||||
const splitTransformer = createSplitFunctionDeclarationsTransformer(
|
||||
collectedSplits
|
||||
);
|
||||
|
||||
const funcInlineTransformer = createFuncInlineTransformer(collectedSplits);
|
||||
@ -70,6 +75,8 @@ const [result] = ts.transform(source, [
|
||||
splitTransformer,
|
||||
funcInlineTransformer,
|
||||
inlineListFromArrayCalls,
|
||||
replaceUtilsUpdateWithObjectSpread,
|
||||
convertFunctionExpressionsToArrowFuncs,
|
||||
]).transformed;
|
||||
|
||||
const printer = ts.createPrinter();
|
||||
|
@ -1,7 +1,6 @@
|
||||
import ts from 'typescript';
|
||||
|
||||
export type FuncSplit = {
|
||||
originalName: string;
|
||||
rawLambdaName: string;
|
||||
arity: number;
|
||||
};
|
||||
@ -12,12 +11,18 @@ const deriveRawLambdaName = (wrappedName: string): string =>
|
||||
const wrapperRegex = /F(?<arity>[1-9]+[0-9]*)/;
|
||||
|
||||
export const createSplitFunctionDeclarationsTransformer = (
|
||||
reportSplit: (split: FuncSplit) => void
|
||||
splits: Map<string, FuncSplit>
|
||||
): ts.TransformerFactory<ts.SourceFile> => context => {
|
||||
return sourceFile => {
|
||||
const visitor = (node: ts.Node): ts.VisitResult<ts.Node> => {
|
||||
// detects "var a"
|
||||
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](..)"
|
||||
if (node.initializer && ts.isCallExpression(node.initializer)) {
|
||||
const callExpression = node.initializer.expression;
|
||||
@ -31,6 +36,16 @@ export const createSplitFunctionDeclarationsTransformer = (
|
||||
if (args.length === 1) {
|
||||
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
|
||||
// detects "var a = F123( function (a) {return a})"
|
||||
// or "var a = F123( a => a)"
|
||||
@ -39,11 +54,12 @@ export const createSplitFunctionDeclarationsTransformer = (
|
||||
ts.isFunctionExpression(maybeFuncExpression)
|
||||
) {
|
||||
// TODO typecheck?
|
||||
const arity = Number(maybeMatch.groups.arity);
|
||||
const originalName = node.name.text;
|
||||
const rawLambdaName = deriveRawLambdaName(originalName);
|
||||
|
||||
reportSplit({ arity, originalName, rawLambdaName });
|
||||
splits.set(originalName, {
|
||||
arity,
|
||||
rawLambdaName,
|
||||
});
|
||||
|
||||
const lambdaDeclaration = ts.createVariableDeclaration(
|
||||
rawLambdaName,
|
||||
@ -78,7 +94,7 @@ export const createSplitFunctionDeclarationsTransformer = (
|
||||
const invocationRegex = /A(?<arity>[1-9]+[0-9]*)/;
|
||||
|
||||
export const createFuncInlineTransformer = (
|
||||
splits: FuncSplit[]
|
||||
splits: Map<string, FuncSplit>
|
||||
): ts.TransformerFactory<ts.SourceFile> => context => {
|
||||
return sourceFile => {
|
||||
const visitor = (node: ts.Node): ts.VisitResult<ts.Node> => {
|
||||
@ -103,17 +119,17 @@ export const createFuncInlineTransformer = (
|
||||
|
||||
if (args.length !== arity) {
|
||||
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) {
|
||||
return ts.createCall(
|
||||
ts.createIdentifier(split.rawLambdaName),
|
||||
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,
|
||||
InlineMode,
|
||||
} from './experiments/inlineListFromArray';
|
||||
import { convertFunctionExpressionsToArrowFuncs } from './experiments/modernizeJS';
|
||||
|
||||
const elmOutput = `
|
||||
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);
|
||||
|
||||
_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);
|
||||
@ -71,9 +82,9 @@ console.log(printer.printFile(source));
|
||||
console.log(printer.printFile(newFile));
|
||||
console.log('----------AFTER SPLIT TRANSFORM ----------------');
|
||||
|
||||
const collectedSplits: FuncSplit[] = [];
|
||||
const splitTransformer = createSplitFunctionDeclarationsTransformer(split =>
|
||||
collectedSplits.push(split)
|
||||
const collectedSplits = new Map<string, FuncSplit>();
|
||||
const splitTransformer = createSplitFunctionDeclarationsTransformer(
|
||||
collectedSplits
|
||||
);
|
||||
const [sourceWithSplittedFunctions] = ts.transform(newFile, [
|
||||
splitTransformer,
|
||||
@ -103,3 +114,22 @@ const [sourceWithInlinedListFromArr] = ts.transform(
|
||||
).transformed;
|
||||
|
||||
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 =
|
||||
let
|
||||
f x =
|
||||
addMyType x
|
||||
|
||||
g =
|
||||
f
|
||||
|
||||
sum =
|
||||
List.foldl addMyType 0 many
|
||||
List.foldl g 0 many
|
||||
in
|
||||
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$html$Html$text = $elm$virtual_dom$VirtualDom$text;
|
||||
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(
|
||||
$elm$core$String$fromInt(sum));
|
||||
}();
|
||||
|
@ -3741,12 +3741,11 @@
|
||||
var $elm$virtual_dom$VirtualDom$text = _VirtualDom_text;
|
||||
var $elm$html$Html$text = $elm$virtual_dom$VirtualDom$text;
|
||||
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($elm$core$String$fromInt(sum));
|
||||
})();
|
||||
_Platform_export({
|
||||
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user