Skip to content

Instantly share code, notes, and snippets.

@PotOfCoffee2Go
Last active March 15, 2024 08:52
Show Gist options
  • Save PotOfCoffee2Go/90396dbb8bd8670b4f39ca94ace48a20 to your computer and use it in GitHub Desktop.
Save PotOfCoffee2Go/90396dbb8bd8670b4f39ca94ace48a20 to your computer and use it in GitHub Desktop.
Javascript cellular automata RLE parser

RLE parser usage

See the RLE section for definition of RLE format.

This class expects the (extended) RLE input to be correctly formatted. It is a two state parser, thus multi-state RLE will not parse properly.

The output this.pattern is a string with spaces(dead)/zero(alive) with lines separated by '\n'.

As a convenience, the parse() function returns this.pattern.

Install:

npm i gist:90396dbb8bd8670b4f39ca94ace48a20 --save

The parse(rleFileText) function returns this.pattern containing spaces(dead) and zero(alive) in a newline separated string.

const RLE = require('RLE-parser');
const rle = new RLE;

rle.parse(`
#C Example of glider
x = 3, y = 3
3o$o$bo!
`);

console.log(rle.pattern);

Expected result:

000
0
 0
{
"name": "RLE-parser",
"version": "0.0.1",
"description": "Cellular automata (extended) RLE format parser",
"main": "rle.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": ["cellular automata", "RLE parser"],
"author": "PotOfCoffee2Go <https://github.com/PotOfCoffee2Go>",
"license": "ISC"
}
/**
* See the RLE section of
* http://psoup.math.wisc.edu/mcell/ca_files_formats.html#RLE
* for definition of RLE format.
*
* This class expects the (extended) RLE input to be
* correctly formatted. It is a two state parser, thus
* multi-state RLE will not parse properly.
*
* The output 'this.pattern' is a string with spaces(dead)/zero(alive)
* with lines separated by '\n'.
*
* The parse() function returns 'this.pattern'.
*
*/
class RLE {
constructor(text){ this.parse(text); }
/**
* Initalize the class to parse given text
*/
init(text) {
this.text = text || '#C Empty text';
this.pattern = ''; this.done = false;
this.lines = this.text.split(/\r\n|\r|\n/)
.map(l => ({ type: '', data: l.trim()}))
.filter(l => l.data.length > 0);
}
/**
* Three types of input lines are identified
* - comment - contains any line starting with '#'
* - header - contains the RLE x,y,rule values
* - pattern - are lines that contain the pattern to be parsed
*/
setTypes() {
this.lines.forEach(line => {
if (line.data[0] === '#') line.type = 'comment';
else if (/[xX]/.test(line.data[0])) line.type = 'header';
else if (/\d*[oObB]+[$!]+/.test(line.data)) line.type = 'pattern';
})
}
/**
* The operand is to be executed 'n' number of times
*/
execOp(op) {
for (let n = 0; n < op.n; n++) {
this.pattern += this.operation(op.o);
}
}
/**
* Return a space/zero/new-line depending on operand
*/
operation(operand) {
if (!this.done) {
switch (operand) {
case 'o': return '0';
case 'b': return ' ';
case '$': return '\n';
case '!': this.done = true; return '\n';
default: return ' ';
}
}
return '';
}
/**
* Main entry-point of the class
* Returns 'this.pattern' containing spaces(dead) or zero(alive)
* in a newline separated string
*/
parse(text) {
this.init(text);
this.setTypes();
let tokens = this.lines
.filter(l => l.type === 'pattern') // interested in the pattern
.map(l => l.data).join('').toLowerCase() // get the pattern data into one string
.replace(/[^\dob$!]/g,'b') // any unknown operand is 'b' (dead)
.match(/\d*[ob$!]/g); // build array of tokens
if (tokens) {
// insure each token has a number of iterations ( 1 is default )
tokens.forEach((op, idx) => { tokens[idx] = /\d/.test(op[0]) ? op : '1' + op });
// build and execute operations - number of iterations and operand
tokens.join('')
.match(/\d+[ob$!]/g)
.map(token => ({ n: parseInt(token), o: token.replace(/\d+/,'') }))
.forEach(op => this.execOp(op)); // execute the the operations
}
return this.pattern;
}
}
module.exports = RLE;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment