mirror of
https://github.com/voltrevo/ValueScript.git
synced 2026-01-13 15:38:06 -05:00
Port playground
This commit is contained in:
3
website2/.gitignore
vendored
3
website2/.gitignore
vendored
@@ -21,3 +21,6 @@ dist-ssr
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
|
||||
# Custom
|
||||
/public/value_script_bg.wasm
|
||||
|
||||
44
website2/package-lock.json
generated
44
website2/package-lock.json
generated
@@ -21,8 +21,10 @@
|
||||
"eslint": "^8.36.0",
|
||||
"eslint-config-prettier": "^8.8.0",
|
||||
"eslint-plugin-react": "^7.32.2",
|
||||
"monaco-editor": "^0.36.1",
|
||||
"sass": "^1.60.0",
|
||||
"typescript": "^4.9.3",
|
||||
"valuescript": "^0.0.4",
|
||||
"vite": "^4.2.0"
|
||||
}
|
||||
},
|
||||
@@ -2553,6 +2555,12 @@
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/monaco-editor": {
|
||||
"version": "0.36.1",
|
||||
"resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.36.1.tgz",
|
||||
"integrity": "sha512-/CaclMHKQ3A6rnzBzOADfwdSJ25BFoFT0Emxsc4zYVyav5SkK9iA6lEtIeuN/oRYbwPgviJT+t3l+sjFa28jYg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/ms": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||
@@ -3386,6 +3394,15 @@
|
||||
"punycode": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/valuescript": {
|
||||
"version": "0.0.4",
|
||||
"resolved": "https://registry.npmjs.org/valuescript/-/valuescript-0.0.4.tgz",
|
||||
"integrity": "sha512-fYzoanlq7loL6co6WEw5+5RwcoIN6vOoOkTaotkzmYIsWAuAf/rUlnOWteR962vQdnrVZDS0RUMvJiXzJbBS3Q==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"wait-your-turn": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/vite": {
|
||||
"version": "4.2.1",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-4.2.1.tgz",
|
||||
@@ -3435,6 +3452,12 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/wait-your-turn": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/wait-your-turn/-/wait-your-turn-1.0.1.tgz",
|
||||
"integrity": "sha512-UejbIY32KXhghXGwH4J2pTKUNvgdrCjdDGrtrdfHHJUAwXCok1l9ptEp4n13lg6PuyQIgxPGkWyKeJvvKeAqsA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/which": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
||||
@@ -5196,6 +5219,12 @@
|
||||
"brace-expansion": "^1.1.7"
|
||||
}
|
||||
},
|
||||
"monaco-editor": {
|
||||
"version": "0.36.1",
|
||||
"resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.36.1.tgz",
|
||||
"integrity": "sha512-/CaclMHKQ3A6rnzBzOADfwdSJ25BFoFT0Emxsc4zYVyav5SkK9iA6lEtIeuN/oRYbwPgviJT+t3l+sjFa28jYg==",
|
||||
"dev": true
|
||||
},
|
||||
"ms": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||
@@ -5763,6 +5792,15 @@
|
||||
"punycode": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"valuescript": {
|
||||
"version": "0.0.4",
|
||||
"resolved": "https://registry.npmjs.org/valuescript/-/valuescript-0.0.4.tgz",
|
||||
"integrity": "sha512-fYzoanlq7loL6co6WEw5+5RwcoIN6vOoOkTaotkzmYIsWAuAf/rUlnOWteR962vQdnrVZDS0RUMvJiXzJbBS3Q==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"wait-your-turn": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"vite": {
|
||||
"version": "4.2.1",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-4.2.1.tgz",
|
||||
@@ -5776,6 +5814,12 @@
|
||||
"rollup": "^3.18.0"
|
||||
}
|
||||
},
|
||||
"wait-your-turn": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/wait-your-turn/-/wait-your-turn-1.0.1.tgz",
|
||||
"integrity": "sha512-UejbIY32KXhghXGwH4J2pTKUNvgdrCjdDGrtrdfHHJUAwXCok1l9ptEp4n13lg6PuyQIgxPGkWyKeJvvKeAqsA==",
|
||||
"dev": true
|
||||
},
|
||||
"which": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
||||
|
||||
@@ -22,8 +22,10 @@
|
||||
"eslint": "^8.36.0",
|
||||
"eslint-config-prettier": "^8.8.0",
|
||||
"eslint-plugin-react": "^7.32.2",
|
||||
"monaco-editor": "^0.36.1",
|
||||
"sass": "^1.60.0",
|
||||
"typescript": "^4.9.3",
|
||||
"valuescript": "^0.0.4",
|
||||
"vite": "^4.2.0"
|
||||
}
|
||||
}
|
||||
|
||||
1
website2/src/playground/Ripple-2s-200px.svg
Normal file
1
website2/src/playground/Ripple-2s-200px.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg width="200px" height="200px" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid" class="lds-ripple" style="background: none;"><circle cx="50" cy="50" r="36.9703" fill="none" ng-attr-stroke="{{config.c1}}" ng-attr-stroke-width="{{config.width}}" stroke="#f3dcb2" stroke-width="2"><animate attributeName="r" calcMode="spline" values="0;40" keyTimes="0;1" dur="2" keySplines="0 0.2 0.8 1" begin="-1s" repeatCount="indefinite"></animate><animate attributeName="opacity" calcMode="spline" values="1;0" keyTimes="0;1" dur="2" keySplines="0.2 0 0.8 1" begin="-1s" repeatCount="indefinite"></animate></circle><circle cx="50" cy="50" r="19.2068" fill="none" ng-attr-stroke="{{config.c2}}" ng-attr-stroke-width="{{config.width}}" stroke="#cacbc5" stroke-width="2"><animate attributeName="r" calcMode="spline" values="0;40" keyTimes="0;1" dur="2" keySplines="0 0.2 0.8 1" begin="0s" repeatCount="indefinite"></animate><animate attributeName="opacity" calcMode="spline" values="1;0" keyTimes="0;1" dur="2" keySplines="0.2 0 0.8 1" begin="0s" repeatCount="indefinite"></animate></circle></svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
501
website2/src/playground/files.ts
Normal file
501
website2/src/playground/files.ts
Normal file
@@ -0,0 +1,501 @@
|
||||
import nil from './helpers/nil';
|
||||
|
||||
function blockTrim(text: string) {
|
||||
let lines = text.split('\n');
|
||||
|
||||
while (lines.length > 0 && /^ *$/.test(lines[0])) {
|
||||
lines.shift();
|
||||
}
|
||||
|
||||
while (lines.length > 0 && /^ *$/.test(lines[lines.length - 1])) {
|
||||
lines.pop();
|
||||
}
|
||||
|
||||
let minIndent = Infinity;
|
||||
|
||||
for (const line of lines) {
|
||||
if (line.trim() === '') {
|
||||
continue;
|
||||
}
|
||||
|
||||
const match = line.match(/^ */);
|
||||
|
||||
if (match === null || match[0].length >= minIndent) {
|
||||
continue;
|
||||
}
|
||||
|
||||
minIndent = match[0].length;
|
||||
}
|
||||
|
||||
lines = lines.map((line) => line.slice(minIndent));
|
||||
|
||||
return lines.join('\n');
|
||||
}
|
||||
|
||||
const files: Record<string, string | nil> = {
|
||||
'tutorial/hello.ts': blockTrim(`
|
||||
// Welcome to the ValueScript playground!
|
||||
//
|
||||
// This playground also acts as a tutorial by describing a variety of
|
||||
// examples. All examples are editable with live updates to their outputs.
|
||||
//
|
||||
// Keeping with tradition, here is the hello world program.
|
||||
|
||||
export default function main() {
|
||||
return "Hello world!";
|
||||
}
|
||||
|
||||
// When you're ready, click the next arrow ('>') above to continue.
|
||||
`),
|
||||
|
||||
'tutorial/valueSemantics.ts': blockTrim(`
|
||||
export default function main() {
|
||||
const leftBowl = ['apple', 'mango'];
|
||||
|
||||
let rightBowl = leftBowl;
|
||||
rightBowl.push('peach');
|
||||
|
||||
return leftBowl.includes("peach");
|
||||
// TypeScript: true
|
||||
// ValueScript: false
|
||||
}
|
||||
|
||||
// In TypeScript, \`leftBowl\` and \`rightBowl\` are the same object, and
|
||||
// that object changes. In ValueScript, objects are just data, they don't
|
||||
// change. When you change \`rightBowl\`, you are changing the *variable*
|
||||
// and therefore \`leftBowl\` doesn't change.
|
||||
`),
|
||||
|
||||
'tutorial/revertOnCatch.ts': blockTrim(`
|
||||
export default function () {
|
||||
let x = 0;
|
||||
|
||||
try {
|
||||
x++;
|
||||
throw new Error("boom");
|
||||
} catch {}
|
||||
|
||||
return x;
|
||||
// TypeScript: 1
|
||||
// ValueScript: 0
|
||||
}
|
||||
|
||||
// In ValueScript, a try block is a transaction - it either runs to
|
||||
// completion, or it is reverted. This is impractical in TypeScript,
|
||||
// but in ValueScript all we have to do is snapshot the variables and
|
||||
// restore from them on catch. This works because all mutation is
|
||||
// local - nothing else can be affected.
|
||||
`),
|
||||
|
||||
'examples/factorial.ts': blockTrim(`
|
||||
export default function main() {
|
||||
return factorial(5);
|
||||
}
|
||||
|
||||
function factorial(n: number): number {
|
||||
if (n === 0) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return n * factorial(n - 1);
|
||||
}
|
||||
`),
|
||||
|
||||
'examples/counter.ts': blockTrim(`
|
||||
export default function main() {
|
||||
let c = new Counter();
|
||||
|
||||
return [c.get(), c.get(), c.get()];
|
||||
}
|
||||
|
||||
class Counter {
|
||||
next = 1;
|
||||
|
||||
get() {
|
||||
return this.next++;
|
||||
}
|
||||
}
|
||||
`),
|
||||
|
||||
'examples/reverse.ts': blockTrim(`
|
||||
export default function main() {
|
||||
const values = ['a', 'b', 'c'];
|
||||
|
||||
return [values, reverse(values)];
|
||||
}
|
||||
|
||||
function reverse<T>(arr: T[]) {
|
||||
let left = 0;
|
||||
let right = arr.length - 1;
|
||||
|
||||
while (left < right) {
|
||||
[arr[left], arr[right]] = [arr[right], arr[left]];
|
||||
|
||||
left++;
|
||||
right--;
|
||||
}
|
||||
|
||||
return arr;
|
||||
|
||||
// This version also works:
|
||||
// arr.reverse();
|
||||
// return arr;
|
||||
}
|
||||
`),
|
||||
|
||||
'examples/binaryTree.ts': blockTrim(`
|
||||
export default function main() {
|
||||
let tree = new BinaryTree<number>();
|
||||
|
||||
tree.insert(2);
|
||||
tree.insert(5);
|
||||
tree.insert(1);
|
||||
|
||||
const treeSnapshot = tree;
|
||||
|
||||
tree.insert(3);
|
||||
tree.insert(4);
|
||||
|
||||
return [treeSnapshot.toArray(), tree.toArray()];
|
||||
}
|
||||
|
||||
class BinaryTree<T> {
|
||||
left?: BinaryTree<T>;
|
||||
value?: T;
|
||||
right?: BinaryTree<T>;
|
||||
|
||||
insert(newValue: T) {
|
||||
if (this.value === undefined) {
|
||||
this.value = newValue;
|
||||
return;
|
||||
}
|
||||
|
||||
if (newValue < this.value) {
|
||||
this.left ??= new BinaryTree();
|
||||
this.left.insert(newValue);
|
||||
} else {
|
||||
this.right ??= new BinaryTree();
|
||||
this.right.insert(newValue);
|
||||
}
|
||||
}
|
||||
|
||||
toArray() {
|
||||
let res: T[] = [];
|
||||
|
||||
if (this.left) {
|
||||
res = res.concat(this.left.toArray());
|
||||
}
|
||||
|
||||
if (this.value !== undefined) {
|
||||
res.push(this.value);
|
||||
}
|
||||
|
||||
if (this.right) {
|
||||
res = res.concat(this.right.toArray());
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
}
|
||||
`),
|
||||
|
||||
'examples/mergeSort.ts': blockTrim(`
|
||||
export default function main() {
|
||||
const x = [7, 18, 9, 11, 16, 3, 8, 2, 5, 4, 6, 14, 15, 17, 10, 12, 1, 13];
|
||||
|
||||
return mergeSort(x, (a, b) => a - b);
|
||||
}
|
||||
|
||||
function mergeSort<T>(vals: T[], cmp: (a: T, b: T) => number): T[] {
|
||||
const len = vals.length;
|
||||
|
||||
if (len <= 1) {
|
||||
return vals;
|
||||
}
|
||||
|
||||
if (len === 2) {
|
||||
if (cmp(vals[0], vals[1]) > 0) {
|
||||
return [vals[1], vals[0]];
|
||||
}
|
||||
|
||||
return vals;
|
||||
}
|
||||
|
||||
const mid = vals.length / 2;
|
||||
|
||||
const leftSorted = mergeSort(vals.slice(0, mid), cmp);
|
||||
const rightSorted = mergeSort(vals.slice(mid), cmp);
|
||||
|
||||
let res: T[] = [];
|
||||
|
||||
let left = 0;
|
||||
const leftLen = leftSorted.length;
|
||||
let right = 0;
|
||||
const rightLen = rightSorted.length;
|
||||
|
||||
while (left < leftLen && right < rightLen) {
|
||||
if (cmp(leftSorted[left], rightSorted[right]) <= 0) {
|
||||
res.push(leftSorted[left++]);
|
||||
} else {
|
||||
res.push(rightSorted[right++]);
|
||||
}
|
||||
}
|
||||
|
||||
while (left < leftLen) {
|
||||
res.push(leftSorted[left++]);
|
||||
}
|
||||
|
||||
while (right < rightLen) {
|
||||
res.push(rightSorted[right++]);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
`),
|
||||
|
||||
'examples/quickSort.ts': blockTrim(`
|
||||
export default function main() {
|
||||
const x = [7, 18, 9, 11, 16, 3, 8, 2, 5, 4, 6, 14, 15, 17, 10, 12, 1, 13];
|
||||
|
||||
return quickSort(x, (a, b) => a - b);
|
||||
}
|
||||
|
||||
function quickSort<T>(vals: T[], cmp: (a: T, b: T) => number) {
|
||||
// Demonstrates the ability to do in-place updates in ValueScript.
|
||||
//
|
||||
// There's only one reference to \`vals\`, so we can mutate it in-place
|
||||
// without violating value semantics.
|
||||
//
|
||||
// (At the time of writing the internals aren't very careful about
|
||||
// minimizing ref counters so this might not be happening, but the logic
|
||||
// to mutate in-place when the ref count is one is already there. This
|
||||
// will be optimized/fixed in the future.)
|
||||
//
|
||||
// More on quickSort:
|
||||
// https://www.youtube.com/watch?v=Hoixgm4-P4M
|
||||
|
||||
const len = vals.length;
|
||||
let ranges: [number, number][] = [[0, len - 1]];
|
||||
|
||||
while (true) {
|
||||
const range = ranges.shift();
|
||||
|
||||
if (!range) {
|
||||
return vals;
|
||||
}
|
||||
|
||||
const [start, end] = range;
|
||||
|
||||
if (end - start <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let i = start;
|
||||
let j = end;
|
||||
|
||||
let pivotIndex = Math.floor((i + j) / 2);
|
||||
[vals[pivotIndex], vals[j]] = [vals[j], vals[pivotIndex]];
|
||||
const pivot = vals[j];
|
||||
j--;
|
||||
|
||||
while (true) {
|
||||
while (cmp(vals[i], pivot) < 0) {
|
||||
i++;
|
||||
}
|
||||
|
||||
while (cmp(vals[j], pivot) > 0) {
|
||||
j--;
|
||||
}
|
||||
|
||||
if (i < j) {
|
||||
[vals[i], vals[j]] = [vals[j], vals[i]];
|
||||
i++;
|
||||
j--;
|
||||
continue;
|
||||
}
|
||||
|
||||
[vals[i], vals[end]] = [vals[end], vals[i]];
|
||||
|
||||
ranges.push([start, i - 1]);
|
||||
ranges.push([i + 1, end]);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
`),
|
||||
|
||||
'examples/idGenerationError.ts': blockTrim(`
|
||||
export default function main() {
|
||||
let nextId = 1;
|
||||
|
||||
function generateId() {
|
||||
const result = nextId;
|
||||
nextId++;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
return [
|
||||
generateId(),
|
||||
generateId(),
|
||||
generateId(),
|
||||
];
|
||||
}
|
||||
`),
|
||||
|
||||
'examples/idGeneration.ts': blockTrim(`
|
||||
export default function main() {
|
||||
let idGen = new IdGenerator();
|
||||
|
||||
return [
|
||||
idGen.generate(),
|
||||
idGen.generate(),
|
||||
idGen.generate(),
|
||||
];
|
||||
}
|
||||
|
||||
class IdGenerator {
|
||||
nextId: number;
|
||||
|
||||
constructor() {
|
||||
this.nextId = 1;
|
||||
}
|
||||
|
||||
generate() {
|
||||
const result = this.nextId;
|
||||
this.nextId++;
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
`),
|
||||
|
||||
'examples/sideEffectsArticle/enablePirateError.ts': blockTrim(`
|
||||
export default function main() {
|
||||
let pirateEnabled = false;
|
||||
|
||||
function greet() {
|
||||
if (!pirateEnabled) {
|
||||
return "Hi";
|
||||
}
|
||||
|
||||
return "Ahoy";
|
||||
}
|
||||
|
||||
function enablePirate() {
|
||||
pirateEnabled = true;
|
||||
return "Done";
|
||||
}
|
||||
|
||||
return [
|
||||
greet(),
|
||||
enablePirate(),
|
||||
greet(),
|
||||
];
|
||||
}
|
||||
`),
|
||||
|
||||
'examples/sideEffectsArticle/lyingAboutA.ts': blockTrim(`
|
||||
export default function main() {
|
||||
let a = 5;
|
||||
a += 2;
|
||||
|
||||
return a;
|
||||
}
|
||||
`),
|
||||
|
||||
'examples/sideEffectsArticle/add1To50WithMutation.ts': blockTrim(`
|
||||
export default function main() {
|
||||
let sum = 0;
|
||||
|
||||
for (let i = 1; i <= 50; i++) {
|
||||
sum += i;
|
||||
}
|
||||
|
||||
return sum;
|
||||
}
|
||||
`),
|
||||
|
||||
'examples/sideEffectsArticle/add1To50WithoutMutation.ts': blockTrim(`
|
||||
export default function main() {
|
||||
return makeRange(1, 51)
|
||||
.reduce((a, b) => a + b);
|
||||
}
|
||||
|
||||
function makeRange(start: number, end: number): number[] {
|
||||
if (start === end) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return [start].concat(
|
||||
makeRange(start + 1, end),
|
||||
);
|
||||
}
|
||||
`),
|
||||
|
||||
'examples/sideEffectsArticle/enablePirateWorkaround.ts': blockTrim(`
|
||||
export default function main() {
|
||||
let pirateEnabled = false;
|
||||
|
||||
function greet(pirateEnabled: boolean) {
|
||||
if (!pirateEnabled) {
|
||||
return "Hi";
|
||||
}
|
||||
|
||||
return "Ahoy";
|
||||
}
|
||||
|
||||
function enablePirate(
|
||||
pirateEnabled: boolean,
|
||||
): [boolean, string] {
|
||||
pirateEnabled = true;
|
||||
return [pirateEnabled, "Done"];
|
||||
}
|
||||
|
||||
const greetResponse1 = greet(pirateEnabled);
|
||||
|
||||
let enablePirateResponse: string;
|
||||
[pirateEnabled, enablePirateResponse] = enablePirate(pirateEnabled);
|
||||
|
||||
const greetResponse2 = greet(pirateEnabled);
|
||||
|
||||
return [
|
||||
greetResponse1,
|
||||
enablePirateResponse,
|
||||
greetResponse2,
|
||||
];
|
||||
}
|
||||
`),
|
||||
|
||||
'examples/sideEffectsArticle/actorEnablePirate.ts': blockTrim(`
|
||||
export default function main() {
|
||||
let actor = new Actor();
|
||||
|
||||
return [
|
||||
actor.greet(),
|
||||
actor.enablePirate(),
|
||||
actor.greet(),
|
||||
];
|
||||
}
|
||||
|
||||
class Actor {
|
||||
pirateEnabled = false;
|
||||
|
||||
greet() {
|
||||
if (!this.pirateEnabled) {
|
||||
return "Hi";
|
||||
}
|
||||
|
||||
return "Ahoy";
|
||||
}
|
||||
|
||||
enablePirate() {
|
||||
this.pirateEnabled = true;
|
||||
return "Done";
|
||||
}
|
||||
}
|
||||
`),
|
||||
};
|
||||
|
||||
export default files;
|
||||
8
website2/src/playground/helpers/assert.ts
Normal file
8
website2/src/playground/helpers/assert.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
export default function assert(
|
||||
value: boolean,
|
||||
msg = "value was not true",
|
||||
): asserts value {
|
||||
if (value !== true) {
|
||||
throw new Error(`Assertion failed: ${msg}`);
|
||||
}
|
||||
}
|
||||
4
website2/src/playground/helpers/nil.ts
Normal file
4
website2/src/playground/helpers/nil.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
const nil = undefined;
|
||||
type nil = undefined;
|
||||
|
||||
export default nil;
|
||||
9
website2/src/playground/helpers/notNil.ts
Normal file
9
website2/src/playground/helpers/notNil.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import nil from './nil';
|
||||
|
||||
export default function notNil<T>(value: T | nil): T {
|
||||
if (value === nil) {
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
36
website2/src/playground/index.html
Normal file
36
website2/src/playground/index.html
Normal file
@@ -0,0 +1,36 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name=viewport content="width=device-width, initial-scale=1">
|
||||
|
||||
<link rel="stylesheet" href="styles.scss" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="editor">
|
||||
<div id="editor-loading"></div>
|
||||
</div>
|
||||
<div id="file-selector">
|
||||
<div id="file-previous"><</div>
|
||||
<div id="file-location">
|
||||
<select></select>
|
||||
</div>
|
||||
<div id="file-next">></div>
|
||||
</div>
|
||||
|
||||
<div id="display">
|
||||
<div class="display-title">Outcome</div>
|
||||
<div id="outcome">"The playground is loading"</div>
|
||||
|
||||
<div class="display-title">Diagnostics</div>
|
||||
<div id="diagnostics"></div>
|
||||
|
||||
<div class="display-title">Assembly</div>
|
||||
<div id="vsm">@main = function() {
|
||||
mov "The playground is loading" %return
|
||||
}</div>
|
||||
</div>
|
||||
|
||||
<script src="index.ts" type="module" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
256
website2/src/playground/index.ts
Normal file
256
website2/src/playground/index.ts
Normal file
@@ -0,0 +1,256 @@
|
||||
import files from './files';
|
||||
import assert from './helpers/assert';
|
||||
import nil from './helpers/nil';
|
||||
import notNil from './helpers/notNil';
|
||||
import VslibPool, {
|
||||
CompilerOutput,
|
||||
Diagnostic,
|
||||
Job,
|
||||
RunResult,
|
||||
} from './vslib/VslibPool';
|
||||
import monaco from './monaco';
|
||||
|
||||
function domQuery<T = HTMLElement>(query: string): T {
|
||||
return <T> <unknown> notNil(document.querySelector(query) ?? nil);
|
||||
}
|
||||
|
||||
const editorEl = domQuery('#editor');
|
||||
|
||||
const selectEl = domQuery<HTMLSelectElement>('#file-location select');
|
||||
const filePreviousEl = domQuery('#file-previous');
|
||||
const fileNextEl = domQuery('#file-next');
|
||||
const outcomeEl = domQuery('#outcome');
|
||||
const vsmEl = domQuery('#vsm');
|
||||
const diagnosticsEl = domQuery('#diagnostics');
|
||||
|
||||
for (const filename of Object.keys(files)) {
|
||||
const option = document.createElement('option');
|
||||
option.textContent = filename;
|
||||
selectEl.appendChild(option);
|
||||
}
|
||||
|
||||
let currentFile = '';
|
||||
|
||||
editorEl.innerHTML = '';
|
||||
|
||||
(async () => {
|
||||
const vslibPool = new VslibPool();
|
||||
|
||||
(window as any).vslibPool = vslibPool;
|
||||
|
||||
const editor = monaco.editor.create(editorEl, {
|
||||
theme: 'vs-dark',
|
||||
value: '',
|
||||
language: 'typescript',
|
||||
});
|
||||
|
||||
setTimeout(() => changeFile(location.hash.slice(1)));
|
||||
|
||||
globalThis.addEventListener('hashchange', () => {
|
||||
changeFile(location.hash.slice(1));
|
||||
});
|
||||
|
||||
globalThis.addEventListener('resize', () => editor.layout());
|
||||
|
||||
const model = notNil(editor.getModel() ?? nil);
|
||||
|
||||
model.updateOptions({ tabSize: 2, insertSpaces: true });
|
||||
|
||||
function changeFile(newFile: string) {
|
||||
if (currentFile === '') {
|
||||
currentFile = Object.keys(files)[0];
|
||||
} else if (newFile === currentFile) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (newFile === '') {
|
||||
newFile = Object.keys(files)[0];
|
||||
}
|
||||
|
||||
const fileIdx = Object.keys(files).indexOf(newFile);
|
||||
|
||||
if (fileIdx !== -1) {
|
||||
currentFile = newFile;
|
||||
}
|
||||
|
||||
location.hash = currentFile;
|
||||
selectEl.selectedIndex = fileIdx;
|
||||
|
||||
const content = files[currentFile];
|
||||
assert(content !== nil);
|
||||
|
||||
model.setValue(content);
|
||||
}
|
||||
|
||||
selectEl.addEventListener('change', () => {
|
||||
changeFile(selectEl.value);
|
||||
});
|
||||
|
||||
const moveFileIndex = (change: number) => () => {
|
||||
const filenames = Object.keys(files);
|
||||
let idx = filenames.indexOf(currentFile);
|
||||
|
||||
if (idx === -1) {
|
||||
throw new Error('This should not happen');
|
||||
}
|
||||
|
||||
idx += change;
|
||||
idx = Math.max(idx, 0);
|
||||
idx = Math.min(idx, filenames.length - 1);
|
||||
|
||||
changeFile(filenames[idx]);
|
||||
};
|
||||
|
||||
filePreviousEl.addEventListener('click', moveFileIndex(-1));
|
||||
fileNextEl.addEventListener('click', moveFileIndex(1));
|
||||
|
||||
let timerId: undefined | number = undefined;
|
||||
|
||||
model.onDidChangeContent(() => {
|
||||
files[currentFile] = model.getValue();
|
||||
clearTimeout(timerId);
|
||||
|
||||
timerId = setTimeout(handleUpdate, 100) as unknown as number;
|
||||
});
|
||||
|
||||
let compileJob: Job<CompilerOutput> | nil = nil;
|
||||
let runJob: Job<RunResult> | nil = nil;
|
||||
let updateId = 0;
|
||||
|
||||
function handleUpdate() {
|
||||
updateId++;
|
||||
const currentUpdateId = updateId;
|
||||
compileJob?.cancel();
|
||||
runJob?.cancel();
|
||||
|
||||
const source = model.getValue();
|
||||
|
||||
compileJob = vslibPool.compile(source);
|
||||
runJob = vslibPool.run(source);
|
||||
|
||||
renderJob(
|
||||
compileJob,
|
||||
vsmEl,
|
||||
(el, compilerOutput) => {
|
||||
el.textContent = compilerOutput.assembly.join('\n');
|
||||
},
|
||||
);
|
||||
|
||||
renderJob(
|
||||
runJob,
|
||||
outcomeEl,
|
||||
(el, runResult) => {
|
||||
if ('Ok' in runResult.output) {
|
||||
el.textContent = runResult.output.Ok;
|
||||
} else if ('Err' in runResult.output) {
|
||||
el.textContent = `Uncaught exception: ${runResult.output.Err}`;
|
||||
} else {
|
||||
never(runResult.output);
|
||||
}
|
||||
|
||||
diagnosticsEl.innerHTML = '';
|
||||
|
||||
for (const diagnostic of runResult.diagnostics) {
|
||||
const diagnosticEl = document.createElement('div');
|
||||
|
||||
diagnosticEl.classList.add(
|
||||
'diagnostic',
|
||||
toKebabCase(diagnostic.level),
|
||||
);
|
||||
|
||||
const { line, col } = toLineCol(source, diagnostic.span.start);
|
||||
diagnosticEl.textContent = `${line}:${col}: ${diagnostic.message}`;
|
||||
|
||||
diagnosticsEl.appendChild(diagnosticEl);
|
||||
}
|
||||
|
||||
monaco.editor.setModelMarkers(
|
||||
model,
|
||||
'valuescript',
|
||||
runResult.diagnostics.map((diagnostic) => {
|
||||
const { line, col } = toLineCol(source, diagnostic.span.start);
|
||||
const { line: endLine, col: endCol } = toLineCol(
|
||||
source,
|
||||
diagnostic.span.end,
|
||||
);
|
||||
|
||||
return {
|
||||
severity: toMonacoSeverity(diagnostic.level),
|
||||
startLineNumber: line,
|
||||
startColumn: col,
|
||||
endLineNumber: endLine,
|
||||
endColumn: endCol,
|
||||
message: diagnostic.message,
|
||||
};
|
||||
}),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
function renderJob<T>(
|
||||
job: Job<T>,
|
||||
el: HTMLElement,
|
||||
apply: (el: HTMLElement, jobResult: T) => void,
|
||||
) {
|
||||
const startTime = Date.now();
|
||||
|
||||
const loadingInterval = setInterval(() => {
|
||||
if (currentUpdateId === updateId) {
|
||||
el.textContent = `Loading... ${
|
||||
((Date.now() - startTime) / 1000).toFixed(1)
|
||||
}s`;
|
||||
}
|
||||
}, 100);
|
||||
|
||||
(async () => {
|
||||
try {
|
||||
apply(el, await job.wait());
|
||||
el.classList.remove('error');
|
||||
} catch (err: any) {
|
||||
if (!(err instanceof Error)) {
|
||||
// deno-lint-ignore no-ex-assign
|
||||
err = new Error(`Non-error exception ${err}`);
|
||||
}
|
||||
|
||||
if (err.message !== 'Canceled') {
|
||||
el.textContent = err.message;
|
||||
el.classList.add('error');
|
||||
}
|
||||
} finally {
|
||||
clearInterval(loadingInterval);
|
||||
}
|
||||
})();
|
||||
}
|
||||
}
|
||||
|
||||
function toMonacoSeverity(level: Diagnostic['level']) {
|
||||
switch (level) {
|
||||
case 'Error':
|
||||
return monaco.MarkerSeverity.Error;
|
||||
case 'InternalError':
|
||||
return monaco.MarkerSeverity.Error;
|
||||
case 'Lint':
|
||||
return monaco.MarkerSeverity.Warning;
|
||||
case 'CompilerDebug':
|
||||
return monaco.MarkerSeverity.Info;
|
||||
}
|
||||
}
|
||||
})();
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
function never(_: never): never {
|
||||
throw new Error('This should not happen');
|
||||
}
|
||||
|
||||
function toKebabCase(str: string): string {
|
||||
// account for leading capital letters
|
||||
str = str.replace(/^[A-Z]/, (match) => match.toLowerCase());
|
||||
|
||||
return str.replace(/[A-Z]/g, (match) => `-${match.toLowerCase()}`);
|
||||
}
|
||||
|
||||
function toLineCol(str: string, index: number): { line: number; col: number } {
|
||||
const lines = str.slice(0, index).split('\n');
|
||||
|
||||
return { line: lines.length, col: lines[lines.length - 1].length + 1 };
|
||||
}
|
||||
27
website2/src/playground/monaco.ts
Normal file
27
website2/src/playground/monaco.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import * as monaco from 'monaco-editor';
|
||||
export { monaco as default };
|
||||
import editorWorker from 'monaco-editor/esm/vs/editor/editor.worker?worker';
|
||||
import jsonWorker from 'monaco-editor/esm/vs/language/json/json.worker?worker';
|
||||
import cssWorker from 'monaco-editor/esm/vs/language/css/css.worker?worker';
|
||||
import htmlWorker from 'monaco-editor/esm/vs/language/html/html.worker?worker';
|
||||
import tsWorker from 'monaco-editor/esm/vs/language/typescript/ts.worker?worker';
|
||||
|
||||
self.MonacoEnvironment = {
|
||||
getWorker(_: any, label: string) {
|
||||
if (label === 'json') {
|
||||
return new jsonWorker();
|
||||
}
|
||||
if (label === 'css' || label === 'scss' || label === 'less') {
|
||||
return new cssWorker();
|
||||
}
|
||||
if (label === 'html' || label === 'handlebars' || label === 'razor') {
|
||||
return new htmlWorker();
|
||||
}
|
||||
if (label === 'typescript' || label === 'javascript') {
|
||||
return new tsWorker();
|
||||
}
|
||||
return new editorWorker();
|
||||
},
|
||||
};
|
||||
|
||||
monaco.languages.typescript.typescriptDefaults.setEagerModelSync(true);
|
||||
214
website2/src/playground/styles.scss
Normal file
214
website2/src/playground/styles.scss
Normal file
@@ -0,0 +1,214 @@
|
||||
body {
|
||||
background-color: #1e1e1e;
|
||||
color: #d4d4d4;
|
||||
font-family: monospace;
|
||||
font-size: 1.2em;
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
position: fixed;
|
||||
}
|
||||
|
||||
a, a:visited {
|
||||
color: hsl(227, 83%, 69%);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
#file-selector {
|
||||
background-color: #181818;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
font-size: 1.5em;
|
||||
display: flex;
|
||||
width: 50vw;
|
||||
border-bottom: 1px solid black;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
#file-selector > div {
|
||||
width: 2em;
|
||||
padding: 0.5em;
|
||||
}
|
||||
|
||||
#file-location {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
#file-location select, #file-location option {
|
||||
background-color: #181818;
|
||||
border: none;
|
||||
color: white;
|
||||
font-family: monospace;
|
||||
font-size: 1em;
|
||||
width: 100%;
|
||||
-webkit-appearance: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#file-previous, #file-next {
|
||||
color: hsl(44, 100%, 50%);
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#editor {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 4em;
|
||||
height: calc(100vh - 4em);
|
||||
width: 50vw;
|
||||
}
|
||||
|
||||
#editor-loading {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-image: url('Ripple-2s-200px.svg');
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
}
|
||||
|
||||
#display {
|
||||
border-left: 1px solid black;
|
||||
position: absolute;
|
||||
left: 50vw;
|
||||
top: 0em;
|
||||
height: 100vh;
|
||||
width: 50vw;
|
||||
overflow-y: scroll;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
#display > div, #state {
|
||||
white-space: pre-wrap;
|
||||
padding: 1.5em;
|
||||
}
|
||||
|
||||
#display > div:not(:first-child) {
|
||||
border-top: 1px solid black;
|
||||
}
|
||||
|
||||
#display .display-title {
|
||||
text-align: center;
|
||||
font-size: 1.5em;
|
||||
background-color: #181818;
|
||||
padding: 0.5em;
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
#display #diagnostics {
|
||||
padding: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
#display .diagnostic {
|
||||
padding: 0.5em 1.5em;
|
||||
}
|
||||
|
||||
#diagnostics > .diagnostic:not(:first-child) {
|
||||
border-top: 1px solid black;
|
||||
}
|
||||
|
||||
.diagnostic.info {
|
||||
background-color: hsla(240, 100%, 50%, 0.1);
|
||||
}
|
||||
|
||||
.diagnostic.warn, .diagnostic.lint {
|
||||
background-color: hsla(30, 100%, 50%, 0.1);
|
||||
}
|
||||
|
||||
.diagnostic.error {
|
||||
background-color: hsla(0, 100%, 50%, 0.1);
|
||||
}
|
||||
|
||||
#display .diagnostic .diagnostic {
|
||||
border: 1px solid black;
|
||||
}
|
||||
|
||||
#display .diagnostic .diagnostic:first-child {
|
||||
margin-top: 0.5em;
|
||||
}
|
||||
|
||||
#display .diagnostic .diagnostic:not(:first-child) {
|
||||
border-top: 0;
|
||||
}
|
||||
|
||||
#display #application {
|
||||
display: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
#display #application.active {
|
||||
display: block;
|
||||
}
|
||||
|
||||
input[type=text] {
|
||||
background-color: transparent;
|
||||
color: #d4d4d4;
|
||||
font-family: monospace;
|
||||
font-size: 1em;
|
||||
border: 0;
|
||||
border-right: 1px solid black;
|
||||
padding: 0.5em 1.5em;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.button {
|
||||
background-color: rgba(0, 0, 255, 0.2);
|
||||
padding: 0.5em;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.content > div {
|
||||
border-top: 1px solid black;
|
||||
}
|
||||
|
||||
#state, .state-title {
|
||||
border-top: 1px solid black;
|
||||
}
|
||||
|
||||
#state-refresh {
|
||||
float: left;
|
||||
transform: rotate(-45deg);
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
padding: 0 0.5em;
|
||||
}
|
||||
|
||||
#display > #stats {
|
||||
padding: 1em;
|
||||
}
|
||||
|
||||
#stats .table-wrap {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
#stats table {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#stats td {
|
||||
padding: 0.5em;
|
||||
}
|
||||
|
||||
#stats td:first-child {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
#stats td {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
#stats td:nth-child(2) {
|
||||
width: 4em;
|
||||
}
|
||||
|
||||
.clickable:hover {
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
#vsm.error {
|
||||
background-color: hsla(0, 100%, 50%, 0.05);
|
||||
}
|
||||
129
website2/src/playground/vslib/VslibPool.ts
Normal file
129
website2/src/playground/vslib/VslibPool.ts
Normal file
@@ -0,0 +1,129 @@
|
||||
import * as valuescript from 'valuescript';
|
||||
import nil from '../helpers/nil';
|
||||
import { initVslib } from './index';
|
||||
|
||||
const workerScript = [
|
||||
initVslib.toString(),
|
||||
(async function main() {
|
||||
const vslib = await initVslib();
|
||||
|
||||
self.postMessage('ready');
|
||||
|
||||
self.onmessage = (evt) => {
|
||||
const { method, args } = evt.data;
|
||||
|
||||
if (method === 'compile') {
|
||||
try {
|
||||
self.postMessage({ ok: vslib.compile(args[0]) });
|
||||
} catch (err) {
|
||||
self.postMessage({ err });
|
||||
}
|
||||
}
|
||||
|
||||
if (method === 'run') {
|
||||
try {
|
||||
self.postMessage({ ok: vslib.run(args[0]) });
|
||||
} catch (err) {
|
||||
self.postMessage({ err });
|
||||
}
|
||||
}
|
||||
};
|
||||
}).toString(),
|
||||
'main();',
|
||||
].join('\n\n');
|
||||
|
||||
const workerUrl = URL.createObjectURL(
|
||||
new Blob([workerScript], { type: 'application/javascript' }),
|
||||
);
|
||||
|
||||
export type Diagnostic = {
|
||||
level: 'Lint' | 'Error' | 'InternalError' | 'CompilerDebug';
|
||||
message: string;
|
||||
span: {
|
||||
start: number;
|
||||
end: number;
|
||||
ctxt: number;
|
||||
};
|
||||
};
|
||||
|
||||
export type CompilerOutput = {
|
||||
diagnostics: Diagnostic[];
|
||||
assembly: string[];
|
||||
};
|
||||
|
||||
export type RunResult = {
|
||||
diagnostics: Diagnostic[];
|
||||
output:
|
||||
| { Ok: string }
|
||||
| { Err: string };
|
||||
};
|
||||
|
||||
export type Job<T> = {
|
||||
wait: () => Promise<T>;
|
||||
cancel: () => void;
|
||||
};
|
||||
|
||||
export function mapJob<U, V>(job: Job<U>, f: (x: U) => V): Job<V> {
|
||||
return {
|
||||
wait: () => job.wait().then(f),
|
||||
cancel: job.cancel,
|
||||
};
|
||||
}
|
||||
|
||||
export default class VslibPool {
|
||||
#pool = new valuescript.WorkerPool(workerUrl);
|
||||
|
||||
run(source: string) {
|
||||
return this.#Job('run', [source]) as Job<RunResult>;
|
||||
}
|
||||
|
||||
compile(source: string) {
|
||||
return this.#Job('compile', [source]) as Job<CompilerOutput>;
|
||||
}
|
||||
|
||||
#Job(method: string, args: unknown[]) {
|
||||
let canceled = false;
|
||||
let finished = false;
|
||||
let outerTerminate: (() => void) | nil = nil;
|
||||
|
||||
const resultPromise = this.#pool.use((worker, terminate) => {
|
||||
if (canceled) {
|
||||
finished = true;
|
||||
return Promise.reject(new Error('canceled'));
|
||||
}
|
||||
|
||||
outerTerminate = terminate;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
worker.postMessage({ method, args });
|
||||
|
||||
worker.onmessage = (evt) => {
|
||||
if ('ok' in evt.data) {
|
||||
resolve(JSON.parse(evt.data.ok));
|
||||
} else if ('err' in evt.data) {
|
||||
if (evt.data.err instanceof Error) {
|
||||
reject(evt.data.err);
|
||||
} else {
|
||||
reject(new Error(`${evt.data.err}`));
|
||||
}
|
||||
} else {
|
||||
reject(new Error(`Unexpected message: ${evt.data}`));
|
||||
}
|
||||
|
||||
finished = true;
|
||||
};
|
||||
});
|
||||
}) as Promise<unknown>;
|
||||
|
||||
return {
|
||||
wait: () => resultPromise,
|
||||
cancel: () => {
|
||||
canceled = true;
|
||||
|
||||
if (!finished && outerTerminate) {
|
||||
outerTerminate();
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
142
website2/src/playground/vslib/index.ts
Normal file
142
website2/src/playground/vslib/index.ts
Normal file
@@ -0,0 +1,142 @@
|
||||
export async function initVslib() {
|
||||
// deno-lint-ignore no-explicit-any
|
||||
const wasm: Record<string, any> = (await WebAssembly.instantiateStreaming(
|
||||
fetch(`${location.origin}/value_script_bg.wasm`),
|
||||
{
|
||||
'./valuescript_wasm_bg.js': { __wbindgen_throw },
|
||||
},
|
||||
)).instance.exports;
|
||||
|
||||
let WASM_VECTOR_LEN = 0;
|
||||
|
||||
let cachegetUint8Memory0: Uint8Array | null = null;
|
||||
function getUint8Memory0() {
|
||||
if (
|
||||
cachegetUint8Memory0 === null ||
|
||||
cachegetUint8Memory0.buffer !== wasm.memory.buffer
|
||||
) {
|
||||
cachegetUint8Memory0 = new Uint8Array(wasm.memory.buffer);
|
||||
}
|
||||
return cachegetUint8Memory0;
|
||||
}
|
||||
|
||||
const cachedTextEncoder = new TextEncoder();
|
||||
|
||||
const encodeString = function (arg: string, view: Uint8Array) {
|
||||
return cachedTextEncoder.encodeInto(arg, view);
|
||||
};
|
||||
|
||||
function passStringToWasm0(
|
||||
arg: string,
|
||||
malloc: (len: number) => number,
|
||||
realloc: (a: number, b: number, c: number) => number,
|
||||
) {
|
||||
if (realloc === undefined) {
|
||||
const buf = cachedTextEncoder.encode(arg);
|
||||
const ptr = malloc(buf.length);
|
||||
getUint8Memory0().subarray(ptr, ptr + buf.length).set(buf);
|
||||
WASM_VECTOR_LEN = buf.length;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
let len = arg.length;
|
||||
let ptr = malloc(len);
|
||||
|
||||
const mem = getUint8Memory0();
|
||||
|
||||
let offset = 0;
|
||||
|
||||
for (; offset < len; offset++) {
|
||||
const code = arg.charCodeAt(offset);
|
||||
if (code > 0x7F) break;
|
||||
mem[ptr + offset] = code;
|
||||
}
|
||||
|
||||
if (offset !== len) {
|
||||
if (offset !== 0) {
|
||||
arg = arg.slice(offset);
|
||||
}
|
||||
ptr = realloc(ptr, len, len = offset + arg.length * 3);
|
||||
const view = getUint8Memory0().subarray(ptr + offset, ptr + len);
|
||||
const ret = encodeString(arg, view);
|
||||
|
||||
offset += ret.written!;
|
||||
}
|
||||
|
||||
WASM_VECTOR_LEN = offset;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
let cachegetInt32Memory0: Int32Array | null = null;
|
||||
function getInt32Memory0() {
|
||||
if (
|
||||
cachegetInt32Memory0 === null ||
|
||||
cachegetInt32Memory0.buffer !== wasm.memory.buffer
|
||||
) {
|
||||
cachegetInt32Memory0 = new Int32Array(wasm.memory.buffer);
|
||||
}
|
||||
return cachegetInt32Memory0;
|
||||
}
|
||||
|
||||
const cachedTextDecoder = new TextDecoder('utf-8', {
|
||||
ignoreBOM: true,
|
||||
fatal: true,
|
||||
});
|
||||
|
||||
cachedTextDecoder.decode();
|
||||
|
||||
function getStringFromWasm0(ptr: number, len: number) {
|
||||
return cachedTextDecoder.decode(getUint8Memory0().subarray(ptr, ptr + len));
|
||||
}
|
||||
|
||||
function compile(source: string) {
|
||||
let r0, r1;
|
||||
|
||||
try {
|
||||
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
|
||||
const ptr0 = passStringToWasm0(
|
||||
source,
|
||||
wasm.__wbindgen_malloc,
|
||||
wasm.__wbindgen_realloc,
|
||||
);
|
||||
const len0 = WASM_VECTOR_LEN;
|
||||
wasm.compile(retptr, ptr0, len0);
|
||||
r0 = getInt32Memory0()[retptr / 4 + 0];
|
||||
r1 = getInt32Memory0()[retptr / 4 + 1];
|
||||
return getStringFromWasm0(r0, r1);
|
||||
} finally {
|
||||
wasm.__wbindgen_add_to_stack_pointer(16);
|
||||
wasm.__wbindgen_free(r0, r1);
|
||||
}
|
||||
}
|
||||
|
||||
function run(source: string) {
|
||||
let r0, r1;
|
||||
|
||||
try {
|
||||
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
|
||||
const ptr0 = passStringToWasm0(
|
||||
source,
|
||||
wasm.__wbindgen_malloc,
|
||||
wasm.__wbindgen_realloc,
|
||||
);
|
||||
const len0 = WASM_VECTOR_LEN;
|
||||
wasm.run(retptr, ptr0, len0);
|
||||
r0 = getInt32Memory0()[retptr / 4 + 0];
|
||||
r1 = getInt32Memory0()[retptr / 4 + 1];
|
||||
return getStringFromWasm0(r0, r1);
|
||||
} finally {
|
||||
wasm.__wbindgen_add_to_stack_pointer(16);
|
||||
wasm.__wbindgen_free(r0, r1);
|
||||
}
|
||||
}
|
||||
|
||||
function __wbindgen_throw(arg0: number, arg1: number) {
|
||||
throw new Error(getStringFromWasm0(arg0, arg1));
|
||||
}
|
||||
|
||||
return {
|
||||
compile,
|
||||
run,
|
||||
};
|
||||
}
|
||||
@@ -7,6 +7,7 @@ const outDir = resolve(__dirname, 'dist');
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
publicDir: resolve(__dirname, 'public'),
|
||||
root: src,
|
||||
plugins: [react()],
|
||||
build: {
|
||||
@@ -15,7 +16,7 @@ export default defineConfig({
|
||||
rollupOptions: {
|
||||
input: {
|
||||
main: resolve(src, 'index.html'),
|
||||
app: resolve(src, 'app.html'),
|
||||
playground: resolve(src, 'playground', 'index.html'),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user