initial commit

This commit is contained in:
CismonX 2020-11-06 17:07:33 +08:00
commit 770bd49e49
Signed by: cismonx
GPG Key ID: 3094873E29A482FB
9 changed files with 349 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
.vscode/
node_modules/

21
LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2020 CismonX <admin@cismon.net>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

48
README.md Normal file
View File

@ -0,0 +1,48 @@
# README
[![MIT License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
type-unlambda - [Unlambda](http://www.madore.org/~david/programs/unlambda) interpreter implemented in TypeScript's type system
## Getting Started
Installation:
```sh
npm install --save-dev @esolangs/type-unlambda
```
Usage:
```typescript
import Unlambda from '@esolangs/type-unlambda';
type Code = '``@c`d``s`|k`@c';
type Input = 'Hello!';
type Output = Unlambda<Code, Input>; // Output == '!olleH'
```
Screenshots:
![Reverse print string](https://user-images.githubusercontent.com/19173506/98339205-4efdfd80-2046-11eb-880d-f2b2333d61a0.png)
## Notes
You're lilely to get the following error when trying to run a program with type-unlambda:
> Type instantiation is excessively deep and possibly infinite.ts(2589).
To write loops in TypeScript's type system, we have to use recursions, like we do in other purely functional programming languages. However, TypeScript's type system is not meant for general purpose programming, and recursion has its limits.
In [src/compiler/checker.ts](https://raw.githubusercontent.com/microsoft/TypeScript/release-4.1/src/compiler/checker.ts), there is a hard-coded limit for type instantiation:
```typescript
if (instantiationDepth === 50 || instantiationCount >= 5000000) {
// ...
return errorType;
}
```
You may expect that there is an option somewhere where this limit can be configured, like `-ftemplate-depth=n` in gcc/clang. Unfortuanately, there isn't, [and it's likely to stay that way](https://github.com/microsoft/TypeScript/pull/29602).
To workaround this limitation, we modify the code of `tsserver` or `tsc` and loosen these limits until the error no longer applies. Changing `instantiationDepth` to `1000` is sufficient to run the example above.

14
package-lock.json generated Normal file
View File

@ -0,0 +1,14 @@
{
"name": "type-unlambda",
"version": "0.1.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"typescript": {
"version": "4.1.0-dev.20201102",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.1.0-dev.20201102.tgz",
"integrity": "sha512-kyL2MUGRx69NgowtHpnabYzNA3N8CR+XW51kL9my6MfSXyrojytW5P4YmaayHGQhWLRmzzdDvHfkf2PQBHgbUw==",
"dev": true
}
}
}

26
package.json Normal file
View File

@ -0,0 +1,26 @@
{
"name": "type-unlambda",
"version": "0.1.0",
"description": "Unlambda interpreter implemented in TypeScript's type system",
"keywords": [
"Unlambda",
"interpreter",
"esoteric-language"
],
"repository": {
"url": "https://github.com/esolangs/type-unlambda",
"type": "git"
},
"author": {
"name": "CismonX",
"email": "admin@cismon.net",
"url": "https://cismon.net"
},
"license": "MIT",
"devDependencies": {
"typescript": "^4.1.0-dev.20201102"
},
"peerDependencies": {
"typescript": "^4.1.0-dev.20201102"
}
}

29
src/index.ts Normal file
View File

@ -0,0 +1,29 @@
/**
* index.ts - Unlambda interpreter entry.
*
* @author CismonX <admin@cismon.net>
* @license MIT
*/
import { Parse, ParseResult } from './parser';
import { Eval } from './runtime';
/**
* Get the parse result expression, or `never` if parse failed.
*/
type ParseResultValue<T extends ParseResult> = T[1] extends '' ? T[0] : never;
/**
* Get the output string of an evalalutation result, or `never` if evaluation failed.
*/
type EvalResultOutput<T> = T extends [infer F, [infer I, infer O, infer C]] ? O : never;
/**
* Given the source code of an Unlambda program, and input string.
*
* Returns the output of program execution, or `never` if something went wrong.
*/
type Unlambda<Code extends string, Input extends string = ''> =
EvalResultOutput<Eval<ParseResultValue<Parse<Code>>, [], [Input, '', '']>>;
export default Unlambda;

76
src/language.ts Normal file
View File

@ -0,0 +1,76 @@
/**
* language.ts - Definitions and utilities related to the Unlambda language.
*
* @author CismonX <admin@cismon.net>
* @license MIT
*/
/**
* Character which is readable and printable by Unlambda programs.
*/
export type Char =
| '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' | 'A' | 'B' | 'C' | 'D' | 'E' | 'F' | 'G'
| 'H' | 'I' | 'J' | 'K' | 'L' | 'M' | 'N' | 'O' | 'P' | 'Q' | 'R' | 'S' | 'T' | 'U' | 'V' | 'W' | 'X'
| 'Y' | 'Z' | 'a' | 'b' | 'c' | 'd' | 'e' | 'f' | 'g' | 'h' | 'i' | 'j' | 'k' | 'l' | 'm' | 'n' | 'o'
| 'p' | 'q' | 'r' | 's' | 't' | 'u' | 'v' | 'w' | 'x' | 'y' | 'z' | ' ' | '!' | '"' | '#' | '$' | '%'
| '&' | "'" | '(' | ')' | '*' | '+' | ',' | '-' | '.' | '/' | ':' | ';' | '<' | '=' | '>' | '?' | '@'
| '[' | ']' | '^' | '_' | '`' | '{' | '|' | '}' | '~' | '\\' | '\n';
/**
* A single character function.
*/
export type FuncNoChar = FuncS | FuncK | FuncI | FuncV | FuncC | FuncD | FuncE | FuncR | FuncRead | FuncComp | FuncPipe;
export type FuncS = 's' | 'S';
export type FuncK = 'k' | 'K';
export type FuncI = 'i' | 'I';
export type FuncV = 'v' | 'V';
export type FuncC = 'c' | 'C';
export type FuncD = 'd' | 'D';
export type FuncE = 'e' | 'E';
export type FuncR = 'r' | 'R';
export type FuncRead = '@';
export type FuncPipe = '|';
/**
* A function which holds a character.
*/
export type FuncChar = FuncPrint | FuncComp;
export type FuncPrint = `.${Char}`;
export type FuncComp = `?${Char}`;
/**
* A primitive function.
*/
export type PrimitiveFunc = FuncNoChar | FuncChar;
/**
* A function obtained by curry-ing primitive functions.
*/
export type CurriedFunc = FuncK1 | FuncS1 | FuncS2 | FuncC1 | FuncD1;
export type FuncK1 = ['k1', Func];
export type FuncS1 = ['s1', Func];
export type FuncS2 = ['s2', Func, Func];
export type FuncC1 = ['c1', any];
export type FuncD1 = ['d1', Expression];
/**
* An Unlambda builtin function.
*/
export type Func = PrimitiveFunc | CurriedFunc;
/**
* Get the holding character of a function.
*/
export type GetFuncChar<S extends FuncChar> = S extends `${infer _}${infer U}` ? U : never;
/**
* An expression is either a function or an application of two expressions.
*/
export type Expression = [Expression, Expression] | Func;
/**
* An application of two expressions.
*
* Returns `never` if either expression is invalid.
*/
export type Application<L extends Expression, R extends Expression> = [L, R];

44
src/parser.ts Normal file
View File

@ -0,0 +1,44 @@
/**
* parser.ts - Unlambda parser.
*
* @author CismonX <admin@cismon.net>
* @license MIT
*/
import { Application, Expression, FuncChar, FuncNoChar } from './language';
/**
* Parse result is a tuple `[E, R]`, where `E` is the parsed expression, and `R` is the unparsed code fragment.
*
* If `E` is `never`, or `R` is not `''`, the Unlambda code given to the parser is malformed.
*/
export type ParseResult = [Expression, string];
/**
* Parse the given Unlambda code. Returns a `ParseResult`.
*/
export type Parse<Code extends string> =
// `C` is the first character of the code, `R` is the rest.
Code extends `${infer C}${infer R}` ?
// Trim comment line.
C extends '#' ? Parse<R extends `${infer _}\n${infer N}` ? N : ''>
// Trim whitespace.
: C extends ' ' | '\n' | '\t' ? Parse<R>
// Apply the result of two consective parse.
: C extends '`' ? ParseApply<Parse<R>>
// A single character function.
: C extends FuncNoChar ? [C, R]
// A `.x` or `?x` function.
: R extends `${infer C1}${infer R1}` ? `${C}${C1}` extends FuncChar ? [`${C}${C1}`, R1] : [never, R]
: [never, R]
: [never, Code];
/**
* Given the left part (the "operator") of an application, parse the right part (the "operand").
*/
type ParseApply<L extends ParseResult> = ParseApplyResult<L[0], Parse<L[1]>>;
/**
* Given the parse result of two parts of an application, return the final parse result.
*/
type ParseApplyResult<L extends Expression, R extends ParseResult> = [Application<L, R[0]>, R[1]];

89
src/runtime.ts Normal file
View File

@ -0,0 +1,89 @@
/**
* runtime.ts - Unlambda runtime.
*
* @author CismonX <admin@cismon.net>
* @license MIT
*/
import {
Char, Func, GetFuncChar,
FuncS, FuncK, FuncI, FuncV, FuncC, FuncD, FuncE, FuncR, FuncRead, FuncPipe, FuncPrint, FuncComp
} from './language';
/**
* Concatenate two strings.
*/
type Concat<S1, S2> = S1 extends string ? S2 extends string ? `${S1}${S2}` : never : never;
/**
* Apply a continuation to a function.
*/
type Continue<Cont, F /* extends Func */, IO> =
// The left part of application is evalutated.
Cont extends ['a1', infer R, infer Cont1] ?
// Function `d` delays the evaluation and stores the operand expression `F` in a promise `` `dF ``.
F extends FuncD ? Continue<Cont1, ['d1', R], IO>
// Evalutate the right part (the "operand") of application.
: Eval<R, ['a2', F, Cont1], IO>
// Apply the operator to the operand, both evaluated.
: Cont extends ['a2', infer R, infer Cont1] ? Apply<R, F, Cont1, IO>
// Evaluation is completed.
: [F, IO];
/**
* Apply function `L` to `R`.
*/
type Apply<L /* extends Func */, R /* extends Func */, Cont, IO> =
// `I` is the input string, `O` the output string, and `C` the "current character".
IO extends [infer I, infer O, infer C] ?
// Function `k` takes an argument `X` and returns `` `kX ``.
L extends FuncK ? Continue<Cont, ['k1', R], IO>
// Function `s` takes an argument `X` and returns `` `sX ``.
: L extends FuncS ? Continue<Cont, ['s1', R], IO>
// Function `i` takes an argument and returns it.
: L extends FuncI ? Continue<Cont, R, IO>
// Function `` `kX `` takes an argument, ignores it and returns `X`.
: L extends ['k1', infer X] ? Continue<Cont, X, IO>
// Function `` `sX `` takes an argument `Y` and returns ` ``sXY `.
: L extends ['s1', infer X] ? Continue<Cont, ['s2', X, R], IO>
// Function ` ``sXY ` takes an argument `Z` and returns ``` ``XZ`YZ ```.
: L extends ['s2', infer X, infer Y] ? Eval<[[X, R], [Y, R]], Cont, IO>
// Function `c` takes an argument `X` and returns `` `X<cont> `` where `<cont>` is the current continuation,
// or the value passed to `<cont>` if the latter is applied.
: L extends FuncC ? Eval<[R, ['c1', Cont]], Cont, IO>
// A continuation takes and argument `X` and jump to the point in history where function `c` was called,
// making `c` returns `X`.
: L extends ['c1', infer Cont1] ? Continue<Cont1, R, IO>
// Function `` `dE `` takes an argument `Y` and evaluates `E`, giving a function `X`, and returns `` `XY ``.
: L extends ['d1', infer E] ? Eval<[E, R], Cont, IO>
// Function `v` takes an argument and returns `v`.
: L extends FuncV ? Continue<Cont, FuncV, IO>
// Function `.x` works like `i` with the side affect of printing a character `x`.
: L extends FuncPrint ? Continue<Cont, R, [I, Concat<O, GetFuncChar<L>>, C]>
// Function `r` is an alias for function `.<\n>` (a dot followed by a newline).
: L extends FuncR ? Continue<Cont, R, [I, Concat<O, '\n'>, C]>
// Function `@` takes an argument `X`, read a character from input, make it the "current character",
: L extends FuncRead ? I extends `${infer C1}${infer R1}` ?
// returns `` `Xi `` if the character is successfully read,
Eval<[R, FuncI], Cont, [R1, O, C1]>
// or `` `Xv `` if not.
: Eval<[R, FuncV], Cont, ['', O, '']>
// Function `?x` takes an argument `X` and returns `` `Xi `` if "current character" is `x`, or `` `Xv `` if not.
: L extends FuncComp ? Eval<[R, GetFuncChar<L> extends C ? FuncI : FuncV], Cont, IO>
// Function `|` takes an argument `X` and returns `` `X.x `` where `x` is the "current character",
// or `` `Xv `` if there's no "current character".
: L extends FuncPipe ? Eval<[R, C extends Char ? `.${C}` : FuncV], Cont, IO>
// Function `e` takes an argument `X` and exits the program, pretending the evaluation result is `X`.
: L extends FuncE ? [R, IO]
: never
: never;
/**
* Evalutate an expression.
*/
export type Eval<E /* extends Expression */, Cont, IO> =
// Expression is a function,
E extends Func ? Continue<Cont, E, IO>
// Expression is an application, evalutate the left part (the "operator").
: E extends [infer L, infer R] ? Eval<L, ['a1', R, Cont], IO>
: never;