ether-code 0.9.0 → 0.9.1

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.
@@ -226,12 +226,6 @@ class EtherParserPHP extends EtherParserBase {
226
226
  this.phpModifiers = ['statique', 'static', 'final', 'finale', 'abstrait', 'abstraite', 'abstract', 'readonly', 'lecture seule']
227
227
  }
228
228
 
229
- safeStr(val) {
230
- if (typeof val === 'string') return val
231
- if (val && typeof val === 'object') return String(val.name || val.value || '')
232
- return String(val || '')
233
- }
234
-
235
229
  translatePHP(word) {
236
230
  if (!word) return word
237
231
  const lower = this.normalizeAccents(String(word))
@@ -278,10 +272,6 @@ class EtherParserPHP extends EtherParserBase {
278
272
 
279
273
  const value = token.value != null ? this.normalizeAccents(String(token.value).toLowerCase()) : ''
280
274
 
281
- if (this.isDeclare(value)) {
282
- return this.parseDeclare(lang)
283
- }
284
-
285
275
  if (this.isNamespace(value)) {
286
276
  return this.parseNamespace(lang)
287
277
  }
@@ -290,6 +280,10 @@ class EtherParserPHP extends EtherParserBase {
290
280
  return this.parseUseStatement(lang)
291
281
  }
292
282
 
283
+ if (this.isDeclare(value)) {
284
+ return this.parseDeclareStatement(lang)
285
+ }
286
+
293
287
  if (this.isClassDeclaration(value)) {
294
288
  return this.parseClassDeclaration(lang)
295
289
  }
@@ -326,16 +320,6 @@ class EtherParserPHP extends EtherParserBase {
326
320
  return this.parseSwitch(lang)
327
321
  }
328
322
 
329
- if (token.type === TokenType.IDENTIFIER) {
330
- const multiWordCall = this.tryParseMultiWordCallFromStart(lang)
331
- if (multiWordCall) {
332
- return {
333
- type: 'ExpressionStatement',
334
- expression: multiWordCall
335
- }
336
- }
337
- }
338
-
339
323
  if (this.isTry(value)) {
340
324
  return this.parseTryStatement(lang)
341
325
  }
@@ -385,23 +369,17 @@ class EtherParserPHP extends EtherParserBase {
385
369
  }
386
370
 
387
371
  isNamespace(value) {
388
- if (value === 'namespace' || value === 'espace de noms') return true
389
- if (value === 'espace') {
390
- const next1 = this.peek(1)
391
- const next2 = this.peek(2)
392
- if (next1 && next2) {
393
- const val1 = this.normalizeAccents(String(next1.value).toLowerCase())
394
- const val2 = this.normalizeAccents(String(next2.value).toLowerCase())
395
- if (val1 === 'de' && val2 === 'noms') return true
396
- }
397
- }
398
- return false
372
+ return ['namespace', 'espace de noms'].includes(value)
399
373
  }
400
374
 
401
375
  isUse(value) {
402
376
  return ['utiliser', 'use'].includes(value)
403
377
  }
404
378
 
379
+ isDeclare(value) {
380
+ return ['declarer', 'declare'].includes(value)
381
+ }
382
+
405
383
  isClassDeclaration(value) {
406
384
  const keywords = ['classe', 'class', 'abstrait', 'abstraite', 'abstract', 'final', 'finale', 'readonly']
407
385
  if (keywords.includes(value)) {
@@ -493,54 +471,8 @@ class EtherParserPHP extends EtherParserBase {
493
471
  return value === 'match'
494
472
  }
495
473
 
496
- isDeclare(value) {
497
- return ['declare', 'declarer', 'déclarer'].includes(value)
498
- }
499
-
500
- parseDeclare(lang) {
501
- this.advance()
502
-
503
- const directives = []
504
-
505
- if (this.match(TokenType.LPAREN)) {
506
- do {
507
- const nameToken = this.current()
508
- const name = nameToken ? this.safeStr(nameToken.value) : ''
509
- this.advance()
510
-
511
- this.match(TokenType.EQUALS)
512
-
513
- const value = this.parseExpression(lang)
514
- directives.push({ name, value })
515
- } while (this.match(TokenType.COMMA))
516
-
517
- this.match(TokenType.RPAREN)
518
- } else {
519
- const nameToken = this.current()
520
- const name = nameToken ? this.safeStr(nameToken.value) : ''
521
- this.advance()
522
-
523
- this.match(TokenType.EQUALS)
524
-
525
- const value = this.parseExpression(lang)
526
- directives.push({ name, value })
527
- }
528
-
529
- return {
530
- type: 'DeclareStatement',
531
- directives: directives
532
- }
533
- }
534
-
535
474
  parseNamespace(lang) {
536
- const first = this.advance()
537
- const firstVal = this.normalizeAccents(String(first.value).toLowerCase())
538
-
539
- if (firstVal === 'espace' && firstVal !== 'espace de noms') {
540
- this.advance()
541
- this.advance()
542
- }
543
-
475
+ this.advance()
544
476
  const name = this.parseNamespacePath(lang)
545
477
  this.skipNewlines()
546
478
 
@@ -608,6 +540,42 @@ class EtherParserPHP extends EtherParserBase {
608
540
  }
609
541
  }
610
542
 
543
+ parseDeclareStatement(lang) {
544
+ this.advance()
545
+
546
+ const directives = []
547
+
548
+ do {
549
+ const nameToken = this.current()
550
+ if (!nameToken || nameToken.type !== TokenType.IDENTIFIER) break
551
+
552
+ let name = String(nameToken.value)
553
+ this.advance()
554
+
555
+ if (name === 'strict' && this.current()) {
556
+ const next = this.normalizeAccents(String(this.current().value).toLowerCase())
557
+ if (next === 'types' || next === '_types') {
558
+ name = 'strict_types'
559
+ this.advance()
560
+ }
561
+ }
562
+
563
+ this.match(TokenType.EQUALS)
564
+
565
+ const value = this.parseExpression(lang)
566
+
567
+ directives.push({
568
+ name: name,
569
+ value: value
570
+ })
571
+ } while (this.match(TokenType.COMMA))
572
+
573
+ return {
574
+ type: 'DeclareStatement',
575
+ directives: directives
576
+ }
577
+ }
578
+
611
579
  parseClassDeclaration(lang) {
612
580
  const modifiers = []
613
581
 
@@ -743,6 +711,10 @@ class EtherParserPHP extends EtherParserBase {
743
711
  return this.parseTraitUse(lang)
744
712
  }
745
713
 
714
+ if (val === 'variable' || val === 'var') {
715
+ this.advance()
716
+ }
717
+
746
718
  return this.parsePropertyDeclaration(lang, visibility, isStatic, isReadonly)
747
719
  }
748
720
 
@@ -803,11 +775,6 @@ class EtherParserPHP extends EtherParserBase {
803
775
  }
804
776
 
805
777
  parsePropertyDeclaration(lang, visibility, isStatic, isReadonly) {
806
- const val = this.current() ? this.normalizeAccents(String(this.current().value).toLowerCase()) : ''
807
- if (val === 'variable' || val === 'var') {
808
- this.advance()
809
- }
810
-
811
778
  let typeHint = null
812
779
 
813
780
  const current = this.current()
@@ -819,10 +786,12 @@ class EtherParserPHP extends EtherParserBase {
819
786
  }
820
787
 
821
788
  const nameToken = this.current()
822
- let name = this.safeStr(nameToken ? nameToken.value : 'property')
789
+ let name = nameToken ? nameToken.value : 'property'
823
790
 
824
- if (name.startsWith('$')) {
791
+ if (typeof name === 'string' && name.startsWith('$')) {
825
792
  name = name.substring(1)
793
+ } else if (typeof name !== 'string') {
794
+ name = String(name)
826
795
  }
827
796
 
828
797
  if (nameToken) this.advance()
@@ -1091,6 +1060,7 @@ class EtherParserPHP extends EtherParserBase {
1091
1060
  }
1092
1061
  }
1093
1062
 
1063
+ this.match(TokenType.COLON)
1094
1064
  this.skipNewlines()
1095
1065
 
1096
1066
  let body
@@ -1109,8 +1079,6 @@ class EtherParserPHP extends EtherParserBase {
1109
1079
  }
1110
1080
  }
1111
1081
 
1112
- this.match(TokenType.COLON)
1113
- this.skipNewlines()
1114
1082
  body = this.parseBlock(lang)
1115
1083
 
1116
1084
  return {
@@ -1135,7 +1103,7 @@ class EtherParserPHP extends EtherParserBase {
1135
1103
  }
1136
1104
 
1137
1105
  const varToken = this.current()
1138
- let varName = this.safeStr(varToken ? varToken.value : '')
1106
+ let varName = varToken ? String(varToken.value) : ''
1139
1107
  if (varName.startsWith('$')) {
1140
1108
  varName = varName.substring(1)
1141
1109
  }
@@ -1180,7 +1148,6 @@ class EtherParserPHP extends EtherParserBase {
1180
1148
  let variadic = false
1181
1149
  let byReference = false
1182
1150
  let defaultValue = null
1183
- let name = null
1184
1151
 
1185
1152
  const val = this.current() ? this.normalizeAccents(String(this.current().value).toLowerCase()) : ''
1186
1153
  if (this.phpVisibility.includes(val)) {
@@ -1194,6 +1161,19 @@ class EtherParserPHP extends EtherParserBase {
1194
1161
  this.advance()
1195
1162
  }
1196
1163
 
1164
+ const currentType = this.current()
1165
+ if (currentType && currentType.type === TokenType.IDENTIFIER) {
1166
+ const typeLower = this.normalizeAccents(String(currentType.value).toLowerCase())
1167
+ const nextToken = this.peek(1)
1168
+
1169
+ if (nextToken && nextToken.type === TokenType.COLON) {
1170
+ } else if (this.phpTypes[typeLower] || (typeLower[0] && typeLower[0] === typeLower[0].toUpperCase())) {
1171
+ if (!nextToken || nextToken.type !== TokenType.EQUALS) {
1172
+ typeHint = this.parseTypeHint(lang)
1173
+ }
1174
+ }
1175
+ }
1176
+
1197
1177
  if (this.match(TokenType.SPREAD) || this.matchValue('...')) {
1198
1178
  variadic = true
1199
1179
  }
@@ -1202,33 +1182,17 @@ class EtherParserPHP extends EtherParserBase {
1202
1182
  byReference = true
1203
1183
  }
1204
1184
 
1205
- const currentToken = this.current()
1206
- if (!currentToken) return null
1207
-
1208
- const nextToken = this.peek(1)
1185
+ const nameToken = this.current()
1186
+ if (!nameToken) return null
1209
1187
 
1210
- if (nextToken && nextToken.type === TokenType.COLON) {
1211
- name = this.safeStr(currentToken.value)
1212
- if (name.startsWith('$')) name = name.substring(1)
1213
- this.advance()
1214
- this.advance()
1215
- typeHint = this.parseTypeHint(lang)
1216
- } else {
1217
- const typeLower = this.normalizeAccents(String(currentToken.value).toLowerCase())
1218
- if (this.phpTypes[typeLower] || /^[A-Z]/.test(currentToken.value)) {
1219
- typeHint = this.parseTypeHint(lang)
1220
- }
1221
-
1222
- const nameToken = this.current()
1223
- if (nameToken && nameToken.type === TokenType.IDENTIFIER) {
1224
- name = this.safeStr(nameToken.value)
1225
- if (name.startsWith('$')) name = name.substring(1)
1226
- this.advance()
1227
- }
1188
+ let name = String(nameToken.value)
1189
+ if (name.startsWith('$')) {
1190
+ name = name.substring(1)
1228
1191
  }
1192
+ this.advance()
1229
1193
 
1230
- if (!name) {
1231
- name = 'param'
1194
+ if (this.match(TokenType.COLON)) {
1195
+ typeHint = this.parseTypeHint(lang)
1232
1196
  }
1233
1197
 
1234
1198
  if (this.match(TokenType.EQUALS)) {
@@ -1317,7 +1281,7 @@ class EtherParserPHP extends EtherParserBase {
1317
1281
  }
1318
1282
 
1319
1283
  const nameToken = this.current()
1320
- let name = this.safeStr(nameToken ? nameToken.value : 'variable')
1284
+ let name = nameToken ? String(nameToken.value) : 'variable'
1321
1285
  if (name.startsWith('$')) {
1322
1286
  name = name.substring(1)
1323
1287
  }
@@ -1357,37 +1321,59 @@ class EtherParserPHP extends EtherParserBase {
1357
1321
 
1358
1322
  const elseVal = this.current() ? this.normalizeAccents(String(this.current().value).toLowerCase()) : ''
1359
1323
  if (elseVal === 'sinon si' || elseVal === 'elseif') {
1360
- alternate = this.parseConditional(lang)
1361
- alternate.type = 'IfStatement'
1362
- } else if (elseVal === 'sinon') {
1324
+ this.advance()
1325
+
1326
+ let elseCondition
1327
+ if (this.match(TokenType.LPAREN)) {
1328
+ elseCondition = this.parseExpression(lang)
1329
+ this.match(TokenType.RPAREN)
1330
+ } else {
1331
+ elseCondition = this.parseConditionUntilColon(lang)
1332
+ }
1333
+
1334
+ this.match(TokenType.COLON)
1335
+ this.skipNewlines()
1336
+ const elseConsequent = this.parseBlock(lang)
1337
+
1338
+ alternate = {
1339
+ type: 'IfStatement',
1340
+ condition: elseCondition,
1341
+ consequent: elseConsequent,
1342
+ alternate: null
1343
+ }
1344
+
1345
+ this.skipNewlines()
1346
+ const nextElse = this.current() ? this.normalizeAccents(String(this.current().value).toLowerCase()) : ''
1347
+ if (nextElse === 'sinon' || nextElse === 'else') {
1348
+ this.advance()
1349
+ this.match(TokenType.COLON)
1350
+ this.skipNewlines()
1351
+ alternate.alternate = this.parseBlock(lang)
1352
+ }
1353
+ } else if (elseVal === 'sinon' || elseVal === 'else') {
1363
1354
  this.advance()
1364
1355
 
1365
1356
  const nextVal = this.current() ? this.normalizeAccents(String(this.current().value).toLowerCase()) : ''
1366
1357
  if (nextVal === 'si' || nextVal === 'if') {
1367
1358
  alternate = this.parseConditional(lang)
1368
- alternate.type = 'IfStatement'
1359
+ alternate.type = 'ElseIfStatement'
1369
1360
  } else {
1370
1361
  this.match(TokenType.COLON)
1371
1362
  this.skipNewlines()
1372
1363
  alternate = this.parseBlock(lang)
1373
1364
  }
1374
- } else if (elseVal === 'else') {
1375
- this.advance()
1376
- this.match(TokenType.COLON)
1377
- this.skipNewlines()
1378
- alternate = this.parseBlock(lang)
1379
1365
  }
1380
1366
 
1381
1367
  return {
1382
1368
  type: 'IfStatement',
1383
- test: condition,
1369
+ condition: condition,
1384
1370
  consequent: consequent,
1385
1371
  alternate: alternate
1386
1372
  }
1387
1373
  }
1388
1374
 
1389
1375
  parseConditionUntilColon(lang) {
1390
- const startPos = this.pos
1376
+ const tokens = []
1391
1377
  let depth = 0
1392
1378
 
1393
1379
  while (!this.isAtEnd()) {
@@ -1404,23 +1390,36 @@ class EtherParserPHP extends EtherParserBase {
1404
1390
  break
1405
1391
  }
1406
1392
 
1393
+ tokens.push(token)
1407
1394
  this.advance()
1408
1395
  }
1409
1396
 
1410
- const endPos = this.pos
1411
- this.pos = startPos
1412
-
1413
- if (endPos > startPos) {
1414
- return this.parseExpression(lang)
1397
+ if (tokens.length === 0) {
1398
+ return { type: 'Literal', value: true }
1415
1399
  }
1416
1400
 
1417
- return { type: 'Literal', value: true }
1401
+ const savedPos = this.pos
1402
+ const savedTokens = this.tokens
1403
+ this.tokens = tokens
1404
+ this.pos = 0
1405
+
1406
+ const expr = this.parseExpression(lang)
1407
+
1408
+ this.tokens = savedTokens
1409
+ this.pos = savedPos
1410
+
1411
+ return expr
1418
1412
  }
1419
1413
 
1420
1414
  parseLoop(lang) {
1421
1415
  const token = this.current()
1422
1416
  const value = this.normalizeAccents(String(token.value).toLowerCase())
1423
1417
 
1418
+ if (value === 'pour chaque' || value === 'foreach') {
1419
+ this.advance()
1420
+ return this.parseForeach(lang)
1421
+ }
1422
+
1424
1423
  if (value === 'pour') {
1425
1424
  const next = this.peek(1)
1426
1425
  if (next && this.normalizeAccents(String(next.value).toLowerCase()) === 'chaque') {
@@ -1431,16 +1430,14 @@ class EtherParserPHP extends EtherParserBase {
1431
1430
  return this.parseFor(lang)
1432
1431
  }
1433
1432
 
1434
- if (value === 'foreach' || value === 'pour chaque') {
1433
+ if (value === 'tant que' || value === 'while') {
1435
1434
  this.advance()
1436
- return this.parseForeach(lang)
1435
+ return this.parseWhile(lang)
1437
1436
  }
1438
1437
 
1439
- if (value === 'while' || value === 'tant') {
1438
+ if (value === 'tant') {
1440
1439
  this.advance()
1441
- if (value === 'tant') {
1442
- this.matchValue('que')
1443
- }
1440
+ this.matchValue('que')
1444
1441
  return this.parseWhile(lang)
1445
1442
  }
1446
1443
 
@@ -1454,7 +1451,7 @@ class EtherParserPHP extends EtherParserBase {
1454
1451
 
1455
1452
  parseFor(lang) {
1456
1453
  this.advance()
1457
-
1454
+
1458
1455
  let init = null
1459
1456
  let test = null
1460
1457
  let update = null
@@ -1475,19 +1472,13 @@ class EtherParserPHP extends EtherParserBase {
1475
1472
  }
1476
1473
  this.match(TokenType.RPAREN)
1477
1474
  } else {
1478
- if (!this.check(TokenType.SEMICOLON) && !this.check(TokenType.COLON)) {
1479
- init = this.parseExpression(lang)
1480
- }
1475
+ init = this.parseForInitUntilSemicolon(lang)
1481
1476
  this.match(TokenType.SEMICOLON)
1482
1477
 
1483
- if (!this.check(TokenType.SEMICOLON) && !this.check(TokenType.COLON)) {
1484
- test = this.parseExpression(lang)
1485
- }
1478
+ test = this.parseForConditionUntilSemicolon(lang)
1486
1479
  this.match(TokenType.SEMICOLON)
1487
1480
 
1488
- if (!this.check(TokenType.COLON) && !this.check(TokenType.NEWLINE)) {
1489
- update = this.parseExpression(lang)
1490
- }
1481
+ update = this.parseForUpdateUntilColon(lang)
1491
1482
  }
1492
1483
 
1493
1484
  this.match(TokenType.COLON)
@@ -1503,6 +1494,82 @@ class EtherParserPHP extends EtherParserBase {
1503
1494
  }
1504
1495
  }
1505
1496
 
1497
+ parseForInitUntilSemicolon(lang) {
1498
+ const tokens = []
1499
+ while (!this.isAtEnd()) {
1500
+ const token = this.current()
1501
+ if (!token) break
1502
+ if (token.type === TokenType.SEMICOLON) break
1503
+ tokens.push(token)
1504
+ this.advance()
1505
+ }
1506
+
1507
+ if (tokens.length === 0) return null
1508
+
1509
+ const savedPos = this.pos
1510
+ const savedTokens = this.tokens
1511
+ this.tokens = tokens
1512
+ this.pos = 0
1513
+
1514
+ const expr = this.parseExpression(lang)
1515
+
1516
+ this.tokens = savedTokens
1517
+ this.pos = savedPos
1518
+
1519
+ return expr
1520
+ }
1521
+
1522
+ parseForConditionUntilSemicolon(lang) {
1523
+ const tokens = []
1524
+ while (!this.isAtEnd()) {
1525
+ const token = this.current()
1526
+ if (!token) break
1527
+ if (token.type === TokenType.SEMICOLON) break
1528
+ tokens.push(token)
1529
+ this.advance()
1530
+ }
1531
+
1532
+ if (tokens.length === 0) return null
1533
+
1534
+ const savedPos = this.pos
1535
+ const savedTokens = this.tokens
1536
+ this.tokens = tokens
1537
+ this.pos = 0
1538
+
1539
+ const expr = this.parseExpression(lang)
1540
+
1541
+ this.tokens = savedTokens
1542
+ this.pos = savedPos
1543
+
1544
+ return expr
1545
+ }
1546
+
1547
+ parseForUpdateUntilColon(lang) {
1548
+ const tokens = []
1549
+ while (!this.isAtEnd()) {
1550
+ const token = this.current()
1551
+ if (!token) break
1552
+ if (token.type === TokenType.COLON) break
1553
+ if (token.type === TokenType.NEWLINE) break
1554
+ tokens.push(token)
1555
+ this.advance()
1556
+ }
1557
+
1558
+ if (tokens.length === 0) return null
1559
+
1560
+ const savedPos = this.pos
1561
+ const savedTokens = this.tokens
1562
+ this.tokens = tokens
1563
+ this.pos = 0
1564
+
1565
+ const expr = this.parseExpression(lang)
1566
+
1567
+ this.tokens = savedTokens
1568
+ this.pos = savedPos
1569
+
1570
+ return expr
1571
+ }
1572
+
1506
1573
  parseForeach(lang) {
1507
1574
  let array = null
1508
1575
  let key = null
@@ -1522,7 +1589,7 @@ class EtherParserPHP extends EtherParserBase {
1522
1589
  }
1523
1590
 
1524
1591
  const firstVar = this.current()
1525
- let firstName = this.safeStr(firstVar ? firstVar.value : 'item')
1592
+ let firstName = firstVar ? String(firstVar.value) : 'item'
1526
1593
  if (firstName.startsWith('$')) firstName = firstName.substring(1)
1527
1594
  this.advance()
1528
1595
 
@@ -1534,7 +1601,7 @@ class EtherParserPHP extends EtherParserBase {
1534
1601
  }
1535
1602
 
1536
1603
  const valueVar = this.current()
1537
- value = this.safeStr(valueVar ? valueVar.value : 'value')
1604
+ value = valueVar ? String(valueVar.value) : 'value'
1538
1605
  if (value.startsWith('$')) value = value.substring(1)
1539
1606
  this.advance()
1540
1607
  } else {
@@ -1543,12 +1610,28 @@ class EtherParserPHP extends EtherParserBase {
1543
1610
 
1544
1611
  this.match(TokenType.RPAREN)
1545
1612
  } else {
1546
- const arrayToken = this.current()
1547
- if (arrayToken) {
1548
- array = { type: 'Identifier', name: this.safeStr(arrayToken.value) }
1613
+ const tokens = []
1614
+ while (!this.isAtEnd()) {
1615
+ const token = this.current()
1616
+ if (!token) break
1617
+ const tokenVal = this.normalizeAccents(String(token.value).toLowerCase())
1618
+ if (tokenVal === 'comme' || tokenVal === 'as') break
1619
+ if (token.type === TokenType.COLON) break
1620
+ if (token.type === TokenType.NEWLINE) break
1621
+ tokens.push(token)
1549
1622
  this.advance()
1550
1623
  }
1551
1624
 
1625
+ if (tokens.length > 0) {
1626
+ const savedPos = this.pos
1627
+ const savedTokens = this.tokens
1628
+ this.tokens = tokens
1629
+ this.pos = 0
1630
+ array = this.parseExpression(lang)
1631
+ this.tokens = savedTokens
1632
+ this.pos = savedPos
1633
+ }
1634
+
1552
1635
  const asVal = this.current() ? this.normalizeAccents(String(this.current().value).toLowerCase()) : ''
1553
1636
  if (asVal === 'comme' || asVal === 'as') {
1554
1637
  this.advance()
@@ -1559,23 +1642,27 @@ class EtherParserPHP extends EtherParserBase {
1559
1642
  }
1560
1643
 
1561
1644
  const firstVar = this.current()
1562
- let firstName = this.safeStr(firstVar ? firstVar.value : 'item')
1563
- if (firstName.startsWith('$')) firstName = firstName.substring(1)
1564
- this.advance()
1645
+ if (firstVar && firstVar.type === TokenType.IDENTIFIER) {
1646
+ let firstName = String(firstVar.value)
1647
+ if (firstName.startsWith('$')) firstName = firstName.substring(1)
1648
+ this.advance()
1565
1649
 
1566
- if (this.match(TokenType.DOUBLE_ARROW) || this.matchValue('=>')) {
1567
- key = firstName
1568
-
1569
- if (this.match(TokenType.AMPERSAND)) {
1570
- byRef = true
1650
+ if (this.match(TokenType.DOUBLE_ARROW) || this.matchValue('=>')) {
1651
+ key = firstName
1652
+
1653
+ if (this.match(TokenType.AMPERSAND)) {
1654
+ byRef = true
1655
+ }
1656
+
1657
+ const valueVar = this.current()
1658
+ if (valueVar && valueVar.type === TokenType.IDENTIFIER) {
1659
+ value = String(valueVar.value)
1660
+ if (value.startsWith('$')) value = value.substring(1)
1661
+ this.advance()
1662
+ }
1663
+ } else {
1664
+ value = firstName
1571
1665
  }
1572
-
1573
- const valueVar = this.current()
1574
- value = this.safeStr(valueVar ? valueVar.value : 'value')
1575
- if (value.startsWith('$')) value = value.substring(1)
1576
- this.advance()
1577
- } else {
1578
- value = firstName
1579
1666
  }
1580
1667
  }
1581
1668
 
@@ -1608,7 +1695,7 @@ class EtherParserPHP extends EtherParserBase {
1608
1695
 
1609
1696
  return {
1610
1697
  type: 'WhileStatement',
1611
- test: condition,
1698
+ condition: condition,
1612
1699
  body: body
1613
1700
  }
1614
1701
  }
@@ -1686,25 +1773,29 @@ class EtherParserPHP extends EtherParserBase {
1686
1773
 
1687
1774
  if (val === 'cas' || val === 'case') {
1688
1775
  this.advance()
1689
- const test = this.parseExpression(lang)
1776
+ const test = this.parseConditionUntilColon(lang)
1690
1777
  this.match(TokenType.COLON)
1691
1778
  this.skipNewlines()
1692
- this.match(TokenType.INDENT)
1693
1779
 
1780
+ const hasInnerIndent = this.match(TokenType.INDENT)
1694
1781
  const consequent = []
1782
+
1695
1783
  while (!this.isAtEnd()) {
1696
1784
  this.skipNewlines()
1697
- const cur = this.current()
1698
- if (!cur) break
1785
+ const nextToken = this.current()
1786
+ if (!nextToken) break
1699
1787
 
1700
- const nextVal = this.normalizeAccents(String(cur.value).toLowerCase())
1701
- if (['cas', 'case', 'defaut', 'default'].includes(nextVal)) {
1788
+ const nextVal = this.normalizeAccents(String(nextToken.value).toLowerCase())
1789
+ if (['cas', 'case', 'defaut', 'default', 'défaut'].includes(nextVal)) {
1702
1790
  break
1703
1791
  }
1704
- if (cur.type === TokenType.RBRACE || cur.type === TokenType.DEDENT) {
1705
- this.advance()
1792
+ if (nextToken.type === TokenType.RBRACE || nextToken.type === TokenType.DEDENT) {
1793
+ if (hasInnerIndent) {
1794
+ this.advance()
1795
+ }
1706
1796
  break
1707
1797
  }
1798
+
1708
1799
  const stmt = this.parseStatement(lang)
1709
1800
  if (stmt) consequent.push(stmt)
1710
1801
  this.skipNewlines()
@@ -1715,21 +1806,26 @@ class EtherParserPHP extends EtherParserBase {
1715
1806
  test: test,
1716
1807
  consequent: consequent
1717
1808
  })
1718
- } else if (val === 'defaut' || val === 'default') {
1809
+ } else if (val === 'defaut' || val === 'default' || val === 'défaut') {
1719
1810
  this.advance()
1720
1811
  this.match(TokenType.COLON)
1721
1812
  this.skipNewlines()
1722
- this.match(TokenType.INDENT)
1723
1813
 
1814
+ const hasInnerIndent = this.match(TokenType.INDENT)
1724
1815
  const consequent = []
1816
+
1725
1817
  while (!this.isAtEnd()) {
1726
1818
  this.skipNewlines()
1727
- const cur = this.current()
1728
- if (!cur) break
1729
- if (cur.type === TokenType.RBRACE || cur.type === TokenType.DEDENT) {
1730
- this.advance()
1819
+ const nextToken = this.current()
1820
+ if (!nextToken) break
1821
+
1822
+ if (nextToken.type === TokenType.RBRACE || nextToken.type === TokenType.DEDENT) {
1823
+ if (hasInnerIndent) {
1824
+ this.advance()
1825
+ }
1731
1826
  break
1732
1827
  }
1828
+
1733
1829
  const stmt = this.parseStatement(lang)
1734
1830
  if (stmt) consequent.push(stmt)
1735
1831
  this.skipNewlines()
@@ -1833,7 +1929,7 @@ class EtherParserPHP extends EtherParserBase {
1833
1929
  do {
1834
1930
  const typeToken = this.current()
1835
1931
  if (typeToken && typeToken.type === TokenType.IDENTIFIER) {
1836
- const typeName = this.safeStr(typeToken.value)
1932
+ const typeName = String(typeToken.value)
1837
1933
  if (typeName.startsWith('$')) {
1838
1934
  param = typeName.substring(1)
1839
1935
  } else {
@@ -1846,7 +1942,7 @@ class EtherParserPHP extends EtherParserBase {
1846
1942
  if (!param) {
1847
1943
  const paramToken = this.current()
1848
1944
  if (paramToken && paramToken.type === TokenType.IDENTIFIER) {
1849
- param = this.safeStr(paramToken.value)
1945
+ param = String(paramToken.value)
1850
1946
  if (param.startsWith('$')) param = param.substring(1)
1851
1947
  this.advance()
1852
1948
  }
@@ -1855,7 +1951,7 @@ class EtherParserPHP extends EtherParserBase {
1855
1951
  } else {
1856
1952
  const typeToken = this.current()
1857
1953
  if (typeToken && typeToken.type === TokenType.IDENTIFIER) {
1858
- types.push(this.safeStr(typeToken.value))
1954
+ types.push(String(typeToken.value))
1859
1955
  this.advance()
1860
1956
  }
1861
1957
 
@@ -1864,7 +1960,7 @@ class EtherParserPHP extends EtherParserBase {
1864
1960
  this.advance()
1865
1961
  const paramToken = this.current()
1866
1962
  if (paramToken && paramToken.type === TokenType.IDENTIFIER) {
1867
- param = this.safeStr(paramToken.value)
1963
+ param = String(paramToken.value)
1868
1964
  if (param.startsWith('$')) param = param.substring(1)
1869
1965
  this.advance()
1870
1966
  }
@@ -1979,7 +2075,7 @@ class EtherParserPHP extends EtherParserBase {
1979
2075
  const variables = []
1980
2076
  do {
1981
2077
  const varToken = this.current()
1982
- let varName = this.safeStr(varToken ? varToken.value : '')
2078
+ let varName = varToken ? String(varToken.value) : ''
1983
2079
  if (varName.startsWith('$')) varName = varName.substring(1)
1984
2080
  this.advance()
1985
2081
  variables.push(varName)
@@ -2196,12 +2292,12 @@ class EtherParserPHP extends EtherParserBase {
2196
2292
  return { type: 'UnaryExpression', operator: '!', operand }
2197
2293
  }
2198
2294
 
2199
- if (this.match(TokenType.MINUS) && !this.check(TokenType.MINUS)) {
2295
+ if (this.match(TokenType.MINUS)) {
2200
2296
  const operand = this.parseUnary(lang)
2201
2297
  return { type: 'UnaryExpression', operator: '-', operand }
2202
2298
  }
2203
2299
 
2204
- if (this.match(TokenType.PLUS) && !this.check(TokenType.PLUS)) {
2300
+ if (this.match(TokenType.PLUS)) {
2205
2301
  const operand = this.parseUnary(lang)
2206
2302
  return { type: 'UnaryExpression', operator: '+', operand }
2207
2303
  }
@@ -2216,15 +2312,12 @@ class EtherParserPHP extends EtherParserBase {
2216
2312
  return { type: 'UnaryExpression', operator: '~', operand }
2217
2313
  }
2218
2314
 
2219
- const current = this.current()
2220
- if (current && current.type === TokenType.PLUS && current.value === '++') {
2221
- this.advance()
2315
+ if (this.match(TokenType.DOUBLE_PLUS)) {
2222
2316
  const operand = this.parseUnary(lang)
2223
2317
  return { type: 'UpdateExpression', operator: '++', prefix: true, operand }
2224
2318
  }
2225
2319
 
2226
- if (current && current.type === TokenType.MINUS && current.value === '--') {
2227
- this.advance()
2320
+ if (this.match(TokenType.DOUBLE_MINUS)) {
2228
2321
  const operand = this.parseUnary(lang)
2229
2322
  return { type: 'UpdateExpression', operator: '--', prefix: true, operand }
2230
2323
  }
@@ -2333,123 +2426,14 @@ class EtherParserPHP extends EtherParserBase {
2333
2426
  }
2334
2427
  }
2335
2428
 
2336
- tryParseMultiWordCall(firstName, lang) {
2337
- const words = [firstName]
2338
- let lookAhead = 0
2339
-
2340
- while (true) {
2341
- const nextToken = this.peek(lookAhead)
2342
- if (!nextToken) break
2343
-
2344
- if (nextToken.type === TokenType.LPAREN) {
2345
- if (words.length > 1) {
2346
- for (let i = 0; i < lookAhead; i++) {
2347
- this.advance()
2348
- }
2349
- this.advance()
2350
-
2351
- const args = []
2352
- while (!this.isAtEnd() && !this.check(TokenType.RPAREN)) {
2353
- this.skipNewlines()
2354
- if (this.check(TokenType.RPAREN)) break
2355
-
2356
- if (this.match(TokenType.SPREAD) || this.matchValue('...')) {
2357
- const arg = this.parseExpression(lang)
2358
- args.push({ type: 'SpreadElement', argument: arg })
2359
- } else {
2360
- const arg = this.parseExpression(lang)
2361
- args.push(arg)
2362
- }
2363
- this.match(TokenType.COMMA)
2364
- this.skipNewlines()
2365
- }
2366
- this.match(TokenType.RPAREN)
2367
-
2368
- const funcName = words.join(' ')
2369
- return {
2370
- type: 'CallExpression',
2371
- callee: { type: 'Identifier', name: funcName },
2372
- arguments: args
2373
- }
2374
- }
2375
- break
2376
- }
2377
-
2378
- if (nextToken.type === TokenType.IDENTIFIER) {
2379
- words.push(this.safeStr(nextToken.value))
2380
- lookAhead++
2381
- } else {
2382
- break
2383
- }
2384
- }
2385
-
2386
- return null
2387
- }
2388
-
2389
- tryParseMultiWordCallFromStart(lang) {
2390
- const words = []
2391
- let lookAhead = 0
2392
-
2393
- while (true) {
2394
- const token = this.peek(lookAhead)
2395
- if (!token) break
2396
-
2397
- if (token.type === TokenType.LPAREN) {
2398
- if (words.length >= 2) {
2399
- for (let i = 0; i < lookAhead; i++) {
2400
- this.advance()
2401
- }
2402
- this.advance()
2403
-
2404
- const args = []
2405
- while (!this.isAtEnd() && !this.check(TokenType.RPAREN)) {
2406
- this.skipNewlines()
2407
- if (this.check(TokenType.RPAREN)) break
2408
-
2409
- if (this.match(TokenType.SPREAD) || this.matchValue('...')) {
2410
- const arg = this.parseExpression(lang)
2411
- args.push({ type: 'SpreadElement', argument: arg })
2412
- } else {
2413
- const arg = this.parseExpression(lang)
2414
- args.push(arg)
2415
- }
2416
- this.match(TokenType.COMMA)
2417
- this.skipNewlines()
2418
- }
2419
- this.match(TokenType.RPAREN)
2420
-
2421
- const funcName = words.join(' ')
2422
- return {
2423
- type: 'CallExpression',
2424
- callee: { type: 'Identifier', name: funcName },
2425
- arguments: args
2426
- }
2427
- }
2428
- break
2429
- }
2430
-
2431
- if (token.type === TokenType.IDENTIFIER) {
2432
- words.push(this.safeStr(token.value))
2433
- lookAhead++
2434
- } else {
2435
- break
2436
- }
2437
- }
2438
-
2439
- return null
2440
- }
2441
-
2442
2429
  parsePostfix(lang) {
2443
2430
  let expr = this.parseCall(lang)
2444
2431
 
2445
- const current = this.current()
2446
- if (current && current.type === TokenType.PLUS && current.value === '++') {
2447
- this.advance()
2432
+ if (this.match(TokenType.DOUBLE_PLUS) || this.matchValue('++')) {
2448
2433
  return { type: 'UpdateExpression', operator: '++', prefix: false, operand: expr }
2449
2434
  }
2450
2435
 
2451
- if (current && current.type === TokenType.MINUS && current.value === '--') {
2452
- this.advance()
2436
+ if (this.match(TokenType.DOUBLE_MINUS) || this.matchValue('--')) {
2453
2437
  return { type: 'UpdateExpression', operator: '--', prefix: false, operand: expr }
2454
2438
  }
2455
2439
 
@@ -2459,13 +2443,6 @@ class EtherParserPHP extends EtherParserBase {
2459
2443
  parseCall(lang) {
2460
2444
  let expr = this.parsePrimary(lang)
2461
2445
 
2462
- if (expr && expr.type === 'Identifier') {
2463
- const multiWordResult = this.tryParseMultiWordCall(expr.name, lang)
2464
- if (multiWordResult) {
2465
- return multiWordResult
2466
- }
2467
- }
2468
-
2469
2446
  while (true) {
2470
2447
  if (this.match(TokenType.LPAREN)) {
2471
2448
  const args = []
@@ -2548,11 +2525,11 @@ class EtherParserPHP extends EtherParserBase {
2548
2525
 
2549
2526
  if (token.type === TokenType.STRING) {
2550
2527
  this.advance()
2551
- let value = this.safeStr(token.value)
2528
+ let value = token.value
2552
2529
  if ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'"))) {
2553
2530
  value = value.slice(1, -1)
2554
2531
  }
2555
- const isDouble = value.startsWith('"') || this.safeStr(token.value).startsWith('"')
2532
+ const isDouble = token.value.startsWith('"')
2556
2533
  return { type: 'StringLiteral', value, doubleQuoted: isDouble }
2557
2534
  }
2558
2535
 
@@ -2562,7 +2539,7 @@ class EtherParserPHP extends EtherParserBase {
2562
2539
  }
2563
2540
 
2564
2541
  if (token.type === TokenType.IDENTIFIER) {
2565
- const value = this.safeStr(token.value)
2542
+ const value = token.value
2566
2543
  const valueLower = this.normalizeAccents(value.toLowerCase())
2567
2544
 
2568
2545
  if (valueLower === 'vrai' || valueLower === 'true') {
@@ -2612,7 +2589,7 @@ class EtherParserPHP extends EtherParserBase {
2612
2589
  return this.parseMatchExpression(lang)
2613
2590
  }
2614
2591
 
2615
- if (value.startsWith('$')) {
2592
+ if (typeof value === 'string' && value.startsWith('$')) {
2616
2593
  this.advance()
2617
2594
  return { type: 'Variable', name: value.substring(1) }
2618
2595
  }
@@ -2623,7 +2600,7 @@ class EtherParserPHP extends EtherParserBase {
2623
2600
 
2624
2601
  if (token.type === TokenType.VARIABLE) {
2625
2602
  this.advance()
2626
- let name = this.safeStr(token.value)
2603
+ let name = String(token.value)
2627
2604
  if (name.startsWith('$')) name = name.substring(1)
2628
2605
  return { type: 'Variable', name }
2629
2606
  }