Created
November 9, 2023 01:01
-
-
Save Theaninova/af663e78bfaaf471267269be1428f3bd to your computer and use it in GitHub Desktop.
TSFuck - The Brainfuck interpreter written using TypeScript Types
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
// TSF*** | |
type IncrementPointer = '>' | |
type DecrementPointer = '<' | |
type IncrementByte = '+' | |
type DecrementByte = '-' | |
type Output = '.' | |
type Input = ',' | |
type LoopOpen = '[' | |
type LoopClose = ']' | |
type Token = IncrementPointer | DecrementPointer | IncrementByte | DecrementByte | Output | LoopOpen | LoopClose | |
type Terminal = null | |
type LoopOpenInstruction = `0${LoopOpen}${number}` | |
type LoopCloseInstruction = `1${LoopClose}${number}` | |
type Instruction = LoopOpenInstruction | LoopCloseInstruction | IncrementPointer | DecrementPointer | IncrementByte | DecrementByte | Output | Terminal | |
/* Virtual Machine */ | |
type Tape<T> = [T[], T, T[]] | |
// there are other ways to do this, but since brainfuck specifies exactly one byte per cell this seems like the best solution overall | |
type Inc = {0: 1; 1: 2; 2: 3; 3: 4; 4: 5; 5: 6; 6: 7; 7: 8; 8: 9; 9: 10; 10: 11; 11: 12; 12: 13; 13: 14; 14: 15; 15: 16; 16: 17; 17: 18; 18: 19; 19: 20; 20: 21; 21: 22; 22: 23; 23: 24; 24: 25; 25: 26; 26: 27; 27: 28; 28: 29; 29: 30; 30: 31; 31: 32; 32: 33; 33: 34; 34: 35; 35: 36; 36: 37; 37: 38; 38: 39; 39: 40; 40: 41; 41: 42; 42: 43; 43: 44; 44: 45; 45: 46; 46: 47; 47: 48; 48: 49; 49: 50; 50: 51; 51: 52; 52: 53; 53: 54; 54: 55; 55: 56; 56: 57; 57: 58; 58: 59; 59: 60; 60: 61; 61: 62; 62: 63; 63: 64; 64: 65; 65: 66; 66: 67; 67: 68; 68: 69; 69: 70; 70: 71; 71: 72; 72: 73; 73: 74; 74: 75; 75: 76; 76: 77; 77: 78; 78: 79; 79: 80; 80: 81; 81: 82; 82: 83; 83: 84; 84: 85; 85: 86; 86: 87; 87: 88; 88: 89; 89: 90; 90: 91; 91: 92; 92: 93; 93: 94; 94: 95; 95: 96; 96: 97; 97: 98; 98: 99; 99: 100; 100: 101; 101: 102; 102: 103; 103: 104; 104: 105; 105: 106; 106: 107; 107: 108; 108: 109; 109: 110; 110: 111; 111: 112; 112: 113; 113: 114; 114: 115; 115: 116; 116: 117; 117: 118; 118: 119; 119: 120; 120: 121; 121: 122; 122: 123; 123: 124; 124: 125; 125: 126; 126: 127; 127: 128; 128: 129; 129: 130; 130: 131; 131: 132; 132: 133; 133: 134; 134: 135; 135: 136; 136: 137; 137: 138; 138: 139; 139: 140; 140: 141; 141: 142; 142: 143; 143: 144; 144: 145; 145: 146; 146: 147; 147: 148; 148: 149; 149: 150; 150: 151; 151: 152; 152: 153; 153: 154; 154: 155; 155: 156; 156: 157; 157: 158; 158: 159; 159: 160; 160: 161; 161: 162; 162: 163; 163: 164; 164: 165; 165: 166; 166: 167; 167: 168; 168: 169; 169: 170; 170: 171; 171: 172; 172: 173; 173: 174; 174: 175; 175: 176; 176: 177; 177: 178; 178: 179; 179: 180; 180: 181; 181: 182; 182: 183; 183: 184; 184: 185; 185: 186; 186: 187; 187: 188; 188: 189; 189: 190; 190: 191; 191: 192; 192: 193; 193: 194; 194: 195; 195: 196; 196: 197; 197: 198; 198: 199; 199: 200; 200: 201; 201: 202; 202: 203; 203: 204; 204: 205; 205: 206; 206: 207; 207: 208; 208: 209; 209: 210; 210: 211; 211: 212; 212: 213; 213: 214; 214: 215; 215: 216; 216: 217; 217: 218; 218: 219; 219: 220; 220: 221; 221: 222; 222: 223; 223: 224; 224: 225; 225: 226; 226: 227; 227: 228; 228: 229; 229: 230; 230: 231; 231: 232; 232: 233; 233: 234; 234: 235; 235: 236; 236: 237; 237: 238; 238: 239; 239: 240; 240: 241; 241: 242; 242: 243; 243: 244; 244: 245; 245: 246; 246: 247; 247: 248; 248: 249; 249: 250; 250: 251; 251: 252; 252: 253; 253: 254; 254: 255; 255: 0} | |
type Dec = {0: 255; 1: 0; 2: 1; 3: 2; 4: 3; 5: 4; 6: 5; 7: 6; 8: 7; 9: 8; 10: 9; 11: 10; 12: 11; 13: 12; 14: 13; 15: 14; 16: 15; 17: 16; 18: 17; 19: 18; 20: 19; 21: 20; 22: 21; 23: 22; 24: 23; 25: 24; 26: 25; 27: 26; 28: 27; 29: 28; 30: 29; 31: 30; 32: 31; 33: 32; 34: 33; 35: 34; 36: 35; 37: 36; 38: 37; 39: 38; 40: 39; 41: 40; 42: 41; 43: 42; 44: 43; 45: 44; 46: 45; 47: 46; 48: 47; 49: 48; 50: 49; 51: 50; 52: 51; 53: 52; 54: 53; 55: 54; 56: 55; 57: 56; 58: 57; 59: 58; 60: 59; 61: 60; 62: 61; 63: 62; 64: 63; 65: 64; 66: 65; 67: 66; 68: 67; 69: 68; 70: 69; 71: 70; 72: 71; 73: 72; 74: 73; 75: 74; 76: 75; 77: 76; 78: 77; 79: 78; 80: 79; 81: 80; 82: 81; 83: 82; 84: 83; 85: 84; 86: 85; 87: 86; 88: 87; 89: 88; 90: 89; 91: 90; 92: 91; 93: 92; 94: 93; 95: 94; 96: 95; 97: 96; 98: 97; 99: 98; 100: 99; 101: 100; 102: 101; 103: 102; 104: 103; 105: 104; 106: 105; 107: 106; 108: 107; 109: 108; 110: 109; 111: 110; 112: 111; 113: 112; 114: 113; 115: 114; 116: 115; 117: 116; 118: 117; 119: 118; 120: 119; 121: 120; 122: 121; 123: 122; 124: 123; 125: 124; 126: 125; 127: 126; 128: 127; 129: 128; 130: 129; 131: 130; 132: 131; 133: 132; 134: 133; 135: 134; 136: 135; 137: 136; 138: 137; 139: 138; 140: 139; 141: 140; 142: 141; 143: 142; 144: 143; 145: 144; 146: 145; 147: 146; 148: 147; 149: 148; 150: 149; 151: 150; 152: 151; 153: 152; 154: 153; 155: 154; 156: 155; 157: 156; 158: 157; 159: 158; 160: 159; 161: 160; 162: 161; 163: 162; 164: 163; 165: 164; 166: 165; 167: 166; 168: 167; 169: 168; 170: 169; 171: 170; 172: 171; 173: 172; 174: 173; 175: 174; 176: 175; 177: 176; 178: 177; 179: 178; 180: 179; 181: 180; 182: 181; 183: 182; 184: 183; 185: 184; 186: 185; 187: 186; 188: 187; 189: 188; 190: 189; 191: 190; 192: 191; 193: 192; 194: 193; 195: 194; 196: 195; 197: 196; 198: 197; 199: 198; 200: 199; 201: 200; 202: 201; 203: 202; 204: 203; 205: 204; 206: 205; 207: 206; 208: 207; 209: 208; 210: 209; 211: 210; 212: 211; 213: 212; 214: 213; 215: 214; 216: 215; 217: 216; 218: 217; 219: 218; 220: 219; 221: 220; 222: 221; 223: 222; 224: 223; 225: 224; 226: 225; 227: 226; 228: 227; 229: 228; 230: 229; 231: 230; 232: 231; 233: 232; 234: 233; 235: 234; 236: 235; 237: 236; 238: 237; 239: 238; 240: 239; 241: 240; 242: 241; 243: 242; 244: 243; 245: 244; 246: 245; 247: 246; 248: 247; 249: 248; 250: 249; 251: 250; 252: 251; 253: 252; 254: 253; 255: 254} | |
type Byte = keyof Inc | |
type Zero = 0 | |
type MemoryTape = Tape<Byte> | |
type EmptyMemoryTape = [[], Zero, []] | |
type InstructionTape = Tape<Instruction> | |
type StdOut = number[] | |
type VirtualMachine = [MemoryTape, InstructionTape, StdOut] | |
/* Tape Movement */ | |
type MoveInstructionTapeLeft<TAPE extends InstructionTape> = MoveTapeLeft<Terminal, Instruction, TAPE> | |
type MoveMemoryTapeLeft<TAPE extends MemoryTape> = MoveTapeLeft<Zero, Byte, TAPE> | |
type MoveTapeLeft<EMPTY, V, TAPE extends Tape<V>> = TAPE[2] extends [infer HEAD extends V, ...infer TAIL] | |
? [[...TAPE[0], TAPE[1]], HEAD, TAIL extends V[] ? TAIL : []] | |
: [[...TAPE[0], TAPE[1]], EMPTY, []] | |
type MoveInstructionTapeRight<TAPE extends InstructionTape> = MoveTapeRight<Terminal, Instruction, TAPE> | |
type MoveMemoryTapeRight<TAPE extends MemoryTape> = MoveTapeRight<Zero, Byte, TAPE> | |
type MoveTapeRight<EMPTY, V, TAPE extends Tape<V>> = TAPE[0] extends [...infer HEAD, infer TAIL extends V] | |
? [HEAD extends V[] ? HEAD : [], TAIL, [TAPE[1], ...TAPE[2]]] | |
: [[], EMPTY, [TAPE[1], ...TAPE[2]]] | |
/* Eval Code */ | |
type ResolveJumpBack<VM extends VirtualMachine, JUMP extends LoopOpenInstruction> = | |
VM[1][1] extends JUMP ? Run<[VM[0], MoveInstructionTapeLeft<VM[1]>, VM[2]]> : ResolveJumpBack<[VM[0], MoveInstructionTapeRight<VM[1]>, VM[2]], JUMP> | |
type ResolveJumpForward<VM extends VirtualMachine, JUMP extends LoopCloseInstruction> = | |
VM[1][1] extends JUMP ? Run<[VM[0], MoveInstructionTapeLeft<VM[1]>, VM[2]]> : ResolveJumpForward<[VM[0], MoveInstructionTapeLeft<VM[1]>, VM[2]], JUMP> | |
type Run<VM extends VirtualMachine> = | |
VM[1][1] extends Terminal ? VM : | |
VM[1][1] extends IncrementByte ? Run<[[VM[0][0], Inc[VM[0][1]], VM[0][2]], MoveInstructionTapeLeft<VM[1]>, VM[2]]>: | |
VM[1][1] extends DecrementByte ? Run<[[VM[0][0], Dec[VM[0][1]], VM[0][2]], MoveInstructionTapeLeft<VM[1]>, VM[2]]> : | |
VM[1][1] extends IncrementPointer ? Run<[MoveMemoryTapeLeft<VM[0]>, MoveInstructionTapeLeft<VM[1]>, VM[2]]> : | |
VM[1][1] extends DecrementPointer ? Run<[MoveMemoryTapeRight<VM[0]>, MoveInstructionTapeLeft<VM[1]>, VM[2]]> : | |
VM[1][1] extends Output ? Run<[VM[0], MoveInstructionTapeLeft<VM[1]>, [...VM[2], VM[0][1]]]> : | |
VM[1][1] extends `${VM[0][1] extends 0 ? 0 : 1}${LoopOpen}${infer DEPTH extends number}` ? ResolveJumpForward<VM, `1${LoopClose}${DEPTH}`> : | |
VM[1][1] extends `${VM[0][1] extends 0 ? 0 : 1}${LoopClose}${infer DEPTH extends number}` ? ResolveJumpBack<VM, `0${LoopOpen}${DEPTH}`> : | |
Run<[VM[0], MoveInstructionTapeLeft<VM[1]>, VM[2]]> | |
/* Tokenizer */ | |
type GetSourceTokens<SOURCE extends string> = SOURCE extends `${infer TOKEN extends Token}` ? [TOKEN] : | |
SOURCE extends `${infer TOKEN extends Token}${infer REST extends string}` ? [TOKEN, ...GetSourceTokens<REST>] : never | |
/* Bracket Matching */ | |
type FormatToken<T extends Token, DEPTH extends Byte> = T extends LoopOpen ? `0${LoopOpen}${DEPTH}` : T extends LoopClose ? `1${LoopClose}${Dec[DEPTH]}` : T | |
type GetDepth<T extends Token, DEPTH extends Byte> = T extends LoopOpen ? Inc[DEPTH] : T extends LoopClose ? Dec[DEPTH] : DEPTH | |
type MatchParens<T extends Token[], DEPTH extends Byte = 0> = | |
T extends [infer TOKEN extends Token] ? [FormatToken<TOKEN, DEPTH>] : | |
T extends [infer TOKEN extends Token, ...infer REST extends Token[]] ? [FormatToken<TOKEN, DEPTH>, ...MatchParens<REST, GetDepth<TOKEN, DEPTH>>] : never | |
/* Parser */ | |
type TSFucompile<SOURCE extends string> = [EmptyMemoryTape, MoveInstructionTapeLeft<[[], Terminal, [...MatchParens<GetSourceTokens<SOURCE>>, Terminal]]>, []] | |
// unfortunately I haven't figured out how to circumvent recursion depth limits, so things like '-[-]' will fail | |
type Test = TSFucompile<'++>+++++[<+>-]++++++++[<++++++>-]<.'> | |
type Out = Run<Test> | |
type Result = Out[2] // [55] |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment