Exemplo n.º 1
0
        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="programCode"></param>
        /// <param name="input">Function to call when input command (,) is executed.</param>
        /// <param name="output">Function to call when output command (.) is executed.</param>
        /// <param name="function">Callback handler to notify that a function is being executed: callback(instruction).</param>
        /// <param name="options">Additional interpreter options.</param>
        public Interpreter(string programCode, Func <byte> input, Action <byte> output, Action <char> function = null, Function[] options = null)
        {
            // Save the program code
            this.m_Source = programCode.ToCharArray();

            // Store the i/o delegates
            this.m_Input  = input;
            this.m_Output = output;

            // Set any additional options.
            if (options != null)
            {
                this.m_Options = options;
            }

            m_CurrentCallStack = m_CallStack;

            // Create the instruction set for Basic Brainfuck.
            this.m_InstructionSet.Add('+', () => { if (!m_ExitLoop)
                                                   {
                                                       this.m_Memory[this.m_DataPointer]++;
                                                   }
                                      });
            this.m_InstructionSet.Add('-', () => { if (!m_ExitLoop)
                                                   {
                                                       this.m_Memory[this.m_DataPointer]--;
                                                   }
                                      });
            // add NOP instruction.
            this.m_InstructionSet.Add('N', () => { if (!m_ExitLoop)
                                                   {
                                                       ;
                                                   }
                                      });
            this.m_InstructionSet.Add('>', () => { if (!m_ExitLoop)
                                                   {
                                                       this.m_DataPointer++;
                                                   }
                                      });
            this.m_InstructionSet.Add('<', () => { if (!m_ExitLoop)
                                                   {
                                                       this.m_DataPointer--;
                                                   }
                                      });

            this.m_InstructionSet.Add('.', () => { if (!m_ExitLoop)
                                                   {
                                                       this.m_Output(this.m_Memory[this.m_DataPointer]);
                                                   }
                                      });

            // Prompt for input. If inside a function, pull input from parent memory, using the current FunctionInputPointer. Each call for input advances the parent memory cell that gets read from, allowing the passing of multiple values as input to a function.
            this.m_InstructionSet.Add(',', () => { if (!m_ExitLoop)
                                                   {
                                                       m_Memory[this.m_DataPointer] = IsInsideFunction ? this.m_Memory[this.m_FunctionInputPointer++] : this.m_Input();
                                                   }
                                      });

            this.m_InstructionSet.Add('[', () =>
            {
                if (!m_ExitLoop && this.m_Memory[this.m_DataPointer] == 0)
                {
                    // Jump forward to the matching ] and exit this loop (skip over all inner loops).
                    m_ExitLoop = true;

                    // Remember this instruction pointer, so when we get past all inner loops and finally pop this one off the stack, we know we're done.
                    m_ExitLoopInstructionPointer = this.m_InstructionPointer;
                }

                this.m_CurrentCallStack.Push(this.m_InstructionPointer);
            });
            this.m_InstructionSet.Add(']', () =>
            {
                var temp = this.m_CurrentCallStack.Pop();

                if (!m_ExitLoop)
                {
                    this.m_InstructionPointer = this.m_Memory[this.m_DataPointer] != 0
                        ? temp - 1
                        : this.m_InstructionPointer;
                }
                else
                {
                    // Continue executing after loop.
                    if (temp == m_ExitLoopInstructionPointer)
                    {
                        // We've finally exited the loop.
                        m_ExitLoop = false;
                        m_ExitLoopInstructionPointer = 0;
                    }
                }
            });

            // Create the instruction set for Brainfuck Extended Type 3.
            this.m_InstructionSet.Add('0', () => { if (!m_ExitLoop)
                                                   {
                                                       this.m_Memory[this.m_DataPointer] = 0;
                                                   }
                                      });
            this.m_InstructionSet.Add('1', () => { if (!m_ExitLoop)
                                                   {
                                                       this.m_Memory[this.m_DataPointer] = 16;
                                                   }
                                      });
            this.m_InstructionSet.Add('2', () => { if (!m_ExitLoop)
                                                   {
                                                       this.m_Memory[this.m_DataPointer] = 32;
                                                   }
                                      });
            this.m_InstructionSet.Add('3', () => { if (!m_ExitLoop)
                                                   {
                                                       this.m_Memory[this.m_DataPointer] = 48;
                                                   }
                                      });
            this.m_InstructionSet.Add('4', () => { if (!m_ExitLoop)
                                                   {
                                                       this.m_Memory[this.m_DataPointer] = 64;
                                                   }
                                      });
            this.m_InstructionSet.Add('5', () => { if (!m_ExitLoop)
                                                   {
                                                       this.m_Memory[this.m_DataPointer] = 80;
                                                   }
                                      });
            this.m_InstructionSet.Add('6', () => { if (!m_ExitLoop)
                                                   {
                                                       this.m_Memory[this.m_DataPointer] = 96;
                                                   }
                                      });
            this.m_InstructionSet.Add('7', () => { if (!m_ExitLoop)
                                                   {
                                                       this.m_Memory[this.m_DataPointer] = 112;
                                                   }
                                      });
            this.m_InstructionSet.Add('8', () => { if (!m_ExitLoop)
                                                   {
                                                       this.m_Memory[this.m_DataPointer] = 128;
                                                   }
                                      });
            this.m_InstructionSet.Add('9', () => { if (!m_ExitLoop)
                                                   {
                                                       this.m_Memory[this.m_DataPointer] = 144;
                                                   }
                                      });
            this.m_InstructionSet.Add('A', () => { if (!m_ExitLoop)
                                                   {
                                                       this.m_Memory[this.m_DataPointer] = 160;
                                                   }
                                      });
            this.m_InstructionSet.Add('B', () => { if (!m_ExitLoop)
                                                   {
                                                       this.m_Memory[this.m_DataPointer] = 176;
                                                   }
                                      });
            this.m_InstructionSet.Add('C', () => { if (!m_ExitLoop)
                                                   {
                                                       this.m_Memory[this.m_DataPointer] = 192;
                                                   }
                                      });
            this.m_InstructionSet.Add('D', () => { if (!m_ExitLoop)
                                                   {
                                                       this.m_Memory[this.m_DataPointer] = 208;
                                                   }
                                      });
            this.m_InstructionSet.Add('E', () => { if (!m_ExitLoop)
                                                   {
                                                       this.m_Memory[this.m_DataPointer] = 224;
                                                   }
                                      });
            this.m_InstructionSet.Add('F', () => { if (!m_ExitLoop)
                                                   {
                                                       this.m_Memory[this.m_DataPointer] = 240;
                                                   }
                                      });
            this.m_InstructionSet.Add('*', () => { if (!m_ExitLoop)
                                                   {
                                                       this.m_ReturnValue = this.m_Memory[this.m_DataPointer];
                                                   }
                                      });
            this.m_InstructionSet.Add('@', () =>
            {
                if (IsInsideFunction)
                {
                    // Exit function.
                    var temp = m_FunctionCallStack.Pop();

                    // Restore the data pointer.
                    this.m_DataPointer = temp.DataPointer;

                    /*if (this.m_ReturnValue.HasValue)
                     * {
                     *  this.m_Memory[this.m_DataPointer] = this.m_ReturnValue.Value;
                     * }*/

                    // Restore the call stack.
                    this.m_CurrentCallStack = temp.CallStack;
                    // Restore exit loop status.
                    this.m_ExitLoop = temp.ExitLoop;
                    // Restore exit loop instruction pointer.
                    this.m_ExitLoopInstructionPointer = temp.ExitLoopInstructionPointer;
                    // Restore ticks.
                    this.m_Ticks = temp.Ticks;
                    // Restore global storage.
                    this.m_Storage = this.m_ReturnValue.HasValue ? this.m_ReturnValue.Value : temp.Storage;
                    // Restore parent return value.
                    this.m_ReturnValue = temp.ReturnValue;
                    // Restore max iteraction count.
                    this.m_MaxIterationCount = temp.MaxIterationCount;
                    // Restore the instruction pointer.
                    this.m_InstructionPointer = temp.InstructionPointer;
                    // Restore function input pointer.
                    this.m_FunctionInputPointer = temp.FunctionInputPointer;
                }
                else
                {
                    // Exit program.
                    this.m_Stop = true;
                }
            });
            this.m_InstructionSet.Add('$', () =>
            {
                if (!m_ExitLoop)
                {
                    // If we're inside a function, use the function's own global storage (separate from the main program).
                    // However, if this is the last storage command in the function code, then use the main/calling-function storage, to allow returning a value.
                    if (IsInsideFunction && this.m_Source[m_InstructionPointer + 1] == '@')
                    {
                        // Set function return value.
                        this.m_ReturnValue = this.m_Memory[this.m_DataPointer];
                    }
                    else
                    {
                        // Set global storage for this main program or function.
                        this.m_Storage = this.m_Memory[this.m_DataPointer];
                    }
                }
            });

            this.m_InstructionSet.Add('!', () => { if (!m_ExitLoop)
                                                   {
                                                       this.m_Memory[this.m_DataPointer] = this.m_Storage;
                                                   }
                                      });

            // Scan code for function definitions and store their starting memory addresses.
            ScanFunctions(programCode);

            // If we found any functions, create the instruction set for them.
            for (char inst = 'a'; inst < m_NextFunctionCharacter; inst++)
            {
                char instruction = inst; // closure
                this.m_InstructionSet.Add(instruction, () =>
                {
                    if (!m_ExitLoop)
                    {
                        // Record a list of executed function names from the main program (not a function calling another function).
                        if (!IsInsideFunction)
                        {
                            if (m_ExecutedFunctions.ContainsKey(instruction))
                            {
                                m_ExecutedFunctions[instruction]++;
                            }
                            else
                            {
                                m_ExecutedFunctions.Add(instruction, 1);
                            }
                        }

                        if (function != null)
                        {
                            // Notify caller of a function being executed.
                            function(instruction);
                        }

                        // Store the current instruction pointer and data pointer before we move to the function.
                        var functionCallObj = new FunctionCallObj {
                            InstructionPointer = this.m_InstructionPointer, DataPointer = this.m_DataPointer, FunctionInputPointer = this.m_FunctionInputPointer, CallStack = this.m_CurrentCallStack, ExitLoop = this.m_ExitLoop, ExitLoopInstructionPointer = this.m_ExitLoopInstructionPointer, Ticks = this.m_Ticks, Instruction = instruction, Storage = this.m_Storage, ReturnValue = this.m_ReturnValue, MaxIterationCount = this.m_MaxIterationCount
                        };
                        this.m_FunctionCallStack.Push(functionCallObj);

                        // Give the function a fresh call stack.
                        this.m_CurrentCallStack           = new Stack <int>();
                        this.m_ExitLoop                   = false;
                        this.m_ExitLoopInstructionPointer = 0;

                        // Initialize the function global storage.
                        this.m_Storage     = 0;
                        this.m_ReturnValue = null;

                        // Load options for this function.
                        FunctionInst functionOptions = m_Functions[instruction];

                        // Set the function input pointer to the parent's starting memory. Calls for input (,) from within the function will read from parent's memory, each call advances the parent memory cell that gets read from. This allows passing multiple values to a function.
                        // Note, if we set the starting m_FunctionInputPointer to 0, functions will read from the first input position (0).
                        // If we set it to m_DataPointer, functions will read input from the current position in the parent memory (n). This is trickier for the GA to figure out, because it may have to downshift the memory back to 0 before calling the function so that the function gets all input. Setting this to 0 makes it easier for the function to get the input.
                        this.m_FunctionInputPointer = functionOptions.ReadInputAtMemoryStart ? 0 : this.m_DataPointer;

                        // Set the data pointer to the functions starting memory address.
                        this.m_DataPointer = _functionSize * (instruction - 96); // each function gets a space of 1000 memory slots.

                        // Clear function memory.
                        Array.Clear(this.m_Memory, this.m_DataPointer, _functionSize);

                        // Set ticks to 0.
                        this.m_Ticks = 0;

                        // Set the max iteration count for this function, if one was specified.
                        this.m_MaxIterationCount = functionOptions.MaxIterationCount > 0 ? functionOptions.MaxIterationCount : this.m_MaxIterationCount;

                        // Set the instruction pointer to the beginning of the function.
                        this.m_InstructionPointer = functionOptions.InstructionPointer;
                    }
                });
            }
        }
Exemplo n.º 2
0
        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="programCode"></param>
        /// <param name="input"></param>
        /// <param name="output"></param>
        public Interpreter(string programCode, Func <byte> input, Action <byte> output, Action <char> function = null)
        {
            // Save the program code
            this.m_Source = programCode.ToCharArray();

            // Store the i/o delegates
            this.m_Input  = input;
            this.m_Output = output;

            m_CurrentCallStack = m_CallStack;

            // Create the instruction set for Basic Bf.
            this.m_InstructionSet.Add('+', () => { if (!m_ExitLoop)
                                                   {
                                                       this.m_Memory[this.m_DataPointer]++;
                                                   }
                                      });
            this.m_InstructionSet.Add('-', () => { if (!m_ExitLoop)
                                                   {
                                                       this.m_Memory[this.m_DataPointer]--;
                                                   }
                                      });

            ////todo arithmetic overflow
            //this.m_InstructionSet.Add('+', () => { if (!m_ExitLoop && this.m_Memory[this.m_DataPointer] < 255) this.m_Memory[this.m_DataPointer]++; });
            //this.m_InstructionSet.Add('-', () => { if (!m_ExitLoop && this.m_Memory[this.m_DataPointer] > 0) this.m_Memory[this.m_DataPointer]--; });


            this.m_InstructionSet.Add('>', () => { if (!m_ExitLoop)
                                                   {
                                                       this.m_DataPointer++;
                                                   }
                                      });
            this.m_InstructionSet.Add('<', () => { if (!m_ExitLoop)
                                                   {
                                                       this.m_DataPointer--;
                                                   }
                                      });

            this.m_InstructionSet.Add('.', () => { if (!m_ExitLoop)
                                                   {
                                                       this.m_Output(this.m_Memory[this.m_DataPointer]);
                                                   }
                                      });

            // Prompt for input. If inside a function, pull input from parent memory, using the current FunctionInputPointer. Each call for input advances the parent memory cell that gets read from, allowing the passing of multiple values as input to a function.
            this.m_InstructionSet.Add(',', () => { if (!m_ExitLoop)
                                                   {
                                                       m_Memory[this.m_DataPointer] = m_FunctionCallStack.Count == 0 ? this.m_Input() : this.m_Memory[this.m_FunctionInputPointer++];
                                                   }
                                      });

            this.m_InstructionSet.Add('[', () =>
            {
                if (!m_ExitLoop && this.m_Memory[this.m_DataPointer] == 0)
                {
                    // Jump forward to the matching ] and exit this loop (skip over all inner loops).
                    m_ExitLoop = true;

                    // Remember this instruction pointer, so when we get past all inner loops and finally pop this one off the stack, we know we're done.
                    m_ExitLoopInstructionPointer = this.m_InstructionPointer;
                }

                this.m_CurrentCallStack.Push(this.m_InstructionPointer);
            });
            this.m_InstructionSet.Add(']', () =>
            {
                var temp = this.m_CurrentCallStack.Pop();

                if (!m_ExitLoop)
                {
                    this.m_InstructionPointer = this.m_Memory[this.m_DataPointer] != 0
                        ? temp - 1
                        : this.m_InstructionPointer;
                }
                else
                {
                    // Continue executing after loop.
                    if (temp == m_ExitLoopInstructionPointer)
                    {
                        // We've finally exited the loop.
                        m_ExitLoop = false;
                        m_ExitLoopInstructionPointer = 0;
                    }
                }
            });

            //// Create the instruction set for Bf Extended Type 3.
            //this.m_InstructionSet.Add('0', () => { if (!m_ExitLoop) this.m_Memory[this.m_DataPointer] = 0; });
            //this.m_InstructionSet.Add('1', () => { if (!m_ExitLoop) this.m_Memory[this.m_DataPointer] = 16; });
            //this.m_InstructionSet.Add('2', () => { if (!m_ExitLoop) this.m_Memory[this.m_DataPointer] = 32; });
            //this.m_InstructionSet.Add('3', () => { if (!m_ExitLoop) this.m_Memory[this.m_DataPointer] = 48; });
            //this.m_InstructionSet.Add('4', () => { if (!m_ExitLoop) this.m_Memory[this.m_DataPointer] = 64; });
            //this.m_InstructionSet.Add('5', () => { if (!m_ExitLoop) this.m_Memory[this.m_DataPointer] = 80; });
            //this.m_InstructionSet.Add('6', () => { if (!m_ExitLoop) this.m_Memory[this.m_DataPointer] = 96; });
            //this.m_InstructionSet.Add('7', () => { if (!m_ExitLoop) this.m_Memory[this.m_DataPointer] = 112; });
            //this.m_InstructionSet.Add('8', () => { if (!m_ExitLoop) this.m_Memory[this.m_DataPointer] = 128; });
            //this.m_InstructionSet.Add('9', () => { if (!m_ExitLoop) this.m_Memory[this.m_DataPointer] = 144; });
            //this.m_InstructionSet.Add('A', () => { if (!m_ExitLoop) this.m_Memory[this.m_DataPointer] = 160; });
            //this.m_InstructionSet.Add('B', () => { if (!m_ExitLoop) this.m_Memory[this.m_DataPointer] = 176; });
            //this.m_InstructionSet.Add('C', () => { if (!m_ExitLoop) this.m_Memory[this.m_DataPointer] = 192; });
            //this.m_InstructionSet.Add('D', () => { if (!m_ExitLoop) this.m_Memory[this.m_DataPointer] = 208; });
            //this.m_InstructionSet.Add('E', () => { if (!m_ExitLoop) this.m_Memory[this.m_DataPointer] = 224; });
            //this.m_InstructionSet.Add('F', () => { if (!m_ExitLoop) this.m_Memory[this.m_DataPointer] = 240; });
            //this.m_InstructionSet.Add('@', () =>
            //{
            //    if (m_FunctionCallStack.Count > 0)
            //    {
            //        // Exit function.
            //        var temp = m_FunctionCallStack.Pop();

            //        // Restore the data pointer.
            //        this.m_DataPointer = temp.DataPointer;
            //        // Restore the call stack.
            //        this.m_CurrentCallStack = temp.CallStack;
            //        // Restore exit loop status.
            //        this.m_ExitLoop = temp.ExitLoop;
            //        // Restore exit loop instruction pointer.
            //        this.m_ExitLoopInstructionPointer = temp.ExitLoopInstructionPointer;
            //        // Restore ticks.
            //        this.m_Ticks = temp.Ticks;
            //        // Restore the instruction pointer.
            //        this.m_InstructionPointer = temp.InstructionPointer;
            //        // Restore function input pointer.
            //        this.m_FunctionInputPointer = temp.FunctionInputPointer;
            //    }
            //    else
            //    {
            //        // Exit program.
            //        this.m_Stop = true;
            //    }
            //});
            //this.m_InstructionSet.Add('$', () => { if (!m_ExitLoop) this.m_Storage = this.m_Memory[this.m_DataPointer]; });
            //this.m_InstructionSet.Add('!', () => { if (!m_ExitLoop) this.m_Memory[this.m_DataPointer] = this.m_Storage; });

            // Scan code for function definitions and store their starting memory addresses.
            ScanFunctions(programCode);

            // If we found any functions, create the instruction set for them.
            for (char inst = 'a'; inst < m_NextFunctionCharacter; inst++)
            {
                char instruction = inst; // closure
                this.m_InstructionSet.Add(instruction, () =>
                {
                    if (!m_ExitLoop)
                    {
                        // Record a list of executed function names from the main program (not a function calling another function).
                        if (m_FunctionCallStack.Count == 0)
                        {
                            if (m_ExecutedFunctions.ContainsKey(instruction))
                            {
                                m_ExecutedFunctions[instruction]++;
                            }
                            else
                            {
                                m_ExecutedFunctions.Add(instruction, 1);
                            }
                        }

                        if (function != null)
                        {
                            function(instruction);
                        }

                        // Store the current instruction pointer and data pointer before we move to the function.
                        var functionCallObj = new FunctionCallObj {
                            InstructionPointer = this.m_InstructionPointer, DataPointer = this.m_DataPointer, FunctionInputPointer = this.m_FunctionInputPointer, CallStack = this.m_CurrentCallStack, ExitLoop = this.m_ExitLoop, ExitLoopInstructionPointer = this.m_ExitLoopInstructionPointer, Ticks = this.m_Ticks
                        };
                        this.m_FunctionCallStack.Push(functionCallObj);

                        // Give the function a fresh call stack.
                        this.m_CurrentCallStack           = new Stack <int>();
                        this.m_ExitLoop                   = false;
                        this.m_ExitLoopInstructionPointer = 0;

                        // Set the function input pointer to the parent's starting memory. Calls for input (,) from within the function will read from parent's memory, each call advances the parent memory cell that gets read from. This allows passing multiple values to a function.
                        this.m_FunctionInputPointer = this.m_DataPointer;

                        // Set the data pointer to the functions starting memory address.
                        this.m_DataPointer = _functionSize * (instruction - 96); // each function gets a space of 1000 memory slots.

                        // Clear function memory.
                        Array.Clear(this.m_Memory, this.m_DataPointer, _functionSize);

                        // Set the instruction pointer to the beginning of the function.
                        this.m_InstructionPointer = m_Functions[instruction];
                    }
                });
            }
        }
Exemplo n.º 3
0
        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="programCode"></param>
        /// <param name="input"></param>
        /// <param name="output"></param>
        public Interpreter(string programCode, Func<byte> input, Action<byte> output)
        {
            // Save the program code
            this.m_Source = programCode.ToCharArray();

            // Store the i/o delegates
            this.m_Input = input;
            this.m_Output = output;

            m_CurrentCallStack = m_CallStack;

            // Create the instruction set for Basic Brainfuck.
            this.m_InstructionSet.Add('+', () => { if (!m_ExitLoop) this.m_Memory[this.m_DataPointer]++; });
            this.m_InstructionSet.Add('-', () => { if (!m_ExitLoop) this.m_Memory[this.m_DataPointer]--; });

            this.m_InstructionSet.Add('>', () => { if (!m_ExitLoop) this.m_DataPointer++; });
            this.m_InstructionSet.Add('<', () => { if (!m_ExitLoop) this.m_DataPointer--; });

            this.m_InstructionSet.Add('.', () => { if (!m_ExitLoop) this.m_Output(this.m_Memory[this.m_DataPointer]); });

            // Prompt for input. If inside a function, pull input from parent memory, using the current FunctionInputPointer. Each call for input advances the parent memory cell that gets read from, allowing the passing of multiple values as input to a function.
            this.m_InstructionSet.Add(',', () => { if (!m_ExitLoop) this.m_Memory[this.m_DataPointer] = m_FunctionCallStack.Count == 0 ? this.m_Input() : this.m_Memory[this.m_FunctionInputPointer++]; });

            this.m_InstructionSet.Add('[', () =>
            {
                if (!m_ExitLoop && this.m_Memory[this.m_DataPointer] == 0)
                {
                    // Jump forward to the matching ] and exit this loop (skip over all inner loops).
                    m_ExitLoop = true;

                    // Remember this instruction pointer, so when we get past all inner loops and finally pop this one off the stack, we know we're done.
                    m_ExitLoopInstructionPointer = this.m_InstructionPointer;
                }

                this.m_CurrentCallStack.Push(this.m_InstructionPointer);
            });
            this.m_InstructionSet.Add(']', () =>
            {
                var temp = this.m_CurrentCallStack.Pop();

                if (!m_ExitLoop)
                {
                    this.m_InstructionPointer = this.m_Memory[this.m_DataPointer] != 0
                        ? temp - 1
                        : this.m_InstructionPointer;
                }
                else
                {
                    // Continue executing after loop.
                    if (temp == m_ExitLoopInstructionPointer)
                    {
                        // We've finally exited the loop.
                        m_ExitLoop = false;
                        m_ExitLoopInstructionPointer = 0;
                    }
                }
            });

            // Create the instruction set for Brainfuck Extended Type 3.
            this.m_InstructionSet.Add('0', () => { if (!m_ExitLoop) this.m_Memory[this.m_DataPointer] = 0; });
            this.m_InstructionSet.Add('1', () => { if (!m_ExitLoop) this.m_Memory[this.m_DataPointer] = 16; });
            this.m_InstructionSet.Add('2', () => { if (!m_ExitLoop) this.m_Memory[this.m_DataPointer] = 32; });
            this.m_InstructionSet.Add('3', () => { if (!m_ExitLoop) this.m_Memory[this.m_DataPointer] = 48; });
            this.m_InstructionSet.Add('4', () => { if (!m_ExitLoop) this.m_Memory[this.m_DataPointer] = 64; });
            this.m_InstructionSet.Add('5', () => { if (!m_ExitLoop) this.m_Memory[this.m_DataPointer] = 80; });
            this.m_InstructionSet.Add('6', () => { if (!m_ExitLoop) this.m_Memory[this.m_DataPointer] = 96; });
            this.m_InstructionSet.Add('7', () => { if (!m_ExitLoop) this.m_Memory[this.m_DataPointer] = 112; });
            this.m_InstructionSet.Add('8', () => { if (!m_ExitLoop) this.m_Memory[this.m_DataPointer] = 128; });
            this.m_InstructionSet.Add('9', () => { if (!m_ExitLoop) this.m_Memory[this.m_DataPointer] = 144; });
            this.m_InstructionSet.Add('A', () => { if (!m_ExitLoop) this.m_Memory[this.m_DataPointer] = 160; });
            this.m_InstructionSet.Add('B', () => { if (!m_ExitLoop) this.m_Memory[this.m_DataPointer] = 176; });
            this.m_InstructionSet.Add('C', () => { if (!m_ExitLoop) this.m_Memory[this.m_DataPointer] = 192; });
            this.m_InstructionSet.Add('D', () => { if (!m_ExitLoop) this.m_Memory[this.m_DataPointer] = 208; });
            this.m_InstructionSet.Add('E', () => { if (!m_ExitLoop) this.m_Memory[this.m_DataPointer] = 224; });
            this.m_InstructionSet.Add('F', () => { if (!m_ExitLoop) this.m_Memory[this.m_DataPointer] = 240; });
            this.m_InstructionSet.Add('@', () =>
            {
                if (m_FunctionCallStack.Count > 0)
                {
                    // Exit function.
                    var temp = m_FunctionCallStack.Pop();

                    // Restore the data pointer.
                    this.m_DataPointer = temp.DataPointer;
                    // Restore the call stack.
                    this.m_CurrentCallStack = temp.CallStack;
                    // Restore exit loop status.
                    this.m_ExitLoop = temp.ExitLoop;
                    // Restore exit loop instruction pointer.
                    this.m_ExitLoopInstructionPointer = temp.ExitLoopInstructionPointer;
                    // Restore ticks.
                    this.m_Ticks = temp.Ticks;
                    // Restore the instruction pointer.
                    this.m_InstructionPointer = temp.InstructionPointer;
                    // Restore function input pointer.
                    this.m_FunctionInputPointer = temp.FunctionInputPointer;
                }
                else
                {
                    // Exit program.
                    this.m_Stop = true;
                }
            });
            this.m_InstructionSet.Add('$', () => { if (!m_ExitLoop) this.m_Storage = this.m_Memory[this.m_DataPointer]; });
            this.m_InstructionSet.Add('!', () => { if (!m_ExitLoop) this.m_Memory[this.m_DataPointer] = this.m_Storage; });

            // Scan code for function definitions and store their starting memory addresses.
            ScanFunctions(programCode);

            // If we found any functions, create the instruction set for them.
            for (char inst = 'a'; inst < m_NextFunctionCharacter; inst++)
            {
                char instruction = inst; // closure
                this.m_InstructionSet.Add(instruction, () =>
                {
                    if (!m_ExitLoop)
                    {
                        // Record a list of executed function names from the main program (not a function calling another function).
                        if (m_FunctionCallStack.Count == 0)
                        {
                            m_ExecutedFunctions.Add(instruction);
                        }

                        // Store the current instruction pointer and data pointer before we move to the function.
                        var functionCallObj = new FunctionCallObj { InstructionPointer = this.m_InstructionPointer, DataPointer = this.m_DataPointer, FunctionInputPointer = this.m_FunctionInputPointer, CallStack = this.m_CurrentCallStack, ExitLoop = this.m_ExitLoop, ExitLoopInstructionPointer = this.m_ExitLoopInstructionPointer, Ticks = this.m_Ticks };
                        this.m_FunctionCallStack.Push(functionCallObj);

                        // Give the function a fresh call stack.
                        this.m_CurrentCallStack = new Stack<int>();
                        this.m_ExitLoop = false;
                        this.m_ExitLoopInstructionPointer = 0;

                        // Set the function input pointer to the parent's starting memory. Calls for input (,) from within the function will read from parent's memory, each call advances the parent memory cell that gets read from. This allows passing multiple values to a function.
                        this.m_FunctionInputPointer = this.m_DataPointer;

                        // Set the data pointer to the functions starting memory address.
                        this.m_DataPointer = _functionSize * (instruction - 96); // each function gets a space of 1000 memory slots.

                        // Clear function memory.
                        Array.Clear(this.m_Memory, this.m_DataPointer, _functionSize);

                        // Set the instruction pointer to the beginning of the function.
                        this.m_InstructionPointer = m_Functions[instruction];
                    }
                });
            }
        }