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:
- Store lots of static data
- 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)?