UNPKG

10.3 kBJavaScriptView Raw
1const {Expr, Token, Setter, Expression, SetterExpression, SpliceSetterExpression, TokenTypeData} = require('./lang');
2const _ = require('lodash');
3const t = require('babel-types');
4const NaiveCompiler = require('./naive-compiler');
5const {splitSettersGetters, normalizeAndTagAllGetters, findFuncExpr} = require('./expr-tagging');
6const fs = require('fs-extra');
7const path = require('path');
8const {writeFile, readFile} = require('fs-extra');
9const FlowCompiler = require('./flow-compiler');
10const {
11 extractAllTypeDeclerations,
12 flowAnnotationToRustType,
13 rustTypeBorrows,
14 isHashMap,
15 isStruct
16} = require('./rust-types');
17const noDollar = str => str.replace('$', '_');
18
19const constantsTypeAnnotations = {
20 string: 'stringLiteralTypeAnnotation',
21 number: 'numberLiteralTypeAnnotation',
22 boolean: 'booleanLiteralTypeAnnotation'
23};
24
25class RustCompiler extends NaiveCompiler {
26 constructor(model, options) {
27 const {getters, setters} = splitSettersGetters(model);
28 super({...model, ...normalizeAndTagAllGetters(getters, setters)}, options);
29 }
30
31 // buildDerived(name) {
32 // return `$${name}Build();`;
33 // }
34
35 classOfToken(token) {
36 if (token instanceof Expression) {
37 return this.exprAnnotations[token[0].$id];
38 } else if (constantsTypeAnnotations[typeof token]) {
39 return {type: constantsTypeAnnotations[typeof token]};
40 } else if (token.$type === 'root') {
41 return this.types.Model;
42 } else if (token.$type === 'val') {
43 return this.exprAnnotations[findFuncExpr(this.getters, token.$funcId)[0].$id].params[0].typeAnnotation;
44 } else if (token.$type === 'key') {
45 return this.exprAnnotations[findFuncExpr(this.getters, token.$funcId)].params[1].typeAnnotation;
46 } else if (token.$type === 'topLevel') {
47 return this.topLevelType;
48 } else if (token.$type && token.$id && this.exprAnnotations[token.$id]) {
49 return this.exprAnnotations[token.$id];
50 }
51 throw new Error(`tried to classify unknown token ${JSON.stringify(token)}`);
52 }
53
54 resolveToken(token) {
55 const raw = this.classOfToken(token);
56 if (raw && raw.type === 'GenericTypeAnnotation') {
57 return this.types[raw.id.name];
58 }
59 return raw;
60 }
61
62 //self.model.todos.iter().map(|(k, v)|(k.clone(),self.__11(v,&k,None))).collect()
63
64 generateExpr(expr) {
65 console.log(JSON.stringify(expr, null, 2));
66 const currentToken = expr instanceof Expression ? expr[0] : expr;
67 const annotated = [this.resolveToken(expr)].concat(
68 expr instanceof Expression ? expr.slice(1).map(this.resolveToken.bind(this)) : []
69 );
70 const tokenType = currentToken.$type;
71 switch (tokenType) {
72 case 'func':
73 return `Instance::__${currentToken.$id}`;
74 case 'root':
75 return 'model';
76 case 'val':
77 case 'key':
78 case 'arg0':
79 case 'arg1':
80 case 'context':
81 return `${tokenType}`;
82 case 'topLevel':
83 return 'topLevel';
84 case 'get':
85 if (isHashMap(annotated[2])) {
86 return `*${this.generateExpr(expr[2])}.get(&${this.generateExpr(expr[1])}${
87 annotated[1].type === 'NullableTypeAnnotation' ? '.clone().unwrap()' : ''
88 }).unwrap()`;
89 } else if (isStruct(annotated[2])) {
90 if (annotated[1].type === 'stringLiteralTypeAnnotation') {
91 return `${this.generateExpr(expr[2])}.${noDollar(expr[1])}`;
92 }
93 }
94 return `/* unknown get ${JSON.stringify({expr, annotated}, null, 2)}*/`;
95 case 'ternary':
96 if (annotated[1].type === 'NullableTypeAnnotation') {
97 return `if ${this.generateExpr(expr[1])}.toJsBool() {
98 ${this.generateExpr(expr[2])}
99 } else {
100 ${this.generateExpr(expr[3])}
101 }
102 `;
103 }
104 return `/* ${JSON.stringify(annotated)}*/
105 if ${this.generateExpr(expr[1])} {${this.generateExpr(expr[2])}} else {${this.generateExpr(expr[3])}}`;
106 case 'mapValues':
107 return `${this.generateExpr(expr[2])}.iter().map(|(k, v)|(k.clone(),Instance::__${
108 expr[1][0].$id
109 }(model,topLevel,funcLib,interner, v,k.clone(),None))).collect()`;
110 /*case 'and':
111 return (
112 '(' +
113 expr
114 .slice(1)
115 .map(e => this.generateExpr(e))
116 .map(part => `(${part})`)
117 .join('&&') +
118 ')'
119 );
120 case 'or':
121 return (
122 '(' +
123 expr
124 .slice(1)
125 .map(e => this.generateExpr(e))
126 .map(part => `(${part})`)
127 .join('||') +
128 ')'
129 );
130 case 'not':
131 return `!(${this.generateExpr(expr[1])})`;
132 case 'ternary':
133 return `((${this.generateExpr(expr[1])})?(${this.generateExpr(expr[2])}):(${this.generateExpr(expr[3])}))`;
134 case 'array':
135 return `[${expr
136 .slice(1)
137 .map(t => this.generateExpr(t))
138 .join(',')}]`;
139 case 'object':
140 return `{${_.range(1, expr.length, 2)
141 .map(idx => `"${expr[idx]}": ${this.generateExpr(expr[idx + 1])}`)
142 .join(',')}}`;
143 case 'range':
144 return `range(${this.generateExpr(expr[1])}, ${expr.length > 2 ? this.generateExpr(expr[2]) : '0'}, ${
145 expr.length > 2 ? this.generateExpr(expr[2]) : '1'
146 })`;
147 case 'keys':
148 case 'values':
149 case 'assign':
150 case 'defaults':
151 case 'size':
152 return `${tokenType}(${this.generateExpr(expr[1])})`;
153 case 'eq':
154 case 'lt':
155 case 'lte':
156 case 'gt':
157 case 'gte':
158 case 'plus':
159 case 'minus':
160 case 'mult':
161 case 'div':
162 case 'mod':
163 return `(${this.generateExpr(expr[1])}) ${nativeOps[tokenType]} (${this.generateExpr(expr[2])})`;
164 case 'get':
165 return `${this.generateExpr(expr[2])}[${this.generateExpr(expr[1])}]`;
166 case 'mapValues':
167 case 'filterBy':
168 case 'groupBy':
169 case 'mapKeys':
170 case 'map':
171 case 'any':
172 case 'filter':
173 case 'keyBy':
174 case 'anyValues':
175 case 'recursiveMap':
176 case 'recursiveMapValues':
177 return `${tokenType}(${this.generateExpr(expr[1])}, ${this.generateExpr(expr[2])}, ${
178 typeof expr[3] === 'undefined' ? null : this.generateExpr(expr[3])
179 })`;
180 case 'loop':
181 return 'loop';
182 case 'recur':
183 return `${this.generateExpr(expr[1])}(${this.generateExpr(expr[2])})`;
184 case 'func':
185 return currentToken.$funcId;
186 case 'root':
187 return '$model';
188 case 'null':
189
190 case 'topLevel':
191 return `$res`;
192 case 'call':
193 return `$funcLib[${this.generateExpr(expr[1])}](${expr
194 .slice(2)
195 .map(subExpr => this.generateExpr(subExpr))
196 .join(',')})`;*/
197 default:
198 if (typeof currentToken === 'boolean' || typeof currentToken === 'number') {
199 return `${currentToken}`;
200 }
201 return `/*${JSON.stringify(currentToken)}*/`;
202 }
203 }
204
205 get template() {
206 return require('./templates/rust-simple.js');
207 }
208
209 generateAnnotations() {
210 const annotationsFile = path.join(__dirname, '..', 'cache', `${this.hash()}.json`);
211 console.log(annotationsFile);
212 try {
213 this.annotations = fs.readJsonSync(annotationsFile);
214 console.log('annotations: found in cache');
215 } catch (e) {
216 const flowCompiler = new FlowCompiler(Object.assign({}, this.getters, this.setters), this.options);
217 flowCompiler.compile();
218 this.annotations = flowCompiler.annotations;
219 fs.writeJsonSync(annotationsFile, this.annotations);
220 console.log('annotations: not found in cache, generated new');
221 }
222 return this.annotations;
223 }
224
225 topLevelOverrides() {
226 return Object.assign({}, super.topLevelOverrides(), {
227 NAME: _.upperFirst(this.options.name),
228 SETTERS: () => '',
229 STRUCTS: () =>
230 Object.values(this.types)
231 .concat([this.topLevelType])
232 .map(
233 type => `${flowAnnotationToRustType(type)}
234impl JsConvertable for ${type.name} {
235 fn toJsBool(&self) -> bool {
236 true
237 }
238}
239`
240 )
241 .join('')
242 });
243 }
244 // RETURN_TYPE: () => flowAnnotationToRustType(this.exprAnnotations[expr[0].$id])
245
246 exprTemplatePlaceholders(expr, funcName) {
247 console.log(JSON.stringify(expr), funcName, this.exprAnnotations[expr[0].$id]);
248 const exprAnnotate = this.exprAnnotations[expr[0].$id];
249 const currentToken = expr[0].$type;
250 return {
251 ROOTNAME: noDollar(expr[0].$rootName),
252 FUNCNAME: noDollar(funcName),
253 EXPR1: () => this.generateExpr(expr[1]),
254 EXPR: () => this.generateExpr(expr),
255 ID: () => expr[0].$id,
256 RETURN: () => flowAnnotationToRustType(currentToken === 'func' ? exprAnnotate.returnType : exprAnnotate),
257 ARGS: () =>
258 currentToken === 'func' ?
259 exprAnnotate.params
260 .map(
261 param =>
262 `,${param.name.name}: ${rustTypeBorrows(param.typeAnnotation) ? '&' : ''}${flowAnnotationToRustType(
263 param.typeAnnotation
264 )}`
265 )
266 .join('') :
267 ''
268 };
269 }
270
271 buildDerived(name) {
272 const prefix = `self.topLevel.${noDollar(name)} = `;
273 return `${prefix} Instance::_${noDollar(name)}(&self.model,&self.topLevel,&self.funcLib,&mut self.interner);`;
274 }
275
276 tagTokenAnnotations(expr, name) {
277 if (expr instanceof Expression) {
278 expr.forEach(child => {
279 if (child instanceof Expression && child[0].$type === 'func') { //eslint-disable-line no-empty
280 }
281 });
282 }
283 }
284
285 compile() {
286 this.generateAnnotations();
287 this.annotations = extractAllTypeDeclerations(this.annotations);
288 this.types = this.annotations.types;
289 this.exprAnnotations = this.annotations.expr;
290 this.topLevelType = {
291 name: 'TopLevel',
292 type: 'ObjectTypeAnnotation',
293 indexers: [],
294 properties: _.map(this.getters, (expr, name) => ({
295 type: 'ObjectTypeProperty',
296 key: {
297 type: 'Identifier',
298 name: noDollar(name)
299 },
300 value: this.exprAnnotations[expr[0].$id]
301 }))
302 };
303 _.forEach(this.getters, this.tagTokenAnnotations);
304 return super.compile();
305 }
306
307 get lang() {
308 return 'rust';
309 }
310}
311
312module.exports = RustCompiler;