UNPKG

6.01 kBJavaScriptView Raw
1const _ = require('lodash');
2const {flattenExpression, searchExpressions, searchExpressionsWithoutInnerFunctions, getAllFunctions, flattenExpressionWithoutInnerFunctions} = require('./expr-search');
3const {memoizeExprFunc, memoize} = require('./memoize');
4const {exprHash, hashString} = require('./expr-hash');
5const {TokenTypeData, Expression, Get, Expr, TopLevel, Token, Invoke, FuncArg, Func} = require('./lang');
6const {generateName, generateNameFromTag} = require('./expr-names');
7const objectHash = require('object-hash');
8
9const countTokens = memoizeExprFunc(expr => _.sum(expr.map(countTokens)), () => 1)
10
11function tryToHoist(expr) {
12 return TokenTypeData[expr[0].$type].tryToHoist;
13}
14
15const isStaticExpression = memoize(expr => {
16 const res = true;
17 const areChildrenStatic = expr.map(token => {
18 if (token instanceof Expression) {
19 return isStaticExpression(token);
20 } else if (token.$type === 'val' || token.$type === 'key' || token.$type === 'context') {
21 return false;
22 }
23 return true;
24 });
25 return _.every(areChildrenStatic, (isChildStatic, index) => isChildStatic || expr[index] instanceof Expression && expr[index][0].$type === 'func');
26});
27
28const getRewriteUsingTopLevels = namesByExpr => {
29 const rewriteUsingTopLevels = memoizeExprFunc(
30 expr => {
31 const str = exprHash(expr);
32 if (namesByExpr[str]) {
33 return Expr(Get, namesByExpr[str], TopLevel);
34 }
35 return expr.map(child => rewriteUsingTopLevels(child));
36 },
37 token => token
38 );
39 return rewriteUsingTopLevels;
40};
41
42
43function rewriteStaticsToTopLevels(getters) {
44 const allExpressions = flattenExpression(...Object.values(getters));
45 const allStaticExpressions = _.filter(allExpressions, isStaticExpression);
46 const allStaticAsStrings = allStaticExpressions.reduce((acc, e) => {
47 acc[exprHash(e)] = e;
48 return acc;
49 }, {});
50 const namesByExpr = _(getters)
51 .mapValues(e => exprHash(e))
52 .invert()
53 .value();
54 let nodeIndex = 1;
55 _.forEach(allStaticAsStrings, (e, s) => {
56 if (!namesByExpr[s] && tryToHoist(e)) {
57 namesByExpr[s] = `$${e[0].$type}${generateName(namesByExpr, e)}${nodeIndex++}`;
58 }
59 });
60 const rewriteUsingTopLevels = getRewriteUsingTopLevels(namesByExpr);
61 const newGetters = {};
62 _.forEach(namesByExpr, (name, hash) => {
63 newGetters[name] = allStaticAsStrings[hash].map(rewriteUsingTopLevels);
64 });
65 _.forEach(getters, (expr, name) => {
66 if (!newGetters[name]) {
67 newGetters[name] = Expr(Get, namesByExpr[exprHash(expr)], TopLevel);
68 }
69 })
70 return newGetters;
71}
72
73function rewriteLocalsToFunctions(getters) {
74 const exprs = flattenExpression(...Object.values(getters));
75 // console.log(exprs.length)
76
77 const parentMap = new Map();
78 searchExpressions(expr => {
79 if (expr[0].$type !== 'func') {
80 expr.forEach(child => {
81 if (child instanceof Expression) {
82 if (!parentMap.has(child)) {
83 parentMap.set(child, []);
84 }
85 parentMap.get(child).push(expr);
86 }
87 })
88 }
89 }, Object.values(getters))
90 const countIdenticals = {};
91 exprs.forEach(e => {
92 const parents = parentMap.get(e);
93 // console.log(parents && parents.length);
94 if (e instanceof Expression && e[0].$type !== 'func' && parents && parents.length > 1) {
95 const hash = exprHash(e);
96 const children = flattenExpressionWithoutInnerFunctions(e);
97 // console.log(parents && parents.length, children.length);
98 countIdenticals[hash] = {counter: parents.length, children}
99 }
100 });
101
102 const newGetters = {};
103 const namesByHash = {};
104 const localTokens = {
105 val: true,
106 key: true,
107 context: true,
108 loop: true
109 }
110
111 function rewriteExpr(e) {
112 if (e instanceof Expression) {
113 const hash = exprHash(e);
114 const found = countIdenticals[hash];
115 if (found && found.counter > 2 && found.children.length > 4) {
116 const name = namesByHash[hash] ? namesByHash[hash] : `$$${generateNameFromTag(e)}${hash}`;
117 if (!namesByHash[name]) {
118 const tokens = _(found.children)
119 .flatten()
120 .filter(t => t instanceof Token && localTokens[t.$type])
121 .map(t => t.$type)
122 .uniq()
123 .map(t => new Token(t))
124 .value()
125 found.tokens = tokens;
126 namesByHash[hash] = name;
127 newGetters[name] = Expr(Func, Expr(...e.map(rewriteExpr)), ...found.tokens);
128 }
129 return Expr(Invoke, name, ...found.tokens.map(t => new Token(t.$type)));
130 }
131 return Expr(...e.map(rewriteExpr));
132 }
133 return e;
134 }
135
136 Object.assign(newGetters, _.mapValues(getters, rewriteExpr))
137 return newGetters;
138}
139
140function rewriteUniqueByHash(getters) {
141 const exprs = flattenExpression(Object.values(getters));
142 const allHashes = {};
143 exprs.forEach(e => {
144 const hash = exprHash(e);
145 allHashes[hash] = allHashes[hash] || [];
146 allHashes[hash].push(e)
147 })
148 const canonical = {};
149 function getCanoncial(expr) {
150 if (expr instanceof Expression) {
151 const hash = exprHash(expr);
152 if (!canonical.hasOwnProperty(hash)) {
153 canonical[hash] = Expr(...expr.map(getCanoncial));
154 }
155 return canonical[hash];
156 }
157 return expr;
158 }
159 const newGetters = _.mapValues(getters, getCanoncial)
160 return newGetters;
161}
162
163module.exports = {
164 rewriteLocalsToFunctions,
165 rewriteStaticsToTopLevels,
166 rewriteUniqueByHash
167}
\No newline at end of file