Wrap lines to 79 characters max.
This commit is contained in:
parent
8b7a161651
commit
ca19bc599e
27
README.md
27
README.md
|
@ -29,15 +29,19 @@ Screenshots:
|
||||||
|
|
||||||
## Notes
|
## Notes
|
||||||
|
|
||||||
You're likely to get the following error when trying to run a program with type-unlambda:
|
You're likely to get the following error when trying to run a program with
|
||||||
|
type-unlambda:
|
||||||
|
|
||||||
> Type instantiation is excessively deep and possibly infinite.ts(2589).
|
> 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
|
To write loops in TypeScript's type system, we have to use recursions,
|
||||||
programming languages. Meanwhile, we use [CPS][] to implement continuations, which also introduces heavy recursion.
|
like we do in other purely functional programming languages. Meanwhile, we use
|
||||||
However, TypeScript's type system is not meant for general purpose programming, and recursion has its limits.
|
[CPS][] to implement continuations, which also introduces heavy recursion.
|
||||||
|
However, TypeScript's type system is not meant for general purpose programming,
|
||||||
|
and recursion has its limits.
|
||||||
|
|
||||||
In [src/compiler/checker.ts][TSC checker], there is a hard-coded limit for type instantiation:
|
In [src/compiler/checker.ts][TSC checker], there is a hard-coded limit for
|
||||||
|
type instantiation:
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
if (instantiationDepth === 50 || instantiationCount >= 5000000) {
|
if (instantiationDepth === 50 || instantiationCount >= 5000000) {
|
||||||
|
@ -46,11 +50,13 @@ if (instantiationDepth === 50 || instantiationCount >= 5000000) {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
You may expect that there is an option somewhere that this limit can be configured, like `-ftemplate-depth=n` in
|
You may expect that there is an option somewhere that this limit can be
|
||||||
gcc/clang. Unfortunately, there isn't, [and it's likely to stay that way][PR 29602].
|
configured, like `-ftemplate-depth=n` in gcc/clang. Unfortunately, there isn't,
|
||||||
|
[and it's likely to stay that way][PR 29602].
|
||||||
|
|
||||||
To workaround this limitation, we modify the code of `tsserver` or `tsc` in `node_modules`, until the error no longer
|
To workaround this limitation, we modify the code of `tsserver` or `tsc`
|
||||||
applies. Changing `instantiationDepth` to `1000` is sufficient to run the example above.
|
in `node_modules`, until the error no longer applies. Changing
|
||||||
|
`instantiationDepth` to `1000` is sufficient to run the example above.
|
||||||
|
|
||||||
|
|
||||||
<!-- Reference Links -->
|
<!-- Reference Links -->
|
||||||
|
@ -58,5 +64,6 @@ applies. Changing `instantiationDepth` to `1000` is sufficient to run the exampl
|
||||||
[MIT License]: https://img.shields.io/badge/license-MIT-blue.svg
|
[MIT License]: https://img.shields.io/badge/license-MIT-blue.svg
|
||||||
[Unlambda]: http://www.madore.org/~david/programs/unlambda/
|
[Unlambda]: http://www.madore.org/~david/programs/unlambda/
|
||||||
[CPS]: https://en.wikipedia.org/wiki/Continuation-passing_style/
|
[CPS]: https://en.wikipedia.org/wiki/Continuation-passing_style/
|
||||||
[TSC checker]: https://github.com/microsoft/TypeScript/blob/v4.1.2/src/compiler/checker.ts
|
[TSC checker]:
|
||||||
|
https://github.com/microsoft/TypeScript/blob/v4.1.2/src/compiler/checker.ts
|
||||||
[PR 29602]: https://github.com/microsoft/TypeScript/pull/29602
|
[PR 29602]: https://github.com/microsoft/TypeScript/pull/29602
|
||||||
|
|
|
@ -9,17 +9,20 @@
|
||||||
* Character which is readable and printable by Unlambda programs.
|
* Character which is readable and printable by Unlambda programs.
|
||||||
*/
|
*/
|
||||||
export type Char =
|
export type Char =
|
||||||
| '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' | 'A' | 'B' | 'C' | 'D' | 'E' | 'F' | 'G'
|
| '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' | 'A' | 'B'
|
||||||
| 'H' | 'I' | 'J' | 'K' | 'L' | 'M' | 'N' | 'O' | 'P' | 'Q' | 'R' | 'S' | 'T' | 'U' | 'V' | 'W' | 'X'
|
| 'C' | 'D' | 'E' | 'F' | 'G' | 'H' | 'I' | 'J' | 'K' | 'L' | 'M' | 'N'
|
||||||
| 'Y' | 'Z' | 'a' | 'b' | 'c' | 'd' | 'e' | 'f' | 'g' | 'h' | 'i' | 'j' | 'k' | 'l' | 'm' | 'n' | 'o'
|
| 'O' | 'P' | 'Q' | 'R' | 'S' | 'T' | 'U' | 'V' | 'W' | 'X' | 'Y' | 'Z'
|
||||||
| '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'
|
||||||
| '[' | ']' | '^' | '_' | '`' | '{' | '|' | '}' | '~' | '\\' | '\n';
|
| 'y' | 'z' | ' ' | '!' | '"' | '#' | '$' | '%' | '&' | "'" | '(' | ')'
|
||||||
|
| '*' | '+' | ',' | '-' | '.' | '/' | ':' | ';' | '<' | '=' | '>' | '?'
|
||||||
|
| '@' | '[' | ']' | '^' | '_' | '`' | '{' | '|' | '}' | '~' | '\\' | '\n';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A single character function.
|
* A single character function.
|
||||||
*/
|
*/
|
||||||
export type FuncNoChar = FuncS | FuncK | FuncI | FuncV | FuncC | FuncD | FuncE | FuncR | FuncRead | FuncComp | FuncPipe;
|
export type FuncNoChar = FuncRead | FuncComp | FuncPipe |
|
||||||
|
FuncS | FuncK | FuncI | FuncV | FuncC | FuncD | FuncE | FuncR;
|
||||||
export type FuncS = 's' | 'S';
|
export type FuncS = 's' | 'S';
|
||||||
export type FuncK = 'k' | 'K';
|
export type FuncK = 'k' | 'K';
|
||||||
export type FuncI = 'i' | 'I';
|
export type FuncI = 'i' | 'I';
|
||||||
|
@ -61,7 +64,8 @@ export type Func = PrimitiveFunc | CurriedFunc;
|
||||||
/**
|
/**
|
||||||
* Get the holding character of a function.
|
* Get the holding character of a function.
|
||||||
*/
|
*/
|
||||||
export type GetFuncChar<S extends FuncChar> = S extends `${infer _}${infer U}` ? U : never;
|
export type GetFuncChar<S /* extends FuncChar */> =
|
||||||
|
S extends `${any}${infer U}` ? U : never;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An expression is either a function or an application of two expressions.
|
* An expression is either a function or an application of two expressions.
|
||||||
|
@ -69,8 +73,6 @@ export type GetFuncChar<S extends FuncChar> = S extends `${infer _}${infer U}` ?
|
||||||
export type Expression = [Expression, Expression] | Func;
|
export type Expression = [Expression, Expression] | Func;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An application of two expressions.
|
* A whitespace character in Unlambda code.
|
||||||
*
|
|
||||||
* Returns `never` if either expression is invalid.
|
|
||||||
*/
|
*/
|
||||||
export type Application<L extends Expression, R extends Expression> = [L, R];
|
export type WhiteSpace = ' ' | '\n' | '\t';
|
||||||
|
|
|
@ -5,12 +5,14 @@
|
||||||
* @license MIT
|
* @license MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Application, Expression, FuncChar, FuncNoChar } from './language';
|
import { Expression, FuncChar, FuncNoChar, WhiteSpace } from './language';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse result is a tuple `[E, R]`, where `E` is the parsed expression, and `R` is the unparsed code fragment.
|
* 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.
|
* If `E` is `never`, or `R` is more than whitespaces and comments,
|
||||||
|
* the Unlambda code given to the parser is malformed.
|
||||||
*/
|
*/
|
||||||
export type ParseResult = [Expression, string];
|
export type ParseResult = [Expression, string];
|
||||||
|
|
||||||
|
@ -21,24 +23,28 @@ export type Parse<Code extends string> =
|
||||||
// `C` is the first character of the code, `R` is the rest.
|
// `C` is the first character of the code, `R` is the rest.
|
||||||
Code extends `${infer C}${infer R}` ?
|
Code extends `${infer C}${infer R}` ?
|
||||||
// Trim comment line.
|
// Trim comment line.
|
||||||
C extends '#' ? Parse<R extends `${infer _}\n${infer N}` ? N : ''>
|
C extends '#' ? Parse<R extends `${any}\n${infer N}` ? N : ''>
|
||||||
// Trim whitespace.
|
// Trim whitespace.
|
||||||
: C extends ' ' | '\n' | '\t' ? Parse<R>
|
: C extends WhiteSpace ? Parse<R>
|
||||||
// Apply the result of two consective parse.
|
// Apply the result of two consective parse.
|
||||||
: C extends '`' ? ParseApply<Parse<R>>
|
: C extends '`' ? ParseApply<Parse<R>>
|
||||||
// A single character function.
|
// A single character function.
|
||||||
: C extends FuncNoChar ? [C, R]
|
: C extends FuncNoChar ? [C, R]
|
||||||
// A `.x` or `?x` function.
|
// A `.x` or `?x` function.
|
||||||
: R extends `${infer C1}${infer R1}` ? `${C}${C1}` extends FuncChar ? [`${C}${C1}`, R1] : [never, R]
|
: R extends `${infer C1}${infer R1}` ?
|
||||||
|
`${C}${C1}` extends FuncChar ? [`${C}${C1}`, R1] : [never, R]
|
||||||
: [never, R]
|
: [never, R]
|
||||||
: [never, Code];
|
: [never, Code];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Given the left part (the "operator") of an application, parse the right part (the "operand").
|
* 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]>>;
|
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.
|
* 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]];
|
type ParseApplyResult<L extends Expression, R extends ParseResult> =
|
||||||
|
[[L, R[0]], R[1]];
|
||||||
|
|
|
@ -6,14 +6,15 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Char, Func, GetFuncChar,
|
Char, Func, GetFuncChar, FuncRead, FuncPipe, FuncPrint, FuncComp,
|
||||||
FuncS, FuncK, FuncI, FuncV, FuncC, FuncD, FuncE, FuncR, FuncRead, FuncPipe, FuncPrint, FuncComp
|
FuncS, FuncK, FuncI, FuncV, FuncC, FuncD, FuncE, FuncR,
|
||||||
} from './language';
|
} from './language';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Concatenate two strings.
|
* Concatenate two strings.
|
||||||
*/
|
*/
|
||||||
type Concat<S1, S2> = S1 extends string ? S2 extends string ? `${S1}${S2}` : never : never;
|
type Concat<S1, S2> =
|
||||||
|
S1 extends string ? S2 extends string ? `${S1}${S2}` : never : never;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Apply a continuation to a function.
|
* Apply a continuation to a function.
|
||||||
|
@ -21,7 +22,8 @@ type Concat<S1, S2> = S1 extends string ? S2 extends string ? `${S1}${S2}` : nev
|
||||||
type Continue<Cont, F /* extends Func */, IO> =
|
type Continue<Cont, F /* extends Func */, IO> =
|
||||||
// The left part of application is evalutated.
|
// The left part of application is evalutated.
|
||||||
Cont extends ['a1', infer R, infer Cont1] ?
|
Cont extends ['a1', infer R, infer Cont1] ?
|
||||||
// Function `d` delays the evaluation and stores the operand expression `F` in a promise `` `dF ``.
|
// Function `d` delays the evaluation and stores the
|
||||||
|
// operand expression `F` in a promise `` `dF ``.
|
||||||
F extends FuncD ? Continue<Cont1, ['d1', R], IO>
|
F extends FuncD ? Continue<Cont1, ['d1', R], IO>
|
||||||
// Evalutate the right part (the "operand") of application.
|
// Evalutate the right part (the "operand") of application.
|
||||||
: Eval<R, ['a2', F, Cont1], IO>
|
: Eval<R, ['a2', F, Cont1], IO>
|
||||||
|
@ -34,7 +36,8 @@ type Continue<Cont, F /* extends Func */, IO> =
|
||||||
* Apply function `L` to `R`.
|
* Apply function `L` to `R`.
|
||||||
*/
|
*/
|
||||||
type Apply<L /* extends Func */, R /* extends Func */, Cont, IO> =
|
type Apply<L /* extends Func */, R /* extends Func */, Cont, IO> =
|
||||||
// `I` is the input string, `O` the output string, and `C` the "current character".
|
// `I` is the input string, `O` the output string,
|
||||||
|
// and `C` the "current character".
|
||||||
IO extends [infer I, infer O, infer C] ?
|
IO extends [infer I, infer O, infer C] ?
|
||||||
// Function `k` takes an argument `X` and returns `` `kX ``.
|
// Function `k` takes an argument `X` and returns `` `kX ``.
|
||||||
L extends FuncK ? Continue<Cont, ['k1', R], IO>
|
L extends FuncK ? Continue<Cont, ['k1', R], IO>
|
||||||
|
@ -46,34 +49,46 @@ type Apply<L /* extends Func */, R /* extends Func */, Cont, IO> =
|
||||||
: L extends ['k1', infer X] ? Continue<Cont, X, IO>
|
: L extends ['k1', infer X] ? Continue<Cont, X, IO>
|
||||||
// Function `` `sX `` takes an argument `Y` and returns ` ``sXY `.
|
// Function `` `sX `` takes an argument `Y` and returns ` ``sXY `.
|
||||||
: L extends ['s1', infer X] ? Continue<Cont, ['s2', X, R], IO>
|
: L extends ['s1', infer X] ? Continue<Cont, ['s2', X, R], IO>
|
||||||
// Function ` ``sXY ` takes an argument `Z` and returns ``` ``XZ`YZ ```.
|
// Function ` ``sXY ` takes an argument `Z`
|
||||||
|
// and returns ``` ``XZ`YZ ```.
|
||||||
: L extends ['s2', infer X, infer Y] ? Eval<[[X, R], [Y, R]], Cont, IO>
|
: 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,
|
// 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.
|
// or the value passed to `<cont>` if the latter is applied.
|
||||||
: L extends FuncC ? Eval<[R, ['c1', Cont]], Cont, IO>
|
: 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,
|
// A continuation takes and argument `X` and jump to the point
|
||||||
// making `c` returns `X`.
|
// in history where function `c` was called, making `c` returns `X`.
|
||||||
: L extends ['c1', infer Cont1] ? Continue<Cont1, R, IO>
|
: 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 ``.
|
// 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>
|
: L extends ['d1', infer E] ? Eval<[E, R], Cont, IO>
|
||||||
// Function `v` takes an argument and returns `v`.
|
// Function `v` takes an argument and returns `v`.
|
||||||
: L extends FuncV ? Continue<Cont, FuncV, IO>
|
: L extends FuncV ? Continue<Cont, FuncV, IO>
|
||||||
// Function `.x` works like `i` with the side affect of printing a character `x`.
|
// Function `.x` works like `i` with the side affect of
|
||||||
: L extends FuncPrint ? Continue<Cont, R, [I, Concat<O, GetFuncChar<L>>, C]>
|
// printing a character `x`.
|
||||||
// Function `r` is an alias for function `.<\n>` (a dot followed by a newline).
|
: 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]>
|
: 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",
|
// Function `@` takes an argument `X`, read a character from input,
|
||||||
|
// make it the "current character",
|
||||||
: L extends FuncRead ? I extends `${infer C1}${infer R1}` ?
|
: L extends FuncRead ? I extends `${infer C1}${infer R1}` ?
|
||||||
// returns `` `Xi `` if the character is successfully read,
|
// returns `` `Xi `` if the character is successfully read,
|
||||||
Eval<[R, FuncI], Cont, [R1, O, C1]>
|
Eval<[R, FuncI], Cont, [R1, O, C1]>
|
||||||
// or `` `Xv `` if not.
|
// or `` `Xv `` if not.
|
||||||
: Eval<[R, FuncV], Cont, ['', O, '']>
|
: Eval<[R, FuncV], Cont, ['', O, '']>
|
||||||
// Function `?x` takes an argument `X` and returns `` `Xi `` if "current character" is `x`, or `` `Xv `` if not.
|
// Function `?x` takes an argument `X` and returns `` `Xi ``
|
||||||
: L extends FuncComp ? Eval<[R, GetFuncChar<L> extends C ? FuncI : FuncV], Cont, IO>
|
// if "current character" is `x`, or `` `Xv `` if not.
|
||||||
// Function `|` takes an argument `X` and returns `` `X.x `` where `x` is the "current character",
|
: 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".
|
// or `` `Xv `` if there's no "current character".
|
||||||
: L extends FuncPipe ? Eval<[R, C extends Char ? `.${C}` : FuncV], Cont, IO>
|
: L extends FuncPipe ?
|
||||||
// Function `e` takes an argument `X` and exits the program, pretending the evaluation result is `X`.
|
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]
|
: L extends FuncE ? [R, IO]
|
||||||
: never
|
: never
|
||||||
: never;
|
: never;
|
||||||
|
@ -82,8 +97,8 @@ type Apply<L /* extends Func */, R /* extends Func */, Cont, IO> =
|
||||||
* Evalutate an expression.
|
* Evalutate an expression.
|
||||||
*/
|
*/
|
||||||
export type Eval<E /* extends Expression */, Cont, IO> =
|
export type Eval<E /* extends Expression */, Cont, IO> =
|
||||||
// Expression is a function,
|
// Expression is a function, apply the continuation.
|
||||||
E extends Func ? Continue<Cont, E, IO>
|
E extends Func ? Continue<Cont, E, IO>
|
||||||
// Expression is an application, evalutate the left part (the "operator").
|
// Expression is an application, evaluate the left part (the "operator").
|
||||||
: E extends [infer L, infer R] ? Eval<L, ['a1', R, Cont], IO>
|
: E extends [infer L, infer R] ? Eval<L, ['a1', R, Cont], IO>
|
||||||
: never;
|
: never;
|
||||||
|
|
|
@ -11,12 +11,14 @@ import { Eval } from './runtime';
|
||||||
/**
|
/**
|
||||||
* Get the parse result expression, or `never` if parse failed.
|
* Get the parse result expression, or `never` if parse failed.
|
||||||
*/
|
*/
|
||||||
type ParseResultValue<T extends ParseResult> = T[1] extends '' ? T[0] : never;
|
type ParseResultValue<T extends ParseResult> =
|
||||||
|
Parse<T[1]> extends [never, ''] ? T[0] : never;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the output string of an evalalutation result, or `never` if evaluation failed.
|
* 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;
|
type EvalResultOutput<T> = T extends [any, [any, infer O, any]] ? O : never;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Given the source code of an Unlambda program, and input string.
|
* Given the source code of an Unlambda program, and input string.
|
||||||
|
|
Loading…
Reference in New Issue