diff options
Diffstat (limited to 'examples')
| -rw-r--r-- | examples/README.md | 13 | ||||
| -rw-r--r-- | examples/branching.ts | 24 | ||||
| -rw-r--r-- | examples/createFn.ts | 24 | ||||
| -rw-r--r-- | examples/mapper.ts | 36 | ||||
| -rw-r--r-- | examples/recursion.ts | 18 | ||||
| -rw-r--r-- | examples/runtime.ts | 15 |
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] |
