ether-code 0.9.2 → 0.9.5

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.
package/cli/ether.js CHANGED
@@ -6,7 +6,7 @@ const http = require('http')
6
6
  const { EtherCompiler } = require('./compiler')
7
7
  const { Watcher } = require('./watcher')
8
8
 
9
- const VERSION = '0.9.2'
9
+ const VERSION = '0.9.5'
10
10
 
11
11
  const COLORS = {
12
12
  reset: '\x1b[0m',
@@ -134,7 +134,6 @@ class PHPGenerator {
134
134
  'formater nombre': 'number_format',
135
135
  'date': 'date',
136
136
  'temps': 'time',
137
- 'maintenant': 'time',
138
137
  'creer date': 'mktime',
139
138
  'date vers temps': 'strtotime',
140
139
  'formater date': 'date_format',
@@ -305,9 +304,33 @@ class PHPGenerator {
305
304
  return this.keywordMap[normalized] || word
306
305
  }
307
306
 
307
+ translateMethodName(word) {
308
+ if (!word) return word
309
+ const normalized = this.normalizeAccents(word)
310
+ const magicMethods = {
311
+ 'constructeur': '__construct',
312
+ 'destructeur': '__destruct',
313
+ 'obtenir': '__get',
314
+ 'definir': '__set',
315
+ 'appeler': '__call',
316
+ 'vers chaine': '__toString',
317
+ 'invoquer': '__invoke',
318
+ 'cloner': '__clone',
319
+ 'serialiser': '__serialize',
320
+ 'deserialiser': '__unserialize',
321
+ 'deboguer': '__debugInfo'
322
+ }
323
+ return magicMethods[normalized] || word
324
+ }
325
+
308
326
  extractTypeName(typeNode) {
309
327
  if (!typeNode) return ''
310
328
  if (typeof typeNode === 'string') return this.translateKeyword(typeNode)
329
+ if (typeNode.type === 'TypeHint') {
330
+ const types = (typeNode.types || []).map(t => this.translateKeyword(t))
331
+ const result = types.join('|')
332
+ return typeNode.nullable ? '?' + result : result
333
+ }
311
334
  if (typeNode.name) return this.translateKeyword(typeNode.name)
312
335
  if (typeNode.type === 'Identifier') return this.translateKeyword(typeNode.name || typeNode.value || '')
313
336
  if (typeNode.value) return this.translateKeyword(typeNode.value)
@@ -317,6 +340,9 @@ class PHPGenerator {
317
340
  generate(ast) {
318
341
  this.output = ''
319
342
  this.indent = 0
343
+ this.closureVariables = new Set()
344
+
345
+ this.collectClosureVariables(ast)
320
346
 
321
347
  if (ast.phpTag !== false) {
322
348
  this.output = '<?php\n'
@@ -341,6 +367,32 @@ class PHPGenerator {
341
367
  return this.output.trim()
342
368
  }
343
369
 
370
+ collectClosureVariables(ast) {
371
+ const nodes = Array.isArray(ast) ? ast : (ast?.body || [ast])
372
+
373
+ for (const node of nodes) {
374
+ if (!node) continue
375
+
376
+ if (node.type === 'VariableDeclaration') {
377
+ const init = node.init || node.value
378
+ if (init && (init.type === 'ArrowFunctionExpression' || init.type === 'FunctionExpression' || init.type === 'ClosureExpression')) {
379
+ const name = node.name || node.id?.name
380
+ if (name) this.closureVariables.add(name)
381
+ }
382
+ }
383
+
384
+ if (node.body) {
385
+ this.collectClosureVariables(node.body)
386
+ }
387
+ if (node.consequent) {
388
+ this.collectClosureVariables(Array.isArray(node.consequent) ? node.consequent : [node.consequent])
389
+ }
390
+ if (node.alternate) {
391
+ this.collectClosureVariables(Array.isArray(node.alternate) ? node.alternate : [node.alternate])
392
+ }
393
+ }
394
+ }
395
+
344
396
  generateNode(node) {
345
397
  if (!node) return ''
346
398
 
@@ -376,6 +428,8 @@ class PHPGenerator {
376
428
  return this.generateForStatement(node)
377
429
  case 'ForEachStatement':
378
430
  return this.generateForEachStatement(node)
431
+ case 'ForeachStatement':
432
+ return this.generateForEachStatement(node)
379
433
  case 'WhileStatement':
380
434
  return this.generateWhileStatement(node)
381
435
  case 'DoWhileStatement':
@@ -474,9 +528,19 @@ class PHPGenerator {
474
528
  }
475
529
 
476
530
  generateUseStatement(node) {
477
- const name = node.name || node.source
478
- const alias = node.alias ? ` as ${node.alias}` : ''
479
- this.writeLine(`use ${name}${alias};`)
531
+ if (node.imports && node.imports.length > 0) {
532
+ for (const imp of node.imports) {
533
+ const name = imp.path || imp.name
534
+ const alias = imp.alias ? ` as ${imp.alias}` : ''
535
+ const useType = node.useType ? `${node.useType} ` : ''
536
+ this.writeLine(`use ${useType}${name}${alias};`)
537
+ }
538
+ } else {
539
+ const name = node.name || node.source
540
+ const alias = node.alias ? ` as ${node.alias}` : ''
541
+ const useType = node.useType ? `${node.useType} ` : ''
542
+ this.writeLine(`use ${useType}${name}${alias};`)
543
+ }
480
544
  return ''
481
545
  }
482
546
 
@@ -619,7 +683,7 @@ class PHPGenerator {
619
683
  this.writeLine(`${visibility} ${isStatic}${isReadonly}${typeHint}${name}${value};`)
620
684
  } else if (node.type === 'Method' || node.kind === 'method' || node.type === 'MethodDeclaration') {
621
685
  const name = node.name || node.key?.name || ''
622
- const translatedName = this.translateKeyword(name)
686
+ const translatedName = this.translateMethodName(name)
623
687
  const params = (node.params || []).map(p => this.generateParam(p)).join(', ')
624
688
  let returnType = ''
625
689
 
@@ -673,7 +737,7 @@ class PHPGenerator {
673
737
 
674
738
  const visibility = 'public'
675
739
  const name = node.name || node.key?.name || ''
676
- const translatedName = this.translateKeyword(name)
740
+ const translatedName = this.translateMethodName(name)
677
741
  const params = (node.params || []).map(p => this.generateParam(p)).join(', ')
678
742
  let returnType = ''
679
743
 
@@ -705,7 +769,8 @@ class PHPGenerator {
705
769
  let declaration = 'enum ' + (node.name || '')
706
770
 
707
771
  if (node.backingType) {
708
- declaration += ': ' + this.translateKeyword(node.backingType)
772
+ const type = this.extractTypeName(node.backingType)
773
+ if (type) declaration += ': ' + type
709
774
  }
710
775
 
711
776
  this.writeLine(declaration + ' {')
@@ -855,21 +920,26 @@ class PHPGenerator {
855
920
  this.generateBody(node.block)
856
921
  this.indent--
857
922
 
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)
923
+ const catches = node.catches || (node.handler ? [node.handler] : [])
924
+ for (const catchClause of catches) {
925
+ const types = catchClause.types || []
926
+ const exceptionType = types.length > 0
927
+ ? types.join('|')
928
+ : (catchClause.exceptionType || catchClause.type || 'Exception')
929
+ const param = catchClause.param
930
+ ? this.generateVariable(catchClause.param)
862
931
  : '$e'
863
932
  this.writeLine(`} catch (${exceptionType} ${param}) {`)
864
933
  this.indent++
865
- this.generateBody(node.handler.body)
934
+ this.generateBody(catchClause.body)
866
935
  this.indent--
867
936
  }
868
937
 
869
- if (node.finalizer) {
938
+ const finallyBlock = node.finally || node.finalizer
939
+ if (finallyBlock) {
870
940
  this.writeLine('} finally {')
871
941
  this.indent++
872
- this.generateBody(node.finalizer)
942
+ this.generateBody(finallyBlock)
873
943
  this.indent--
874
944
  }
875
945
 
@@ -916,19 +986,82 @@ class PHPGenerator {
916
986
 
917
987
  generateCallExpression(node) {
918
988
  let callee
989
+ let originalName = ''
919
990
 
920
991
  if (typeof node.callee === 'string') {
921
- callee = this.translateFunction(node.callee)
992
+ originalName = node.callee
993
+ if (this.closureVariables && this.closureVariables.has(node.callee)) {
994
+ callee = '$' + node.callee
995
+ } else {
996
+ callee = this.translateFunction(node.callee)
997
+ }
922
998
  } else if (node.callee.type === 'Identifier') {
923
- callee = this.translateFunction(node.callee.name)
999
+ originalName = node.callee.name
1000
+ if (this.closureVariables && this.closureVariables.has(node.callee.name)) {
1001
+ callee = '$' + node.callee.name
1002
+ } else {
1003
+ callee = this.translateFunction(node.callee.name)
1004
+ }
1005
+ } else if (node.callee.type === 'Variable') {
1006
+ callee = this.generateVariable(node.callee)
924
1007
  } else if (node.callee.type === 'MemberExpression' || node.callee.type === 'StaticMemberExpression') {
925
1008
  callee = this.generateNode(node.callee)
926
1009
  } else {
927
1010
  callee = this.generateNode(node.callee)
928
1011
  }
929
1012
 
930
- const args = (node.arguments || []).map(a => this.generateNode(a)).join(', ')
931
- return `${callee}(${args})`
1013
+ let args = (node.arguments || []).map(a => this.generateNode(a))
1014
+
1015
+ args = this.reorderArguments(callee, args, originalName)
1016
+ args = this.addDefaultArguments(callee, args)
1017
+
1018
+ return `${callee}(${args.join(', ')})`
1019
+ }
1020
+
1021
+ reorderArguments(func, args, originalName) {
1022
+ const normalized = this.normalizeAccents(originalName)
1023
+
1024
+ if ((func === 'str_replace' || normalized === 'remplacer') && args.length === 3) {
1025
+ return [args[1], args[2], args[0]]
1026
+ }
1027
+
1028
+ if ((func === 'array_map' || normalized === 'mapper' || normalized === 'mapper tableau' || normalized === 'transformer') && args.length === 2) {
1029
+ return [args[1], args[0]]
1030
+ }
1031
+
1032
+ return args
1033
+ }
1034
+
1035
+ addDefaultArguments(func, args) {
1036
+ if (func === 'password_hash' && args.length === 1) {
1037
+ return [...args, 'PASSWORD_DEFAULT']
1038
+ }
1039
+
1040
+ return args
1041
+ }
1042
+
1043
+ isNativeFunction(name) {
1044
+ const natives = [
1045
+ 'echo', 'print', 'var_dump', 'print_r', 'die', 'exit',
1046
+ 'strlen', 'substr', 'strpos', 'str_replace', 'strtoupper', 'strtolower',
1047
+ 'trim', 'ltrim', 'rtrim', 'explode', 'implode', 'sprintf', 'printf',
1048
+ 'count', 'array_push', 'array_pop', 'array_shift', 'array_unshift',
1049
+ 'array_merge', 'array_filter', 'array_map', 'array_reduce', 'array_keys',
1050
+ 'array_values', 'array_sum', 'array_unique', 'array_reverse', 'sort',
1051
+ 'in_array', 'array_key_exists', 'array_search',
1052
+ 'is_array', 'is_string', 'is_int', 'is_float', 'is_bool', 'is_null',
1053
+ 'is_numeric', 'is_object', 'is_callable', 'isset', 'empty', 'gettype',
1054
+ 'intval', 'floatval', 'strval', 'boolval', 'settype',
1055
+ 'abs', 'ceil', 'floor', 'round', 'sqrt', 'pow', 'rand', 'max', 'min', 'pi',
1056
+ 'date', 'time', 'strtotime', 'mktime',
1057
+ 'file_exists', 'file_get_contents', 'file_put_contents', 'fopen', 'fclose', 'fread', 'fwrite',
1058
+ 'json_encode', 'json_decode', 'preg_match', 'preg_match_all', 'preg_replace',
1059
+ 'header', 'session_start', 'session_destroy', 'password_hash', 'password_verify',
1060
+ 'include', 'include_once', 'require', 'require_once',
1061
+ 'class_exists', 'function_exists', 'method_exists', 'defined', 'define', 'constant',
1062
+ 'new', 'throw'
1063
+ ]
1064
+ return natives.includes(name)
932
1065
  }
933
1066
 
934
1067
  generateMemberExpression(node) {
@@ -936,9 +1069,9 @@ class PHPGenerator {
936
1069
  let property
937
1070
 
938
1071
  if (typeof node.property === 'string') {
939
- property = node.property
1072
+ property = this.translateMemberProperty(node.property)
940
1073
  } else if (node.property.type === 'Identifier') {
941
- property = this.translateFunction(node.property.name)
1074
+ property = this.translateMemberProperty(node.property.name)
942
1075
  } else {
943
1076
  property = this.generateNode(node.property)
944
1077
  }
@@ -951,6 +1084,26 @@ class PHPGenerator {
951
1084
  return `${object}${operator}${property}`
952
1085
  }
953
1086
 
1087
+ translateMemberProperty(name) {
1088
+ const translations = {
1089
+ 'valeur': 'value',
1090
+ 'nom': 'name',
1091
+ 'message': 'message',
1092
+ 'code': 'code',
1093
+ 'fichier': 'file',
1094
+ 'ligne': 'line',
1095
+ 'precedent': 'previous',
1096
+ 'trace': 'trace',
1097
+ 'formater': 'format',
1098
+ 'ajouter': 'add',
1099
+ 'modifier': 'modify',
1100
+ 'obtenir': 'get',
1101
+ 'definir': 'set'
1102
+ }
1103
+ const normalized = this.normalizeAccents(name)
1104
+ return translations[normalized] || name
1105
+ }
1106
+
954
1107
  generateStaticMemberExpression(node) {
955
1108
  const cls = node.class?.name || this.generateNode(node.class)
956
1109
  let member = node.member?.name || this.generateNode(node.member)
@@ -1044,12 +1197,6 @@ class PHPGenerator {
1044
1197
  const name = node.name || node
1045
1198
  if (typeof name !== 'string') return '$var'
1046
1199
 
1047
- const normalized = this.normalizeAccents(name)
1048
-
1049
- if (this.functionMap[normalized]) {
1050
- return this.functionMap[normalized]
1051
- }
1052
-
1053
1200
  const translated = this.translateKeyword(name)
1054
1201
 
1055
1202
  if (translated.startsWith('$')) return translated
@@ -1057,11 +1204,11 @@ class PHPGenerator {
1057
1204
  return translated
1058
1205
  }
1059
1206
  if (/^[A-Z]/.test(translated)) return translated
1060
- if (translated === name && !name.startsWith('$')) {
1207
+ if (!name.startsWith('$')) {
1061
1208
  return '$' + name
1062
1209
  }
1063
1210
 
1064
- return translated.startsWith('$') ? translated : '$' + translated
1211
+ return name
1065
1212
  }
1066
1213
 
1067
1214
  generateVariable(node) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ether-code",
3
- "version": "0.9.2",
3
+ "version": "0.9.5",
4
4
  "description": "Ether - Le langage intentionnel",
5
5
  "main": "cli/compiler.js",
6
6
  "bin": {
@@ -159,7 +159,6 @@ class EtherParserPHP extends EtherParserBase {
159
159
  'json decoder': 'json_decode',
160
160
  'date': 'date',
161
161
  'temps': 'time',
162
- 'maintenant': 'time',
163
162
  'demarrer session': 'session_start',
164
163
  'detruire session': 'session_destroy',
165
164
  'definir cookie': 'setcookie',
@@ -635,6 +634,9 @@ class EtherParserPHP extends EtherParserBase {
635
634
  type: 'ClassDeclaration',
636
635
  name: name,
637
636
  modifiers: modifiers,
637
+ abstract: modifiers.includes('abstract'),
638
+ final: modifiers.includes('final'),
639
+ readonly: modifiers.includes('readonly'),
638
640
  superClass: superClass,
639
641
  interfaces: interfaces,
640
642
  body: body