ether-code 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +130 -0
  3. package/cli/compiler.js +298 -0
  4. package/cli/ether.js +532 -0
  5. package/cli/watcher.js +106 -0
  6. package/generators/css-generator.js +583 -0
  7. package/generators/graphql-generator.js +868 -0
  8. package/generators/html-generator.js +745 -0
  9. package/generators/js-generator.js +909 -0
  10. package/generators/node-generator.js +467 -0
  11. package/generators/php-generator.js +706 -0
  12. package/generators/python-generator.js +913 -0
  13. package/generators/react-generator.js +599 -0
  14. package/generators/ruby-generator.js +904 -0
  15. package/generators/sql-generator.js +988 -0
  16. package/generators/ts-generator.js +569 -0
  17. package/i18n/i18n-css.json +743 -0
  18. package/i18n/i18n-graphql.json +1531 -0
  19. package/i18n/i18n-html.json +572 -0
  20. package/i18n/i18n-js.json +2790 -0
  21. package/i18n/i18n-node.json +2442 -0
  22. package/i18n/i18n-php.json +4306 -0
  23. package/i18n/i18n-python.json +3080 -0
  24. package/i18n/i18n-react.json +1784 -0
  25. package/i18n/i18n-ruby.json +1858 -0
  26. package/i18n/i18n-sql.json +3466 -0
  27. package/i18n/i18n-ts.json +442 -0
  28. package/lexer/ether-lexer.js +728 -0
  29. package/lexer/tokens.js +292 -0
  30. package/package.json +45 -0
  31. package/parsers/ast-css.js +545 -0
  32. package/parsers/ast-graphql.js +424 -0
  33. package/parsers/ast-html.js +886 -0
  34. package/parsers/ast-js.js +750 -0
  35. package/parsers/ast-node.js +2440 -0
  36. package/parsers/ast-php.js +957 -0
  37. package/parsers/ast-react.js +580 -0
  38. package/parsers/ast-ruby.js +895 -0
  39. package/parsers/ast-ts.js +1352 -0
  40. package/parsers/css-parser.js +1981 -0
  41. package/parsers/graphql-parser.js +2011 -0
  42. package/parsers/html-parser.js +1181 -0
  43. package/parsers/js-parser.js +2564 -0
  44. package/parsers/node-parser.js +2644 -0
  45. package/parsers/php-parser.js +3037 -0
  46. package/parsers/react-parser.js +1035 -0
  47. package/parsers/ruby-parser.js +2680 -0
  48. package/parsers/ts-parser.js +3881 -0
@@ -0,0 +1,706 @@
1
+ const fs = require('fs')
2
+
3
+ class PHPGenerator {
4
+ constructor(i18nPath = null) {
5
+ this.i18n = null
6
+ this.indent = 0
7
+ this.output = ''
8
+ this.keywordMap = {}
9
+ this.functionMap = {}
10
+
11
+ if (i18nPath) {
12
+ this.loadI18n(i18nPath)
13
+ }
14
+ }
15
+
16
+ loadI18n(filePath) {
17
+ const content = fs.readFileSync(filePath, 'utf-8')
18
+ this.i18n = JSON.parse(content)
19
+ this.buildMaps()
20
+ }
21
+
22
+ buildMaps() {
23
+ this.keywordMap = {}
24
+ this.functionMap = {}
25
+
26
+ if (!this.i18n) return
27
+
28
+ const addToMap = (map, section) => {
29
+ if (!section) return
30
+ for (const [key, translations] of Object.entries(section)) {
31
+ if (translations && translations.fr && translations.php) {
32
+ map[translations.fr.toLowerCase()] = translations.php
33
+ }
34
+ }
35
+ }
36
+
37
+ addToMap(this.keywordMap, this.i18n.variables)
38
+ addToMap(this.keywordMap, this.i18n.types)
39
+ addToMap(this.keywordMap, this.i18n.operateursArithmetiques)
40
+ addToMap(this.keywordMap, this.i18n.operateursComparaison)
41
+ addToMap(this.keywordMap, this.i18n.operateursLogiques)
42
+ addToMap(this.keywordMap, this.i18n.conditions)
43
+ addToMap(this.keywordMap, this.i18n.boucles)
44
+ addToMap(this.keywordMap, this.i18n.fonctions)
45
+ addToMap(this.keywordMap, this.i18n.classes)
46
+ addToMap(this.keywordMap, this.i18n.visibilite)
47
+ addToMap(this.keywordMap, this.i18n.erreursExceptions)
48
+
49
+ addToMap(this.functionMap, this.i18n.chainesManipulation)
50
+ addToMap(this.functionMap, this.i18n.chainesRecherche)
51
+ addToMap(this.functionMap, this.i18n.chainesCasse)
52
+ addToMap(this.functionMap, this.i18n.tableauxBase)
53
+ addToMap(this.functionMap, this.i18n.tableauxTri)
54
+ addToMap(this.functionMap, this.i18n.tableauxFonctionnels)
55
+ addToMap(this.functionMap, this.i18n.mathsBase)
56
+ addToMap(this.functionMap, this.i18n.datesBase)
57
+ addToMap(this.functionMap, this.i18n.fichiersOuverture)
58
+ addToMap(this.functionMap, this.i18n.fichiersOperations)
59
+ addToMap(this.functionMap, this.i18n.regexFonctions)
60
+ addToMap(this.functionMap, this.i18n.jsonFonctions)
61
+ }
62
+
63
+ translate(word) {
64
+ const lower = word.toLowerCase()
65
+ return this.keywordMap[lower] || this.functionMap[lower] || this.translateGeneric(word)
66
+ }
67
+
68
+ translateGeneric(word) {
69
+ const translations = {
70
+ 'variable': '',
71
+ 'constante': 'const',
72
+ 'fonction': 'function',
73
+ 'retourner': 'return',
74
+ 'si': 'if',
75
+ 'sinon': 'else',
76
+ 'sinon si': 'elseif',
77
+ 'pour': 'for',
78
+ 'pour chaque': 'foreach',
79
+ 'tant que': 'while',
80
+ 'faire': 'do',
81
+ 'selon': 'switch',
82
+ 'cas': 'case',
83
+ 'défaut': 'default',
84
+ 'sortir': 'break',
85
+ 'continuer': 'continue',
86
+ 'essayer': 'try',
87
+ 'attraper': 'catch',
88
+ 'finalement': 'finally',
89
+ 'lancer': 'throw',
90
+ 'nouveau': 'new',
91
+ 'classe': 'class',
92
+ 'interface': 'interface',
93
+ 'trait': 'trait',
94
+ 'étend': 'extends',
95
+ 'implémente': 'implements',
96
+ 'utiliser': 'use',
97
+ 'public': 'public',
98
+ 'privé': 'private',
99
+ 'protégé': 'protected',
100
+ 'statique': 'static',
101
+ 'final': 'final',
102
+ 'abstrait': 'abstract',
103
+ 'constructeur': '__construct',
104
+ 'destructeur': '__destruct',
105
+ 'obtenir': '__get',
106
+ 'définir': '__set',
107
+ 'appeler': '__call',
108
+ 'vers chaîne': '__toString',
109
+ 'espace de noms': 'namespace',
110
+ 'comme': 'as',
111
+ 'vrai': 'true',
112
+ 'faux': 'false',
113
+ 'nul': 'null',
114
+ 'ceci': '$this',
115
+ 'parent': 'parent',
116
+ 'soi': 'self',
117
+ 'et': '&&',
118
+ 'ou': '||',
119
+ 'non': '!',
120
+ 'egal': '===',
121
+ 'different': '!==',
122
+ 'concatener': '.',
123
+ 'tableau': 'array',
124
+ 'chaîne': 'string',
125
+ 'entier': 'int',
126
+ 'flottant': 'float',
127
+ 'booléen': 'bool',
128
+ 'objet': 'object',
129
+ 'vide': 'void',
130
+ 'mixte': 'mixed',
131
+ 'afficher': 'echo',
132
+ 'imprimer': 'print',
133
+ 'longueur': 'strlen',
134
+ 'compter': 'count',
135
+ 'exploser': 'explode',
136
+ 'imploser': 'implode',
137
+ 'remplacer': 'str_replace',
138
+ 'position': 'strpos',
139
+ 'sous chaîne': 'substr',
140
+ 'majuscules': 'strtoupper',
141
+ 'minuscules': 'strtolower',
142
+ 'supprimer espaces': 'trim',
143
+ 'ajouter': 'array_push',
144
+ 'retirer': 'array_pop',
145
+ 'fusionner': 'array_merge',
146
+ 'filtrer': 'array_filter',
147
+ 'mapper': 'array_map',
148
+ 'réduire': 'array_reduce',
149
+ 'trier': 'sort',
150
+ 'inverser': 'array_reverse',
151
+ 'clés': 'array_keys',
152
+ 'valeurs': 'array_values',
153
+ 'existe': 'isset',
154
+ 'vide test': 'empty',
155
+ 'est tableau': 'is_array',
156
+ 'est chaîne': 'is_string',
157
+ 'est entier': 'is_int',
158
+ 'est flottant': 'is_float',
159
+ 'est numerique': 'is_numeric',
160
+ 'est nul': 'is_null',
161
+ 'type de': 'gettype',
162
+ 'convertir chaîne': 'strval',
163
+ 'convertir entier': 'intval',
164
+ 'convertir flottant': 'floatval',
165
+ 'date': 'date',
166
+ 'temps': 'time',
167
+ 'maintenant': 'now',
168
+ 'fichier existe': 'file_exists',
169
+ 'lire fichier': 'file_get_contents',
170
+ 'écrire fichier': 'file_put_contents',
171
+ 'ouvrir': 'fopen',
172
+ 'fermer': 'fclose',
173
+ 'lire': 'fread',
174
+ 'écrire': 'fwrite',
175
+ 'json encoder': 'json_encode',
176
+ 'json décoder': 'json_decode',
177
+ 'json decoder': 'json_decode',
178
+ 'regex correspondance': 'preg_match',
179
+ 'regex toutes': 'preg_match_all',
180
+ 'regex remplacer': 'preg_replace',
181
+ 'en tete': 'header',
182
+ 'rediriger': 'header',
183
+ 'session démarrer': 'session_start',
184
+ 'session': '$_SESSION',
185
+ 'post': '$_POST',
186
+ 'get': '$_GET',
187
+ 'requête': '$_REQUEST',
188
+ 'serveur': '$_SERVER',
189
+ 'fichiers': '$_FILES',
190
+ 'cookie': '$_COOKIE',
191
+ 'mourir': 'die',
192
+ 'sortie': 'exit',
193
+ 'inclure': 'include',
194
+ 'inclure une fois': 'include_once',
195
+ 'requiert': 'require',
196
+ 'requiert une fois': 'require_once'
197
+ }
198
+
199
+ const lower = word.toLowerCase()
200
+ return translations[lower] || word
201
+ }
202
+
203
+ generate(ast) {
204
+ this.output = ''
205
+ this.indent = 0
206
+
207
+ if (Array.isArray(ast)) {
208
+ for (const node of ast) {
209
+ const result = this.generateNode(node)
210
+ if (result !== undefined && result !== '' && this.output === '') {
211
+ return result
212
+ }
213
+ }
214
+ } else if (ast && ast.type) {
215
+ const result = this.generateNode(ast)
216
+ if (result !== undefined && result !== '' && this.output === '') {
217
+ return result
218
+ }
219
+ } else if (ast && ast.body) {
220
+ for (const node of ast.body) {
221
+ const result = this.generateNode(node)
222
+ if (result !== undefined && result !== '' && this.output === '') {
223
+ return result
224
+ }
225
+ }
226
+ }
227
+
228
+ return this.output.trim() || '<?php'
229
+ }
230
+
231
+ generateNode(node) {
232
+ if (!node) return ''
233
+
234
+ switch (node.type) {
235
+ case 'VariableDeclaration':
236
+ return this.generateVariableDeclaration(node)
237
+ case 'FunctionDeclaration':
238
+ return this.generateFunctionDeclaration(node)
239
+ case 'ClassDeclaration':
240
+ return this.generateClassDeclaration(node)
241
+ case 'IfStatement':
242
+ return this.generateIfStatement(node)
243
+ case 'ForStatement':
244
+ return this.generateForStatement(node)
245
+ case 'ForEachStatement':
246
+ return this.generateForEachStatement(node)
247
+ case 'WhileStatement':
248
+ return this.generateWhileStatement(node)
249
+ case 'DoWhileStatement':
250
+ return this.generateDoWhileStatement(node)
251
+ case 'SwitchStatement':
252
+ return this.generateSwitchStatement(node)
253
+ case 'TryStatement':
254
+ return this.generateTryStatement(node)
255
+ case 'ReturnStatement':
256
+ return this.generateReturnStatement(node)
257
+ case 'ThrowStatement':
258
+ return this.generateThrowStatement(node)
259
+ case 'BreakStatement':
260
+ this.writeLine('break;')
261
+ return
262
+ case 'ContinueStatement':
263
+ this.writeLine('continue;')
264
+ return
265
+ case 'ExpressionStatement':
266
+ return this.generateExpressionStatement(node)
267
+ case 'EchoStatement':
268
+ return this.generateEchoStatement(node)
269
+ case 'NamespaceDeclaration':
270
+ return this.generateNamespace(node)
271
+ case 'UseStatement':
272
+ return this.generateUseStatement(node)
273
+ case 'CallExpression':
274
+ return this.generateCallExpression(node)
275
+ case 'MemberExpression':
276
+ return this.generateMemberExpression(node)
277
+ case 'BinaryExpression':
278
+ return this.generateBinaryExpression(node)
279
+ case 'AssignmentExpression':
280
+ return this.generateAssignmentExpression(node)
281
+ case 'UpdateExpression':
282
+ return this.generateUpdateExpression(node)
283
+ case 'ArrayExpression':
284
+ return this.generateArrayExpression(node)
285
+ case 'ObjectExpression':
286
+ return this.generateObjectExpression(node)
287
+ case 'Identifier':
288
+ return this.generateIdentifier(node)
289
+ case 'Literal':
290
+ return this.generateLiteral(node)
291
+ case 'NewExpression':
292
+ return this.generateNewExpression(node)
293
+ default:
294
+ if (node.name && !node.type) {
295
+ return this.generateIdentifier(node)
296
+ }
297
+ return ''
298
+ }
299
+ }
300
+
301
+ generateUpdateExpression(node) {
302
+ const arg = this.generateNode(node.argument)
303
+ const op = node.operator
304
+ if (node.prefix) {
305
+ return `${op}${arg}`
306
+ }
307
+ return `${arg}${op}`
308
+ }
309
+
310
+ generateVariableDeclaration(node) {
311
+ const declarations = node.declarations || []
312
+
313
+ for (const decl of declarations) {
314
+ const name = this.generateIdentifier(decl.id || { name: decl.name })
315
+ if (decl.init) {
316
+ const init = this.generateNode(decl.init)
317
+ this.writeLine(`${name} = ${init};`)
318
+ } else {
319
+ this.writeLine(`${name};`)
320
+ }
321
+ }
322
+ }
323
+
324
+ generateFunctionDeclaration(node) {
325
+ const name = this.translate(node.name || node.id?.name || '')
326
+ const params = (node.params || []).map(p => this.generateParam(p)).join(', ')
327
+ const returnType = node.returnType ? ': ' + this.translate(node.returnType) : ''
328
+
329
+ this.writeLine(`function ${name}(${params})${returnType} {`)
330
+ this.indent++
331
+
332
+ if (node.body) {
333
+ if (Array.isArray(node.body)) {
334
+ for (const stmt of node.body) {
335
+ this.generateNode(stmt)
336
+ }
337
+ } else {
338
+ this.generateNode(node.body)
339
+ }
340
+ }
341
+
342
+ this.indent--
343
+ this.writeLine('}')
344
+ this.writeLine('')
345
+ }
346
+
347
+ generateParam(param) {
348
+ let result = ''
349
+
350
+ if (param.type && param.type !== 'Identifier') {
351
+ result += this.translate(param.type) + ' '
352
+ }
353
+
354
+ result += this.generateIdentifier(param)
355
+
356
+ if (param.default) {
357
+ result += ' = ' + this.generateNode(param.default)
358
+ }
359
+
360
+ return result
361
+ }
362
+
363
+ generateClassDeclaration(node) {
364
+ const name = node.name || node.id?.name || ''
365
+ let declaration = 'class ' + name
366
+
367
+ if (node.extends) {
368
+ declaration += ' extends ' + node.extends
369
+ }
370
+
371
+ if (node.implements && node.implements.length > 0) {
372
+ declaration += ' implements ' + node.implements.join(', ')
373
+ }
374
+
375
+ this.writeLine(declaration + ' {')
376
+ this.indent++
377
+
378
+ const members = node.body?.body || node.members || node.body || []
379
+ for (const member of members) {
380
+ this.generateClassMember(member)
381
+ }
382
+
383
+ this.indent--
384
+ this.writeLine('}')
385
+ this.writeLine('')
386
+ }
387
+
388
+ generateClassMember(node) {
389
+ const visibility = node.visibility || 'public'
390
+ const isStatic = node.static ? 'static ' : ''
391
+
392
+ if (node.type === 'Property' || node.kind === 'property') {
393
+ const name = this.generateIdentifier(node.key || { name: node.name })
394
+ const typeHint = node.typeHint ? this.translate(node.typeHint) + ' ' : ''
395
+ const value = node.value ? ' = ' + this.generateNode(node.value) : ''
396
+ this.writeLine(`${visibility} ${isStatic}${typeHint}${name}${value};`)
397
+ this.writeLine('')
398
+ } else if (node.type === 'Method' || node.kind === 'method' || node.type === 'FunctionDeclaration') {
399
+ const name = this.translate(node.key?.name || node.name || '')
400
+ const params = (node.params || node.value?.params || []).map(p => this.generateParam(p)).join(', ')
401
+ const returnType = node.returnType ? ': ' + this.translate(node.returnType) : ''
402
+
403
+ this.writeLine(`${visibility} ${isStatic}function ${name}(${params})${returnType} {`)
404
+ this.indent++
405
+
406
+ const body = node.body || node.value?.body
407
+ if (body) {
408
+ if (Array.isArray(body)) {
409
+ for (const stmt of body) {
410
+ this.generateNode(stmt)
411
+ }
412
+ } else if (body.body) {
413
+ for (const stmt of body.body) {
414
+ this.generateNode(stmt)
415
+ }
416
+ } else {
417
+ this.generateNode(body)
418
+ }
419
+ }
420
+
421
+ this.indent--
422
+ this.writeLine('}')
423
+ this.writeLine('')
424
+ }
425
+ }
426
+
427
+ generateIfStatement(node) {
428
+ const test = this.generateNode(node.test)
429
+ this.writeLine(`if (${test}) {`)
430
+ this.indent++
431
+ this.generateNode(node.consequent)
432
+ this.indent--
433
+
434
+ if (node.alternate) {
435
+ if (node.alternate.type === 'IfStatement') {
436
+ this.output = this.output.trimEnd() + '\n' + this.getIndent() + '} elseif '
437
+ const altTest = this.generateNode(node.alternate.test)
438
+ this.output += `(${altTest}) {\n`
439
+ this.indent++
440
+ this.generateNode(node.alternate.consequent)
441
+ this.indent--
442
+ if (node.alternate.alternate) {
443
+ if (node.alternate.alternate.type === 'IfStatement') {
444
+ this.generateIfStatement({ test: node.alternate.alternate.test, consequent: node.alternate.alternate.consequent, alternate: node.alternate.alternate.alternate })
445
+ } else {
446
+ this.writeLine('} else {')
447
+ this.indent++
448
+ this.generateNode(node.alternate.alternate)
449
+ this.indent--
450
+ this.writeLine('}')
451
+ }
452
+ } else {
453
+ this.writeLine('}')
454
+ }
455
+ } else {
456
+ this.writeLine('} else {')
457
+ this.indent++
458
+ this.generateNode(node.alternate)
459
+ this.indent--
460
+ this.writeLine('}')
461
+ }
462
+ } else {
463
+ this.writeLine('}')
464
+ }
465
+ }
466
+
467
+ generateForStatement(node) {
468
+ const init = node.init ? this.generateNode(node.init).replace(/;$/, '') : ''
469
+ const test = node.test ? this.generateNode(node.test) : ''
470
+ const update = node.update ? this.generateNode(node.update) : ''
471
+
472
+ this.writeLine(`for (${init}; ${test}; ${update}) {`)
473
+ this.indent++
474
+ this.generateNode(node.body)
475
+ this.indent--
476
+ this.writeLine('}')
477
+ }
478
+
479
+ generateForEachStatement(node) {
480
+ const array = this.generateNode(node.array || node.right)
481
+ const value = this.generateIdentifier(node.value || node.left)
482
+ const key = node.key ? this.generateIdentifier(node.key) + ' => ' : ''
483
+
484
+ this.writeLine(`foreach (${array} as ${key}${value}) {`)
485
+ this.indent++
486
+ this.generateNode(node.body)
487
+ this.indent--
488
+ this.writeLine('}')
489
+ }
490
+
491
+ generateWhileStatement(node) {
492
+ const test = this.generateNode(node.test)
493
+ this.writeLine(`while (${test}) {`)
494
+ this.indent++
495
+ this.generateNode(node.body)
496
+ this.indent--
497
+ this.writeLine('}')
498
+ }
499
+
500
+ generateDoWhileStatement(node) {
501
+ this.writeLine('do {')
502
+ this.indent++
503
+ this.generateNode(node.body)
504
+ this.indent--
505
+ const test = this.generateNode(node.test)
506
+ this.writeLine(`} while (${test});`)
507
+ }
508
+
509
+ generateSwitchStatement(node) {
510
+ const discriminant = this.generateNode(node.discriminant)
511
+ this.writeLine(`switch (${discriminant}) {`)
512
+ this.indent++
513
+
514
+ for (const caseNode of node.cases || []) {
515
+ if (caseNode.test) {
516
+ const test = this.generateNode(caseNode.test)
517
+ this.writeLine(`case ${test}:`)
518
+ } else {
519
+ this.writeLine('default:')
520
+ }
521
+ this.indent++
522
+ for (const stmt of caseNode.consequent || []) {
523
+ this.generateNode(stmt)
524
+ }
525
+ this.indent--
526
+ }
527
+
528
+ this.indent--
529
+ this.writeLine('}')
530
+ }
531
+
532
+ generateTryStatement(node) {
533
+ this.writeLine('try {')
534
+ this.indent++
535
+ this.generateNode(node.block)
536
+ this.indent--
537
+
538
+ if (node.handler) {
539
+ const exceptionType = node.handler.type || 'Exception'
540
+ const param = node.handler.param ? this.generateIdentifier(node.handler.param) : '$e'
541
+ this.writeLine(`} catch (${exceptionType} ${param}) {`)
542
+ this.indent++
543
+ this.generateNode(node.handler.body)
544
+ this.indent--
545
+ }
546
+
547
+ if (node.finalizer) {
548
+ this.writeLine('} finally {')
549
+ this.indent++
550
+ this.generateNode(node.finalizer)
551
+ this.indent--
552
+ }
553
+
554
+ this.writeLine('}')
555
+ }
556
+
557
+ generateReturnStatement(node) {
558
+ if (node.argument) {
559
+ const arg = this.generateNode(node.argument)
560
+ this.writeLine(`return ${arg};`)
561
+ } else {
562
+ this.writeLine('return;')
563
+ }
564
+ }
565
+
566
+ generateThrowStatement(node) {
567
+ const arg = this.generateNode(node.argument)
568
+ this.writeLine(`throw ${arg};`)
569
+ }
570
+
571
+ generateExpressionStatement(node) {
572
+ const expr = this.generateNode(node.expression)
573
+ this.writeLine(`${expr};`)
574
+ }
575
+
576
+ generateEchoStatement(node) {
577
+ const args = (node.arguments || [node.argument]).map(a => this.generateNode(a)).join(', ')
578
+ this.writeLine(`echo ${args};`)
579
+ }
580
+
581
+ generateNamespace(node) {
582
+ this.writeLine(`namespace ${node.name};`)
583
+ this.writeLine('')
584
+ }
585
+
586
+ generateUseStatement(node) {
587
+ const name = node.name || node.source
588
+ const alias = node.alias ? ' as ' + node.alias : ''
589
+ this.writeLine(`use ${name}${alias};`)
590
+ }
591
+
592
+ generateCallExpression(node) {
593
+ const callee = typeof node.callee === 'string'
594
+ ? this.translate(node.callee)
595
+ : this.generateNode(node.callee)
596
+ const args = (node.arguments || []).map(a => this.generateNode(a)).join(', ')
597
+ return `${callee}(${args})`
598
+ }
599
+
600
+ generateMemberExpression(node) {
601
+ const object = this.generateNode(node.object)
602
+ const property = typeof node.property === 'string'
603
+ ? node.property
604
+ : this.generateNode(node.property)
605
+
606
+ if (node.static) {
607
+ return `${object}::${property}`
608
+ }
609
+ if (node.computed) {
610
+ return `${object}[${property}]`
611
+ }
612
+ return `${object}->${property}`
613
+ }
614
+
615
+ generateBinaryExpression(node) {
616
+ const left = this.generateNode(node.left)
617
+ const right = this.generateNode(node.right)
618
+ let op = node.operator
619
+
620
+ const opMap = {
621
+ 'et': '&&',
622
+ 'ou': '||',
623
+ 'concatener': '.'
624
+ }
625
+ op = opMap[op] || op
626
+
627
+ return `${left} ${op} ${right}`
628
+ }
629
+
630
+ generateAssignmentExpression(node) {
631
+ const left = this.generateNode(node.left)
632
+ const right = this.generateNode(node.right)
633
+ const op = node.operator || '='
634
+ return `${left} ${op} ${right}`
635
+ }
636
+
637
+ generateArrayExpression(node) {
638
+ const elements = (node.elements || []).map(e => {
639
+ if (e.key) {
640
+ const key = this.generateNode(e.key)
641
+ const value = this.generateNode(e.value || e)
642
+ return `${key} => ${value}`
643
+ }
644
+ return this.generateNode(e)
645
+ })
646
+
647
+ if (elements.length === 0) return '[]'
648
+ if (elements.length <= 3) {
649
+ return `[${elements.join(', ')}]`
650
+ }
651
+ return `[\n${this.getIndent()} ${elements.join(',\n' + this.getIndent() + ' ')}\n${this.getIndent()}]`
652
+ }
653
+
654
+ generateObjectExpression(node) {
655
+ return this.generateArrayExpression(node)
656
+ }
657
+
658
+ generateIdentifier(node) {
659
+ const name = node.name || node
660
+ if (typeof name !== 'string') return '$var'
661
+
662
+ const translated = this.translate(name)
663
+
664
+ if (translated.startsWith('$')) return translated
665
+ if (['true', 'false', 'null', 'self', 'parent', 'static'].includes(translated)) {
666
+ return translated
667
+ }
668
+ if (/^[A-Z]/.test(translated)) return translated
669
+
670
+ return '$' + translated
671
+ }
672
+
673
+ generateLiteral(node) {
674
+ if (node.raw) return node.raw
675
+ if (typeof node.value === 'string') {
676
+ return `"${node.value.replace(/"/g, '\\"')}"`
677
+ }
678
+ if (node.value === null) return 'null'
679
+ if (node.value === true) return 'true'
680
+ if (node.value === false) return 'false'
681
+ return String(node.value)
682
+ }
683
+
684
+ generateNewExpression(node) {
685
+ const callee = typeof node.callee === 'string'
686
+ ? node.callee
687
+ : this.generateNode(node.callee)
688
+ const args = (node.arguments || []).map(a => this.generateNode(a)).join(', ')
689
+ return `new ${callee}(${args})`
690
+ }
691
+
692
+ writeLine(text) {
693
+ if (this.output === '') {
694
+ this.output = '<?php '
695
+ }
696
+ this.output += this.getIndent() + text + '\n'
697
+ }
698
+
699
+ getIndent() {
700
+ return ' '.repeat(this.indent)
701
+ }
702
+ }
703
+
704
+ module.exports = {
705
+ PHPGenerator
706
+ }