JavaScript Brainfuck Compiler

I successfully created a BrainFuck compiler in JavaScript, which functions perfectly with this input:

++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++.

Expected output: Hello World!

However, when I use the following nested loop input, my code gets stuck in an endless loop:

+[-[<<[+[--->]-[<<<]]]>>>-]>-.---.>..>.<<<<-.<+.>>>>>.>.<<.<-.

Expected output: hello world

Upon examining the code execution step by step, it seems that the values being written to the BF memory continuously decrease and more memory cells are involved in the process. Despite not finding any apparent mistake in the execution of each statement, I am unable to determine how the loop is supposed to terminate. It appears as though the program have no other option but to loop indefinitely since the values involved will never reach zero.

Surprisingly, when I run it on the tutorialspoint Online Brainfuck Compiler, the output generated is "hello world"!

Where could my error lie?

Provided below is the code snippet for reference:

class BrainfuckInterpreter {
    constructor() {
      this.memory = new Array(30000).fill(0); // Brainfuck memory tape
      this.pointer = 0; // Memory pointer
      this.inputBuffer = ''; // Input buffer for ,
      this.outputBuffer = ''; // Output buffer for .
      this.loopStack = []; // Stack to keep track of loop positions
    }
  
    interpret(code, input) {
      this.inputBuffer = input;
      this.outputBuffer = '';
      this.pointer = 0;
      this.loopStack = [];
  
      for (let i = 0; i < code.length; i++) {
        const command = code.charAt(i);
  
        switch (command) {
          case '>':
            this.pointer++;
            break;
  
          case '<':
            this.pointer--;
            break;
  
          case '+':
            this.memory[this.pointer]++;
            break;
  
          case '-':
            this.memory[this.pointer]--;
            break;
  
          case '.':
            this.outputBuffer += String.fromCharCode(this.memory[this.pointer]);
            break;
  
          case ',':
            if (this.inputBuffer.length > 0) {
              this.memory[this.pointer] = this.inputBuffer.charCodeAt(0);
              this.inputBuffer = this.inputBuffer.slice(1);
            } else {
              this.memory[this.pointer] = 0;
            }
            break;
  
            case '[':
              if (this.memory[this.pointer] === 0) {
                  let depth = 1;
                  while (depth > 0) {
                      i++;
                      if (code.charAt(i) === '[') depth++;
                      if (code.charAt(i) === ']') depth--;
                  }
              } else {
                  this.loopStack.push(i);
              }
              break;
          
          case ']':
              if (this.memory[this.pointer] !== 0) {
                  i = this.loopStack[this.loopStack.length - 1];
              } else {
                  this.loopStack.pop();
              }
              break;
              
            }
      }
  
      return this.outputBuffer;
    }
  }
  function Compile()
  {
    // Example usage:
    let code = '+[-[<<[+[--->]-[<<<]]]>>>-]>-.---.>..>.<<<<-.<+.>>>>>.>.<<.<-.';
    let input = '';
    const interpreter = new BrainfuckInterpreter();
    let output = interpreter.interpret(code, input);
    // Display the output
    document.getElementById('output_field').innerText = this.output;
    console.log(output);
  }

Answer №1

The issue arises when a memory cell is allowed to have a negative value in the BF memory. Since BF memory is meant to consist of bytes, values outside the 0 to 255 range should not be permitted. Instead, modular arithmetic should be applied.

To address this problem, modify the execution of the - and + commands as shown below:

            case '+':
                this.memory[this.pointer] = (this.memory[this.pointer] + 1) % 256;
                break;
      
            case '-':
                this.memory[this.pointer] = (this.memory[this.pointer] + 255) % 256;
                break;

Revised code snippet:

class BrainfuckInterpreter {
    constructor() {
        this.memory = new Array(30000).fill(0); // Brainfuck memory tape
        this.pointer = 0; // Memory pointer
        this.inputBuffer = ''; // Input buffer for ,
        this.outputBuffer = ''; // Output buffer for .
        this.loopStack = []; // Stack to keep track of loop positions
    }
  
    interpret(code, input) {
        this.inputBuffer = input;
        this.outputBuffer = '';
        this.pointer = 0;
        this.loopStack = [];

        for (let i = 0; i < code.length; i++) {
            const command = code.charAt(i);
      
            switch (command) {
            case '>':
                this.pointer++;
                break;
      
            case '<':
                this.pointer--;
                break;
      
            case '+':
                this.memory[this.pointer] = (this.memory[this.pointer] + 1) % 256;
                break;
      
            case '-':
                this.memory[this.pointer] = (this.memory[this.pointer] + 255) % 256;
                break;
      
            case '.':
                this.outputBuffer += String.fromCharCode(this.memory[this.pointer]);
                break;
      
            case ',':
                if (this.inputBuffer.length > 0) {
                    this.memory[this.pointer] = this.inputBuffer.charCodeAt(0);
                    this.inputBuffer = this.inputBuffer.slice(1);
                } else {
                    this.memory[this.pointer] = 0;
                }
                break;
      
            case '[':
                if (this.memory[this.pointer] === 0) {
                    let depth = 1;
                    while (depth > 0) {
                        i++;
                        if (code.charAt(i) === '[') depth++;
                        if (code.charAt(i) === ']') depth--;
                    }
                } else {
                    this.loopStack.push(i);
                }
                break;
              
            case ']':
                if (this.memory[this.pointer] !== 0) {
                    i = this.loopStack[this.loopStack.length - 1];
                } else {
                    this.loopStack.pop();
                }
                break;
                  
            }
        }
      
        return this.outputBuffer;
    }
}

function Compile()
{
    // Example usage:
    let code = '+[-[<<[+[--->]-[<<<]]]>>>-]>-.---.>..>.<<<<-.<+.>>>>>.>.<<.<-.';
    let input = '';
    const interpreter = new BrainfuckInterpreter();
    let output = interpreter.interpret(code, input);
    // Display the output
    //document.getElementById('output_field').innerText = this.output;
    console.log(output);
}

Compile();

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Validating the length of form inputs and selected items from a dropdown menu

I have the following form and I am attempting to validate its states as follows: themeName is required and should have a minimum of 4 characters. themeLangFrom selected value cannot be the same as themeLangTo (and vice versa). I want to display a span er ...

Discover the best way to highlight a specific area using imgareaelect

The code snippet provided is for uploading an image and performing some operations on it using jQuery UI Tooltip. The code includes various scripts and stylesheets to handle the image upload functionality. After uploading an image, the code checks if the ...

How can I stop popup labels from appearing in MapBox GL JS when I don't want them to be visible?

Having created an application using MapBox GL JS, I have placed numerous markers all around the globe. As the mouse hovers over these markers, a description box pops up, which is what I intended. However, I am encountering an issue where these labels flick ...

Retrieving Django view information using AJAX

Currently, I am in the process of developing an application that updates the main page's data via AJAX. After reading through a helpful response on this Stackoverflow post, I implemented AJAX to refresh my page. In addition to this, I have a specific ...

Issue encountered while attempting to include a background image in React JS

I encountered an issue when attempting to include a background image in react js. ./src/index.css (./node_modules/css-loader/dist/cjs.js??ref--5-oneOf-4-1!./node_modules/postcss-loader/src??postcss!./src/index.css) Unable to locate module: An attem ...

Conflicting Transformation Properties Causing CSS Issues Within a Single Element

I'm currently working on a user interface where users can drag and drop a box with a red outline to position it on the screen within a black box. See the UI here Alternatively, users can also move the box by adjusting the inputs on the right side. ...

What are the steps to utilize the `<script setup>` feature?

Vue version: 3.0.11 Here is the code snippet: <template> <button @click="inc">{{ count }}</button> </template> <script setup> import { ref } from 'vue' export const count = ref(0) export const i ...

Transfer the information from dropped files in a div to a separate page

document.getElementById('drag_drop').ondrop = function(event) { event.preventDefault(); var form_data = new FormData(); var drop_files = event.dataTransfer.files; for(var count = 0; count < drop_files.length; count++) ...

What is the reason why createServer() is often not recognized as a function?

After installing express globally and npm on my express app, I am encountering issues with both intellisence and the app itself (I am using visual studio code on mac OS Yosemite). Below is a snippet of the code: /// <reference path="typings/node/node. ...

removing the mapStateToProps function will result in an undefined value

I am new to React and I'm in the process of converting a class component to functional components using hooks. I need some guidance on safely removing 'mapStateToProps' without encountering undefined errors. I have two pages, A.js and B.js. ...

Potential Scope Problem in Angular JS Controller

The HTML code snippet I have is as follows: <body ng-controller = "Control as control"> <button ng-click = "control.prepareAction()">Do Action </button> <div id="one" ng-hide = "!control.showOne" > <div> <h6> ...

Leveraging the power of ReactJS for efficiency in executing multiple API calls concurrently

I'm encountering an issue with the following code snippet: let tmpContributors = [...this.state.contributors]; for (let i = 0; i < 10; i++) {//10 most active contributors due to performance and github limits contributorPropertiesPromises.pus ...

Activate the Masterpage menu to emphasize its current state

I am currently utilizing the AdminLTE template on my website. One issue I have encountered is with the menu and its child menus. When redirecting to different pages using image buttons, the menu collapses back to its original state. While navigating throu ...

Error message: When the mouse hovers over, display the chart(.js) results in TypeError: t is

I encountered a persistent error that I just can't seem to resolve. My goal is to showcase a chart using Chart.js when the mouse hovers over the canvas. However, upon hovering over the canvas, I keep getting a TypeError: t is null error. Error: { ...

Exploring search filters using KnockoutJS

I'm currently working on incorporating a search filter into my web application. After reading some informative articles and exploring various Jsfiddles, I've attempted to enable searching by TypeName to display the corresponding row with that spe ...

Using Vue Router's `push()` method within an asynchronous function will solely modify the URL address

I created a custom loading component that displays while the route is being changed. To ensure the loading component properly waits for a second, the method needs to be asynchronous. However, since implementing this feature, I noticed that the router.push ...

Transmitting PHP variable to JavaScript using Callback Method

I am looking to implement password validation using JavaScript along with an Ajax function. Upon successful validation, I aim to return a boolean variable (true or false) and perform specific actions in my PHP file based on the callback. Unfortunately, my ...

Dividing the JSON dataset into smaller segments

Here is an example demonstrating how to split the JSON data: var data = [ { BankName: 'SBI', IFSC: 'SBIN0002688' }, { BankName: 'ICICI', IFSC: 'ICIC0003931', MICR: '500229094'}, { BankName: 'RBI ...

Utilizing the no-data-text attribute in v-combobox with Vuetify: Tips and tricks

My application includes a simple combobox, and I am trying to set a default text message to display when there are no entries in the items props. To achieve this goal, I utilized the no-data-text prop by passing a specific variable to it. <v-combobox ...

Creating a customized tooltip without the need for calling $(document).foundation() and using modernizr

I'm facing an issue with initializing the foundation tool-tip without having to initialize everything (such as $(document).foundation()). It seems like a simple task, but I am struggling. I have two questions in mind: (1) How can I utilize new Founda ...