I'm very new to functional world. I've written a simple brainfuck interpreter as my first F# program.
What I would like to know:
- Am I using the right data structure for each situation?
- Is my code easy to understand?
- Is my code style similar to common F# codes (naming, organization etc)?
- Is my code "very declarative" or I'm still thinking in the imperative way?
What is not so relevant:
- Performance (but if I've made something very bad, let me now!)
- More features
Obs.: I assume input is always a valid brainfuck code and that's ok to me.
open System open System.IO let getValue (position:int) (memory:Map<int, int>) = match Map.tryFind position memory with | None -> 0 | value -> value.Value let calcAccumulator (acc:int) (instruction:char) (search:char) (miss:char) = match instruction with | x when x = search -> acc+1 | x when x = miss -> acc-1 | _ -> acc let rec findMatch (code:string) (search:char) (miss:char) (inc:int) (current:int) (acc:int) = let instruction = code.[current] match instruction, acc with | x, 0 when x = search -> current+1 | _ -> findMatch code search miss inc (current+inc) (calcAccumulator acc instruction search miss) let updateMemory (instruction:char) (position:int) (memory:Map<int, int>) = let oldValue = getValue position memory let newValue = match instruction with | '+' -> oldValue + 1 | '-' -> oldValue - 1 | ',' -> Console.ReadKey().KeyChar |> Convert.ToInt32 | _ -> oldValue Map.add position newValue memory let updateOutput (instruction) (value:int) = if instruction = '.' then Console.Write ((char) value) let updatePosition (instruction:char) (position:int) = match instruction with | '>' -> position+1 | '<' -> position-1 | _ -> position let updateIndex (code:string) (index:int) (value:int) = match code.[index], value with | '[', 0 -> findMatch code ']' '[' 1 (index+1) 0 | ']', x when x <> 0 -> findMatch code '[' ']' -1 (index-1) 0 | _ -> index+1 let rec interpretHelper (code:string) (index:int) (memory:Map<int, int>) (position:int) = match code.Length with | x when x = index -> memory | _ -> let instruction = code.[index] let newPosition = updatePosition instruction position let newMemory = updateMemory instruction position memory let newIndex = updateIndex code index (getValue position memory) updateOutput instruction (getValue position memory) interpretHelper code newIndex newMemory newPosition let interpret (code:string) = interpretHelper code 0 Map.empty 0 let options (args:string array) = let pathToFile = args. let showMemory = match args with | [| _ ; "-memory" |] -> true | _ -> false pathToFile, showMemory let onlyCode (text:string) = let validInstructions = "+-.,][<>".ToCharArray() String.filter (fun x -> Array.contains x validInstructions) text [<EntryPoint>] let main args = let pathToFile, showMemory = options args let code = onlyCode (IO.File.ReadAllText pathToFile) let memory = interpret code if showMemory then Map.iter (fun a b -> printf "\n%3d: %d" a b) memory 0