porffor 0.2.0-6aff0fa → 0.2.0-6bc63ef

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.
Files changed (54) hide show
  1. package/CONTRIBUTING.md +256 -0
  2. package/LICENSE +20 -20
  3. package/README.md +115 -82
  4. package/asur/index.js +624 -340
  5. package/byg/index.js +216 -0
  6. package/compiler/2c.js +2 -53
  7. package/compiler/{sections.js → assemble.js} +60 -14
  8. package/compiler/builtins/annexb_string.js +72 -0
  9. package/compiler/builtins/annexb_string.ts +18 -0
  10. package/compiler/builtins/array.ts +145 -0
  11. package/compiler/builtins/base64.ts +7 -84
  12. package/compiler/builtins/boolean.ts +18 -0
  13. package/compiler/builtins/crypto.ts +120 -0
  14. package/compiler/builtins/date.ts +2067 -0
  15. package/compiler/builtins/escape.ts +141 -0
  16. package/compiler/builtins/function.ts +5 -0
  17. package/compiler/builtins/int.ts +145 -0
  18. package/compiler/builtins/number.ts +529 -0
  19. package/compiler/builtins/object.ts +4 -0
  20. package/compiler/builtins/porffor.d.ts +44 -7
  21. package/compiler/builtins/set.ts +187 -0
  22. package/compiler/builtins/string.ts +1080 -0
  23. package/compiler/builtins.js +400 -120
  24. package/compiler/{codeGen.js → codegen.js} +850 -402
  25. package/compiler/decompile.js +2 -3
  26. package/compiler/embedding.js +22 -22
  27. package/compiler/encoding.js +94 -10
  28. package/compiler/expression.js +1 -1
  29. package/compiler/generated_builtins.js +1613 -3
  30. package/compiler/index.js +16 -16
  31. package/compiler/log.js +2 -2
  32. package/compiler/opt.js +28 -27
  33. package/compiler/parse.js +36 -30
  34. package/compiler/precompile.js +37 -46
  35. package/compiler/prefs.js +7 -6
  36. package/compiler/prototype.js +20 -36
  37. package/compiler/types.js +38 -0
  38. package/compiler/wasmSpec.js +14 -1
  39. package/compiler/wrap.js +79 -69
  40. package/package.json +9 -5
  41. package/porf +2 -0
  42. package/rhemyn/compile.js +44 -26
  43. package/rhemyn/parse.js +322 -320
  44. package/rhemyn/test/parse.js +58 -58
  45. package/runner/compare.js +33 -34
  46. package/runner/debug.js +117 -0
  47. package/runner/index.js +69 -12
  48. package/runner/profiler.js +22 -30
  49. package/runner/repl.js +40 -13
  50. package/runner/sizes.js +37 -37
  51. package/runner/version.js +3 -3
  52. package/runner/info.js +0 -89
  53. package/runner/transform.js +0 -15
  54. package/util/enum.js +0 -20
package/compiler/wrap.js CHANGED
@@ -1,27 +1,74 @@
1
1
  import compile from './index.js';
2
2
  import decompile from './decompile.js';
3
3
  import { encodeVector, encodeLocal } from './encoding.js';
4
- import Prefs from './prefs.js';
4
+ import { TYPES } from './types.js';
5
5
  import { log } from './log.js';
6
+ import Prefs from './prefs.js';
6
7
 
7
8
  const bold = x => `\u001b[1m${x}\u001b[0m`;
8
9
 
9
- const typeBase = 0x00;
10
- const internalTypeBase = 0x10;
11
- const TYPES = {
12
- [typeBase]: 'number',
13
- [typeBase + 1]: 'boolean',
14
- [typeBase + 2]: 'string',
15
- [typeBase + 3]: 'undefined',
16
- [typeBase + 4]: 'object',
17
- [typeBase + 5]: 'function',
18
- [typeBase + 6]: 'symbol',
19
- [typeBase + 7]: 'bigint',
20
-
21
- // internal
22
- [internalTypeBase]: '_array',
23
- [internalTypeBase + 1]: '_regexp',
24
- [internalTypeBase + 2]: '_bytestring'
10
+ const porfToJSValue = (memory, funcs, value, type) => {
11
+ switch (type) {
12
+ case TYPES.boolean: return Boolean(value);
13
+ case TYPES.undefined: return undefined;
14
+ case TYPES.object: return value === 0 ? null : {};
15
+
16
+ case TYPES.function: {
17
+ // wasm func index, including all imports
18
+ const func = funcs.find(x => (x.originalIndex ?? x.index) === value);
19
+ // if (!func) return value;
20
+ if (!func) return function () {};
21
+
22
+ // make fake empty func for repl/etc
23
+ return {[func.name]() {}}[func.name];
24
+ }
25
+
26
+ case TYPES.string: {
27
+ const length = (new Int32Array(memory.buffer, value, 1))[0];
28
+ return Array.from(new Uint16Array(memory.buffer, value + 4, length)).map(x => String.fromCharCode(x)).join('');
29
+ }
30
+
31
+ case TYPES.bytestring: {
32
+ const length = (new Int32Array(memory.buffer, value, 1))[0];
33
+ return Array.from(new Uint8Array(memory.buffer, value + 4, length)).map(x => String.fromCharCode(x)).join('');
34
+ }
35
+
36
+ case TYPES.array: {
37
+ const length = (new Int32Array(memory.buffer, value, 1))[0];
38
+
39
+ // have to slice because of memory alignment (?)
40
+ const buf = memory.buffer.slice(value + 4, value + 4 + 8 * length);
41
+ return Array.from(new Float64Array(buf, 0, length));
42
+ }
43
+
44
+ case TYPES.date: {
45
+ const t = (new Float64Array(memory.buffer, value, 1))[0];
46
+ return new Date(t);
47
+ }
48
+
49
+ case TYPES.set: {
50
+ const size = (new Int32Array(memory.buffer, value, 1))[0];
51
+
52
+ const out = new Set();
53
+ for (let i = 0; i < size; i++) {
54
+ const offset = value + 4 + (i * 9);
55
+
56
+ // have to slice because of memory alignment (?)
57
+ const v = (new Float64Array(memory.buffer.slice(offset, offset + 8), 0, 1))[0];
58
+ const t = (new Uint8Array(memory.buffer, offset + 8, 1))[0];
59
+
60
+ // console.log(`reading value at index ${i}...`)
61
+ // console.log(' memory:', Array.from(new Uint8Array(memory.buffer, offset, 9)).map(x => x.toString(16).padStart(2, '0')).join(' '));
62
+ // console.log(' read:', { value: v, type: t }, '\n');
63
+
64
+ out.add(porfToJSValue(memory, funcs, v, t));
65
+ }
66
+
67
+ return out;
68
+ }
69
+
70
+ default: return value;
71
+ }
25
72
  };
26
73
 
27
74
  export default async (source, flags = [ 'module' ], customImports = {}, print = str => process.stdout.write(str)) => {
@@ -30,12 +77,14 @@ export default async (source, flags = [ 'module' ], customImports = {}, print =
30
77
  const t1 = performance.now();
31
78
  const { wasm, funcs, globals, tags, exceptions, pages, c } = compile(source, flags);
32
79
 
80
+ globalThis.porfDebugInfo = { funcs, globals };
81
+
33
82
  if (source.includes('export function')) flags.push('module');
34
83
 
35
84
  // (await import('node:fs')).writeFileSync('out.wasm', Buffer.from(wasm));
36
85
 
37
86
  times.push(performance.now() - t1);
38
- if (flags.includes('info')) console.log(bold(`compiled in ${times[0].toFixed(2)}ms`));
87
+ if (Prefs.profileCompiler) console.log(bold(`compiled in ${times[0].toFixed(2)}ms`));
39
88
 
40
89
  const t2 = performance.now();
41
90
 
@@ -51,7 +100,10 @@ export default async (source, flags = [ 'module' ], customImports = {}, print =
51
100
  '': {
52
101
  p: valtype === 'i64' ? i => print(Number(i).toString()) : i => print(i.toString()),
53
102
  c: valtype === 'i64' ? i => print(String.fromCharCode(Number(i))) : i => print(String.fromCharCode(i)),
54
- t: _ => performance.now(),
103
+ t: () => performance.now(),
104
+ u: () => performance.timeOrigin,
105
+ y: () => {},
106
+ z: () => {},
55
107
  ...customImports
56
108
  }
57
109
  });
@@ -59,8 +111,10 @@ export default async (source, flags = [ 'module' ], customImports = {}, print =
59
111
  // only backtrace for runner, not test262/etc
60
112
  if (!process.argv[1].includes('/runner')) throw e;
61
113
 
62
- const funcInd = parseInt(e.message.match(/function #([0-9]+) /)[1]);
63
- const blobOffset = parseInt(e.message.split('@')[1]);
114
+ const funcInd = parseInt(e.message.match(/function #([0-9]+) /)?.[1]);
115
+ const blobOffset = parseInt(e.message.split('@')?.[1]);
116
+
117
+ if (!funcInd) throw e;
64
118
 
65
119
  // convert blob offset -> function wasm offset.
66
120
  // this is not good code and is somewhat duplicated
@@ -138,7 +192,7 @@ export default async (source, flags = [ 'module' ], customImports = {}, print =
138
192
  }
139
193
 
140
194
  times.push(performance.now() - t2);
141
- if (flags.includes('info')) console.log(`instantiated in ${times[1].toFixed(2)}ms`);
195
+ if (Prefs.profileCompiler) console.log(`instantiated in ${times[1].toFixed(2)}ms`);
142
196
 
143
197
  const exports = {};
144
198
 
@@ -158,55 +212,11 @@ export default async (source, flags = [ 'module' ], customImports = {}, print =
158
212
 
159
213
  exports[func.name] = function() {
160
214
  try {
161
- const _ret = exp.apply(this, arguments);
162
-
163
- if (_ret == null) return undefined;
164
-
165
- const [ ret, type ] = _ret;
166
-
167
- // if (ret >= typeBase && ret <= typeBase + 8) return ret > (typeBase + 7) ? 'object' : TYPES[ret];
168
-
169
- switch (TYPES[type]) {
170
- case 'boolean': return Boolean(ret);
171
- case 'undefined': return undefined;
172
- case 'object': return ret === 0 ? null : {};
215
+ const ret = exp.apply(this, arguments);
173
216
 
174
- case '_array': {
175
- const pointer = ret;
176
- const length = new Int32Array(memory.buffer, pointer, 1);
217
+ if (ret == null) return undefined;
177
218
 
178
- // have to slice because of memory alignment
179
- const buf = memory.buffer.slice(pointer + 4, pointer + 4 + 8 * length);
180
-
181
- return Array.from(new Float64Array(buf));
182
- }
183
-
184
- case 'string': {
185
- const pointer = ret;
186
- const length = new Int32Array(memory.buffer, pointer, 1);
187
-
188
- return Array.from(new Uint16Array(memory.buffer, pointer + 4, length)).map(x => String.fromCharCode(x)).join('');
189
- }
190
-
191
- case '_bytestring': {
192
- const pointer = ret;
193
- const length = new Int32Array(memory.buffer, pointer, 1);
194
-
195
- return Array.from(new Uint8Array(memory.buffer, pointer + 4, length)).map(x => String.fromCharCode(x)).join('');
196
- }
197
-
198
- case 'function': {
199
- // wasm func index, including all imports
200
- const func = funcs.find(x => (x.originalIndex ?? x.index) === ret);
201
- // if (!func) return ret;
202
- if (!func) return function () {};
203
-
204
- // make fake empty func for repl/etc
205
- return {[func.name]() {}}[func.name];
206
- }
207
-
208
- default: return ret;
209
- }
219
+ return porfToJSValue(memory, funcs, ret[0], ret[1])
210
220
  } catch (e) {
211
221
  if (e.is && e.is(exceptTag)) {
212
222
  const exceptId = e.getArg(exceptTag, 0);
package/package.json CHANGED
@@ -1,21 +1,25 @@
1
1
  {
2
2
  "name": "porffor",
3
3
  "description": "a basic experimental wip aot optimizing js -> wasm engine/compiler/runtime in js",
4
- "version": "0.2.0-6aff0fa",
4
+ "version": "0.2.0-6bc63ef",
5
5
  "author": "CanadaHonk",
6
6
  "license": "MIT",
7
+ "scripts": {
8
+ "precompile": "node ./compiler/precompile.js"
9
+ },
7
10
  "dependencies": {
8
- "acorn": "^8.9.0"
11
+ "acorn": "^8.11.3",
12
+ "node-repl-polyfill": "^0.1.1"
9
13
  },
10
14
  "optionalDependencies": {
11
- "@babel/parser": "^7.23.6",
15
+ "@babel/parser": "^7.24.4",
12
16
  "hermes-parser": "^0.18.2",
13
17
  "meriyah": "^4.3.9"
14
18
  },
15
19
  "bin": {
16
20
  "porf": "./runner/index.js"
17
21
  },
18
- "main": "./runner/index.js",
22
+ "main": "./compiler/wrap.js",
19
23
  "type": "module",
20
24
  "repository": {
21
25
  "type": "git",
@@ -25,4 +29,4 @@
25
29
  "url": "https://github.com/CanadaHonk/porffor/issues"
26
30
  },
27
31
  "homepage": "https://porffor.goose.icu"
28
- }
32
+ }
package/porf CHANGED
@@ -1,2 +1,4 @@
1
1
  #!/bin/sh
2
2
  node runner/index.js "$@"
3
+ # deno run -A runner/index.js "$@"
4
+ # bun runner/index.js "$@"
package/rhemyn/compile.js CHANGED
@@ -1,8 +1,8 @@
1
- import { Blocktype, Opcodes, Valtype, PageSize, ValtypeSize } from '../compiler/wasmSpec.js';
1
+ import { Blocktype, Opcodes, Valtype, ValtypeSize } from '../compiler/wasmSpec.js';
2
2
  import { number } from '../compiler/embedding.js';
3
- import { signedLEB128, unsignedLEB128 } from '../compiler/encoding.js';
4
3
  import parse from './parse.js';
5
4
  import Prefs from '../compiler/prefs.js';
5
+ import { TYPES } from '../compiler/types.js';
6
6
 
7
7
  // local indexes
8
8
  const BasePointer = 0; // base string pointer
@@ -14,7 +14,7 @@ const Length = 5;
14
14
  const Tmp = 6;
15
15
 
16
16
  let exprLastGet = false;
17
- const generate = (node, negated = false, get = true, func = 'test') => {
17
+ const generate = (node, negated = false, get = true, stringSize = 2, func = 'test') => {
18
18
  let out = [];
19
19
  switch (node.type) {
20
20
  case 'Expression':
@@ -42,7 +42,7 @@ const generate = (node, negated = false, get = true, func = 'test') => {
42
42
  // generate checks
43
43
  ...node.body.flatMap((x, i) => {
44
44
  exprLastGet = x.type !== 'Group' && i === (node.body.length - 1);
45
- return generate(x, negated);
45
+ return generate(x, negated, true, stringSize, func);
46
46
  }),
47
47
 
48
48
  // reached end without branching out, successful match
@@ -56,9 +56,9 @@ const generate = (node, negated = false, get = true, func = 'test') => {
56
56
 
57
57
  [ Opcodes.end ],
58
58
 
59
- // increment iter pointer by sizeof i16
59
+ // increment iter pointer by string size
60
60
  [ Opcodes.local_get, IterPointer ],
61
- ...number(ValtypeSize.i16, Valtype.i32),
61
+ ...number(stringSize, Valtype.i32),
62
62
  [ Opcodes.i32_add ],
63
63
  [ Opcodes.local_set, IterPointer ],
64
64
 
@@ -91,34 +91,34 @@ const generate = (node, negated = false, get = true, func = 'test') => {
91
91
  break;
92
92
 
93
93
  case 'Character':
94
- out = generateChar(node, node.negated ^ negated, get);
94
+ out = generateChar(node, node.negated ^ negated, get, stringSize);
95
95
  break;
96
96
 
97
97
  case 'Set':
98
- out = generateSet(node, node.negated, get);
98
+ out = generateSet(node, node.negated, get, stringSize);
99
99
  break;
100
100
 
101
101
  case 'Group':
102
- out = generateGroup(node, negated, get);
102
+ out = generateGroup(node, negated, get, stringSize);
103
103
  break;
104
104
 
105
105
  case 'Range':
106
- out = generateRange(node, negated, get);
106
+ out = generateRange(node, negated, get, stringSize);
107
107
  break;
108
108
  }
109
109
 
110
110
  return out;
111
111
  };
112
112
 
113
- const getNextChar = () => [
113
+ const getNextChar = (stringSize) => [
114
114
  // get char from pointer
115
115
  [ Opcodes.local_get, Pointer ],
116
- [ Opcodes.i32_load16_u, Math.log2(ValtypeSize.i16) - 1, ...unsignedLEB128(0) ],
116
+ [ stringSize == 2 ? Opcodes.i32_load16_u : Opcodes.i32_load8_u, 0, 0 ],
117
117
 
118
118
  ...(exprLastGet ? [] : [
119
- // pointer += sizeof i16
119
+ // pointer += string size
120
120
  [ Opcodes.local_get, Pointer ],
121
- ...number(ValtypeSize.i16, Valtype.i32),
121
+ ...number(stringSize, Valtype.i32),
122
122
  [ Opcodes.i32_add ],
123
123
  [ Opcodes.local_set, Pointer ]
124
124
  ])
@@ -134,21 +134,21 @@ const checkFailure = () => [
134
134
  [ Opcodes.br_if, 0 ]
135
135
  ];
136
136
 
137
- const generateChar = (node, negated, get) => {
137
+ const generateChar = (node, negated, get, stringSize) => {
138
138
  return [
139
- ...(get ? getNextChar() : []),
139
+ ...(get ? getNextChar(stringSize) : []),
140
140
  ...number(node.char.charCodeAt(0), Valtype.i32),
141
141
  negated ? [ Opcodes.i32_eq ] : [ Opcodes.i32_ne ],
142
142
  ...(get ? checkFailure(): [])
143
143
  ];
144
144
  };
145
145
 
146
- const generateSet = (node, negated, get) => {
146
+ const generateSet = (node, negated, get, stringSize) => {
147
147
  // for a single char we do not need a tmp, it is like just
148
148
  const singleChar = node.body.length === 1 && node.body[0].type === 'Character';
149
149
 
150
150
  let out = [
151
- ...(get ? getNextChar() : []),
151
+ ...(get ? getNextChar(stringSize) : []),
152
152
  ...(singleChar ? [] : [ [ Opcodes.local_set, Tmp ] ]),
153
153
  ];
154
154
 
@@ -156,11 +156,11 @@ const generateSet = (node, negated, get) => {
156
156
  out = [
157
157
  ...out,
158
158
  ...(singleChar ? [] : [ [ Opcodes.local_get, Tmp ] ]),
159
- ...generate(x, negated, false)
159
+ ...generate(x, negated, false, stringSize)
160
160
  ];
161
161
  }
162
162
 
163
- out = out.concat(new Array(node.body.length - 1).fill(negated ? [ Opcodes.i32_or ] : [ Opcodes.i32_and ]));
163
+ if (node.body.length > 0) out = out.concat(new Array(node.body.length - 1).fill(negated ? [ Opcodes.i32_or ] : [ Opcodes.i32_and ]));
164
164
 
165
165
  return [
166
166
  ...out,
@@ -168,9 +168,9 @@ const generateSet = (node, negated, get) => {
168
168
  ];
169
169
  };
170
170
 
171
- const generateRange = (node, negated, get) => {
171
+ const generateRange = (node, negated, get, stringSize) => {
172
172
  return [
173
- ...(get ? getNextChar() : []),
173
+ ...(get ? getNextChar(stringSize) : []),
174
174
  ...(get ? [ [ Opcodes.local_tee, Tmp ] ] : []),
175
175
 
176
176
  ...number(node.from.charCodeAt(0), Valtype.i32),
@@ -188,11 +188,29 @@ const generateRange = (node, negated, get) => {
188
188
  };
189
189
 
190
190
  const generateGroup = (node, negated, get) => {
191
+ // todo
192
+ return [];
193
+ };
191
194
 
195
+ const wrapFunc = (regex, func, name, index) => {
196
+ const parsed = parse(regex);
197
+
198
+ return outputFunc([
199
+ [ Opcodes.local_get, 1 ],
200
+ ...number(TYPES.string, Valtype.i32),
201
+ [ Opcodes.i32_eq ],
202
+ [ Opcodes.if, Valtype.i32 ],
203
+ // string
204
+ ...generate(parsed, false, true, 2, func),
205
+ [ Opcodes.else ],
206
+ // bytestring
207
+ ...generate(parsed, false, true, 1, func),
208
+ [ Opcodes.end ]
209
+ ], name, index);
192
210
  };
193
211
 
194
- export const test = (regex, index = 0, name = 'regex_test_' + regex) => outputFunc(generate(parse(regex), false, true, 'test'), name, index);
195
- export const search = (regex, index = 0, name = 'regex_search_' + regex) => outputFunc(generate(parse(regex), false, true, 'search'), name, index);
212
+ export const test = (regex, index = 0, name = 'regex_test_' + regex) => wrapFunc(regex, 'test', name, index);
213
+ export const search = (regex, index = 0, name = 'regex_search_' + regex) => wrapFunc(regex, 'search', name, index);
196
214
 
197
215
  const outputFunc = (wasm, name, index) => ({
198
216
  name,
@@ -200,9 +218,9 @@ const outputFunc = (wasm, name, index) => ({
200
218
  wasm,
201
219
 
202
220
  export: true,
203
- params: [ Valtype.i32 ],
221
+ params: [ Valtype.i32, Valtype.i32 ],
204
222
  returns: [ Valtype.i32 ],
205
- returnType: 0xffffffffffff1, // boolean - todo: do not hardcode this
223
+ returnType: TYPES.boolean,
206
224
  locals: {
207
225
  basePointer: { idx: 0, type: Valtype.i32 },
208
226
  iterPointer: { idx: 1, type: Valtype.i32 },