mirror of
https://github.com/voltrevo/ValueScript.git
synced 2026-04-18 03:00:27 -04:00
Use actual files for samples via import.meta.glob
This commit is contained in:
@@ -1,505 +0,0 @@
|
||||
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 to continue, click next below.
|
||||
`),
|
||||
|
||||
'/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.
|
||||
`),
|
||||
|
||||
'/tutorial/binaryTree.ts': blockTrim(`
|
||||
import BinaryTree from '../lib/BinaryTree';
|
||||
|
||||
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()];
|
||||
}
|
||||
`),
|
||||
|
||||
'/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/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";
|
||||
}
|
||||
}
|
||||
`),
|
||||
|
||||
'/lib/BinaryTree.ts': blockTrim(`
|
||||
export default 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;
|
||||
}
|
||||
}
|
||||
`),
|
||||
};
|
||||
|
||||
export default files;
|
||||
25
website/src/playground/files/index.ts
Normal file
25
website/src/playground/files/index.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import nil from '../helpers/nil';
|
||||
import raw from './raw';
|
||||
|
||||
const files: Record<string, string | nil> = {
|
||||
...pick(raw, [
|
||||
'/tutorial/hello.ts',
|
||||
'/tutorial/valueSemantics.ts',
|
||||
'/tutorial/revertOnCatch.ts',
|
||||
'/tutorial/binaryTree.ts',
|
||||
]),
|
||||
...raw,
|
||||
};
|
||||
|
||||
export default files;
|
||||
|
||||
function pick<T, K extends keyof T>(obj: T, keys: K[]): Pick<T, K> {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const result: any = {};
|
||||
|
||||
for (const key of keys) {
|
||||
result[key] = obj[key];
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
16
website/src/playground/files/raw.ts
Normal file
16
website/src/playground/files/raw.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import assert from '../helpers/assert';
|
||||
|
||||
const entries = Object.entries(
|
||||
import.meta.glob(
|
||||
'./root/**/*.ts',
|
||||
{ as: 'raw' },
|
||||
),
|
||||
);
|
||||
|
||||
export default Object.fromEntries(await Promise.all(
|
||||
entries.map(async ([path, module]) => {
|
||||
const prefix = './root';
|
||||
assert(path.startsWith(prefix));
|
||||
return [path.slice('./root'.length), await module()];
|
||||
}),
|
||||
));
|
||||
8
website/src/playground/files/root/.eslintrc.cjs
Normal file
8
website/src/playground/files/root/.eslintrc.cjs
Normal file
@@ -0,0 +1,8 @@
|
||||
module.exports = {
|
||||
extends: [
|
||||
"../../../.eslintrc.cjs",
|
||||
],
|
||||
rules: {
|
||||
quotes: ["error", "double"],
|
||||
},
|
||||
};
|
||||
13
website/src/playground/files/root/examples/counter.ts
Normal file
13
website/src/playground/files/root/examples/counter.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
export default function main() {
|
||||
let c = new Counter();
|
||||
|
||||
return [c.get(), c.get(), c.get()];
|
||||
}
|
||||
|
||||
class Counter {
|
||||
next = 1;
|
||||
|
||||
get() {
|
||||
return this.next++;
|
||||
}
|
||||
}
|
||||
11
website/src/playground/files/root/examples/factorial.ts
Normal file
11
website/src/playground/files/root/examples/factorial.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
export default function main() {
|
||||
return factorial(5);
|
||||
}
|
||||
|
||||
function factorial(n: number): number {
|
||||
if (n === 0) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return n * factorial(n - 1);
|
||||
}
|
||||
24
website/src/playground/files/root/examples/idGeneration.ts
Normal file
24
website/src/playground/files/root/examples/idGeneration.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
export default function main() {
|
||||
let nextId = 1;
|
||||
|
||||
function generateId() {
|
||||
const result = nextId;
|
||||
nextId++;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
return [
|
||||
generateId(),
|
||||
generateId(),
|
||||
generateId(),
|
||||
];
|
||||
}
|
||||
51
website/src/playground/files/root/examples/mergeSort.ts
Normal file
51
website/src/playground/files/root/examples/mergeSort.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
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;
|
||||
}
|
||||
69
website/src/playground/files/root/examples/quickSort.ts
Normal file
69
website/src/playground/files/root/examples/quickSort.ts
Normal file
@@ -0,0 +1,69 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
23
website/src/playground/files/root/examples/reverse.ts
Normal file
23
website/src/playground/files/root/examples/reverse.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
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;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
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";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
export default function main() {
|
||||
let sum = 0;
|
||||
|
||||
for (let i = 1; i <= 50; i++) {
|
||||
sum += i;
|
||||
}
|
||||
|
||||
return sum;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
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),
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
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(),
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
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,
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
export default function main() {
|
||||
let a = 5;
|
||||
a += 2;
|
||||
|
||||
return a;
|
||||
}
|
||||
38
website/src/playground/files/root/lib/BinaryTree.ts
Normal file
38
website/src/playground/files/root/lib/BinaryTree.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
export default 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;
|
||||
}
|
||||
}
|
||||
16
website/src/playground/files/root/tutorial/binaryTree.ts
Normal file
16
website/src/playground/files/root/tutorial/binaryTree.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import BinaryTree from '../lib/BinaryTree';
|
||||
|
||||
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()];
|
||||
}
|
||||
12
website/src/playground/files/root/tutorial/hello.ts
Normal file
12
website/src/playground/files/root/tutorial/hello.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
// 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 to continue, click next below.
|
||||
18
website/src/playground/files/root/tutorial/revertOnCatch.ts
Normal file
18
website/src/playground/files/root/tutorial/revertOnCatch.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
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.
|
||||
15
website/src/playground/files/root/tutorial/valueSemantics.ts
Normal file
15
website/src/playground/files/root/tutorial/valueSemantics.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
export default function main() {
|
||||
const leftBowl = ["apple", "mango"];
|
||||
|
||||
const 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.
|
||||
@@ -19,5 +19,6 @@ export default defineConfig({
|
||||
playground: resolve(src, 'playground', 'index.html'),
|
||||
},
|
||||
},
|
||||
target: 'esnext',
|
||||
},
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user