ether-code 0.9.1 → 0.9.2

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.
@@ -5,571 +5,778 @@ class PHPGenerator {
5
5
  this.i18n = null
6
6
  this.indent = 0
7
7
  this.output = ''
8
- this.translationMap = {}
9
- this.functionMap = {}
10
- this.supportedLanguages = ['fr', 'en', 'es', 'ru', 'zh', 'ja']
11
8
 
12
9
  if (i18nPath) {
13
10
  this.loadI18n(i18nPath)
14
11
  }
12
+
13
+ this.buildFunctionMap()
15
14
  }
16
15
 
17
16
  loadI18n(filePath) {
18
17
  const content = fs.readFileSync(filePath, 'utf-8')
19
18
  this.i18n = JSON.parse(content)
20
- this.buildTranslationMap()
21
- }
22
-
23
- buildTranslationMap() {
24
- this.translationMap = {}
25
- this.functionMap = {}
26
-
27
- if (!this.i18n) return
28
-
29
- for (const [sectionName, section] of Object.entries(this.i18n)) {
30
- if (sectionName === 'metadata' || typeof section !== 'object') continue
31
-
32
- for (const lang of this.supportedLanguages) {
33
- if (!section[lang]) continue
34
-
35
- for (const [etherWord, phpCode] of Object.entries(section[lang])) {
36
- const fixed = this.normalizeUTF8(etherWord)
37
- const normalizedKey = fixed.toLowerCase().trim()
38
- const cleanPhp = this.cleanPhpValue(phpCode)
39
-
40
- if (phpCode.endsWith('()')) {
41
- this.functionMap[normalizedKey] = cleanPhp
42
- const withoutAccents = this.removeAccents(normalizedKey)
43
- if (withoutAccents !== normalizedKey) {
44
- this.functionMap[withoutAccents] = cleanPhp
45
- }
46
- }
47
-
48
- this.translationMap[normalizedKey] = cleanPhp
49
-
50
- const withoutAccents = this.removeAccents(normalizedKey)
51
- if (withoutAccents !== normalizedKey) {
52
- this.translationMap[withoutAccents] = cleanPhp
53
- }
54
- }
55
- }
56
- }
57
19
  }
58
20
 
59
- normalizeUTF8(str) {
21
+ normalizeAccents(str) {
60
22
  if (typeof str !== 'string') return String(str)
61
- try {
62
- const bytes = new Uint8Array(str.length)
63
- for (let i = 0; i < str.length; i++) {
64
- bytes[i] = str.charCodeAt(i)
65
- }
66
- const decoder = new TextDecoder('utf-8')
67
- return decoder.decode(bytes)
68
- } catch (e) {
69
- return str
23
+ return str.toLowerCase()
24
+ .normalize('NFD')
25
+ .replace(/[\u0300-\u036f]/g, '')
26
+ .replace(/-/g, ' ')
27
+ .replace(/_/g, ' ')
28
+ }
29
+
30
+ buildFunctionMap() {
31
+ this.functionMap = {
32
+ 'est chaine': 'is_string',
33
+ 'est entier': 'is_int',
34
+ 'est flottant': 'is_float',
35
+ 'est tableau': 'is_array',
36
+ 'est booleen': 'is_bool',
37
+ 'est nul': 'is_null',
38
+ 'est null': 'is_null',
39
+ 'est objet': 'is_object',
40
+ 'est numerique': 'is_numeric',
41
+ 'est callable': 'is_callable',
42
+ 'est appelable': 'is_callable',
43
+ 'est ressource': 'is_resource',
44
+ 'est scalaire': 'is_scalar',
45
+ 'est vide': 'empty',
46
+ 'est iterable': 'is_iterable',
47
+ 'est comptable': 'is_countable',
48
+ 'obtenir type': 'gettype',
49
+ 'definir type': 'settype',
50
+ 'valeur entier': 'intval',
51
+ 'valeur entiere': 'intval',
52
+ 'valeur flottant': 'floatval',
53
+ 'valeur chaine': 'strval',
54
+ 'valeur booleen': 'boolval',
55
+ 'longueur': 'strlen',
56
+ 'sous chaine': 'substr',
57
+ 'sous-chaine': 'substr',
58
+ 'position': 'strpos',
59
+ 'remplacer': 'str_replace',
60
+ 'exploser': 'explode',
61
+ 'imploser': 'implode',
62
+ 'majuscules': 'strtoupper',
63
+ 'minuscules': 'strtolower',
64
+ 'couper espaces': 'trim',
65
+ 'rogner': 'trim',
66
+ 'rogner gauche': 'ltrim',
67
+ 'rogner droite': 'rtrim',
68
+ 'repeter': 'str_repeat',
69
+ 'inverser chaine': 'strrev',
70
+ 'remplir': 'str_pad',
71
+ 'remplir gauche': 'str_pad',
72
+ 'compter': 'count',
73
+ 'trier': 'sort',
74
+ 'trier inverse': 'rsort',
75
+ 'trier associatif': 'asort',
76
+ 'trier cles': 'ksort',
77
+ 'inverser': 'array_reverse',
78
+ 'fusionner': 'array_merge',
79
+ 'fusionner tableaux': 'array_merge',
80
+ 'filtrer': 'array_filter',
81
+ 'filtrer tableau': 'array_filter',
82
+ 'mapper': 'array_map',
83
+ 'mapper tableau': 'array_map',
84
+ 'transformer': 'array_map',
85
+ 'reduire': 'array_reduce',
86
+ 'reduire tableau': 'array_reduce',
87
+ 'cles': 'array_keys',
88
+ 'tableau cles': 'array_keys',
89
+ 'valeurs': 'array_values',
90
+ 'tableau valeurs': 'array_values',
91
+ 'pousser': 'array_push',
92
+ 'ajouter': 'array_push',
93
+ 'retirer': 'array_pop',
94
+ 'rechercher': 'array_search',
95
+ 'existe': 'array_key_exists',
96
+ 'cle existe': 'array_key_exists',
97
+ 'dans tableau': 'in_array',
98
+ 'somme': 'array_sum',
99
+ 'somme tableau': 'array_sum',
100
+ 'unique': 'array_unique',
101
+ 'decouper': 'array_slice',
102
+ 'epissure': 'array_splice',
103
+ 'combiner': 'array_combine',
104
+ 'remplir tableau': 'array_fill',
105
+ 'colonne': 'array_column',
106
+ 'compter valeurs': 'array_count_values',
107
+ 'difference': 'array_diff',
108
+ 'intersection': 'array_intersect',
109
+ 'premier': 'reset',
110
+ 'dernier': 'end',
111
+ 'suivant': 'next',
112
+ 'precedent': 'prev',
113
+ 'courant': 'current',
114
+ 'cle courante': 'key',
115
+ 'melanger': 'shuffle',
116
+ 'absolu': 'abs',
117
+ 'plafond': 'ceil',
118
+ 'plancher': 'floor',
119
+ 'arrondir': 'round',
120
+ 'arrondi': 'round',
121
+ 'racine carree': 'sqrt',
122
+ 'puissance': 'pow',
123
+ 'aleatoire': 'rand',
124
+ 'aleatoire entier': 'random_int',
125
+ 'maximum': 'max',
126
+ 'minimum': 'min',
127
+ 'pi': 'pi',
128
+ 'exponentielle': 'exp',
129
+ 'logarithme': 'log',
130
+ 'logarithme10': 'log10',
131
+ 'sinus': 'sin',
132
+ 'cosinus': 'cos',
133
+ 'tangente': 'tan',
134
+ 'formater nombre': 'number_format',
135
+ 'date': 'date',
136
+ 'temps': 'time',
137
+ 'maintenant': 'time',
138
+ 'creer date': 'mktime',
139
+ 'date vers temps': 'strtotime',
140
+ 'formater date': 'date_format',
141
+ 'fichier existe': 'file_exists',
142
+ 'lire fichier': 'file_get_contents',
143
+ 'ecrire fichier': 'file_put_contents',
144
+ 'ouvrir fichier': 'fopen',
145
+ 'fermer fichier': 'fclose',
146
+ 'lire ligne': 'fgets',
147
+ 'ecrire ligne': 'fputs',
148
+ 'est fichier': 'is_file',
149
+ 'est repertoire': 'is_dir',
150
+ 'creer repertoire': 'mkdir',
151
+ 'supprimer fichier': 'unlink',
152
+ 'renommer': 'rename',
153
+ 'copier': 'copy',
154
+ 'taille fichier': 'filesize',
155
+ 'encoder json': 'json_encode',
156
+ 'decoder json': 'json_decode',
157
+ 'json encoder': 'json_encode',
158
+ 'json decoder': 'json_decode',
159
+ 'correspondre': 'preg_match',
160
+ 'regex correspondance': 'preg_match',
161
+ 'regex toutes': 'preg_match_all',
162
+ 'regex remplacer': 'preg_replace',
163
+ 'demarrer session': 'session_start',
164
+ 'detruire session': 'session_destroy',
165
+ 'definir cookie': 'setcookie',
166
+ 'hacher': 'hash',
167
+ 'hacher mot de passe': 'password_hash',
168
+ 'verifier mot de passe': 'password_verify',
169
+ 'md5': 'md5',
170
+ 'sha1': 'sha1',
171
+ 'base64 encoder': 'base64_encode',
172
+ 'base64 decoder': 'base64_decode',
173
+ 'url encoder': 'urlencode',
174
+ 'url decoder': 'urldecode',
175
+ 'htmlentites': 'htmlentities',
176
+ 'html special': 'htmlspecialchars',
177
+ 'enlever balises': 'strip_tags',
178
+ 'echapper': 'addslashes',
179
+ 'preparer': 'prepare',
180
+ 'executer': 'execute',
181
+ 'recuperer': 'fetch',
182
+ 'recuperer tout': 'fetchAll',
183
+ 'nombre lignes': 'rowCount',
184
+ 'dernier id': 'lastInsertId',
185
+ 'afficher info': 'var_dump',
186
+ 'imprimer r': 'print_r',
187
+ 'exporter': 'var_export',
188
+ 'vide': 'empty',
189
+ 'defini': 'isset',
190
+ 'supprimer': 'unset',
191
+ 'sortie': 'exit',
192
+ 'mourir': 'die',
193
+ 'dormir': 'sleep',
194
+ 'micro dormir': 'usleep',
195
+ 'entete': 'header',
196
+ 'en tete': 'header',
197
+ 'rediriger': 'header',
198
+ 'serialiser': 'serialize',
199
+ 'deserialiser': 'unserialize',
200
+ 'formater': 'format',
201
+ 'getmessage': 'getMessage'
70
202
  }
71
- }
72
203
 
73
- cleanPhpValue(value) {
74
- if (typeof value !== 'string') return value
75
- let cleaned = value.replace(/\(\)$/, '')
76
- return cleaned
204
+ this.keywordMap = {
205
+ 'variable': '',
206
+ 'constante': 'const',
207
+ 'fonction': 'function',
208
+ 'retourner': 'return',
209
+ 'si': 'if',
210
+ 'sinon': 'else',
211
+ 'sinon si': 'elseif',
212
+ 'pour': 'for',
213
+ 'pour chaque': 'foreach',
214
+ 'tant que': 'while',
215
+ 'faire': 'do',
216
+ 'selon': 'switch',
217
+ 'cas': 'case',
218
+ 'defaut': 'default',
219
+ 'sortir': 'break',
220
+ 'continuer': 'continue',
221
+ 'essayer': 'try',
222
+ 'attraper': 'catch',
223
+ 'finalement': 'finally',
224
+ 'lancer': 'throw',
225
+ 'nouveau': 'new',
226
+ 'nouvelle': 'new',
227
+ 'classe': 'class',
228
+ 'interface': 'interface',
229
+ 'trait': 'trait',
230
+ 'etend': 'extends',
231
+ 'etendre': 'extends',
232
+ 'implemente': 'implements',
233
+ 'implementer': 'implements',
234
+ 'utiliser': 'use',
235
+ 'public': 'public',
236
+ 'publique': 'public',
237
+ 'prive': 'private',
238
+ 'privee': 'private',
239
+ 'protege': 'protected',
240
+ 'protegee': 'protected',
241
+ 'statique': 'static',
242
+ 'final': 'final',
243
+ 'finale': 'final',
244
+ 'abstrait': 'abstract',
245
+ 'abstraite': 'abstract',
246
+ 'constructeur': '__construct',
247
+ 'destructeur': '__destruct',
248
+ 'obtenir': '__get',
249
+ 'definir': '__set',
250
+ 'appeler': '__call',
251
+ 'vers chaine': '__toString',
252
+ 'espace de noms': 'namespace',
253
+ 'comme': 'as',
254
+ 'vrai': 'true',
255
+ 'faux': 'false',
256
+ 'nul': 'null',
257
+ 'rien': 'null',
258
+ 'ceci': '$this',
259
+ 'parent': 'parent',
260
+ 'self': 'self',
261
+ 'soi': 'self',
262
+ 'et': '&&',
263
+ 'ou': '||',
264
+ 'non': '!',
265
+ 'egal': '===',
266
+ 'different': '!==',
267
+ 'concatener': '.',
268
+ 'tableau': 'array',
269
+ 'chaine': 'string',
270
+ 'entier': 'int',
271
+ 'flottant': 'float',
272
+ 'booleen': 'bool',
273
+ 'objet': 'object',
274
+ 'vide type': 'void',
275
+ 'void': 'void',
276
+ 'mixte': 'mixed',
277
+ 'afficher': 'echo',
278
+ 'imprimer': 'print',
279
+ 'inclure': 'include',
280
+ 'inclure une fois': 'include_once',
281
+ 'requiert': 'require',
282
+ 'requerir': 'require',
283
+ 'requiert une fois': 'require_once',
284
+ 'requerir une fois': 'require_once',
285
+ 'rendement': 'yield',
286
+ 'rendement de': 'yield from'
287
+ }
77
288
  }
78
289
 
79
290
  translate(word) {
80
- if (!word || typeof word !== 'string') return word
81
-
82
- const normalized = word.toLowerCase().trim()
83
-
84
- if (this.translationMap[normalized]) {
85
- return this.translationMap[normalized]
86
- }
87
-
88
- const withoutAccents = this.removeAccents(normalized)
89
- if (this.translationMap[withoutAccents]) {
90
- return this.translationMap[withoutAccents]
91
- }
92
-
93
- return word
291
+ if (!word) return word
292
+ const normalized = this.normalizeAccents(word)
293
+ return this.functionMap[normalized] || this.keywordMap[normalized] || word
94
294
  }
95
295
 
96
296
  translateFunction(word) {
97
- if (!word || typeof word !== 'string') return word
98
-
99
- const normalized = word.toLowerCase().trim()
100
- const withoutAccents = this.removeAccents(normalized)
101
-
102
- if (this.functionMap[normalized]) {
103
- return this.functionMap[normalized]
104
- }
105
-
106
- if (this.functionMap[withoutAccents]) {
107
- return this.functionMap[withoutAccents]
108
- }
109
-
110
- if (this.translationMap[normalized]) {
111
- return this.translationMap[normalized]
112
- }
113
-
114
- if (this.translationMap[withoutAccents]) {
115
- return this.translationMap[withoutAccents]
116
- }
117
-
118
- return word
297
+ if (!word) return word
298
+ const normalized = this.normalizeAccents(word)
299
+ return this.functionMap[normalized] || word
119
300
  }
120
301
 
121
- removeAccents(str) {
122
- return str.normalize('NFD').replace(/[\u0300-\u036f]/g, '')
302
+ translateKeyword(word) {
303
+ if (!word) return word
304
+ const normalized = this.normalizeAccents(word)
305
+ return this.keywordMap[normalized] || word
306
+ }
307
+
308
+ extractTypeName(typeNode) {
309
+ if (!typeNode) return ''
310
+ if (typeof typeNode === 'string') return this.translateKeyword(typeNode)
311
+ if (typeNode.name) return this.translateKeyword(typeNode.name)
312
+ if (typeNode.type === 'Identifier') return this.translateKeyword(typeNode.name || typeNode.value || '')
313
+ if (typeNode.value) return this.translateKeyword(typeNode.value)
314
+ return ''
123
315
  }
124
316
 
125
317
  generate(ast) {
126
- this.output = '<?php\n'
318
+ this.output = ''
127
319
  this.indent = 0
128
-
129
- if (ast.strict) {
130
- this.writeLine("declare(strict_types=1);")
131
- this.writeLine('')
320
+
321
+ if (ast.phpTag !== false) {
322
+ this.output = '<?php\n'
132
323
  }
133
324
 
134
- if (Array.isArray(ast.body)) {
135
- for (const node of ast.body) {
325
+ if (Array.isArray(ast)) {
326
+ for (const node of ast) {
136
327
  this.generateNode(node)
137
328
  }
138
- } else if (ast.body) {
139
- this.generateNode(ast.body)
140
- } else {
329
+ } else if (ast && ast.type === 'Program') {
330
+ for (const node of ast.body || []) {
331
+ this.generateNode(node)
332
+ }
333
+ } else if (ast && ast.type) {
141
334
  this.generateNode(ast)
335
+ } else if (ast && ast.body) {
336
+ for (const node of ast.body) {
337
+ this.generateNode(node)
338
+ }
142
339
  }
143
340
 
144
- return this.output
341
+ return this.output.trim()
145
342
  }
146
343
 
147
344
  generateNode(node) {
148
345
  if (!node) return ''
149
346
 
150
- if (Array.isArray(node)) {
151
- return node.map(n => this.generateNode(n)).join('')
347
+ switch (node.type) {
348
+ case 'Program':
349
+ for (const stmt of node.body || []) {
350
+ this.generateNode(stmt)
351
+ }
352
+ return ''
353
+ case 'DeclareStatement':
354
+ return this.generateDeclareStatement(node)
355
+ case 'NamespaceDeclaration':
356
+ return this.generateNamespace(node)
357
+ case 'UseStatement':
358
+ return this.generateUseStatement(node)
359
+ case 'ConstantDeclaration':
360
+ return this.generateConstantDeclaration(node)
361
+ case 'VariableDeclaration':
362
+ return this.generateVariableDeclaration(node)
363
+ case 'FunctionDeclaration':
364
+ return this.generateFunctionDeclaration(node)
365
+ case 'ClassDeclaration':
366
+ return this.generateClassDeclaration(node)
367
+ case 'InterfaceDeclaration':
368
+ return this.generateInterfaceDeclaration(node)
369
+ case 'TraitDeclaration':
370
+ return this.generateTraitDeclaration(node)
371
+ case 'EnumDeclaration':
372
+ return this.generateEnumDeclaration(node)
373
+ case 'IfStatement':
374
+ return this.generateIfStatement(node)
375
+ case 'ForStatement':
376
+ return this.generateForStatement(node)
377
+ case 'ForEachStatement':
378
+ return this.generateForEachStatement(node)
379
+ case 'WhileStatement':
380
+ return this.generateWhileStatement(node)
381
+ case 'DoWhileStatement':
382
+ return this.generateDoWhileStatement(node)
383
+ case 'SwitchStatement':
384
+ return this.generateSwitchStatement(node)
385
+ case 'TryStatement':
386
+ return this.generateTryStatement(node)
387
+ case 'ReturnStatement':
388
+ return this.generateReturnStatement(node)
389
+ case 'ThrowStatement':
390
+ return this.generateThrowStatement(node)
391
+ case 'BreakStatement':
392
+ this.writeLine('break;')
393
+ return ''
394
+ case 'ContinueStatement':
395
+ this.writeLine('continue;')
396
+ return ''
397
+ case 'ExpressionStatement':
398
+ return this.generateExpressionStatement(node)
399
+ case 'EchoStatement':
400
+ return this.generateEchoStatement(node)
401
+ case 'BlockStatement':
402
+ return this.generateBlockStatement(node)
403
+ case 'CallExpression':
404
+ return this.generateCallExpression(node)
405
+ case 'MemberExpression':
406
+ return this.generateMemberExpression(node)
407
+ case 'StaticMemberExpression':
408
+ return this.generateStaticMemberExpression(node)
409
+ case 'BinaryExpression':
410
+ return this.generateBinaryExpression(node)
411
+ case 'LogicalExpression':
412
+ return this.generateBinaryExpression(node)
413
+ case 'AssignmentExpression':
414
+ return this.generateAssignmentExpression(node)
415
+ case 'UnaryExpression':
416
+ return this.generateUnaryExpression(node)
417
+ case 'UpdateExpression':
418
+ return this.generateUpdateExpression(node)
419
+ case 'ConditionalExpression':
420
+ return this.generateConditionalExpression(node)
421
+ case 'ArrayExpression':
422
+ return this.generateArrayExpression(node)
423
+ case 'ObjectExpression':
424
+ return this.generateObjectExpression(node)
425
+ case 'Identifier':
426
+ return this.generateIdentifier(node)
427
+ case 'Variable':
428
+ return this.generateVariable(node)
429
+ case 'Literal':
430
+ return this.generateLiteral(node)
431
+ case 'StringLiteral':
432
+ return this.generateStringLiteral(node)
433
+ case 'NewExpression':
434
+ return this.generateNewExpression(node)
435
+ case 'ThisExpression':
436
+ return '$this'
437
+ case 'FunctionExpression':
438
+ return this.generateFunctionExpression(node)
439
+ case 'ArrowFunctionExpression':
440
+ return this.generateArrowFunctionExpression(node)
441
+ case 'IndexExpression':
442
+ return this.generateIndexExpression(node)
443
+ case 'NullCoalesceExpression':
444
+ return this.generateNullCoalesceExpression(node)
445
+ case 'YieldExpression':
446
+ return this.generateYieldExpression(node)
447
+ default:
448
+ return ''
152
449
  }
450
+ }
153
451
 
154
- const type = node.type || node.nodeType
155
-
156
- const generators = {
157
- 'Program': () => this.generateProgram(node),
158
- 'FunctionDeclaration': () => this.generateFunctionDeclaration(node),
159
- 'ClassDeclaration': () => this.generateClassDeclaration(node),
160
- 'InterfaceDeclaration': () => this.generateInterfaceDeclaration(node),
161
- 'TraitDeclaration': () => this.generateTraitDeclaration(node),
162
- 'EnumDeclaration': () => this.generateEnumDeclaration(node),
163
- 'MethodDefinition': () => this.generateMethodDefinition(node),
164
- 'MethodDeclaration': () => this.generateMethodDefinition(node),
165
- 'PropertyDeclaration': () => this.generatePropertyDeclaration(node),
166
- 'VariableDeclaration': () => this.generateVariableDeclaration(node),
167
- 'ConstantDeclaration': () => this.generateConstantDeclaration(node),
168
- 'IfStatement': () => this.generateIfStatement(node),
169
- 'ForStatement': () => this.generateForStatement(node),
170
- 'ForEachStatement': () => this.generateForEachStatement(node),
171
- 'ForeachStatement': () => this.generateForEachStatement(node),
172
- 'WhileStatement': () => this.generateWhileStatement(node),
173
- 'DoWhileStatement': () => this.generateDoWhileStatement(node),
174
- 'SwitchStatement': () => this.generateSwitchStatement(node),
175
- 'MatchExpression': () => this.generateMatchExpression(node),
176
- 'TryStatement': () => this.generateTryStatement(node),
177
- 'ReturnStatement': () => this.generateReturnStatement(node),
178
- 'YieldExpression': () => this.generateYieldExpression(node),
179
- 'ThrowStatement': () => this.generateThrowStatement(node),
180
- 'BreakStatement': () => this.writeLine('break;'),
181
- 'ContinueStatement': () => this.writeLine('continue;'),
182
- 'ExpressionStatement': () => this.generateExpressionStatement(node),
183
- 'EchoStatement': () => this.generateEchoStatement(node),
184
- 'PrintStatement': () => this.generatePrintStatement(node),
185
- 'Namespace': () => this.generateNamespace(node),
186
- 'UseStatement': () => this.generateUseStatement(node),
187
- 'DeclareStatement': () => this.generateDeclareStatement(node),
188
- 'IncludeStatement': () => this.generateIncludeStatement(node),
189
- 'Include': () => this.generateInclude(node),
190
- 'CallExpression': () => this.generateCallExpression(node),
191
- 'MemberExpression': () => this.generateMemberExpression(node),
192
- 'StaticMemberExpression': () => this.generateStaticMemberExpression(node),
193
- 'IndexExpression': () => this.generateIndexExpression(node),
194
- 'BinaryExpression': () => this.generateBinaryExpression(node),
195
- 'LogicalExpression': () => this.generateBinaryExpression(node),
196
- 'UnaryExpression': () => this.generateUnaryExpression(node),
197
- 'AssignmentExpression': () => this.generateAssignmentExpression(node),
198
- 'UpdateExpression': () => this.generateUpdateExpression(node),
199
- 'ConditionalExpression': () => this.generateConditionalExpression(node),
200
- 'NullCoalescing': () => this.generateNullCoalescing(node),
201
- 'ArrayExpression': () => this.generateArrayExpression(node),
202
- 'ObjectExpression': () => this.generateObjectExpression(node),
203
- 'ArrowFunction': () => this.generateArrowFunction(node),
204
- 'ArrowFunctionExpression': () => this.generateArrowFunction(node),
205
- 'FunctionExpression': () => this.generateFunctionExpression(node),
206
- 'Closure': () => this.generateClosure(node),
207
- 'Identifier': () => this.generateIdentifier(node),
208
- 'ThisExpression': () => '$this',
209
- 'Literal': () => this.generateLiteral(node),
210
- 'StringLiteral': () => this.generateLiteral(node),
211
- 'NumericLiteral': () => this.generateLiteral(node),
212
- 'BooleanLiteral': () => this.generateLiteral(node),
213
- 'NullLiteral': () => 'null',
214
- 'NewExpression': () => this.generateNewExpression(node),
215
- 'InstanceOf': () => this.generateInstanceOf(node),
216
- 'BlockStatement': () => this.generateBlockStatement(node),
217
- 'NamespaceDeclaration': () => this.generateNamespace(node),
218
- 'EmptyStatement': () => '',
219
- 'Comment': () => ''
452
+ generateDeclareStatement(node) {
453
+ const directives = []
454
+ if (node.strictTypes) {
455
+ directives.push('strict_types=1')
220
456
  }
221
-
222
- if (generators[type]) {
223
- return generators[type]()
457
+ if (node.ticks) {
458
+ directives.push(`ticks=${node.ticks}`)
224
459
  }
225
-
226
- if (node.expression) {
227
- return this.generateNode(node.expression)
460
+ if (node.encoding) {
461
+ directives.push(`encoding='${node.encoding}'`)
462
+ }
463
+ if (directives.length > 0) {
464
+ this.writeLine(`declare(${directives.join(', ')});`)
228
465
  }
466
+ return ''
467
+ }
229
468
 
469
+ generateNamespace(node) {
470
+ const name = node.name || node.path
471
+ this.writeLine(`namespace ${name};`)
472
+ this.writeLine('')
230
473
  return ''
231
474
  }
232
475
 
233
- generateProgram(node) {
234
- for (const stmt of node.body || []) {
235
- this.generateNode(stmt)
476
+ generateUseStatement(node) {
477
+ const name = node.name || node.source
478
+ const alias = node.alias ? ` as ${node.alias}` : ''
479
+ this.writeLine(`use ${name}${alias};`)
480
+ return ''
481
+ }
482
+
483
+ generateConstantDeclaration(node) {
484
+ const name = node.name || node.id?.name
485
+ const value = this.generateNode(node.value || node.init)
486
+ this.writeLine(`define('${name}', ${value});`)
487
+ return ''
488
+ }
489
+
490
+ generateVariableDeclaration(node) {
491
+ const declarations = node.declarations || [node]
492
+
493
+ for (const decl of declarations) {
494
+ const name = this.generateVariable(decl.id || decl)
495
+ if (decl.init) {
496
+ const init = this.generateNode(decl.init)
497
+ this.writeLine(`${name} = ${init};`)
498
+ }
236
499
  }
500
+ return ''
237
501
  }
238
502
 
239
503
  generateFunctionDeclaration(node) {
240
- const name = node.name || node.id?.name || 'anonymous'
241
- const params = this.generateParams(node.params || [])
242
- const returnType = node.returnType ? ': ' + this.translateType(node.returnType) : ''
504
+ const name = node.name || node.id?.name || ''
505
+ const params = (node.params || []).map(p => this.generateParam(p)).join(', ')
506
+ let returnType = ''
243
507
 
508
+ if (node.returnType) {
509
+ const type = this.extractTypeName(node.returnType)
510
+ if (type) returnType = ': ' + type
511
+ }
512
+
244
513
  this.writeLine(`function ${name}(${params})${returnType} {`)
245
514
  this.indent++
246
- this.generateNode(node.body)
515
+
516
+ if (node.body) {
517
+ this.generateBody(node.body)
518
+ }
519
+
247
520
  this.indent--
248
521
  this.writeLine('}')
249
522
  this.writeLine('')
523
+ return ''
250
524
  }
251
525
 
252
- generateParams(params) {
253
- return params.map(p => {
254
- let result = ''
255
-
256
- if (p.visibility) {
257
- result += this.translate(p.visibility) + ' '
258
- }
259
-
260
- if (p.typeHint) {
261
- result += this.translateType(p.typeHint) + ' '
262
- }
263
-
264
- if (p.variadic) {
265
- result += '...'
266
- }
267
-
268
- if (p.reference || p.byReference) {
269
- result += '&'
270
- }
271
-
272
- const name = p.name || p.id?.name || p
273
- result += '$' + (typeof name === 'string' ? name : name.name || 'param')
274
-
275
- if (p.default !== undefined && p.default !== null) {
276
- result += ' = ' + this.generateNode(p.default)
277
- }
278
-
279
- return result
280
- }).join(', ')
281
- }
282
-
283
- translateType(type) {
284
- if (!type) return ''
526
+ generateParam(param) {
527
+ let result = ''
285
528
 
286
- if (typeof type === 'string') {
287
- const translated = this.translate(type)
288
- const typeMap = {
289
- 'chaîne': 'string', 'chaine': 'string', 'string': 'string',
290
- 'entier': 'int', 'integer': 'int', 'int': 'int',
291
- 'flottant': 'float', 'float': 'float',
292
- 'booléen': 'bool', 'booleen': 'bool', 'boolean': 'bool', 'bool': 'bool',
293
- 'tableau': 'array', 'array': 'array',
294
- 'objet': 'object', 'object': 'object',
295
- 'nul': 'null', 'null': 'null',
296
- 'vide': 'void', 'void': 'void',
297
- 'mixte': 'mixed', 'mixed': 'mixed',
298
- 'appelable': 'callable', 'callable': 'callable',
299
- 'itérable': 'iterable', 'iterable': 'iterable',
300
- 'jamais': 'never', 'never': 'never'
529
+ if (param.typeHint || (param.type && param.type !== 'Parameter' && param.type !== 'Identifier')) {
530
+ const type = param.typeHint || param.type
531
+ if (typeof type === 'string' && type !== 'Parameter' && type !== 'Identifier') {
532
+ result += this.translateKeyword(type) + ' '
533
+ } else if (type && type.name) {
534
+ result += this.translateKeyword(type.name) + ' '
301
535
  }
302
- return typeMap[translated.toLowerCase()] || typeMap[type.toLowerCase()] || type
303
536
  }
304
537
 
305
- if (type.type === 'TypeHint' && type.types && type.types.length > 0) {
306
- const prefix = type.nullable ? '?' : ''
307
- if (type.union || type.types.length > 1) {
308
- return prefix + type.types.map(t => this.translateType(t)).join('|')
309
- }
310
- return prefix + this.translateType(type.types[0])
538
+ if (param.reference) {
539
+ result += '&'
311
540
  }
312
541
 
313
- if (type.union) {
314
- return type.types.map(t => this.translateType(t)).join('|')
542
+ if (param.variadic) {
543
+ result += '...'
315
544
  }
316
545
 
317
- if (type.intersection) {
318
- return type.types.map(t => this.translateType(t)).join('&')
319
- }
546
+ result += this.generateVariable(param)
320
547
 
321
- if (type.nullable) {
322
- return '?' + this.translateType(type.type)
548
+ if (param.default !== undefined && param.default !== null) {
549
+ result += ' = ' + this.generateNode(param.default)
323
550
  }
324
551
 
325
- return type.name || type
552
+ return result
553
+ }
554
+
555
+ generateBody(body) {
556
+ if (Array.isArray(body)) {
557
+ for (const stmt of body) {
558
+ this.generateNode(stmt)
559
+ }
560
+ } else if (body && body.body) {
561
+ for (const stmt of body.body) {
562
+ this.generateNode(stmt)
563
+ }
564
+ } else if (body) {
565
+ this.generateNode(body)
566
+ }
326
567
  }
327
568
 
328
569
  generateClassDeclaration(node) {
329
570
  let declaration = ''
330
571
 
331
- const modifiers = node.modifiers || []
332
- if (modifiers.includes('abstract') || node.abstract) declaration += 'abstract '
333
- if (modifiers.includes('final') || node.final) declaration += 'final '
334
- if (modifiers.includes('readonly') || node.readonly) declaration += 'readonly '
335
-
336
- declaration += 'class ' + (node.name || node.id?.name)
572
+ if (node.abstract) declaration += 'abstract '
573
+ if (node.final) declaration += 'final '
574
+ if (node.readonly) declaration += 'readonly '
337
575
 
338
- if (node.superClass) {
339
- const superName = typeof node.superClass === 'string' ? node.superClass : node.superClass.name
340
- declaration += ' extends ' + superName
341
- } else if (node.extends) {
342
- declaration += ' extends ' + (typeof node.extends === 'string' ? node.extends : node.extends.name)
576
+ declaration += 'class ' + (node.name || node.id?.name || '')
577
+
578
+ if (node.extends || node.superClass) {
579
+ declaration += ' extends ' + (node.extends || node.superClass)
343
580
  }
344
-
345
- if (node.interfaces && node.interfaces.length > 0) {
346
- declaration += ' implements ' + node.interfaces.map(i => typeof i === 'string' ? i : i.name).join(', ')
347
- } else if (node.implements && node.implements.length > 0) {
348
- declaration += ' implements ' + node.implements.map(i => typeof i === 'string' ? i : i.name).join(', ')
581
+
582
+ if (node.implements && node.implements.length > 0) {
583
+ declaration += ' implements ' + node.implements.join(', ')
349
584
  }
350
-
585
+
351
586
  this.writeLine(declaration + ' {')
352
587
  this.indent++
353
-
354
- for (const member of node.body || node.members || []) {
355
- this.generateNode(member)
588
+
589
+ const members = node.body?.body || node.members || node.body || []
590
+ for (const member of members) {
591
+ this.generateClassMember(member)
356
592
  }
357
-
593
+
358
594
  this.indent--
359
595
  this.writeLine('}')
360
596
  this.writeLine('')
597
+ return ''
361
598
  }
362
599
 
363
- generateInterfaceDeclaration(node) {
364
- let declaration = 'interface ' + this.translate(node.name || node.id?.name)
600
+ generateClassMember(node) {
601
+ if (!node) return
602
+
603
+ if (node.type === 'UseTraitStatement') {
604
+ this.writeLine(`use ${node.traits.join(', ')};`)
605
+ return
606
+ }
607
+
608
+ const visibility = node.visibility || 'public'
609
+ const isStatic = node.static ? 'static ' : ''
610
+ const isReadonly = node.readonly ? 'readonly ' : ''
365
611
 
612
+ if (node.type === 'Property' || node.kind === 'property' || node.type === 'PropertyDeclaration') {
613
+ const name = '$' + (node.name || node.key?.name || node.id?.name || '')
614
+ let typeHint = ''
615
+ if (node.typeHint) {
616
+ typeHint = this.translateKeyword(node.typeHint) + ' '
617
+ }
618
+ const value = node.value ? ' = ' + this.generateNode(node.value) : ''
619
+ this.writeLine(`${visibility} ${isStatic}${isReadonly}${typeHint}${name}${value};`)
620
+ } else if (node.type === 'Method' || node.kind === 'method' || node.type === 'MethodDeclaration') {
621
+ const name = node.name || node.key?.name || ''
622
+ const translatedName = this.translateKeyword(name)
623
+ const params = (node.params || []).map(p => this.generateParam(p)).join(', ')
624
+ let returnType = ''
625
+
626
+ if (node.returnType) {
627
+ const type = this.extractTypeName(node.returnType)
628
+ if (type) returnType = ': ' + type
629
+ }
630
+
631
+ const abstractMod = node.abstract ? 'abstract ' : ''
632
+
633
+ if (node.abstract) {
634
+ this.writeLine(`${abstractMod}${visibility} ${isStatic}function ${translatedName}(${params})${returnType};`)
635
+ } else {
636
+ this.writeLine(`${visibility} ${isStatic}function ${translatedName}(${params})${returnType} {`)
637
+ this.indent++
638
+
639
+ if (node.body) {
640
+ this.generateBody(node.body)
641
+ }
642
+
643
+ this.indent--
644
+ this.writeLine('}')
645
+ }
646
+ this.writeLine('')
647
+ }
648
+ }
649
+
650
+ generateInterfaceDeclaration(node) {
651
+ let declaration = 'interface ' + (node.name || '')
652
+
366
653
  if (node.extends && node.extends.length > 0) {
367
- declaration += ' extends ' + node.extends.map(e => this.translate(e)).join(', ')
654
+ declaration += ' extends ' + node.extends.join(', ')
368
655
  }
369
-
656
+
370
657
  this.writeLine(declaration + ' {')
371
658
  this.indent++
372
-
373
- for (const member of node.body || node.members || []) {
374
- this.generateNode(member)
659
+
660
+ const members = node.body?.body || node.members || node.body || []
661
+ for (const member of members) {
662
+ this.generateInterfaceMember(member)
375
663
  }
376
-
664
+
377
665
  this.indent--
378
666
  this.writeLine('}')
379
667
  this.writeLine('')
668
+ return ''
380
669
  }
381
670
 
382
- generateTraitDeclaration(node) {
383
- this.writeLine('trait ' + this.translate(node.name || node.id?.name) + ' {')
384
- this.indent++
671
+ generateInterfaceMember(node) {
672
+ if (!node) return
673
+
674
+ const visibility = 'public'
675
+ const name = node.name || node.key?.name || ''
676
+ const translatedName = this.translateKeyword(name)
677
+ const params = (node.params || []).map(p => this.generateParam(p)).join(', ')
678
+ let returnType = ''
385
679
 
386
- for (const member of node.body || node.members || []) {
387
- this.generateNode(member)
680
+ if (node.returnType) {
681
+ const type = this.extractTypeName(node.returnType)
682
+ if (type) returnType = ': ' + type
388
683
  }
389
684
 
390
- this.indent--
391
- this.writeLine('}')
685
+ this.writeLine(`${visibility} function ${translatedName}(${params})${returnType};`)
392
686
  this.writeLine('')
393
687
  }
394
688
 
395
- generateEnumDeclaration(node) {
396
- let declaration = 'enum ' + this.translate(node.name || node.id?.name)
397
-
398
- if (node.backingType) {
399
- declaration += ': ' + this.translateType(node.backingType)
400
- }
401
-
402
- this.writeLine(declaration + ' {')
689
+ generateTraitDeclaration(node) {
690
+ this.writeLine('trait ' + (node.name || '') + ' {')
403
691
  this.indent++
404
-
405
- for (const member of node.cases || node.members || []) {
406
- if (member.value !== undefined) {
407
- this.writeLine(`case ${member.name} = ${this.generateNode(member.value)};`)
408
- } else {
409
- this.writeLine(`case ${member.name};`)
410
- }
411
- }
412
-
413
- for (const method of node.methods || []) {
414
- this.generateNode(method)
692
+
693
+ const members = node.body?.body || node.members || node.body || []
694
+ for (const member of members) {
695
+ this.generateClassMember(member)
415
696
  }
416
-
697
+
417
698
  this.indent--
418
699
  this.writeLine('}')
419
700
  this.writeLine('')
701
+ return ''
420
702
  }
421
703
 
422
- generateMethodDefinition(node) {
423
- let declaration = ''
424
-
425
- const visibility = node.visibility || node.access || 'public'
426
- declaration += this.translate(visibility) + ' '
427
-
428
- if (node.static) declaration += 'static '
429
- if (node.final) declaration += 'final '
430
- if (node.abstract) declaration += 'abstract '
431
-
432
- const name = this.translateMethodName(node.name || node.key?.name)
433
- const params = this.generateParams(node.params || node.value?.params || [])
434
- const returnType = node.returnType ? ': ' + this.translateType(node.returnType) : ''
435
-
436
- const body = node.body || node.value?.body
437
- const hasEmptyBody = !body || (body.type === 'BlockStatement' && (!body.body || body.body.length === 0))
438
-
439
- if (node.abstract || hasEmptyBody) {
440
- this.writeLine(`${declaration}function ${name}(${params})${returnType};`)
441
- } else {
442
- this.writeLine(`${declaration}function ${name}(${params})${returnType} {`)
443
- this.indent++
444
- this.generateNode(body)
445
- this.indent--
446
- this.writeLine('}')
447
- }
448
- this.writeLine('')
449
- }
450
-
451
- translateMethodName(name) {
452
- if (!name) return 'method'
704
+ generateEnumDeclaration(node) {
705
+ let declaration = 'enum ' + (node.name || '')
453
706
 
454
- const magicMethods = {
455
- 'constructeur': '__construct',
456
- 'constructor': '__construct',
457
- 'destructeur': '__destruct',
458
- 'destructor': '__destruct',
459
- 'obtenir': '__get',
460
- 'get': '__get',
461
- 'définir': '__set',
462
- 'definir': '__set',
463
- 'set': '__set',
464
- 'appeler': '__call',
465
- 'call': '__call',
466
- 'appeler statique': '__callStatic',
467
- 'call static': '__callStatic',
468
- 'vers chaîne': '__toString',
469
- 'vers chaine': '__toString',
470
- 'to string': '__toString',
471
- 'invoquer': '__invoke',
472
- 'invoke': '__invoke',
473
- 'cloner': '__clone',
474
- 'clone': '__clone',
475
- 'dormir': '__sleep',
476
- 'sleep': '__sleep',
477
- 'réveiller': '__wakeup',
478
- 'reveiller': '__wakeup',
479
- 'wakeup': '__wakeup',
480
- 'sérialiser': '__serialize',
481
- 'serialiser': '__serialize',
482
- 'serialize': '__serialize',
483
- 'désérialiser': '__unserialize',
484
- 'deserialiser': '__unserialize',
485
- 'unserialize': '__unserialize',
486
- 'isset': '__isset',
487
- 'existe propriété': '__isset',
488
- 'existe propriete': '__isset',
489
- 'unset': '__unset',
490
- 'détruire propriété': '__unset',
491
- 'detruire propriete': '__unset',
492
- 'déboguer info': '__debugInfo',
493
- 'deboguer info': '__debugInfo',
494
- 'debug info': '__debugInfo'
707
+ if (node.backingType) {
708
+ declaration += ': ' + this.translateKeyword(node.backingType)
495
709
  }
496
-
497
- const normalized = name.toLowerCase().trim()
498
- return magicMethods[normalized] || name
499
- }
500
710
 
501
- generatePropertyDeclaration(node) {
502
- let declaration = ''
503
-
504
- const visibility = node.visibility || node.access || 'public'
505
- declaration += this.translate(visibility) + ' '
506
-
507
- if (node.static) declaration += 'static '
508
- if (node.readonly) declaration += 'readonly '
509
-
510
- if (node.typeHint) {
511
- declaration += this.translateType(node.typeHint) + ' '
512
- }
513
-
514
- const name = node.name || node.key?.name
515
- declaration += '$' + name
516
-
517
- if (node.default !== undefined && node.default !== null) {
518
- declaration += ' = ' + this.generateNode(node.default)
519
- } else if (node.value !== undefined && node.value !== null) {
520
- declaration += ' = ' + this.generateNode(node.value)
521
- }
522
-
523
- this.writeLine(declaration + ';')
524
- }
711
+ this.writeLine(declaration + ' {')
712
+ this.indent++
525
713
 
526
- generateVariableDeclaration(node) {
527
- for (const decl of node.declarations || [node]) {
528
- const name = decl.name || decl.id?.name || decl.id
529
- const varName = '$' + (typeof name === 'string' ? name : name.name || 'var')
530
-
531
- if (decl.init !== undefined) {
532
- const value = this.generateNode(decl.init)
533
- this.writeLine(`${varName} = ${value};`)
714
+ const cases = node.cases || node.members || []
715
+ for (const caseNode of cases) {
716
+ if (caseNode.value !== undefined) {
717
+ this.writeLine(`case ${caseNode.name} = ${this.generateNode(caseNode.value)};`)
534
718
  } else {
535
- this.writeLine(`${varName} = null;`)
719
+ this.writeLine(`case ${caseNode.name};`)
536
720
  }
537
721
  }
538
- }
539
722
 
540
- generateConstantDeclaration(node) {
541
- const name = node.name || node.id?.name
542
- const value = this.generateNode(node.value || node.init)
543
-
544
- if (node.classLevel) {
545
- const visibility = node.visibility ? this.translate(node.visibility) + ' ' : ''
546
- this.writeLine(`${visibility}const ${name.toUpperCase()} = ${value};`)
547
- } else {
548
- this.writeLine(`define('${name.toUpperCase()}', ${value});`)
549
- }
723
+ this.indent--
724
+ this.writeLine('}')
725
+ this.writeLine('')
726
+ return ''
550
727
  }
551
728
 
552
729
  generateIfStatement(node) {
553
- const test = this.generateNode(node.test || node.condition)
730
+ const test = this.generateNode(node.test)
554
731
  this.writeLine(`if (${test}) {`)
555
732
  this.indent++
556
- this.generateNode(node.consequent)
733
+ this.generateBody(node.consequent)
557
734
  this.indent--
558
-
735
+
559
736
  if (node.alternate) {
560
- if (node.alternate.type === 'IfStatement' || node.alternate.type === 'ElseIfStatement') {
561
- this.write('} else')
562
- this.generateIfStatement({ ...node.alternate, isElseIf: true })
737
+ if (node.alternate.type === 'IfStatement') {
738
+ const altTest = this.generateNode(node.alternate.test)
739
+ this.writeLine(`} elseif (${altTest}) {`)
740
+ this.indent++
741
+ this.generateBody(node.alternate.consequent)
742
+ this.indent--
743
+ if (node.alternate.alternate) {
744
+ this.generateElse(node.alternate.alternate)
745
+ } else {
746
+ this.writeLine('}')
747
+ }
563
748
  } else {
564
749
  this.writeLine('} else {')
565
750
  this.indent++
566
- this.generateNode(node.alternate)
751
+ this.generateBody(node.alternate)
567
752
  this.indent--
568
753
  this.writeLine('}')
569
754
  }
570
755
  } else {
571
756
  this.writeLine('}')
572
757
  }
758
+ return ''
759
+ }
760
+
761
+ generateElse(node) {
762
+ if (node.type === 'IfStatement') {
763
+ const altTest = this.generateNode(node.test)
764
+ this.writeLine(`} elseif (${altTest}) {`)
765
+ this.indent++
766
+ this.generateBody(node.consequent)
767
+ this.indent--
768
+ if (node.alternate) {
769
+ this.generateElse(node.alternate)
770
+ } else {
771
+ this.writeLine('}')
772
+ }
773
+ } else {
774
+ this.writeLine('} else {')
775
+ this.indent++
776
+ this.generateBody(node)
777
+ this.indent--
778
+ this.writeLine('}')
779
+ }
573
780
  }
574
781
 
575
782
  generateForStatement(node) {
@@ -579,48 +786,43 @@ class PHPGenerator {
579
786
 
580
787
  this.writeLine(`for (${init}; ${test}; ${update}) {`)
581
788
  this.indent++
582
- this.generateNode(node.body)
789
+ this.generateBody(node.body)
583
790
  this.indent--
584
791
  this.writeLine('}')
792
+ return ''
585
793
  }
586
794
 
587
795
  generateForEachStatement(node) {
588
- const array = this.generateNode(node.array || node.right || node.iterable)
589
-
590
- const valueNode = node.value || node.left || node.valueVar
591
- const value = typeof valueNode === 'string'
592
- ? '$' + valueNode
593
- : this.generateIdentifier(valueNode)
594
-
595
- let key = ''
596
- const keyNode = node.key || node.keyVar
597
- if (keyNode) {
598
- key = (typeof keyNode === 'string' ? '$' + keyNode : this.generateIdentifier(keyNode)) + ' => '
599
- }
796
+ const array = this.generateNode(node.array || node.right)
797
+ const value = this.generateVariable(node.value || node.left)
798
+ const key = node.key ? this.generateVariable(node.key) + ' => ' : ''
600
799
 
601
800
  this.writeLine(`foreach (${array} as ${key}${value}) {`)
602
801
  this.indent++
603
- this.generateNode(node.body)
802
+ this.generateBody(node.body)
604
803
  this.indent--
605
804
  this.writeLine('}')
805
+ return ''
606
806
  }
607
807
 
608
808
  generateWhileStatement(node) {
609
- const test = this.generateNode(node.test || node.condition)
809
+ const test = this.generateNode(node.test)
610
810
  this.writeLine(`while (${test}) {`)
611
811
  this.indent++
612
- this.generateNode(node.body)
812
+ this.generateBody(node.body)
613
813
  this.indent--
614
814
  this.writeLine('}')
815
+ return ''
615
816
  }
616
817
 
617
818
  generateDoWhileStatement(node) {
618
819
  this.writeLine('do {')
619
820
  this.indent++
620
- this.generateNode(node.body)
821
+ this.generateBody(node.body)
621
822
  this.indent--
622
- const test = this.generateNode(node.test || node.condition)
823
+ const test = this.generateNode(node.test)
623
824
  this.writeLine(`} while (${test});`)
825
+ return ''
624
826
  }
625
827
 
626
828
  generateSwitchStatement(node) {
@@ -644,56 +846,35 @@ class PHPGenerator {
644
846
 
645
847
  this.indent--
646
848
  this.writeLine('}')
647
- }
648
-
649
- generateMatchExpression(node) {
650
- const subject = this.generateNode(node.subject || node.discriminant)
651
- let result = `match (${subject}) {\n`
652
-
653
- for (const arm of node.arms || node.cases || []) {
654
- this.indent++
655
- if (arm.conditions) {
656
- const conditions = arm.conditions.map(c => this.generateNode(c)).join(', ')
657
- result += this.getIndent() + `${conditions} => ${this.generateNode(arm.body)},\n`
658
- } else if (arm.default) {
659
- result += this.getIndent() + `default => ${this.generateNode(arm.body)},\n`
660
- }
661
- this.indent--
662
- }
663
-
664
- result += this.getIndent() + '}'
665
- return result
849
+ return ''
666
850
  }
667
851
 
668
852
  generateTryStatement(node) {
669
853
  this.writeLine('try {')
670
854
  this.indent++
671
- this.generateNode(node.block)
855
+ this.generateBody(node.block)
672
856
  this.indent--
673
857
 
674
- const handlers = node.catches || node.handlers || (node.handler ? [node.handler] : [])
675
- for (const handler of handlers) {
676
- const types = handler.types && handler.types.length > 0
677
- ? handler.types.join(' | ')
678
- : (handler.type || 'Exception')
679
- const param = handler.param
680
- ? (typeof handler.param === 'string' ? '$' + handler.param : this.generateIdentifier(handler.param))
858
+ if (node.handler) {
859
+ const exceptionType = node.handler.exceptionType || node.handler.type || 'Exception'
860
+ const param = node.handler.param
861
+ ? this.generateVariable(node.handler.param)
681
862
  : '$e'
682
- this.writeLine(`} catch (${types} ${param}) {`)
863
+ this.writeLine(`} catch (${exceptionType} ${param}) {`)
683
864
  this.indent++
684
- this.generateNode(handler.body)
865
+ this.generateBody(node.handler.body)
685
866
  this.indent--
686
867
  }
687
868
 
688
- const finallyBlock = node.finally || node.finalizer
689
- if (finallyBlock) {
869
+ if (node.finalizer) {
690
870
  this.writeLine('} finally {')
691
871
  this.indent++
692
- this.generateNode(finallyBlock)
872
+ this.generateBody(node.finalizer)
693
873
  this.indent--
694
874
  }
695
875
 
696
876
  this.writeLine('}')
877
+ return ''
697
878
  }
698
879
 
699
880
  generateReturnStatement(node) {
@@ -703,80 +884,34 @@ class PHPGenerator {
703
884
  } else {
704
885
  this.writeLine('return;')
705
886
  }
706
- }
707
-
708
- generateYieldExpression(node) {
709
- const arg = node.argument ? this.generateNode(node.argument) : ''
710
- if (node.delegate) {
711
- return `yield from ${arg}`
712
- }
713
- return `yield ${arg}`
887
+ return ''
714
888
  }
715
889
 
716
890
  generateThrowStatement(node) {
717
891
  const arg = this.generateNode(node.argument)
718
892
  this.writeLine(`throw ${arg};`)
893
+ return ''
719
894
  }
720
895
 
721
896
  generateExpressionStatement(node) {
722
897
  const expr = this.generateNode(node.expression)
723
- this.writeLine(`${expr};`)
898
+ if (expr) {
899
+ this.writeLine(`${expr};`)
900
+ }
901
+ return ''
724
902
  }
725
903
 
726
904
  generateEchoStatement(node) {
727
- const exprs = node.expressions || node.arguments || [node.argument]
728
- const args = exprs.filter(Boolean).map(a => this.generateNode(a)).join(', ')
905
+ const args = (node.expressions || node.arguments || [node.argument]).filter(Boolean).map(a => this.generateNode(a)).join(', ')
729
906
  this.writeLine(`echo ${args};`)
907
+ return ''
730
908
  }
731
909
 
732
- generatePrintStatement(node) {
733
- const arg = this.generateNode(node.argument)
734
- this.writeLine(`print ${arg};`)
735
- }
736
-
737
- generateNamespace(node) {
738
- this.writeLine(`namespace ${node.name};`)
739
- this.writeLine('')
740
- }
741
-
742
- generateUseStatement(node) {
743
- const typePrefix = node.useType === 'function' ? 'function ' : (node.useType === 'const' ? 'const ' : '')
744
-
745
- if (node.imports && node.imports.length > 0) {
746
- for (const imp of node.imports) {
747
- const path = imp.path || imp.name || imp
748
- const alias = imp.alias ? ' as ' + imp.alias : ''
749
- this.writeLine(`use ${typePrefix}${path}${alias};`)
750
- }
751
- } else {
752
- const name = node.name || node.source || 'undefined'
753
- const alias = node.alias ? ' as ' + node.alias : ''
754
- this.writeLine(`use ${typePrefix}${name}${alias};`)
755
- }
756
- }
757
-
758
- generateDeclareStatement(node) {
759
- const directives = (node.directives || []).map(d => {
760
- const value = typeof d.value === 'object' ? this.generateNode(d.value) : d.value
761
- return `${d.name}=${value}`
762
- }).join(', ')
763
- this.writeLine(`declare(${directives});`)
764
- }
765
-
766
- generateIncludeStatement(node) {
767
- const type = node.once
768
- ? (node.required ? 'require_once' : 'include_once')
769
- : (node.required ? 'require' : 'include')
770
- const path = typeof node.path === 'string' ? `'${node.path}'` : this.generateNode(node.path)
771
- this.writeLine(`${type} ${path};`)
772
- }
773
-
774
- generateInclude(node) {
775
- if (node.resolved && node.children) {
776
- for (const child of node.children) {
777
- this.generateNode(child)
778
- }
910
+ generateBlockStatement(node) {
911
+ for (const stmt of node.body || []) {
912
+ this.generateNode(stmt)
779
913
  }
914
+ return ''
780
915
  }
781
916
 
782
917
  generateCallExpression(node) {
@@ -784,34 +919,15 @@ class PHPGenerator {
784
919
 
785
920
  if (typeof node.callee === 'string') {
786
921
  callee = this.translateFunction(node.callee)
787
- } else if (node.callee.type === 'MemberExpression') {
788
- callee = this.generateMemberExpression(node.callee)
789
- } else if (node.callee.type === 'StaticMemberExpression') {
790
- callee = this.generateStaticMemberExpression(node.callee)
791
922
  } else if (node.callee.type === 'Identifier') {
792
- const name = node.callee.name
793
- const translated = this.translateFunction(name)
794
- if (translated !== name) {
795
- callee = translated
796
- } else if (name.includes(' ')) {
797
- callee = name.replace(/ /g, '_')
798
- } else {
799
- callee = name
800
- }
923
+ callee = this.translateFunction(node.callee.name)
924
+ } else if (node.callee.type === 'MemberExpression' || node.callee.type === 'StaticMemberExpression') {
925
+ callee = this.generateNode(node.callee)
801
926
  } else {
802
927
  callee = this.generateNode(node.callee)
803
928
  }
804
929
 
805
- const args = (node.arguments || []).map(a => {
806
- if (a.spread) {
807
- return '...' + this.generateNode(a.argument || a)
808
- }
809
- if (a.name && a.value) {
810
- return `${a.name}: ${this.generateNode(a.value)}`
811
- }
812
- return this.generateNode(a)
813
- }).join(', ')
814
-
930
+ const args = (node.arguments || []).map(a => this.generateNode(a)).join(', ')
815
931
  return `${callee}(${args})`
816
932
  }
817
933
 
@@ -822,53 +938,24 @@ class PHPGenerator {
822
938
  if (typeof node.property === 'string') {
823
939
  property = node.property
824
940
  } else if (node.property.type === 'Identifier') {
825
- let propName = node.property.name
826
- let translated = this.translateFunction(propName)
827
- if (translated.includes('->')) {
828
- translated = translated.split('->').pop()
829
- } else if (translated.includes('::')) {
830
- translated = translated.split('::').pop()
831
- } else if (translated !== propName && !translated.includes('(')) {
832
- translated = propName
833
- }
834
- property = translated
941
+ property = this.translateFunction(node.property.name)
835
942
  } else {
836
943
  property = this.generateNode(node.property)
837
944
  }
838
945
 
839
- if (node.static) {
840
- return `${object}::${property}`
841
- }
946
+ const operator = node.nullsafe ? '?->' : '->'
947
+
842
948
  if (node.computed) {
843
949
  return `${object}[${property}]`
844
950
  }
845
- if (node.nullSafe) {
846
- return `${object}?->${property}`
847
- }
848
- return `${object}->${property}`
951
+ return `${object}${operator}${property}`
849
952
  }
850
953
 
851
954
  generateStaticMemberExpression(node) {
852
- const classNode = node.class || node.object
853
- const className = classNode.type === 'Identifier' ? classNode.name : this.generateNode(classNode)
854
-
855
- const memberNode = node.member || node.property
856
- let memberName
857
- if (typeof memberNode === 'string') {
858
- memberName = memberNode
859
- } else if (memberNode.type === 'Identifier') {
860
- memberName = this.translateMethodName(memberNode.name)
861
- } else {
862
- memberName = this.generateNode(memberNode)
863
- }
864
-
865
- return `${className}::${memberName}`
866
- }
867
-
868
- generateIndexExpression(node) {
869
- const object = this.generateNode(node.object)
870
- const index = this.generateNode(node.index)
871
- return `${object}[${index}]`
955
+ const cls = node.class?.name || this.generateNode(node.class)
956
+ let member = node.member?.name || this.generateNode(node.member)
957
+ member = this.translateKeyword(member)
958
+ return `${cls}::${member}`
872
959
  }
873
960
 
874
961
  generateBinaryExpression(node) {
@@ -877,47 +964,41 @@ class PHPGenerator {
877
964
  let op = node.operator
878
965
 
879
966
  const opMap = {
880
- 'et': '&&', 'and': '&&',
881
- 'ou': '||', 'or': '||',
882
- 'non': '!', 'not': '!',
883
- 'égal': '===', 'egal': '===', 'equal': '===',
884
- 'différent': '!==', 'different': '!==', 'not equal': '!==',
885
- 'concaténer': '.', 'concatener': '.', 'concatenate': '.',
886
- 'vaisseau spatial': '<=>', 'spaceship': '<=>',
887
- 'fusion null': '??', 'null coalescing': '??'
967
+ 'et': '&&',
968
+ 'ou': '||',
969
+ 'concatener': '.',
970
+ 'concat': '.'
888
971
  }
889
-
890
- op = opMap[op?.toLowerCase()] || op
972
+ op = opMap[this.normalizeAccents(op)] || op
973
+
974
+ return `${left} ${op} ${right}`
975
+ }
891
976
 
892
- const result = `${left} ${op} ${right}`
893
- return node.parenthesized ? `(${result})` : result
977
+ generateAssignmentExpression(node) {
978
+ const left = this.generateNode(node.left)
979
+ const right = this.generateNode(node.right)
980
+ const op = node.operator || '='
981
+ return `${left} ${op} ${right}`
894
982
  }
895
983
 
896
984
  generateUnaryExpression(node) {
897
- const argument = this.generateNode(node.argument || node.operand)
898
- const op = node.operator === 'non' || node.operator === 'not' ? '!' : node.operator
985
+ const operand = this.generateNode(node.operand || node.argument)
986
+ const op = node.operator === 'non' ? '!' : node.operator
899
987
 
900
988
  if (node.prefix !== false) {
901
- return `${op}${argument}`
989
+ return `${op}${operand}`
902
990
  }
903
- return `${argument}${op}`
991
+ return `${operand}${op}`
904
992
  }
905
993
 
906
994
  generateUpdateExpression(node) {
907
- const argument = this.generateNode(node.argument || node.operand)
995
+ const operand = this.generateNode(node.operand || node.argument)
908
996
  const op = node.operator
909
997
 
910
998
  if (node.prefix) {
911
- return `${op}${argument}`
999
+ return `${op}${operand}`
912
1000
  }
913
- return `${argument}${op}`
914
- }
915
-
916
- generateAssignmentExpression(node) {
917
- const left = this.generateNode(node.left)
918
- const right = this.generateNode(node.right)
919
- const op = node.operator || '='
920
- return `${left} ${op} ${right}`
1001
+ return `${operand}${op}`
921
1002
  }
922
1003
 
923
1004
  generateConditionalExpression(node) {
@@ -927,31 +1008,24 @@ class PHPGenerator {
927
1008
  return `${test} ? ${consequent} : ${alternate}`
928
1009
  }
929
1010
 
930
- generateNullCoalescing(node) {
931
- const left = this.generateNode(node.left)
932
- const right = this.generateNode(node.right)
933
- return `${left} ?? ${right}`
934
- }
935
-
936
1011
  generateArrayExpression(node) {
937
1012
  const elements = (node.elements || []).map(e => {
938
- if (!e) return 'null'
939
1013
  if (e.type === 'ArrayElement') {
940
- if (e.key !== null && e.key !== undefined) {
1014
+ if (e.key) {
941
1015
  const key = this.generateNode(e.key)
942
1016
  const value = this.generateNode(e.value)
943
1017
  return `${key} => ${value}`
944
1018
  }
945
1019
  return this.generateNode(e.value)
946
1020
  }
947
- if (e.key !== undefined && e.key !== null) {
1021
+ if (e.type === 'SpreadElement') {
1022
+ return '...' + this.generateNode(e.argument)
1023
+ }
1024
+ if (e.key) {
948
1025
  const key = this.generateNode(e.key)
949
1026
  const value = this.generateNode(e.value || e)
950
1027
  return `${key} => ${value}`
951
1028
  }
952
- if (e.spread) {
953
- return '...' + this.generateNode(e.argument || e)
954
- }
955
1029
  return this.generateNode(e)
956
1030
  })
957
1031
 
@@ -966,112 +1040,44 @@ class PHPGenerator {
966
1040
  return this.generateArrayExpression(node)
967
1041
  }
968
1042
 
969
- generateArrowFunction(node) {
970
- const params = this.generateParams(node.params || [])
971
- const body = this.generateNode(node.body)
972
- return `fn(${params}) => ${body}`
973
- }
974
-
975
- generateFunctionExpression(node) {
976
- const params = this.generateParams(node.params || [])
977
- const uses = node.uses || node.use || []
978
- const useVars = uses.length > 0
979
- ? ` use (${uses.map(u => (u.byReference || u.byRef ? '&' : '') + '$' + (u.name || u)).join(', ')})`
980
- : ''
981
- const returnType = node.returnType ? ': ' + this.translateType(node.returnType) : ''
982
-
983
- let result = `function(${params})${useVars}${returnType} {\n`
1043
+ generateIdentifier(node) {
1044
+ const name = node.name || node
1045
+ if (typeof name !== 'string') return '$var'
984
1046
 
985
- const savedOutput = this.output
986
- const savedIndent = this.indent
987
- this.output = ''
988
- this.indent++
1047
+ const normalized = this.normalizeAccents(name)
989
1048
 
990
- const body = node.body
991
- if (body && body.type === 'BlockStatement' && body.body) {
992
- for (const stmt of body.body) {
993
- this.generateNode(stmt)
994
- }
995
- } else if (body) {
996
- this.writeLine('return ' + this.generateNode(body) + ';')
1049
+ if (this.functionMap[normalized]) {
1050
+ return this.functionMap[normalized]
997
1051
  }
998
1052
 
999
- const bodyOutput = this.output
1000
- this.output = savedOutput
1001
- this.indent = savedIndent
1002
-
1003
- result += bodyOutput
1004
- result += this.getIndent() + '}'
1005
- return result
1006
- }
1007
-
1008
- generateClosure(node) {
1009
- const params = this.generateParams(node.params || [])
1010
- const useVars = node.use && node.use.length > 0
1011
- ? ` use (${node.use.map(u => (u.byRef ? '&' : '') + '$' + this.translate(u.name || u)).join(', ')})`
1012
- : ''
1013
- const returnType = node.returnType ? ': ' + this.translateType(node.returnType) : ''
1014
-
1015
- let result = `function(${params})${useVars}${returnType} {\n`
1016
- this.indent++
1053
+ const translated = this.translateKeyword(name)
1017
1054
 
1018
- if (Array.isArray(node.body)) {
1019
- for (const stmt of node.body) {
1020
- result += this.getIndent() + this.generateNode(stmt)
1021
- }
1022
- } else {
1023
- result += this.getIndent() + 'return ' + this.generateNode(node.body) + ';\n'
1055
+ if (translated.startsWith('$')) return translated
1056
+ if (['true', 'false', 'null', 'self', 'parent', 'static'].includes(translated)) {
1057
+ return translated
1058
+ }
1059
+ if (/^[A-Z]/.test(translated)) return translated
1060
+ if (translated === name && !name.startsWith('$')) {
1061
+ return '$' + name
1024
1062
  }
1025
1063
 
1026
- this.indent--
1027
- result += this.getIndent() + '}'
1028
- return result
1064
+ return translated.startsWith('$') ? translated : '$' + translated
1029
1065
  }
1030
1066
 
1031
- generateIdentifier(node) {
1067
+ generateVariable(node) {
1068
+ if (!node) return '$var'
1069
+
1032
1070
  const name = node.name || node.id?.name || node
1033
1071
  if (typeof name !== 'string') return '$var'
1034
1072
 
1035
- const keywords = [
1036
- 'true', 'false', 'null',
1037
- 'self', 'parent', 'static',
1038
- 'this', '$this', 'vrai', 'faux', 'nul', 'ceci'
1039
- ]
1040
- const lowerName = name.toLowerCase()
1041
-
1042
- if (lowerName === 'vrai') return 'true'
1043
- if (lowerName === 'faux') return 'false'
1044
- if (lowerName === 'nul') return 'null'
1045
- if (lowerName === 'ceci' || lowerName === 'this') return '$this'
1046
-
1047
- if (keywords.includes(lowerName)) {
1048
- return name
1049
- }
1050
-
1051
1073
  if (name.startsWith('$')) return name
1052
-
1053
- if (/^[A-Z]/.test(name)) return name
1054
-
1055
- if (name.includes('(') || name.includes('::') || name.includes('->')) return name
1056
-
1057
- const superglobals = ['_GET', '_POST', '_SERVER', '_SESSION', '_COOKIE', '_FILES', '_REQUEST', '_ENV', 'GLOBALS']
1058
- if (superglobals.includes(name) || superglobals.includes(name.toUpperCase())) {
1059
- return '$' + name.toUpperCase()
1060
- }
1061
-
1062
1074
  return '$' + name
1063
1075
  }
1064
1076
 
1065
1077
  generateLiteral(node) {
1066
- if (node.raw !== undefined) return node.raw
1078
+ if (node.raw) return node.raw
1067
1079
  if (typeof node.value === 'string') {
1068
- const escaped = node.value
1069
- .replace(/\\/g, '\\\\')
1070
- .replace(/"/g, '\\"')
1071
- .replace(/\n/g, '\\n')
1072
- .replace(/\r/g, '\\r')
1073
- .replace(/\t/g, '\\t')
1074
- return `"${escaped}"`
1080
+ return `"${node.value.replace(/"/g, '\\"')}"`
1075
1081
  }
1076
1082
  if (node.value === null) return 'null'
1077
1083
  if (node.value === true) return 'true'
@@ -1079,28 +1085,83 @@ class PHPGenerator {
1079
1085
  return String(node.value)
1080
1086
  }
1081
1087
 
1088
+ generateStringLiteral(node) {
1089
+ const quote = node.doubleQuoted ? '"' : "'"
1090
+ const value = node.value.replace(new RegExp(quote, 'g'), '\\' + quote)
1091
+ return `${quote}${value}${quote}`
1092
+ }
1093
+
1082
1094
  generateNewExpression(node) {
1083
- const callee = typeof node.callee === 'string'
1084
- ? this.translate(node.callee)
1085
- : this.generateNode(node.callee)
1095
+ let callee
1096
+ if (typeof node.callee === 'string') {
1097
+ callee = node.callee
1098
+ } else if (node.callee.type === 'Identifier') {
1099
+ callee = node.callee.name
1100
+ } else {
1101
+ callee = this.generateNode(node.callee)
1102
+ }
1086
1103
  const args = (node.arguments || []).map(a => this.generateNode(a)).join(', ')
1087
1104
  return `new ${callee}(${args})`
1088
1105
  }
1089
1106
 
1090
- generateInstanceOf(node) {
1107
+ generateFunctionExpression(node) {
1108
+ const params = (node.params || []).map(p => this.generateParam(p)).join(', ')
1109
+
1110
+ let uses = ''
1111
+ if (node.uses && node.uses.length > 0) {
1112
+ const useVars = node.uses.map(u => {
1113
+ const ref = u.byRef ? '&' : ''
1114
+ return ref + this.generateVariable(u)
1115
+ }).join(', ')
1116
+ uses = ` use (${useVars})`
1117
+ }
1118
+
1119
+ let returnType = ''
1120
+ if (node.returnType) {
1121
+ returnType = ': ' + this.translateKeyword(node.returnType)
1122
+ }
1123
+
1124
+ let body = ''
1125
+ if (node.body) {
1126
+ const savedOutput = this.output
1127
+ const savedIndent = this.indent
1128
+ this.output = ''
1129
+ this.indent = 0
1130
+ this.generateBody(node.body)
1131
+ body = this.output
1132
+ this.output = savedOutput
1133
+ this.indent = savedIndent
1134
+ }
1135
+
1136
+ return `function(${params})${uses}${returnType} {\n${body}}`
1137
+ }
1138
+
1139
+ generateArrowFunctionExpression(node) {
1140
+ const params = (node.params || []).map(p => this.generateParam(p)).join(', ')
1141
+ const body = this.generateNode(node.body)
1142
+ return `fn(${params}) => ${body}`
1143
+ }
1144
+
1145
+ generateIndexExpression(node) {
1146
+ const object = this.generateNode(node.object)
1147
+ const index = node.index ? this.generateNode(node.index) : ''
1148
+ return `${object}[${index}]`
1149
+ }
1150
+
1151
+ generateNullCoalesceExpression(node) {
1091
1152
  const left = this.generateNode(node.left)
1092
1153
  const right = this.generateNode(node.right)
1093
- return `${left} instanceof ${right}`
1154
+ return `${left} ?? ${right}`
1094
1155
  }
1095
1156
 
1096
- generateBlockStatement(node) {
1097
- for (const stmt of node.body || []) {
1098
- this.generateNode(stmt)
1157
+ generateYieldExpression(node) {
1158
+ if (node.delegate) {
1159
+ return `yield from ${this.generateNode(node.argument)}`
1099
1160
  }
1100
- }
1101
-
1102
- write(text) {
1103
- this.output += text
1161
+ if (node.argument) {
1162
+ return `yield ${this.generateNode(node.argument)}`
1163
+ }
1164
+ return 'yield'
1104
1165
  }
1105
1166
 
1106
1167
  writeLine(text) {
@@ -1112,4 +1173,6 @@ class PHPGenerator {
1112
1173
  }
1113
1174
  }
1114
1175
 
1115
- module.exports = { PHPGenerator }
1176
+ module.exports = {
1177
+ PHPGenerator
1178
+ }