1 | const {Expr, Token, Setter, Expression, SetterExpression, SpliceSetterExpression, TokenTypeData} = require('./lang');
|
2 | const _ = require('lodash');
|
3 | const t = require('babel-types');
|
4 | const NaiveCompiler = require('./naive-compiler');
|
5 | const {splitSettersGetters, normalizeAndTagAllGetters, findFuncExpr} = require('./expr-tagging');
|
6 | const fs = require('fs-extra');
|
7 | const path = require('path');
|
8 | const {writeFile, readFile} = require('fs-extra');
|
9 | const FlowCompiler = require('./flow-compiler');
|
10 | const {
|
11 | extractAllTypeDeclerations,
|
12 | flowAnnotationToRustType,
|
13 | rustTypeBorrows,
|
14 | isHashMap,
|
15 | isStruct
|
16 | } = require('./rust-types');
|
17 | const noDollar = str => str.replace('$', '_');
|
18 |
|
19 | const constantsTypeAnnotations = {
|
20 | string: 'stringLiteralTypeAnnotation',
|
21 | number: 'numberLiteralTypeAnnotation',
|
22 | boolean: 'booleanLiteralTypeAnnotation'
|
23 | };
|
24 |
|
25 | class RustCompiler extends NaiveCompiler {
|
26 | constructor(model, options) {
|
27 | const {getters, setters} = splitSettersGetters(model);
|
28 | super({...model, ...normalizeAndTagAllGetters(getters, setters)}, options);
|
29 | }
|
30 |
|
31 |
|
32 |
|
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 |
|
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 | |
111 |
|
112 |
|
113 |
|
114 |
|
115 |
|
116 |
|
117 |
|
118 |
|
119 |
|
120 |
|
121 |
|
122 |
|
123 |
|
124 |
|
125 |
|
126 |
|
127 |
|
128 |
|
129 |
|
130 |
|
131 |
|
132 |
|
133 |
|
134 |
|
135 |
|
136 |
|
137 |
|
138 |
|
139 |
|
140 |
|
141 |
|
142 |
|
143 |
|
144 |
|
145 |
|
146 |
|
147 |
|
148 |
|
149 |
|
150 |
|
151 |
|
152 |
|
153 |
|
154 |
|
155 |
|
156 |
|
157 |
|
158 |
|
159 |
|
160 |
|
161 |
|
162 |
|
163 |
|
164 |
|
165 |
|
166 |
|
167 |
|
168 |
|
169 |
|
170 |
|
171 |
|
172 |
|
173 |
|
174 |
|
175 |
|
176 |
|
177 |
|
178 |
|
179 |
|
180 |
|
181 |
|
182 |
|
183 |
|
184 |
|
185 |
|
186 |
|
187 |
|
188 |
|
189 |
|
190 |
|
191 |
|
192 |
|
193 |
|
194 |
|
195 |
|
196 |
|
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)}
|
234 | impl JsConvertable for ${type.name} {
|
235 | fn toJsBool(&self) -> bool {
|
236 | true
|
237 | }
|
238 | }
|
239 | `
|
240 | )
|
241 | .join('')
|
242 | });
|
243 | }
|
244 |
|
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') {
|
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 |
|
312 | module.exports = RustCompiler;
|