ether-code 0.8.0 → 0.8.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.
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.8.0'
9
+ const VERSION = '0.8.2'
10
10
 
11
11
  const COLORS = {
12
12
  reset: '\x1b[0m',
@@ -46,7 +46,8 @@ class PHPGenerator {
46
46
  }
47
47
 
48
48
  translate(word) {
49
- if (!word || typeof word !== 'string') return word
49
+ if (!word) return word
50
+ if (typeof word !== 'string') return String(word)
50
51
 
51
52
  const normalized = word.toLowerCase().trim()
52
53
 
@@ -63,6 +64,7 @@ class PHPGenerator {
63
64
  }
64
65
 
65
66
  removeAccents(str) {
67
+ if (typeof str !== 'string') return String(str)
66
68
  return str.normalize('NFD').replace(/[\u0300-\u036f]/g, '')
67
69
  }
68
70
 
@@ -140,12 +142,19 @@ class PHPGenerator {
140
142
  'ArrayExpression': () => this.generateArrayExpression(node),
141
143
  'ObjectExpression': () => this.generateObjectExpression(node),
142
144
  'ArrowFunction': () => this.generateArrowFunction(node),
145
+ 'ArrowFunctionExpression': () => this.generateArrowFunction(node),
143
146
  'Closure': () => this.generateClosure(node),
147
+ 'FunctionExpression': () => this.generateClosure(node),
144
148
  'Identifier': () => this.generateIdentifier(node),
149
+ 'Variable': () => this.generateVariable(node),
145
150
  'Literal': () => this.generateLiteral(node),
151
+ 'StringLiteral': () => this.generateStringLiteral(node),
146
152
  'NewExpression': () => this.generateNewExpression(node),
147
153
  'InstanceOf': () => this.generateInstanceOf(node),
148
154
  'BlockStatement': () => this.generateBlockStatement(node),
155
+ 'ThisExpression': () => '$this',
156
+ 'StaticMemberExpression': () => this.generateStaticMemberExpression(node),
157
+ 'IndexExpression': () => this.generateIndexExpression(node),
149
158
  'EmptyStatement': () => '',
150
159
  'Comment': () => ''
151
160
  }
@@ -168,11 +177,12 @@ class PHPGenerator {
168
177
  }
169
178
 
170
179
  generateFunctionDeclaration(node) {
171
- const name = this.translate(node.name || node.id?.name || 'anonymous')
180
+ const name = this.safeString(node.name || node.id?.name || 'anonymous')
181
+ const translatedName = this.translate(name)
172
182
  const params = this.generateParams(node.params || [])
173
183
  const returnType = node.returnType ? ': ' + this.translateType(node.returnType) : ''
174
184
 
175
- this.writeLine(`function ${name}(${params})${returnType} {`)
185
+ this.writeLine(`function ${translatedName}(${params})${returnType} {`)
176
186
  this.indent++
177
187
  this.generateNode(node.body)
178
188
  this.indent--
@@ -200,8 +210,8 @@ class PHPGenerator {
200
210
  result += '&'
201
211
  }
202
212
 
203
- const name = p.name || p.id?.name || p
204
- result += '$' + this.translate(typeof name === 'string' ? name : name.name || 'param')
213
+ const name = this.safeString(p.name || p.id?.name || p)
214
+ result += '$' + this.translate(name)
205
215
 
206
216
  if (p.default !== undefined) {
207
217
  result += ' = ' + this.generateNode(p.default)
@@ -211,6 +221,14 @@ class PHPGenerator {
211
221
  }).join(', ')
212
222
  }
213
223
 
224
+ safeString(value) {
225
+ if (typeof value === 'string') return value
226
+ if (value && typeof value === 'object') {
227
+ return value.name || value.value || 'unknown'
228
+ }
229
+ return String(value || 'unknown')
230
+ }
231
+
214
232
  translateType(type) {
215
233
  if (!type) return ''
216
234
 
@@ -230,7 +248,8 @@ class PHPGenerator {
230
248
  'itérable': 'iterable', 'iterable': 'iterable',
231
249
  'jamais': 'never', 'never': 'never'
232
250
  }
233
- return typeMap[translated.toLowerCase()] || typeMap[type.toLowerCase()] || type
251
+ const lower = translated.toLowerCase()
252
+ return typeMap[lower] || typeMap[type.toLowerCase()] || type
234
253
  }
235
254
 
236
255
  if (type.union) {
@@ -245,7 +264,11 @@ class PHPGenerator {
245
264
  return '?' + this.translateType(type.type)
246
265
  }
247
266
 
248
- return type.name || type
267
+ if (type.name) {
268
+ return this.translateType(type.name)
269
+ }
270
+
271
+ return String(type)
249
272
  }
250
273
 
251
274
  generateClassDeclaration(node) {
@@ -255,14 +278,15 @@ class PHPGenerator {
255
278
  if (node.final) declaration += 'final '
256
279
  if (node.readonly) declaration += 'readonly '
257
280
 
258
- declaration += 'class ' + this.translate(node.name || node.id?.name)
281
+ const className = this.safeString(node.name || node.id?.name)
282
+ declaration += 'class ' + this.translate(className)
259
283
 
260
284
  if (node.extends) {
261
- declaration += ' extends ' + this.translate(node.extends)
285
+ declaration += ' extends ' + this.translate(this.safeString(node.extends))
262
286
  }
263
287
 
264
288
  if (node.implements && node.implements.length > 0) {
265
- declaration += ' implements ' + node.implements.map(i => this.translate(i)).join(', ')
289
+ declaration += ' implements ' + node.implements.map(i => this.translate(this.safeString(i))).join(', ')
266
290
  }
267
291
 
268
292
  this.writeLine(declaration + ' {')
@@ -278,10 +302,11 @@ class PHPGenerator {
278
302
  }
279
303
 
280
304
  generateInterfaceDeclaration(node) {
281
- let declaration = 'interface ' + this.translate(node.name || node.id?.name)
305
+ const name = this.safeString(node.name || node.id?.name)
306
+ let declaration = 'interface ' + this.translate(name)
282
307
 
283
308
  if (node.extends && node.extends.length > 0) {
284
- declaration += ' extends ' + node.extends.map(e => this.translate(e)).join(', ')
309
+ declaration += ' extends ' + node.extends.map(e => this.translate(this.safeString(e))).join(', ')
285
310
  }
286
311
 
287
312
  this.writeLine(declaration + ' {')
@@ -297,7 +322,8 @@ class PHPGenerator {
297
322
  }
298
323
 
299
324
  generateTraitDeclaration(node) {
300
- this.writeLine('trait ' + this.translate(node.name || node.id?.name) + ' {')
325
+ const name = this.safeString(node.name || node.id?.name)
326
+ this.writeLine('trait ' + this.translate(name) + ' {')
301
327
  this.indent++
302
328
 
303
329
  for (const member of node.body || node.members || []) {
@@ -310,7 +336,8 @@ class PHPGenerator {
310
336
  }
311
337
 
312
338
  generateEnumDeclaration(node) {
313
- let declaration = 'enum ' + this.translate(node.name || node.id?.name)
339
+ const name = this.safeString(node.name || node.id?.name)
340
+ let declaration = 'enum ' + this.translate(name)
314
341
 
315
342
  if (node.backingType) {
316
343
  declaration += ': ' + this.translateType(node.backingType)
@@ -346,7 +373,7 @@ class PHPGenerator {
346
373
  if (node.final) declaration += 'final '
347
374
  if (node.abstract) declaration += 'abstract '
348
375
 
349
- const name = this.translateMethodName(node.name || node.key?.name)
376
+ const name = this.translateMethodName(this.safeString(node.name || node.key?.name))
350
377
  const params = this.generateParams(node.params || node.value?.params || [])
351
378
  const returnType = node.returnType ? ': ' + this.translateType(node.returnType) : ''
352
379
 
@@ -425,8 +452,8 @@ class PHPGenerator {
425
452
  declaration += this.translateType(node.type) + ' '
426
453
  }
427
454
 
428
- const name = this.translate(node.name || node.key?.name)
429
- declaration += '$' + name
455
+ const name = this.safeString(node.name || node.key?.name)
456
+ declaration += '$' + this.translate(name)
430
457
 
431
458
  if (node.value !== undefined) {
432
459
  declaration += ' = ' + this.generateNode(node.value)
@@ -437,8 +464,8 @@ class PHPGenerator {
437
464
 
438
465
  generateVariableDeclaration(node) {
439
466
  for (const decl of node.declarations || [node]) {
440
- const name = decl.name || decl.id?.name || decl.id
441
- const varName = '$' + this.translate(typeof name === 'string' ? name : name.name || 'var')
467
+ const name = this.safeString(decl.name || decl.id?.name || decl.id)
468
+ const varName = '$' + this.translate(name)
442
469
 
443
470
  if (decl.init !== undefined) {
444
471
  const value = this.generateNode(decl.init)
@@ -450,7 +477,7 @@ class PHPGenerator {
450
477
  }
451
478
 
452
479
  generateConstantDeclaration(node) {
453
- const name = node.name || node.id?.name
480
+ const name = this.safeString(node.name || node.id?.name)
454
481
  const value = this.generateNode(node.value || node.init)
455
482
 
456
483
  if (node.classLevel) {
@@ -498,8 +525,8 @@ class PHPGenerator {
498
525
 
499
526
  generateForEachStatement(node) {
500
527
  const array = this.generateNode(node.array || node.right || node.iterable)
501
- const value = this.generateIdentifier(node.value || node.left || node.valueVar)
502
- const key = node.key || node.keyVar ? this.generateIdentifier(node.key || node.keyVar) + ' => ' : ''
528
+ const value = this.generateVariable(node.value || node.left || node.valueVar)
529
+ const key = node.key || node.keyVar ? this.generateVariable(node.key || node.keyVar) + ' => ' : ''
503
530
 
504
531
  this.writeLine(`foreach (${array} as ${key}${value}) {`)
505
532
  this.indent++
@@ -576,7 +603,7 @@ class PHPGenerator {
576
603
 
577
604
  for (const handler of node.handlers || (node.handler ? [node.handler] : [])) {
578
605
  const exceptionType = handler.type || handler.param?.type || 'Exception'
579
- const param = handler.param ? this.generateIdentifier(handler.param) : '$e'
606
+ const param = handler.param ? this.generateVariable(handler.param) : '$e'
580
607
  this.writeLine(`} catch (${exceptionType} ${param}) {`)
581
608
  this.indent++
582
609
  this.generateNode(handler.body)
@@ -623,12 +650,13 @@ class PHPGenerator {
623
650
  }
624
651
 
625
652
  generateNamespace(node) {
626
- this.writeLine(`namespace ${node.name};`)
653
+ const name = this.safeString(node.name)
654
+ this.writeLine(`namespace ${name};`)
627
655
  this.writeLine('')
628
656
  }
629
657
 
630
658
  generateUseStatement(node) {
631
- const name = node.name || node.source
659
+ const name = this.safeString(node.name || node.source)
632
660
  const alias = node.alias ? ' as ' + node.alias : ''
633
661
  const type = node.type === 'function' ? 'function ' : (node.type === 'const' ? 'const ' : '')
634
662
  this.writeLine(`use ${type}${name}${alias};`)
@@ -655,17 +683,19 @@ class PHPGenerator {
655
683
 
656
684
  if (typeof node.callee === 'string') {
657
685
  callee = this.translate(node.callee)
658
- } else if (node.callee.type === 'MemberExpression') {
686
+ } else if (node.callee && node.callee.type === 'MemberExpression') {
659
687
  callee = this.generateMemberExpression(node.callee)
688
+ } else if (node.callee && node.callee.type === 'StaticMemberExpression') {
689
+ callee = this.generateStaticMemberExpression(node.callee)
660
690
  } else {
661
691
  callee = this.generateNode(node.callee)
662
692
  }
663
693
 
664
694
  const args = (node.arguments || []).map(a => {
665
- if (a.spread) {
695
+ if (a && a.type === 'SpreadElement') {
666
696
  return '...' + this.generateNode(a.argument || a)
667
697
  }
668
- if (a.name && a.value) {
698
+ if (a && a.type === 'NamedArgument') {
669
699
  return `${a.name}: ${this.generateNode(a.value)}`
670
700
  }
671
701
  return this.generateNode(a)
@@ -676,9 +706,15 @@ class PHPGenerator {
676
706
 
677
707
  generateMemberExpression(node) {
678
708
  const object = this.generateNode(node.object)
679
- const property = typeof node.property === 'string'
680
- ? node.property
681
- : this.generateNode(node.property)
709
+ let property
710
+
711
+ if (typeof node.property === 'string') {
712
+ property = node.property
713
+ } else if (node.property && node.property.name) {
714
+ property = node.property.name
715
+ } else {
716
+ property = this.generateNode(node.property)
717
+ }
682
718
 
683
719
  if (node.static) {
684
720
  return `${object}::${property}`
@@ -686,12 +722,33 @@ class PHPGenerator {
686
722
  if (node.computed) {
687
723
  return `${object}[${property}]`
688
724
  }
689
- if (node.nullSafe) {
725
+ if (node.nullsafe || node.operator === '?->') {
690
726
  return `${object}?->${property}`
691
727
  }
692
728
  return `${object}->${property}`
693
729
  }
694
730
 
731
+ generateStaticMemberExpression(node) {
732
+ const cls = this.generateNode(node.class)
733
+ let member
734
+
735
+ if (typeof node.member === 'string') {
736
+ member = node.member
737
+ } else if (node.member && node.member.name) {
738
+ member = node.member.name
739
+ } else {
740
+ member = this.generateNode(node.member)
741
+ }
742
+
743
+ return `${cls}::${member}`
744
+ }
745
+
746
+ generateIndexExpression(node) {
747
+ const object = this.generateNode(node.object)
748
+ const index = node.index ? this.generateNode(node.index) : ''
749
+ return `${object}[${index}]`
750
+ }
751
+
695
752
  generateBinaryExpression(node) {
696
753
  const left = this.generateNode(node.left)
697
754
  const right = this.generateNode(node.right)
@@ -708,14 +765,16 @@ class PHPGenerator {
708
765
  'fusion null': '??', 'null coalescing': '??'
709
766
  }
710
767
 
711
- op = opMap[op?.toLowerCase()] || op
768
+ const opLower = op ? op.toLowerCase() : op
769
+ op = opMap[opLower] || op
712
770
 
713
771
  return `${left} ${op} ${right}`
714
772
  }
715
773
 
716
774
  generateUnaryExpression(node) {
717
775
  const argument = this.generateNode(node.argument)
718
- const op = node.operator === 'non' || node.operator === 'not' ? '!' : node.operator
776
+ let op = node.operator
777
+ if (op === 'non' || op === 'not') op = '!'
719
778
 
720
779
  if (node.prefix) {
721
780
  return `${op}${argument}`
@@ -756,14 +815,22 @@ class PHPGenerator {
756
815
  generateArrayExpression(node) {
757
816
  const elements = (node.elements || []).map(e => {
758
817
  if (!e) return 'null'
759
- if (e.key !== undefined) {
818
+ if (e.type === 'SpreadElement') {
819
+ return '...' + this.generateNode(e.argument || e)
820
+ }
821
+ if (e.type === 'ArrayElement') {
822
+ if (e.key !== null && e.key !== undefined) {
823
+ const key = this.generateNode(e.key)
824
+ const value = this.generateNode(e.value)
825
+ return `${key} => ${value}`
826
+ }
827
+ return this.generateNode(e.value)
828
+ }
829
+ if (e.key !== undefined && e.key !== null) {
760
830
  const key = this.generateNode(e.key)
761
831
  const value = this.generateNode(e.value || e)
762
832
  return `${key} => ${value}`
763
833
  }
764
- if (e.spread) {
765
- return '...' + this.generateNode(e.argument || e)
766
- }
767
834
  return this.generateNode(e)
768
835
  })
769
836
 
@@ -786,19 +853,19 @@ class PHPGenerator {
786
853
 
787
854
  generateClosure(node) {
788
855
  const params = this.generateParams(node.params || [])
789
- const useVars = node.use && node.use.length > 0
790
- ? ` use (${node.use.map(u => (u.byRef ? '&' : '') + '$' + this.translate(u.name || u)).join(', ')})`
856
+ const useVars = node.uses && node.uses.length > 0
857
+ ? ` use (${node.uses.map(u => (u.byRef ? '&' : '') + '$' + this.translate(this.safeString(u.name || u))).join(', ')})`
791
858
  : ''
792
859
  const returnType = node.returnType ? ': ' + this.translateType(node.returnType) : ''
793
860
 
794
861
  let result = `function(${params})${useVars}${returnType} {\n`
795
862
  this.indent++
796
863
 
797
- if (Array.isArray(node.body)) {
798
- for (const stmt of node.body) {
864
+ if (node.body && Array.isArray(node.body.body)) {
865
+ for (const stmt of node.body.body) {
799
866
  result += this.getIndent() + this.generateNode(stmt)
800
867
  }
801
- } else {
868
+ } else if (node.body) {
802
869
  result += this.getIndent() + 'return ' + this.generateNode(node.body) + ';\n'
803
870
  }
804
871
 
@@ -807,34 +874,47 @@ class PHPGenerator {
807
874
  return result
808
875
  }
809
876
 
877
+ generateVariable(node) {
878
+ if (!node) return '$var'
879
+
880
+ const name = this.safeString(node.name || node.id?.name || node)
881
+ const translated = this.translate(name)
882
+ const translatedStr = String(translated)
883
+
884
+ if (translatedStr.startsWith('$')) return translatedStr
885
+ return '$' + translatedStr
886
+ }
887
+
810
888
  generateIdentifier(node) {
811
- const name = node.name || node.id?.name || node
812
- if (typeof name !== 'string') return '$var'
889
+ if (!node) return '$var'
813
890
 
891
+ const name = this.safeString(node.name || node.id?.name || node)
814
892
  const translated = this.translate(name)
893
+ const translatedStr = String(translated)
815
894
 
816
- if (translated.startsWith('$')) return translated
895
+ if (translatedStr.startsWith('$')) return translatedStr
817
896
 
818
897
  const keywords = [
819
898
  'true', 'false', 'null',
820
899
  'self', 'parent', 'static',
821
900
  'this', '$this'
822
901
  ]
823
- if (keywords.includes(translated.toLowerCase())) {
824
- if (translated.toLowerCase() === 'this') return '$this'
825
- return translated
902
+ if (keywords.includes(translatedStr.toLowerCase())) {
903
+ if (translatedStr.toLowerCase() === 'this') return '$this'
904
+ return translatedStr
826
905
  }
827
906
 
828
- if (/^[A-Z]/.test(translated)) return translated
907
+ if (/^[A-Z]/.test(translatedStr)) return translatedStr
829
908
 
830
- if (translated.includes('(') || translated.includes('::')) return translated
909
+ if (translatedStr.includes('(') || translatedStr.includes('::')) return translatedStr
831
910
 
832
911
  const superglobals = ['_GET', '_POST', '_SERVER', '_SESSION', '_COOKIE', '_FILES', '_REQUEST', '_ENV', 'GLOBALS']
833
- if (superglobals.includes(translated) || superglobals.includes(translated.toUpperCase())) {
834
- return '$' + translated.toUpperCase()
912
+ const upper = translatedStr.toUpperCase()
913
+ if (superglobals.includes(translatedStr) || superglobals.includes(upper)) {
914
+ return '$' + upper
835
915
  }
836
916
 
837
- return '$' + translated
917
+ return '$' + translatedStr
838
918
  }
839
919
 
840
920
  generateLiteral(node) {
@@ -854,10 +934,26 @@ class PHPGenerator {
854
934
  return String(node.value)
855
935
  }
856
936
 
937
+ generateStringLiteral(node) {
938
+ const value = node.value || ''
939
+ const escaped = value
940
+ .replace(/\\/g, '\\\\')
941
+ .replace(/"/g, '\\"')
942
+ .replace(/\n/g, '\\n')
943
+ .replace(/\r/g, '\\r')
944
+ .replace(/\t/g, '\\t')
945
+ return node.doubleQuoted !== false ? `"${escaped}"` : `'${escaped}'`
946
+ }
947
+
857
948
  generateNewExpression(node) {
858
- const callee = typeof node.callee === 'string'
859
- ? this.translate(node.callee)
860
- : this.generateNode(node.callee)
949
+ let callee
950
+ if (typeof node.callee === 'string') {
951
+ callee = this.translate(node.callee)
952
+ } else if (node.callee && node.callee.name) {
953
+ callee = this.translate(node.callee.name)
954
+ } else {
955
+ callee = this.generateNode(node.callee)
956
+ }
861
957
  const args = (node.arguments || []).map(a => this.generateNode(a)).join(', ')
862
958
  return `new ${callee}(${args})`
863
959
  }
@@ -887,6 +983,4 @@ class PHPGenerator {
887
983
  }
888
984
  }
889
985
 
890
- module.exports = {
891
- PHPGenerator
892
- }
986
+ module.exports = { PHPGenerator }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ether-code",
3
- "version": "0.8.0",
3
+ "version": "0.8.2",
4
4
  "description": "Ether - Le langage intentionnel",
5
5
  "main": "cli/compiler.js",
6
6
  "bin": {