Предисловие
Я работаю над созданием строки Aдоступа Cуправления Sстрокой (или Sсистемой) (ACS) Парсер/интерпретатор с PEG.js. Строки ACS обычно используются в системах досок объявлений (BBS) для проверки прав доступа к определенным областям доски. Например, см. документацию Renegade по ACS.
Примеры строк ACS
Ниже приведены некоторые упрощенные строки и их английские переводы для иллюстрации:
// Has GM123 OR NOT GM456
GM123|!GM456
// Has GM123 OR NOT (GM456 AND GM789) (note: AND is implied in this grammar if not specified)
GM123|!(GM456GM789)
// Has GM123 AND NOT GM456 OR has GM789
GM123!GM456|GM789
// Has GM1 OR (NOT GM2 OR GM3)
GM1|(!GM2|GM3)
Чего я пытаюсь достичь
Что я хотел бы здесь сделать, так это проанализировать и интерпретировать (или "запустить") строку ACS и в конечном итоге получить финальное логическое значение.
Грамматика на данный момент
Ниже приведен грамматик PEG.js, с которым я уже работал. Обратите внимание, что сами строки ACS немного сложнее, чем в приведенных выше примерах (я допускаю, например, GM['abc','def']), но я думаю, что до этого момента это довольно очевидно.
{
function checkAccessSingle(acsName, arg) {
return true;
}
function checkAccessMulti(acsName, args, anyMatch) {
return true;
}
function makeNot(not, x) {
return not ? !x : x;
}
}
start
= acsString
whitespaceChar
= ' '
ws
= whitespaceChar*
lineTerminatorChar
= [\r\n\u2028\u2029]
decimalDigit
= [0-9]
integer
= decimalDigit+ { return parseInt(text(), 10); }
asciiPrintableChar
= [ -~]
singleAsciiStringChar
= !("'") asciiPrintableChar { return text(); }
doubleAsciiStringChar
= !('"') asciiPrintableChar { return text(); }
nonEmptyStringLiteral
= "'" chars:singleAsciiStringChar+ "'" { return chars.join(''); }
/ '"' chars:doubleAsciiStringChar+ '"' { return chars.join(''); }
AND
= '&'
OR
= '|'
NOT
= '!'
acsName
= n:([A-Z][A-Z]) { return n.join(''); }
acsArg
= nonEmptyStringLiteral
/ integer
acsArgs
= first:acsArg rest:(ws ',' ws a:acsArg { return a; })* {
var args = [ first ];
for(var i = 0; i < rest.length; ++i) {
args.push(rest[i]);
}
return args;
}
singleAcsCheck
= not:NOT? n:acsName a:acsArg* {
return function() {
makeNot(not, checkAccessSingle(n, a));
}
}
/ not:NOT? n:acsName '[' a:acsArgs ']' {
return function() {
return makeNot(not, checkAccessMulti(n, a, false));
}
}
/ not:NOT? n:acsName '{' a:acsArgs '}' {
return function() {
return makeNot(not, checkAccessMulti(n, a, true));
}
}
multiAcsCheck
= singleAcsCheck+
acsString = multiAcsCheck
Где мне нужна помощь
Основная проблема, с которой я столкнулся (если не с другими, с которыми я еще не сталкивался!) - это обработка приоритета с помощью () и предложений OR. Это может быть что-то простое, но я работал над этим несколько дней, и у меня есть кое-что короткое. Опять же, то, чего я в конечном счете пытаюсь добиться здесь, это передать строку ACS и вывести окончательный логический результат. Различные «команды» ACS (например, «GM» в приведенном выше примере) должны вызывать методы, выполняющие грязную работу.