mirror of
https://github.com/fluencelabs/assemblyscript
synced 2025-04-25 07:02:13 +00:00
Initial commit
This commit is contained in:
commit
1d53303b47
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
node_modules/
|
||||||
|
npm-debug.*
|
||||||
|
out/
|
25
README.md
Normal file
25
README.md
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
AssemblyScript NEXT
|
||||||
|
===================
|
||||||
|
|
||||||
|
This repository contains compiler component prototypes for the next iteration of the AssemblyScript compiler written in AssemblyScript itself.
|
||||||
|
|
||||||
|
Note that the code uses some features and standard library components that are not yet supported by any version of asc. To account for this, the code has been written in "portable AssemblyScript", a TypeScript-compatible subset of a subset of a superset of JavaScript, that also compiles to JavaScript using TSC.
|
||||||
|
|
||||||
|
Why is this necessary?
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
Well, it isn't, but: In order to be able to compile the AssemblyScript compiler itself to WebAssembly eventually, we cannot depend on TypeScript because it is written in vanilla TypeScript and makes use of quite a few non-AOT-compatible dynamic features of JavaScript.
|
||||||
|
|
||||||
|
Cons:
|
||||||
|
- A lot of work
|
||||||
|
- Dealing with TypeScript compatibility issues
|
||||||
|
|
||||||
|
Pros:
|
||||||
|
- One day compiling to WebAssembly for performance
|
||||||
|
- Necessary features only, reducing binary size
|
||||||
|
- Linking against Binaryen compiled to WebAssembly, reducing overhead
|
||||||
|
|
||||||
|
Side effects:
|
||||||
|
- Good fire test for the compiler
|
||||||
|
- Good benchmark when comparing both versions
|
||||||
|
- Benefits standard library design ideas
|
219
package-lock.json
generated
Normal file
219
package-lock.json
generated
Normal file
@ -0,0 +1,219 @@
|
|||||||
|
{
|
||||||
|
"requires": true,
|
||||||
|
"lockfileVersion": 1,
|
||||||
|
"dependencies": {
|
||||||
|
"@types/chalk": {
|
||||||
|
"version": "0.4.31",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/chalk/-/chalk-0.4.31.tgz",
|
||||||
|
"integrity": "sha1-ox10JBprHtu5c8822XooloNKUfk=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"@types/diff": {
|
||||||
|
"version": "3.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/diff/-/diff-3.2.2.tgz",
|
||||||
|
"integrity": "sha512-q3zfJvaTroV5BjAAR+peTHEGAAhGrPX0z2EzCzpt2mwFA+qzUn2nigJLqSekXRtdULKmT8am7zjvTMZSapIgHw==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"@types/long": {
|
||||||
|
"version": "3.0.32",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/long/-/long-3.0.32.tgz",
|
||||||
|
"integrity": "sha512-ZXyOOm83p7X8p3s0IYM3VeueNmHpkk/yMlP8CLeOnEcu6hIwPH7YjZBvhQkR0ZFS2DqZAxKtJ/M5fcuv3OU5BA==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"@types/node": {
|
||||||
|
"version": "8.0.28",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-8.0.28.tgz",
|
||||||
|
"integrity": "sha512-HupkFXEv3O3KSzcr3Ylfajg0kaerBg1DyaZzRBBQfrU3NN1mTBRE7sCveqHwXLS5Yrjvww8qFzkzYQQakG9FuQ=="
|
||||||
|
},
|
||||||
|
"ansi-styles": {
|
||||||
|
"version": "3.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz",
|
||||||
|
"integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"color-convert": "1.9.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"arrify": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz",
|
||||||
|
"integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"binaryen": {
|
||||||
|
"version": "37.0.0-nightly.20170912",
|
||||||
|
"resolved": "https://registry.npmjs.org/binaryen/-/binaryen-37.0.0-nightly.20170912.tgz",
|
||||||
|
"integrity": "sha512-yXLgHkUvTMqEV1vkixAaldnS4w6WOrqY+30Cx9k+JjBzmA6wJTQr0F32xFg/4MPr4NRZOdP/AnI8ais4nhfgIw=="
|
||||||
|
},
|
||||||
|
"chalk": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.1.0.tgz",
|
||||||
|
"integrity": "sha512-LUHGS/dge4ujbXMJrnihYMcL4AoOweGnw9Tp3kQuqy1Kx5c1qKjqvMJZ6nVJPMWJtKCTN72ZogH3oeSO9g9rXQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"ansi-styles": "3.2.0",
|
||||||
|
"escape-string-regexp": "1.0.5",
|
||||||
|
"supports-color": "4.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"color-convert": {
|
||||||
|
"version": "1.9.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.0.tgz",
|
||||||
|
"integrity": "sha1-Gsz5fdc5uYO/mU1W/sj5WFNkG3o=",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"color-name": "1.1.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"color-name": {
|
||||||
|
"version": "1.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
|
||||||
|
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"diff": {
|
||||||
|
"version": "3.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/diff/-/diff-3.3.1.tgz",
|
||||||
|
"integrity": "sha512-MKPHZDMB0o6yHyDryUOScqZibp914ksXwAMYMTHj6KO8UeKsRYNJD3oNCKjTqZon+V488P7N/HzXF8t7ZR95ww==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"escape-string-regexp": {
|
||||||
|
"version": "1.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
|
||||||
|
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"has-flag": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz",
|
||||||
|
"integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"long": {
|
||||||
|
"version": "3.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/long/-/long-3.2.0.tgz",
|
||||||
|
"integrity": "sha1-2CG3E4yhy1gcFymQ7xTbIAtcR0s=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"make-error": {
|
||||||
|
"version": "1.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.0.tgz",
|
||||||
|
"integrity": "sha1-Uq06M5zPEM5itAQLcI/nByRLi5Y=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"minimist": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
|
||||||
|
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"mkdirp": {
|
||||||
|
"version": "0.5.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
|
||||||
|
"integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"minimist": "0.0.8"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"minimist": {
|
||||||
|
"version": "0.0.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
|
||||||
|
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
|
||||||
|
"dev": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"source-map": {
|
||||||
|
"version": "0.5.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
|
||||||
|
"integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"source-map-support": {
|
||||||
|
"version": "0.4.18",
|
||||||
|
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz",
|
||||||
|
"integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"source-map": "0.5.7"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"strip-bom": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
|
||||||
|
"integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"strip-json-comments": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
|
||||||
|
"integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"supports-color": {
|
||||||
|
"version": "4.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.4.0.tgz",
|
||||||
|
"integrity": "sha512-rKC3+DyXWgK0ZLKwmRsrkyHVZAjNkfzeehuFWdGGcqGDTZFH73+RH6S/RDAAxl9GusSjZSUWYLmT9N5pzXFOXQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"has-flag": "2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ts-node": {
|
||||||
|
"version": "3.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-3.3.0.tgz",
|
||||||
|
"integrity": "sha1-wTxqMCTjC+EYDdUwOPwgkonUv2k=",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"arrify": "1.0.1",
|
||||||
|
"chalk": "2.1.0",
|
||||||
|
"diff": "3.3.1",
|
||||||
|
"make-error": "1.3.0",
|
||||||
|
"minimist": "1.2.0",
|
||||||
|
"mkdirp": "0.5.1",
|
||||||
|
"source-map-support": "0.4.18",
|
||||||
|
"tsconfig": "6.0.0",
|
||||||
|
"v8flags": "3.0.0",
|
||||||
|
"yn": "2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"tsconfig": {
|
||||||
|
"version": "6.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/tsconfig/-/tsconfig-6.0.0.tgz",
|
||||||
|
"integrity": "sha1-aw6DdgA9evGGT434+J3QBZ/80DI=",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"strip-bom": "3.0.0",
|
||||||
|
"strip-json-comments": "2.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"typescript": {
|
||||||
|
"version": "2.5.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-2.5.2.tgz",
|
||||||
|
"integrity": "sha1-A4qV99m7tCCxvzW6MdTFwd0//jQ=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"user-home": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/user-home/-/user-home-1.1.1.tgz",
|
||||||
|
"integrity": "sha1-K1viOjK2Onyd640PKNSFcko98ZA=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"v8flags": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/v8flags/-/v8flags-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-AGl+C+4qpeSu2g3JxCD/mGFFOs/vVZ3XREkD3ibQXEqr4Y4zgIrPWW124/IKJFHOIVFIoH8miWrLf0o84HYjwA==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"user-home": "1.1.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"yn": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/yn/-/yn-2.0.0.tgz",
|
||||||
|
"integrity": "sha1-5a2ryKz0CPY4X8dklWhMiOavaJo=",
|
||||||
|
"dev": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
16
package.json
Normal file
16
package.json
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/chalk": "^0.4.31",
|
||||||
|
"@types/diff": "^3.2.2",
|
||||||
|
"@types/long": "^3.0.32",
|
||||||
|
"chalk": "^2.1.0",
|
||||||
|
"diff": "^3.3.1",
|
||||||
|
"long": "^3.2.0",
|
||||||
|
"ts-node": "^3.3.0",
|
||||||
|
"typescript": "^2.5.2"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@types/node": "^8.0.28",
|
||||||
|
"binaryen": "37.0.0-nightly.20170912"
|
||||||
|
}
|
||||||
|
}
|
32
scripts/build-diagnostics.js
Normal file
32
scripts/build-diagnostics.js
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
var fs = require("fs");
|
||||||
|
|
||||||
|
var messages = require(__dirname + "/../src/diagnosticMessages.json");
|
||||||
|
|
||||||
|
var sb = [ "// code below is generated from diagnosticsMessages.json by scripts/build-diagnostics\n\n" ];
|
||||||
|
|
||||||
|
function makeKey(text) {
|
||||||
|
return text.replace(/[^\w]+/g, "_").replace(/_+$/, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
sb.push("export enum DiagnosticCode {\n");
|
||||||
|
|
||||||
|
var first = true;
|
||||||
|
Object.keys(messages).forEach(text => {
|
||||||
|
var key = makeKey(text);
|
||||||
|
if (first)
|
||||||
|
first = false;
|
||||||
|
else {
|
||||||
|
sb.push(",\n");
|
||||||
|
}
|
||||||
|
sb.push(" " + key + " = " + messages[text]);
|
||||||
|
});
|
||||||
|
|
||||||
|
sb.push("\n}\n\nexport function diagnosticCodeToString(code: DiagnosticCode): string {\n switch (code) {\n");
|
||||||
|
|
||||||
|
Object.keys(messages).forEach(text => {
|
||||||
|
sb.push(" case " + messages[text] + ": return " + JSON.stringify(text) + ";\n");
|
||||||
|
});
|
||||||
|
|
||||||
|
sb.push(" default: return \"\";\n }\n}\n");
|
||||||
|
|
||||||
|
fs.writeFileSync(__dirname + "/../src/diagnosticMessages.generated.ts", sb.join(""), { encoding: "utf8" });
|
1785
src/ast.ts
Normal file
1785
src/ast.ts
Normal file
File diff suppressed because it is too large
Load Diff
252
src/binaryen.d.ts
vendored
Normal file
252
src/binaryen.d.ts
vendored
Normal file
@ -0,0 +1,252 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
Binaryen's C-API.
|
||||||
|
|
||||||
|
The WebAssembly version of the compiler will be linked against Binaryen
|
||||||
|
compiled to WebAssembly with these functions present in the binary while the
|
||||||
|
JS version calls them on the global object.
|
||||||
|
|
||||||
|
see: https://github.com/WebAssembly/binaryen/blob/master/src/binaryen-c.h
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare function _malloc(size: usize): usize;
|
||||||
|
declare function _free(ptr: usize): void;
|
||||||
|
|
||||||
|
declare type BinaryenIndex = u32;
|
||||||
|
|
||||||
|
declare type BinaryenType = u32;
|
||||||
|
|
||||||
|
declare function _BinaryenNone(): BinaryenType;
|
||||||
|
declare function _BinaryenInt32(): BinaryenType;
|
||||||
|
declare function _BinaryenInt64(): BinaryenType;
|
||||||
|
declare function _BinaryenFloat32(): BinaryenType;
|
||||||
|
declare function _BinaryenFloat64(): BinaryenType;
|
||||||
|
declare function _BinaryenUndefined(): BinaryenType;
|
||||||
|
|
||||||
|
declare type BinaryenModuleRef = usize;
|
||||||
|
|
||||||
|
declare function _BinaryenModuleCreate(): BinaryenModuleRef;
|
||||||
|
declare function _BinaryenModuleDispose(module: BinaryenModuleRef): void;
|
||||||
|
|
||||||
|
declare type BinaryenFunctionTypeRef = usize;
|
||||||
|
declare type CString = usize;
|
||||||
|
declare type CArray<T> = usize;
|
||||||
|
|
||||||
|
declare function _BinaryenAddFunctionType(module: BinaryenModuleRef, name: CString, result: BinaryenType, paramTypes: CArray<BinaryenType>, numParams: BinaryenIndex): BinaryenFunctionTypeRef;
|
||||||
|
|
||||||
|
declare type BinaryenLiteral = CArray<u8>;
|
||||||
|
|
||||||
|
// LLVM C ABI with `out` being a buffer of 16 bytes receiving the BinaryenLiteral struct
|
||||||
|
// union value starts at offset 8 due to alignment (?)
|
||||||
|
declare function _BinaryenLiteralInt32(out: BinaryenLiteral, x: i32): void;
|
||||||
|
declare function _BinaryenLiteralInt64(out: BinaryenLiteral, x: i32, y: i32): void;
|
||||||
|
declare function _BinaryenLiteralFloat32(out: BinaryenLiteral, x: f32): void;
|
||||||
|
declare function _BinaryenLiteralFloat64(out: BinaryenLiteral, x: f64): void;
|
||||||
|
declare function _BinaryenLiteralFloat32Bits(out: BinaryenLiteral, x: i32): void;
|
||||||
|
declare function _BinaryenLiteralFloat64Bits(out: BinaryenLiteral, x: i32, y: i32): void;
|
||||||
|
|
||||||
|
declare type BinaryenOp = i32;
|
||||||
|
|
||||||
|
declare function _BinaryenClzInt32(): BinaryenOp;
|
||||||
|
declare function _BinaryenCtzInt32(): BinaryenOp;
|
||||||
|
declare function _BinaryenPopcntInt32(): BinaryenOp;
|
||||||
|
declare function _BinaryenNegFloat32(): BinaryenOp;
|
||||||
|
declare function _BinaryenAbsFloat32(): BinaryenOp;
|
||||||
|
declare function _BinaryenCeilFloat32(): BinaryenOp;
|
||||||
|
declare function _BinaryenFloorFloat32(): BinaryenOp;
|
||||||
|
declare function _BinaryenTruncFloat32(): BinaryenOp;
|
||||||
|
declare function _BinaryenNearestFloat32(): BinaryenOp;
|
||||||
|
declare function _BinaryenSqrtFloat32(): BinaryenOp;
|
||||||
|
declare function _BinaryenEqZInt32(): BinaryenOp;
|
||||||
|
declare function _BinaryenClzInt64(): BinaryenOp;
|
||||||
|
declare function _BinaryenCtzInt64(): BinaryenOp;
|
||||||
|
declare function _BinaryenPopcntInt64(): BinaryenOp;
|
||||||
|
declare function _BinaryenNegFloat64(): BinaryenOp;
|
||||||
|
declare function _BinaryenAbsFloat64(): BinaryenOp;
|
||||||
|
declare function _BinaryenCeilFloat64(): BinaryenOp;
|
||||||
|
declare function _BinaryenFloorFloat64(): BinaryenOp;
|
||||||
|
declare function _BinaryenTruncFloat64(): BinaryenOp;
|
||||||
|
declare function _BinaryenNearestFloat64(): BinaryenOp;
|
||||||
|
declare function _BinaryenSqrtFloat64(): BinaryenOp;
|
||||||
|
declare function _BinaryenEqZInt64(): BinaryenOp;
|
||||||
|
declare function _BinaryenExtendSInt32(): BinaryenOp;
|
||||||
|
declare function _BinaryenExtendUInt32(): BinaryenOp;
|
||||||
|
declare function _BinaryenWrapInt64(): BinaryenOp;
|
||||||
|
declare function _BinaryenTruncSFloat32ToInt32(): BinaryenOp;
|
||||||
|
declare function _BinaryenTruncSFloat32ToInt64(): BinaryenOp;
|
||||||
|
declare function _BinaryenTruncUFloat32ToInt32(): BinaryenOp;
|
||||||
|
declare function _BinaryenTruncUFloat32ToInt64(): BinaryenOp;
|
||||||
|
declare function _BinaryenTruncSFloat64ToInt32(): BinaryenOp;
|
||||||
|
declare function _BinaryenTruncSFloat64ToInt64(): BinaryenOp;
|
||||||
|
declare function _BinaryenTruncUFloat64ToInt32(): BinaryenOp;
|
||||||
|
declare function _BinaryenTruncUFloat64ToInt64(): BinaryenOp;
|
||||||
|
declare function _BinaryenReinterpretFloat32(): BinaryenOp;
|
||||||
|
declare function _BinaryenReinterpretFloat64(): BinaryenOp;
|
||||||
|
declare function _BinaryenConvertSInt32ToFloat32(): BinaryenOp;
|
||||||
|
declare function _BinaryenConvertSInt32ToFloat64(): BinaryenOp;
|
||||||
|
declare function _BinaryenConvertUInt32ToFloat32(): BinaryenOp;
|
||||||
|
declare function _BinaryenConvertUInt32ToFloat64(): BinaryenOp;
|
||||||
|
declare function _BinaryenConvertSInt64ToFloat32(): BinaryenOp;
|
||||||
|
declare function _BinaryenConvertSInt64ToFloat64(): BinaryenOp;
|
||||||
|
declare function _BinaryenConvertUInt64ToFloat32(): BinaryenOp;
|
||||||
|
declare function _BinaryenConvertUInt64ToFloat64(): BinaryenOp;
|
||||||
|
declare function _BinaryenPromoteFloat32(): BinaryenOp;
|
||||||
|
declare function _BinaryenDemoteFloat64(): BinaryenOp;
|
||||||
|
declare function _BinaryenReinterpretInt32(): BinaryenOp;
|
||||||
|
declare function _BinaryenReinterpretInt64(): BinaryenOp;
|
||||||
|
declare function _BinaryenAddInt32(): BinaryenOp;
|
||||||
|
declare function _BinaryenSubInt32(): BinaryenOp;
|
||||||
|
declare function _BinaryenMulInt32(): BinaryenOp;
|
||||||
|
declare function _BinaryenDivSInt32(): BinaryenOp;
|
||||||
|
declare function _BinaryenDivUInt32(): BinaryenOp;
|
||||||
|
declare function _BinaryenRemSInt32(): BinaryenOp;
|
||||||
|
declare function _BinaryenRemUInt32(): BinaryenOp;
|
||||||
|
declare function _BinaryenAndInt32(): BinaryenOp;
|
||||||
|
declare function _BinaryenOrInt32(): BinaryenOp;
|
||||||
|
declare function _BinaryenXorInt32(): BinaryenOp;
|
||||||
|
declare function _BinaryenShlInt32(): BinaryenOp;
|
||||||
|
declare function _BinaryenShrUInt32(): BinaryenOp;
|
||||||
|
declare function _BinaryenShrSInt32(): BinaryenOp;
|
||||||
|
declare function _BinaryenRotLInt32(): BinaryenOp;
|
||||||
|
declare function _BinaryenRotRInt32(): BinaryenOp;
|
||||||
|
declare function _BinaryenEqInt32(): BinaryenOp;
|
||||||
|
declare function _BinaryenNeInt32(): BinaryenOp;
|
||||||
|
declare function _BinaryenLtSInt32(): BinaryenOp;
|
||||||
|
declare function _BinaryenLtUInt32(): BinaryenOp;
|
||||||
|
declare function _BinaryenLeSInt32(): BinaryenOp;
|
||||||
|
declare function _BinaryenLeUInt32(): BinaryenOp;
|
||||||
|
declare function _BinaryenGtSInt32(): BinaryenOp;
|
||||||
|
declare function _BinaryenGtUInt32(): BinaryenOp;
|
||||||
|
declare function _BinaryenGeSInt32(): BinaryenOp;
|
||||||
|
declare function _BinaryenGeUInt32(): BinaryenOp;
|
||||||
|
declare function _BinaryenAddInt64(): BinaryenOp;
|
||||||
|
declare function _BinaryenSubInt64(): BinaryenOp;
|
||||||
|
declare function _BinaryenMulInt64(): BinaryenOp;
|
||||||
|
declare function _BinaryenDivSInt64(): BinaryenOp;
|
||||||
|
declare function _BinaryenDivUInt64(): BinaryenOp;
|
||||||
|
declare function _BinaryenRemSInt64(): BinaryenOp;
|
||||||
|
declare function _BinaryenRemUInt64(): BinaryenOp;
|
||||||
|
declare function _BinaryenAndInt64(): BinaryenOp;
|
||||||
|
declare function _BinaryenOrInt64(): BinaryenOp;
|
||||||
|
declare function _BinaryenXorInt64(): BinaryenOp;
|
||||||
|
declare function _BinaryenShlInt64(): BinaryenOp;
|
||||||
|
declare function _BinaryenShrUInt64(): BinaryenOp;
|
||||||
|
declare function _BinaryenShrSInt64(): BinaryenOp;
|
||||||
|
declare function _BinaryenRotLInt64(): BinaryenOp;
|
||||||
|
declare function _BinaryenRotRInt64(): BinaryenOp;
|
||||||
|
declare function _BinaryenEqInt64(): BinaryenOp;
|
||||||
|
declare function _BinaryenNeInt64(): BinaryenOp;
|
||||||
|
declare function _BinaryenLtSInt64(): BinaryenOp;
|
||||||
|
declare function _BinaryenLtUInt64(): BinaryenOp;
|
||||||
|
declare function _BinaryenLeSInt64(): BinaryenOp;
|
||||||
|
declare function _BinaryenLeUInt64(): BinaryenOp;
|
||||||
|
declare function _BinaryenGtSInt64(): BinaryenOp;
|
||||||
|
declare function _BinaryenGtUInt64(): BinaryenOp;
|
||||||
|
declare function _BinaryenGeSInt64(): BinaryenOp;
|
||||||
|
declare function _BinaryenGeUInt64(): BinaryenOp;
|
||||||
|
declare function _BinaryenAddFloat32(): BinaryenOp;
|
||||||
|
declare function _BinaryenSubFloat32(): BinaryenOp;
|
||||||
|
declare function _BinaryenMulFloat32(): BinaryenOp;
|
||||||
|
declare function _BinaryenDivFloat32(): BinaryenOp;
|
||||||
|
declare function _BinaryenCopySignFloat32(): BinaryenOp;
|
||||||
|
declare function _BinaryenMinFloat32(): BinaryenOp;
|
||||||
|
declare function _BinaryenMaxFloat32(): BinaryenOp;
|
||||||
|
declare function _BinaryenEqFloat32(): BinaryenOp;
|
||||||
|
declare function _BinaryenNeFloat32(): BinaryenOp;
|
||||||
|
declare function _BinaryenLtFloat32(): BinaryenOp;
|
||||||
|
declare function _BinaryenLeFloat32(): BinaryenOp;
|
||||||
|
declare function _BinaryenGtFloat32(): BinaryenOp;
|
||||||
|
declare function _BinaryenGeFloat32(): BinaryenOp;
|
||||||
|
declare function _BinaryenAddFloat64(): BinaryenOp;
|
||||||
|
declare function _BinaryenSubFloat64(): BinaryenOp;
|
||||||
|
declare function _BinaryenMulFloat64(): BinaryenOp;
|
||||||
|
declare function _BinaryenDivFloat64(): BinaryenOp;
|
||||||
|
declare function _BinaryenCopySignFloat64(): BinaryenOp;
|
||||||
|
declare function _BinaryenMinFloat64(): BinaryenOp;
|
||||||
|
declare function _BinaryenMaxFloat64(): BinaryenOp;
|
||||||
|
declare function _BinaryenEqFloat64(): BinaryenOp;
|
||||||
|
declare function _BinaryenNeFloat64(): BinaryenOp;
|
||||||
|
declare function _BinaryenLtFloat64(): BinaryenOp;
|
||||||
|
declare function _BinaryenLeFloat64(): BinaryenOp;
|
||||||
|
declare function _BinaryenGtFloat64(): BinaryenOp;
|
||||||
|
declare function _BinaryenGeFloat64(): BinaryenOp;
|
||||||
|
declare function _BinaryenPageSize(): BinaryenOp;
|
||||||
|
declare function _BinaryenCurrentMemory(): BinaryenOp;
|
||||||
|
declare function _BinaryenGrowMemory(): BinaryenOp;
|
||||||
|
declare function _BinaryenHasFeature(): BinaryenOp;
|
||||||
|
|
||||||
|
declare type BinaryenExpressionRef = usize;
|
||||||
|
|
||||||
|
declare function _BinaryenBlock(module: BinaryenModuleRef, name: CString, children: CArray<BinaryenExpressionRef>, numChildren: BinaryenIndex, type: BinaryenType): BinaryenExpressionRef;
|
||||||
|
declare function _BinaryenIf(module: BinaryenModuleRef, condition: BinaryenExpressionRef, ifTrue: BinaryenExpressionRef, ifFalse: BinaryenExpressionRef): BinaryenExpressionRef;
|
||||||
|
declare function _BinaryenLoop(module: BinaryenModuleRef, name: CString, body: BinaryenExpressionRef): BinaryenExpressionRef;
|
||||||
|
declare function _BinaryenBreak(module: BinaryenModuleRef, name: CString, condition: BinaryenExpressionRef, value: BinaryenExpressionRef): BinaryenExpressionRef;
|
||||||
|
declare function _BinaryenSwitch(module: BinaryenModuleRef, names: CArray<CString>, numNames: BinaryenIndex, defaultName: CString, condition: BinaryenExpressionRef, value: BinaryenExpressionRef): BinaryenExpressionRef;
|
||||||
|
declare function _BinaryenCall(module: BinaryenModuleRef, target: CString, operands: CArray<BinaryenExpressionRef>, numOperands: BinaryenIndex, returnType: BinaryenType): BinaryenExpressionRef;
|
||||||
|
declare function _BinaryenCallImport(module: BinaryenModuleRef, target: CString, operands: CArray<BinaryenExpressionRef>, numOperands: BinaryenIndex, returnType: BinaryenType): BinaryenExpressionRef;
|
||||||
|
declare function _BinaryenCallIndirect(module: BinaryenModuleRef, target: BinaryenExpressionRef, operands: CArray<BinaryenExpressionRef>, numOperands: BinaryenIndex, type: CString): BinaryenExpressionRef;
|
||||||
|
declare function _BinaryenGetLocal(module: BinaryenModuleRef, index: BinaryenIndex, type: BinaryenType): BinaryenExpressionRef;
|
||||||
|
declare function _BinaryenSetLocal(module: BinaryenModuleRef, index: BinaryenIndex, value: BinaryenExpressionRef): BinaryenExpressionRef;
|
||||||
|
declare function _BinaryenTeeLocal(module: BinaryenModuleRef, index: BinaryenIndex, value: BinaryenExpressionRef): BinaryenExpressionRef;
|
||||||
|
declare function _BinaryenGetGlobal(module: BinaryenModuleRef, name: CString, type: BinaryenType): BinaryenExpressionRef;
|
||||||
|
declare function _BinaryenSetGlobal(module: BinaryenModuleRef, name: CString, value: BinaryenExpressionRef): BinaryenExpressionRef;
|
||||||
|
declare function _BinaryenLoad(module: BinaryenModuleRef, bytes: u32, signed: i8, offset: u32, align: u32, type: BinaryenType, ptr: BinaryenExpressionRef): BinaryenExpressionRef;
|
||||||
|
declare function _BinaryenStore(module: BinaryenModuleRef, bytes: u32, offset: u32, align: u32, ptr: BinaryenExpressionRef, value: BinaryenExpressionRef, type: BinaryenType): BinaryenExpressionRef;
|
||||||
|
declare function _BinaryenConst(module: BinaryenModuleRef, value: BinaryenLiteral): BinaryenExpressionRef;
|
||||||
|
declare function _BinaryenUnary(module: BinaryenModuleRef, op: BinaryenOp, value: BinaryenExpressionRef): BinaryenExpressionRef;
|
||||||
|
declare function _BinaryenBinary(module: BinaryenModuleRef, op: BinaryenOp, left: BinaryenExpressionRef, right: BinaryenExpressionRef): BinaryenExpressionRef;
|
||||||
|
declare function _BinaryenSelect(module: BinaryenModuleRef, condition: BinaryenExpressionRef, ifTrue: BinaryenExpressionRef, ifFalse: BinaryenExpressionRef): BinaryenExpressionRef;
|
||||||
|
declare function _BinaryenDrop(module: BinaryenModuleRef, value: BinaryenExpressionRef): BinaryenExpressionRef;
|
||||||
|
declare function _BinaryenReturn(module: BinaryenModuleRef, value: BinaryenExpressionRef): BinaryenExpressionRef;
|
||||||
|
declare function _BinaryenHost(module: BinaryenModuleRef, op: BinaryenOp, name: CString | 0, operands: CArray<BinaryenExpressionRef>, numOperands: BinaryenIndex): BinaryenExpressionRef;
|
||||||
|
declare function _BinaryenNop(module: BinaryenModuleRef): BinaryenExpressionRef;
|
||||||
|
declare function _BinaryenUnreachable(module: BinaryenModuleRef): BinaryenExpressionRef;
|
||||||
|
|
||||||
|
declare function _BinaryenExpressionPrint(expr: BinaryenExpressionRef): void;
|
||||||
|
|
||||||
|
declare type BinaryenFunctionRef = usize;
|
||||||
|
|
||||||
|
declare function _BinaryenAddFunction(module: BinaryenModuleRef, name: CString, type: BinaryenFunctionTypeRef, varTypes: CArray<BinaryenType>, numVarTypes: BinaryenIndex, body: BinaryenExpressionRef): BinaryenFunctionRef;
|
||||||
|
|
||||||
|
declare type BinaryenImportRef = usize;
|
||||||
|
|
||||||
|
declare function _BinaryenAddImport(module: BinaryenModuleRef, internalName: CString, externalModuleName: CString, externalBaseName: CString, type: BinaryenFunctionTypeRef): BinaryenImportRef;
|
||||||
|
declare function _BinaryenRemoveImport(module: BinaryenModuleRef, internalName: CString): void;
|
||||||
|
|
||||||
|
declare type BinaryenExportRef = usize;
|
||||||
|
|
||||||
|
declare function _BinaryenAddExport(module: BinaryenModuleRef, internalName: CString, externalName: CString): BinaryenExportRef;
|
||||||
|
declare function _BinaryenRemoveExport(module: BinaryenModuleRef, externalName: CString): void;
|
||||||
|
|
||||||
|
declare function _BinaryenAddGlobal(module: BinaryenModuleRef, name: CString, type: BinaryenType, mutable: i8, init: BinaryenExpressionRef): BinaryenImportRef;
|
||||||
|
|
||||||
|
declare function _BinaryenSetFunctionTable(module: BinaryenModuleRef, funcs: CArray<BinaryenFunctionRef>, numFuncs: BinaryenIndex): void;
|
||||||
|
|
||||||
|
declare function _BinaryenSetMemory(module: BinaryenModuleRef, initial: BinaryenIndex, maximum: BinaryenIndex, exportName: CString, segments: CArray<CArray<u8>>, segmentOffsets: CArray<BinaryenExpressionRef>, segmentSizes: CArray<BinaryenIndex>, numSegments: BinaryenIndex): void;
|
||||||
|
|
||||||
|
declare function _BinaryenSetStart(module: BinaryenModuleRef, start: BinaryenFunctionRef): void;
|
||||||
|
|
||||||
|
declare function _BinaryenModuleParse(text: CString): BinaryenModuleRef;
|
||||||
|
declare function _BinaryenModulePrint(module: BinaryenModuleRef): void;
|
||||||
|
declare function _BinaryenModulePrintAsmjs(module: BinaryenModuleRef): void;
|
||||||
|
declare function _BinaryenModuleValidate(module: BinaryenModuleRef): i32;
|
||||||
|
declare function _BinaryenModuleOptimize(module: BinaryenModuleRef): void;
|
||||||
|
declare function _BinaryenModuleAutoDrop(module: BinaryenModuleRef): void;
|
||||||
|
declare function _BinaryenModuleWrite(module: BinaryenModuleRef, output: CString, outputSize: usize): usize;
|
||||||
|
declare function _BinaryenModuleRead(input: CString, inputSize: usize): BinaryenModuleRef;
|
||||||
|
declare function _BinaryenModuleInterpret(module: BinaryenModuleRef): void;
|
||||||
|
|
||||||
|
declare type RelooperRef = usize;
|
||||||
|
declare type RelooperBlockRef = usize;
|
||||||
|
|
||||||
|
declare function _RelooperCreate(): RelooperRef;
|
||||||
|
declare function _RelooperAddBlock(relooper: RelooperRef, code: BinaryenExpressionRef): RelooperBlockRef;
|
||||||
|
declare function _RelooperAddBranch(from: RelooperBlockRef, to: RelooperBlockRef, condition: BinaryenExpressionRef, code: BinaryenExpressionRef): void;
|
||||||
|
declare function _RelooperAddBlockWithSwitch(relooper: RelooperRef, code: BinaryenExpressionRef, condition: BinaryenExpressionRef): RelooperBlockRef;
|
||||||
|
declare function _RelooperAddBranchForSwitch(from: RelooperBlockRef, to: RelooperBlockRef, indexes: CArray<BinaryenIndex>, numIndexes: BinaryenIndex, code: BinaryenExpressionRef): void;
|
||||||
|
declare function _RelooperRenderAndDispose(relooper: RelooperRef, entry: RelooperBlockRef, labelHelper: BinaryenIndex, module: BinaryenModuleRef): BinaryenExpressionRef;
|
||||||
|
|
||||||
|
declare function _BinaryenSetAPITracing(on: i32): void;
|
||||||
|
|
||||||
|
declare function _BinaryenGetFunctionTypeBySignature(module: BinaryenModuleRef, result: BinaryenType, paramTypes: CArray<BinaryenType>, numParams: BinaryenIndex): BinaryenFunctionTypeRef;
|
585
src/binaryen.ts
Normal file
585
src/binaryen.ts
Normal file
@ -0,0 +1,585 @@
|
|||||||
|
import { U64 } from "./util";
|
||||||
|
import { Target } from "./compiler";
|
||||||
|
|
||||||
|
export enum Type {
|
||||||
|
None = _BinaryenNone(),
|
||||||
|
I32 = _BinaryenInt32(),
|
||||||
|
I64 = _BinaryenInt64(),
|
||||||
|
F32 = _BinaryenFloat32(),
|
||||||
|
F64 = _BinaryenFloat64(),
|
||||||
|
Undefined = _BinaryenUndefined()
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum UnaryOp {
|
||||||
|
ClzI32 = _BinaryenClzInt32(),
|
||||||
|
CtzI32 = _BinaryenCtzInt32(),
|
||||||
|
PopcntI32 = _BinaryenPopcntInt32(),
|
||||||
|
NegF32 = _BinaryenNegFloat32(),
|
||||||
|
AbsF32 = _BinaryenAbsFloat32(),
|
||||||
|
CeilF32 = _BinaryenCeilFloat32(),
|
||||||
|
FloorF32 = _BinaryenFloorFloat32(),
|
||||||
|
TruncF32 = _BinaryenTruncFloat32(),
|
||||||
|
NearestF32 = _BinaryenNearestFloat32(),
|
||||||
|
SqrtF32 = _BinaryenSqrtFloat32(),
|
||||||
|
EqzI32 = _BinaryenEqZInt32(),
|
||||||
|
ClzI64 = _BinaryenClzInt64(),
|
||||||
|
CtzI64 = _BinaryenCtzInt64(),
|
||||||
|
PopcntI64 = _BinaryenPopcntInt64(),
|
||||||
|
NegF64 = _BinaryenNegFloat64(),
|
||||||
|
AbsF64 = _BinaryenAbsFloat64(),
|
||||||
|
CeilF64 = _BinaryenCeilFloat64(),
|
||||||
|
FloorF64 = _BinaryenFloorFloat64(),
|
||||||
|
TruncF64 = _BinaryenTruncFloat64(),
|
||||||
|
NearestF64 = _BinaryenNearestFloat64(),
|
||||||
|
SqrtF64 = _BinaryenSqrtFloat64(),
|
||||||
|
EqzI64 = _BinaryenEqZInt64(),
|
||||||
|
ExtendI32 = _BinaryenExtendSInt32(),
|
||||||
|
ExtendU32 = _BinaryenExtendUInt32(),
|
||||||
|
WrapI64 = _BinaryenWrapInt64(),
|
||||||
|
TruncF32_I32 = _BinaryenTruncSFloat32ToInt32(),
|
||||||
|
TruncF32_I64 = _BinaryenTruncSFloat32ToInt64(),
|
||||||
|
TruncF32_U32 = _BinaryenTruncUFloat32ToInt32(),
|
||||||
|
TruncF32_U64 = _BinaryenTruncUFloat32ToInt64(),
|
||||||
|
TruncF64_I32 = _BinaryenTruncSFloat64ToInt32(),
|
||||||
|
TruncF64_I64 = _BinaryenTruncSFloat64ToInt64(),
|
||||||
|
TruncF64_U32 = _BinaryenTruncUFloat64ToInt32(),
|
||||||
|
TruncF64_U64 = _BinaryenTruncUFloat64ToInt64(),
|
||||||
|
ReinterpretF32 = _BinaryenReinterpretFloat32(),
|
||||||
|
ReinterpretF64 = _BinaryenReinterpretFloat64(),
|
||||||
|
ConvertI32_F32 = _BinaryenConvertSInt32ToFloat32(),
|
||||||
|
ConvertI32_F64 = _BinaryenConvertSInt32ToFloat64(),
|
||||||
|
ConvertU32_F32 = _BinaryenConvertUInt32ToFloat32(),
|
||||||
|
ConvertU32_F64 = _BinaryenConvertUInt32ToFloat64(),
|
||||||
|
ConvertI64_F32 = _BinaryenConvertSInt64ToFloat32(),
|
||||||
|
ConvertI64_F64 = _BinaryenConvertSInt64ToFloat64(),
|
||||||
|
ConvertU64_F32 = _BinaryenConvertUInt64ToFloat32(),
|
||||||
|
ConvertU64_F64 = _BinaryenConvertUInt64ToFloat64(),
|
||||||
|
PromoteF32 = _BinaryenPromoteFloat32(),
|
||||||
|
DemoteF64 = _BinaryenDemoteFloat64(),
|
||||||
|
ReinterpretI32 = _BinaryenReinterpretInt32(),
|
||||||
|
ReinterpretI64 = _BinaryenReinterpretInt64()
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum BinaryOp {
|
||||||
|
AddI32 = _BinaryenAddInt32(),
|
||||||
|
SubI32 = _BinaryenSubInt32(),
|
||||||
|
MulI32 = _BinaryenMulInt32(),
|
||||||
|
DivI32 = _BinaryenDivSInt32(),
|
||||||
|
DivU32 = _BinaryenDivUInt32(),
|
||||||
|
RemI32 = _BinaryenRemSInt32(),
|
||||||
|
RemU32 = _BinaryenRemUInt32(),
|
||||||
|
AndI32 = _BinaryenAndInt32(),
|
||||||
|
OrI32 = _BinaryenOrInt32(),
|
||||||
|
XorI32 = _BinaryenXorInt32(),
|
||||||
|
ShlI32 = _BinaryenShlInt32(),
|
||||||
|
ShrU32 = _BinaryenShrUInt32(),
|
||||||
|
ShrI32 = _BinaryenShrSInt32(),
|
||||||
|
RotlI32 = _BinaryenRotLInt32(),
|
||||||
|
RotrI32 = _BinaryenRotRInt32(),
|
||||||
|
EqI32 = _BinaryenEqInt32(),
|
||||||
|
NeI32 = _BinaryenNeInt32(),
|
||||||
|
LtI32 = _BinaryenLtSInt32(),
|
||||||
|
LtU32 = _BinaryenLtUInt32(),
|
||||||
|
LeI32 = _BinaryenLeSInt32(),
|
||||||
|
LeU32 = _BinaryenLeUInt32(),
|
||||||
|
GtI32 = _BinaryenGtSInt32(),
|
||||||
|
GtU32 = _BinaryenGtUInt32(),
|
||||||
|
GeI32 = _BinaryenGeSInt32(),
|
||||||
|
GeU32 = _BinaryenGeUInt32(),
|
||||||
|
AddI64 = _BinaryenAddInt64(),
|
||||||
|
SubI64 = _BinaryenSubInt64(),
|
||||||
|
MulI64 = _BinaryenMulInt64(),
|
||||||
|
DivI64 = _BinaryenDivSInt64(),
|
||||||
|
DivU64 = _BinaryenDivUInt64(),
|
||||||
|
RemI64 = _BinaryenRemSInt64(),
|
||||||
|
RemU64 = _BinaryenRemUInt64(),
|
||||||
|
AndI64 = _BinaryenAndInt64(),
|
||||||
|
OrI64 = _BinaryenOrInt64(),
|
||||||
|
XorI64 = _BinaryenXorInt64(),
|
||||||
|
ShlI64 = _BinaryenShlInt64(),
|
||||||
|
ShrU64 = _BinaryenShrUInt64(),
|
||||||
|
ShrI64 = _BinaryenShrSInt64(),
|
||||||
|
RotlI64 = _BinaryenRotLInt64(),
|
||||||
|
RotrI64 = _BinaryenRotRInt64(),
|
||||||
|
EqI64 = _BinaryenEqInt64(),
|
||||||
|
NeI64 = _BinaryenNeInt64(),
|
||||||
|
LtI64 = _BinaryenLtSInt64(),
|
||||||
|
LtU64 = _BinaryenLtUInt64(),
|
||||||
|
LeI64 = _BinaryenLeSInt64(),
|
||||||
|
LeU64 = _BinaryenLeUInt64(),
|
||||||
|
GtI64 = _BinaryenGtSInt64(),
|
||||||
|
GtU64 = _BinaryenGtUInt64(),
|
||||||
|
GeI64 = _BinaryenGeSInt64(),
|
||||||
|
GeU64 = _BinaryenGeUInt64(),
|
||||||
|
AddF32 = _BinaryenAddFloat32(),
|
||||||
|
SubF32 = _BinaryenSubFloat32(),
|
||||||
|
MulF32 = _BinaryenMulFloat32(),
|
||||||
|
DivF32 = _BinaryenDivFloat32(),
|
||||||
|
CopysignF32 = _BinaryenCopySignFloat32(),
|
||||||
|
MinF32 = _BinaryenMinFloat32(),
|
||||||
|
MaxF32 = _BinaryenMaxFloat32(),
|
||||||
|
EqF32 = _BinaryenEqFloat32(),
|
||||||
|
NeF32 = _BinaryenNeFloat32(),
|
||||||
|
LtF32 = _BinaryenLtFloat32(),
|
||||||
|
LeF32 = _BinaryenLeFloat32(),
|
||||||
|
GtF32 = _BinaryenGtFloat32(),
|
||||||
|
GeF32 = _BinaryenGeFloat32(),
|
||||||
|
AddF64 = _BinaryenAddFloat64(),
|
||||||
|
SubF64 = _BinaryenSubFloat64(),
|
||||||
|
MulF64 = _BinaryenMulFloat64(),
|
||||||
|
DivF64 = _BinaryenDivFloat64(),
|
||||||
|
CopysignF64 = _BinaryenCopySignFloat64(),
|
||||||
|
MinF64 = _BinaryenMinFloat64(),
|
||||||
|
MaxF64 = _BinaryenMaxFloat64(),
|
||||||
|
EqF64 = _BinaryenEqFloat64(),
|
||||||
|
NeF64 = _BinaryenNeFloat64(),
|
||||||
|
LtF64 = _BinaryenLtFloat64(),
|
||||||
|
LeF64 = _BinaryenLeFloat64(),
|
||||||
|
GtF64 = _BinaryenGtFloat64(),
|
||||||
|
GeF64 = _BinaryenGeFloat64()
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum HostOp {
|
||||||
|
PageSize = _BinaryenPageSize(),
|
||||||
|
CurrentMemory = _BinaryenCurrentMemory(),
|
||||||
|
GrowMemory = _BinaryenGrowMemory(),
|
||||||
|
HasFeature = _BinaryenHasFeature()
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum AtomicRMWOp { // TODO: not yet part of the C-API
|
||||||
|
Add,
|
||||||
|
Sub,
|
||||||
|
And,
|
||||||
|
Or,
|
||||||
|
Xor,
|
||||||
|
Xchg
|
||||||
|
}
|
||||||
|
|
||||||
|
export class MemorySegment {
|
||||||
|
|
||||||
|
buffer: Uint8Array;
|
||||||
|
offset: U64;
|
||||||
|
|
||||||
|
static create(buffer: Uint8Array, offset: U64) {
|
||||||
|
const segment: MemorySegment = new MemorySegment();
|
||||||
|
segment.buffer = buffer;
|
||||||
|
segment.offset = offset;
|
||||||
|
return segment;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Module {
|
||||||
|
|
||||||
|
ref: BinaryenModuleRef;
|
||||||
|
lit: BinaryenLiteral;
|
||||||
|
|
||||||
|
static create(): Module {
|
||||||
|
const module: Module = new Module();
|
||||||
|
module.ref = _BinaryenModuleCreate();
|
||||||
|
module.lit = _malloc(16);
|
||||||
|
return module;
|
||||||
|
}
|
||||||
|
|
||||||
|
static createFrom(buffer: Uint8Array): Module {
|
||||||
|
const cArr: CArray<u8> = allocU8Array(buffer);
|
||||||
|
try {
|
||||||
|
const module: Module = new Module();
|
||||||
|
module.ref = _BinaryenModuleRead(cArr, buffer.length);
|
||||||
|
module.lit = _malloc(16);
|
||||||
|
return module;
|
||||||
|
} finally {
|
||||||
|
_free(cArr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static MAX_MEMORY_WASM32: BinaryenIndex = 0xffff;
|
||||||
|
|
||||||
|
// types
|
||||||
|
|
||||||
|
addFunctionType(name: string, result: Type, paramTypes: Type[]): BinaryenFunctionRef {
|
||||||
|
const cStr: CString = allocString(name);
|
||||||
|
const cArr: CArray<i32> = allocI32Array(paramTypes);
|
||||||
|
try {
|
||||||
|
return _BinaryenAddFunctionType(this.ref, cStr, result, cArr, paramTypes.length);
|
||||||
|
} finally {
|
||||||
|
_free(cStr);
|
||||||
|
_free(cArr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getFunctionTypeBySignature(result: Type, paramTypes: Type[]): BinaryenFunctionTypeRef {
|
||||||
|
const cArr: CArray<i32> = allocI32Array(paramTypes);
|
||||||
|
try {
|
||||||
|
return _BinaryenGetFunctionTypeBySignature(this.ref, result, cArr, paramTypes.length);
|
||||||
|
} finally {
|
||||||
|
_free(cArr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// expressions
|
||||||
|
|
||||||
|
createI32(value: i32): BinaryenExpressionRef {
|
||||||
|
_BinaryenLiteralInt32(this.lit, value);
|
||||||
|
return _BinaryenConst(this.ref, this.lit);
|
||||||
|
}
|
||||||
|
|
||||||
|
createI64(lo: i32, hi: i32): BinaryenExpressionRef {
|
||||||
|
_BinaryenLiteralInt64(this.lit, lo, hi);
|
||||||
|
return _BinaryenConst(this.ref, this.lit);
|
||||||
|
}
|
||||||
|
|
||||||
|
createF32(value: f32): BinaryenExpressionRef {
|
||||||
|
_BinaryenLiteralFloat32(this.lit, value);
|
||||||
|
return _BinaryenConst(this.ref, this.lit);
|
||||||
|
}
|
||||||
|
|
||||||
|
createF64(value: f64): BinaryenExpressionRef {
|
||||||
|
_BinaryenLiteralFloat64(this.lit, value);
|
||||||
|
return _BinaryenConst(this.ref, this.lit);
|
||||||
|
}
|
||||||
|
|
||||||
|
createUnary(op: UnaryOp, expr: BinaryenExpressionRef): BinaryenExpressionRef {
|
||||||
|
return _BinaryenUnary(this.ref, op, expr);
|
||||||
|
}
|
||||||
|
|
||||||
|
createBinary(op: BinaryOp, left: BinaryenExpressionRef, right: BinaryenExpressionRef): BinaryenExpressionRef {
|
||||||
|
return _BinaryenBinary(this.ref, op, left, right);
|
||||||
|
}
|
||||||
|
|
||||||
|
createHost(op: HostOp, name: string | null = null, operands: BinaryenExpressionRef[] | null = null): BinaryenExpressionRef {
|
||||||
|
const cStr: CString = allocString(name);
|
||||||
|
const cArr: CArray<i32> = allocI32Array(operands);
|
||||||
|
try {
|
||||||
|
return _BinaryenHost(this.ref, op, cStr, cArr, operands ? (<BinaryenExpressionRef[]>operands).length : 0);
|
||||||
|
} finally {
|
||||||
|
_free(cStr);
|
||||||
|
_free(cArr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
createGetLocal(index: i32, type: Type): BinaryenExpressionRef {
|
||||||
|
return _BinaryenGetLocal(this.ref, index, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
createTeeLocal(index: i32, value: BinaryenExpressionRef): BinaryenExpressionRef {
|
||||||
|
return _BinaryenTeeLocal(this.ref, index, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
createGetGlobal(name: string, type: Type): BinaryenExpressionRef {
|
||||||
|
const cStr: CString = allocString(name);
|
||||||
|
try {
|
||||||
|
return _BinaryenGetGlobal(this.ref, cStr, type);
|
||||||
|
} finally {
|
||||||
|
_free(cStr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
createTeeGlobal(name: string, value: BinaryenExpressionRef, type: Type): BinaryenExpressionRef {
|
||||||
|
// emulated, lives here for simplicity reasons
|
||||||
|
return this.createBlock(null, [
|
||||||
|
this.createSetGlobal(name, value),
|
||||||
|
this.createGetGlobal(name, type)
|
||||||
|
], type);
|
||||||
|
}
|
||||||
|
|
||||||
|
// statements
|
||||||
|
|
||||||
|
createSetLocal(index: i32, value: BinaryenExpressionRef): BinaryenExpressionRef {
|
||||||
|
return _BinaryenSetLocal(this.ref, index, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
createSetGlobal(name: string, value: BinaryenExpressionRef): BinaryenExpressionRef {
|
||||||
|
const cStr: CString = allocString(name);
|
||||||
|
try {
|
||||||
|
return _BinaryenSetGlobal(this.ref, cStr, value);
|
||||||
|
} finally {
|
||||||
|
_free(cStr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
createBlock(label: string | null, children: BinaryenExpressionRef[], type: Type = Type.Undefined): BinaryenExpressionRef {
|
||||||
|
const cStr: CString = allocString(label);
|
||||||
|
const cArr: CArray<i32> = allocI32Array(children);
|
||||||
|
try {
|
||||||
|
return _BinaryenBlock(this.ref, cStr, cArr, children.length, type);
|
||||||
|
} finally {
|
||||||
|
_free(cStr);
|
||||||
|
_free(cArr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
createBreak(label: string | null, condition: BinaryenExpressionRef = 0, value: BinaryenExpressionRef = 0): BinaryenExpressionRef {
|
||||||
|
const cStr: CString = allocString(label);
|
||||||
|
try {
|
||||||
|
return _BinaryenBreak(this.ref, cStr, condition, value);
|
||||||
|
} finally {
|
||||||
|
_free(cStr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
createDrop(expression: BinaryenExpressionRef): BinaryenExpressionRef {
|
||||||
|
return _BinaryenDrop(this.ref, expression);
|
||||||
|
}
|
||||||
|
|
||||||
|
createLoop(label: string | null, body: BinaryenExpressionRef): BinaryenExpressionRef {
|
||||||
|
const cStr: CString = allocString(label);
|
||||||
|
try {
|
||||||
|
return _BinaryenLoop(this.ref, cStr, body);
|
||||||
|
} finally {
|
||||||
|
_free(cStr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
createIf(condition: BinaryenExpressionRef, ifTrue: BinaryenExpressionRef, ifFalse: BinaryenExpressionRef = 0): BinaryenExpressionRef {
|
||||||
|
return _BinaryenIf(this.ref, condition, ifTrue, ifFalse);
|
||||||
|
}
|
||||||
|
|
||||||
|
createNop(): BinaryenExpressionRef {
|
||||||
|
return _BinaryenNop(this.ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
createReturn(expression: BinaryenExpressionRef = 0): BinaryenExpressionRef {
|
||||||
|
return _BinaryenReturn(this.ref, expression);
|
||||||
|
}
|
||||||
|
|
||||||
|
createSelect(condition: BinaryenExpressionRef, ifTrue: BinaryenExpressionRef, ifFalse: BinaryenExpressionRef): BinaryenExpressionRef {
|
||||||
|
return _BinaryenSelect(this.ref, condition, ifTrue, ifFalse);
|
||||||
|
}
|
||||||
|
|
||||||
|
createSwitch(names: string[], defaultName: string | null, condition: BinaryenExpressionRef, value: BinaryenExpressionRef = 0): BinaryenExpressionRef {
|
||||||
|
const strs: CString[] = new Array(names.length);
|
||||||
|
let i: i32, k: i32 = names.length;
|
||||||
|
for (i = 0; i < k; ++i) strs[i] = allocString(names[i]);
|
||||||
|
const cArr: CArray<i32> = allocI32Array(strs);
|
||||||
|
const cStr: CString = allocString(defaultName);
|
||||||
|
try {
|
||||||
|
return _BinaryenSwitch(this.ref, cArr, k, cStr, condition, value);
|
||||||
|
} finally {
|
||||||
|
for (i = 0; i < k; ++i) _free(strs[i]);
|
||||||
|
_free(cArr);
|
||||||
|
_free(cStr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
createCall(target: BinaryenFunctionRef, operands: BinaryenExpressionRef[], returnType: Type): BinaryenExpressionRef {
|
||||||
|
const cArr: CArray<i32> = allocI32Array(operands);
|
||||||
|
try {
|
||||||
|
return _BinaryenCall(this.ref, target, cArr, operands.length, returnType);
|
||||||
|
} finally {
|
||||||
|
_free(cArr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
createCallImport(target: BinaryenImportRef, operands: BinaryenExpressionRef[], returnType: Type): BinaryenExpressionRef {
|
||||||
|
const cArr: CArray<i32> = allocI32Array(operands);
|
||||||
|
try {
|
||||||
|
return _BinaryenCallImport(this.ref, target, cArr, operands.length, returnType);
|
||||||
|
} finally {
|
||||||
|
_free(cArr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
createUnreachable(): BinaryenExpressionRef {
|
||||||
|
return _BinaryenUnreachable(this.ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
// meta
|
||||||
|
|
||||||
|
addGlobal(name: string, type: Type, mutable: bool, initializer: BinaryenExpressionRef): BinaryenImportRef {
|
||||||
|
const cStr: CString = allocString(name);
|
||||||
|
try {
|
||||||
|
return _BinaryenAddGlobal(this.ref, cStr, type, mutable ? 1 : 0, initializer);
|
||||||
|
} finally {
|
||||||
|
_free(cStr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addFunction(name: string, type: Type, varTypes: Type[], body: BinaryenExpressionRef): BinaryenFunctionRef {
|
||||||
|
const cStr: CString = allocString(name);
|
||||||
|
const cArr: CArray<i32> = allocI32Array(varTypes);
|
||||||
|
try {
|
||||||
|
return _BinaryenAddFunction(this.ref, cStr, type, cArr, varTypes.length, body);
|
||||||
|
} finally {
|
||||||
|
_free(cStr);
|
||||||
|
_free(cArr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addExport(internalName: string, externalName: string): BinaryenExportRef {
|
||||||
|
const cStr1: CString = allocString(internalName);
|
||||||
|
const cStr2: CString = allocString(externalName);
|
||||||
|
try {
|
||||||
|
return _BinaryenAddExport(this.ref, cStr1, cStr2);
|
||||||
|
} finally {
|
||||||
|
_free(cStr1);
|
||||||
|
_free(cStr2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
removeExport(externalName: string): void {
|
||||||
|
const cStr = allocString(externalName);
|
||||||
|
try {
|
||||||
|
_BinaryenRemoveExport(this.ref, cStr);
|
||||||
|
} finally {
|
||||||
|
_free(cStr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addImport(internalName: string, externalModuleName: string, externalBaseName: string, type: BinaryenFunctionTypeRef): BinaryenImportRef {
|
||||||
|
const cStr1: CString = allocString(internalName);
|
||||||
|
const cStr2: CString = allocString(externalModuleName);
|
||||||
|
const cStr3: CString = allocString(externalBaseName);
|
||||||
|
try {
|
||||||
|
return _BinaryenAddImport(this.ref, cStr1, cStr2, cStr3, type);
|
||||||
|
} finally {
|
||||||
|
_free(cStr1);
|
||||||
|
_free(cStr2);
|
||||||
|
_free(cStr3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
removeImport(internalName: string): void {
|
||||||
|
const cStr: CString = allocString(internalName);
|
||||||
|
try {
|
||||||
|
_BinaryenRemoveImport(this.ref, cStr);
|
||||||
|
} finally {
|
||||||
|
_free(cStr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setMemory(initial: BinaryenIndex, maximum: BinaryenIndex, segments: MemorySegment[], target: Target, exportName: string | null = null): void {
|
||||||
|
const cStr: CString = allocString(exportName);
|
||||||
|
let i: i32, k: i32 = segments.length;
|
||||||
|
const segs: CArray<u8>[] = new Array(k);
|
||||||
|
const offs: BinaryenExpressionRef[] = new Array(k);
|
||||||
|
const sizs: BinaryenIndex[] = new Array(k);
|
||||||
|
for (i = 0; i < k; ++i) {
|
||||||
|
const buffer: Uint8Array = segments[i].buffer;
|
||||||
|
const offset: U64 = segments[i].offset;
|
||||||
|
segs[i] = allocU8Array(buffer);
|
||||||
|
offs[i] = target == Target.WASM64
|
||||||
|
? this.createI64(offset.lo, offset.hi)
|
||||||
|
: this.createI32(offset.toI32());
|
||||||
|
sizs[i] = buffer.length;
|
||||||
|
}
|
||||||
|
const cArr1: CArray<i32> = allocI32Array(segs);
|
||||||
|
const cArr2: CArray<i32> = allocI32Array(offs);
|
||||||
|
const cArr3: CArray<i32> = allocI32Array(sizs);
|
||||||
|
try {
|
||||||
|
_BinaryenSetMemory(this.ref, initial, maximum, cStr, cArr1, cArr2, cArr3, k);
|
||||||
|
} finally {
|
||||||
|
_free(cStr);
|
||||||
|
for (i = 0; i < k; ++i) _free(segs[i]);
|
||||||
|
_free(cArr1);
|
||||||
|
_free(cArr2);
|
||||||
|
_free(cArr3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setStart(func: BinaryenFunctionRef): void {
|
||||||
|
_BinaryenSetStart(this.ref, func);
|
||||||
|
}
|
||||||
|
|
||||||
|
optimize(): void {
|
||||||
|
_BinaryenModuleOptimize(this.ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
validate(): bool {
|
||||||
|
return _BinaryenModuleValidate(this.ref) == 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
dispose(): void {
|
||||||
|
_BinaryenModuleDispose(this.ref);
|
||||||
|
_free(this.lit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// helpers
|
||||||
|
// TODO: investigate stack allocation?
|
||||||
|
|
||||||
|
function allocU8Array(u8s: Uint8Array | null): CArray<u8> {
|
||||||
|
if (!u8s) return 0;
|
||||||
|
const ptr: usize = _malloc((<Uint8Array>u8s).length);
|
||||||
|
let idx: usize = ptr;
|
||||||
|
for (let i: i32 = 0, k: i32 = (<Uint8Array>u8s).length; i < k; ++i)
|
||||||
|
store<u8>(idx++, (<Uint8Array>u8s)[i])
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
function allocI32Array(i32s: i32[] | null): CArray<i32> {
|
||||||
|
if (!i32s) return 0;
|
||||||
|
const ptr: usize = _malloc((<i32[]>i32s).length << 2);
|
||||||
|
let idx: usize = ptr;
|
||||||
|
for (let i: i32 = 0, k: i32 = (<i32[]>i32s).length; i < k; ++i) {
|
||||||
|
let val: i32 = (<i32[]>i32s)[i];
|
||||||
|
store<u8>(idx , ( val & 0xff) as u8);
|
||||||
|
store<u8>(idx + 1, ((val >> 8) & 0xff) as u8);
|
||||||
|
store<u8>(idx + 2, ((val >> 16) & 0xff) as u8);
|
||||||
|
store<u8>(idx + 3, ( val >>> 24 ) as u8);
|
||||||
|
idx += 4;
|
||||||
|
}
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
function stringLengthUTF8(str: string): usize {
|
||||||
|
let len: i32 = 0;
|
||||||
|
for (let i: i32 = 0, k: i32 = str.length; i < k; ++i) {
|
||||||
|
let u: i32 = str.charCodeAt(i);
|
||||||
|
if (u >= 0xD800 && u <= 0xDFFF && i + 1 < k)
|
||||||
|
u = 0x10000 + ((u & 0x3FF) << 10) | (str.charCodeAt(++i) & 0x3FF);
|
||||||
|
if (u <= 0x7F)
|
||||||
|
++len;
|
||||||
|
else if (u <= 0x7FF)
|
||||||
|
len += 2;
|
||||||
|
else if (u <= 0xFFFF)
|
||||||
|
len += 3;
|
||||||
|
else if (u <= 0x1FFFFF)
|
||||||
|
len += 4;
|
||||||
|
else if (u <= 0x3FFFFFF)
|
||||||
|
len += 5;
|
||||||
|
else
|
||||||
|
len += 6;
|
||||||
|
}
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
function allocString(str: string | null): CString {
|
||||||
|
if (!str) return 0;
|
||||||
|
const ptr: usize = _malloc(stringLengthUTF8((<string>str)) + 1);
|
||||||
|
let idx: usize = ptr;
|
||||||
|
for (let i: i32 = 0, k = (<string>str).length; i < k; ++i) {
|
||||||
|
let u: i32 = (<string>str).charCodeAt(i);
|
||||||
|
if (u >= 0xD800 && u <= 0xDFFF && i + 1 < k)
|
||||||
|
u = 0x10000 + ((u & 0x3FF) << 10) | ((<string>str).charCodeAt(++i) & 0x3FF);
|
||||||
|
if (u <= 0x7F)
|
||||||
|
store<u8>(idx++, u as u8);
|
||||||
|
else if (u <= 0x7FF) {
|
||||||
|
store<u8>(idx++, (0xC0 | (u >>> 6) ) as u8);
|
||||||
|
store<u8>(idx++, (0x80 | ( u & 63)) as u8);
|
||||||
|
} else if (u <= 0xFFFF) {
|
||||||
|
store<u8>(idx++, (0xE0 | (u >>> 12) ) as u8);
|
||||||
|
store<u8>(idx++, (0x80 | ((u >>> 6) & 63)) as u8);
|
||||||
|
store<u8>(idx++, (0x80 | ( u & 63)) as u8);
|
||||||
|
} else if (u <= 0x1FFFFF) {
|
||||||
|
store<u8>(idx++, (0xF0 | (u >>> 18) ) as u8);
|
||||||
|
store<u8>(idx++, (0x80 | ((u >>> 12) & 63)) as u8);
|
||||||
|
store<u8>(idx++, (0x80 | ((u >>> 6) & 63)) as u8);
|
||||||
|
store<u8>(idx++, (0x80 | ( u & 63)) as u8);
|
||||||
|
} else if (u <= 0x3FFFFFF) {
|
||||||
|
store<u8>(idx++, (0xF8 | (u >>> 24) ) as u8);
|
||||||
|
store<u8>(idx++, (0x80 | ((u >>> 18) & 63)) as u8);
|
||||||
|
store<u8>(idx++, (0x80 | ((u >>> 12) & 63)) as u8);
|
||||||
|
store<u8>(idx++, (0x80 | ((u >>> 6) & 63)) as u8);
|
||||||
|
store<u8>(idx++, (0x80 | ( u & 63)) as u8);
|
||||||
|
} else {
|
||||||
|
store<u8>(idx++, (0xFC | (u >>> 30) ) as u8);
|
||||||
|
store<u8>(idx++, (0x80 | ((u >>> 24) & 63)) as u8);
|
||||||
|
store<u8>(idx++, (0x80 | ((u >>> 18) & 63)) as u8);
|
||||||
|
store<u8>(idx++, (0x80 | ((u >>> 12) & 63)) as u8);
|
||||||
|
store<u8>(idx++, (0x80 | ((u >>> 6) & 63)) as u8);
|
||||||
|
store<u8>(idx++, (0x80 | ( u & 63)) as u8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
store<u8>(idx, 0);
|
||||||
|
return ptr;
|
||||||
|
}
|
653
src/compiler.ts
Normal file
653
src/compiler.ts
Normal file
@ -0,0 +1,653 @@
|
|||||||
|
import { Module, MemorySegment, UnaryOp, BinaryOp, HostOp } from "./binaryen";
|
||||||
|
import { DiagnosticCode, DiagnosticMessage, DiagnosticEmitter } from "./diagnostics";
|
||||||
|
import { hasModifier } from "./parser";
|
||||||
|
import { Program } from "./program";
|
||||||
|
import { CharCode, U64 } from "./util";
|
||||||
|
import {
|
||||||
|
|
||||||
|
NodeKind,
|
||||||
|
TypeNode,
|
||||||
|
|
||||||
|
// statements
|
||||||
|
BlockStatement,
|
||||||
|
BreakStatement,
|
||||||
|
ClassDeclaration,
|
||||||
|
ContinueStatement,
|
||||||
|
DoStatement,
|
||||||
|
EmptyStatement,
|
||||||
|
EnumDeclaration,
|
||||||
|
ExpressionStatement,
|
||||||
|
FunctionDeclaration,
|
||||||
|
ForStatement,
|
||||||
|
IfStatement,
|
||||||
|
MethodDeclaration,
|
||||||
|
ModifierKind,
|
||||||
|
NamespaceDeclaration,
|
||||||
|
ReturnStatement,
|
||||||
|
Statement,
|
||||||
|
SwitchStatement,
|
||||||
|
ThrowStatement,
|
||||||
|
TryStatement,
|
||||||
|
VariableDeclaration,
|
||||||
|
VariableStatement,
|
||||||
|
WhileStatement,
|
||||||
|
|
||||||
|
// expressions
|
||||||
|
ArrayLiteralExpression,
|
||||||
|
AssertionExpression,
|
||||||
|
BinaryExpression,
|
||||||
|
CallExpression,
|
||||||
|
ElementAccessExpression,
|
||||||
|
Expression,
|
||||||
|
FloatLiteralExpression,
|
||||||
|
IdentifierExpression,
|
||||||
|
IntegerLiteralExpression,
|
||||||
|
LiteralExpression,
|
||||||
|
LiteralKind,
|
||||||
|
NewExpression,
|
||||||
|
ParenthesizedExpression,
|
||||||
|
PropertyAccessExpression,
|
||||||
|
SelectExpression,
|
||||||
|
StringLiteralExpression,
|
||||||
|
UnaryPostfixExpression,
|
||||||
|
UnaryPrefixExpression
|
||||||
|
|
||||||
|
} from "./ast";
|
||||||
|
import {
|
||||||
|
|
||||||
|
Enum,
|
||||||
|
Class,
|
||||||
|
Field,
|
||||||
|
Function,
|
||||||
|
GlobalVariable,
|
||||||
|
LocalVariable,
|
||||||
|
Namespace,
|
||||||
|
Method,
|
||||||
|
Source,
|
||||||
|
Type,
|
||||||
|
TypeKind
|
||||||
|
|
||||||
|
} from "./reflection";
|
||||||
|
|
||||||
|
export enum Target {
|
||||||
|
WASM32,
|
||||||
|
WASM64
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Options {
|
||||||
|
target: Target = Target.WASM32;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Compiler extends DiagnosticEmitter {
|
||||||
|
|
||||||
|
program: Program;
|
||||||
|
options: Options;
|
||||||
|
module: Module;
|
||||||
|
|
||||||
|
currentType: Type = Type.void;
|
||||||
|
currentClass: Class | null = null;
|
||||||
|
currentFunction: Function | null = null;
|
||||||
|
breakMajor: i32 = 0;
|
||||||
|
breakMinor: i32 = 0;
|
||||||
|
|
||||||
|
memoryOffset: U64 = new U64(8, 0); // leave space for (any size of) NULL
|
||||||
|
memorySegments: MemorySegment[] = new Array();
|
||||||
|
|
||||||
|
static compile(program: Program, options: Options | null = null): Module {
|
||||||
|
const compiler: Compiler = new Compiler(program, options);
|
||||||
|
return compiler.compile();
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(program: Program, options: Options | null = null) {
|
||||||
|
super(program.diagnostics);
|
||||||
|
this.program = program;
|
||||||
|
this.options = options ? options : new Options();
|
||||||
|
this.module = Module.create();
|
||||||
|
}
|
||||||
|
|
||||||
|
compile(): Module {
|
||||||
|
const program: Program = this.program;
|
||||||
|
|
||||||
|
// initialize lookup maps
|
||||||
|
program.initialize(this.options.target);
|
||||||
|
|
||||||
|
// start by compiling entry file exports
|
||||||
|
const entrySource: Source = program.sources[0];
|
||||||
|
for (let i: i32 = 0, k = entrySource.statements.length; i < k; ++i) {
|
||||||
|
const statement: Statement = entrySource.statements[i];
|
||||||
|
switch (statement.kind) {
|
||||||
|
|
||||||
|
case NodeKind.CLASS:
|
||||||
|
if (hasModifier(ModifierKind.EXPORT, (<ClassDeclaration>statement).modifiers) && !(<ClassDeclaration>statement).typeParameters.length) {
|
||||||
|
const cl: Class = Class.create(<ClassDeclaration>statement, []).exportAs((<ClassDeclaration>statement).identifier.name);
|
||||||
|
this.program.addClass(cl);
|
||||||
|
this.compileClass(cl);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NodeKind.ENUM:
|
||||||
|
if (hasModifier(ModifierKind.EXPORT, (<EnumDeclaration>statement).modifiers)) {
|
||||||
|
const en: Enum = Enum.create(<EnumDeclaration>statement).exportAs((<EnumDeclaration>statement).identifier.name);
|
||||||
|
this.program.addEnum(en);
|
||||||
|
this.compileEnum(en);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NodeKind.FUNCTION:
|
||||||
|
if (hasModifier(ModifierKind.EXPORT, (<FunctionDeclaration>statement).modifiers) && !(<FunctionDeclaration>statement).typeParameters.length) {
|
||||||
|
const fn: Function = Function.create(<FunctionDeclaration>statement, []).exportAs((<FunctionDeclaration>statement).identifier.name);
|
||||||
|
this.program.addFunction(fn);
|
||||||
|
this.compileFunction(fn);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NodeKind.NAMESPACE:
|
||||||
|
if (hasModifier(ModifierKind.EXPORT, (<NamespaceDeclaration>statement).modifiers)) {
|
||||||
|
const ns: Namespace = Namespace.create(<NamespaceDeclaration>statement).exportAs((<NamespaceDeclaration>statement).identifier.name);
|
||||||
|
this.program.addNamespace(ns);
|
||||||
|
this.compileNamespace(ns);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NodeKind.VARIABLE:
|
||||||
|
if (hasModifier(ModifierKind.EXPORT, (<VariableStatement>statement).modifiers)) {
|
||||||
|
const gls: GlobalVariable[] = GlobalVariable.create(<VariableStatement>statement);
|
||||||
|
for (let j: i32 = 0, l: i32 = gls.length; j < l; ++j) {
|
||||||
|
const gl: GlobalVariable = gls[j];
|
||||||
|
gl.exportName = (<VariableDeclaration>gl.declaration).identifier.name; // WASM can't do this right now
|
||||||
|
this.program.addGlobal(gl);
|
||||||
|
this.compileGlobal(gl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NodeKind.EXPORT:
|
||||||
|
// obtain referenced declaration and export that
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// set up memory size and static segments
|
||||||
|
const initial: U64 = this.memoryOffset.clone();
|
||||||
|
const initialOverlaps: U64 = initial.clone();
|
||||||
|
initialOverlaps.and32(0xffff);
|
||||||
|
if (!initialOverlaps.isZero) {
|
||||||
|
initial.or32(0xffff);
|
||||||
|
initial.add32(1);
|
||||||
|
}
|
||||||
|
initial.shru32(16);
|
||||||
|
this.module.setMemory(initial.toI32(), Module.MAX_MEMORY_WASM32 /* TODO: not WASM64 compatible yet */, this.memorySegments, this.options.target, "memory");
|
||||||
|
|
||||||
|
return this.module;
|
||||||
|
}
|
||||||
|
|
||||||
|
compileClass(cl: Class): void {
|
||||||
|
throw new Error("not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
compileEnum(en: Enum): void {
|
||||||
|
throw new Error("not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
compileFunction(fn: Function): void {
|
||||||
|
throw new Error("not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
compileGlobal(gl: GlobalVariable): void {
|
||||||
|
throw new Error("not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
compileNamespace(ns: Namespace): void {
|
||||||
|
throw new Error("not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
// memory
|
||||||
|
|
||||||
|
addMemorySegment(buffer: Uint8Array): MemorySegment {
|
||||||
|
if (this.memoryOffset.lo & 7) { // align to 8 bytes so any possible data type is aligned here
|
||||||
|
this.memoryOffset.or32(7);
|
||||||
|
this.memoryOffset.add32(1);
|
||||||
|
}
|
||||||
|
const segment: MemorySegment = MemorySegment.create(buffer, this.memoryOffset.clone());
|
||||||
|
this.memorySegments.push(segment);
|
||||||
|
this.memoryOffset.add32(buffer.length);
|
||||||
|
return segment;
|
||||||
|
}
|
||||||
|
|
||||||
|
// types
|
||||||
|
|
||||||
|
resolveType(node: TypeNode, reportNotFound: bool = true): Type | null {
|
||||||
|
const types: Map<string,Type> = this.program.types;
|
||||||
|
const name: string = node.identifier.name;
|
||||||
|
if (types.has(name)) {
|
||||||
|
const type: Type = <Type>types.get(name);
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
if (reportNotFound)
|
||||||
|
this.error(DiagnosticCode.Cannot_find_name_0, node.identifier.range, name);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// statements
|
||||||
|
|
||||||
|
compileStatement(statement: Statement): BinaryenExpressionRef {
|
||||||
|
switch (statement.kind) {
|
||||||
|
|
||||||
|
case NodeKind.BLOCK:
|
||||||
|
return this.compileBlockStatement(<BlockStatement>statement);
|
||||||
|
|
||||||
|
case NodeKind.BREAK:
|
||||||
|
return this.compileBreakStatement(<BreakStatement>statement);
|
||||||
|
|
||||||
|
case NodeKind.CONTINUE:
|
||||||
|
return this.compileContinueStatement(<ContinueStatement>statement);
|
||||||
|
|
||||||
|
case NodeKind.DO:
|
||||||
|
return this.compileDoStatement(<DoStatement>statement);
|
||||||
|
|
||||||
|
case NodeKind.EMPTY:
|
||||||
|
return this.compileEmptyStatement(<EmptyStatement>statement);
|
||||||
|
|
||||||
|
case NodeKind.EXPRESSION:
|
||||||
|
return this.compileExpressionStatement(<ExpressionStatement>statement);
|
||||||
|
|
||||||
|
case NodeKind.FOR:
|
||||||
|
return this.compileForStatement(<ForStatement>statement);
|
||||||
|
|
||||||
|
case NodeKind.IF:
|
||||||
|
return this.compileIfStatement(<IfStatement>statement);
|
||||||
|
|
||||||
|
case NodeKind.RETURN:
|
||||||
|
return this.compileReturnStatement(<ReturnStatement>statement);
|
||||||
|
|
||||||
|
case NodeKind.SWITCH:
|
||||||
|
return this.compileSwitchStatement(<SwitchStatement>statement);
|
||||||
|
|
||||||
|
case NodeKind.THROW:
|
||||||
|
return this.compileThrowStatement(<ThrowStatement>statement);
|
||||||
|
|
||||||
|
case NodeKind.TRY:
|
||||||
|
return this.compileTryStatement(<TryStatement>statement);
|
||||||
|
|
||||||
|
case NodeKind.VARIABLE:
|
||||||
|
return this.compileVariableStatement(<VariableStatement>statement);
|
||||||
|
|
||||||
|
case NodeKind.WHILE:
|
||||||
|
return this.compileWhileStatement(<WhileStatement>statement);
|
||||||
|
}
|
||||||
|
throw new Error("not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
compileBlockStatement(statement: BlockStatement): BinaryenExpressionRef {
|
||||||
|
const substatements: Statement[] = statement.statements;
|
||||||
|
const children: BinaryenExpressionRef[] = new Array(substatements.length);
|
||||||
|
for (let i: i32 = 0, k: i32 = substatements.length; i < k; ++i)
|
||||||
|
children[i] = this.compileStatement(substatements[i]);
|
||||||
|
return this.module.createBlock(null, children);
|
||||||
|
}
|
||||||
|
|
||||||
|
compileBreakStatement(statement: BreakStatement): BinaryenExpressionRef {
|
||||||
|
throw new Error("not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
compileContinueStatement(statement: ContinueStatement): BinaryenExpressionRef {
|
||||||
|
throw new Error("not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
compileDoStatement(statement: DoStatement): BinaryenExpressionRef {
|
||||||
|
throw new Error("not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
compileEmptyStatement(statement: EmptyStatement): BinaryenExpressionRef {
|
||||||
|
return this.module.createNop();
|
||||||
|
}
|
||||||
|
|
||||||
|
compileExpressionStatement(statement: ExpressionStatement): BinaryenExpressionRef {
|
||||||
|
const expression: BinaryenExpressionRef = this.compileExpression(statement.expression, Type.void);
|
||||||
|
return this.currentType == Type.void ? expression : this.module.createDrop(expression);
|
||||||
|
}
|
||||||
|
|
||||||
|
compileForStatement(statement: ForStatement): BinaryenExpressionRef {
|
||||||
|
const initializer: BinaryenExpressionRef = statement.initializer ? this.compileStatement(<Statement>statement.initializer) : 0;
|
||||||
|
const condition: BinaryenExportRef = statement.condition ? this.compileExpression(<Expression>statement.condition, Type.i32) : 0;
|
||||||
|
const incrementor: BinaryenExportRef = statement.incrementor ? this.compileExpression(<Expression>statement.incrementor, Type.void) : 0;
|
||||||
|
throw new Error("not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
compileIfStatement(statement: IfStatement): BinaryenExpressionRef {
|
||||||
|
const condition: BinaryenExpressionRef = this.compileExpression(statement.condition, Type.i32);
|
||||||
|
const ifTrue: BinaryenExpressionRef = this.compileStatement(statement.statement);
|
||||||
|
const ifFalse: BinaryenExportRef = statement.elseStatement ? this.compileStatement(<Statement>statement.elseStatement) : 0;
|
||||||
|
return this.module.createIf(condition, ifTrue, ifFalse);
|
||||||
|
}
|
||||||
|
|
||||||
|
compileReturnStatement(statement: ReturnStatement): BinaryenExpressionRef {
|
||||||
|
if (this.currentFunction) {
|
||||||
|
const expression: BinaryenExpressionRef = statement.expression ? this.compileExpression(<Expression>statement.expression, (<Function>this.currentFunction).returnType) : 0;
|
||||||
|
return this.module.createReturn(expression);
|
||||||
|
}
|
||||||
|
return this.module.createUnreachable();
|
||||||
|
}
|
||||||
|
|
||||||
|
compileSwitchStatement(statement: SwitchStatement): BinaryenExpressionRef {
|
||||||
|
throw new Error("not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
compileThrowStatement(statement: ThrowStatement): BinaryenExpressionRef {
|
||||||
|
return this.module.createUnreachable(); // TODO: waiting for exception-handling spec
|
||||||
|
}
|
||||||
|
|
||||||
|
compileTryStatement(statement: TryStatement): BinaryenExpressionRef {
|
||||||
|
throw new Error("not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
compileVariableStatement(statement: VariableStatement): BinaryenExpressionRef {
|
||||||
|
throw new Error("not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
compileWhileStatement(statement: WhileStatement): BinaryenExpressionRef {
|
||||||
|
throw new Error("not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
// expressions
|
||||||
|
|
||||||
|
compileExpression(expression: Expression, resultType: Type): BinaryenExpressionRef {
|
||||||
|
this.currentType = resultType;
|
||||||
|
|
||||||
|
let expr: BinaryenExpressionRef;
|
||||||
|
switch (expression.kind) {
|
||||||
|
|
||||||
|
case NodeKind.ASSERTION:
|
||||||
|
expr = this.compileAssertionExpression(<AssertionExpression>expression, resultType);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NodeKind.BINARY:
|
||||||
|
expr = this.compileBinaryExpression(<BinaryExpression>expression, resultType);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NodeKind.CALL:
|
||||||
|
expr = this.compileCallExpression(<CallExpression>expression, resultType);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NodeKind.ELEMENTACCESS:
|
||||||
|
expr = this.compileElementAccessExpression(<ElementAccessExpression>expression, resultType);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NodeKind.IDENTIFIER:
|
||||||
|
expr = this.compileIdentifierExpression(<IdentifierExpression>expression, resultType);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NodeKind.LITERAL:
|
||||||
|
expr = this.compileLiteralExpression(<LiteralExpression>expression, resultType);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NodeKind.NEW:
|
||||||
|
expr = this.compileNewExpression(<NewExpression>expression, resultType);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NodeKind.PARENTHESIZED:
|
||||||
|
expr = this.compileParenthesizedExpression(<ParenthesizedExpression>expression, resultType);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NodeKind.PROPERTYACCESS:
|
||||||
|
expr = this.compilePropertyAccessExpression(<PropertyAccessExpression>expression, resultType);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NodeKind.SELECT:
|
||||||
|
expr = this.compileSelectExpression(<SelectExpression>expression, resultType);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NodeKind.UNARYPOSTFIX:
|
||||||
|
expr = this.compileUnaryPostfixExpression(<UnaryPostfixExpression>expression, resultType);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NodeKind.UNARYPREFIX:
|
||||||
|
expr = this.compileUnaryPrefixExpression(<UnaryPrefixExpression>expression, resultType);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new Error("unexpected expression kind");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.currentType != resultType) {
|
||||||
|
expr = this.convertExpression(expr, this.currentType, resultType);
|
||||||
|
this.currentType = resultType;
|
||||||
|
}
|
||||||
|
|
||||||
|
return expr;
|
||||||
|
}
|
||||||
|
|
||||||
|
convertExpression(expr: BinaryenExpressionRef, fromType: Type, toType: Type): BinaryenExpressionRef {
|
||||||
|
|
||||||
|
// void to any
|
||||||
|
if (fromType.kind == TypeKind.VOID)
|
||||||
|
throw new Error("illegal conversion");
|
||||||
|
|
||||||
|
// any to void
|
||||||
|
if (toType.kind == TypeKind.VOID)
|
||||||
|
return this.module.createDrop(expr);
|
||||||
|
|
||||||
|
const fromFloat: bool = fromType.isAnyFloat;
|
||||||
|
const toFloat: bool = toType.isAnyFloat;
|
||||||
|
|
||||||
|
const mod: Module = this.module;
|
||||||
|
let losesInformation: bool = false;
|
||||||
|
|
||||||
|
if (fromFloat) {
|
||||||
|
|
||||||
|
// float to float
|
||||||
|
if (toFloat) {
|
||||||
|
if (fromType.kind == TypeKind.F32) {
|
||||||
|
|
||||||
|
// f32 to f64
|
||||||
|
if (toType.kind == TypeKind.F64)
|
||||||
|
expr = mod.createUnary(UnaryOp.PromoteF32, expr);
|
||||||
|
|
||||||
|
// f64 to f32
|
||||||
|
} else if (toType.kind == TypeKind.F32) {
|
||||||
|
losesInformation = true;
|
||||||
|
expr = mod.createUnary(UnaryOp.DemoteF64, expr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// float to int
|
||||||
|
} else {
|
||||||
|
losesInformation = true;
|
||||||
|
|
||||||
|
// f32 to int
|
||||||
|
if (fromType.kind == TypeKind.F32) {
|
||||||
|
if (toType.isSignedInteger) {
|
||||||
|
if (toType.isLongInteger)
|
||||||
|
expr = mod.createUnary(UnaryOp.TruncF32_I64, expr);
|
||||||
|
else {
|
||||||
|
expr = mod.createUnary(UnaryOp.TruncF32_I32, expr);
|
||||||
|
if (toType.isSmallInteger) {
|
||||||
|
expr = mod.createBinary(BinaryOp.ShlI32, expr, mod.createI32(toType.smallIntegerShift));
|
||||||
|
expr = mod.createBinary(BinaryOp.ShrI32, expr, mod.createI32(toType.smallIntegerShift));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (toType.isLongInteger)
|
||||||
|
expr = mod.createUnary(UnaryOp.TruncF32_U64, expr);
|
||||||
|
else {
|
||||||
|
expr = mod.createUnary(UnaryOp.TruncF32_U32, expr);
|
||||||
|
if (toType.isSmallInteger)
|
||||||
|
expr = mod.createBinary(BinaryOp.AndI32, expr, mod.createI32(toType.smallIntegerMask));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// f64 to int
|
||||||
|
} else {
|
||||||
|
if (toType.isSignedInteger) {
|
||||||
|
if (toType.isLongInteger)
|
||||||
|
expr = mod.createUnary(UnaryOp.TruncF64_I64, expr);
|
||||||
|
else {
|
||||||
|
expr = mod.createUnary(UnaryOp.TruncF64_I32, expr);
|
||||||
|
if (toType.isSmallInteger) {
|
||||||
|
expr = mod.createBinary(BinaryOp.ShlI32, expr, mod.createI32(toType.smallIntegerShift));
|
||||||
|
expr = mod.createBinary(BinaryOp.ShrI32, expr, mod.createI32(toType.smallIntegerShift));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (toType.isLongInteger)
|
||||||
|
expr = mod.createUnary(UnaryOp.TruncF64_U64, expr);
|
||||||
|
else {
|
||||||
|
expr = mod.createUnary(UnaryOp.TruncF64_U32, expr);
|
||||||
|
if (toType.isSmallInteger)
|
||||||
|
expr = mod.createBinary(BinaryOp.AndI32, expr, mod.createI32(toType.smallIntegerMask));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// int to float
|
||||||
|
} else if (toFloat) {
|
||||||
|
|
||||||
|
// int to f32
|
||||||
|
if (toType.kind == TypeKind.F32) {
|
||||||
|
if (fromType.isLongInteger) {
|
||||||
|
losesInformation = true;
|
||||||
|
if (fromType.isSignedInteger)
|
||||||
|
expr = mod.createUnary(UnaryOp.ConvertI64_F32, expr);
|
||||||
|
else
|
||||||
|
expr = mod.createUnary(UnaryOp.ConvertU64_F32, expr);
|
||||||
|
} else
|
||||||
|
if (fromType.isSignedInteger)
|
||||||
|
expr = mod.createUnary(UnaryOp.ConvertI32_F32, expr);
|
||||||
|
else
|
||||||
|
expr = mod.createUnary(UnaryOp.ConvertU32_F32, expr);
|
||||||
|
|
||||||
|
// int to f64
|
||||||
|
} else {
|
||||||
|
if (fromType.isLongInteger) {
|
||||||
|
losesInformation = true;
|
||||||
|
if (fromType.isSignedInteger)
|
||||||
|
expr = mod.createUnary(UnaryOp.ConvertI64_F64, expr);
|
||||||
|
else
|
||||||
|
expr = mod.createUnary(UnaryOp.ConvertU64_F64, expr);
|
||||||
|
} else
|
||||||
|
if (fromType.isSignedInteger)
|
||||||
|
expr = mod.createUnary(UnaryOp.ConvertI32_F64, expr);
|
||||||
|
else
|
||||||
|
expr = mod.createUnary(UnaryOp.ConvertU32_F64, expr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// int to int
|
||||||
|
} else {
|
||||||
|
if (fromType.isLongInteger) {
|
||||||
|
|
||||||
|
// i64 to i32
|
||||||
|
if (!toType.isLongInteger) {
|
||||||
|
losesInformation = true;
|
||||||
|
expr = mod.createUnary(UnaryOp.WrapI64, expr);
|
||||||
|
if (toType.isSmallInteger) {
|
||||||
|
if (toType.isSignedInteger) {
|
||||||
|
expr = mod.createBinary(BinaryOp.ShlI32, expr, mod.createI32(toType.smallIntegerShift));
|
||||||
|
expr = mod.createBinary(BinaryOp.ShrI32, expr, mod.createI32(toType.smallIntegerShift));
|
||||||
|
} else
|
||||||
|
expr = mod.createBinary(BinaryOp.AndI32, expr, mod.createI32(toType.smallIntegerMask));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// i32 to i64
|
||||||
|
} else if (toType.isLongInteger) {
|
||||||
|
if (toType.isSignedInteger)
|
||||||
|
expr = mod.createUnary(UnaryOp.ExtendI32, expr);
|
||||||
|
else
|
||||||
|
expr = mod.createUnary(UnaryOp.ExtendU32, expr);
|
||||||
|
|
||||||
|
// i32 to smaller/change of signage i32
|
||||||
|
} else if (toType.isSmallInteger && (fromType.size > toType.size || (fromType.size == toType.size && fromType.kind != toType.kind))) {
|
||||||
|
losesInformation = true;
|
||||||
|
if (toType.isSignedInteger) {
|
||||||
|
expr = mod.createBinary(BinaryOp.ShlI32, expr, mod.createI32(toType.smallIntegerShift));
|
||||||
|
expr = mod.createBinary(BinaryOp.ShrI32, expr, mod.createI32(toType.smallIntegerShift));
|
||||||
|
} else
|
||||||
|
expr = mod.createBinary(BinaryOp.AndI32, expr, mod.createI32(toType.smallIntegerMask));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return expr;
|
||||||
|
}
|
||||||
|
|
||||||
|
compileAssertionExpression(expression: AssertionExpression, contextualType: Type): BinaryenExpressionRef {
|
||||||
|
const toType: Type | null = this.resolveType(expression.toType); // reports
|
||||||
|
if (toType && toType != contextualType) {
|
||||||
|
const expr: BinaryenExpressionRef = this.compileExpression(expression.expression, <Type>toType);
|
||||||
|
return this.convertExpression(expr, this.currentType, <Type>toType);
|
||||||
|
}
|
||||||
|
return this.compileExpression(expression.expression, contextualType);
|
||||||
|
}
|
||||||
|
|
||||||
|
compileBinaryExpression(expression: BinaryExpression, contextualType: Type): BinaryenExpressionRef {
|
||||||
|
throw new Error("not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
compileCallExpression(expression: CallExpression, contextualType: Type): BinaryenExpressionRef {
|
||||||
|
throw new Error("not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
compileElementAccessExpression(expression: ElementAccessExpression, contextualType: Type): BinaryenExpressionRef {
|
||||||
|
throw new Error("not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
compileIdentifierExpression(expression: IdentifierExpression, contextualType: Type): BinaryenExpressionRef {
|
||||||
|
throw new Error("not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
compileLiteralExpression(expression: LiteralExpression, contextualType: Type): BinaryenExpressionRef {
|
||||||
|
switch (expression.literalKind) {
|
||||||
|
// case LiteralKind.ARRAY:
|
||||||
|
|
||||||
|
case LiteralKind.FLOAT:
|
||||||
|
if (contextualType == Type.f32)
|
||||||
|
return this.module.createF32((<FloatLiteralExpression>expression).value);
|
||||||
|
this.currentType = Type.f64;
|
||||||
|
return this.module.createF64((<FloatLiteralExpression>expression).value);
|
||||||
|
|
||||||
|
case LiteralKind.INTEGER:
|
||||||
|
if (contextualType == Type.bool)
|
||||||
|
return this.module.createI32((<IntegerLiteralExpression>expression).value.isOdd ? 1 : 0)
|
||||||
|
if (contextualType.isLongInteger)
|
||||||
|
return this.module.createI64((<IntegerLiteralExpression>expression).value.lo, (<IntegerLiteralExpression>expression).value.hi);
|
||||||
|
const value: i32 = (<IntegerLiteralExpression>expression).value.toI32();
|
||||||
|
if (contextualType.isSmallInteger)
|
||||||
|
return contextualType.isSignedInteger
|
||||||
|
? this.module.createI32(value << contextualType.smallIntegerShift >> contextualType.smallIntegerShift)
|
||||||
|
: this.module.createI32(value & contextualType.smallIntegerMask);
|
||||||
|
return this.module.createI32(value);
|
||||||
|
|
||||||
|
// case LiteralKind.OBJECT:
|
||||||
|
// case LiteralKind.REGEXP:
|
||||||
|
// case LiteralKind.STRING:
|
||||||
|
}
|
||||||
|
throw new Error("not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
compileNewExpression(expression: NewExpression, contextualType: Type): BinaryenExpressionRef {
|
||||||
|
throw new Error("not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
compileParenthesizedExpression(expression: ParenthesizedExpression, contextualType: Type): BinaryenExpressionRef {
|
||||||
|
return this.compileExpression(expression.expression, contextualType);
|
||||||
|
}
|
||||||
|
|
||||||
|
compilePropertyAccessExpression(expression: PropertyAccessExpression, contextualType: Type): BinaryenExpressionRef {
|
||||||
|
throw new Error("not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
compileSelectExpression(expression: SelectExpression, contextualType: Type): BinaryenExpressionRef {
|
||||||
|
const condition: BinaryenExpressionRef = this.compileExpression(expression.condition, Type.i32);
|
||||||
|
const ifThen: BinaryenExpressionRef = this.compileExpression(expression.ifThen, contextualType);
|
||||||
|
const ifElse: BinaryenExpressionRef = this.compileExpression(expression.ifElse, contextualType);
|
||||||
|
return this.module.createSelect(condition, ifThen, ifElse);
|
||||||
|
}
|
||||||
|
|
||||||
|
compileUnaryPostfixExpression(expression: UnaryPostfixExpression, contextualType: Type): BinaryenExpressionRef {
|
||||||
|
throw new Error("not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
compileUnaryPrefixExpression(expression: UnaryPrefixExpression, contextualType: Type): BinaryenExpressionRef {
|
||||||
|
throw new Error("not implemented");
|
||||||
|
}
|
||||||
|
}
|
100
src/diagnosticMessages.generated.ts
Normal file
100
src/diagnosticMessages.generated.ts
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
// code below is generated from diagnosticsMessages.json by scripts/build-diagnostics
|
||||||
|
|
||||||
|
export enum DiagnosticCode {
|
||||||
|
Conversion_from_type_0_to_1_requires_an_explicit_cast = 100,
|
||||||
|
Basic_type_0_cannot_be_nullable = 101,
|
||||||
|
Unterminated_string_literal = 1002,
|
||||||
|
Identifier_expected = 1003,
|
||||||
|
_0_expected = 1005,
|
||||||
|
A_file_cannot_have_a_reference_to_itself = 1006,
|
||||||
|
Trailing_comma_not_allowed = 1009,
|
||||||
|
Unexpected_token = 1012,
|
||||||
|
A_rest_parameter_must_be_last_in_a_parameter_list = 1014,
|
||||||
|
A_required_parameter_cannot_follow_an_optional_parameter = 1016,
|
||||||
|
Statements_are_not_allowed_in_ambient_contexts = 1036,
|
||||||
|
Initializers_are_not_allowed_in_ambient_contexts = 1039,
|
||||||
|
_0_modifier_cannot_be_used_here = 1042,
|
||||||
|
Type_parameters_cannot_appear_on_a_constructor_declaration = 1092,
|
||||||
|
Type_annotation_cannot_appear_on_a_constructor_declaration = 1093,
|
||||||
|
An_accessor_cannot_have_type_parameters = 1094,
|
||||||
|
A_set_accessor_cannot_have_a_return_type_annotation = 1095,
|
||||||
|
Type_parameter_list_cannot_be_empty = 1098,
|
||||||
|
A_return_statement_can_only_be_used_within_a_function_body = 1108,
|
||||||
|
Expression_expected = 1109,
|
||||||
|
Type_expected = 1110,
|
||||||
|
A_default_clause_cannot_appear_more_than_once_in_a_switch_statement = 1113,
|
||||||
|
Duplicate_label_0 = 1114,
|
||||||
|
Octal_literals_are_not_allowed_in_strict_mode = 1121,
|
||||||
|
Digit_expected = 1124,
|
||||||
|
Hexadecimal_digit_expected = 1125,
|
||||||
|
Unexpected_end_of_text = 1126,
|
||||||
|
Invalid_character = 1127,
|
||||||
|
_case_or_default_expected = 1130,
|
||||||
|
String_literal_expected = 1141,
|
||||||
|
Line_break_not_permitted_here = 1142,
|
||||||
|
Declaration_expected = 1146,
|
||||||
|
Unterminated_regular_expression_literal = 1161,
|
||||||
|
Binary_digit_expected = 1177,
|
||||||
|
Octal_digit_expected = 1178,
|
||||||
|
An_implementation_cannot_be_declared_in_ambient_contexts = 1183,
|
||||||
|
An_extended_Unicode_escape_value_must_be_between_0x0_and_0x10FFFF_inclusive = 1198,
|
||||||
|
Unterminated_Unicode_escape_sequence = 1199,
|
||||||
|
_abstract_modifier_can_only_appear_on_a_class_method_or_property_declaration = 1242,
|
||||||
|
Duplicate_identifier_0 = 2300,
|
||||||
|
Cannot_find_name_0 = 2304,
|
||||||
|
Generic_type_0_requires_1_type_argument_s = 2314,
|
||||||
|
Type_0_is_not_generic = 2315,
|
||||||
|
Type_0_is_not_assignable_to_type_1 = 2322,
|
||||||
|
Function_implementation_is_missing_or_not_immediately_following_the_declaration = 2391
|
||||||
|
}
|
||||||
|
|
||||||
|
export function diagnosticCodeToString(code: DiagnosticCode): string {
|
||||||
|
switch (code) {
|
||||||
|
case 100: return "Conversion from type '{0}' to '{1}' requires an explicit cast.";
|
||||||
|
case 101: return "Basic type '{0}' cannot be nullable.";
|
||||||
|
case 1002: return "Unterminated string literal.";
|
||||||
|
case 1003: return "Identifier expected.";
|
||||||
|
case 1005: return "'{0}' expected.";
|
||||||
|
case 1006: return "A file cannot have a reference to itself.";
|
||||||
|
case 1009: return "Trailing comma not allowed.";
|
||||||
|
case 1012: return "Unexpected token.";
|
||||||
|
case 1014: return "A rest parameter must be last in a parameter list.";
|
||||||
|
case 1016: return "A required parameter cannot follow an optional parameter.";
|
||||||
|
case 1036: return "Statements are not allowed in ambient contexts.";
|
||||||
|
case 1039: return "Initializers are not allowed in ambient contexts.";
|
||||||
|
case 1042: return "'{0}' modifier cannot be used here.";
|
||||||
|
case 1092: return "Type parameters cannot appear on a constructor declaration.";
|
||||||
|
case 1093: return "Type annotation cannot appear on a constructor declaration.";
|
||||||
|
case 1094: return "An accessor cannot have type parameters.";
|
||||||
|
case 1095: return "A 'set' accessor cannot have a return type annotation.";
|
||||||
|
case 1098: return "Type parameter list cannot be empty.";
|
||||||
|
case 1108: return "A 'return' statement can only be used within a function body.";
|
||||||
|
case 1109: return "Expression expected.";
|
||||||
|
case 1110: return "Type expected.";
|
||||||
|
case 1113: return "A 'default' clause cannot appear more than once in a 'switch' statement.";
|
||||||
|
case 1114: return "Duplicate label '{0}'.";
|
||||||
|
case 1121: return "Octal literals are not allowed in strict mode.";
|
||||||
|
case 1124: return "Digit expected.";
|
||||||
|
case 1125: return "Hexadecimal digit expected.";
|
||||||
|
case 1126: return "Unexpected end of text.";
|
||||||
|
case 1127: return "Invalid character.";
|
||||||
|
case 1130: return "'case' or 'default' expected.";
|
||||||
|
case 1141: return "String literal expected.";
|
||||||
|
case 1142: return "Line break not permitted here.";
|
||||||
|
case 1146: return "Declaration expected.";
|
||||||
|
case 1161: return "Unterminated regular expression literal.";
|
||||||
|
case 1177: return "Binary digit expected.";
|
||||||
|
case 1178: return "Octal digit expected.";
|
||||||
|
case 1183: return "An implementation cannot be declared in ambient contexts.";
|
||||||
|
case 1198: return "An extended Unicode escape value must be between 0x0 and 0x10FFFF inclusive.";
|
||||||
|
case 1199: return "Unterminated Unicode escape sequence.";
|
||||||
|
case 1242: return "'abstract' modifier can only appear on a class, method, or property declaration.";
|
||||||
|
case 2300: return "Duplicate identifier '{0}'.";
|
||||||
|
case 2304: return "Cannot find name '{0}'.";
|
||||||
|
case 2314: return "Generic type '{0}' requires {1} type argument(s).";
|
||||||
|
case 2315: return "Type '{0}' is not generic.";
|
||||||
|
case 2322: return "Type '{0}' is not assignable to type '{1}'.";
|
||||||
|
case 2391: return "Function implementation is missing or not immediately following the declaration.";
|
||||||
|
default: return "";
|
||||||
|
}
|
||||||
|
}
|
49
src/diagnosticMessages.json
Normal file
49
src/diagnosticMessages.json
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
{
|
||||||
|
"Conversion from type '{0}' to '{1}' requires an explicit cast.": 100,
|
||||||
|
"Basic type '{0}' cannot be nullable.": 101,
|
||||||
|
|
||||||
|
"Unterminated string literal.": 1002,
|
||||||
|
"Identifier expected.": 1003,
|
||||||
|
"'{0}' expected.": 1005,
|
||||||
|
"A file cannot have a reference to itself.": 1006,
|
||||||
|
"Trailing comma not allowed.": 1009,
|
||||||
|
"Unexpected token.": 1012,
|
||||||
|
"A rest parameter must be last in a parameter list.": 1014,
|
||||||
|
"A required parameter cannot follow an optional parameter.": 1016,
|
||||||
|
"Statements are not allowed in ambient contexts.": 1036,
|
||||||
|
"Initializers are not allowed in ambient contexts.": 1039,
|
||||||
|
"'{0}' modifier cannot be used here.": 1042,
|
||||||
|
"Type parameters cannot appear on a constructor declaration.": 1092,
|
||||||
|
"Type annotation cannot appear on a constructor declaration.": 1093,
|
||||||
|
"An accessor cannot have type parameters.": 1094,
|
||||||
|
"A 'set' accessor cannot have a return type annotation.": 1095,
|
||||||
|
"Type parameter list cannot be empty.": 1098,
|
||||||
|
"A 'return' statement can only be used within a function body.": 1108,
|
||||||
|
"Expression expected.": 1109,
|
||||||
|
"Type expected.": 1110,
|
||||||
|
"A 'default' clause cannot appear more than once in a 'switch' statement.": 1113,
|
||||||
|
"Duplicate label '{0}'.": 1114,
|
||||||
|
"Octal literals are not allowed in strict mode.": 1121,
|
||||||
|
"Digit expected.": 1124,
|
||||||
|
"Hexadecimal digit expected.": 1125,
|
||||||
|
"Unexpected end of text.": 1126,
|
||||||
|
"Invalid character.": 1127,
|
||||||
|
"'case' or 'default' expected.": 1130,
|
||||||
|
"String literal expected.": 1141,
|
||||||
|
"Line break not permitted here.": 1142,
|
||||||
|
"Declaration expected.": 1146,
|
||||||
|
"Unterminated regular expression literal.": 1161,
|
||||||
|
"Binary digit expected.": 1177,
|
||||||
|
"Octal digit expected.": 1178,
|
||||||
|
"An implementation cannot be declared in ambient contexts.": 1183,
|
||||||
|
"An extended Unicode escape value must be between 0x0 and 0x10FFFF inclusive.": 1198,
|
||||||
|
"Unterminated Unicode escape sequence.": 1199,
|
||||||
|
"'abstract' modifier can only appear on a class, method, or property declaration.": 1242,
|
||||||
|
|
||||||
|
"Duplicate identifier '{0}'.": 2300,
|
||||||
|
"Cannot find name '{0}'.": 2304,
|
||||||
|
"Generic type '{0}' requires {1} type argument(s).": 2314,
|
||||||
|
"Type '{0}' is not generic.": 2315,
|
||||||
|
"Type '{0}' is not assignable to type '{1}'.": 2322,
|
||||||
|
"Function implementation is missing or not immediately following the declaration.": 2391
|
||||||
|
}
|
167
src/diagnostics.ts
Normal file
167
src/diagnostics.ts
Normal file
@ -0,0 +1,167 @@
|
|||||||
|
import { Range } from "./ast";
|
||||||
|
import { CharCode, isLineBreak, sb } from "./util";
|
||||||
|
import { DiagnosticCode, diagnosticCodeToString } from "./diagnosticMessages.generated";
|
||||||
|
|
||||||
|
export { DiagnosticCode, diagnosticCodeToString } from "./diagnosticMessages.generated";
|
||||||
|
|
||||||
|
export enum DiagnosticCategory {
|
||||||
|
INFO,
|
||||||
|
WARNING,
|
||||||
|
ERROR
|
||||||
|
}
|
||||||
|
|
||||||
|
export function diagnosticCategoryToString(category: DiagnosticCategory): string {
|
||||||
|
if (category == DiagnosticCategory.INFO) return "INFO";
|
||||||
|
if (category == DiagnosticCategory.WARNING) return "WARNING";
|
||||||
|
if (category == DiagnosticCategory.ERROR) return "ERROR";
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
const colorBlue: string = "\u001b[93m";
|
||||||
|
const colorYellow: string = "\u001b[93m";
|
||||||
|
const colorRed: string = "\u001b[91m";
|
||||||
|
const colorReset: string = "\u001b[0m";
|
||||||
|
|
||||||
|
export function diagnosticCategoryToColor(category: DiagnosticCategory): string {
|
||||||
|
if (category == DiagnosticCategory.INFO) return colorBlue;
|
||||||
|
if (category == DiagnosticCategory.WARNING) return colorYellow;
|
||||||
|
if (category == DiagnosticCategory.ERROR) return colorRed;
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
export class DiagnosticMessage {
|
||||||
|
|
||||||
|
code: i32;
|
||||||
|
category: DiagnosticCategory;
|
||||||
|
message: string;
|
||||||
|
range: Range | null = null;
|
||||||
|
|
||||||
|
constructor(code: i32, category: DiagnosticCategory, message: string) {
|
||||||
|
this.code = code;
|
||||||
|
this.category = category;
|
||||||
|
this.message = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
static create(code: DiagnosticCode, category: DiagnosticCategory, arg0: string | null = null, arg1: string | null = null): DiagnosticMessage {
|
||||||
|
let message: string = diagnosticCodeToString(code);
|
||||||
|
if (arg0 != null)
|
||||||
|
message = message.replace("{0}", arg0);
|
||||||
|
if (arg1 != null)
|
||||||
|
message = message.replace("{1}", arg1);
|
||||||
|
return new DiagnosticMessage(code, category, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
static createInfo(code: DiagnosticCode, arg0: string | null = null, arg1: string | null = null): DiagnosticMessage {
|
||||||
|
return DiagnosticMessage.create(code, DiagnosticCategory.INFO, arg0, arg1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static createWarning(code: DiagnosticCode, arg0: string | null = null, arg1: string | null = null): DiagnosticMessage {
|
||||||
|
return DiagnosticMessage.create(code, DiagnosticCategory.WARNING, arg0, arg1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static createError(code: DiagnosticCode, arg0: string | null = null, arg1: string | null = null): DiagnosticMessage {
|
||||||
|
return DiagnosticMessage.create(code, DiagnosticCategory.ERROR, arg0, arg1);
|
||||||
|
}
|
||||||
|
|
||||||
|
withRange(range: Range): this {
|
||||||
|
this.range = range;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function formatDiagnosticMessage(message: DiagnosticMessage, useColors: bool = false, showContext: bool = false): string {
|
||||||
|
// format context first (uses same string builder)
|
||||||
|
let context: string = "";
|
||||||
|
if (message.range && showContext)
|
||||||
|
context = formatDiagnosticContext(message.range, useColors)
|
||||||
|
|
||||||
|
// general information
|
||||||
|
sb.length = 0;
|
||||||
|
if (useColors) sb.push(diagnosticCategoryToColor(message.category));
|
||||||
|
sb.push(diagnosticCategoryToString(message.category));
|
||||||
|
if (useColors) sb.push(colorReset);
|
||||||
|
sb.push(" AS");
|
||||||
|
sb.push(message.code.toString());
|
||||||
|
sb.push(": ");
|
||||||
|
sb.push(message.message);
|
||||||
|
|
||||||
|
// range information if available
|
||||||
|
if (message.range) {
|
||||||
|
const range: Range = message.range;
|
||||||
|
const text: string = range.source.text;
|
||||||
|
if (showContext) {
|
||||||
|
sb.push("\n");
|
||||||
|
sb.push(context);
|
||||||
|
}
|
||||||
|
sb.push("\n");
|
||||||
|
let pos: i32 = range.start;
|
||||||
|
let line: i32 = 1;
|
||||||
|
let column: i32 = 0;
|
||||||
|
while (pos-- > 0)
|
||||||
|
if (isLineBreak(text.charCodeAt(pos)))
|
||||||
|
line++;
|
||||||
|
else if (line == 1)
|
||||||
|
column++;
|
||||||
|
sb.push(" in ");
|
||||||
|
sb.push(range.source.path);
|
||||||
|
sb.push("(");
|
||||||
|
sb.push(line.toString());
|
||||||
|
sb.push(",");
|
||||||
|
sb.push(column.toString());
|
||||||
|
sb.push(")");
|
||||||
|
}
|
||||||
|
return sb.join("");
|
||||||
|
}
|
||||||
|
|
||||||
|
export function formatDiagnosticContext(range: Range, useColors: bool = false): string {
|
||||||
|
const text: string = range.source.text;
|
||||||
|
const len: i32 = text.length;
|
||||||
|
let start: i32 = range.start;
|
||||||
|
let end: i32 = range.end;
|
||||||
|
while (start > 0 && !isLineBreak(text.charCodeAt(start - 1)))
|
||||||
|
start--;
|
||||||
|
while (end < len && !isLineBreak(text.charCodeAt(end)))
|
||||||
|
end++;
|
||||||
|
sb.length = 0;
|
||||||
|
sb.push("\n ");
|
||||||
|
sb.push(text.substring(start, end));
|
||||||
|
sb.push("\n ");
|
||||||
|
while (start < range.start) {
|
||||||
|
sb.push(" ");
|
||||||
|
start++;
|
||||||
|
}
|
||||||
|
if (useColors) sb.push(colorRed);
|
||||||
|
if (range.start == range.end) {
|
||||||
|
sb.push("^");
|
||||||
|
} else while (start++ < range.end)
|
||||||
|
sb.push("~");
|
||||||
|
if (useColors) sb.push(colorReset);
|
||||||
|
return sb.join("");
|
||||||
|
}
|
||||||
|
|
||||||
|
export abstract class DiagnosticEmitter {
|
||||||
|
|
||||||
|
diagnostics: DiagnosticMessage[];
|
||||||
|
|
||||||
|
constructor(diagnostics: DiagnosticMessage[] | null = null) {
|
||||||
|
this.diagnostics = diagnostics ? <DiagnosticMessage[]>diagnostics : new Array();
|
||||||
|
}
|
||||||
|
|
||||||
|
emitDiagnostic(code: DiagnosticCode, category: DiagnosticCategory, range: Range, arg0: string | null = null, arg1: string | null = null) {
|
||||||
|
const message: DiagnosticMessage = DiagnosticMessage.create(code, category, arg0, arg1).withRange(range);
|
||||||
|
this.diagnostics.push(message);
|
||||||
|
console.log(formatDiagnosticMessage(message, true, true)); // temporary
|
||||||
|
}
|
||||||
|
|
||||||
|
error(code: DiagnosticCode, range: Range, arg0: string | null = null, arg1: string | null = null): void {
|
||||||
|
this.emitDiagnostic(code, DiagnosticCategory.ERROR, range, arg0, arg1);
|
||||||
|
}
|
||||||
|
|
||||||
|
info(code: DiagnosticCode, range: Range, arg0: string | null = null, arg1: string | null = null): void {
|
||||||
|
this.emitDiagnostic(code, DiagnosticCategory.INFO, range, arg0, arg1);
|
||||||
|
}
|
||||||
|
|
||||||
|
warning(code: DiagnosticCode, range: Range, arg0: string | null = null, arg1: string | null = null): void {
|
||||||
|
this.emitDiagnostic(code, DiagnosticCategory.WARNING, range, arg0, arg1);
|
||||||
|
}
|
||||||
|
}
|
16
src/glue/js.d.ts
vendored
Normal file
16
src/glue/js.d.ts
vendored
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
// Aliased AssemblyScript types. Beware of semantic differences.
|
||||||
|
declare type i8 = number;
|
||||||
|
declare type u8 = number;
|
||||||
|
declare type i16 = number;
|
||||||
|
declare type u16 = number;
|
||||||
|
declare type i32 = number;
|
||||||
|
declare type u32 = number;
|
||||||
|
declare type isize = number;
|
||||||
|
declare type usize = number;
|
||||||
|
declare type f32 = number;
|
||||||
|
declare type f64 = number;
|
||||||
|
declare type bool = boolean;
|
||||||
|
|
||||||
|
// Raw memory access (here: Binaryen memory, T=u8)
|
||||||
|
declare function store<T>(ptr: usize, val: u8): void;
|
||||||
|
declare function load<T>(ptr: usize): u8;
|
16
src/glue/js.ts
Normal file
16
src/glue/js.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
const globalScope = typeof window !== "undefined" && window
|
||||||
|
|| typeof global !== "undefined" && global
|
||||||
|
|| self;
|
||||||
|
|
||||||
|
globalScope["store"] = function store_u8(ptr, val) {
|
||||||
|
binaryen.HEAPU8[ptr] = val;
|
||||||
|
};
|
||||||
|
|
||||||
|
globalScope["load"] = function load_u8(ptr) {
|
||||||
|
return binaryen.HEAPU8[ptr];
|
||||||
|
};
|
||||||
|
|
||||||
|
const binaryen = require("binaryen");
|
||||||
|
for (const key in binaryen)
|
||||||
|
if (/^_(?:Binaryen|Relooper|malloc$|free$)/.test(key))
|
||||||
|
globalScope[key] = binaryen[key];
|
0
src/glue/wasm.ts
Normal file
0
src/glue/wasm.ts
Normal file
58
src/index.ts
Normal file
58
src/index.ts
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
Exports a C-like API to the embedder.
|
||||||
|
|
||||||
|
[obtain entrySource, entryPath]
|
||||||
|
parseFile(entrySource, entryPath) -> parser
|
||||||
|
while nextPath = nextFile(parser)
|
||||||
|
[obtain nextSource]
|
||||||
|
parseFile(nextSource, nextPath)
|
||||||
|
|
||||||
|
Checking for errors:
|
||||||
|
|
||||||
|
while diagnostic = nextDiagnostic(parser)
|
||||||
|
[print] formatDiagnostic(diagnostic, useColors?, showContext?)
|
||||||
|
if (isError(diagnostic))
|
||||||
|
[abort parsing afterwards]
|
||||||
|
|
||||||
|
compile(parser) -> module
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Module } from "./binaryen";
|
||||||
|
import { Compiler } from "./compiler";
|
||||||
|
import { DiagnosticMessage, DiagnosticCategory, DiagnosticCode } from "./diagnostics";
|
||||||
|
import { Parser } from "./parser";
|
||||||
|
import { Program } from "./program";
|
||||||
|
|
||||||
|
export function parseFile(text: string, path: string, parser: Parser | null = null): Parser {
|
||||||
|
let isEntry: bool = false;
|
||||||
|
if (!parser) {
|
||||||
|
parser = new Parser();
|
||||||
|
isEntry = true;
|
||||||
|
}
|
||||||
|
parser.parseFile(text, path, isEntry);
|
||||||
|
return parser;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function nextFile(parser: Parser): string | null {
|
||||||
|
return parser.nextFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function nextDiagnostic(parser: Parser): DiagnosticMessage | null {
|
||||||
|
const program: Program = parser.program;
|
||||||
|
if (program.diagnosticsOffset < program.diagnostics.length)
|
||||||
|
return program.diagnostics[program.diagnosticsOffset++];
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isError(message: DiagnosticMessage): bool {
|
||||||
|
return message.category == DiagnosticCategory.ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function compile(parser: Parser): Module {
|
||||||
|
const program: Program = parser.finish();
|
||||||
|
return Compiler.compile(program);
|
||||||
|
}
|
||||||
|
|
||||||
|
export { formatDiagnosticMessage as formatDiagnostic } from "./diagnostics";
|
1543
src/parser.ts
Normal file
1543
src/parser.ts
Normal file
File diff suppressed because it is too large
Load Diff
325
src/program.ts
Normal file
325
src/program.ts
Normal file
@ -0,0 +1,325 @@
|
|||||||
|
import { Target } from "./compiler";
|
||||||
|
import { DiagnosticCode, DiagnosticMessage, DiagnosticEmitter } from "./diagnostics";
|
||||||
|
import { Source, Type, Class, Enum, Function, GlobalVariable, Namespace } from "./reflection";
|
||||||
|
import { hasModifier } from "./parser";
|
||||||
|
import { normalizePath, resolvePath } from "./util";
|
||||||
|
import {
|
||||||
|
|
||||||
|
Node,
|
||||||
|
NodeKind,
|
||||||
|
SourceNode,
|
||||||
|
ModifierKind,
|
||||||
|
|
||||||
|
ClassDeclaration,
|
||||||
|
DeclarationStatement,
|
||||||
|
EnumDeclaration,
|
||||||
|
EnumValueDeclaration,
|
||||||
|
FieldDeclaration,
|
||||||
|
FunctionDeclaration,
|
||||||
|
ImportDeclaration,
|
||||||
|
ImportStatement,
|
||||||
|
InterfaceDeclaration,
|
||||||
|
MethodDeclaration,
|
||||||
|
NamespaceDeclaration,
|
||||||
|
Statement,
|
||||||
|
VariableDeclaration,
|
||||||
|
VariableStatement
|
||||||
|
|
||||||
|
} from "./ast";
|
||||||
|
|
||||||
|
export class Program extends DiagnosticEmitter {
|
||||||
|
|
||||||
|
sources: Source[];
|
||||||
|
diagnosticsOffset: i32 = 0;
|
||||||
|
target: Target = Target.WASM32;
|
||||||
|
|
||||||
|
names: Map<string,DeclarationStatement> = new Map();
|
||||||
|
types: Map<string,Type> = new Map();
|
||||||
|
|
||||||
|
classes: Class[] = new Array();
|
||||||
|
enums: Enum[] = new Array();
|
||||||
|
functions: Function[] = new Array();
|
||||||
|
globals: GlobalVariable[] = new Array();
|
||||||
|
namespaces: Namespace[] = new Array();
|
||||||
|
|
||||||
|
constructor(diagnostics: DiagnosticMessage[] | null = null) {
|
||||||
|
super(diagnostics);
|
||||||
|
this.sources = new Array();
|
||||||
|
}
|
||||||
|
|
||||||
|
initialize(target: Target): void {
|
||||||
|
this.target = target;
|
||||||
|
initializeBasicTypes(this.types, target);
|
||||||
|
|
||||||
|
const importStatements: ImportStatement[] = new Array();
|
||||||
|
|
||||||
|
// build a lookup map of global names to declarations
|
||||||
|
for (let i: i32 = 0, k: i32 = this.sources.length; i < k; ++i) {
|
||||||
|
const source: Source = this.sources[i];
|
||||||
|
const statements: Statement[] = source.statements;
|
||||||
|
for (let j: i32 = 0, l: i32 = statements.length; j < l; ++j) {
|
||||||
|
const statement: Statement = statements[j];
|
||||||
|
switch (statement.kind) {
|
||||||
|
|
||||||
|
case NodeKind.CLASS:
|
||||||
|
this.initializeClass(<ClassDeclaration>statement);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NodeKind.ENUM:
|
||||||
|
this.initializeEnum(<EnumDeclaration>statement);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NodeKind.FUNCTION:
|
||||||
|
this.initializeFunction(<FunctionDeclaration>statement);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NodeKind.IMPORT:
|
||||||
|
this.initializeImports(<ImportStatement>statement);
|
||||||
|
importStatements.push(<ImportStatement>statement);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NodeKind.INTERFACE:
|
||||||
|
this.initializeInterface(<InterfaceDeclaration>statement);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NodeKind.NAMESPACE:
|
||||||
|
this.initializeNamespace(<NamespaceDeclaration>statement);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NodeKind.VARIABLE:
|
||||||
|
this.initializeVariables(<VariableStatement>statement);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// resolve imports to their respective declarations
|
||||||
|
for (let i: i32 = 0, k: i32 = importStatements.length; i < k; ++i) {
|
||||||
|
const statement: ImportStatement = importStatements[i];
|
||||||
|
const importPath: string = resolvePath(normalizePath(statement.path), statement.range.source.normalizedPath);
|
||||||
|
const members: ImportDeclaration[] = statement.declarations;
|
||||||
|
for (let j: i32 = 0, l: i32 = members.length; j < l; ++j){
|
||||||
|
const declaration: ImportDeclaration = members[j];
|
||||||
|
const importedName: string = declaration.externalIdentifier.name;
|
||||||
|
for (let m: i32 = 0, n: i32 = this.sources.length; m < n; ++n) {
|
||||||
|
const source: Source = this.sources[m];
|
||||||
|
if (source.path == importPath) {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
initializeClass(declaration: ClassDeclaration): void {
|
||||||
|
this.addName(declaration);
|
||||||
|
const members: DeclarationStatement[] = declaration.members;
|
||||||
|
for (let j: i32 = 0, l: i32 = members.length; j < l; ++j) {
|
||||||
|
const statement: Statement = members[j];
|
||||||
|
switch (statement.kind) {
|
||||||
|
|
||||||
|
case NodeKind.FIELD:
|
||||||
|
this.initializeField(<FieldDeclaration>statement);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NodeKind.METHOD:
|
||||||
|
this.initializeMethod(<MethodDeclaration>statement);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new Error("unexpected class member");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
initializeField(declaration: FieldDeclaration): void {
|
||||||
|
this.addName(declaration);
|
||||||
|
}
|
||||||
|
|
||||||
|
initializeEnum(declaration: EnumDeclaration): void {
|
||||||
|
this.addName(declaration);
|
||||||
|
const members: EnumValueDeclaration[] = declaration.members;
|
||||||
|
for (let i: i32 = 0, k: i32 = members.length; i < k; ++i)
|
||||||
|
this.initializeEnumValue(members[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
initializeEnumValue(declaration: EnumValueDeclaration): void {
|
||||||
|
this.addName(declaration);
|
||||||
|
}
|
||||||
|
|
||||||
|
initializeFunction(declaration: FunctionDeclaration): void {
|
||||||
|
this.addName(declaration);
|
||||||
|
}
|
||||||
|
|
||||||
|
initializeImports(statement: ImportStatement): void {
|
||||||
|
const members: ImportDeclaration[] = statement.declarations;
|
||||||
|
for (let i: i32 = 0, k: i32 = members.length; i < k; ++i)
|
||||||
|
this.initializeImport(members[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
initializeImport(declaration: ImportDeclaration): void {
|
||||||
|
this.addName(declaration);
|
||||||
|
}
|
||||||
|
|
||||||
|
initializeInterface(declaration: InterfaceDeclaration): void {
|
||||||
|
this.addName(declaration);
|
||||||
|
const members: Statement[] = declaration.members;
|
||||||
|
for (let j: i32 = 0, l: i32 = members.length; j < l; ++j) {
|
||||||
|
const statement: Statement = members[j];
|
||||||
|
switch (statement.kind) {
|
||||||
|
|
||||||
|
case NodeKind.FIELD:
|
||||||
|
this.initializeField(<FieldDeclaration>statement);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NodeKind.METHOD:
|
||||||
|
this.initializeMethod(<MethodDeclaration>statement);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new Error("unexpected interface member");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
initializeMethod(declaration: MethodDeclaration): void {
|
||||||
|
this.addName(declaration);
|
||||||
|
}
|
||||||
|
|
||||||
|
initializeNamespace(declaration: NamespaceDeclaration): void {
|
||||||
|
this.addName(declaration);
|
||||||
|
const members: Statement[] = declaration.members;
|
||||||
|
for (let j: i32 = 0, l: i32 = members.length; j < l; ++j) {
|
||||||
|
const statement: Statement = members[j];
|
||||||
|
switch (statement.kind) {
|
||||||
|
|
||||||
|
case NodeKind.CLASS:
|
||||||
|
this.initializeClass(<ClassDeclaration>statement);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NodeKind.ENUM:
|
||||||
|
this.initializeEnum(<EnumDeclaration>statement);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NodeKind.FUNCTION:
|
||||||
|
this.initializeFunction(<FunctionDeclaration>statement);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NodeKind.INTERFACE:
|
||||||
|
this.initializeInterface(<InterfaceDeclaration>statement);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NodeKind.NAMESPACE:
|
||||||
|
this.initializeNamespace(<NamespaceDeclaration>statement);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NodeKind.VARIABLE:
|
||||||
|
this.initializeVariables(<VariableStatement>statement);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new Error("unexpected namespace member");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
initializeVariables(statement: VariableStatement): void {
|
||||||
|
const declarations: VariableDeclaration[] = statement.members;
|
||||||
|
for (let i: i32 = 0, k = declarations.length; i < k; ++i) {
|
||||||
|
const declaration: VariableDeclaration = declarations[i];
|
||||||
|
this.addName(declaration);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addName(declaration: DeclarationStatement): void {
|
||||||
|
const name: string = this.mangleName(declaration);
|
||||||
|
if (this.names.has(name))
|
||||||
|
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, name); // recoverable
|
||||||
|
else
|
||||||
|
this.names.set(name, declaration);
|
||||||
|
}
|
||||||
|
|
||||||
|
mangleName(declaration: DeclarationStatement): string {
|
||||||
|
let name: string = declaration.identifier.name;
|
||||||
|
let parent: Node | null = declaration.parent;
|
||||||
|
if (parent) {
|
||||||
|
switch (parent.kind) {
|
||||||
|
|
||||||
|
case NodeKind.SOURCE:
|
||||||
|
return (<SourceNode>parent).path + "/" + name;
|
||||||
|
|
||||||
|
case NodeKind.CLASS: {
|
||||||
|
if (
|
||||||
|
(declaration.kind == NodeKind.FIELD && !hasModifier(ModifierKind.STATIC, (<FieldDeclaration>declaration).modifiers)) ||
|
||||||
|
(declaration.kind == NodeKind.METHOD && !hasModifier(ModifierKind.STATIC, (<MethodDeclaration>declaration).modifiers))
|
||||||
|
)
|
||||||
|
return this.mangleName(<DeclarationStatement>parent) + "#" + name;
|
||||||
|
// otherwise fall through
|
||||||
|
}
|
||||||
|
case NodeKind.ENUM:
|
||||||
|
case NodeKind.ENUMVALUE:
|
||||||
|
case NodeKind.NAMESPACE:
|
||||||
|
return this.mangleName(<DeclarationStatement>parent) + "." + name;
|
||||||
|
|
||||||
|
case NodeKind.IMPORT: {
|
||||||
|
const impParent: Node | null = (<ImportStatement>parent).parent;
|
||||||
|
if (impParent && impParent.kind == NodeKind.SOURCE)
|
||||||
|
return (<SourceNode>impParent).path + "/" + name;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case NodeKind.VARIABLE: {
|
||||||
|
const varParent: Node | null = (<VariableStatement>parent).parent;
|
||||||
|
if (varParent) {
|
||||||
|
if (varParent.kind == NodeKind.SOURCE)
|
||||||
|
return <SourceNode>varParent == this.sources[0] ? name : (<SourceNode>varParent).path + "/" + name;
|
||||||
|
if (varParent.kind == NodeKind.NAMESPACE)
|
||||||
|
return this.mangleName(<DeclarationStatement>varParent) + "." + name;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new Error("unexpected parent");
|
||||||
|
}
|
||||||
|
|
||||||
|
addClass(cl: Class): void {
|
||||||
|
cl.declaration.reflectionIndex = this.classes.length;
|
||||||
|
this.classes.push(cl);
|
||||||
|
}
|
||||||
|
|
||||||
|
addEnum(en: Enum): void {
|
||||||
|
en.declaration.reflectionIndex = this.enums.length;
|
||||||
|
this.enums.push(en);
|
||||||
|
}
|
||||||
|
|
||||||
|
addFunction(fn: Function): void {
|
||||||
|
fn.declaration.reflectionIndex = this.functions.length;
|
||||||
|
this.functions.push(fn);
|
||||||
|
}
|
||||||
|
|
||||||
|
addGlobal(gl: GlobalVariable): void {
|
||||||
|
gl.declaration.reflectionIndex = this.globals.length;
|
||||||
|
this.globals.push(gl);
|
||||||
|
}
|
||||||
|
|
||||||
|
addNamespace(ns: Namespace): void {
|
||||||
|
ns.declaration.reflectionIndex = this.namespaces.length;
|
||||||
|
this.namespaces.push(ns);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function initializeBasicTypes(types: Map<string,Type>, target: Target) {
|
||||||
|
types.set("i8", Type.i8);
|
||||||
|
types.set("i16", Type.i16);
|
||||||
|
types.set("i32", Type.i32);
|
||||||
|
types.set("i64", Type.i64);
|
||||||
|
types.set("isize", target == Target.WASM32 ? Type.isize32 : Type.isize64);
|
||||||
|
types.set("u8", Type.u8);
|
||||||
|
types.set("u16", Type.u16);
|
||||||
|
types.set("u32", Type.u32);
|
||||||
|
types.set("u64", Type.u64);
|
||||||
|
types.set("usize", target == Target.WASM32 ? Type.usize32 : Type.usize64);
|
||||||
|
types.set("bool", Type.bool);
|
||||||
|
types.set("void", Type.void);
|
||||||
|
}
|
310
src/reflection.ts
Normal file
310
src/reflection.ts
Normal file
@ -0,0 +1,310 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
Reflection objects are largely independent of their respective declarations in
|
||||||
|
order to make it easier to introduce internal objects.
|
||||||
|
|
||||||
|
Base
|
||||||
|
├ Class
|
||||||
|
├ Enum
|
||||||
|
├ Field
|
||||||
|
├ Function
|
||||||
|
│ └ Method
|
||||||
|
├ Import
|
||||||
|
├ Namespace
|
||||||
|
├ Source
|
||||||
|
├ Type ~ TypeKind
|
||||||
|
└ VariableBase
|
||||||
|
├ GlobalVariable
|
||||||
|
└ LocalVariable
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {
|
||||||
|
ClassDeclaration,
|
||||||
|
DeclarationStatement,
|
||||||
|
EnumDeclaration,
|
||||||
|
Expression,
|
||||||
|
FunctionDeclaration,
|
||||||
|
ImportDeclaration,
|
||||||
|
ImportStatement,
|
||||||
|
MethodDeclaration,
|
||||||
|
ModifierKind,
|
||||||
|
NamespaceDeclaration,
|
||||||
|
Node,
|
||||||
|
FieldDeclaration,
|
||||||
|
SourceNode,
|
||||||
|
Statement,
|
||||||
|
NodeKind,
|
||||||
|
TypeParameter,
|
||||||
|
VariableDeclaration,
|
||||||
|
VariableStatement
|
||||||
|
} from "./ast";
|
||||||
|
import { DiagnosticMessage } from "./diagnostics";
|
||||||
|
import { Token, Tokenizer, Range } from "./tokenizer";
|
||||||
|
import { hasModifier } from "./parser";
|
||||||
|
import { normalizePath } from "./util";
|
||||||
|
|
||||||
|
export abstract class Base {
|
||||||
|
|
||||||
|
name: string;
|
||||||
|
exportName: string | null = null;
|
||||||
|
|
||||||
|
constructor(name: string) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
get isExport(): bool { return this.exportName != null; }
|
||||||
|
|
||||||
|
exportAs(exportName: string): this {
|
||||||
|
this.exportName = exportName;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const enum TypeKind {
|
||||||
|
I8,
|
||||||
|
I16,
|
||||||
|
I32,
|
||||||
|
I64,
|
||||||
|
ISIZE,
|
||||||
|
U8,
|
||||||
|
U16,
|
||||||
|
U32,
|
||||||
|
U64,
|
||||||
|
USIZE,
|
||||||
|
F32,
|
||||||
|
F64,
|
||||||
|
BOOL,
|
||||||
|
VOID
|
||||||
|
}
|
||||||
|
|
||||||
|
export function typeKindToString(kind: TypeKind): string {
|
||||||
|
switch (kind) {
|
||||||
|
case TypeKind.I8: return "i8";
|
||||||
|
case TypeKind.I16: return "i16";
|
||||||
|
case TypeKind.I32: return "i32";
|
||||||
|
case TypeKind.I64: return "i64";
|
||||||
|
case TypeKind.ISIZE: return "isize";
|
||||||
|
case TypeKind.U8: return "u8";
|
||||||
|
case TypeKind.U16: return "u16";
|
||||||
|
case TypeKind.U32: return "u32";
|
||||||
|
case TypeKind.U64: return "u64";
|
||||||
|
case TypeKind.USIZE: return "usize";
|
||||||
|
case TypeKind.F32: return "f32";
|
||||||
|
case TypeKind.F64: return "f64";
|
||||||
|
case TypeKind.BOOL: return "bool";
|
||||||
|
case TypeKind.VOID: return "void";
|
||||||
|
}
|
||||||
|
return "invalid";
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Type extends Base {
|
||||||
|
|
||||||
|
kind: TypeKind;
|
||||||
|
size: i32;
|
||||||
|
|
||||||
|
constructor(kind: TypeKind, size: i32) {
|
||||||
|
super(typeKindToString(kind));
|
||||||
|
this.kind = kind;
|
||||||
|
this.size = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
get bitSize(): i32 { return this.size << 3; }
|
||||||
|
get smallIntegerShift(): i32 { return 32 - (this.size << 3); }
|
||||||
|
get smallIntegerMask(): i32 { return -1 >>> 32 - (this.size << 3); }
|
||||||
|
|
||||||
|
get isAnyInteger(): bool { return this.kind >= TypeKind.I8 && this.kind <= TypeKind.USIZE; }
|
||||||
|
get isSmallInteger(): bool { return this.size == 1 || this.size == 2; }
|
||||||
|
get isLongInteger(): bool { return this.size == 8 && this.kind != TypeKind.F64; }
|
||||||
|
get isUnsignedInteger(): bool { return this.kind >= TypeKind.U8 && this.kind <= TypeKind.USIZE; }
|
||||||
|
get isSignedInteger(): bool { return this.kind >= TypeKind.I8 && this.kind <= TypeKind.ISIZE; }
|
||||||
|
get isAnySize(): bool { return this.kind == TypeKind.ISIZE || this.kind == TypeKind.USIZE; }
|
||||||
|
get isAnyFloat(): bool { return this.kind == TypeKind.F32 || this.kind == TypeKind.F64; }
|
||||||
|
|
||||||
|
toString(): string {
|
||||||
|
return typeKindToString(this.kind);
|
||||||
|
}
|
||||||
|
|
||||||
|
static readonly i8: Type = new Type(TypeKind.I8, 1);
|
||||||
|
static readonly i16: Type = new Type(TypeKind.I16, 2);
|
||||||
|
static readonly i32: Type = new Type(TypeKind.I32, 4);
|
||||||
|
static readonly i64: Type = new Type(TypeKind.I64, 8);
|
||||||
|
static readonly isize32: Type = new Type(TypeKind.I32, 4);
|
||||||
|
static readonly isize64: Type = new Type(TypeKind.I64, 8);
|
||||||
|
static readonly u8: Type = new Type(TypeKind.U8, 1);
|
||||||
|
static readonly u16: Type = new Type(TypeKind.U16, 2);
|
||||||
|
static readonly u32: Type = new Type(TypeKind.U32, 4);
|
||||||
|
static readonly u64: Type = new Type(TypeKind.U64, 8);
|
||||||
|
static readonly usize32: Type = new Type(TypeKind.U32, 4);
|
||||||
|
static readonly usize64: Type = new Type(TypeKind.U64, 8);
|
||||||
|
static readonly f32: Type = new Type(TypeKind.F32, 4);
|
||||||
|
static readonly f64: Type = new Type(TypeKind.F64, 8);
|
||||||
|
static readonly bool: Type = new Type(TypeKind.BOOL, 1);
|
||||||
|
static readonly void: Type = new Type(TypeKind.VOID, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Source extends SourceNode {
|
||||||
|
|
||||||
|
text: string;
|
||||||
|
tokenizer: Tokenizer | null;
|
||||||
|
statements: Statement[];
|
||||||
|
isEntry: bool;
|
||||||
|
normalizedPath: string;
|
||||||
|
|
||||||
|
constructor(path: string, text: string, isEntry: bool = false) {
|
||||||
|
super();
|
||||||
|
this.range = new Range(this, 0, text.length);
|
||||||
|
this.path = path;
|
||||||
|
this.text = text;
|
||||||
|
this.statements = new Array();
|
||||||
|
this.isEntry = isEntry;
|
||||||
|
this.normalizedPath = normalizePath(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
get isDeclaration(): bool { return !this.isEntry && this.path.endsWith(".d.ts"); }
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Import extends Base {
|
||||||
|
|
||||||
|
declaration: ImportDeclaration | null;
|
||||||
|
externalName: string;
|
||||||
|
|
||||||
|
static create(declaration: ImportStatement): Import[] {
|
||||||
|
const count: i32 = declaration.declarations.length;
|
||||||
|
const imports: Import[] = new Array(count);
|
||||||
|
for (let i: i32 = 0; i < count; ++i) {
|
||||||
|
const decl: ImportDeclaration = declaration.declarations[i];
|
||||||
|
const imprt: Import = new Import(decl.identifier.name);
|
||||||
|
imprt.declaration = decl;
|
||||||
|
imprt.externalName = decl.externalIdentifier.name;
|
||||||
|
imports[i] = imprt;
|
||||||
|
}
|
||||||
|
return imports;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export abstract class Variable extends Base {
|
||||||
|
type: Type;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class GlobalVariable extends Variable {
|
||||||
|
|
||||||
|
declaration: VariableDeclaration;
|
||||||
|
mutable: bool;
|
||||||
|
|
||||||
|
static create(declaration: VariableStatement): GlobalVariable[] {
|
||||||
|
const mutable: bool = hasModifier(ModifierKind.CONST, declaration.modifiers);
|
||||||
|
const count: i32 = declaration.members.length;
|
||||||
|
const variables: GlobalVariable[] = new Array(count);
|
||||||
|
for (let i: i32 = 0; i < count; ++i) {
|
||||||
|
const decl: VariableDeclaration = declaration.members[i];
|
||||||
|
const variable: GlobalVariable = new GlobalVariable(decl.identifier.name);
|
||||||
|
variable.declaration = decl;
|
||||||
|
variable.mutable = mutable;
|
||||||
|
variables[i] = variable;
|
||||||
|
}
|
||||||
|
return variables;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class LocalVariable extends Variable {
|
||||||
|
|
||||||
|
declaration: VariableDeclaration;
|
||||||
|
index: i32;
|
||||||
|
|
||||||
|
static create(declaration: VariableStatement, index: i32): LocalVariable[] {
|
||||||
|
const count: i32 = declaration.members.length;
|
||||||
|
const variables: LocalVariable[] = new Array(count);
|
||||||
|
for (let i: i32 = 0; i < count; ++i) {
|
||||||
|
const decl: VariableDeclaration = declaration.members[i];
|
||||||
|
const variable: LocalVariable = new LocalVariable(decl.identifier.name);
|
||||||
|
variable.declaration = decl;
|
||||||
|
variable.index = index;
|
||||||
|
variables[i] = variable;
|
||||||
|
}
|
||||||
|
return variables;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Namespace extends Base {
|
||||||
|
|
||||||
|
declaration: NamespaceDeclaration;
|
||||||
|
members: Base[];
|
||||||
|
|
||||||
|
static create(declaration: NamespaceDeclaration): Namespace {
|
||||||
|
const ns: Namespace = new Namespace(declaration.identifier.name);
|
||||||
|
ns.declaration = declaration;
|
||||||
|
const members: Base[] = ns.members = new Array();
|
||||||
|
// TODO: insert members
|
||||||
|
return ns;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Enum extends Base {
|
||||||
|
|
||||||
|
declaration: EnumDeclaration;
|
||||||
|
isConst: bool;
|
||||||
|
values: Map<string,Expression | null> = new Map();
|
||||||
|
|
||||||
|
static create(declaration: EnumDeclaration): Enum {
|
||||||
|
const enm: Enum = new Enum(declaration.identifier.name);
|
||||||
|
enm.declaration = declaration;
|
||||||
|
enm.isConst = hasModifier(ModifierKind.CONST, declaration.modifiers);
|
||||||
|
// TODO: insert values
|
||||||
|
return enm;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Function extends Base {
|
||||||
|
|
||||||
|
declaration: FunctionDeclaration;
|
||||||
|
typeArguments: Type[];
|
||||||
|
returnType: Type;
|
||||||
|
statements: Statement[];
|
||||||
|
|
||||||
|
static create(declaration: FunctionDeclaration, typeArguments: Type[]): Function {
|
||||||
|
throw new Error("not implemented");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Class extends Base {
|
||||||
|
|
||||||
|
declaration: ClassDeclaration;
|
||||||
|
typeArguments: Type[];
|
||||||
|
baseClass: Class | null;
|
||||||
|
memberNames: Set<string>;
|
||||||
|
methods: Map<string,Method>;
|
||||||
|
fields: Map<string,Field>;
|
||||||
|
|
||||||
|
static create(declaration: ClassDeclaration, typeArguments: Type[]): Class {
|
||||||
|
const clazz: Class = new Class(declaration.identifier.name);
|
||||||
|
clazz.typeArguments = typeArguments;
|
||||||
|
return clazz;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Method extends Function {
|
||||||
|
|
||||||
|
declaration: MethodDeclaration; // more specific
|
||||||
|
isInstance: bool;
|
||||||
|
|
||||||
|
static create(declaration: MethodDeclaration, typeArguments: Type[]): Method {
|
||||||
|
throw new Error("not implemented");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Field extends Base {
|
||||||
|
|
||||||
|
declaration: FieldDeclaration | null;
|
||||||
|
type: Type;
|
||||||
|
offset: i32;
|
||||||
|
initializer: Expression | null;
|
||||||
|
|
||||||
|
static create(declaration: FieldDeclaration, offset: i32): Field {
|
||||||
|
const field: Field = new Field(declaration.identifier.name);
|
||||||
|
field.declaration = declaration;
|
||||||
|
field.offset = offset;
|
||||||
|
field.initializer = declaration.initializer;
|
||||||
|
return field;
|
||||||
|
}
|
||||||
|
}
|
1158
src/tokenizer.ts
Normal file
1158
src/tokenizer.ts
Normal file
File diff suppressed because one or more lines are too long
33
src/tsconfig.json
Normal file
33
src/tsconfig.json
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "es5",
|
||||||
|
"module": "commonjs",
|
||||||
|
"lib": [
|
||||||
|
"dom",
|
||||||
|
"es6"
|
||||||
|
],
|
||||||
|
"types": [
|
||||||
|
"node"
|
||||||
|
],
|
||||||
|
"strictNullChecks": true,
|
||||||
|
"alwaysStrict": true,
|
||||||
|
"outDir": "../out"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"ast.ts",
|
||||||
|
"binaryen.d.ts",
|
||||||
|
"binaryen.ts",
|
||||||
|
"compiler.ts",
|
||||||
|
"diagnosticMessages.generated.ts",
|
||||||
|
"diagnostics.ts",
|
||||||
|
"glue/js.d.ts",
|
||||||
|
"glue/js.ts",
|
||||||
|
"index.ts",
|
||||||
|
"parser.ts",
|
||||||
|
"program.ts",
|
||||||
|
"reflection.ts",
|
||||||
|
"tokenizer.ts",
|
||||||
|
"util.ts",
|
||||||
|
"util/i64.ts"
|
||||||
|
]
|
||||||
|
}
|
228
src/util.ts
Normal file
228
src/util.ts
Normal file
@ -0,0 +1,228 @@
|
|||||||
|
export { I64, U64 } from "./util/i64";
|
||||||
|
|
||||||
|
export const enum CharCode {
|
||||||
|
|
||||||
|
NULL = 0,
|
||||||
|
LINEFEED = 0x0A,
|
||||||
|
CARRIAGERETURN = 0x0D,
|
||||||
|
LINESEPARATOR = 0x2028,
|
||||||
|
PARAGRAPHSEPARATOR = 0x2029,
|
||||||
|
NEXTLINE = 0x0085,
|
||||||
|
|
||||||
|
SPACE = 0x20,
|
||||||
|
NONBREAKINGSPACE = 0xA0,
|
||||||
|
ENQUAD = 0x2000,
|
||||||
|
EMQUAD = 0x2001,
|
||||||
|
ENSPACE = 0x2002,
|
||||||
|
EMSPACE = 0x2003,
|
||||||
|
THREEPEREMSPACE = 0x2004,
|
||||||
|
FOURPEREMSPACE = 0x2005,
|
||||||
|
SIXPEREMSPACE = 0x2006,
|
||||||
|
FIGURESPACE = 0x2007,
|
||||||
|
PUNCTUATIONSPACE = 0x2008,
|
||||||
|
THINSPACE = 0x2009,
|
||||||
|
HAIRSPACE = 0x200A,
|
||||||
|
ZEROWIDTHSPACE = 0x200B,
|
||||||
|
NARRINOBREAKSPACE = 0x202F,
|
||||||
|
IDEOGRAPHICSPACE = 0x3000,
|
||||||
|
MATHEMATICALSPACE = 0x205F,
|
||||||
|
OGHAM = 0x1680,
|
||||||
|
|
||||||
|
_ = 0x5F,
|
||||||
|
$ = 0x24,
|
||||||
|
|
||||||
|
_0 = 0x30,
|
||||||
|
_1 = 0x31,
|
||||||
|
_2 = 0x32,
|
||||||
|
_3 = 0x33,
|
||||||
|
_4 = 0x34,
|
||||||
|
_5 = 0x35,
|
||||||
|
_6 = 0x36,
|
||||||
|
_7 = 0x37,
|
||||||
|
_8 = 0x38,
|
||||||
|
_9 = 0x39,
|
||||||
|
|
||||||
|
a = 0x61,
|
||||||
|
b = 0x62,
|
||||||
|
c = 0x63,
|
||||||
|
d = 0x64,
|
||||||
|
e = 0x65,
|
||||||
|
f = 0x66,
|
||||||
|
g = 0x67,
|
||||||
|
h = 0x68,
|
||||||
|
i = 0x69,
|
||||||
|
j = 0x6A,
|
||||||
|
k = 0x6B,
|
||||||
|
l = 0x6C,
|
||||||
|
m = 0x6D,
|
||||||
|
n = 0x6E,
|
||||||
|
o = 0x6F,
|
||||||
|
p = 0x70,
|
||||||
|
q = 0x71,
|
||||||
|
r = 0x72,
|
||||||
|
s = 0x73,
|
||||||
|
t = 0x74,
|
||||||
|
u = 0x75,
|
||||||
|
v = 0x76,
|
||||||
|
w = 0x77,
|
||||||
|
x = 0x78,
|
||||||
|
y = 0x79,
|
||||||
|
z = 0x7A,
|
||||||
|
|
||||||
|
A = 0x41,
|
||||||
|
B = 0x42,
|
||||||
|
C = 0x43,
|
||||||
|
D = 0x44,
|
||||||
|
E = 0x45,
|
||||||
|
F = 0x46,
|
||||||
|
G = 0x47,
|
||||||
|
H = 0x48,
|
||||||
|
I = 0x49,
|
||||||
|
J = 0x4A,
|
||||||
|
K = 0x4B,
|
||||||
|
L = 0x4C,
|
||||||
|
M = 0x4D,
|
||||||
|
N = 0x4E,
|
||||||
|
O = 0x4F,
|
||||||
|
P = 0x50,
|
||||||
|
Q = 0x51,
|
||||||
|
R = 0x52,
|
||||||
|
S = 0x53,
|
||||||
|
T = 0x54,
|
||||||
|
U = 0x55,
|
||||||
|
V = 0x56,
|
||||||
|
W = 0x57,
|
||||||
|
X = 0x58,
|
||||||
|
Y = 0x59,
|
||||||
|
Z = 0x5a,
|
||||||
|
|
||||||
|
AMPERSAND = 0x26,
|
||||||
|
ASTERISK = 0x2A,
|
||||||
|
AT = 0x40,
|
||||||
|
BACKSLASH = 0x5C,
|
||||||
|
BACKTICK = 0x60,
|
||||||
|
BAR = 0x7C,
|
||||||
|
CARET = 0x5E,
|
||||||
|
CLOSEBRACE = 0x7D,
|
||||||
|
CLOSEBRACKET = 0x5D,
|
||||||
|
CLOSEPAREN = 0x29,
|
||||||
|
COLON = 0x3A,
|
||||||
|
COMMA = 0x2C,
|
||||||
|
DOT = 0x2E,
|
||||||
|
DOUBLEQUOTE = 0x22,
|
||||||
|
EQUALS = 0x3D,
|
||||||
|
EXCLAMATION = 0x21,
|
||||||
|
GREATERTHAN = 0x3E,
|
||||||
|
HASH = 0x23,
|
||||||
|
LESSTHAN = 0x3C,
|
||||||
|
MINUS = 0x2D,
|
||||||
|
OPENBRACE = 0x7B,
|
||||||
|
OPENBRACKET = 0x5B,
|
||||||
|
OPENPAREN = 0x28,
|
||||||
|
PERCENT = 0x25,
|
||||||
|
PLUS = 0x2B,
|
||||||
|
QUESTION = 0x3F,
|
||||||
|
SEMICOLON = 0x3B,
|
||||||
|
SINGLEQUOTE = 0x27,
|
||||||
|
SLASH = 0x2F,
|
||||||
|
TILDE = 0x7E,
|
||||||
|
|
||||||
|
BACKSPACE = 0x08,
|
||||||
|
FORMFEED = 0x0C,
|
||||||
|
BYTEORDERMARK = 0xFEFF,
|
||||||
|
TAB = 0x09,
|
||||||
|
VERTICALTAB = 0x0B
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isLineBreak(c: i32): bool {
|
||||||
|
return c == CharCode.LINEFEED
|
||||||
|
|| c == CharCode.CARRIAGERETURN
|
||||||
|
|| c == CharCode.LINESEPARATOR
|
||||||
|
|| c == CharCode.PARAGRAPHSEPARATOR;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const sb: string[] = new Array(256); // shared string builder. 64-bit without growing: (4+4+8) + 8*256 = 16b + 2kb
|
||||||
|
|
||||||
|
export function normalizePath(path: string, separator: CharCode = CharCode.SLASH): string {
|
||||||
|
// expects a relative path
|
||||||
|
|
||||||
|
let pos: i32 = 0;
|
||||||
|
let len: i32 = path.length;
|
||||||
|
|
||||||
|
// trim leading './'
|
||||||
|
while (pos + 1 < len && path.charCodeAt(pos) == CharCode.DOT && path.charCodeAt(pos + 1) == separator)
|
||||||
|
pos += 2;
|
||||||
|
if (pos > 0) {
|
||||||
|
path = path.substring(pos);
|
||||||
|
len -= pos;
|
||||||
|
pos = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
let atEnd: bool;
|
||||||
|
while (pos + 1 < len) {
|
||||||
|
atEnd = false;
|
||||||
|
|
||||||
|
// we are only interested in '/.' sequences ...
|
||||||
|
if (path.charCodeAt(pos) == separator && path.charCodeAt(pos + 1) == CharCode.DOT) {
|
||||||
|
|
||||||
|
// '/.' ( '/' | $ )
|
||||||
|
if (
|
||||||
|
(atEnd = pos + 2 == len)
|
||||||
|
||
|
||||||
|
pos + 2 < len && path.charCodeAt(pos + 2) == separator
|
||||||
|
) {
|
||||||
|
path = atEnd
|
||||||
|
? path.substring(0, pos)
|
||||||
|
: path.substring(0, pos) + path.substring(pos + 2);
|
||||||
|
len -= 2;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// '/.' ( './' | '.' $ )
|
||||||
|
if (
|
||||||
|
(atEnd = pos + 3 == len) && path.charCodeAt(pos + 2) == CharCode.DOT
|
||||||
|
||
|
||||||
|
pos + 3 < len && path.charCodeAt(pos + 2) == CharCode.DOT && path.charCodeAt(pos + 3) == separator
|
||||||
|
) {
|
||||||
|
|
||||||
|
// find preceeding '/'
|
||||||
|
let ipos: i32 = pos;
|
||||||
|
while (--ipos >= 0) {
|
||||||
|
if (path.charCodeAt(ipos) == separator) {
|
||||||
|
if (pos - ipos != 3 || path.charCodeAt(ipos + 1) != CharCode.DOT || path.charCodeAt(ipos + 2) != CharCode.DOT) { // exclude '..' itself
|
||||||
|
path = atEnd
|
||||||
|
? path.substring(0, ipos)
|
||||||
|
: path.substring(0, ipos) + path.substring(pos + 3);
|
||||||
|
len -= pos + 3 - ipos;
|
||||||
|
pos = ipos - 1; // incremented again at end of loop
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if there's no preceeding '/', trim start if non-empty
|
||||||
|
if (ipos < 0 && pos > 0) {
|
||||||
|
if (pos != 2 || path.charCodeAt(0) != CharCode.DOT || path.charCodeAt(1) != CharCode.DOT) { // exclude '..' itself
|
||||||
|
path = path.substring(pos + 4);
|
||||||
|
len = path.length;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pos++;
|
||||||
|
}
|
||||||
|
return len > 0 ? path : ".";
|
||||||
|
}
|
||||||
|
|
||||||
|
export function dirname(normalizedPath: string, separator: CharCode = CharCode.SLASH): string {
|
||||||
|
let pos: i32 = normalizedPath.length;
|
||||||
|
while (--pos > 0)
|
||||||
|
if (normalizedPath.charCodeAt(pos) == separator)
|
||||||
|
return normalizedPath.substring(0, pos);
|
||||||
|
return ".";
|
||||||
|
}
|
||||||
|
|
||||||
|
export function resolvePath(normalizedPath: string, normalizedOrigin: string, separator: CharCode = CharCode.SLASH): string {
|
||||||
|
return normalizePath(dirname(normalizedOrigin, separator) + String.fromCharCode(separator) + normalizedPath);
|
||||||
|
}
|
520
src/util/i64.ts
Normal file
520
src/util/i64.ts
Normal file
@ -0,0 +1,520 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
To remain compatible with TSC / compiling to JS, we have to emulate I64s in a
|
||||||
|
portable way. The following is based on long.js with the main difference being
|
||||||
|
that instances are mutable and operations affect 'this'. In our scenario,
|
||||||
|
that's useful because it's mostly used for constant evaluation and we are
|
||||||
|
exclusively interested in the result (saves a heap of allocations).
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
// TODO: div/mod
|
||||||
|
|
||||||
|
const I64_MIN_LO: i32 = 0;
|
||||||
|
const I64_MIN_HI: i32 = 0x80000000 | 0;
|
||||||
|
|
||||||
|
export class I64 {
|
||||||
|
|
||||||
|
lo: i32;
|
||||||
|
hi: i32;
|
||||||
|
|
||||||
|
static fromI32(n: i32): I64 {
|
||||||
|
return new I64(n, n < 0 ? -1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(lo: i32 = 0, hi: i32 = 0) {
|
||||||
|
this.lo = lo;
|
||||||
|
this.hi = hi;
|
||||||
|
}
|
||||||
|
|
||||||
|
get isZero(): bool {
|
||||||
|
return this.lo == 0 && this.hi == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
get isPositive(): bool {
|
||||||
|
return this.hi >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
get isNegative(): bool {
|
||||||
|
return this.hi < 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
get isOdd(): bool {
|
||||||
|
return (this.lo & 1) == 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
get isEven(): bool {
|
||||||
|
return (this.lo & 1) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
toI32(): i32 {
|
||||||
|
return this.lo;
|
||||||
|
}
|
||||||
|
|
||||||
|
eq(other: I64): bool {
|
||||||
|
return this.eq32(other.lo, other.hi);
|
||||||
|
}
|
||||||
|
|
||||||
|
eq32(lo: i32, hi: i32 = 0): bool {
|
||||||
|
return this.lo == lo && this.hi == hi;
|
||||||
|
}
|
||||||
|
|
||||||
|
ne(other: I64): bool {
|
||||||
|
return this.ne32(other.lo, other.hi);
|
||||||
|
}
|
||||||
|
|
||||||
|
ne32(lo: i32, hi: i32 = 0): bool {
|
||||||
|
return this.lo != lo || this.hi != hi;
|
||||||
|
}
|
||||||
|
|
||||||
|
neg(): void {
|
||||||
|
this.lo = ~this.lo;
|
||||||
|
this.hi = ~this.hi;
|
||||||
|
this.add32(1, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
add(other: I64): void {
|
||||||
|
this.add32(other.lo, other.hi);
|
||||||
|
}
|
||||||
|
|
||||||
|
add32(lo: i32, hi: i32 = 0): void {
|
||||||
|
i64_add_internal(this.lo, this.hi, lo, hi);
|
||||||
|
this.lo = i64_lo;
|
||||||
|
this.hi = i64_hi;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub(other: I64): void {
|
||||||
|
this.sub32(other.lo, other.hi);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub32(lo: i32, hi: i32 = 0): void {
|
||||||
|
i64_add_internal(~lo, ~hi, 1, 0);
|
||||||
|
this.add32(i64_lo, i64_hi);
|
||||||
|
}
|
||||||
|
|
||||||
|
comp(other: I64): i32 {
|
||||||
|
return this.comp32(other.lo, other.hi);
|
||||||
|
}
|
||||||
|
|
||||||
|
comp32(lo: i32, hi: i32 = 0): i32 {
|
||||||
|
if (this.lo == lo && this.hi == hi)
|
||||||
|
return 0;
|
||||||
|
if (this.hi < 0 && hi >= 0)
|
||||||
|
return -1;
|
||||||
|
if (this.hi >= 0 && hi < 0)
|
||||||
|
return 1;
|
||||||
|
i64_add_internal(~lo, ~hi, 1, 0);
|
||||||
|
i64_add_internal(this.lo, this.hi, i64_lo, i64_hi);
|
||||||
|
return i64_hi < 0 ? -1 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
lt(other: I64): bool {
|
||||||
|
return this.lt32(other.lo, other.hi);
|
||||||
|
}
|
||||||
|
|
||||||
|
lt32(lo: i32, hi: i32 = 0): bool {
|
||||||
|
return this.comp32(lo, hi) < 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
lte(other: I64): bool {
|
||||||
|
return this.lte32(other.lo, other.hi);
|
||||||
|
}
|
||||||
|
|
||||||
|
lte32(lo: i32, hi: i32 = 0): bool {
|
||||||
|
return this.comp32(lo, hi) <= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
gt(other: I64): bool {
|
||||||
|
return this.gt32(other.lo, other.hi);
|
||||||
|
}
|
||||||
|
|
||||||
|
gt32(lo: i32, hi: i32 = 0): bool {
|
||||||
|
return this.comp32(lo, hi) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
gte(other: I64): bool {
|
||||||
|
return this.gte32(other.lo, other.hi);
|
||||||
|
}
|
||||||
|
|
||||||
|
gte32(lo: i32, hi: i32 = 0): bool {
|
||||||
|
return this.comp32(lo, hi) >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
mul(other: I64): void {
|
||||||
|
this.mul32(other.lo, other.hi);
|
||||||
|
}
|
||||||
|
|
||||||
|
mul32(lo: i32, hi: i32 = 0): void {
|
||||||
|
if (this.lo == 0 && this.hi == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (lo == 0 && hi == 0) {
|
||||||
|
this.lo = 0;
|
||||||
|
this.hi = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// this == MIN
|
||||||
|
if (this.lo == I64_MIN_LO && this.hi == I64_MIN_HI) {
|
||||||
|
this.lo = 0; // == MIN_LO
|
||||||
|
this.hi = lo & 1 ? I64_MIN_HI : 0; // other.isOdd ? this = MIN : this = ZERO
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// other == MIN
|
||||||
|
if (lo == I64_MIN_LO && hi == I64_MIN_HI) {
|
||||||
|
this.hi = this.lo & 1 ? I64_MIN_HI : 0; // this.isOdd ? this = MIN : this = ZERO
|
||||||
|
this.lo = 0; // == MIN_LO
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.hi < 0) {
|
||||||
|
this.neg();
|
||||||
|
|
||||||
|
// both negative: negate both and multiply
|
||||||
|
if (hi < 0) {
|
||||||
|
i64_add_internal(~lo, ~hi, 1, 0);
|
||||||
|
i64_mul_internal(this.lo, this.hi, i64_lo, i64_hi);
|
||||||
|
this.lo = i64_lo;
|
||||||
|
this.hi = i64_hi;
|
||||||
|
|
||||||
|
// this negative: negate this, multiply and negate result
|
||||||
|
} else {
|
||||||
|
i64_mul_internal(this.lo, this.hi, lo, hi);
|
||||||
|
this.lo = i64_lo;
|
||||||
|
this.hi = i64_hi;
|
||||||
|
this.neg();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
|
||||||
|
// other negative: negate other, multiply and negate result
|
||||||
|
} else if (hi < 0) {
|
||||||
|
i64_add_internal(~lo, ~hi, 1, 0);
|
||||||
|
i64_mul_internal(this.lo, this.hi, i64_lo, i64_hi);
|
||||||
|
this.lo = i64_lo;
|
||||||
|
this.hi = i64_hi;
|
||||||
|
this.neg();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// both positive
|
||||||
|
i64_mul_internal(this.lo, this.hi, lo, hi);
|
||||||
|
this.lo = i64_lo;
|
||||||
|
this.hi = i64_hi;
|
||||||
|
}
|
||||||
|
|
||||||
|
div(other: I64): void {
|
||||||
|
this.div32(other.lo, other.hi);
|
||||||
|
}
|
||||||
|
|
||||||
|
div32(lo: i32, hi: i32 = 0): void {
|
||||||
|
// other == 0
|
||||||
|
if (lo == 0 && hi == 0)
|
||||||
|
throw new Error("division by zero");
|
||||||
|
|
||||||
|
// this == 0
|
||||||
|
if (this.lo == 0 && this.hi == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// this == MIN
|
||||||
|
if (this.lo == I64_MIN_LO && this.hi == I64_MIN_HI) {
|
||||||
|
|
||||||
|
// other == 1 or -1
|
||||||
|
if (lo == 1 && hi == 0 || lo == -1 && hi == -1) // -MIN == MIN
|
||||||
|
return;
|
||||||
|
|
||||||
|
// both == MIN
|
||||||
|
if (lo == I64_MIN_LO && hi == I64_MIN_HI) {
|
||||||
|
this.lo = 1;
|
||||||
|
this.hi = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// |other| >= 2, so |this/other| < |MIN_VALUE|
|
||||||
|
let tempLo: i32 = this.lo;
|
||||||
|
let tempHi: i32 = this.hi;
|
||||||
|
this.shr32(1, 0);
|
||||||
|
this.div32(lo, hi);
|
||||||
|
this.shl32(1, 0);
|
||||||
|
if (this.lo == 0 && this.hi == 0) {
|
||||||
|
if (hi < 0) {
|
||||||
|
this.lo = 1;
|
||||||
|
this.hi = 0;
|
||||||
|
} else {
|
||||||
|
this.lo = -1;
|
||||||
|
this.hi = -1;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
i64_mul_internal(lo, hi, this.lo, this.hi);
|
||||||
|
this.lo = tempLo;
|
||||||
|
this.hi = tempHi;
|
||||||
|
tempLo = i64_lo;
|
||||||
|
tempHi = i64_hi;
|
||||||
|
this.div32(lo, hi);
|
||||||
|
this.sub32(i64_lo, i64_hi);
|
||||||
|
i64_add_internal(tempLo, tempHi, this.lo, this.hi);
|
||||||
|
this.lo = i64_lo;
|
||||||
|
this.hi = i64_hi;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.hi < 0) {
|
||||||
|
this.neg();
|
||||||
|
|
||||||
|
// both negative: negate both and divide
|
||||||
|
if (hi < 0) {
|
||||||
|
i64_add_internal(~lo, ~hi, 1, 0);
|
||||||
|
i64_div_internal(this.lo, this.hi, i64_lo, i64_hi);
|
||||||
|
this.lo = i64_lo;
|
||||||
|
this.hi = i64_hi;
|
||||||
|
|
||||||
|
// this negative: negate this, divide and negate result
|
||||||
|
} else {
|
||||||
|
i64_div_internal(this.lo, this.hi, lo, hi);
|
||||||
|
this.lo = i64_lo;
|
||||||
|
this.hi = i64_hi;
|
||||||
|
this.neg();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
|
||||||
|
// other negative: negate other, divide and negate result
|
||||||
|
} else if (hi < 0) {
|
||||||
|
i64_add_internal(~lo, ~hi, 1, 0);
|
||||||
|
i64_div_internal(this.lo, this.hi, i64_lo, i64_hi);
|
||||||
|
this.lo = i64_lo;
|
||||||
|
this.hi = i64_hi;
|
||||||
|
this.neg();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// both positive
|
||||||
|
i64_div_internal(this.lo, this.hi, lo, hi);
|
||||||
|
this.lo = i64_lo;
|
||||||
|
this.hi = i64_hi;
|
||||||
|
}
|
||||||
|
|
||||||
|
mod(other: I64): void {
|
||||||
|
this.mod32(other.lo, other.hi);
|
||||||
|
}
|
||||||
|
|
||||||
|
mod32(lo: i32, hi: i32 = 0): void {
|
||||||
|
const thisLo: i32 = this.lo;
|
||||||
|
const thisHi: i32 = this.hi;
|
||||||
|
this.div32(lo, hi);
|
||||||
|
this.mul32(lo, hi);
|
||||||
|
const resLo: i32 = this.lo;
|
||||||
|
const resHi: i32 = this.hi;
|
||||||
|
this.lo = thisLo;
|
||||||
|
this.hi = thisHi;
|
||||||
|
this.sub32(resLo, resHi);
|
||||||
|
}
|
||||||
|
|
||||||
|
not(): void {
|
||||||
|
this.lo = ~this.lo;
|
||||||
|
this.hi = ~this.hi;
|
||||||
|
}
|
||||||
|
|
||||||
|
and(other: I64): void {
|
||||||
|
this.and32(other.lo, other.hi);
|
||||||
|
}
|
||||||
|
|
||||||
|
and32(lo: i32, hi: i32 = 0): void {
|
||||||
|
this.lo &= lo;
|
||||||
|
this.hi &= hi;
|
||||||
|
}
|
||||||
|
|
||||||
|
or(other: I64): void {
|
||||||
|
this.or32(other.lo, other.hi);
|
||||||
|
}
|
||||||
|
|
||||||
|
or32(lo: i32, hi: i32 = 0): void {
|
||||||
|
this.lo |= lo;
|
||||||
|
this.hi |= hi;
|
||||||
|
}
|
||||||
|
|
||||||
|
xor(other: I64): void {
|
||||||
|
this.xor32(other.lo, other.hi);
|
||||||
|
}
|
||||||
|
|
||||||
|
xor32(lo: i32, hi: i32 = 0): void {
|
||||||
|
this.lo ^= lo;
|
||||||
|
this.hi ^= hi;
|
||||||
|
}
|
||||||
|
|
||||||
|
shl(other: I64): void {
|
||||||
|
this.shl32(other.lo, other.hi);
|
||||||
|
}
|
||||||
|
|
||||||
|
shl32(lo: i32, hi: i32 = 0): void {
|
||||||
|
if ((lo &= 63) == 0)
|
||||||
|
return;
|
||||||
|
if (lo < 32) {
|
||||||
|
this.hi = (this.hi << lo) | (this.lo >>> (32 - lo));
|
||||||
|
this.lo = this.lo << lo;
|
||||||
|
} else {
|
||||||
|
this.hi = this.lo << (lo - 32);
|
||||||
|
this.lo = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
shr(other: I64): void {
|
||||||
|
this.shr32(other.lo, other.hi);
|
||||||
|
}
|
||||||
|
|
||||||
|
shr32(lo: i32, hi: i32 = 0): void {
|
||||||
|
if ((lo &= 63) == 0)
|
||||||
|
return;
|
||||||
|
if (lo < 32) {
|
||||||
|
this.lo = (this.lo >>> lo) | (this.hi << (32 - lo));
|
||||||
|
this.hi = this.hi >> lo;
|
||||||
|
} else {
|
||||||
|
this.lo = this.hi >> (lo - 32);
|
||||||
|
this.hi = this.hi >= 0 ? 0 : -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
shru(other: I64): void {
|
||||||
|
this.shru32(other.lo, other.hi);
|
||||||
|
}
|
||||||
|
|
||||||
|
shru32(lo: i32, hi: i32 = 0): void {
|
||||||
|
if ((lo &= 63) == 0)
|
||||||
|
return;
|
||||||
|
if (lo < 32) {
|
||||||
|
this.lo = (this.lo >>> lo) | (this.hi << (32 - lo));
|
||||||
|
this.hi = (this.hi >>> lo) | 0;
|
||||||
|
} else if (lo == 32) {
|
||||||
|
this.lo = this.hi;
|
||||||
|
this.hi = 0;
|
||||||
|
} else {
|
||||||
|
this.lo = (this.hi >>> (lo - 32)) | 0;
|
||||||
|
this.hi = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
clone(): I64 {
|
||||||
|
return new I64(this.lo, this.hi);
|
||||||
|
}
|
||||||
|
|
||||||
|
toString(): string {
|
||||||
|
let negative: bool = false;
|
||||||
|
if (this.hi < 0) {
|
||||||
|
i64_add_internal(~this.lo, ~this.hi, 1, 0);
|
||||||
|
negative = true;
|
||||||
|
} else {
|
||||||
|
i64_lo = this.lo;
|
||||||
|
i64_hi = this.hi;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i64_hi) {
|
||||||
|
let lo: string = (i64_lo as u32 >>> 0).toString(16);
|
||||||
|
while (lo.length < 8)
|
||||||
|
lo = "0" + lo;
|
||||||
|
return (negative ? "-0x" : "0x") + (i64_hi as u32 >>> 0).toString(16) + lo;
|
||||||
|
}
|
||||||
|
return negative ? "-" + i64_lo.toString() : i64_lo.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let i64_lo: i32 = 0;
|
||||||
|
let i64_hi: i32 = 0;
|
||||||
|
|
||||||
|
function i64_add_internal(lo: i32, hi: i32, otherLo: i32, otherHi: i32): void {
|
||||||
|
let a48: i32 = hi >>> 16;
|
||||||
|
let a32: i32 = hi & 0xFFFF;
|
||||||
|
let a16: i32 = lo >>> 16;
|
||||||
|
let a00: i32 = lo & 0xFFFF;
|
||||||
|
|
||||||
|
let b48: i32 = otherHi >>> 16;
|
||||||
|
let b32: i32 = otherHi & 0xFFFF;
|
||||||
|
let b16: i32 = otherLo >>> 16;
|
||||||
|
let b00: i32 = otherLo & 0xFFFF;
|
||||||
|
|
||||||
|
let c48: i32 = 0, c32: i32 = 0, c16: i32 = 0, c00: i32 = 0;
|
||||||
|
c00 += a00 + b00;
|
||||||
|
c16 += c00 >>> 16;
|
||||||
|
c00 &= 0xFFFF;
|
||||||
|
c16 += a16 + b16;
|
||||||
|
c32 += c16 >>> 16;
|
||||||
|
c16 &= 0xFFFF;
|
||||||
|
c32 += a32 + b32;
|
||||||
|
c48 += c32 >>> 16;
|
||||||
|
c32 &= 0xFFFF;
|
||||||
|
c48 += a48 + b48;
|
||||||
|
c48 &= 0xFFFF;
|
||||||
|
|
||||||
|
i64_lo = (c16 << 16) | c00;
|
||||||
|
i64_hi = (c48 << 16) | c32;
|
||||||
|
}
|
||||||
|
|
||||||
|
function i64_mul_internal(lo: i32, hi: i32, otherLo: i32, otherHi: i32): void {
|
||||||
|
let a48: i32 = hi >>> 16;
|
||||||
|
let a32: i32 = hi & 0xFFFF;
|
||||||
|
let a16: i32 = lo >>> 16;
|
||||||
|
let a00: i32 = lo & 0xFFFF;
|
||||||
|
|
||||||
|
let b48: i32 = otherHi >>> 16;
|
||||||
|
let b32: i32 = otherHi & 0xFFFF;
|
||||||
|
let b16: i32 = otherLo >>> 16;
|
||||||
|
let b00: i32 = otherLo & 0xFFFF;
|
||||||
|
|
||||||
|
var c48 = 0, c32 = 0, c16 = 0, c00 = 0;
|
||||||
|
c00 += a00 * b00;
|
||||||
|
c16 += c00 >>> 16;
|
||||||
|
c00 &= 0xFFFF;
|
||||||
|
c16 += a16 * b00;
|
||||||
|
c32 += c16 >>> 16;
|
||||||
|
c16 &= 0xFFFF;
|
||||||
|
c16 += a00 * b16;
|
||||||
|
c32 += c16 >>> 16;
|
||||||
|
c16 &= 0xFFFF;
|
||||||
|
c32 += a32 * b00;
|
||||||
|
c48 += c32 >>> 16;
|
||||||
|
c32 &= 0xFFFF;
|
||||||
|
c32 += a16 * b16;
|
||||||
|
c48 += c32 >>> 16;
|
||||||
|
c32 &= 0xFFFF;
|
||||||
|
c32 += a00 * b32;
|
||||||
|
c48 += c32 >>> 16;
|
||||||
|
c32 &= 0xFFFF;
|
||||||
|
c48 += a48 * b00 + a32 * b16 + a16 * b32 + a00 * b48;
|
||||||
|
c48 &= 0xFFFF;
|
||||||
|
|
||||||
|
i64_lo = (c16 << 16) | c00;
|
||||||
|
i64_hi = (c48 << 16) | c32;
|
||||||
|
}
|
||||||
|
|
||||||
|
function i64_div_internal(lo: i32, hi: i32, otherLo: i32, otherHi: i32): void {
|
||||||
|
throw new Error("not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
export class U64 extends I64 {
|
||||||
|
|
||||||
|
static fromI32(n: i32): U64 {
|
||||||
|
return new U64(n, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
get isPositive(): bool {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
get isNegative(): bool {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
comp32(lo: i32, hi: i32): i32 {
|
||||||
|
// uses both a cast and a js-like shift for portability
|
||||||
|
return ((hi as u32 >>> 0) > (this.hi as u32 >>> 0)) || (hi == this.hi && (lo as u32 >>> 0) > (this.lo as u32 >>> 0)) ? -1 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
neg(): void {
|
||||||
|
this.lo = ~this.lo;
|
||||||
|
this.hi = ~this.hi;
|
||||||
|
this.add32(1, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
clone(): U64 {
|
||||||
|
return new U64(this.lo, this.hi);
|
||||||
|
}
|
||||||
|
}
|
56
tests/binaryen.ts
Normal file
56
tests/binaryen.ts
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
/// <reference path="../src/binaryen.d.ts" />
|
||||||
|
import "../src/glue/js";
|
||||||
|
import { Type, Module, MemorySegment, BinaryOp } from "../src/binaryen";
|
||||||
|
import { Target } from "../src/compiler";
|
||||||
|
import { U64 } from "../src/util";
|
||||||
|
|
||||||
|
const mod = Module.create();
|
||||||
|
|
||||||
|
mod.setMemory(1, Module.MAX_MEMORY_WASM32, [
|
||||||
|
MemorySegment.create(new Uint8Array(4), U64.fromI32(8)),
|
||||||
|
MemorySegment.create(new Uint8Array(4), U64.fromI32(12))
|
||||||
|
], Target.WASM32, "memory");
|
||||||
|
|
||||||
|
const add = mod.addFunctionType("iii", Type.I32, [ Type.I32, Type.I32 ]);
|
||||||
|
mod.addFunction("add", add, [], mod.createReturn(
|
||||||
|
mod.createBinary(BinaryOp.AddI32,
|
||||||
|
mod.createGetLocal(0, Type.I32),
|
||||||
|
mod.createGetLocal(1, Type.I32)
|
||||||
|
)
|
||||||
|
));
|
||||||
|
mod.addExport("add", "add");
|
||||||
|
|
||||||
|
const lit = mod.addFunctionType("I", Type.I64, []);
|
||||||
|
mod.addFunction("lit", lit, [], mod.createReturn(
|
||||||
|
mod.createI64(0, 0x80000000) // I64_MIN
|
||||||
|
));
|
||||||
|
mod.addExport("lit", "lit");
|
||||||
|
|
||||||
|
mod.addGlobal("42", Type.I32, false, mod.createI32(42));
|
||||||
|
|
||||||
|
const aSwitch = mod.addFunctionType("i", Type.I32, []);
|
||||||
|
mod.addFunction("aSwitch", aSwitch, [], mod.createBlock("", [
|
||||||
|
mod.createBlock("a", [
|
||||||
|
mod.createBlock("b", [
|
||||||
|
mod.createBlock("c", [
|
||||||
|
mod.createBlock("d", [
|
||||||
|
mod.createSwitch(
|
||||||
|
["a", "b", "c"],
|
||||||
|
"d",
|
||||||
|
mod.createI32(4)
|
||||||
|
)
|
||||||
|
]),
|
||||||
|
mod.createReturn(mod.createI32(3))
|
||||||
|
]),
|
||||||
|
mod.createReturn(mod.createI32(2))
|
||||||
|
]),
|
||||||
|
mod.createReturn(mod.createI32(1))
|
||||||
|
]),
|
||||||
|
mod.createReturn(mod.createI32(0))
|
||||||
|
]));
|
||||||
|
mod.addExport("aSwitch", "aSwitch");
|
||||||
|
|
||||||
|
// mod.optimize();
|
||||||
|
if (mod.validate())
|
||||||
|
_BinaryenModulePrint(mod.ref);
|
||||||
|
_BinaryenModuleDispose(mod.ref);
|
37
tests/i64.ts
Normal file
37
tests/i64.ts
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
import { I64 } from "../src/util/i64";
|
||||||
|
import * as Long from "long";
|
||||||
|
import * as assert from "assert";
|
||||||
|
|
||||||
|
function test(fn, lo, hi, otherLo, otherHi) {
|
||||||
|
let expected = Long.fromBits(lo, hi)[fn](Long.fromBits(otherLo, otherHi));
|
||||||
|
let actual = new I64(lo, hi); actual[fn + "32"](otherLo, otherHi);
|
||||||
|
assert.equal(actual.lo, expected.low, fn + " lo ");
|
||||||
|
assert.equal(actual.hi, expected.high, fn + " hi");
|
||||||
|
}
|
||||||
|
|
||||||
|
function rand() {
|
||||||
|
let r = Math.random();
|
||||||
|
// 10% edge cases
|
||||||
|
if (r < 0.05) return 0x80000000 | 0;
|
||||||
|
else if (r < 0.1) return 0;
|
||||||
|
return (Math.random() * 0xffffffff) | 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
let i = 0;
|
||||||
|
while (i++ < 1000000) {
|
||||||
|
let lo = rand();
|
||||||
|
let hi = rand();
|
||||||
|
let otherLo = rand();
|
||||||
|
let otherHi = rand();
|
||||||
|
// console.log(lo, hi, otherLo, otherHi);
|
||||||
|
test("add", lo, hi, otherLo, otherHi);
|
||||||
|
test("sub", lo, hi, otherLo, otherHi);
|
||||||
|
test("mul", lo, hi, otherLo, otherHi);
|
||||||
|
test("shl", lo, hi, otherLo, otherHi);
|
||||||
|
test("shr", lo, hi, otherLo, otherHi);
|
||||||
|
test("shru", lo, hi, otherLo, otherHi);
|
||||||
|
test("and", lo, hi, otherLo, otherHi);
|
||||||
|
test("or", lo, hi, otherLo, otherHi);
|
||||||
|
test("xor", lo, hi, otherLo, otherHi);
|
||||||
|
}
|
||||||
|
console.log("done");
|
12
tests/parser/fixtures/class.ts
Normal file
12
tests/parser/fixtures/class.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
export class Test<T> {
|
||||||
|
instanceFunction(): void {
|
||||||
|
}
|
||||||
|
static staticFunction(): void {
|
||||||
|
}
|
||||||
|
get instanceGetter(): i32 {
|
||||||
|
}
|
||||||
|
static set staticSetter(v: i32): i32 {
|
||||||
|
}
|
||||||
|
instanceField: i32;
|
||||||
|
static staticField: i32;
|
||||||
|
}
|
10
tests/parser/fixtures/enum.ts
Normal file
10
tests/parser/fixtures/enum.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
export const enum A {
|
||||||
|
B = 1,
|
||||||
|
C,
|
||||||
|
D = 3
|
||||||
|
}
|
||||||
|
enum E {
|
||||||
|
F,
|
||||||
|
G = 1 + 2,
|
||||||
|
H = 3 * 4
|
||||||
|
}
|
4
tests/parser/fixtures/function.ts
Normal file
4
tests/parser/fixtures/function.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
function simple(): void {
|
||||||
|
}
|
||||||
|
function typeparams<T, V extends T>(a: V | null = null): void {
|
||||||
|
}
|
171
tests/parser/fixtures/precedence.tree.ts
Normal file
171
tests/parser/fixtures/precedence.tree.ts
Normal file
@ -0,0 +1,171 @@
|
|||||||
|
// incrementing precedence
|
||||||
|
a
|
||||||
|
=
|
||||||
|
b
|
||||||
|
?
|
||||||
|
X
|
||||||
|
:
|
||||||
|
c
|
||||||
|
||
|
||||||
|
d
|
||||||
|
&&
|
||||||
|
e
|
||||||
|
|
|
||||||
|
f
|
||||||
|
^
|
||||||
|
g
|
||||||
|
&
|
||||||
|
h
|
||||||
|
==
|
||||||
|
i
|
||||||
|
<
|
||||||
|
j
|
||||||
|
<<
|
||||||
|
k
|
||||||
|
+
|
||||||
|
l
|
||||||
|
/
|
||||||
|
++
|
||||||
|
m
|
||||||
|
++
|
||||||
|
;
|
||||||
|
// decrementing precedence
|
||||||
|
++
|
||||||
|
a
|
||||||
|
++
|
||||||
|
/
|
||||||
|
b
|
||||||
|
+
|
||||||
|
c
|
||||||
|
<<
|
||||||
|
d
|
||||||
|
<
|
||||||
|
e
|
||||||
|
==
|
||||||
|
f
|
||||||
|
&
|
||||||
|
g
|
||||||
|
^
|
||||||
|
h
|
||||||
|
|
|
||||||
|
i
|
||||||
|
&&
|
||||||
|
j
|
||||||
|
||
|
||||||
|
k
|
||||||
|
?
|
||||||
|
X
|
||||||
|
:
|
||||||
|
y
|
||||||
|
=
|
||||||
|
l
|
||||||
|
;
|
||||||
|
// assignment (right-to-left)
|
||||||
|
a
|
||||||
|
=
|
||||||
|
b
|
||||||
|
+=
|
||||||
|
c
|
||||||
|
-=
|
||||||
|
d
|
||||||
|
**=
|
||||||
|
e
|
||||||
|
*=
|
||||||
|
f
|
||||||
|
/=
|
||||||
|
g
|
||||||
|
%=
|
||||||
|
h
|
||||||
|
<<=
|
||||||
|
i
|
||||||
|
>>=
|
||||||
|
j
|
||||||
|
>>>=
|
||||||
|
k
|
||||||
|
&=
|
||||||
|
l
|
||||||
|
^=
|
||||||
|
m
|
||||||
|
|=
|
||||||
|
n
|
||||||
|
;
|
||||||
|
// equality (left-to-right)
|
||||||
|
a
|
||||||
|
==
|
||||||
|
b
|
||||||
|
!=
|
||||||
|
c
|
||||||
|
===
|
||||||
|
d
|
||||||
|
!==
|
||||||
|
e
|
||||||
|
;
|
||||||
|
// relational (left-to-right)
|
||||||
|
a
|
||||||
|
<
|
||||||
|
b
|
||||||
|
<=
|
||||||
|
c
|
||||||
|
>
|
||||||
|
d
|
||||||
|
>=
|
||||||
|
e
|
||||||
|
;
|
||||||
|
// bitwise shift (left-to-right)
|
||||||
|
a
|
||||||
|
<<
|
||||||
|
b
|
||||||
|
>>
|
||||||
|
c
|
||||||
|
>>>
|
||||||
|
d
|
||||||
|
;
|
||||||
|
// addition (left-to-right)
|
||||||
|
a
|
||||||
|
+
|
||||||
|
b
|
||||||
|
-
|
||||||
|
c
|
||||||
|
;
|
||||||
|
// exponentiation (right-to-left), multiplication (left-to-right)
|
||||||
|
a
|
||||||
|
**
|
||||||
|
b
|
||||||
|
*
|
||||||
|
c
|
||||||
|
/
|
||||||
|
d
|
||||||
|
%
|
||||||
|
e
|
||||||
|
**
|
||||||
|
f
|
||||||
|
*
|
||||||
|
g
|
||||||
|
;
|
||||||
|
// prefix (right-to-left)
|
||||||
|
delete
|
||||||
|
void
|
||||||
|
typeof
|
||||||
|
--
|
||||||
|
++
|
||||||
|
-
|
||||||
|
+
|
||||||
|
~
|
||||||
|
!
|
||||||
|
a
|
||||||
|
;
|
||||||
|
// parenthesized
|
||||||
|
(
|
||||||
|
a
|
||||||
|
+
|
||||||
|
(
|
||||||
|
b
|
||||||
|
/
|
||||||
|
c
|
||||||
|
)
|
||||||
|
-
|
||||||
|
d
|
||||||
|
)
|
||||||
|
*
|
||||||
|
e
|
||||||
|
;
|
46
tests/parser/index.ts
Normal file
46
tests/parser/index.ts
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
import * as fs from "fs";
|
||||||
|
import * as diff from "diff";
|
||||||
|
import * as chalk from "chalk";
|
||||||
|
|
||||||
|
import "../../src/glue/js";
|
||||||
|
import { NodeKind, ExpressionStatement } from "../../src/ast";
|
||||||
|
import { Parser } from "../../src/parser";
|
||||||
|
|
||||||
|
const files = fs.readdirSync(__dirname + "/fixtures");
|
||||||
|
files.forEach(filename => {
|
||||||
|
if (filename.charAt(0) == "_") return;
|
||||||
|
const isTree = filename.indexOf(".tree.") >= 0;
|
||||||
|
const parser = new Parser();
|
||||||
|
const text = fs.readFileSync(__dirname + "/fixtures/" + filename, { encoding: "utf8" }).replace(/\r?\n/g, "\n").replace(/^\/\/.*\r?\n/mg, "");
|
||||||
|
parser.parseFile(text, filename, true);
|
||||||
|
var sb: string[] = [];
|
||||||
|
if (isTree) {
|
||||||
|
const statements = parser.program.sources[0].statements;
|
||||||
|
statements.forEach(stmt => {
|
||||||
|
if (stmt.kind != NodeKind.EXPRESSION) return;
|
||||||
|
(<ExpressionStatement>stmt).expression.serializeAsTree(sb, 0);
|
||||||
|
sb.push(";\n");
|
||||||
|
});
|
||||||
|
} else
|
||||||
|
parser.program.sources[0].serialize(sb);
|
||||||
|
const actual = sb.join("");
|
||||||
|
const expected = isTree ? text : text.replace(/^\s+/mg, "");
|
||||||
|
const diffs = diff.diffLines(expected, actual);
|
||||||
|
let changed = false;
|
||||||
|
diffs.forEach(part => {
|
||||||
|
if (part.added || part.removed)
|
||||||
|
changed = true;
|
||||||
|
});
|
||||||
|
if (changed) { // print it
|
||||||
|
console.log("Differences in " + filename + ":");
|
||||||
|
diffs.forEach(part => {
|
||||||
|
if (part.added || part.removed)
|
||||||
|
changed = true;
|
||||||
|
process.stderr.write((part.added ? chalk.green : part.removed ? chalk.red : chalk.grey)(part.value));
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
console.log("No differences in " + filename + ".");
|
||||||
|
}
|
||||||
|
// parser.program.initialize();
|
||||||
|
// console.log(parser.program.names);
|
||||||
|
});
|
8
tests/path.ts
Normal file
8
tests/path.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import { normalizePath, resolvePath } from "../src/util";
|
||||||
|
import * as path from "path";
|
||||||
|
|
||||||
|
let test = "./Y/./N/./N/../../././../Y/./.";
|
||||||
|
console.log(normalizePath(test));
|
||||||
|
console.log(path.posix.normalize(test));
|
||||||
|
|
||||||
|
console.log(resolvePath("../../..", "lib/util/i64.ts"));
|
23
tests/tokenizer.ts
Normal file
23
tests/tokenizer.ts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import "../src/glue/js";
|
||||||
|
import { Tokenizer, Token } from "../src/tokenizer";
|
||||||
|
import { Source } from "../src/reflection";
|
||||||
|
import * as fs from "fs";
|
||||||
|
|
||||||
|
const text = fs.readFileSync(__dirname + "/../src/tokenizer.ts").toString();
|
||||||
|
|
||||||
|
const tn = new Tokenizer(new Source("tokenizer.ts", text));
|
||||||
|
|
||||||
|
let token;
|
||||||
|
do {
|
||||||
|
token = tn.next();
|
||||||
|
let range = tn.range();
|
||||||
|
console.log(Token[token] + " -> " + range.source.text.substring(range.start, range.end));
|
||||||
|
if (token == Token.IDENTIFIER)
|
||||||
|
console.log("> " + tn.readIdentifier());
|
||||||
|
else if (token == Token.INTEGERLITERAL)
|
||||||
|
console.log("> " + tn.readInteger());
|
||||||
|
else if (token == Token.FLOATLITERAL)
|
||||||
|
console.log("> " + tn.readFloat());
|
||||||
|
else if (token == Token.STRINGLITERAL)
|
||||||
|
console.log("> " + tn.readString());
|
||||||
|
} while (token != Token.ENDOFFILE);
|
Loading…
x
Reference in New Issue
Block a user