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 CHANGED
@@ -1,28 +1,34 @@
1
- # porffor
2
- a basic experimental wip *aot* optimizing js -> wasm engine/compiler/runtime in js. not serious/intended for (real) use. (this is a straight forward, honest readme)<br>
3
- age: ~1 month
1
+ # Porffor &nbsp;<sup><sub>/ˈpɔrfɔr/ &nbsp;*(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
- ## design
6
- porffor is a very unique js engine, due a very different approach. it is seriously limited, but what it can do, it does pretty well. key differences:
7
- - 100% aot compiled *(not jit)*
8
- - everything is a number
9
- - no constant runtime/preluded code
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
- porffor is mostly 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.
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
- ## limitations
15
- - no full object support yet
16
- - little built-ins/prototype
17
- - no async/promise/await
18
- - no variables between scopes (except args and globals)
19
- - literal callees only in calls (eg `print()` works, `a = print; a()` does not)
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
- ## supported
22
- see [optimizations](#optimizations) for opts implemented/supported.
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
- ### proposals
25
- these include some early (stage 1/0) and/or dead (last commit years ago) proposals but *I* think they are pretty neat, so.
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
- ### language
39
+ ### Language
34
40
 
35
- - number literals
36
- - declaring functions
37
- - calling functions *literal callees only*
41
+ - Number literals
42
+ - Declaring functions
43
+ - Calling functions *literal callees only*
38
44
  - `return`
39
45
  - `let`/`const`/`var` basic declarations
40
- - some basic integer operators (`+-/*%`)
41
- - some basic integer bitwise operators (`&|`)
42
- - equality operators (`==`, `!=`, etc)
43
- - gt/lt operators (`>`, `<`, `>=`, etc)
44
- - some unary operators (`!`, `+`, `-`)
45
- - logical operators (`&&`, `||`)
46
- - declaring multiple variables in one (`let a, b = 0`)
47
- - global variables (`var`/none in top scope)
48
- - functions returning 1 number
49
- - bool literals as ints (not real type)
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
- - anonymous functions
52
- - setting functions using vars (`const foo = function() { ... }`)
53
- - arrow functions
57
+ - Anonymous functions
58
+ - Setting functions using vars (`const foo = function() { ... }`)
59
+ - Arrow functions
54
60
  - `undefined`/`null` as ints (hack)
55
- - update expressions (`a++`, `++b`, `c--`, etc)
61
+ - Update expressions (`a++`, `++b`, `c--`, etc)
56
62
  - `for` loops (`for (let i = 0; i < N; i++)`, etc)
57
- - hack for "chars" as ints (`'X'` -> `88`)
58
- - *basic* objects (hack)
63
+ - *Basic* objects (hack)
59
64
  - `console.log` (hack)
60
65
  - `while` loops
61
66
  - `break` and `continue`
62
- - named export funcs
63
- - iife support
64
- - assignment operators (`+=`, `-=`, `>>=`, `&&=`, etc)
65
- - conditional/ternary operator (`cond ? a : b`)
66
- - recursive functions
67
- - bare returns (`return`)
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
- - basic `try { ... } catch { ... }` (no error given)
70
- - calling functions with non-matching arguments (eg `f(a, b); f(0); f(1, 2, 3);`)
71
- - `typeof` mostly (static-ish)
72
- - runtime errors for undeclared variables (`ReferenceError`), not functions (`TypeError`)
73
- - array creation via `[]` (eg `let arr = [ 1, 2, 3 ]`)
74
- - array member access via `arr[ind]` (eg `arr[0]`)
75
- - string literals (`'hello world'`)
76
- - string member (char) access via `str[ind]` (eg `str[0]`)
77
- - string concat (`+`) (eg `'a' + 'b'`)
78
- - truthy/falsy (eg `!'' == true`)
79
-
80
- ### built-ins
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
- - 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)
85
- - some `Math` funcs (`Math.sqrt`, `Math.abs`, `Math.floor`, `Math.sign`, `Math.round`, `Math.trunc`, `Math.clz32`, `Math.fround`, `Math.random`) (f64 only)
86
- - basic `globalThis` support
87
- - basic `Boolean` and `Number`
88
- - basic `eval` (literals only)
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
- - some of `performance` (`now()`)
91
- - some of `Array.prototype` (`at`, `push`, `pop`, `shift`)
92
- - some of `String.prototype` (`at`, `charAt`, `charCodeAt`)
93
-
94
- ### custom
95
-
96
- - basic `assert` func
97
- - supports i32, i64, and f64 for valtypes
98
- - wip SIMD api (docs needed)
99
- - intrinsic functions (see below)
100
- - inlining wasm via ``asm`...``\` "macro"
101
-
102
- ## soon todo
103
- - arrays
104
- - member setting (`arr[0] = 2`)
105
- - more of `Array` prototype
106
- - arrays/strings inside arrays
107
- - strings
108
- - member setting
109
- - equality
110
- - more math operators (`**`, etc)
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
- - exceptions
113
- - `try { } finally {}`
114
- - rethrowing inside catch
115
- - optimizations
116
- - rewrite local indexes per func for smallest local header and remove unused idxs
117
- - smarter inline selection (snapshots?)
118
- - remove const ifs (`if (true)`, etc)
119
- - use data segments for initing arrays
120
-
121
- ## test262
122
- 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.
123
-
124
- ## optimizations
125
- mostly for reducing size. 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. (this also enables fast + small runtime use as a potential cursed jit in frontend).
126
-
127
- ### traditional opts
128
- - inlining functions (wip, limited)
129
- - inline const math ops
130
- - tail calls (behind flag `-tail-call`)
131
-
132
- ### wasm transforms
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
+ ![Screenshot of comparison chart](https://github.com/CanadaHonk/porffor/assets/19228318/76c75264-cc68-4be1-8891-c06dc389d97a)
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
- - remove some redundant sets/gets
139
- - remove unneeded single just used vars
140
- - remove unneeded blocks (no `br`s inside)
141
- - remove unused imports
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
- ### wasm module
144
- - type cache/index (no repeated types)
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
- ## codebase
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, the bulk of the effort
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
- ## usecases
171
- basically none (other than giving people headaches). potential as a tiny fast advanced expression evaluator (for math)?
195
+ ## Usecases
196
+ Basically none (other than giving people headaches). Potential ideas to come?
172
197
 
173
- ## usage
174
- basically nothing will work :). see files in `test` for examples.
198
+ ## Usage
199
+ Basically nothing will work :). See files in `test` for examples.
175
200
 
176
- 1. clone repo
201
+ 1. Clone repo
177
202
  2. `npm install`
178
- 3. `node test` to run tests (all should pass)
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
- you can also use deno (`deno run -A ...` instead of `node ...`), or bun (`bun ...` instead of `node ...`)
206
+ You can also use Deno (`deno run -A ...` instead of `node ...`), or Bun (`bun ...` instead of `node ...`).
182
207
 
183
- ### flags
184
- - `-raw` for no info logs (just raw js output)
185
- - `-valtype=i32|i64|f64` to set valtype, f64 by default
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
- - `-funcs` to log funcs (internal representations)
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
- ## vscode extension
200
- 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).
201
-
202
- ## wasm output
203
- porffor optimizes for size as much as possible. current output is ~as small as possible (even with manual asm editing) for some simple functions.
204
-
205
- ### example
206
- this javascript (159 bytes unminified):
207
- ```js
208
- function isPrime(number) {
209
- if (number === 1) return false;
210
-
211
- for (let i = 2; i < number; i++) {
212
- if (number % i == 0) return false;
213
- }
214
-
215
- return true;
216
- }
217
- ```
218
-
219
- compiles into this wasm, in 7.3ms (just compile time), 90 bytes large (including module):
220
- ```wasm
221
- (i32) -> (i32) ;; isPrime
222
- local.get 0 ;; number
223
- i32.const 1
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.)