summaryrefslogtreecommitdiff
path: root/examples
diff options
context:
space:
mode:
Diffstat (limited to 'examples')
-rw-r--r--examples/README.md13
-rw-r--r--examples/branching.ts24
-rw-r--r--examples/createFn.ts24
-rw-r--r--examples/mapper.ts36
-rw-r--r--examples/recursion.ts18
-rw-r--r--examples/runtime.ts15
6 files changed, 130 insertions, 0 deletions
diff --git a/examples/README.md b/examples/README.md
new file mode 100644
index 0000000..33c957d
--- /dev/null
+++ b/examples/README.md
@@ -0,0 +1,13 @@
+## A guided tour of KaiScript through examples
+
+Open this in your IDE of choice--make sure your LSP is running so you can inspect types.
+
+1. [Breaking the second wall](./mapper.ts)
+
+2. [Code that does stuff](./runtime.ts)
+
+3. [Useful code](./createFn.ts)
+
+4. [Turing completeness](./branching.ts)
+
+5. [Infinite computation](./recursion.ts)
diff --git a/examples/branching.ts b/examples/branching.ts
new file mode 100644
index 0000000..e504f9d
--- /dev/null
+++ b/examples/branching.ts
@@ -0,0 +1,24 @@
+/*
+KaiScript is turing complete. Use the "?" function to branch execution.
+Only the branch matching the condition will be evaluated.
+*/
+
+import { createFn } from "../src";
+
+const myBranchingFn = createFn<[boolean]>()("fn(b, ?(b, 1, 0))");
+
+// const result: 1
+const result = myBranchingFn(true);
+
+// This is very powerful! For example, if you're bad at addition, KaiScript can check your work
+const isAdditionCorrect = createFn<[m: number, n: number, expected: number]>()(
+ `fn(m,n,e, ?(eq(add(m, n), e), "correct", "incorrect"))`
+);
+
+// const badAddition: "incorrect"
+const badAddition = isAdditionCorrect(5, 10, 2);
+console.log(badAddition);
+
+// const goodAddition: "correct"
+const goodAddition = isAdditionCorrect(5, 10, 15);
+console.log(goodAddition);
diff --git a/examples/createFn.ts b/examples/createFn.ts
new file mode 100644
index 0000000..4f8e22f
--- /dev/null
+++ b/examples/createFn.ts
@@ -0,0 +1,24 @@
+/*
+Writing and immediately evaluating KaiScript programs is of little use.
+You'll find the most benefit from its native interoperability with TypeScript.
+*/
+
+// createFn receives a KaiScript program and evaluates it. If it evaluates to a function,
+// it will return a generic TypeScript function corresponding to this function.
+import { createFn } from "../src";
+
+// We define a function F(n) -> n
+const myFn = createFn()(`fn(n, n)`);
+
+// const result: 5
+const result = myFn(5);
+console.log(result);
+
+// We can use this to define useful functions, and restrict their arguments
+const myUsefulFn = createFn<[number, number]>()(
+ `fn(m, n, add(tostring(m), "*", tostring(n), "=", tostring(mul(m,n))))`
+);
+
+// const usefulResult: "2*5=10"
+const usefulResult = myUsefulFn(2, 5);
+console.log(usefulResult);
diff --git a/examples/mapper.ts b/examples/mapper.ts
new file mode 100644
index 0000000..3485e02
--- /dev/null
+++ b/examples/mapper.ts
@@ -0,0 +1,36 @@
+/*
+If you've ever said "I wish generic types were first class," you're probably doing something wrong.
+On the other hand, you might be doing something right! KaiScript makes this possible.
+*/
+
+import { CallFn, Evaluate, FnPrim, Lex, Parse } from "../src";
+
+// You might have a type that looks like this
+type MyGenericMapper<T extends number> = `${T}`;
+// And another that looks like this
+type MyGenericMapper2<T extends number> = `number:${T}`;
+
+// And another type that wants to consume them
+//@ts-expect-error
+type Map5<Mapper> = Mapper<5>;
+
+// Hmm, can't do that. ...maybe this?
+type Map5_2<Mapper extends <T extends number>() => string> = ReturnType<
+ //@ts-expect-error
+ Mapper<5>
+>;
+
+// But with KaiScript:
+
+type MyKaiScriptMapper = Evaluate<Parse<Lex<"fn(n, tostring(n))">>>[0];
+type MyKaiScriptMapper2 = Evaluate<
+ Parse<Lex<'fn(n, tostring(add("number:", tostring(n))))'>>
+>[0];
+
+type Map5KaiScript<Mapper extends FnPrim> = CallFn<Mapper, [5]>;
+// "5"
+type Mapped1 = Map5KaiScript<MyKaiScriptMapper>;
+// "number:5"
+type Mapped2 = Map5KaiScript<MyKaiScriptMapper2>;
+
+// KaiScript lets you get an extra order of primitivity out of your types
diff --git a/examples/recursion.ts b/examples/recursion.ts
new file mode 100644
index 0000000..8ee899f
--- /dev/null
+++ b/examples/recursion.ts
@@ -0,0 +1,18 @@
+/*
+Any practical programming language can of course handle recursion, and KaiScript is no exception.
+Factorial is a function that is particularly easy to define recursively.
+*/
+
+import { createFn } from "../src";
+
+// bind(name, fn) returns fn inside a closure containing itself bound to name
+const factorial = createFn<[number]>()(
+ `bind(fac,fn(n,?(eq(n, 1),n,mul(n,call(fac,sub(n,1))))))`
+);
+
+// const factRes: 720
+const factRes = factorial(6);
+console.log(factRes);
+
+// You might be disappointed to learn that computing factorials larger than 6
+// fails catastrophically. Blame TypeScript.
diff --git a/examples/runtime.ts b/examples/runtime.ts
new file mode 100644
index 0000000..2a1555c
--- /dev/null
+++ b/examples/runtime.ts
@@ -0,0 +1,15 @@
+/*
+KaiScript isn't just a pretty face (IDE hint), it's also a full and complete interpreted language.
+Any program you write in KaiScript will return exactly what it says it will.
+*/
+
+import { evaluate, lex, parse } from "../src";
+
+// First, hover over myArray in your IDE...
+// const myArray: [[1, 2, 3, 4, 5]]
+const myArray = evaluate(parse(lex(`arr(1,2,3,4,5)`)));
+
+// ...then run this file to see that the output matches
+console.log(myArray);
+
+// It's a nested tuple because all KaiScript programs are arrays. That is, the program <5, 3> would return [5, 3]