Skip to content

Instantly share code, notes, and snippets.

@Theaninova
Created November 9, 2023 01:01
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 Theaninova/af663e78bfaaf471267269be1428f3bd to your computer and use it in GitHub Desktop.
Save Theaninova/af663e78bfaaf471267269be1428f3bd to your computer and use it in GitHub Desktop.
TSFuck - The Brainfuck interpreter written using TypeScript Types
// 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