function detect(s, nums = [1, 2, 3, 4, 5]) {
const REGX = /(\(|\)|\d+|\w+)/g;
const NUM_REGX = new RegExp('^(' + nums.join('|') + ')$');
let tokens = [];
let match;
// tokens
while (match = REGX.exec(s)) {
if (NUM_REGX.test(match[0])) {
tokens.push({
type: 'number',
value: match[0]
});
}
else if (/^(\(|\))$/.test(match[0])) {
tokens.push({
type: 'paren',
value: match[0]
});
}
else if (/^(and|or)$/.test(match[0])) {
tokens.push({
type: 'logic',
value: match[0]
});
}
else {
console.warn('Error: Bad elements - ', match[0]);
return false;
}
}
console.log('TOKENS: ', tokens);
// parser
let parsed = [];
let current = 0;
function walk() {
let token = tokens[current];
if (token === undefined) {
throw new TypeError('Missing right paren.');
}
else if (token.type === 'paren') {
if (token.value === '(') {
const expression = {
type: 'expression',
value: []
};
token = tokens[++current];
if (token === undefined) {
throw new TypeError();
}
while (
(token.type !== 'paren')
|| (
token.type === 'paren'
&& token.value !== ')'
)
) {
expression.value.push(walk());
token = tokens[current];
if (token === undefined) {
throw new TypeError('Missing right paren.');
}
}
current++;
return expression;
}
else {
throw new TypeError('Error paren position.');
}
}
else {
current++;
return token;
}
}
try {
while (current < tokens.length) {
parsed.push(walk());
}
} catch (e) {
console.warn('Error: ', e);
return false;
}
console.log('PARSED: ', parsed);
// validate
function validate(s) {
let hasOr;
let hasAnd;
for (let i = 0; i < s.length; i++) {
const {type, value} = s[i];
if (type === 'expression') {
if (!validate(value)) {
return false;
}
continue;
}
const prev = s[i - 1];
const next = s[i + 1];
if (type === 'logic') {
if (
(hasOr && value === 'and')
|| (hasAnd && value === 'or')
|| (prev === undefined || next === undefined)
|| (prev.type === 'logic' || next.type === 'logic')
) {
return false;
}
hasOr = hasOr || value === 'or';
hasAnd = hasAnd || value === 'and';
}
if (
type === 'number'
&& (next && next.type === 'number')
) {
return false;
}
// TODO
}
return true;
}
return validate(parsed);
}
function test(s) {
console.log('TEST: ', s, detect(s));
}
test('(1 or 2) or (1 and (3 and 5))');
test('(1 or 2 and 3) or (1 and (3 and 5))');
test('(1 or 23) or (1 and (3 and 5))');
test(')1 and 3');
test('(1 and 3');
console