porffor 0.35.0 → 0.35.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/compiler/builtins/__internal_string.ts +74 -0
- package/compiler/builtins/_internal_string.ts +21 -0
- package/compiler/builtins/annexb_string.js +18 -119
- package/compiler/builtins/string.ts +28 -18
- package/compiler/builtins.js +5 -5
- package/compiler/builtins_precompiled.js +366 -369
- package/compiler/codegen.js +87 -185
- package/package.json +1 -1
- package/runner/index.js +1 -1
- package/runner/repl.js +4 -1
package/compiler/codegen.js
CHANGED
@@ -183,6 +183,9 @@ const generate = (scope, decl, global = false, name = undefined, valueUnused = f
|
|
183
183
|
case 'AwaitExpression':
|
184
184
|
return cacheAst(decl, generateAwait(scope, decl));
|
185
185
|
|
186
|
+
case 'TemplateLiteral':
|
187
|
+
return cacheAst(decl, generateTemplate(scope, decl));
|
188
|
+
|
186
189
|
case 'ExportNamedDeclaration':
|
187
190
|
if (!decl.declaration) return todo(scope, 'unsupported export declaration');
|
188
191
|
|
@@ -499,126 +502,19 @@ const performLogicOp = (scope, op, left, right, leftType, rightType) => {
|
|
499
502
|
];
|
500
503
|
};
|
501
504
|
|
502
|
-
const concatStrings = (scope, left, right, leftType, rightType
|
503
|
-
// todo: this should be rewritten into a built-in/func: String.prototype.concat
|
504
|
-
// todo: convert left and right to strings if not
|
505
|
-
// todo: optimize by looking up names in arrays and using that if exists?
|
506
|
-
// todo: optimize this if using literals/known lengths?
|
507
|
-
|
508
|
-
const rightPointer = localTmp(scope, 'concat_right_pointer', Valtype.i32);
|
509
|
-
const rightLength = localTmp(scope, 'concat_right_length', Valtype.i32);
|
510
|
-
const leftLength = localTmp(scope, 'concat_left_length', Valtype.i32);
|
511
|
-
|
512
|
-
const leftPointer = localTmp(scope, 'concat_left_pointer', Valtype.i32);
|
513
|
-
|
514
|
-
// alloc/assign array
|
515
|
-
const out = localTmp(scope, 'concat_out_pointer', Valtype.i32);
|
516
|
-
|
517
|
-
if (!skipTypeCheck && !allBytestrings) includeBuiltin(scope, '__Porffor_bytestringToString');
|
505
|
+
const concatStrings = (scope, left, right, leftType, rightType) => {
|
518
506
|
return [
|
519
|
-
|
520
|
-
...(
|
521
|
-
|
522
|
-
[ Opcodes.local_set, rightPointer ],
|
523
|
-
|
524
|
-
Opcodes.i32_to_u,
|
525
|
-
[ Opcodes.local_set, leftPointer ],
|
526
|
-
] : [
|
527
|
-
...left,
|
528
|
-
Opcodes.i32_to_u,
|
529
|
-
[ Opcodes.local_set, leftPointer ],
|
530
|
-
|
531
|
-
...right,
|
532
|
-
Opcodes.i32_to_u,
|
533
|
-
[ Opcodes.local_set, rightPointer ],
|
534
|
-
]),
|
535
|
-
|
536
|
-
// setup out
|
537
|
-
[ Opcodes.i32_const, 1 ],
|
538
|
-
[ Opcodes.memory_grow, 0 ],
|
539
|
-
[ Opcodes.i32_const, ...signedLEB128(65536) ],
|
540
|
-
[ Opcodes.i32_mul ],
|
541
|
-
[ Opcodes.local_tee, out ],
|
542
|
-
|
543
|
-
// calculate length
|
544
|
-
[ Opcodes.local_get, leftPointer ],
|
545
|
-
[ Opcodes.i32_load, 0, 0 ],
|
546
|
-
[ Opcodes.local_tee, leftLength ],
|
547
|
-
|
548
|
-
[ Opcodes.local_get, rightPointer ],
|
549
|
-
[ Opcodes.i32_load, 0, 0 ],
|
550
|
-
[ Opcodes.local_tee, rightLength ],
|
551
|
-
|
552
|
-
[ Opcodes.i32_add ],
|
553
|
-
|
554
|
-
// store length
|
555
|
-
[ Opcodes.i32_store, Math.log2(ValtypeSize.i32) - 1, 0 ],
|
556
|
-
|
557
|
-
...(skipTypeCheck || allBytestrings ? [] : [
|
558
|
-
...leftType,
|
559
|
-
...number(TYPES.bytestring, Valtype.i32),
|
560
|
-
[ Opcodes.i32_eq ],
|
561
|
-
[ Opcodes.if, Blocktype.void ],
|
562
|
-
[ Opcodes.local_get, leftPointer ],
|
563
|
-
[ Opcodes.local_get, leftLength ],
|
564
|
-
[ Opcodes.call, funcIndex.__Porffor_bytestringToString ],
|
565
|
-
[ Opcodes.local_set, leftPointer ],
|
566
|
-
[ Opcodes.end ],
|
567
|
-
|
568
|
-
...rightType,
|
569
|
-
...number(TYPES.bytestring, Valtype.i32),
|
570
|
-
[ Opcodes.i32_eq ],
|
571
|
-
[ Opcodes.if, Blocktype.void ],
|
572
|
-
[ Opcodes.local_get, rightPointer ],
|
573
|
-
[ Opcodes.local_get, rightLength ],
|
574
|
-
[ Opcodes.call, funcIndex.__Porffor_bytestringToString ],
|
575
|
-
[ Opcodes.local_set, rightPointer ],
|
576
|
-
[ Opcodes.end ]
|
577
|
-
]),
|
578
|
-
|
579
|
-
// copy left
|
580
|
-
// dst = out pointer + length size
|
581
|
-
[ Opcodes.local_get, out ],
|
582
|
-
...number(ValtypeSize.i32, Valtype.i32),
|
583
|
-
[ Opcodes.i32_add ],
|
584
|
-
|
585
|
-
// src = left pointer + length size
|
586
|
-
[ Opcodes.local_get, leftPointer ],
|
587
|
-
...number(ValtypeSize.i32, Valtype.i32),
|
588
|
-
[ Opcodes.i32_add ],
|
589
|
-
|
590
|
-
// size = PageSize - length size. we do not need to calculate length as init value
|
591
|
-
...number(pageSize - ValtypeSize.i32, Valtype.i32),
|
592
|
-
[ ...Opcodes.memory_copy, 0x00, 0x00 ],
|
593
|
-
|
594
|
-
// copy right
|
595
|
-
// dst = out pointer + length size + left length * sizeof valtype
|
596
|
-
[ Opcodes.local_get, out ],
|
597
|
-
...number(ValtypeSize.i32, Valtype.i32),
|
598
|
-
[ Opcodes.i32_add ],
|
599
|
-
|
600
|
-
[ Opcodes.local_get, leftLength ],
|
601
|
-
...number(allBytestrings ? ValtypeSize.i8 : ValtypeSize.i16, Valtype.i32),
|
602
|
-
[ Opcodes.i32_mul ],
|
603
|
-
[ Opcodes.i32_add ],
|
604
|
-
|
605
|
-
// src = right pointer + length size
|
606
|
-
[ Opcodes.local_get, rightPointer ],
|
607
|
-
...number(ValtypeSize.i32, Valtype.i32),
|
608
|
-
[ Opcodes.i32_add ],
|
609
|
-
|
610
|
-
// size = right length * sizeof valtype
|
611
|
-
[ Opcodes.local_get, rightLength ],
|
612
|
-
...number(allBytestrings ? ValtypeSize.i8 : ValtypeSize.i16, Valtype.i32),
|
613
|
-
[ Opcodes.i32_mul ],
|
614
|
-
|
615
|
-
[ ...Opcodes.memory_copy, 0x00, 0x00 ],
|
507
|
+
...left,
|
508
|
+
...(valtypeBinary === Valtype.i32 ? [ [ Opcodes.f64_convert_i32_s ] ] : []),
|
509
|
+
...leftType,
|
616
510
|
|
617
|
-
...
|
511
|
+
...right,
|
512
|
+
...(valtypeBinary === Valtype.i32 ? [ [ Opcodes.f64_convert_i32_s ] ] : []),
|
513
|
+
...rightType,
|
618
514
|
|
619
|
-
|
620
|
-
|
621
|
-
Opcodes.
|
515
|
+
[ Opcodes.call, includeBuiltin(scope, '__Porffor_concatStrings').index ],
|
516
|
+
...setLastType(scope),
|
517
|
+
...(valtypeBinary === Valtype.i32 ? [ Opcodes.i32_trunc_sat_f64_u ] : []),
|
622
518
|
];
|
623
519
|
};
|
624
520
|
|
@@ -829,8 +725,8 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
|
|
829
725
|
return performLogicOp(scope, op, left, right, leftType, rightType);
|
830
726
|
}
|
831
727
|
|
832
|
-
const knownLeft =
|
833
|
-
const knownRight =
|
728
|
+
const knownLeft = knownTypeWithGuess(scope, leftType);
|
729
|
+
const knownRight = knownTypeWithGuess(scope, rightType);
|
834
730
|
|
835
731
|
const eqOp = ['==', '===', '!=', '!==', '>', '>=', '<', '<='].includes(op);
|
836
732
|
const strictOp = op === '===' || op === '!==';
|
@@ -860,23 +756,16 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
|
|
860
756
|
// todo: if equality op and an operand is undefined, return false
|
861
757
|
// todo: niche null hell with 0
|
862
758
|
|
863
|
-
if (knownLeft === TYPES.string || knownRight === TYPES.string)
|
759
|
+
if ((knownLeft === TYPES.string || knownRight === TYPES.string) ||
|
760
|
+
(knownLeft === TYPES.bytestring || knownRight === TYPES.bytestring)) {
|
864
761
|
if (op === '+') {
|
865
762
|
// string concat (a + b)
|
866
|
-
return
|
867
|
-
...left,
|
868
|
-
...right,
|
869
|
-
...concatStrings(scope, [], [], leftType, rightType, false, knownLeft === TYPES.string && knownRight === TYPES.string)
|
870
|
-
];
|
763
|
+
return concatStrings(scope, left, right, leftType, rightType);
|
871
764
|
}
|
872
765
|
|
873
766
|
// not an equality op, NaN
|
874
767
|
if (!eqOp) return number(NaN);
|
875
768
|
|
876
|
-
// else leave bool ops
|
877
|
-
// todo: convert string to number if string and number/bool
|
878
|
-
// todo: string (>|>=|<|<=) string
|
879
|
-
|
880
769
|
// string comparison
|
881
770
|
if (op === '===' || op === '==' || op === '!==' || op === '!=') {
|
882
771
|
return [
|
@@ -884,32 +773,8 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
|
|
884
773
|
...(op === '!==' || op === '!=' ? [ [ Opcodes.i32_eqz ] ] : [])
|
885
774
|
];
|
886
775
|
}
|
887
|
-
}
|
888
|
-
|
889
|
-
if (knownLeft === TYPES.bytestring || knownRight === TYPES.bytestring) {
|
890
|
-
if (op === '+') {
|
891
|
-
// string concat (a + b)
|
892
|
-
return [
|
893
|
-
...left,
|
894
|
-
...right,
|
895
|
-
...concatStrings(scope, [], [], leftType, rightType, knownLeft === TYPES.bytestring && knownRight === TYPES.bytestring)
|
896
|
-
];
|
897
|
-
}
|
898
|
-
|
899
|
-
// not an equality op, NaN
|
900
|
-
if (!eqOp) return number(NaN);
|
901
|
-
|
902
|
-
// else leave bool ops
|
903
|
-
// todo: convert string to number if string and number/bool
|
904
|
-
// todo: string (>|>=|<|<=) string
|
905
776
|
|
906
|
-
//
|
907
|
-
if (op === '===' || op === '==' || op === '!==' || op === '!=') {
|
908
|
-
return [
|
909
|
-
...compareStrings(scope, left, right, leftType, rightType),
|
910
|
-
...(op === '!==' || op === '!=' ? [ [ Opcodes.i32_eqz ] ] : [])
|
911
|
-
];
|
912
|
-
}
|
777
|
+
// todo: proper >|>=|<|<=
|
913
778
|
}
|
914
779
|
|
915
780
|
let ops = operatorOpcode[valtype][op];
|
@@ -943,39 +808,19 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
|
|
943
808
|
tmpRight = localTmp(scope, '__tmpop_right');
|
944
809
|
|
945
810
|
ops.unshift(...stringOnly([
|
946
|
-
// if left
|
947
|
-
...leftType,
|
948
|
-
...number(TYPES.bytestring, Valtype.i32),
|
949
|
-
[ Opcodes.i32_eq ],
|
950
|
-
|
951
|
-
// if right is bytestring
|
952
|
-
...rightType,
|
953
|
-
...number(TYPES.bytestring, Valtype.i32),
|
954
|
-
[ Opcodes.i32_eq ],
|
955
|
-
|
956
|
-
// if both are true
|
957
|
-
[ Opcodes.i32_and ],
|
958
|
-
[ Opcodes.if, Blocktype.void ],
|
959
|
-
...concatStrings(scope, [ [ Opcodes.local_get, tmpLeft ] ], [ [ Opcodes.local_get, tmpRight ] ], leftType, rightType, true),
|
960
|
-
...(op === '!==' || op === '!=' ? [ [ Opcodes.i32_eqz ] ] : []),
|
961
|
-
[ Opcodes.br, 1 ],
|
962
|
-
[ Opcodes.end ],
|
963
|
-
|
964
|
-
// if left is string or bytestring
|
811
|
+
// if left or right are string or bytestring
|
965
812
|
...leftType,
|
966
813
|
...number(TYPE_FLAGS.parity, Valtype.i32),
|
967
814
|
[ Opcodes.i32_or ],
|
968
815
|
...number(TYPES.bytestring, Valtype.i32),
|
969
816
|
[ Opcodes.i32_eq ],
|
970
817
|
|
971
|
-
// if right is string or bytestring
|
972
818
|
...rightType,
|
973
819
|
...number(TYPE_FLAGS.parity, Valtype.i32),
|
974
820
|
[ Opcodes.i32_or ],
|
975
821
|
...number(TYPES.bytestring, Valtype.i32),
|
976
822
|
[ Opcodes.i32_eq ],
|
977
823
|
|
978
|
-
// if either
|
979
824
|
[ Opcodes.i32_or ],
|
980
825
|
[ Opcodes.if, Blocktype.void ],
|
981
826
|
...concatStrings(scope, [ [ Opcodes.local_get, tmpLeft ] ], [ [ Opcodes.local_get, tmpRight ] ], leftType, rightType),
|
@@ -1371,6 +1216,7 @@ const setLastType = (scope, type = []) => [
|
|
1371
1216
|
];
|
1372
1217
|
|
1373
1218
|
const getNodeType = (scope, node) => {
|
1219
|
+
let guess = null;
|
1374
1220
|
const ret = (() => {
|
1375
1221
|
if (node._type) return node._type;
|
1376
1222
|
if (node.type === 'Literal') {
|
@@ -1476,6 +1322,7 @@ const getNodeType = (scope, node) => {
|
|
1476
1322
|
if (node.type === 'AssignmentExpression') {
|
1477
1323
|
const op = node.operator.slice(0, -1) || '=';
|
1478
1324
|
if (op === '=') return getNodeType(scope, node.right);
|
1325
|
+
// if (op === '=') return getNodeType(scope, node.left);
|
1479
1326
|
|
1480
1327
|
return getNodeType(scope, {
|
1481
1328
|
type: ['||', '&&', '??'].includes(op) ? 'LogicalExpression' : 'BinaryExpression',
|
@@ -1495,14 +1342,20 @@ const getNodeType = (scope, node) => {
|
|
1495
1342
|
|
1496
1343
|
const leftType = getNodeType(scope, node.left);
|
1497
1344
|
const rightType = getNodeType(scope, node.right);
|
1498
|
-
const knownLeft =
|
1499
|
-
const knownRight =
|
1345
|
+
const knownLeft = knownTypeWithGuess(scope, leftType);
|
1346
|
+
const knownRight = knownTypeWithGuess(scope, rightType);
|
1500
1347
|
|
1501
|
-
if (knownLeft
|
1502
|
-
|
1503
|
-
|
1348
|
+
if ((knownLeft != null || knownRight != null) && !(
|
1349
|
+
(knownLeft === TYPES.string || knownRight === TYPES.string) ||
|
1350
|
+
(knownLeft === TYPES.bytestring || knownRight === TYPES.bytestring)
|
1351
|
+
)) return TYPES.number;
|
1504
1352
|
|
1505
|
-
if (knownLeft
|
1353
|
+
if (knownLeft === TYPES.string || knownRight === TYPES.string)
|
1354
|
+
return TYPES.string;
|
1355
|
+
|
1356
|
+
// guess bytestring, could really be bytestring or string
|
1357
|
+
if (knownLeft === TYPES.bytestring || knownRight === TYPES.bytestring)
|
1358
|
+
guess = TYPES.bytestring;
|
1506
1359
|
|
1507
1360
|
if (scope.locals['#last_type']) return getLastType(scope);
|
1508
1361
|
|
@@ -1580,8 +1433,10 @@ const getNodeType = (scope, node) => {
|
|
1580
1433
|
return TYPES.number;
|
1581
1434
|
})();
|
1582
1435
|
|
1583
|
-
|
1584
|
-
|
1436
|
+
const out = typeof ret === 'number' ? number(ret, Valtype.i32) : ret;
|
1437
|
+
if (guess != null) out.guess = typeof guess === 'number' ? number(guess, Valtype.i32) : guess;
|
1438
|
+
|
1439
|
+
return out;
|
1585
1440
|
};
|
1586
1441
|
|
1587
1442
|
const generateLiteral = (scope, decl, global, name) => {
|
@@ -2232,6 +2087,9 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
2232
2087
|
|
2233
2088
|
// value
|
2234
2089
|
i32_const: { imms: 1, args: [], returns: 0 },
|
2090
|
+
|
2091
|
+
// dst, src, size, _, _
|
2092
|
+
memory_copy: { imms: 2, args: [ true, true, true ], returns: 0 }
|
2235
2093
|
};
|
2236
2094
|
|
2237
2095
|
const opName = name.slice('__Porffor_wasm_'.length);
|
@@ -2254,9 +2112,12 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
2254
2112
|
// literals only
|
2255
2113
|
const imms = decl.arguments.slice(op.args.length).map(x => x.value);
|
2256
2114
|
|
2115
|
+
let opcode = Opcodes[opName];
|
2116
|
+
if (!Array.isArray(opcode)) opcode = [ opcode ];
|
2117
|
+
|
2257
2118
|
return [
|
2258
2119
|
...argOut,
|
2259
|
-
[
|
2120
|
+
[ ...opcode, ...imms ],
|
2260
2121
|
...(new Array(op.returns).fill(Opcodes.i32_from))
|
2261
2122
|
];
|
2262
2123
|
}
|
@@ -2705,6 +2566,13 @@ const knownType = (scope, type) => {
|
|
2705
2566
|
|
2706
2567
|
return null;
|
2707
2568
|
};
|
2569
|
+
const knownTypeWithGuess = (scope, type) => {
|
2570
|
+
let known = knownType(scope, type);
|
2571
|
+
if (known != null) return known;
|
2572
|
+
|
2573
|
+
if (type.guess != null) return knownType(scope, type.guess);
|
2574
|
+
return known;
|
2575
|
+
};
|
2708
2576
|
|
2709
2577
|
const brTable = (input, bc, returns) => {
|
2710
2578
|
const out = [];
|
@@ -2975,8 +2843,10 @@ const setLocalWithType = (scope, name, isGlobal, decl, tee = false, overrideType
|
|
2975
2843
|
// todo: detect last type then i32 conversion op
|
2976
2844
|
const lastOp = out.at(-1);
|
2977
2845
|
if (lastOp[0] === Opcodes.local_set && lastOp[1] === scope.locals['#last_type']?.idx) {
|
2978
|
-
|
2846
|
+
// set last type -> tee last type
|
2847
|
+
lastOp[0] = Opcodes.local_tee;
|
2979
2848
|
|
2849
|
+
// still set last type due to side effects or type of decl gotten later
|
2980
2850
|
const setOut = setType(scope, name, []);
|
2981
2851
|
out.push(
|
2982
2852
|
// drop if setType is empty
|
@@ -5809,6 +5679,38 @@ const generateClass = (scope, decl) => {
|
|
5809
5679
|
return out;
|
5810
5680
|
};
|
5811
5681
|
|
5682
|
+
export const generateTemplate = (scope, decl) => {
|
5683
|
+
let current = null;
|
5684
|
+
const append = val => {
|
5685
|
+
// console.log(val);
|
5686
|
+
if (!current) {
|
5687
|
+
current = val;
|
5688
|
+
return;
|
5689
|
+
}
|
5690
|
+
|
5691
|
+
current = {
|
5692
|
+
type: 'BinaryExpression',
|
5693
|
+
operator: '+',
|
5694
|
+
left: current,
|
5695
|
+
right: val
|
5696
|
+
};
|
5697
|
+
};
|
5698
|
+
|
5699
|
+
const { expressions, quasis } = decl;
|
5700
|
+
for (let i = 0; i < quasis.length; i++) {
|
5701
|
+
append({
|
5702
|
+
type: 'Literal',
|
5703
|
+
value: quasis[i].value.cooked
|
5704
|
+
});
|
5705
|
+
|
5706
|
+
if (i < expressions.length) {
|
5707
|
+
append(expressions[i]);
|
5708
|
+
}
|
5709
|
+
}
|
5710
|
+
|
5711
|
+
return generate(scope, current);
|
5712
|
+
};
|
5713
|
+
|
5812
5714
|
globalThis._uniqId = 0;
|
5813
5715
|
const uniqId = () => '_' + globalThis._uniqId++;
|
5814
5716
|
|
package/package.json
CHANGED
package/runner/index.js
CHANGED
package/runner/repl.js
CHANGED
@@ -61,7 +61,10 @@ const memoryToString = mem => {
|
|
61
61
|
out += ` \x1B[34m${name}${' '.repeat(longestName - name.length)} \x1B[90m│\x1B[0m \x1B[36m${type}${' '.repeat(longestType - type.length)} \x1B[90m│\x1B[0m `;
|
62
62
|
}
|
63
63
|
|
64
|
-
|
64
|
+
let j = 0;
|
65
|
+
if (i === 0) j = 16;
|
66
|
+
const end = j + 40;
|
67
|
+
for (; j < end; j++) {
|
65
68
|
const val = buf[i * pageSize + j];
|
66
69
|
// if (val === 0) out += '\x1B[2m';
|
67
70
|
if (val === 0) out += '\x1B[90m';
|