-
Notifications
You must be signed in to change notification settings - Fork 0
/
rle-parser.js
138 lines (116 loc) · 3.6 KB
/
rle-parser.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
// RLE grammar: https://gist.github.com/thomasdunn/d70be5cc43a2b29ae93ffb07e7e9c067
export class RleParser {
isComment(line) {
// if it starts with #, its header comment
return line.match(/^#/) !== null;
}
isHeader(line) {
// if it starts with x = or n =, its header
return line.match(/^x\s*=/) !== null;
}
parseHeader(line) {
let width;
let height;
let rule;
// _________________________ x = 3 , y = 3 , rule = B3/S23
const match = line.match(/^\s*x\s*=\s*(\d+)\s*,\s*y\s*=\s*(\d+)(?:\s*,\s*rule\s*=\s*(.*))?$/m);
if (match !== null) {
width = parseInt(match[1], 10);
height = parseInt(match[2], 10);
rule = match[3];
}
return {width, height, rule};
}
parseTextComments(lines) {
const commentLines = lines.filter(line => this.isComment(line), this);
const textCommentLines = [];
commentLines.forEach(line => {
const match = line.match(/^#[Cc]\s*(.*)$/);
if (match !== null) {
textCommentLines.push(match[1]);
}
});
return textCommentLines;
}
getPattern(lines) {
const patternLines = lines
.filter(line => ! this.isComment(line))
.filter(line => ! this.isHeader(line));
// join multiple lines into one, strip out whitespaces, including newlines
let patternLine = patternLines.join('').replace(/\s/g, '');
let endIndicatorIndex = patternLine.indexOf('!');
if (endIndicatorIndex !== -1) {
patternLine = patternLine.substr(0, endIndicatorIndex);
}
return patternLine;
}
parse(contents) {
let pattern;
const name = contents;
const creator = contents;
let comments = [];
// split on newline and filter out empty lines
const lines = contents.split(/\n/g).filter(line => line.length > 0);
pattern = this.getPattern(lines);
comments = this.parseTextComments(lines);
const headerLine = lines.find(line => this.isHeader(line));
const {width, height, rule} = this.parseHeader(headerLine);
return {
pattern: pattern,
rule,
width,
height,
name,
creator,
comments
}
}
toOnCells(pattern) {
const onCells = [];
let x = 0;
let y = 0;
const on = (num=1) => {
for (let i = 0; i < num; i++) {
onCells.push([x++, y]);
}
};
const off = (num=1) => x += num;
const nextLine = (num=1) => {
y += num;
x = 0;
};
const nextItemRegExp = /^(\d*)?([^\d])/;
let num;
let chr;
let match = pattern.match(nextItemRegExp);
while (match !== null) {
num = parseInt(match[1] || 1, 10);
chr = match[2];
if (chr === '$') {
nextLine(num);
}
else if (this.isActive(chr)) {
on(num);
}
else {
off(num);
}
pattern = pattern.substring(match[0].length);
if (pattern === null) {
break;
}
match = pattern.match(nextItemRegExp);
}
return onCells;
}
isActive(chr) {
switch (chr) {
case 'b':
case 'B':
case '.':
return false;
default:
return true;
}
}
}