From ca19bc599e55cdf8cae1472c001a96a098ef54a1 Mon Sep 17 00:00:00 2001 From: CismonX Date: Thu, 3 Jun 2021 21:27:35 +0800 Subject: [PATCH] Wrap lines to 79 characters max. --- README.md | 27 ++++++++++++++--------- src/language.ts | 26 +++++++++++----------- src/parser.ts | 24 +++++++++++++-------- src/runtime.ts | 57 +++++++++++++++++++++++++++++++------------------ src/unlambda.ts | 8 ++++--- 5 files changed, 87 insertions(+), 55 deletions(-) diff --git a/README.md b/README.md index c847bb8..61ebd75 100644 --- a/README.md +++ b/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. @@ -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 diff --git a/src/language.ts b/src/language.ts index a6c2aa8..971acc4 100644 --- a/src/language.ts +++ b/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 `${infer _}${infer U}` ? U : never; +export type GetFuncChar = + 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 `${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, R]; +export type WhiteSpace = ' ' | '\n' | '\t'; diff --git a/src/parser.ts b/src/parser.ts index 0b2910b..72016ba 100644 --- a/src/parser.ts +++ b/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 = // `C` is the first character of the code, `R` is the rest. Code extends `${infer C}${infer R}` ? // Trim comment line. - C extends '#' ? Parse + C extends '#' ? Parse // Trim whitespace. - : C extends ' ' | '\n' | '\t' ? Parse + : C extends WhiteSpace ? Parse // Apply the result of two consective parse. : C extends '`' ? ParseApply> // 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 = ParseApplyResult>; /** - * 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 = [Application, R[1]]; +type ParseApplyResult = + [[L, R[0]], R[1]]; diff --git a/src/runtime.ts b/src/runtime.ts index dec6aff..bcdb981 100644 --- a/src/runtime.ts +++ b/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 extends string ? S2 extends string ? `${S1}${S2}` : never : never; +type Concat = + S1 extends string ? S2 extends string ? `${S1}${S2}` : never : never; /** * Apply a continuation to a function. @@ -21,7 +22,8 @@ type Concat = S1 extends string ? S2 extends string ? `${S1}${S2}` : nev type Continue = // 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 // Evalutate the right part (the "operand") of application. : Eval @@ -34,7 +36,8 @@ type Continue = * Apply function `L` to `R`. */ type Apply = - // `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 @@ -46,34 +49,46 @@ type Apply = : L extends ['k1', infer X] ? Continue // Function `` `sX `` takes an argument `Y` and returns ` ``sXY `. : L extends ['s1', infer X] ? Continue - // 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 `` where `` is the current continuation, + // Function `c` takes an argument `X` and returns `` `X `` + // where `` is the current continuation, // or the value passed to `` 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 - // 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 - // Function `.x` works like `i` with the side affect of printing a character `x`. - : L extends FuncPrint ? Continue>, 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>, C]> + // Function `r` is an alias for function `.<\n>` + // (a dot followed by a newline). : L extends FuncR ? Continue, 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 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 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 = * Evalutate an expression. */ export type Eval = - // Expression is a function, + // Expression is a function, apply the continuation. E extends Func ? Continue - // 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 : never; diff --git a/src/unlambda.ts b/src/unlambda.ts index 45dccc6..63ad7b3 100644 --- a/src/unlambda.ts +++ b/src/unlambda.ts @@ -11,12 +11,14 @@ import { Eval } from './runtime'; /** * Get the parse result expression, or `never` if parse failed. */ -type ParseResultValue = T[1] extends '' ? T[0] : never; +type ParseResultValue = + Parse 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 extends [infer F, [infer I, infer O, infer C]] ? O : never; +type EvalResultOutput = T extends [any, [any, infer O, any]] ? O : never; /** * Given the source code of an Unlambda program, and input string.