porffor 0.1.1 → 0.2.0-c6c8c81
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/README.md +184 -204
- package/compiler/2c.js +377 -0
- package/compiler/builtins/base64.js +91 -91
- package/compiler/builtins.js +19 -13
- package/compiler/codeGen.js +1313 -418
- package/compiler/decompile.js +35 -9
- package/compiler/embedding.js +9 -5
- package/compiler/encoding.js +8 -2
- package/compiler/index.js +55 -17
- package/compiler/log.js +15 -0
- package/compiler/opt.js +357 -258
- package/compiler/parse.js +24 -1
- package/compiler/prototype.js +263 -56
- package/compiler/sections.js +51 -8
- package/compiler/wasmSpec.js +3 -0
- package/compiler/wrap.js +20 -6
- package/package.json +6 -1
- package/porf.cmd +1 -1
- package/rhemyn/README.md +37 -0
- package/rhemyn/compile.js +214 -0
- package/rhemyn/parse.js +321 -0
- package/rhemyn/test/parse.js +59 -0
- package/runner/index.js +54 -31
- package/runner/info.js +37 -2
- package/runner/profile.js +1 -2
- package/runner/repl.js +13 -11
- package/runner/results.json +1 -0
- package/runner/transform.js +15 -36
- package/runner/version.js +10 -0
- package/CNAME +0 -1
- package/index.html +0 -1264
- package/logo.png +0 -0
- package/sw.js +0 -26
package/README.md
CHANGED
@@ -1,28 +1,34 @@
|
|
1
|
-
#
|
2
|
-
|
3
|
-
|
1
|
+
# Porffor <sup><sub>/ˈpɔrfɔr/ *(poor-for)*</sup></sub>
|
2
|
+
A from-scratch experimental **AOT** optimizing JS -> Wasm/C engine/compiler/runtime in JS. Not serious/intended for (real) use. (this is a straight forward, honest readme)<br>
|
3
|
+
Age: ~6 months (very on and off)
|
4
4
|
|
5
|
-
##
|
6
|
-
|
7
|
-
- 100%
|
8
|
-
-
|
9
|
-
-
|
10
|
-
- least Wasm imports possible (only stdio)
|
5
|
+
## Design
|
6
|
+
Porffor is a very unique JS engine, due many wildly different approaches. It is seriously limited, but what it can do, it does pretty well. Key differences:
|
7
|
+
- 100% AOT compiled (no JIT)
|
8
|
+
- No constant runtime/preluded code
|
9
|
+
- Least Wasm imports possible (only I/O)
|
11
10
|
|
12
|
-
|
11
|
+
Porffor is primarily built from scratch, the only thing that is not is the parser (using [Acorn](https://github.com/acornjs/acorn)). Binaryen/etc is not used, we make final wasm binaries ourself. You could imagine it as compiling a language which is a sub (some things unsupported) and super (new/custom apis) set of javascript. Not based on any particular spec version, focusing on function/working over spec compliance.
|
13
12
|
|
14
|
-
##
|
15
|
-
-
|
16
|
-
-
|
17
|
-
-
|
18
|
-
-
|
19
|
-
-
|
13
|
+
## Limitations
|
14
|
+
- No full object support yet
|
15
|
+
- Little built-ins/prototype
|
16
|
+
- No async/promise/await
|
17
|
+
- No variables between scopes (except args and globals)
|
18
|
+
- Literal callees only in calls (eg `print()` works, `a = print; a()` does not)
|
19
|
+
- No `eval()` etc (since it is AOT)
|
20
20
|
|
21
|
-
##
|
22
|
-
|
21
|
+
## Rhemyn
|
22
|
+
Rhemyn is Porffor's own regex engine; it compiles literal regex to Wasm bytecode AOT (remind you of anything?). It is quite basic and WIP. See [its readme](rhemyn/README.md) for more details.
|
23
23
|
|
24
|
-
|
25
|
-
|
24
|
+
## 2c
|
25
|
+
2c is Porffor's own Wasm -> C compiler, using generated Wasm bytecode and internal info to generate specific and efficient/fast C code. Little boilerplate/preluded code or required external files, just for CLI binaries (not like wasm2c very much).
|
26
|
+
|
27
|
+
## Supported
|
28
|
+
See [optimizations](#optimizations) for opts implemented/supported.
|
29
|
+
|
30
|
+
### Proposals
|
31
|
+
These include some early (stage 1/0) and/or dead (last commit years ago) proposals but *I* think they are pretty neat, so.
|
26
32
|
|
27
33
|
#### `Math` proposals (stage 1/0)
|
28
34
|
|
@@ -30,124 +36,139 @@ these include some early (stage 1/0) and/or dead (last commit years ago) proposa
|
|
30
36
|
- [`Math` Extensions Proposal](https://github.com/rwaldron/proposal-math-extensions): `Math.scale`, `Math.radians`, `Math.degrees`, `Math.RAD_PER_DEG`, `Math.DEG_PER_RAD` (stage 1 - last commit september 2020)
|
31
37
|
- [`Math.signbit` Proposal](https://github.com/tc39/proposal-Math.signbit): `Math.signbit` (stage 1 - last commit february 2020)
|
32
38
|
|
33
|
-
###
|
39
|
+
### Language
|
34
40
|
|
35
|
-
-
|
36
|
-
-
|
37
|
-
-
|
41
|
+
- Number literals
|
42
|
+
- Declaring functions
|
43
|
+
- Calling functions *literal callees only*
|
38
44
|
- `return`
|
39
45
|
- `let`/`const`/`var` basic declarations
|
40
|
-
-
|
41
|
-
-
|
42
|
-
-
|
43
|
-
-
|
44
|
-
-
|
45
|
-
-
|
46
|
-
-
|
47
|
-
-
|
48
|
-
-
|
49
|
-
-
|
46
|
+
- Some basic integer operators (`+-/*%`)
|
47
|
+
- Some basic integer bitwise operators (`&|`)
|
48
|
+
- Equality operators (`==`, `!=`, etc)
|
49
|
+
- GT/LT operators (`>`, `<`, `>=`, etc)
|
50
|
+
- Some unary operators (`!`, `+`, `-`)
|
51
|
+
- Logical operators (`&&`, `||`)
|
52
|
+
- Declaring multiple variables in one (`let a, b = 0`)
|
53
|
+
- Global variables (`var`/none in top scope)
|
54
|
+
- Functions returning 1 number
|
55
|
+
- Bool literals as ints (not real type)
|
50
56
|
- `if` and `if ... else`
|
51
|
-
-
|
52
|
-
-
|
53
|
-
-
|
57
|
+
- Anonymous functions
|
58
|
+
- Setting functions using vars (`const foo = function() { ... }`)
|
59
|
+
- Arrow functions
|
54
60
|
- `undefined`/`null` as ints (hack)
|
55
|
-
-
|
61
|
+
- Update expressions (`a++`, `++b`, `c--`, etc)
|
56
62
|
- `for` loops (`for (let i = 0; i < N; i++)`, etc)
|
57
|
-
-
|
58
|
-
- *basic* objects (hack)
|
63
|
+
- *Basic* objects (hack)
|
59
64
|
- `console.log` (hack)
|
60
65
|
- `while` loops
|
61
66
|
- `break` and `continue`
|
62
|
-
-
|
63
|
-
-
|
64
|
-
-
|
65
|
-
-
|
66
|
-
-
|
67
|
-
-
|
67
|
+
- Named export funcs
|
68
|
+
- IIFE support
|
69
|
+
- Assignment operators (`+=`, `-=`, `>>=`, `&&=`, etc)
|
70
|
+
- Conditional/ternary operator (`cond ? a : b`)
|
71
|
+
- Recursive functions
|
72
|
+
- Bare returns (`return`)
|
68
73
|
- `throw` (literals only)
|
69
|
-
-
|
70
|
-
-
|
71
|
-
- `typeof`
|
72
|
-
-
|
73
|
-
-
|
74
|
-
-
|
75
|
-
-
|
76
|
-
-
|
77
|
-
-
|
78
|
-
-
|
79
|
-
|
80
|
-
|
74
|
+
- Basic `try { ... } catch { ... }` (no error given)
|
75
|
+
- Calling functions with non-matching arguments (eg `f(a, b); f(0); f(1, 2, 3);`)
|
76
|
+
- `typeof`
|
77
|
+
- Runtime errors for undeclared variables (`ReferenceError`), not functions (`TypeError`)
|
78
|
+
- Array creation via `[]` (eg `let arr = [ 1, 2, 3 ]`)
|
79
|
+
- Array member access via `arr[ind]` (eg `arr[0]`)
|
80
|
+
- String literals (`'hello world'`)
|
81
|
+
- String member (char) access via `str[ind]` (eg `str[0]`)
|
82
|
+
- String concat (`+`) (eg `'a' + 'b'`)
|
83
|
+
- Truthy/falsy (eg `!'' == true`)
|
84
|
+
- String comparison (eg `'a' == 'a'`, `'a' != 'b'`)
|
85
|
+
- Nullish coalescing operator (`??`)
|
86
|
+
- `for...of` (arrays and strings)
|
87
|
+
- Array member setting (`arr[0] = 2`, `arr[0] += 2`, etc)
|
88
|
+
- Array constructor (`Array(5)`, `new Array(1, 2, 3)`)
|
89
|
+
|
90
|
+
### Built-ins
|
81
91
|
|
82
92
|
- `NaN` and `Infinity` (f64 only)
|
83
93
|
- `isNaN()` and `isFinite()` (f64 only)
|
84
|
-
-
|
85
|
-
-
|
86
|
-
-
|
87
|
-
-
|
88
|
-
-
|
94
|
+
- Most of `Number` (`MAX_VALUE`, `MIN_VALUE`, `MAX_SAFE_INTEGER`, `MIN_SAFE_INTEGER`, `POSITIVE_INFINITY`, `NEGATIVE_INFINITY`, `EPSILON`, `NaN`, `isNaN`, `isFinite`, `isInteger`, `isSafeInteger`) (some f64 only)
|
95
|
+
- Some `Math` funcs (`Math.sqrt`, `Math.abs`, `Math.floor`, `Math.sign`, `Math.round`, `Math.trunc`, `Math.clz32`, `Math.fround`, `Math.random`) (f64 only)
|
96
|
+
- Basic `globalThis` support
|
97
|
+
- Basic `Boolean` and `Number`
|
98
|
+
- Basic `eval` for literals
|
89
99
|
- `Math.random()` using self-made xorshift128+ PRNG
|
90
|
-
-
|
91
|
-
-
|
92
|
-
-
|
93
|
-
|
94
|
-
###
|
95
|
-
|
96
|
-
-
|
97
|
-
-
|
98
|
-
-
|
99
|
-
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
-
|
106
|
-
-
|
107
|
-
-
|
108
|
-
|
109
|
-
-
|
110
|
-
-
|
100
|
+
- Some of `performance` (`now()`)
|
101
|
+
- Some of `Array.prototype` (`at`, `push`, `pop`, `shift`, `fill`)
|
102
|
+
- Some of `String.prototype` (`at`, `charAt`, `charCodeAt`)
|
103
|
+
|
104
|
+
### Custom
|
105
|
+
|
106
|
+
- Supports i32, i64, and f64 for valtypes
|
107
|
+
- Start of a SIMD api (docs needed)
|
108
|
+
- Intrinsic functions (see below)
|
109
|
+
- Inlining wasm via ``asm`...``\` "macro"
|
110
|
+
|
111
|
+
## Todo
|
112
|
+
No particular order and no guarentees, just what could happen soon™
|
113
|
+
|
114
|
+
- Arrays
|
115
|
+
- More of `Array` prototype
|
116
|
+
- Arrays/strings inside arrays
|
117
|
+
- Destructuring
|
118
|
+
- Objects
|
119
|
+
- Basic object expressions (eg `{}`, `{ a: 0 }`)
|
120
|
+
- Wasm
|
121
|
+
- *Basic* Wasm engine (interpreter) in js
|
122
|
+
- More math operators (`**`, etc)
|
111
123
|
- `do { ... } while (...)`
|
112
|
-
-
|
113
|
-
|
114
|
-
-
|
115
|
-
-
|
116
|
-
-
|
117
|
-
|
118
|
-
-
|
119
|
-
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
-
|
130
|
-
|
131
|
-
|
132
|
-
|
124
|
+
- Rewrite `console.log` to work with strings/arrays
|
125
|
+
- Exceptions
|
126
|
+
- Rewrite to use actual strings (optional?)
|
127
|
+
- `try { } finally { }`
|
128
|
+
- Rethrowing inside catch
|
129
|
+
- Optimizations
|
130
|
+
- Rewrite local indexes per func for smallest local header and remove unused idxs
|
131
|
+
- Smarter inline selection (snapshots?)
|
132
|
+
- Remove const ifs (`if (true)`, etc)
|
133
|
+
- Use type(script) information to remove unneeded typechecker code
|
134
|
+
|
135
|
+
## Performance
|
136
|
+
*For the things it supports most of the time*, Porffor is blazingly fast compared to most interpreters, and common engines running without JIT. For those with JIT, it is not that much slower like a traditional interpreter would be; mostly the same or a bit faster/slower depending on what.
|
137
|
+
|
138
|
+

|
139
|
+
|
140
|
+
## Optimizations
|
141
|
+
Mostly for reducing size. I do not really care about compiler perf/time as long as it is reasonable. We do not use/rely on external opt tools (`wasm-opt`, etc), instead doing optimization inside the compiler itself creating even smaller code sizes than `wasm-opt` itself can produce as we have more internal information.
|
142
|
+
|
143
|
+
### Traditional opts
|
144
|
+
- Inlining functions (WIP, limited)
|
145
|
+
- Inline const math ops
|
146
|
+
- Tail calls (behind flag `-tail-call`)
|
147
|
+
|
148
|
+
### Wasm transforms
|
133
149
|
- `local.set`, `local.get` -> `local.tee`
|
134
150
|
- `i32.const 0`, `i32.eq` -> `i32.eqz`
|
135
151
|
- `i64.extend_i32_s`, `i32.wrap_i64` -> ``
|
136
152
|
- `f64.convert_i32_u`, `i32.trunc_sat_f64_s` -> ``
|
137
153
|
- `return`, `end` -> `end`
|
138
|
-
-
|
139
|
-
-
|
140
|
-
-
|
141
|
-
-
|
154
|
+
- Change const, convert to const of converted valtype (eg `f64.const`, `i32.trunc_sat_f64_s -> `i32.const`)
|
155
|
+
- Remove some redundant sets/gets
|
156
|
+
- Remove unneeded single just used vars
|
157
|
+
- Remove unneeded blocks (no `br`s inside)
|
158
|
+
- Remove unused imports
|
159
|
+
- Use data segments for initing arrays/strings
|
160
|
+
|
161
|
+
### Wasm module
|
162
|
+
- Type cache/index (no repeated types)
|
163
|
+
- No main func if empty (and other exports)
|
142
164
|
|
143
|
-
|
144
|
-
|
145
|
-
- no main func if empty (and other exports)
|
165
|
+
## Test262
|
166
|
+
Porffor can run Test262 via some hacks/transforms which remove unsupported features whilst still doing the same asserts (eg simpler error messages using literals only). It currently passes >10% (see latest commit desc for latest and details). Use `node test262` to test, it will also show a difference of overall results between the last commit and current results.
|
146
167
|
|
147
|
-
##
|
168
|
+
## Codebase
|
148
169
|
- `compiler`: contains the compiler itself
|
149
170
|
- `builtins.js`: all built-ins of the engine (spec, custom. vars, funcs)
|
150
|
-
- `codeGen.js`: code (wasm) generation, ast -> wasm
|
171
|
+
- `codeGen.js`: code (wasm) generation, ast -> wasm. The bulk of the effort
|
151
172
|
- `decompile.js`: basic wasm decompiler for debug info
|
152
173
|
- `embedding.js`: utils for embedding consts
|
153
174
|
- `encoding.js`: utils for encoding things as bytes as wasm expects
|
@@ -164,25 +185,34 @@ mostly for reducing size. do not really care about compiler perf/time as long as
|
|
164
185
|
- `info.js`: runs with extra info printed
|
165
186
|
- `repl.js`: basic repl (uses `node:repl`)
|
166
187
|
|
188
|
+
- `rhemyn`: contains [Rhemyn](#rhemyn) - our regex engine (used by Porffor)
|
189
|
+
- `compile.js`: compiles regex ast into wasm bytecode
|
190
|
+
- `parse.js`: own regex parser
|
191
|
+
|
167
192
|
- `test`: contains many test files for majority of supported features
|
168
193
|
- `test262`: test262 runner and utils
|
169
194
|
|
170
|
-
##
|
171
|
-
|
195
|
+
## Usecases
|
196
|
+
Basically none (other than giving people headaches). Potential ideas to come?
|
172
197
|
|
173
|
-
##
|
174
|
-
|
198
|
+
## Usage
|
199
|
+
Basically nothing will work :). See files in `test` for examples.
|
175
200
|
|
176
|
-
1.
|
201
|
+
1. Clone repo
|
177
202
|
2. `npm install`
|
178
|
-
3. `node test` to run tests (
|
203
|
+
3. `node test` to run tests (some will fail)
|
179
204
|
4. `node runner path/to/code.js` to run a file (or `node runner` to use wip repl)
|
180
205
|
|
181
|
-
|
206
|
+
You can also use Deno (`deno run -A ...` instead of `node ...`), or Bun (`bun ...` instead of `node ...`).
|
182
207
|
|
183
|
-
###
|
184
|
-
- `-
|
185
|
-
- `-
|
208
|
+
### Options
|
209
|
+
- `-target=wasm|c|native` (default: `wasm`) to set target output (native compiles c output to binary, see args below)
|
210
|
+
- `-target=c|native` only:
|
211
|
+
- `-o=out.c|out.exe|out` to set file to output c or binary
|
212
|
+
- `-target=native` only:
|
213
|
+
- `-compiler=clang` to set compiler binary (path/name) to use to compile
|
214
|
+
- `-cO=O3` to set compiler opt argument
|
215
|
+
- `-valtype=i32|i64|f64` (default: `f64`) to set valtype
|
186
216
|
- `-O0` to disable opt
|
187
217
|
- `-O1` (default) to enable basic opt (simplify insts, treeshake wasm imports)
|
188
218
|
- `-O2` to enable advanced opt (inlining)
|
@@ -190,86 +220,36 @@ you can also use deno (`deno run -A ...` instead of `node ...`), or bun (`bun ..
|
|
190
220
|
- `-no-run` to not run wasm output, just compile
|
191
221
|
- `-opt-log` to log some opts
|
192
222
|
- `-code-log` to log some codegen (you probably want `-funcs`)
|
193
|
-
- `-
|
223
|
+
- `-regex-log` to log some regex
|
224
|
+
- `-funcs` to log funcs
|
225
|
+
- `-ast-log` to log AST
|
194
226
|
- `-opt-funcs` to log funcs after opt
|
195
227
|
- `-sections` to log sections as hex
|
196
228
|
- `-opt-no-inline` to not inline any funcs
|
197
|
-
- `-tail-call` to enable tail calls (not widely implemented)
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
i32.eq
|
225
|
-
if ;; label @2
|
226
|
-
i32.const 0
|
227
|
-
return
|
228
|
-
end
|
229
|
-
i32.const 2
|
230
|
-
local.set 1 ;; i
|
231
|
-
loop ;; label @2
|
232
|
-
local.get 1 ;; i
|
233
|
-
local.get 0 ;; number
|
234
|
-
i32.lt_s
|
235
|
-
if ;; label @3
|
236
|
-
local.get 0 ;; number
|
237
|
-
local.get 1 ;; i
|
238
|
-
i32.rem_s
|
239
|
-
i32.eqz
|
240
|
-
if ;; label @4
|
241
|
-
i32.const 0
|
242
|
-
return
|
243
|
-
end
|
244
|
-
local.get 1 ;; i
|
245
|
-
i32.const 1
|
246
|
-
i32.add
|
247
|
-
local.set 1 ;; i
|
248
|
-
br 1 ;; goto @2
|
249
|
-
end
|
250
|
-
end
|
251
|
-
i32.const 1
|
252
|
-
end
|
253
|
-
```
|
254
|
-
|
255
|
-
## isn't this the same as assemblyscript?
|
256
|
-
no. they are not alike at all internally and have different goals/ideals:
|
257
|
-
- porffor is made as a generic js engine, not for wasm stuff specifically
|
258
|
-
- porffor takes in js, not a different language or typescript like assemblyscript
|
259
|
-
- porffor is made in pure js and compiles itself, not using binaryen/etc
|
260
|
-
- (also I didn't know it existed when I started this)
|
261
|
-
|
262
|
-
## faq
|
263
|
-
|
264
|
-
### 1. why name
|
265
|
-
`purple` in Welsh is `porffor`. why purple?
|
266
|
-
- no other js engine is purple colored
|
267
|
-
- purple is pretty cool
|
268
|
-
- purple apparently represents "ambition", which is.. one word to describe this project
|
269
|
-
- the hard to speak name is also the noise your brain makes in reaction to this idea
|
270
|
-
|
271
|
-
### 2. why at all
|
272
|
-
yes.
|
273
|
-
|
274
|
-
### 3. but what about spec compliance?
|
275
|
-
lol, no. (sorry.)
|
229
|
+
- `-tail-call` to enable tail calls (experimental + not widely implemented)
|
230
|
+
- `-compile-hints` to enable V8 compilation hints (experimental + doesn't seem to do much?)
|
231
|
+
|
232
|
+
## VSCode extension
|
233
|
+
There is a vscode extension in `porffor-for-vscode` which tweaks js syntax highlighting to be nicer with porffor features (eg highlighting wasm inside of inline asm).
|
234
|
+
|
235
|
+
## Isn't this the same as AssemblyScript/other Wasm langs?
|
236
|
+
No. they are not alike at all internally and have very different goals/ideals:
|
237
|
+
- Porffor is made as a generic JS engine, not for Wasm stuff specifically
|
238
|
+
- Porffor takes in JS, not a different language or typescript
|
239
|
+
- Porffor is made in pure JS and compiles itself, not using Binaryen/etc
|
240
|
+
- (Also I didn't know it existed when I started this, lol)
|
241
|
+
|
242
|
+
## FAQ
|
243
|
+
|
244
|
+
### 1. Why the name?
|
245
|
+
`purple` in Welsh is `porffor`. Why purple?
|
246
|
+
- No other js engine is purple colored
|
247
|
+
- Purple is pretty cool
|
248
|
+
- Purple apparently represents "ambition", which is.. one word to describe this project
|
249
|
+
- The hard to speak name is also the noise your brain makes in reaction to this idea!
|
250
|
+
|
251
|
+
### 2. Why at all?
|
252
|
+
Yes!
|
253
|
+
|
254
|
+
### 3. But what about spec compliance?
|
255
|
+
Lol, no. (sorry.)
|