Access Control Panel (ACS) Parser / Interpreter with PEG.js

Introduction

I have been working on the creation of A ccess C ontrol S tring (or S ystem) (ACS) line Parser / Interpreter with PEG.js. ACS strings are commonly used in bulletin systems (BBS) to check access rights to specific areas of the board. For example, see Revised ACS documentation .

Sample ACS strings

Below are some simplified lines 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 am trying to achieve

What I would like to do here is parsing and interpreting (or "running") the ACS string and ultimately the final boolean.

Grammar so far

Below is the PEG.js grammar I am with so far. Note that the ACS strings themselves are a little more complex than the examples above (say GM ['abc', 'def'], for example), but I think it's pretty clear up to this point.

{   
  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 do I need help

The main issue I'm running into (if not others I haven't encountered yet!) Is handling priority with () and OR clauses. It might be something simple, but I have been working on it for a few days and have been a little behind. Again, what I'm ultimately trying to achieve here is feed into an ACS string and output the final boolean result. Various ACS commands (like "GM" in the above example) have to make method calls that do the dirty work.

+3


source to share


1 answer


Here's a quick demo that parses your example correctly and shows how you could evaluate expressions on the fly (which return a boolean value):



{
  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(''); }

      

+1


source