ether-code 0.8.4 → 0.8.6

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.4'
9
+ const VERSION = '0.8.6'
10
10
 
11
11
  const COLORS = {
12
12
  reset: '\x1b[0m',
@@ -228,22 +228,27 @@ class PHPGenerator {
228
228
  result += this.translate(p.visibility) + ' '
229
229
  }
230
230
 
231
- if (p.type) {
232
- result += this.translateType(p.type) + ' '
231
+ if (p.readonly) {
232
+ result += 'readonly '
233
+ }
234
+
235
+ const typeHint = p.typeHint || p.type
236
+ if (typeHint) {
237
+ result += this.translateType(typeHint) + ' '
233
238
  }
234
239
 
235
240
  if (p.variadic) {
236
241
  result += '...'
237
242
  }
238
243
 
239
- if (p.reference) {
244
+ if (p.byReference || p.reference) {
240
245
  result += '&'
241
246
  }
242
247
 
243
248
  const name = this.safeString(p.name || p.id?.name || p)
244
- result += '$' + this.translate(name)
249
+ result += '$' + name
245
250
 
246
- if (p.default !== undefined) {
251
+ if (p.default !== undefined && p.default !== null) {
247
252
  result += ' = ' + this.generateNode(p.default)
248
253
  }
249
254
 
@@ -263,7 +268,6 @@ class PHPGenerator {
263
268
  if (!type) return ''
264
269
 
265
270
  if (typeof type === 'string') {
266
- const translated = this.translate(type)
267
271
  const typeMap = {
268
272
  'chaîne': 'string', 'chaine': 'string', 'string': 'string',
269
273
  'entier': 'int', 'integer': 'int', 'int': 'int',
@@ -278,19 +282,24 @@ class PHPGenerator {
278
282
  'itérable': 'iterable', 'iterable': 'iterable',
279
283
  'jamais': 'never', 'never': 'never'
280
284
  }
281
- const lower = translated.toLowerCase()
282
- return typeMap[lower] || typeMap[type.toLowerCase()] || type
285
+ const lower = type.toLowerCase()
286
+ return typeMap[lower] || type
287
+ }
288
+
289
+ if (type.type === 'TypeHint' && type.types) {
290
+ const typeStr = type.types.join('|')
291
+ return (type.nullable ? '?' : '') + typeStr
283
292
  }
284
293
 
285
- if (type.union) {
294
+ if (type.union && type.types) {
286
295
  return type.types.map(t => this.translateType(t)).join('|')
287
296
  }
288
297
 
289
- if (type.intersection) {
298
+ if (type.intersection && type.types) {
290
299
  return type.types.map(t => this.translateType(t)).join('&')
291
300
  }
292
301
 
293
- if (type.nullable) {
302
+ if (type.nullable && type.type) {
294
303
  return '?' + this.translateType(type.type)
295
304
  }
296
305
 
@@ -519,7 +528,7 @@ class PHPGenerator {
519
528
  }
520
529
 
521
530
  generateIfStatement(node) {
522
- const test = this.generateNode(node.test)
531
+ const test = this.generateNode(node.test || node.condition)
523
532
  this.writeLine(`if (${test}) {`)
524
533
  this.indent++
525
534
  this.generateNode(node.consequent)
@@ -527,7 +536,7 @@ class PHPGenerator {
527
536
 
528
537
  if (node.alternate) {
529
538
  if (node.alternate.type === 'IfStatement') {
530
- this.write('} else')
539
+ this.write('} else ')
531
540
  this.generateIfStatement({ ...node.alternate, isElseIf: true })
532
541
  } else {
533
542
  this.writeLine('} else {')
@@ -566,7 +575,7 @@ class PHPGenerator {
566
575
  }
567
576
 
568
577
  generateWhileStatement(node) {
569
- const test = this.generateNode(node.test)
578
+ const test = this.generateNode(node.test || node.condition)
570
579
  this.writeLine(`while (${test}) {`)
571
580
  this.indent++
572
581
  this.generateNode(node.body)
@@ -579,7 +588,7 @@ class PHPGenerator {
579
588
  this.indent++
580
589
  this.generateNode(node.body)
581
590
  this.indent--
582
- const test = this.generateNode(node.test)
591
+ const test = this.generateNode(node.test || node.condition)
583
592
  this.writeLine(`} while (${test});`)
584
593
  }
585
594
 
@@ -727,6 +736,9 @@ class PHPGenerator {
727
736
  callee = this.generateMemberExpression(node.callee)
728
737
  } else if (node.callee && node.callee.type === 'StaticMemberExpression') {
729
738
  callee = this.generateStaticMemberExpression(node.callee)
739
+ } else if (node.callee && node.callee.type === 'Identifier') {
740
+ const name = this.safeString(node.callee.name)
741
+ callee = this.translate(name)
730
742
  } else {
731
743
  callee = this.generateNode(node.callee)
732
744
  }
@@ -812,21 +824,21 @@ class PHPGenerator {
812
824
  }
813
825
 
814
826
  generateUnaryExpression(node) {
815
- const argument = this.generateNode(node.argument)
827
+ const argument = this.generateNode(node.argument || node.operand)
816
828
  let op = node.operator
817
829
  if (op === 'non' || op === 'not') op = '!'
818
830
 
819
- if (node.prefix) {
831
+ if (node.prefix !== false) {
820
832
  return `${op}${argument}`
821
833
  }
822
834
  return `${argument}${op}`
823
835
  }
824
836
 
825
837
  generateUpdateExpression(node) {
826
- const argument = this.generateNode(node.argument)
838
+ const argument = this.generateNode(node.argument || node.operand)
827
839
  const op = node.operator
828
840
 
829
- if (node.prefix) {
841
+ if (node.prefix !== false) {
830
842
  return `${op}${argument}`
831
843
  }
832
844
  return `${argument}${op}`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ether-code",
3
- "version": "0.8.4",
3
+ "version": "0.8.6",
4
4
  "description": "Ether - Le langage intentionnel",
5
5
  "main": "cli/compiler.js",
6
6
  "bin": {
@@ -1089,6 +1089,8 @@ class EtherParserPHP extends EtherParserBase {
1089
1089
  }
1090
1090
  }
1091
1091
 
1092
+ this.match(TokenType.COLON)
1093
+ this.skipNewlines()
1092
1094
  body = this.parseBlock(lang)
1093
1095
 
1094
1096
  return {
@@ -1158,6 +1160,7 @@ class EtherParserPHP extends EtherParserBase {
1158
1160
  let variadic = false
1159
1161
  let byReference = false
1160
1162
  let defaultValue = null
1163
+ let name = null
1161
1164
 
1162
1165
  const val = this.current() ? this.normalizeAccents(String(this.current().value).toLowerCase()) : ''
1163
1166
  if (this.phpVisibility.includes(val)) {
@@ -1171,16 +1174,6 @@ class EtherParserPHP extends EtherParserBase {
1171
1174
  this.advance()
1172
1175
  }
1173
1176
 
1174
- const currentType = this.current()
1175
- if (currentType && currentType.type === TokenType.IDENTIFIER) {
1176
- const typeLower = this.normalizeAccents(String(currentType.value).toLowerCase())
1177
- if (this.phpTypes[typeLower] || typeLower[0] === typeLower[0].toUpperCase()) {
1178
- if (!this.peek(1) || this.peek(1).type !== TokenType.EQUALS) {
1179
- typeHint = this.parseTypeHint(lang)
1180
- }
1181
- }
1182
- }
1183
-
1184
1177
  if (this.match(TokenType.SPREAD) || this.matchValue('...')) {
1185
1178
  variadic = true
1186
1179
  }
@@ -1189,14 +1182,34 @@ class EtherParserPHP extends EtherParserBase {
1189
1182
  byReference = true
1190
1183
  }
1191
1184
 
1192
- const nameToken = this.current()
1193
- if (!nameToken) return null
1185
+ const currentToken = this.current()
1186
+ if (!currentToken) return null
1187
+
1188
+ const nextToken = this.peek(1)
1194
1189
 
1195
- let name = this.safeStr(nameToken.value)
1196
- if (name.startsWith('$')) {
1197
- name = name.substring(1)
1190
+ if (nextToken && nextToken.type === TokenType.COLON) {
1191
+ name = this.safeStr(currentToken.value)
1192
+ if (name.startsWith('$')) name = name.substring(1)
1193
+ this.advance()
1194
+ this.advance()
1195
+ typeHint = this.parseTypeHint(lang)
1196
+ } else {
1197
+ const typeLower = this.normalizeAccents(String(currentToken.value).toLowerCase())
1198
+ if (this.phpTypes[typeLower] || /^[A-Z]/.test(currentToken.value)) {
1199
+ typeHint = this.parseTypeHint(lang)
1200
+ }
1201
+
1202
+ const nameToken = this.current()
1203
+ if (nameToken && nameToken.type === TokenType.IDENTIFIER) {
1204
+ name = this.safeStr(nameToken.value)
1205
+ if (name.startsWith('$')) name = name.substring(1)
1206
+ this.advance()
1207
+ }
1208
+ }
1209
+
1210
+ if (!name) {
1211
+ name = 'param'
1198
1212
  }
1199
- this.advance()
1200
1213
 
1201
1214
  if (this.match(TokenType.EQUALS)) {
1202
1215
  defaultValue = this.parseExpression(lang)
@@ -1312,9 +1325,10 @@ class EtherParserPHP extends EtherParserBase {
1312
1325
  condition = this.parseExpression(lang)
1313
1326
  this.match(TokenType.RPAREN)
1314
1327
  } else {
1315
- condition = this.parseExpression(lang)
1328
+ condition = this.parseConditionUntilColon(lang)
1316
1329
  }
1317
1330
 
1331
+ this.match(TokenType.COLON)
1318
1332
  this.skipNewlines()
1319
1333
  const consequent = this.parseBlock(lang)
1320
1334
 
@@ -1322,31 +1336,64 @@ class EtherParserPHP extends EtherParserBase {
1322
1336
  this.skipNewlines()
1323
1337
 
1324
1338
  const elseVal = this.current() ? this.normalizeAccents(String(this.current().value).toLowerCase()) : ''
1325
- if (elseVal === 'sinon si' || elseVal === 'elseif') {
1326
- this.advance()
1327
- alternate = this.parseConditional(lang)
1328
- alternate.type = 'ElseIfStatement'
1329
- } else if (elseVal === 'sinon' || elseVal === 'else') {
1339
+ if (elseVal === 'sinon') {
1330
1340
  this.advance()
1331
1341
 
1332
1342
  const nextVal = this.current() ? this.normalizeAccents(String(this.current().value).toLowerCase()) : ''
1333
1343
  if (nextVal === 'si' || nextVal === 'if') {
1334
1344
  alternate = this.parseConditional(lang)
1335
- alternate.type = 'ElseIfStatement'
1345
+ alternate.type = 'IfStatement'
1336
1346
  } else {
1347
+ this.match(TokenType.COLON)
1337
1348
  this.skipNewlines()
1338
1349
  alternate = this.parseBlock(lang)
1339
1350
  }
1351
+ } else if (elseVal === 'else') {
1352
+ this.advance()
1353
+ this.match(TokenType.COLON)
1354
+ this.skipNewlines()
1355
+ alternate = this.parseBlock(lang)
1340
1356
  }
1341
1357
 
1342
1358
  return {
1343
1359
  type: 'IfStatement',
1344
- condition: condition,
1360
+ test: condition,
1345
1361
  consequent: consequent,
1346
1362
  alternate: alternate
1347
1363
  }
1348
1364
  }
1349
1365
 
1366
+ parseConditionUntilColon(lang) {
1367
+ const startPos = this.pos
1368
+ let depth = 0
1369
+
1370
+ while (!this.isAtEnd()) {
1371
+ const token = this.current()
1372
+ if (!token) break
1373
+
1374
+ if (token.type === TokenType.LPAREN) depth++
1375
+ if (token.type === TokenType.RPAREN) depth--
1376
+
1377
+ if (depth === 0 && token.type === TokenType.COLON) {
1378
+ break
1379
+ }
1380
+ if (depth === 0 && token.type === TokenType.NEWLINE) {
1381
+ break
1382
+ }
1383
+
1384
+ this.advance()
1385
+ }
1386
+
1387
+ const endPos = this.pos
1388
+ this.pos = startPos
1389
+
1390
+ if (endPos > startPos) {
1391
+ return this.parseExpression(lang)
1392
+ }
1393
+
1394
+ return { type: 'Literal', value: true }
1395
+ }
1396
+
1350
1397
  parseLoop(lang) {
1351
1398
  const token = this.current()
1352
1399
  const value = this.normalizeAccents(String(token.value).toLowerCase())
@@ -1385,7 +1432,7 @@ class EtherParserPHP extends EtherParserBase {
1385
1432
 
1386
1433
  parseFor(lang) {
1387
1434
  this.advance()
1388
-
1435
+
1389
1436
  let init = null
1390
1437
  let test = null
1391
1438
  let update = null
@@ -1405,8 +1452,23 @@ class EtherParserPHP extends EtherParserBase {
1405
1452
  update = this.parseExpression(lang)
1406
1453
  }
1407
1454
  this.match(TokenType.RPAREN)
1455
+ } else {
1456
+ if (!this.check(TokenType.SEMICOLON) && !this.check(TokenType.COLON)) {
1457
+ init = this.parseExpression(lang)
1458
+ }
1459
+ this.match(TokenType.SEMICOLON)
1460
+
1461
+ if (!this.check(TokenType.SEMICOLON) && !this.check(TokenType.COLON)) {
1462
+ test = this.parseExpression(lang)
1463
+ }
1464
+ this.match(TokenType.SEMICOLON)
1465
+
1466
+ if (!this.check(TokenType.COLON) && !this.check(TokenType.NEWLINE)) {
1467
+ update = this.parseExpression(lang)
1468
+ }
1408
1469
  }
1409
1470
 
1471
+ this.match(TokenType.COLON)
1410
1472
  this.skipNewlines()
1411
1473
  const body = this.parseBlock(lang)
1412
1474
 
@@ -1450,7 +1512,7 @@ class EtherParserPHP extends EtherParserBase {
1450
1512
  }
1451
1513
 
1452
1514
  const valueVar = this.current()
1453
- let value = this.safeStr(valueVar ? valueVar.value : 'value')
1515
+ value = this.safeStr(valueVar ? valueVar.value : 'value')
1454
1516
  if (value.startsWith('$')) value = value.substring(1)
1455
1517
  this.advance()
1456
1518
  } else {
@@ -1458,8 +1520,44 @@ class EtherParserPHP extends EtherParserBase {
1458
1520
  }
1459
1521
 
1460
1522
  this.match(TokenType.RPAREN)
1523
+ } else {
1524
+ const arrayToken = this.current()
1525
+ if (arrayToken) {
1526
+ array = { type: 'Identifier', name: this.safeStr(arrayToken.value) }
1527
+ this.advance()
1528
+ }
1529
+
1530
+ const asVal = this.current() ? this.normalizeAccents(String(this.current().value).toLowerCase()) : ''
1531
+ if (asVal === 'comme' || asVal === 'as') {
1532
+ this.advance()
1533
+ }
1534
+
1535
+ if (this.match(TokenType.AMPERSAND)) {
1536
+ byRef = true
1537
+ }
1538
+
1539
+ const firstVar = this.current()
1540
+ let firstName = this.safeStr(firstVar ? firstVar.value : 'item')
1541
+ if (firstName.startsWith('$')) firstName = firstName.substring(1)
1542
+ this.advance()
1543
+
1544
+ if (this.match(TokenType.DOUBLE_ARROW) || this.matchValue('=>')) {
1545
+ key = firstName
1546
+
1547
+ if (this.match(TokenType.AMPERSAND)) {
1548
+ byRef = true
1549
+ }
1550
+
1551
+ const valueVar = this.current()
1552
+ value = this.safeStr(valueVar ? valueVar.value : 'value')
1553
+ if (value.startsWith('$')) value = value.substring(1)
1554
+ this.advance()
1555
+ } else {
1556
+ value = firstName
1557
+ }
1461
1558
  }
1462
1559
 
1560
+ this.match(TokenType.COLON)
1463
1561
  this.skipNewlines()
1464
1562
  const body = this.parseBlock(lang)
1465
1563
 
@@ -1479,15 +1577,16 @@ class EtherParserPHP extends EtherParserBase {
1479
1577
  condition = this.parseExpression(lang)
1480
1578
  this.match(TokenType.RPAREN)
1481
1579
  } else {
1482
- condition = this.parseExpression(lang)
1580
+ condition = this.parseConditionUntilColon(lang)
1483
1581
  }
1484
1582
 
1583
+ this.match(TokenType.COLON)
1485
1584
  this.skipNewlines()
1486
1585
  const body = this.parseBlock(lang)
1487
1586
 
1488
1587
  return {
1489
1588
  type: 'WhileStatement',
1490
- condition: condition,
1589
+ test: condition,
1491
1590
  body: body
1492
1591
  }
1493
1592
  }
@@ -2042,12 +2141,12 @@ class EtherParserPHP extends EtherParserBase {
2042
2141
  return { type: 'UnaryExpression', operator: '!', operand }
2043
2142
  }
2044
2143
 
2045
- if (this.match(TokenType.MINUS)) {
2144
+ if (this.match(TokenType.MINUS) && !this.check(TokenType.MINUS)) {
2046
2145
  const operand = this.parseUnary(lang)
2047
2146
  return { type: 'UnaryExpression', operator: '-', operand }
2048
2147
  }
2049
2148
 
2050
- if (this.match(TokenType.PLUS)) {
2149
+ if (this.match(TokenType.PLUS) && !this.check(TokenType.PLUS)) {
2051
2150
  const operand = this.parseUnary(lang)
2052
2151
  return { type: 'UnaryExpression', operator: '+', operand }
2053
2152
  }
@@ -2062,12 +2161,15 @@ class EtherParserPHP extends EtherParserBase {
2062
2161
  return { type: 'UnaryExpression', operator: '~', operand }
2063
2162
  }
2064
2163
 
2065
- if (this.match(TokenType.DOUBLE_PLUS)) {
2164
+ const current = this.current()
2165
+ if (current && current.type === TokenType.PLUS && current.value === '++') {
2166
+ this.advance()
2066
2167
  const operand = this.parseUnary(lang)
2067
2168
  return { type: 'UpdateExpression', operator: '++', prefix: true, operand }
2068
2169
  }
2069
2170
 
2070
- if (this.match(TokenType.DOUBLE_MINUS)) {
2171
+ if (current && current.type === TokenType.MINUS && current.value === '--') {
2172
+ this.advance()
2071
2173
  const operand = this.parseUnary(lang)
2072
2174
  return { type: 'UpdateExpression', operator: '--', prefix: true, operand }
2073
2175
  }
@@ -2179,11 +2281,14 @@ class EtherParserPHP extends EtherParserBase {
2179
2281
  parsePostfix(lang) {
2180
2282
  let expr = this.parseCall(lang)
2181
2283
 
2182
- if (this.match(TokenType.DOUBLE_PLUS)) {
2284
+ const current = this.current()
2285
+ if (current && current.type === TokenType.PLUS && current.value === '++') {
2286
+ this.advance()
2183
2287
  return { type: 'UpdateExpression', operator: '++', prefix: false, operand: expr }
2184
2288
  }
2185
2289
 
2186
- if (this.match(TokenType.DOUBLE_MINUS)) {
2290
+ if (current && current.type === TokenType.MINUS && current.value === '--') {
2291
+ this.advance()
2187
2292
  return { type: 'UpdateExpression', operator: '--', prefix: false, operand: expr }
2188
2293
  }
2189
2294
 
@@ -2344,9 +2449,8 @@ class EtherParserPHP extends EtherParserBase {
2344
2449
  return { type: 'Variable', name: value.substring(1) }
2345
2450
  }
2346
2451
 
2347
- const translated = this.translatePHP(value)
2348
2452
  this.advance()
2349
- return { type: 'Identifier', name: translated || value }
2453
+ return { type: 'Identifier', name: value }
2350
2454
  }
2351
2455
 
2352
2456
  if (token.type === TokenType.VARIABLE) {