-
Notifications
You must be signed in to change notification settings - Fork 18
/
redos.js
executable file
·134 lines (106 loc) · 3.34 KB
/
redos.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
#!/usr/bin/env node
'use strict';
const fs = require('fs');
const chalk = require('chalk');
const esprima = require('esprima');
const safeReg = require('safe-regex');
function traverse(node, work) {
work(node);
for (let key in node) {
if (node.hasOwnProperty(key)) {
let child = node[key];
if (typeof child === 'object' && child !== null) {
if (Array.isArray(child))
child.forEach(node => traverse(node, work) );
else
traverse(child, work);
}
}
}
}
const parse = function(content, cb){
const regexNodes = new RegexNodes();
const ast = esprima.parse( content, {
sourceType: 'module',
loc: true
});
traverse( ast, function(node) {
if (node.regex && node.regex.pattern)
regexNodes.add( new Node(node) );
});
if ( typeof cb === 'function')
cb(regexNodes);
else
return regexNodes;
};
const utility = {
rPad: function(str, n, char, max){
return str + Array( Math.max(n - str.length, 0)).slice(0,max).join(' ');
},
lPad: function(str, n, char, max){
return Array( Math.max(n - str.length, 0)).slice(0,max).join(' ') + str;
},
trimShebang: function(text) {
return text.toString().replace( /^#!([^\r\n]+)/ , function(_, captured) {
return "/* #!"+ captured +' */';
});
}
};
const RegexNodes = function(){
const self = this;
const nodes = [];
this.get = i => nodes[i];
this.add = node => nodes.push(node);
this.results = () => nodes.map( n=>n.getData() );
// Not important, simply gets the max string lenght of the pattern for formatting output. not really used.
this.maxPatternLength = function(){
return nodes.reduce((max,n)=> Math.max(max,n.pattern().length), 0);
}
this.printAll = function(){
nodes.map(n=>console.log(n.format()) );
};
};
const Node = function(node){
// Since Nodes are static, there isn't too much of a point adding functions,
// but it does make formatting strings cleaner.
const safe = safeReg(node.regex.pattern);
const color = safe ? chalk.green: chalk.red;
const pattern = node.regex.pattern; // Redundant, but cleaner...
const colorPattern = () => color(pattern);
this.pattern = () => pattern;
this.getData = () =>({ safe, pattern, loc: node.loc });
this.formatLine = function(){
const start = node.loc.start;
const end = node.loc.end;
return 'Line[' + start.line +':'+ start.column +'->'+ end.line +':'+ end.column+']';
};
this.toString = function(){
return chalk.gray( this.formatLine() ) +' '+ colorPattern();
};
this.format = function(){
return chalk.gray( utility.lPad( this.formatLine(),34) ) +' '+ colorPattern();
}
};
// Do if running directly from CLI.
if (require.main === module) {
if (process.argv.length !== 3){
console.error('Redos CLI command requires a JavaScript filename as the only parameter.');
process.exit(1);
}
const regNodes = new RegexNodes();
const file = process.argv[2];
let content;
try{
content = utility.trimShebang( fs.readFileSync(file) );
}
catch(e){
console.error('Redos encountered an error when trying to read from file: '+file);
console.error(e);
process.exit(1);
}
parse(content, function(regNodes){
console.log( chalk.blue('Processing:'), chalk.white(file));
regNodes.printAll();
});
}
module.exports = parse;