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
|
||||
|
||||
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).
|
||||
|
||||
To write loops in TypeScript's type system, we have to use recursions, like we do in other purely functional
|
||||
programming languages. Meanwhile, we use [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.
|
||||
To write loops in TypeScript's type system, we have to use recursions,
|
||||
like we do in other purely functional programming languages. Meanwhile, we use
|
||||
[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
|
||||
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
|
||||
gcc/clang. Unfortunately, there isn't, [and it's likely to stay that way][PR 29602].
|
||||
You may expect that there is an option somewhere that this limit can be
|
||||
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
|
||||
applies. Changing `instantiationDepth` to `1000` is sufficient to run the example above.
|
||||
To workaround this limitation, we modify the code of `tsserver` or `tsc`
|
||||
in `node_modules`, until the error no longer applies. Changing
|
||||
`instantiationDepth` to `1000` is sufficient to run the example above.
|
||||
|
||||
|
||||
<!-- 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
|
||||
[Unlambda]: http://www.madore.org/~david/programs/unlambda/
|
||||
[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
|
||||
|
|
|
@ -9,17 +9,20 @@
|
|||
* 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';
|
||||
| '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 FuncNoChar = FuncRead | FuncComp | FuncPipe |
|
||||
FuncS | FuncK | FuncI | FuncV | FuncC | FuncD | FuncE | FuncR;
|
||||
export type FuncS = 's' | 'S';
|
||||
export type FuncK = 'k' | 'K';
|
||||
export type FuncI = 'i' | 'I';
|
||||
|
@ -61,7 +64,8 @@ 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;
|
||||
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.
|
||||
|
@ -69,8 +73,6 @@ export type GetFuncChar<S extends FuncChar> = S extends `${infer _}${infer U}` ?
|
|||
export type Expression = [Expression, Expression] | Func;
|
||||
|
||||
/**
|
||||
* An application of two expressions.
|
||||
*
|
||||
* Returns `never` if either expression is invalid.
|
||||
* A whitespace character in Unlambda code.
|
||||
*/
|
||||
export type Application<L extends Expression, R extends Expression> = [L, R];
|
||||
export type WhiteSpace = ' ' | '\n' | '\t';
|
||||
|
|
|
@ -5,12 +5,14 @@
|
|||
* @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];
|
||||
|
||||
|
@ -21,24 +23,28 @@ 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 : ''>
|
||||
C extends '#' ? Parse<R extends `${any}\n${infer N}` ? N : ''>
|
||||
// Trim whitespace.
|
||||
: C extends ' ' | '\n' | '\t' ? Parse<R>
|
||||
: C extends WhiteSpace ? 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]
|
||||
: 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").
|
||||
* 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.
|
||||
* 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 {
|
||||
Char, Func, GetFuncChar,
|
||||
FuncS, FuncK, FuncI, FuncV, FuncC, FuncD, FuncE, FuncR, FuncRead, FuncPipe, FuncPrint, FuncComp
|
||||
Char, Func, GetFuncChar, FuncRead, FuncPipe, FuncPrint, FuncComp,
|
||||
FuncS, FuncK, FuncI, FuncV, FuncC, FuncD, FuncE, FuncR,
|
||||
} from './language';
|
||||
|
||||
/**
|
||||
* 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.
|
||||
|
@ -21,7 +22,8 @@ type Concat<S1, S2> = S1 extends string ? S2 extends string ? `${S1}${S2}` : nev
|
|||
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 ``.
|
||||
// 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>
|
||||
|
@ -34,7 +36,8 @@ type Continue<Cont, F /* extends Func */, 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".
|
||||
// `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>
|
||||
|
@ -46,34 +49,46 @@ type Apply<L /* extends Func */, R /* extends Func */, Cont, IO> =
|
|||
: 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 ```.
|
||||
// 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,
|
||||
// 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`.
|
||||
// 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 ``.
|
||||
// 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).
|
||||
// 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",
|
||||
// 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",
|
||||
// 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 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;
|
||||
|
@ -82,8 +97,8 @@ type Apply<L /* extends Func */, R /* extends Func */, Cont, IO> =
|
|||
* Evalutate an expression.
|
||||
*/
|
||||
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>
|
||||
// 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>
|
||||
: never;
|
||||
|
|
|
@ -11,12 +11,14 @@ 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;
|
||||
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.
|
||||
|
|
Loading…
Reference in New Issue