3

Preface

I'm working on creating a Access Control String (or System) (ACS) string Parser/Interpreter with PEG.js. ACS strings are commonly used on Bulletin Board Systems (BBSs) to check access rights to particular areas of the board. For example, see Renegade's ACS documentation.

Example ACS Strings

Below are some simplified strings and their English translations for illustration:

// 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) 

What I'm Trying to Achieve

What I would like to do here is parse and interpret (or "run") the ACS string and ultimately end up with a final boolean.

Grammar So Far

Below is the PEG.js grammer I've some up with so far. Note that the ACS strings themselves are a bit more complex than the examples above (I allow for example GM['abc','def']) but I think up to this point it's fairly self explanatory.

{   
  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

Where I Need Help

The main issue I'm having (if not others I haven't run into yet!) is handling precedence with () and the OR clauses. This may be something simple, but I've worked on this for days and have some up short. Again, what I'm ultimately attempting to achieve here is to feed in an ACS string and output a final boolean result. The various ACS "commands" (e.g. 'GM' in the above example) should make method calls that do the dirty work.

4

1 回答 1

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(''); }
于 2014-11-04T22:46:13.387 回答