Skip to content

Instantly share code, notes, and snippets.

@dubzzz
Created November 29, 2020 15:42
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dubzzz/6dd965ee771d194d810a1bd84c6e002c to your computer and use it in GitHub Desktop.
Save dubzzz/6dd965ee771d194d810a1bd84c6e002c to your computer and use it in GitHub Desktop.
RPN Eval with Types
// Create a tuple of size N
type Tuple<N extends number, Rest extends any[] = []> = Rest['length'] extends N ? Rest : Tuple<N, [any, ...Rest]>;
type TestTuple_0 = Tuple<0>;
type TestTuple_2 = Tuple<2>;
type TestTuple_5 = Tuple<5>;
// Add two positive numbers
type Add<A extends number, B extends number> = [...Tuple<A>, ...Tuple<B>]['length'] & number;
type TestAdd_1_2 = Add<1, 2>;
type TestAdd_10_4 = Add<10, 4>;
// Multiply two positive numbers
type MulHelper<TA extends any[], TB extends any[], Buffer extends any[] = []> = TA extends [infer TAS, ...infer TAR]
? MulHelper<TAR, TB, [...TB, ...Buffer]>
: Buffer['length'];
type Mul<A extends number, B extends number> = MulHelper<Tuple<A>, Tuple<B>> & number;
type TestMul_1_2 = Mul<1, 2>;
type TestMul_2_4 = Mul<2, 4>;
type TestMul_3_4 = Mul<3, 4>;
type TestMul_10_25 = Mul<10, 25>;
// Parse a positive string representation of a positive number
// as a number
type Parse<S, Acc extends number = 0> = S extends '' ? Acc
: S extends `0${infer Rest}` ? Parse<Rest, Mul<10, Acc>>
: S extends `1${infer Rest}` ? Parse<Rest, Add<Mul<10, Acc>, 1>>
: S extends `2${infer Rest}` ? Parse<Rest, Add<Mul<10, Acc>, 2>>
: S extends `3${infer Rest}` ? Parse<Rest, Add<Mul<10, Acc>, 3>>
: S extends `4${infer Rest}` ? Parse<Rest, Add<Mul<10, Acc>, 4>>
: S extends `5${infer Rest}` ? Parse<Rest, Add<Mul<10, Acc>, 5>>
: S extends `6${infer Rest}` ? Parse<Rest, Add<Mul<10, Acc>, 6>>
: S extends `7${infer Rest}` ? Parse<Rest, Add<Mul<10, Acc>, 7>>
: S extends `8${infer Rest}` ? Parse<Rest, Add<Mul<10, Acc>, 8>>
: S extends `9${infer Rest}` ? Parse<Rest, Add<Mul<10, Acc>, 9>>
: never;
type TestParse_1 = Parse<"0">;
type TestParse_10 = Parse<"10">;
type TestParse_27 = Parse<"27">;
type TestParse_a = Parse<"a">;
// Evaluate a tokenized expression
type Eval<Tokens extends string[], Stack extends string[] = []> =
Tokens extends [infer Term, ...infer Rest] & [string, ...string[]]
? Rest extends string[]
? Term extends `${number}` ? Eval<Rest, [...Stack, Term]>
: Term extends `+`
? Stack extends [...infer RestStack, infer A, infer B]
? RestStack extends string[] ? Eval<Rest, [...RestStack, `${Add<Parse<A>, Parse<B>>}`]> : never
: never
: Term extends `*`
? Stack extends [...infer RestStack, infer A, infer B]
? RestStack extends string[] ? Eval<Rest, [...RestStack, `${Mul<Parse<A>, Parse<B>>}`]> : never
: never
: never
: never
: Stack[0]
type TestEval_1 = Eval<["1"]>;
type TestEval_2_3_plus = Eval<["2", "3", "+"]>;
type TestEval_2_3_times = Eval<["2", "3", "*"]>;
// Split a string
type Split<Expr extends string, Separator extends string, Buffer extends string[] = []> = Expr extends `${infer S}${Separator}${infer Rest}`
? Split<Rest, Separator, [...Buffer, S]>
: [...Buffer, Expr];
type TestSplit_A_B_C = Split<"A B C", " ">;
type TestSplit_ABC = Split<"ABC", " ">;
type TestSplit__ABC_ = Split<" ABC ", " ">;
// Evaluate a RPN notation
// More details: https://en.wikipedia.org/wiki/Reverse_Polish_notation
type RPN<TExpr extends string> = Eval<Split<TExpr, " ">>;
type TestRPN_A = RPN<"1 2 +">;
type TestRPN_B = RPN<"1 2 + 5 +">;
type TestRPN_C = RPN<"1 2 + 5 *">;
type TestRPN_D = RPN<"1 2 5 + *">;
type TestRPN_E = RPN<"3 10 5 + *">;
@dubzzz
Copy link
Author

dubzzz commented Nov 29, 2020

Try it directly in TS Playground:
https://www.typescriptlang.org/play?#code/PTAEGECcFMEMBdqlqeBXADgGyQewGagDOAlgF5IByAUPAJ4ZIAqmOAPJaNAB6IB2AEyKg+aALYAjaJAA0oAErQi8Lr2iDhsPnQDaAXVABeUPoB8RhUvg6A5Dj4BzeAAsbBnvyGhOAfkvLQAC5QFmxoDjkdLTo5ADp4xWU9UwBuWgZmK1CcAH0ABgts8LzU9MYQrNZoHIAmQqq2GtL6cqZKsJyAVnqwtk7S6hBQAEEBAVQAd1xQDFxSeBIANyRRSWkiMqRRgTZh1U9hValZUAAhffUvI+lzYx142KLd0zj4p9PTPVt7J1cDADIROJjmkWpllNscgBGWoWbZsKFyJqgjIVCFjaF5HIAFjhYwReTk2IGQwAsmgsAtsHRJtNZvMlitgetNqByVgABLQLCMSBsJh7DyXTTafRyJjnIUaZCivRyU5ofD4aQXaXRfQWMyFQVqaU6Eh8ZWQELDADKr1iBqNJvkemooAdoD87K5POk-OG8nFp0iDwlFoVSukyXtjuCgaN33UvzcKPK7N2qquzJOkt1ybWkFubIprt5-IawxeIQaH3MgOukDj4Pg7OhsOMCcRoGRrLayjrNRxFgTNSJzVR7drFJyAGZu42KWxR-3q2jh1hMbVupOsASkf00oMwAAFWCQIhIFD0kgLZbEeCQA0OUAwDAwQ98eAIEi4PigAjIGZzU+MoGZ7dkE0f9jlZPcD3Cc0RgAY2gpNDhTCwSgsU14NAGwbCdGDoNDB1glQqUvAAAzyAASABvK0VUSeAAF8iKw8DDzYGi5CbQlsNMUxcKCUACPTYQiKhCiqONGj6MY-dmNYkZ8XYuRhlg4soS4nj8LQoiahEw1qKsCS-CY8IZPheTOKRVTHV4-iDlAIjR2061xIYgypKMqwFLkqcoQ4xToOLUcLLDPiNOxBzdOUfTQEMlj3NknZTN84tiW4yz1MIwTOjCsS9OcqLXJi5QPPiryfKUuR+hSoLrOFWyADYsv8Ojcui4zPLXbyFLK0BasCvDgvS2yAHYGqcySIIK+AirYBKusG3qrI0gAOEacrG6TYpMkrOr8uRFvmtKBNsgBOFaIua-LWuK9rSp20Ajv2kRoGWKs2ysQzoQsaKACI8i+gdWje1zMU+-Kvu8v65yHd6akGkHxq+mGIde5R3pQYxvtgJHAIAUUWWAsDQBAj1QXAAGt1HIaBxh4e8lFIN9WVx-H+TJ9RhAG5Qr0cMU+OfaDSbQznrw1O5kiMHimFZvh2cO-UdONNpIDEC1RMagETCFxwLU1hx9DtSzLL8GjBcvYX9YNi2-EVsQNIoytIqZtcZPueJTT50nxWkMQQwti3gmtjSAGoiJ432Db8N3YH5tCXcteXGsj-m5FV4Zk-j05zbDrOsJoxOBY503uYMPxHYm30EisPO5CIij4Wios5Gist6LF4I+Ce6RQ+zg2247yAu79iolY0gAqEPu-D3mo-z2WHlV3P3bT61U9AVWM4Hiec8r92Ta53Xi9AUvnYeBfp+riiE3r4sm64lvzF756N4nh-O+fx7H7Dl-+9Sqf+Z0PI7RgnnI7D6xhS46DBl9ZIkMrAgK7OObAaBhBgLxmuCBNQvpyC+qOTBoAvqBygf9GscCxw5AWGIJQFhwEI1wdg2hI9CFbiGKabAp4vw61ZCwrAp42DY24PeXe145CmmgBgfcCBcDGgLnveUiprTSLNpqMWYD+FSMOjXSi8dTS0QoiIsRkAJGQB0ZoxyOUeIR1YfAMufFRHiPgJI8usQIzSGET7IKsdnEnD4fePQMDlBcNPDkYYORTg5HAChSxbAvp7HOOAWheCiHzgCfAIJpxwnGGSVE4YaT4lIyAUOZJORUngByBE7hVivojDSXgrBCSmFgEdoTRAX55A7k4HwXAz4FgMzJJIpAAhoDPhIFgIgwRnDwHgBgUZIB1CxAmCQUmJBGACBILAWIkiHDAHmYs4AihnqHhyDuXA3CiDOByB0rpr4+CslaZQfk3i1E2R1tmUumSmAPNqX9RJQ5blBIsLcqJUIWygAId8qwvzzjGABWDYFgdQDdFBX4+Avz0kKDaYC2F8LQAMLBcoX5AARf56KYV1ARdivJg5wVtJyNjIldzsGgG8liuFOK0hAA

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment