Browse Source

Wrap lines to 79 characters max.

primary
CismonX 4 months ago
parent
commit
ca19bc599e
Signed by: CismonX GPG Key ID: 3094873E29A482FB
  1. 27
      README.md
  2. 26
      src/language.ts
  3. 24
      src/parser.ts
  4. 57
      src/runtime.ts
  5. 8
      src/unlambda.ts

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

26
src/language.ts

@ -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';

24
src/parser.ts

@ -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]];

57
src/runtime.ts

@ -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;

8
src/unlambda.ts

@ -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…
Cancel
Save