2
\$\begingroup\$

I'm creating a JavaScript app for a esoteric language, Marbelous. One of the features of this language are the devices. Each cell has a device, and each device has certain properties.

In order to represent this, I've created a massive object (or in my mind: HashMap) to store all of this data. This object stores static data about each object (even the functions are designed to be copied, not run on the original object).

Devices share a lot in common, so I've also created a default device. When a cell is created, I take an empty object, call $.extend() on the default device, and then call extend again on the actual device.

However, I'm not sure if this is the proper way to:

  1. Store lots of static data
  2. Create "data inheritance"

You can find my entire code here, but I've listed the key parts below. My newCell function looks like this:

function newCell(value){
if (value === undefined){
  value = '..';
}
var integerPart = "";
var device = {};
$.extend(device, empty_device);
if (devices[value]){
  $.extend(device, devices[value]);
} else if (devices[value.slice(0,1)]){
  $.extend(device, devices[value.slice(0,1)]);
  integerPart = parseInt(value.slice(1,2),36);
} else {
  $.extend(device, devices[""]);
  integerPart = parseInt(value, 36);
}
if (isNaN(integerPart)){
  throw new Error("Cannot parse cell with value:\""+value+"\"")
}
if (integerPart >= device.range){
  throw new Error("Number \""+integerPart+"\" out of range");
}
var cell = {
  value: integerPart,
  name: device.name,
  initFunction: device.initFunction,
  processFunction: device.processFunction,
  hasSideEffects: device.hasSideEffects,
  deterministic: device.deterministic
};
return cell;
}

And my data looks like this (There is lots of repetition in this code, so you can largely ignore much of it):

  var empty_device = {
    name: "empty",
    hasValue: false,
    range: base,
    deterministic: true,
    hasSideEffects: false,
    processFunction: function(marble){
      return {
        below: [marble]
      };
    },
    initFunction: function(){
      return {};
    }
  }
  var devices = {
    '..': {},
    '  ': {},
    '':{
      name: 'marble',
      hasValue: true,
      range: base*base,
      initFunction: function(marbles, data){
        return {
          below: [data.value]
        }
      }
    },
    '//':{
      name: 'left-deflector',
      processFunction: function(marble){
        return {
          left: marble
        }
      }
    },
    '\\\\':{
      name: 'right-deflector',
      processFunction: function(marble){
        return {
          right: marble
        }
      }
    },
    '@':{
      name: 'portal',
      hasValue: true,
      deterministic: false,
      hasSideEffects: undefined,
      initFunction: function(portals, data){
        this.hasSideEffects = portals.length > 1;
        return {};
      },
      processFunction: function(marble, portals, data){
        if (marble === undefined){
          return {
            below: [data]
          }
        }
        var randomPortal = portals[Math.floor(Math.random()*portals.length)];
        return {
          additionalProcessing: [
            {
              device: randomPortal,
              data: marble
            }
          ]
        }
      }
    },
    '&':{
      name: 'synchronizer',
      hasValue: true,
      deterministic: false,
      processFunction: function(marble, synchronizers){
        if (marble === undefined){
          var returnValue = this.filled;
          this.filled = undefined;
          return {
            below: [returnValue]
          }
        }
        if (this.filled !== undefined){
          this.filled = 0;
        }
        this.filled += marble;
        var allFilled = true;
        $.each(synchronizers, function(i, synchronizer){
          allFilled &= synchronizer.filled !== undefined;
        });
        if (!allFilled){
          return {};
        }
        this.filled = undefined;
        var returnValue =  {
          below: [marble],
          additionalProcessing: []
        };
        $.each(synchronizers, function(i, synchronizer){
          returnValue.additionalProcessing.push({
            device: synchronizer
          });
        });
        return returnValue;
      }
    },
    '=':{
      name: 'equals',
      hasValue: true,
      processFunction: function(marble, devices, data){
        if (marble === data.value){
          return {
            below: [marble]
          }
        } else {
          return {
            right: [marble]
          }
        }
      }
    },
    '>':{
      name: 'greater-than',
      hasValue: true,
      processFunction: function(marble, devices, data){
        if (marble > data.value){
            return {
              below: [marble]
            }
        } else {
          return {
            right: [marble]
          }
        }
      }
    },
    '<':{
      name: 'less-than',
      hasValue: true,
      processFunction: function(marble, devices, data){
        if (marble < data.value){
            return {
              below: [marble]
            }
        } else {
          return {
            right: [marble]
          }
        }
      }
    },
    '+':{
      name: 'add',
      hasValue: true,
      processFunction: function(marble, devices, data){
        return {
          below: [marble+data.value]
        }
      }
    },
    '-':{
      name: 'subtract',
      hasValue: true,
      processFunction: function(marble, devices, data){
        return {
          below: [marble-data.value]
        }
      }
    },
    '++':{
      name: 'increment',
      hasValue: true,
      processFunction: function(marble, devices, data){
        return {
          below: [marble+1]
        }
      }
    },
    '--':{
      name: 'decrement',
      hasValue: true,
      processFunction: function(marble, devices, data){
        return {
          below: [marble-1]
        }
      }
    },
    '^':{
      name: 'bit-check',
      hasValue: true,
      range: 8,
      processFunction: function(marble, devices, data){
        return {
          below: [marble & (1 << data.value)]
        }
      }
    },
    '<<':{
      name: 'bit-shift-left',
      hasValue: false,
      processFunction: function(marble){
        return {
          below: [marble << 1]
        }
      }
    },
    '>>':{
      name: 'bit-shift-right',
      hasValue: false,
      processFunction: function(marble){
        return {
          below: [marble >> 1]
        }
      }
    },
    '~~':{
      name: 'binary-not',
      hasValue: false,
      processFunction: function(marble){
        return {
          below: [~marble]
        }
      }
    },
    ']]':{
      name: 'stdin',
      hasValue: false,
      deterministic: false,
      hasSideEffects: true,
      processFunction: function(marble){
        var nextInput = readSTDIN();
        if (nextInput === undefined){
          return {
            right: [marble]
          }
        } else {
          return {
            below: [nextInput]
          }
        }
      }
    },
    '[[':{
      name: 'stdout',
      hasValue: false,
      deterministic: false,
      hasSideEffects: true,
      processFunction: function(marble){
        writeSTDOUT(marble);
        return {};
      }
    },
    '{':{
      name: 'input',
      hasValue: true,
      initFunction: function(devices, data){
        return {
          below: [data.boardInput[data.value]]
        }
      }
    },
    '}':{
      name: 'output',
      hasValue: true,
      processFunction: function(marble){
        var boardOutput = {};
        boardOutput[data.value] = marble;
        return {
          boardOutput: boardOutput
        };
      }
    },
    '{<':{
      name: 'left-output',
      processFunction: function(marble){
        return {
          boardOutput: {
            left: marble
          }
        }
      }
    },
    '{>':{
      name: 'right-output',
      processFunction: function(marble){
        return {
          boardOutput: {
            right: marble
          }
        }
      }
    },
    '\\/':{
      name: 'trash',
      processFunction: function(){
        return {};
      }
    },
    '/\\':{
      name: 'cloner',
      processFunction: function(marble){
        return {
          left: marble,
          right: marble
        }
      }
    },
    '!!':{
      name: 'terminator',
      processFunction: function(){
        return {
          finish: true,
        }
      }
    },
    '?':{
      name: 'random-range',
      hasValue: true,
      deterministic: false,
      hasSideEffects: true,
      processFunction: function(marble, outputs, data){
        return {
          below: Math.floor(Math.random()*base)
        }
      }
    },
    '??':{
      name: 'random',
      processFunction: function(marble, outputs, data){
        return {
          below: Math.floor(Math.random()*marble)
        }
      }
    },
  };

I don't want to use a database, as my data includes functions and because my data is static.

Is this the right way to do this, or what can I improve (in terms of code structure)?

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.