ether-code 0.9.1 → 0.9.4

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,794 @@ 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.type === 'TypeHint') {
312
+ const types = (typeNode.types || []).map(t => this.translateKeyword(t))
313
+ const result = types.join('|')
314
+ return typeNode.nullable ? '?' + result : result
315
+ }
316
+ if (typeNode.name) return this.translateKeyword(typeNode.name)
317
+ if (typeNode.type === 'Identifier') return this.translateKeyword(typeNode.name || typeNode.value || '')
318
+ if (typeNode.value) return this.translateKeyword(typeNode.value)
319
+ return ''
123
320
  }
124
321
 
125
322
  generate(ast) {
126
- this.output = '<?php\n'
323
+ this.output = ''
127
324
  this.indent = 0
128
-
129
- if (ast.strict) {
130
- this.writeLine("declare(strict_types=1);")
131
- this.writeLine('')
325
+
326
+ if (ast.phpTag !== false) {
327
+ this.output = '<?php\n'
132
328
  }
133
329
 
134
- if (Array.isArray(ast.body)) {
135
- for (const node of ast.body) {
330
+ if (Array.isArray(ast)) {
331
+ for (const node of ast) {
136
332
  this.generateNode(node)
137
333
  }
138
- } else if (ast.body) {
139
- this.generateNode(ast.body)
140
- } else {
334
+ } else if (ast && ast.type === 'Program') {
335
+ for (const node of ast.body || []) {
336
+ this.generateNode(node)
337
+ }
338
+ } else if (ast && ast.type) {
141
339
  this.generateNode(ast)
340
+ } else if (ast && ast.body) {
341
+ for (const node of ast.body) {
342
+ this.generateNode(node)
343
+ }
142
344
  }
143
345
 
144
- return this.output
346
+ return this.output.trim()
145
347
  }
146
348
 
147
349
  generateNode(node) {
148
350
  if (!node) return ''
149
351
 
150
- if (Array.isArray(node)) {
151
- return node.map(n => this.generateNode(n)).join('')
352
+ switch (node.type) {
353
+ case 'Program':
354
+ for (const stmt of node.body || []) {
355
+ this.generateNode(stmt)
356
+ }
357
+ return ''
358
+ case 'DeclareStatement':
359
+ return this.generateDeclareStatement(node)
360
+ case 'NamespaceDeclaration':
361
+ return this.generateNamespace(node)
362
+ case 'UseStatement':
363
+ return this.generateUseStatement(node)
364
+ case 'ConstantDeclaration':
365
+ return this.generateConstantDeclaration(node)
366
+ case 'VariableDeclaration':
367
+ return this.generateVariableDeclaration(node)
368
+ case 'FunctionDeclaration':
369
+ return this.generateFunctionDeclaration(node)
370
+ case 'ClassDeclaration':
371
+ return this.generateClassDeclaration(node)
372
+ case 'InterfaceDeclaration':
373
+ return this.generateInterfaceDeclaration(node)
374
+ case 'TraitDeclaration':
375
+ return this.generateTraitDeclaration(node)
376
+ case 'EnumDeclaration':
377
+ return this.generateEnumDeclaration(node)
378
+ case 'IfStatement':
379
+ return this.generateIfStatement(node)
380
+ case 'ForStatement':
381
+ return this.generateForStatement(node)
382
+ case 'ForEachStatement':
383
+ return this.generateForEachStatement(node)
384
+ case 'WhileStatement':
385
+ return this.generateWhileStatement(node)
386
+ case 'DoWhileStatement':
387
+ return this.generateDoWhileStatement(node)
388
+ case 'SwitchStatement':
389
+ return this.generateSwitchStatement(node)
390
+ case 'TryStatement':
391
+ return this.generateTryStatement(node)
392
+ case 'ReturnStatement':
393
+ return this.generateReturnStatement(node)
394
+ case 'ThrowStatement':
395
+ return this.generateThrowStatement(node)
396
+ case 'BreakStatement':
397
+ this.writeLine('break;')
398
+ return ''
399
+ case 'ContinueStatement':
400
+ this.writeLine('continue;')
401
+ return ''
402
+ case 'ExpressionStatement':
403
+ return this.generateExpressionStatement(node)
404
+ case 'EchoStatement':
405
+ return this.generateEchoStatement(node)
406
+ case 'BlockStatement':
407
+ return this.generateBlockStatement(node)
408
+ case 'CallExpression':
409
+ return this.generateCallExpression(node)
410
+ case 'MemberExpression':
411
+ return this.generateMemberExpression(node)
412
+ case 'StaticMemberExpression':
413
+ return this.generateStaticMemberExpression(node)
414
+ case 'BinaryExpression':
415
+ return this.generateBinaryExpression(node)
416
+ case 'LogicalExpression':
417
+ return this.generateBinaryExpression(node)
418
+ case 'AssignmentExpression':
419
+ return this.generateAssignmentExpression(node)
420
+ case 'UnaryExpression':
421
+ return this.generateUnaryExpression(node)
422
+ case 'UpdateExpression':
423
+ return this.generateUpdateExpression(node)
424
+ case 'ConditionalExpression':
425
+ return this.generateConditionalExpression(node)
426
+ case 'ArrayExpression':
427
+ return this.generateArrayExpression(node)
428
+ case 'ObjectExpression':
429
+ return this.generateObjectExpression(node)
430
+ case 'Identifier':
431
+ return this.generateIdentifier(node)
432
+ case 'Variable':
433
+ return this.generateVariable(node)
434
+ case 'Literal':
435
+ return this.generateLiteral(node)
436
+ case 'StringLiteral':
437
+ return this.generateStringLiteral(node)
438
+ case 'NewExpression':
439
+ return this.generateNewExpression(node)
440
+ case 'ThisExpression':
441
+ return '$this'
442
+ case 'FunctionExpression':
443
+ return this.generateFunctionExpression(node)
444
+ case 'ArrowFunctionExpression':
445
+ return this.generateArrowFunctionExpression(node)
446
+ case 'IndexExpression':
447
+ return this.generateIndexExpression(node)
448
+ case 'NullCoalesceExpression':
449
+ return this.generateNullCoalesceExpression(node)
450
+ case 'YieldExpression':
451
+ return this.generateYieldExpression(node)
452
+ default:
453
+ return ''
152
454
  }
455
+ }
153
456
 
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': () => ''
457
+ generateDeclareStatement(node) {
458
+ const directives = []
459
+ if (node.strictTypes) {
460
+ directives.push('strict_types=1')
220
461
  }
221
-
222
- if (generators[type]) {
223
- return generators[type]()
462
+ if (node.ticks) {
463
+ directives.push(`ticks=${node.ticks}`)
464
+ }
465
+ if (node.encoding) {
466
+ directives.push(`encoding='${node.encoding}'`)
224
467
  }
468
+ if (directives.length > 0) {
469
+ this.writeLine(`declare(${directives.join(', ')});`)
470
+ }
471
+ return ''
472
+ }
225
473
 
226
- if (node.expression) {
227
- return this.generateNode(node.expression)
474
+ generateNamespace(node) {
475
+ const name = node.name || node.path
476
+ this.writeLine(`namespace ${name};`)
477
+ this.writeLine('')
478
+ return ''
479
+ }
480
+
481
+ generateUseStatement(node) {
482
+ if (node.imports && node.imports.length > 0) {
483
+ for (const imp of node.imports) {
484
+ const name = imp.path || imp.name
485
+ const alias = imp.alias ? ` as ${imp.alias}` : ''
486
+ const useType = node.useType ? `${node.useType} ` : ''
487
+ this.writeLine(`use ${useType}${name}${alias};`)
488
+ }
489
+ } else {
490
+ const name = node.name || node.source
491
+ const alias = node.alias ? ` as ${node.alias}` : ''
492
+ const useType = node.useType ? `${node.useType} ` : ''
493
+ this.writeLine(`use ${useType}${name}${alias};`)
228
494
  }
495
+ return ''
496
+ }
229
497
 
498
+ generateConstantDeclaration(node) {
499
+ const name = node.name || node.id?.name
500
+ const value = this.generateNode(node.value || node.init)
501
+ this.writeLine(`define('${name}', ${value});`)
230
502
  return ''
231
503
  }
232
504
 
233
- generateProgram(node) {
234
- for (const stmt of node.body || []) {
235
- this.generateNode(stmt)
505
+ generateVariableDeclaration(node) {
506
+ const declarations = node.declarations || [node]
507
+
508
+ for (const decl of declarations) {
509
+ const name = this.generateVariable(decl.id || decl)
510
+ if (decl.init) {
511
+ const init = this.generateNode(decl.init)
512
+ this.writeLine(`${name} = ${init};`)
513
+ }
236
514
  }
515
+ return ''
237
516
  }
238
517
 
239
518
  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) : ''
519
+ const name = node.name || node.id?.name || ''
520
+ const params = (node.params || []).map(p => this.generateParam(p)).join(', ')
521
+ let returnType = ''
243
522
 
523
+ if (node.returnType) {
524
+ const type = this.extractTypeName(node.returnType)
525
+ if (type) returnType = ': ' + type
526
+ }
527
+
244
528
  this.writeLine(`function ${name}(${params})${returnType} {`)
245
529
  this.indent++
246
- this.generateNode(node.body)
530
+
531
+ if (node.body) {
532
+ this.generateBody(node.body)
533
+ }
534
+
247
535
  this.indent--
248
536
  this.writeLine('}')
249
537
  this.writeLine('')
538
+ return ''
250
539
  }
251
540
 
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 ''
541
+ generateParam(param) {
542
+ let result = ''
285
543
 
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'
544
+ if (param.typeHint || (param.type && param.type !== 'Parameter' && param.type !== 'Identifier')) {
545
+ const type = param.typeHint || param.type
546
+ if (typeof type === 'string' && type !== 'Parameter' && type !== 'Identifier') {
547
+ result += this.translateKeyword(type) + ' '
548
+ } else if (type && type.name) {
549
+ result += this.translateKeyword(type.name) + ' '
301
550
  }
302
- return typeMap[translated.toLowerCase()] || typeMap[type.toLowerCase()] || type
303
551
  }
304
552
 
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])
553
+ if (param.reference) {
554
+ result += '&'
311
555
  }
312
556
 
313
- if (type.union) {
314
- return type.types.map(t => this.translateType(t)).join('|')
557
+ if (param.variadic) {
558
+ result += '...'
315
559
  }
316
560
 
317
- if (type.intersection) {
318
- return type.types.map(t => this.translateType(t)).join('&')
319
- }
561
+ result += this.generateVariable(param)
320
562
 
321
- if (type.nullable) {
322
- return '?' + this.translateType(type.type)
563
+ if (param.default !== undefined && param.default !== null) {
564
+ result += ' = ' + this.generateNode(param.default)
323
565
  }
324
566
 
325
- return type.name || type
567
+ return result
568
+ }
569
+
570
+ generateBody(body) {
571
+ if (Array.isArray(body)) {
572
+ for (const stmt of body) {
573
+ this.generateNode(stmt)
574
+ }
575
+ } else if (body && body.body) {
576
+ for (const stmt of body.body) {
577
+ this.generateNode(stmt)
578
+ }
579
+ } else if (body) {
580
+ this.generateNode(body)
581
+ }
326
582
  }
327
583
 
328
584
  generateClassDeclaration(node) {
329
585
  let declaration = ''
330
586
 
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)
587
+ if (node.abstract) declaration += 'abstract '
588
+ if (node.final) declaration += 'final '
589
+ if (node.readonly) declaration += 'readonly '
337
590
 
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)
591
+ declaration += 'class ' + (node.name || node.id?.name || '')
592
+
593
+ if (node.extends || node.superClass) {
594
+ declaration += ' extends ' + (node.extends || node.superClass)
343
595
  }
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(', ')
596
+
597
+ if (node.implements && node.implements.length > 0) {
598
+ declaration += ' implements ' + node.implements.join(', ')
349
599
  }
350
-
600
+
351
601
  this.writeLine(declaration + ' {')
352
602
  this.indent++
353
-
354
- for (const member of node.body || node.members || []) {
355
- this.generateNode(member)
603
+
604
+ const members = node.body?.body || node.members || node.body || []
605
+ for (const member of members) {
606
+ this.generateClassMember(member)
356
607
  }
357
-
608
+
358
609
  this.indent--
359
610
  this.writeLine('}')
360
611
  this.writeLine('')
612
+ return ''
361
613
  }
362
614
 
363
- generateInterfaceDeclaration(node) {
364
- let declaration = 'interface ' + this.translate(node.name || node.id?.name)
615
+ generateClassMember(node) {
616
+ if (!node) return
617
+
618
+ if (node.type === 'UseTraitStatement') {
619
+ this.writeLine(`use ${node.traits.join(', ')};`)
620
+ return
621
+ }
622
+
623
+ const visibility = node.visibility || 'public'
624
+ const isStatic = node.static ? 'static ' : ''
625
+ const isReadonly = node.readonly ? 'readonly ' : ''
365
626
 
627
+ if (node.type === 'Property' || node.kind === 'property' || node.type === 'PropertyDeclaration') {
628
+ const name = '$' + (node.name || node.key?.name || node.id?.name || '')
629
+ let typeHint = ''
630
+ if (node.typeHint) {
631
+ typeHint = this.translateKeyword(node.typeHint) + ' '
632
+ }
633
+ const value = node.value ? ' = ' + this.generateNode(node.value) : ''
634
+ this.writeLine(`${visibility} ${isStatic}${isReadonly}${typeHint}${name}${value};`)
635
+ } else if (node.type === 'Method' || node.kind === 'method' || node.type === 'MethodDeclaration') {
636
+ const name = node.name || node.key?.name || ''
637
+ const translatedName = this.translateKeyword(name)
638
+ const params = (node.params || []).map(p => this.generateParam(p)).join(', ')
639
+ let returnType = ''
640
+
641
+ if (node.returnType) {
642
+ const type = this.extractTypeName(node.returnType)
643
+ if (type) returnType = ': ' + type
644
+ }
645
+
646
+ const abstractMod = node.abstract ? 'abstract ' : ''
647
+
648
+ if (node.abstract) {
649
+ this.writeLine(`${abstractMod}${visibility} ${isStatic}function ${translatedName}(${params})${returnType};`)
650
+ } else {
651
+ this.writeLine(`${visibility} ${isStatic}function ${translatedName}(${params})${returnType} {`)
652
+ this.indent++
653
+
654
+ if (node.body) {
655
+ this.generateBody(node.body)
656
+ }
657
+
658
+ this.indent--
659
+ this.writeLine('}')
660
+ }
661
+ this.writeLine('')
662
+ }
663
+ }
664
+
665
+ generateInterfaceDeclaration(node) {
666
+ let declaration = 'interface ' + (node.name || '')
667
+
366
668
  if (node.extends && node.extends.length > 0) {
367
- declaration += ' extends ' + node.extends.map(e => this.translate(e)).join(', ')
669
+ declaration += ' extends ' + node.extends.join(', ')
368
670
  }
369
-
671
+
370
672
  this.writeLine(declaration + ' {')
371
673
  this.indent++
372
-
373
- for (const member of node.body || node.members || []) {
374
- this.generateNode(member)
674
+
675
+ const members = node.body?.body || node.members || node.body || []
676
+ for (const member of members) {
677
+ this.generateInterfaceMember(member)
375
678
  }
376
-
679
+
377
680
  this.indent--
378
681
  this.writeLine('}')
379
682
  this.writeLine('')
683
+ return ''
380
684
  }
381
685
 
382
- generateTraitDeclaration(node) {
383
- this.writeLine('trait ' + this.translate(node.name || node.id?.name) + ' {')
384
- this.indent++
686
+ generateInterfaceMember(node) {
687
+ if (!node) return
688
+
689
+ const visibility = 'public'
690
+ const name = node.name || node.key?.name || ''
691
+ const translatedName = this.translateKeyword(name)
692
+ const params = (node.params || []).map(p => this.generateParam(p)).join(', ')
693
+ let returnType = ''
385
694
 
386
- for (const member of node.body || node.members || []) {
387
- this.generateNode(member)
695
+ if (node.returnType) {
696
+ const type = this.extractTypeName(node.returnType)
697
+ if (type) returnType = ': ' + type
388
698
  }
389
699
 
390
- this.indent--
391
- this.writeLine('}')
700
+ this.writeLine(`${visibility} function ${translatedName}(${params})${returnType};`)
392
701
  this.writeLine('')
393
702
  }
394
703
 
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 + ' {')
704
+ generateTraitDeclaration(node) {
705
+ this.writeLine('trait ' + (node.name || '') + ' {')
403
706
  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)
707
+
708
+ const members = node.body?.body || node.members || node.body || []
709
+ for (const member of members) {
710
+ this.generateClassMember(member)
415
711
  }
416
-
712
+
417
713
  this.indent--
418
714
  this.writeLine('}')
419
715
  this.writeLine('')
716
+ return ''
420
717
  }
421
718
 
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'
719
+ generateEnumDeclaration(node) {
720
+ let declaration = 'enum ' + (node.name || '')
453
721
 
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'
722
+ if (node.backingType) {
723
+ const type = this.extractTypeName(node.backingType)
724
+ if (type) declaration += ': ' + type
495
725
  }
496
-
497
- const normalized = name.toLowerCase().trim()
498
- return magicMethods[normalized] || name
499
- }
500
726
 
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
- }
727
+ this.writeLine(declaration + ' {')
728
+ this.indent++
525
729
 
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};`)
730
+ const cases = node.cases || node.members || []
731
+ for (const caseNode of cases) {
732
+ if (caseNode.value !== undefined) {
733
+ this.writeLine(`case ${caseNode.name} = ${this.generateNode(caseNode.value)};`)
534
734
  } else {
535
- this.writeLine(`${varName} = null;`)
735
+ this.writeLine(`case ${caseNode.name};`)
536
736
  }
537
737
  }
538
- }
539
738
 
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
- }
739
+ this.indent--
740
+ this.writeLine('}')
741
+ this.writeLine('')
742
+ return ''
550
743
  }
551
744
 
552
745
  generateIfStatement(node) {
553
- const test = this.generateNode(node.test || node.condition)
746
+ const test = this.generateNode(node.test)
554
747
  this.writeLine(`if (${test}) {`)
555
748
  this.indent++
556
- this.generateNode(node.consequent)
749
+ this.generateBody(node.consequent)
557
750
  this.indent--
558
-
751
+
559
752
  if (node.alternate) {
560
- if (node.alternate.type === 'IfStatement' || node.alternate.type === 'ElseIfStatement') {
561
- this.write('} else')
562
- this.generateIfStatement({ ...node.alternate, isElseIf: true })
753
+ if (node.alternate.type === 'IfStatement') {
754
+ const altTest = this.generateNode(node.alternate.test)
755
+ this.writeLine(`} elseif (${altTest}) {`)
756
+ this.indent++
757
+ this.generateBody(node.alternate.consequent)
758
+ this.indent--
759
+ if (node.alternate.alternate) {
760
+ this.generateElse(node.alternate.alternate)
761
+ } else {
762
+ this.writeLine('}')
763
+ }
563
764
  } else {
564
765
  this.writeLine('} else {')
565
766
  this.indent++
566
- this.generateNode(node.alternate)
767
+ this.generateBody(node.alternate)
567
768
  this.indent--
568
769
  this.writeLine('}')
569
770
  }
570
771
  } else {
571
772
  this.writeLine('}')
572
773
  }
774
+ return ''
775
+ }
776
+
777
+ generateElse(node) {
778
+ if (node.type === 'IfStatement') {
779
+ const altTest = this.generateNode(node.test)
780
+ this.writeLine(`} elseif (${altTest}) {`)
781
+ this.indent++
782
+ this.generateBody(node.consequent)
783
+ this.indent--
784
+ if (node.alternate) {
785
+ this.generateElse(node.alternate)
786
+ } else {
787
+ this.writeLine('}')
788
+ }
789
+ } else {
790
+ this.writeLine('} else {')
791
+ this.indent++
792
+ this.generateBody(node)
793
+ this.indent--
794
+ this.writeLine('}')
795
+ }
573
796
  }
574
797
 
575
798
  generateForStatement(node) {
@@ -579,48 +802,43 @@ class PHPGenerator {
579
802
 
580
803
  this.writeLine(`for (${init}; ${test}; ${update}) {`)
581
804
  this.indent++
582
- this.generateNode(node.body)
805
+ this.generateBody(node.body)
583
806
  this.indent--
584
807
  this.writeLine('}')
808
+ return ''
585
809
  }
586
810
 
587
811
  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
- }
812
+ const array = this.generateNode(node.array || node.right)
813
+ const value = this.generateVariable(node.value || node.left)
814
+ const key = node.key ? this.generateVariable(node.key) + ' => ' : ''
600
815
 
601
816
  this.writeLine(`foreach (${array} as ${key}${value}) {`)
602
817
  this.indent++
603
- this.generateNode(node.body)
818
+ this.generateBody(node.body)
604
819
  this.indent--
605
820
  this.writeLine('}')
821
+ return ''
606
822
  }
607
823
 
608
824
  generateWhileStatement(node) {
609
- const test = this.generateNode(node.test || node.condition)
825
+ const test = this.generateNode(node.test)
610
826
  this.writeLine(`while (${test}) {`)
611
827
  this.indent++
612
- this.generateNode(node.body)
828
+ this.generateBody(node.body)
613
829
  this.indent--
614
830
  this.writeLine('}')
831
+ return ''
615
832
  }
616
833
 
617
834
  generateDoWhileStatement(node) {
618
835
  this.writeLine('do {')
619
836
  this.indent++
620
- this.generateNode(node.body)
837
+ this.generateBody(node.body)
621
838
  this.indent--
622
- const test = this.generateNode(node.test || node.condition)
839
+ const test = this.generateNode(node.test)
623
840
  this.writeLine(`} while (${test});`)
841
+ return ''
624
842
  }
625
843
 
626
844
  generateSwitchStatement(node) {
@@ -644,56 +862,35 @@ class PHPGenerator {
644
862
 
645
863
  this.indent--
646
864
  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
865
+ return ''
666
866
  }
667
867
 
668
868
  generateTryStatement(node) {
669
869
  this.writeLine('try {')
670
870
  this.indent++
671
- this.generateNode(node.block)
871
+ this.generateBody(node.block)
672
872
  this.indent--
673
873
 
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))
874
+ if (node.handler) {
875
+ const exceptionType = node.handler.exceptionType || node.handler.type || 'Exception'
876
+ const param = node.handler.param
877
+ ? this.generateVariable(node.handler.param)
681
878
  : '$e'
682
- this.writeLine(`} catch (${types} ${param}) {`)
879
+ this.writeLine(`} catch (${exceptionType} ${param}) {`)
683
880
  this.indent++
684
- this.generateNode(handler.body)
881
+ this.generateBody(node.handler.body)
685
882
  this.indent--
686
883
  }
687
884
 
688
- const finallyBlock = node.finally || node.finalizer
689
- if (finallyBlock) {
885
+ if (node.finalizer) {
690
886
  this.writeLine('} finally {')
691
887
  this.indent++
692
- this.generateNode(finallyBlock)
888
+ this.generateBody(node.finalizer)
693
889
  this.indent--
694
890
  }
695
891
 
696
892
  this.writeLine('}')
893
+ return ''
697
894
  }
698
895
 
699
896
  generateReturnStatement(node) {
@@ -703,80 +900,34 @@ class PHPGenerator {
703
900
  } else {
704
901
  this.writeLine('return;')
705
902
  }
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}`
903
+ return ''
714
904
  }
715
905
 
716
906
  generateThrowStatement(node) {
717
907
  const arg = this.generateNode(node.argument)
718
908
  this.writeLine(`throw ${arg};`)
909
+ return ''
719
910
  }
720
911
 
721
912
  generateExpressionStatement(node) {
722
913
  const expr = this.generateNode(node.expression)
723
- this.writeLine(`${expr};`)
914
+ if (expr) {
915
+ this.writeLine(`${expr};`)
916
+ }
917
+ return ''
724
918
  }
725
919
 
726
920
  generateEchoStatement(node) {
727
- const exprs = node.expressions || node.arguments || [node.argument]
728
- const args = exprs.filter(Boolean).map(a => this.generateNode(a)).join(', ')
921
+ const args = (node.expressions || node.arguments || [node.argument]).filter(Boolean).map(a => this.generateNode(a)).join(', ')
729
922
  this.writeLine(`echo ${args};`)
923
+ return ''
730
924
  }
731
925
 
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
- }
926
+ generateBlockStatement(node) {
927
+ for (const stmt of node.body || []) {
928
+ this.generateNode(stmt)
779
929
  }
930
+ return ''
780
931
  }
781
932
 
782
933
  generateCallExpression(node) {
@@ -784,34 +935,15 @@ class PHPGenerator {
784
935
 
785
936
  if (typeof node.callee === 'string') {
786
937
  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
938
  } 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
- }
939
+ callee = this.translateFunction(node.callee.name)
940
+ } else if (node.callee.type === 'MemberExpression' || node.callee.type === 'StaticMemberExpression') {
941
+ callee = this.generateNode(node.callee)
801
942
  } else {
802
943
  callee = this.generateNode(node.callee)
803
944
  }
804
945
 
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
-
946
+ const args = (node.arguments || []).map(a => this.generateNode(a)).join(', ')
815
947
  return `${callee}(${args})`
816
948
  }
817
949
 
@@ -822,53 +954,24 @@ class PHPGenerator {
822
954
  if (typeof node.property === 'string') {
823
955
  property = node.property
824
956
  } 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
957
+ property = this.translateFunction(node.property.name)
835
958
  } else {
836
959
  property = this.generateNode(node.property)
837
960
  }
838
961
 
839
- if (node.static) {
840
- return `${object}::${property}`
841
- }
962
+ const operator = node.nullsafe ? '?->' : '->'
963
+
842
964
  if (node.computed) {
843
965
  return `${object}[${property}]`
844
966
  }
845
- if (node.nullSafe) {
846
- return `${object}?->${property}`
847
- }
848
- return `${object}->${property}`
967
+ return `${object}${operator}${property}`
849
968
  }
850
969
 
851
970
  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}]`
971
+ const cls = node.class?.name || this.generateNode(node.class)
972
+ let member = node.member?.name || this.generateNode(node.member)
973
+ member = this.translateKeyword(member)
974
+ return `${cls}::${member}`
872
975
  }
873
976
 
874
977
  generateBinaryExpression(node) {
@@ -877,47 +980,41 @@ class PHPGenerator {
877
980
  let op = node.operator
878
981
 
879
982
  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': '??'
983
+ 'et': '&&',
984
+ 'ou': '||',
985
+ 'concatener': '.',
986
+ 'concat': '.'
888
987
  }
889
-
890
- op = opMap[op?.toLowerCase()] || op
988
+ op = opMap[this.normalizeAccents(op)] || op
891
989
 
892
- const result = `${left} ${op} ${right}`
893
- return node.parenthesized ? `(${result})` : result
990
+ return `${left} ${op} ${right}`
991
+ }
992
+
993
+ generateAssignmentExpression(node) {
994
+ const left = this.generateNode(node.left)
995
+ const right = this.generateNode(node.right)
996
+ const op = node.operator || '='
997
+ return `${left} ${op} ${right}`
894
998
  }
895
999
 
896
1000
  generateUnaryExpression(node) {
897
- const argument = this.generateNode(node.argument || node.operand)
898
- const op = node.operator === 'non' || node.operator === 'not' ? '!' : node.operator
1001
+ const operand = this.generateNode(node.operand || node.argument)
1002
+ const op = node.operator === 'non' ? '!' : node.operator
899
1003
 
900
1004
  if (node.prefix !== false) {
901
- return `${op}${argument}`
1005
+ return `${op}${operand}`
902
1006
  }
903
- return `${argument}${op}`
1007
+ return `${operand}${op}`
904
1008
  }
905
1009
 
906
1010
  generateUpdateExpression(node) {
907
- const argument = this.generateNode(node.argument || node.operand)
1011
+ const operand = this.generateNode(node.operand || node.argument)
908
1012
  const op = node.operator
909
1013
 
910
1014
  if (node.prefix) {
911
- return `${op}${argument}`
1015
+ return `${op}${operand}`
912
1016
  }
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}`
1017
+ return `${operand}${op}`
921
1018
  }
922
1019
 
923
1020
  generateConditionalExpression(node) {
@@ -927,31 +1024,24 @@ class PHPGenerator {
927
1024
  return `${test} ? ${consequent} : ${alternate}`
928
1025
  }
929
1026
 
930
- generateNullCoalescing(node) {
931
- const left = this.generateNode(node.left)
932
- const right = this.generateNode(node.right)
933
- return `${left} ?? ${right}`
934
- }
935
-
936
1027
  generateArrayExpression(node) {
937
1028
  const elements = (node.elements || []).map(e => {
938
- if (!e) return 'null'
939
1029
  if (e.type === 'ArrayElement') {
940
- if (e.key !== null && e.key !== undefined) {
1030
+ if (e.key) {
941
1031
  const key = this.generateNode(e.key)
942
1032
  const value = this.generateNode(e.value)
943
1033
  return `${key} => ${value}`
944
1034
  }
945
1035
  return this.generateNode(e.value)
946
1036
  }
947
- if (e.key !== undefined && e.key !== null) {
1037
+ if (e.type === 'SpreadElement') {
1038
+ return '...' + this.generateNode(e.argument)
1039
+ }
1040
+ if (e.key) {
948
1041
  const key = this.generateNode(e.key)
949
1042
  const value = this.generateNode(e.value || e)
950
1043
  return `${key} => ${value}`
951
1044
  }
952
- if (e.spread) {
953
- return '...' + this.generateNode(e.argument || e)
954
- }
955
1045
  return this.generateNode(e)
956
1046
  })
957
1047
 
@@ -966,112 +1056,44 @@ class PHPGenerator {
966
1056
  return this.generateArrayExpression(node)
967
1057
  }
968
1058
 
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`
1059
+ generateIdentifier(node) {
1060
+ const name = node.name || node
1061
+ if (typeof name !== 'string') return '$var'
984
1062
 
985
- const savedOutput = this.output
986
- const savedIndent = this.indent
987
- this.output = ''
988
- this.indent++
1063
+ const normalized = this.normalizeAccents(name)
989
1064
 
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) + ';')
1065
+ if (this.functionMap[normalized]) {
1066
+ return this.functionMap[normalized]
997
1067
  }
998
1068
 
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++
1069
+ const translated = this.translateKeyword(name)
1017
1070
 
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'
1071
+ if (translated.startsWith('$')) return translated
1072
+ if (['true', 'false', 'null', 'self', 'parent', 'static'].includes(translated)) {
1073
+ return translated
1074
+ }
1075
+ if (/^[A-Z]/.test(translated)) return translated
1076
+ if (translated === name && !name.startsWith('$')) {
1077
+ return '$' + name
1024
1078
  }
1025
1079
 
1026
- this.indent--
1027
- result += this.getIndent() + '}'
1028
- return result
1080
+ return translated.startsWith('$') ? translated : '$' + translated
1029
1081
  }
1030
1082
 
1031
- generateIdentifier(node) {
1083
+ generateVariable(node) {
1084
+ if (!node) return '$var'
1085
+
1032
1086
  const name = node.name || node.id?.name || node
1033
1087
  if (typeof name !== 'string') return '$var'
1034
1088
 
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
1089
  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
1090
  return '$' + name
1063
1091
  }
1064
1092
 
1065
1093
  generateLiteral(node) {
1066
- if (node.raw !== undefined) return node.raw
1094
+ if (node.raw) return node.raw
1067
1095
  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}"`
1096
+ return `"${node.value.replace(/"/g, '\\"')}"`
1075
1097
  }
1076
1098
  if (node.value === null) return 'null'
1077
1099
  if (node.value === true) return 'true'
@@ -1079,28 +1101,83 @@ class PHPGenerator {
1079
1101
  return String(node.value)
1080
1102
  }
1081
1103
 
1104
+ generateStringLiteral(node) {
1105
+ const quote = node.doubleQuoted ? '"' : "'"
1106
+ const value = node.value.replace(new RegExp(quote, 'g'), '\\' + quote)
1107
+ return `${quote}${value}${quote}`
1108
+ }
1109
+
1082
1110
  generateNewExpression(node) {
1083
- const callee = typeof node.callee === 'string'
1084
- ? this.translate(node.callee)
1085
- : this.generateNode(node.callee)
1111
+ let callee
1112
+ if (typeof node.callee === 'string') {
1113
+ callee = node.callee
1114
+ } else if (node.callee.type === 'Identifier') {
1115
+ callee = node.callee.name
1116
+ } else {
1117
+ callee = this.generateNode(node.callee)
1118
+ }
1086
1119
  const args = (node.arguments || []).map(a => this.generateNode(a)).join(', ')
1087
1120
  return `new ${callee}(${args})`
1088
1121
  }
1089
1122
 
1090
- generateInstanceOf(node) {
1123
+ generateFunctionExpression(node) {
1124
+ const params = (node.params || []).map(p => this.generateParam(p)).join(', ')
1125
+
1126
+ let uses = ''
1127
+ if (node.uses && node.uses.length > 0) {
1128
+ const useVars = node.uses.map(u => {
1129
+ const ref = u.byRef ? '&' : ''
1130
+ return ref + this.generateVariable(u)
1131
+ }).join(', ')
1132
+ uses = ` use (${useVars})`
1133
+ }
1134
+
1135
+ let returnType = ''
1136
+ if (node.returnType) {
1137
+ returnType = ': ' + this.translateKeyword(node.returnType)
1138
+ }
1139
+
1140
+ let body = ''
1141
+ if (node.body) {
1142
+ const savedOutput = this.output
1143
+ const savedIndent = this.indent
1144
+ this.output = ''
1145
+ this.indent = 0
1146
+ this.generateBody(node.body)
1147
+ body = this.output
1148
+ this.output = savedOutput
1149
+ this.indent = savedIndent
1150
+ }
1151
+
1152
+ return `function(${params})${uses}${returnType} {\n${body}}`
1153
+ }
1154
+
1155
+ generateArrowFunctionExpression(node) {
1156
+ const params = (node.params || []).map(p => this.generateParam(p)).join(', ')
1157
+ const body = this.generateNode(node.body)
1158
+ return `fn(${params}) => ${body}`
1159
+ }
1160
+
1161
+ generateIndexExpression(node) {
1162
+ const object = this.generateNode(node.object)
1163
+ const index = node.index ? this.generateNode(node.index) : ''
1164
+ return `${object}[${index}]`
1165
+ }
1166
+
1167
+ generateNullCoalesceExpression(node) {
1091
1168
  const left = this.generateNode(node.left)
1092
1169
  const right = this.generateNode(node.right)
1093
- return `${left} instanceof ${right}`
1170
+ return `${left} ?? ${right}`
1094
1171
  }
1095
1172
 
1096
- generateBlockStatement(node) {
1097
- for (const stmt of node.body || []) {
1098
- this.generateNode(stmt)
1173
+ generateYieldExpression(node) {
1174
+ if (node.delegate) {
1175
+ return `yield from ${this.generateNode(node.argument)}`
1099
1176
  }
1100
- }
1101
-
1102
- write(text) {
1103
- this.output += text
1177
+ if (node.argument) {
1178
+ return `yield ${this.generateNode(node.argument)}`
1179
+ }
1180
+ return 'yield'
1104
1181
  }
1105
1182
 
1106
1183
  writeLine(text) {
@@ -1112,4 +1189,6 @@ class PHPGenerator {
1112
1189
  }
1113
1190
  }
1114
1191
 
1115
- module.exports = { PHPGenerator }
1192
+ module.exports = {
1193
+ PHPGenerator
1194
+ }