Парсер/интерпретатор строки управления доступом (ACS) с PEG.js

Предисловие

Я работаю над созданием строки 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» в приведенном выше примере) должны вызывать методы, выполняющие грязную работу.


person NuSkooler    schedule 04.10.2014    source источник
comment
Не будучи знакомым с PEG.js, это звучит как вопрос, на который гораздо больше шансов получить ответ, если вы зададите его в одном из многочисленных мест сообщества PEG.js, перечисленных по адресу pegjs.majda.cz/development   -  person Mike 'Pomax' Kamermans    schedule 13.10.2014
comment
Посмотрите этот ответ: stackoverflow.com/questions/19790748/ Он обрабатывает списки переменной длины.   -  person orange    schedule 26.10.2014
comment
@BartKiers: я обновил примеры, чтобы использовать И/ИЛИ. Я не совсем уверен, как ответить на вопрос приоритета. Я хотел бы оценить слева направо, но с () группировками приоритета. Возможно, я задаю не те вопросы.   -  person NuSkooler    schedule 04.11.2014
comment
@BartKiers: Это то, что я ищу, правильно.   -  person NuSkooler    schedule 05.11.2014


Ответы (1)


Вот краткая демонстрация, которая правильно анализирует ввод вашего примера и показывает, как вы можете оценить выражения на лету (что вернет логическое значение):

{
  function check(name, value) {
    // Dummy implementation: returns true when the name starts with 'A'
    return name.charAt(0) == 'A';
  }
}

start
 = expr

expr
 = or_expr

or_expr
 = left:and_expr '|' right:expr { return left || right; }
 / and_expr

and_expr
 = left:not_expr '&'? right:expr { return left && right; }
 / not_expr

not_expr
 = '!' value:atom { return !value; }
 / atom

atom
 = acs_check
 / '(' value:expr ')' { return value; }

acs_check
 = n:name a:arg { return check(n, a); }

name
 = c:([A-Z][A-Z]) { return c.join(''); }

arg
 = c:[A-Z]+ { return c.join(''); }
 / d:[0-9]+ { return d.join(''); }
person Bart Kiers    schedule 04.11.2014