porffor 0.0.0-c743344 → 0.0.0-d650361

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.
@@ -5,6 +5,7 @@ import { BuiltinFuncs, BuiltinVars, importedFuncs, NULL, UNDEFINED } from "./bui
5
5
  import { PrototypeFuncs } from "./prototype.js";
6
6
  import { number, i32x4 } from "./embedding.js";
7
7
  import parse from "./parse.js";
8
+ import * as Rhemyn from "../rhemyn/compile.js";
8
9
 
9
10
  let globals = {};
10
11
  let globalInd = 0;
@@ -108,8 +109,8 @@ const generate = (scope, decl, global = false, name = undefined) => {
108
109
  case 'WhileStatement':
109
110
  return generateWhile(scope, decl);
110
111
 
111
- /* case 'ForOfStatement':
112
- return generateForOf(scope, decl); */
112
+ case 'ForOfStatement':
113
+ return generateForOf(scope, decl);
113
114
 
114
115
  case 'BreakStatement':
115
116
  return generateBreak(scope, decl);
@@ -151,44 +152,65 @@ const generate = (scope, decl, global = false, name = undefined) => {
151
152
 
152
153
  return [];
153
154
 
154
- case 'TaggedTemplateExpression':
155
- // hack for inline asm
156
- if (decl.tag.name !== 'asm') return todo('tagged template expressions not implemented');
155
+ case 'TaggedTemplateExpression': {
156
+ const funcs = {
157
+ asm: str => {
158
+ let out = [];
157
159
 
158
- const str = decl.quasi.quasis[0].value.raw;
159
- let out = [];
160
+ for (const line of str.split('\n')) {
161
+ const asm = line.trim().split(';;')[0].split(' ');
162
+ if (asm[0] === '') continue; // blank
160
163
 
161
- for (const line of str.split('\n')) {
162
- const asm = line.trim().split(';;')[0].split(' ');
163
- if (asm[0] === '') continue; // blank
164
+ if (asm[0] === 'local') {
165
+ const [ name, idx, type ] = asm.slice(1);
166
+ scope.locals[name] = { idx: parseInt(idx), type: Valtype[type] };
167
+ continue;
168
+ }
164
169
 
165
- if (asm[0] === 'local') {
166
- const [ name, idx, type ] = asm.slice(1);
167
- scope.locals[name] = { idx: parseInt(idx), type: Valtype[type] };
168
- continue;
169
- }
170
+ if (asm[0] === 'returns') {
171
+ scope.returns = asm.slice(1).map(x => Valtype[x]);
172
+ continue;
173
+ }
170
174
 
171
- if (asm[0] === 'returns') {
172
- scope.returns = asm.slice(1).map(x => Valtype[x]);
173
- continue;
174
- }
175
+ if (asm[0] === 'memory') {
176
+ allocPage('asm instrinsic');
177
+ // todo: add to store/load offset insts
178
+ continue;
179
+ }
175
180
 
176
- if (asm[0] === 'memory') {
177
- allocPage('asm instrinsic');
178
- // todo: add to store/load offset insts
179
- continue;
180
- }
181
+ let inst = Opcodes[asm[0].replace('.', '_')];
182
+ if (!inst) throw new Error(`inline asm: inst ${asm[0]} not found`);
183
+
184
+ if (!Array.isArray(inst)) inst = [ inst ];
185
+ const immediates = asm.slice(1).map(x => parseInt(x));
186
+
187
+ out.push([ ...inst, ...immediates ]);
188
+ }
189
+
190
+ return out;
191
+ },
181
192
 
182
- let inst = Opcodes[asm[0].replace('.', '_')];
183
- if (!inst) throw new Error(`inline asm: inst ${asm[0]} not found`);
193
+ __internal_print_type: str => {
194
+ const type = getType(scope, str) - TYPES.number;
184
195
 
185
- if (!Array.isArray(inst)) inst = [ inst ];
186
- const immediates = asm.slice(1).map(x => parseInt(x));
196
+ return [
197
+ ...number(type),
198
+ [ Opcodes.call, importedFuncs.print ],
187
199
 
188
- out.push([ ...inst, ...immediates ]);
200
+ // newline
201
+ ...number(10),
202
+ [ Opcodes.call, importedFuncs.printChar ]
203
+ ];
204
+ }
189
205
  }
190
206
 
191
- return out;
207
+ const name = decl.tag.name;
208
+ // hack for inline asm
209
+ if (!funcs[name]) return todo('tagged template expressions not implemented');
210
+
211
+ const str = decl.quasi.quasis[0].value.raw;
212
+ return funcs[name](str);
213
+ }
192
214
 
193
215
  default:
194
216
  return todo(`no generation for ${decl.type}!`);
@@ -664,29 +686,44 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
664
686
 
665
687
  if (codeLog && (!leftType || !rightType)) log('codegen', 'untracked type in op', op, _name, '\n' + new Error().stack.split('\n').slice(1).join('\n'));
666
688
 
667
- // if strict (in)equal and known types mismatch, return false (===)/true (!==)
668
- if ((op === '===' || op === '!==') && leftType && rightType && leftType !== rightType) {
689
+ const eqOp = ['==', '===', '!=', '!==', '>', '>=', '<', '<='].includes(op);
690
+
691
+ if (leftType && rightType && (
692
+ // if strict (in)equal and known types mismatch, return false (===)/true (!==)
693
+ ((op === '===' || op === '!==') && leftType !== rightType) ||
694
+
695
+ // if equality op and an operand is undefined, return false
696
+ (eqOp && leftType === TYPES.undefined ^ rightType === TYPES.undefined)
697
+ )) {
698
+ // undefined == null
699
+ if (((leftType === TYPES.undefined && rightType === TYPES.object) || (leftType === TYPES.object && rightType === TYPES.undefined)) && (op === '==' || op === '!=')) return [
700
+ ...(leftType === TYPES.object ? left : right),
701
+ ...Opcodes.eqz,
702
+ ...(op === '!=' ? [ [ Opcodes.i32_eqz ] ] : [])
703
+ ];
704
+
669
705
  return [
670
706
  ...left,
671
- ...right,
672
-
673
- // drop values
674
707
  [ Opcodes.drop ],
708
+
709
+ ...right,
675
710
  [ Opcodes.drop ],
676
711
 
677
- // return false (===)/true (!==)
678
- ...number(op === '===' ? 0 : 1, Valtype.i32)
712
+ // return true (!=/!==) or false (else)
713
+ ...number(op === '!=' || op === '!==' ? 1 : 0, Valtype.i32)
679
714
  ];
680
715
  }
681
716
 
717
+ // todo: niche null hell with 0
718
+
682
719
  if (leftType === TYPES.string || rightType === TYPES.string) {
683
720
  if (op === '+') {
684
721
  // string concat (a + b)
685
722
  return concatStrings(scope, left, right, _global, _name, assign);
686
723
  }
687
724
 
688
- // any other math op, NaN
689
- if (!['==', '===', '!=', '!==', '>', '>=', '<', '<='].includes(op)) return number(NaN);
725
+ // not an equality op, NaN
726
+ if (!eqOp) return number(NaN);
690
727
 
691
728
  // else leave bool ops
692
729
  // todo: convert string to number if string and number/bool
@@ -731,21 +768,15 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
731
768
  ];
732
769
  };
733
770
 
734
- let binaryExpDepth = 0;
735
771
  const generateBinaryExp = (scope, decl, _global, _name) => {
736
- binaryExpDepth++;
737
-
738
- const out = [
739
- ...performOp(scope, decl.operator, generate(scope, decl.left), generate(scope, decl.right), getNodeType(scope, decl.left), getNodeType(scope, decl.right), _global, _name)
740
- ];
772
+ const out = performOp(scope, decl.operator, generate(scope, decl.left), generate(scope, decl.right), getNodeType(scope, decl.left), getNodeType(scope, decl.right), _global, _name);
741
773
 
742
774
  if (valtype !== 'i32' && ['==', '===', '!=', '!==', '>', '>=', '<', '<='].includes(decl.operator)) out.push(Opcodes.i32_from_u);
743
775
 
744
- binaryExpDepth--;
745
776
  return out;
746
777
  };
747
778
 
748
- const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes = [], globalInits, returns, returnType, memory, localNames = [], globalNames = [] }) => {
779
+ const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes = [], globalInits, returns, returnType, localNames = [], globalNames = [] }) => {
749
780
  const existing = funcs.find(x => x.name === name);
750
781
  if (existing) return existing;
751
782
 
@@ -781,7 +812,6 @@ const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes
781
812
  returns,
782
813
  returnType: TYPES[returnType ?? 'number'],
783
814
  wasm,
784
- memory,
785
815
  internal: true,
786
816
  index: currentFuncIndex++
787
817
  };
@@ -814,7 +844,8 @@ const TYPES = {
814
844
  bigint: 0xffffffffffff7,
815
845
 
816
846
  // these are not "typeof" types but tracked internally
817
- _array: 0xffffffffffff8
847
+ _array: 0xfffffffffff0f,
848
+ _regexp: 0xfffffffffff1f
818
849
  };
819
850
 
820
851
  const TYPE_NAMES = {
@@ -849,6 +880,8 @@ const getType = (scope, _name) => {
849
880
  const getNodeType = (scope, node) => {
850
881
  if (node.type === 'Literal') {
851
882
  if (['number', 'boolean', 'string', 'undefined', 'object', 'function', 'symbol', 'bigint'].includes(node.value)) return TYPES.number;
883
+ if (node.regex) return TYPES._regexp;
884
+
852
885
  return TYPES[typeof node.value];
853
886
  }
854
887
 
@@ -882,6 +915,11 @@ const getNodeType = (scope, node) => {
882
915
 
883
916
  // literal.func()
884
917
  if (!name && node.callee.type === 'MemberExpression') {
918
+ if (node.callee.object.regex) {
919
+ const funcName = node.callee.property.name;
920
+ return Rhemyn[funcName] ? TYPES.boolean : TYPES.undefined;
921
+ }
922
+
885
923
  const baseType = getNodeType(scope, node.callee.object);
886
924
 
887
925
  const func = node.callee.property.name;
@@ -925,6 +963,11 @@ const getNodeType = (scope, node) => {
925
963
  const generateLiteral = (scope, decl, global, name) => {
926
964
  if (decl.value === null) return number(NULL);
927
965
 
966
+ if (decl.regex) {
967
+ scope.regex[name] = decl.regex;
968
+ return number(1);
969
+ }
970
+
928
971
  switch (typeof decl.value) {
929
972
  case 'number':
930
973
  return number(decl.value);
@@ -986,6 +1029,8 @@ const countLeftover = wasm => {
986
1029
  } else count--;
987
1030
  if (func) count += func.returns.length;
988
1031
  } else count--;
1032
+
1033
+ // console.log(count, decompile([ inst ]).slice(0, -1));
989
1034
  }
990
1035
 
991
1036
  return count;
@@ -1084,6 +1129,25 @@ const generateCall = (scope, decl, _global, _name) => {
1084
1129
 
1085
1130
  // literal.func()
1086
1131
  if (!name && decl.callee.type === 'MemberExpression') {
1132
+ // megahack for /regex/.func()
1133
+ if (decl.callee.object.regex) {
1134
+ const funcName = decl.callee.property.name;
1135
+ const func = Rhemyn[funcName](decl.callee.object.regex.pattern, currentFuncIndex++);
1136
+
1137
+ funcIndex[func.name] = func.index;
1138
+ funcs.push(func);
1139
+
1140
+ return [
1141
+ // make string arg
1142
+ ...generate(scope, decl.arguments[0]),
1143
+
1144
+ // call regex func
1145
+ Opcodes.i32_to_u,
1146
+ [ Opcodes.call, func.index ],
1147
+ Opcodes.i32_from
1148
+ ];
1149
+ }
1150
+
1087
1151
  baseType = getNodeType(scope, decl.callee.object);
1088
1152
 
1089
1153
  const func = decl.callee.property.name;
@@ -1096,6 +1160,31 @@ const generateCall = (scope, decl, _global, _name) => {
1096
1160
  baseName = [...arrays.keys()].pop();
1097
1161
  }
1098
1162
 
1163
+ if (protoName && baseType === TYPES.string && Rhemyn[protoName]) {
1164
+ const func = Rhemyn[protoName](decl.arguments[0].regex.pattern, currentFuncIndex++);
1165
+
1166
+ funcIndex[func.name] = func.index;
1167
+ funcs.push(func);
1168
+
1169
+ const pointer = arrays.get(baseName);
1170
+ const [ local, isGlobal ] = lookupName(scope, baseName);
1171
+
1172
+ return [
1173
+ ...out,
1174
+
1175
+ ...(pointer == null ? [
1176
+ [ isGlobal ? Opcodes.global_get : Opcodes.local_get, local.idx ],
1177
+ Opcodes.i32_to_u,
1178
+ ] : [
1179
+ ...number(pointer, Valtype.i32)
1180
+ ]),
1181
+
1182
+ // call regex func
1183
+ [ Opcodes.call, func.index ],
1184
+ Opcodes.i32_from
1185
+ ];
1186
+ }
1187
+
1099
1188
  if (protoFunc) {
1100
1189
  let pointer = arrays.get(baseName);
1101
1190
 
@@ -1136,9 +1225,10 @@ const generateCall = (scope, decl, _global, _name) => {
1136
1225
 
1137
1226
  [ Opcodes.block, valtypeBinary ],
1138
1227
  ...protoFunc(pointer, {
1139
- cachedI32: [ [ Opcodes.local_get, lengthLocal ] ],
1140
- get: arrayUtil.getLength(pointer),
1141
- getI32: arrayUtil.getLengthI32(pointer),
1228
+ getCachedI32: () => [ [ Opcodes.local_get, lengthLocal ] ],
1229
+ setCachedI32: () => [ [ Opcodes.local_set, lengthLocal ] ],
1230
+ get: () => arrayUtil.getLength(pointer),
1231
+ getI32: () => arrayUtil.getLengthI32(pointer),
1142
1232
  set: value => arrayUtil.setLength(pointer, value),
1143
1233
  setI32: value => arrayUtil.setLengthI32(pointer, value)
1144
1234
  }, generate(scope, decl.arguments[0] ?? DEFAULT_VALUE), protoLocal, (length, itemType) => {
@@ -1205,7 +1295,7 @@ const generateCall = (scope, decl, _global, _name) => {
1205
1295
  if (func && func.throws) scope.throws = true;
1206
1296
 
1207
1297
  for (const arg of args) {
1208
- out.push(...generate(scope, arg));
1298
+ out = out.concat(generate(scope, arg));
1209
1299
  }
1210
1300
 
1211
1301
  out.push([ Opcodes.call, idx ]);
@@ -1345,6 +1435,47 @@ const generateAssign = (scope, decl) => {
1345
1435
  ];
1346
1436
  }
1347
1437
 
1438
+ if (decl.left.type === 'MemberExpression' && decl.left.computed) {
1439
+ // arr[i] | str[i]
1440
+ const name = decl.left.object.name;
1441
+ const pointer = arrays.get(name);
1442
+
1443
+ const aotPointer = pointer != null;
1444
+
1445
+ const newValueTmp = localTmp(scope, '__member_setter_tmp');
1446
+
1447
+ const parentType = getNodeType(scope, decl.left.object);
1448
+
1449
+ const op = decl.operator.slice(0, -1);
1450
+ return [
1451
+ ...(aotPointer ? number(0, Valtype.i32) : [
1452
+ ...generate(scope, decl.left.object),
1453
+ Opcodes.i32_to_u
1454
+ ]),
1455
+
1456
+ // get index as valtype
1457
+ ...generate(scope, decl.left.property),
1458
+
1459
+ // convert to i32 and turn into byte offset by * valtypeSize (4 for i32, 8 for i64/f64)
1460
+ Opcodes.i32_to_u,
1461
+ ...number(ValtypeSize[valtype], Valtype.i32),
1462
+ [ Opcodes.i32_mul ],
1463
+ [ Opcodes.i32_add ],
1464
+
1465
+ ...(op === '' ? generate(scope, decl.right, false, name) : performOp(scope, op, generate(scope, decl.left), generate(scope, decl.right), parentType === TYPES._array ? TYPES.number : TYPES.string, getNodeType(scope, decl.right), false, name, true)),
1466
+ [ Opcodes.local_tee, newValueTmp ],
1467
+
1468
+ ...(parentType === TYPES._array ? [
1469
+ [ Opcodes.store, Math.log2(ValtypeSize[valtype]) - 1, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ]
1470
+ ] : [
1471
+ Opcodes.i32_to_u,
1472
+ [ StoreOps.i16, Math.log2(ValtypeSize.i16) - 1, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ]
1473
+ ]),
1474
+
1475
+ [ Opcodes.local_get, newValueTmp ]
1476
+ ];
1477
+ }
1478
+
1348
1479
  const [ local, isGlobal ] = lookupName(scope, name);
1349
1480
 
1350
1481
  if (local === undefined) {
@@ -1365,8 +1496,10 @@ const generateAssign = (scope, decl) => {
1365
1496
  ];
1366
1497
  }
1367
1498
 
1499
+ typeStates[name] = getNodeType(scope, decl.right);
1500
+
1368
1501
  if (decl.operator === '=') {
1369
- typeStates[name] = getNodeType(scope, decl.right);
1502
+ // typeStates[name] = getNodeType(scope, decl.right);
1370
1503
 
1371
1504
  return [
1372
1505
  ...generate(scope, decl.right, isGlobal, name),
@@ -1510,7 +1643,7 @@ const generateUpdate = (scope, decl) => {
1510
1643
  };
1511
1644
 
1512
1645
  const generateIf = (scope, decl) => {
1513
- const out = truthy(scope, generate(scope, decl.test), decl.test);
1646
+ const out = truthy(scope, generate(scope, decl.test), getNodeType(scope, decl.test));
1514
1647
 
1515
1648
  out.push(Opcodes.i32_to, [ Opcodes.if, Blocktype.void ]);
1516
1649
  depth.push('if');
@@ -1603,18 +1736,106 @@ const generateWhile = (scope, decl) => {
1603
1736
  const generateForOf = (scope, decl) => {
1604
1737
  const out = [];
1605
1738
 
1739
+ const rightType = getNodeType(scope, decl.right);
1740
+ const valtypeSize = rightType === TYPES._array ? ValtypeSize[valtype] : ValtypeSize.i16; // presume array (:()
1741
+
1742
+ // todo: for of inside for of might fuck up?
1743
+ const pointer = localTmp(scope, 'forof_base_pointer', Valtype.i32);
1744
+ const length = localTmp(scope, 'forof_length', Valtype.i32);
1745
+ const counter = localTmp(scope, 'forof_counter', Valtype.i32);
1746
+
1747
+ out.push(
1748
+ // set pointer as right
1749
+ ...generate(scope, decl.right),
1750
+ Opcodes.i32_to_u,
1751
+ [ Opcodes.local_set, pointer ],
1752
+
1753
+ // get length
1754
+ [ Opcodes.local_get, pointer ],
1755
+ [ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, 0 ],
1756
+ [ Opcodes.local_set, length ]
1757
+ );
1758
+
1606
1759
  out.push([ Opcodes.loop, Blocktype.void ]);
1607
- depth.push('while');
1760
+ depth.push('forof');
1608
1761
 
1609
- out.push(...generate(scope, decl.test));
1610
- out.push(Opcodes.i32_to, [ Opcodes.if, Blocktype.void ]);
1611
- depth.push('if');
1762
+ // setup local for left
1763
+ generate(scope, decl.left);
1612
1764
 
1613
- out.push(...generate(scope, decl.body));
1765
+ const leftName = decl.left.declarations[0].id.name;
1614
1766
 
1615
- out.push([ Opcodes.br, 1 ]);
1616
- out.push([ Opcodes.end ], [ Opcodes.end ]);
1617
- depth.pop(); depth.pop();
1767
+ // set type for local
1768
+ typeStates[leftName] = rightType === TYPES._array ? TYPES.number : TYPES.string;
1769
+
1770
+ const [ local, isGlobal ] = lookupName(scope, leftName);
1771
+
1772
+ if (rightType === TYPES._array) { // array
1773
+ out.push(
1774
+ [ Opcodes.local_get, pointer ],
1775
+ [ Opcodes.load, Math.log2(valtypeSize) - 1, ...unsignedLEB128(ValtypeSize.i32) ]
1776
+ );
1777
+ } else { // string
1778
+ const [ newOut, newPointer ] = makeArray(scope, {
1779
+ rawElements: new Array(1)
1780
+ }, isGlobal, leftName, true, 'i16');
1781
+
1782
+ out.push(
1783
+ // setup new/out array
1784
+ ...newOut,
1785
+ [ Opcodes.drop ],
1786
+
1787
+ ...number(0, Valtype.i32), // base 0 for store after
1788
+
1789
+ // load current string ind {arg}
1790
+ [ Opcodes.local_get, pointer ],
1791
+ [ Opcodes.i32_load16_u, Math.log2(ValtypeSize.i16) - 1, ...unsignedLEB128(ValtypeSize.i32) ],
1792
+
1793
+ // store to new string ind 0
1794
+ [ Opcodes.i32_store16, Math.log2(ValtypeSize.i16) - 1, ...unsignedLEB128(newPointer + ValtypeSize.i32) ],
1795
+
1796
+ // return new string (page)
1797
+ ...number(newPointer)
1798
+ );
1799
+ }
1800
+
1801
+ // set left value
1802
+ out.push([ isGlobal ? Opcodes.global_set : Opcodes.local_set, local.idx ]);
1803
+
1804
+ out.push(
1805
+ [ Opcodes.block, Blocktype.void ],
1806
+ [ Opcodes.block, Blocktype.void ]
1807
+ );
1808
+ depth.push('block');
1809
+ depth.push('block');
1810
+
1811
+ out.push(
1812
+ ...generate(scope, decl.body),
1813
+ [ Opcodes.end ]
1814
+ );
1815
+ depth.pop();
1816
+
1817
+ out.push(
1818
+ // increment iter pointer by valtype size
1819
+ [ Opcodes.local_get, pointer ],
1820
+ ...number(valtypeSize, Valtype.i32),
1821
+ [ Opcodes.i32_add ],
1822
+ [ Opcodes.local_set, pointer ],
1823
+
1824
+ // increment counter by 1
1825
+ [ Opcodes.local_get, counter ],
1826
+ ...number(1, Valtype.i32),
1827
+ [ Opcodes.i32_add ],
1828
+ [ Opcodes.local_tee, counter ],
1829
+
1830
+ // loop if counter != length
1831
+ [ Opcodes.local_get, length ],
1832
+ [ Opcodes.i32_ne ],
1833
+ [ Opcodes.br_if, 1 ],
1834
+
1835
+ [ Opcodes.end ], [ Opcodes.end ]
1836
+ );
1837
+ depth.pop();
1838
+ depth.pop();
1618
1839
 
1619
1840
  return out;
1620
1841
  };
@@ -1705,19 +1926,19 @@ const generateAssignPat = (scope, decl) => {
1705
1926
  };
1706
1927
 
1707
1928
  let pages = new Map();
1708
- const allocPage = reason => {
1709
- if (pages.has(reason)) return pages.get(reason);
1929
+ const allocPage = (reason, type) => {
1930
+ if (pages.has(reason)) return pages.get(reason).ind;
1710
1931
 
1711
- let ind = pages.size;
1712
- pages.set(reason, ind);
1932
+ const ind = pages.size;
1933
+ pages.set(reason, { ind, type });
1713
1934
 
1714
- if (allocLog) log('alloc', `allocated new page of memory (${ind}) | ${reason}`);
1935
+ if (allocLog) log('alloc', `allocated new page of memory (${ind}) | ${reason} (type: ${type})`);
1715
1936
 
1716
1937
  return ind;
1717
1938
  };
1718
1939
 
1719
1940
  const freePage = reason => {
1720
- let ind = pages.get(reason);
1941
+ const { ind } = pages.get(reason);
1721
1942
  pages.delete(reason);
1722
1943
 
1723
1944
  if (allocLog) log('alloc', `freed page of memory (${ind}) | ${reason}`);
@@ -1734,7 +1955,7 @@ const itemTypeToValtype = {
1734
1955
  i16: 'i32'
1735
1956
  };
1736
1957
 
1737
- const storeOps = {
1958
+ const StoreOps = {
1738
1959
  i32: Opcodes.i32_store,
1739
1960
  i64: Opcodes.i64_store,
1740
1961
  f64: Opcodes.f64_store,
@@ -1749,7 +1970,7 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
1749
1970
  if (!arrays.has(name) || name === '$undeclared') {
1750
1971
  // todo: can we just have 1 undeclared array? probably not? but this is not really memory efficient
1751
1972
  const uniqueName = name === '$undeclared' ? name + Math.random().toString().slice(2) : name;
1752
- arrays.set(name, allocPage(`${itemType === 'i16' ? 'string' : 'array'}: ${uniqueName}`) * pageSize);
1973
+ arrays.set(name, allocPage(`${itemType === 'i16' ? 'string' : 'array'}: ${uniqueName}`, itemType) * pageSize);
1753
1974
  }
1754
1975
 
1755
1976
  const pointer = arrays.get(name);
@@ -1766,7 +1987,7 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
1766
1987
  [ Opcodes.i32_store, Math.log2(ValtypeSize.i32) - 1, ...unsignedLEB128(pointer) ]
1767
1988
  );
1768
1989
 
1769
- const storeOp = storeOps[itemType];
1990
+ const storeOp = StoreOps[itemType];
1770
1991
  const valtype = itemTypeToValtype[itemType];
1771
1992
 
1772
1993
  if (!initEmpty) for (let i = 0; i < length; i++) {
@@ -1929,7 +2150,6 @@ const generateFunc = (scope, decl) => {
1929
2150
  localInd: 0,
1930
2151
  returns: [ valtypeBinary ],
1931
2152
  returnType: null,
1932
- memory: false,
1933
2153
  throws: false,
1934
2154
  name
1935
2155
  };
@@ -2092,10 +2312,10 @@ const generateFunc = (scope, decl) => {
2092
2312
  };
2093
2313
 
2094
2314
  const generateCode = (scope, decl) => {
2095
- const out = [];
2315
+ let out = [];
2096
2316
 
2097
2317
  for (const x of decl.body) {
2098
- out.push(...generate(scope, x));
2318
+ out = out.concat(generate(scope, x));
2099
2319
  }
2100
2320
 
2101
2321
  return out;
@@ -57,7 +57,7 @@ export default (wasm, name = '', ind = 0, locals = {}, params = [], returns = []
57
57
  out += ` ;; label @${depth}`;
58
58
  }
59
59
 
60
- if (inst[0] === Opcodes.br) {
60
+ if (inst[0] === Opcodes.br || inst[0] === Opcodes.br_if) {
61
61
  out += ` ;; goto @${depth - inst[1]}`;
62
62
  }
63
63
 
package/compiler/index.js CHANGED
@@ -4,6 +4,8 @@ import opt from './opt.js';
4
4
  import produceSections from './sections.js';
5
5
  import decompile from './decompile.js';
6
6
  import { BuiltinPreludes } from './builtins.js';
7
+ import toc from './2c.js';
8
+
7
9
 
8
10
  globalThis.decompile = decompile;
9
11
 
@@ -15,7 +17,8 @@ const areaColors = {
15
17
  codegen: [ 20, 80, 250 ],
16
18
  opt: [ 250, 20, 80 ],
17
19
  sections: [ 20, 250, 80 ],
18
- alloc: [ 250, 250, 20 ]
20
+ alloc: [ 250, 250, 20 ],
21
+ '2c': [ 20, 250, 250 ]
19
22
  };
20
23
 
21
24
  globalThis.log = (area, ...args) => console.log(`\u001b[90m[\u001b[0m${rgb(...areaColors[area], area)}\u001b[90m]\u001b[0m`, ...args);
@@ -36,10 +39,16 @@ const logFuncs = (funcs, globals, exceptions) => {
36
39
  console.log();
37
40
  };
38
41
 
42
+ const getArg = name => process.argv.find(x => x.startsWith(`-${name}=`))?.slice(name.length + 2);
43
+
44
+ const writeFileSync = (typeof process !== 'undefined' ? (await import('node:fs')).writeFileSync : undefined);
45
+ const execSync = (typeof process !== 'undefined' ? (await import('node:child_process')).execSync : undefined);
46
+
39
47
  export default (code, flags) => {
40
48
  globalThis.optLog = process.argv.includes('-opt-log');
41
49
  globalThis.codeLog = process.argv.includes('-code-log');
42
50
  globalThis.allocLog = process.argv.includes('-alloc-log');
51
+ globalThis.regexLog = process.argv.includes('-regex-log');
43
52
 
44
53
  for (const x in BuiltinPreludes) {
45
54
  if (code.indexOf(x + '(') !== -1) code = BuiltinPreludes[x] + code;
@@ -72,5 +81,38 @@ export default (code, flags) => {
72
81
  // console.log([...pages.keys()].map(x => `\x1B[36m - ${x}\x1B[0m`).join('\n'));
73
82
  }
74
83
 
75
- return { wasm: sections, funcs, globals, tags, exceptions, pages };
84
+ const out = { wasm: sections, funcs, globals, tags, exceptions, pages };
85
+
86
+ const target = getArg('target') ?? getArg('t') ?? 'wasm';
87
+ const outFile = getArg('o');
88
+
89
+ if (target === 'c') {
90
+ const c = toc(out);
91
+
92
+ if (outFile) {
93
+ writeFileSync(outFile, c);
94
+ } else {
95
+ console.log(c);
96
+ }
97
+
98
+ process.exit();
99
+ }
100
+
101
+ if (target === 'native') {
102
+ const compiler = getArg('compiler') ?? 'clang';
103
+ const cO = getArg('cO') ?? 'Ofast';
104
+
105
+ const tmpfile = 'tmp.c';
106
+ const args = [ compiler, tmpfile, '-o', outFile ?? (process.platform === 'win32' ? 'out.exe' : 'out'), '-' + cO, '-march=native' ];
107
+
108
+ const c = toc(out);
109
+ writeFileSync(tmpfile, c);
110
+
111
+ // obvious command escape is obvious
112
+ execSync(args.join(' '), { stdio: 'inherit' });
113
+
114
+ process.exit();
115
+ }
116
+
117
+ return out;
76
118
  };
package/compiler/opt.js CHANGED
@@ -142,7 +142,7 @@ export default (funcs, globals) => {
142
142
  depth--;
143
143
  if (depth <= 0) break;
144
144
  }
145
- if (op === Opcodes.br) {
145
+ if (op === Opcodes.br || op === Opcodes.br_if) {
146
146
  hasBranch = true;
147
147
  break;
148
148
  }
package/compiler/parse.js CHANGED
@@ -1,3 +1,4 @@
1
+ // import { parse } from 'acorn';
1
2
  const { parse } = (await import(globalThis.document ? 'https://esm.sh/acorn' : 'acorn'));
2
3
 
3
4
  export default (input, flags) => {