Created
November 26, 2020 18:01
-
-
Save jcreedcmu/24859af7dd5bc7b102496eeccbd6a9fc to your computer and use it in GitHub Desktop.
Parsing sexps in at the type level in typescript
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Parsing sexpressions and turning them into nested array types, | |
// using template literal types. | |
// Cons a head onto an array type, assuming TL is actually an array type | |
type Cons<H, TL> = TL extends any[] ? [H, ...TL] : unknown; | |
// Interpret type names as types | |
type Interpret<TOK extends string> = | |
TOK extends 'number' ? number : | |
TOK extends 'string' ? string : | |
TOK extends 'boolean' ? boolean : unknown; | |
// Interpret and append a type identifier TOK to an array A of tokens | |
type TokCons<TOK, A> = TOK extends string ? Cons<Interpret<TOK>, A> : A; | |
// Concatenate a character C onto a token TOK (or start a new token if TOK is unknown) | |
type TokConcat<TOK, C extends string> = TOK extends string ? `${TOK}${C}` : C; | |
// Lex<unknown, '(number string (boolean string))'> = ["(", number, string, "(", boolean, string, ")", ")"]; | |
// The type parameter T is a string to be tokenized | |
// The type parameter TOK is an accumulator for the current identifier token, unknown if there is none. | |
type Lex<TOK, T extends string> = | |
T extends `(${infer X}` ? TokCons<TOK, Cons<'(', Lex<unknown, X>>> : | |
T extends `)${infer X}` ? TokCons<TOK, Cons<')', Lex<unknown, X>>> : | |
T extends ` ${infer X}` ? TokCons<TOK, Lex<unknown, X>> : | |
T extends `${infer C}${infer X}` ? Lex<TokConcat<TOK, C>, X> : | |
T extends `` ? [] : unknown; | |
// Parse<["(", number, string, "(", boolean, string, ")", ")"], [[]]> = [number, string, [boolean, string]] | |
// The type parameter TOKS is an array type of tokens to be parsed | |
// The type parameter STACKS accumulates sexp arrays | |
type Parse<TOKS, STACK> = | |
TOKS extends ['(', ...infer TL] ? (STACK extends [...infer SBODY] ? Parse<TL, [[], ...SBODY]> : unknown) : | |
TOKS extends [')', ...infer TL] ? (STACK extends [[...infer ACC], [...infer ACC2], ...infer STL] ? Parse<TL, [[...ACC2, ACC], ...STL]> : unknown) : | |
TOKS extends [infer H, ...infer TL] ? (STACK extends [[...infer ACC], ...infer STL] ? Parse<TL, [[...ACC, H], ...STL]> : unknown) : | |
TOKS extends [] ? (STACK extends [[infer FINAL]] ? FINAL : unknown) : unknown; | |
// Convert an sexp string into a nested array type | |
type FromSexp<T extends string> = Parse<Lex<unknown, T>, [[]]>; | |
// Example of use | |
type SexpType = FromSexp<'(number string (boolean string))'>; // [number, string, [boolean, string]] | |
const value: SexpType = [3, 'foo', [true, 'bar']]; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment