Created
August 11, 2011 17:31
-
-
Save markusl/1140246 to your computer and use it in GitHub Desktop.
Brainfuck interpreter written in F#
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
// FSharp (F#) interpreter for brainfuck programming language | |
// Read more from | |
// - http://www.muppetlabs.com/~breadbox/bf/ | |
// - http://fsharp.net | |
// (c) 2011 Markus Lindqvist | |
module brainfuck | |
open System | |
let brainfuckMemorySize = 30000 | |
/// Represents the brainfuck program we are executing | |
type BrainFuck = { | |
/// Contains the complete programe we are executing | |
bfcode : string; | |
/// Contains the current instruction we are executing | |
bfcodelocation : int; | |
/// Contains the program's memory, usually of size 30 000 | |
memory : int array; | |
/// Contains the brainfuck memory pointer | |
memorypointer : int; | |
} | |
/// Construct initial brainfuck state with zero memory and instruction and memory pointers zeroed | |
with static member Initial code = | |
{ new BrainFuck | |
with bfcode = code | |
and bfcodelocation = 0 | |
and memory = Array.zeroCreate(brainfuckMemorySize) | |
and memorypointer = 0 } | |
/// Keep the pointer location within allowed are | |
let wrapPointer pointerlocation = | |
if(pointerlocation < 0) then brainfuckMemorySize-1 | |
else if (pointerlocation = brainfuckMemorySize) then 0 | |
else pointerlocation | |
/// Get next instruction relative to current location | |
let getInstruction bf nth = | |
{ bf with bfcodelocation = bf.bfcodelocation + nth } | |
let nextInstruction bf = getInstruction bf (+1) | |
let prevInstruction bf = getInstruction bf (-1) | |
/// Modify memory at current memory pointer and move to next instruction | |
let modifyMemoryAtPointer bf op = | |
let mem = bf.memory | |
mem.[bf.memorypointer] <- mem.[bf.memorypointer] + op | |
{ nextInstruction bf with memory = mem } | |
/// Modify memory pointer and move to next instruction | |
let modifyMemoryPointer bf op = | |
{nextInstruction bf with memorypointer = wrapPointer (bf.memorypointer + op) } | |
let findLoopMatch bf (char1:char) (char2:char) jumpInstruction = | |
let rec findLoopMatchRec bf nestedLoops = | |
let currentInstruction = bf.bfcode.Chars(bf.bfcodelocation) | |
if (currentInstruction = char1 && nestedLoops = 1) then bf | |
else match currentInstruction with | |
| b when b = char1 -> findLoopMatchRec (jumpInstruction bf) (nestedLoops-1) | |
| a when a = char2 -> findLoopMatchRec (jumpInstruction bf) (nestedLoops+1) | |
| _ -> findLoopMatchRec (jumpInstruction bf) (nestedLoops) | |
findLoopMatchRec bf 0 | |
/// Jump past current loop | |
let endLoop bf = findLoopMatch bf ']' '[' nextInstruction | |
///// Start current loop again | |
let startLoop bf = findLoopMatch bf '[' ']' prevInstruction | |
/// Build a map of brainfuck instructions | |
let brainFuckInstructions = | |
['>', (fun bf -> modifyMemoryPointer bf (+1)); | |
'<', (fun bf -> modifyMemoryPointer bf (-1)); | |
'+', (fun bf -> modifyMemoryAtPointer bf (+1)); | |
'-', (fun bf -> modifyMemoryAtPointer bf (-1)); | |
'[', (fun bf -> if (bf.memory.[bf.memorypointer] = 0) then (endLoop bf) else (nextInstruction bf)); | |
']', (fun bf -> if (bf.memory.[bf.memorypointer] <> 0) then (startLoop bf) else (nextInstruction bf)); | |
'.', (fun bf -> Console.Write(Convert.ToChar(bf.memory.[bf.memorypointer]));(nextInstruction bf)); | |
',', (fun bf -> bf.memory.[bf.memorypointer] <- Console.Read();(nextInstruction bf))] |> Map.ofList | |
/// Execute a brainfuck program | |
let processInstructions (bf:BrainFuck) = | |
let rec processInstructionsRec (bf:BrainFuck) = | |
// Check that we have some code left | |
if(bf.bfcodelocation < bf.bfcode.Length) then | |
// Look up from instruction table | |
if(brainFuckInstructions.ContainsKey(bf.bfcode.Chars(bf.bfcodelocation))) then | |
// Execute the given instruction | |
let instruction = brainFuckInstructions.Item(bf.bfcode.Chars(bf.bfcodelocation)) | |
processInstructionsRec (instruction bf) | |
else // Just move to next instruction if we have unknown character | |
processInstructionsRec (nextInstruction bf) | |
// Start recursively executing instructions | |
processInstructionsRec bf | |
let executeBrainFuck (code:string) = | |
let bf = BrainFuck.Initial code | |
processInstructions bf | |
// Prints: 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89 | |
let bfcode = "+++++++++++>+>>>>++++++++++++++++++++++++++++++++++++++++++++>++++++++++++++++++++++++++++++++<<<<<<[>[>>>>>>+>+<<<<<<<-]>>>>>>>[<<<<<<<+>>>>>>>-]<[>++++++++++[-<-[>>+>+<<<-]>>>[<<<+>>>-]+<[>[-]<[-]]>[<<[>>>+<<<-]>>[-]]<<]>>>[>>+>+<<<-]>>>[<<<+>>>-]+<[>[-]<[-]]>[<<+>>[-]]<<<<<<<]>>>>>[++++++++++++++++++++++++++++++++++++++++++++++++.[-]]++++++++++<[->-<]>++++++++++++++++++++++++++++++++++++++++++++++++.[-]<<<<<<<<<<<<[>>>+>+<<<<-]>>>>[<<<<+>>>>-]<-[>>.>.<<<[-]]<<[>>+>+<<<-]>>>[<<<+>>>-]<<[<+>-]>[<+>-]<<<-]" | |
// Prints: Hello World! | |
//let bfcode = " ++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.--------.>+.>." | |
executeBrainFuck bfcode | |
Console.ReadKey() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment