lib/agent.js
function parseExpression(expr) {
    var parentList = [];
    var parsedTokenizedExpression = [];
    var parsedExpression = '';
    var testInvalidChar;

    if (expr && expr.trim()) {
      parsedExpression = expr.replace(/\040/g, '');
      testInvalidChar = /[^a-zA-Z0-9_&|!()_]/.test(parsedExpression); //valid characters

      if (!testInvalidChar) {
          var pos = '0';
          var prevprev = '?';
          var prev = '?';
          var head = '';
          var key = '';
          var rbrackets = 0;
          var tmpparent = '';
          var tmpexpression = '';
          
          // parser rules:
          //
          // left hand side of rule determines the rule to apply to the current element of the expression:
          //
          //   first element of key indicates the position of the expression element being evaluated:
          //    1 - first position
          //    n - other position
          //   second element of key represents the position before the previous position:
          //    ? - don't care
          //    & - logical AND
          //    | - logical OR
          //   third element of key represents the previous position on the expression:
          //    ? - don't care
          //    ( - opening parenthesis
          //    # - alpha numeric characters and underscore
          //    ) - closing parenthesis
          //    ! - logical NOT
          //
          // right hand side of rule represents valid symbols for that key
          //
          // example:
          //
          //  parsing expression 'a&&b' (one position at a time):
          //  
          //  - 'a' element is evaluated by first rule:
          //    key: 1st position, before previous and previous positions elements don't care
          //    validation: any alpha numeric character or open parenthesis or underscore or NOT 
          //  - '&' element is evaluated by the third rule:
          //    key: (any position but first, indiferent before previous element, any valid previous element)
          //    validation: any alpha numeric character or closing parenthesis or underscore or AND or OR 
          //  - '&' element is evaluated by sixth rule:
          //    key: any position but first, indiferent before previous element, OR previous element
          //    validation: value has to be '&'
          //  - 'b' element is evaluated by the seventh rule:
          //    key: any position but first, '&' before previous element, '&' previous element
          //    validation: any alpha numeric character or open parenthesis or underscore or NOT or opening parenthesis
          //  
          var rules = {
            '1??': /[a-zA-Z0-9_(!]/,
            'n?(': /[a-zA-Z0-9_(!]/,
            'n?#': /[a-zA-Z0-9_)&|]/,
            'n?!': /[a-zA-Z0-9_(]/,
            'n?)': /[&|)]/,
            'n?&': /[&]/,
            'n&&': /[a-zA-Z0-9_(!]/,
            'n&#': /[a-zA-Z0-9_)&|]/,
            'n&(': /[a-zA-Z0-9_(!]/,
            'n?|': /[|]/,
            'n||': /[a-zA-Z0-9_(!]/,
            'n|(': /[a-zA-Z0-9_(!]/,
            'n|#': /[a-zA-Z0-9_)&|]/,
            'n|&': /[]/,
            'n&|': /[]/,
          };

          for (var i = 0; i < parsedExpression.length; i += 1) {
            pos = (i === 0 ? '1' : 'n');
            head = parsedExpression.charAt(i);
            key = pos + prevprev + prev;

            if (!rules[key].test(head)) {
              err.code = 'InvalidCharacter';
              err.message = 'failed dependency expression validation (invalid character at position ' + (i + 1) + ')'; 
              return err;
            }

            if (head === '(') {
              rbrackets += 1;
            }

            if (head === ')') {
              if (rbrackets <= 0) {
                err.code = 'UnopenedParentheses';
                err.message = 'failed dependency expression validation (unopened parenthesis)'; 
                return err;
              } else {
                rbrackets -= 1;
              }
            }

            // last character
            if (i === parsedExpression.length - 1) {
              // ), # -> expression terminators
              if (/[a-zA-Z0-9)]/.test(head)) {
                if (rbrackets !== 0) {
                  err.code = 'UnclosedParentheses';
                  err.message = 'failed dependency expression validation (unclosed parenthesis)';
                  return err;
                }
              } else {
                err.code = 'InvalidTerminator';
                err.message = 'failed dependency expression validation (invalid expression terminator)';
                return err;
              }
            } else {
              if (prev === '&' || prev === '|') {
                prevprev = prev;
              } else {
                prevprev = '?'; // ? -> don't care
              }

              if (/[a-zA-Z0-9_]/.test(head)) {
                prev = '#'; // # -> valid identifier character
              } else {
                prev = head;
              }

            }
            
            // handle parent list and tokenized expression
            if (/[a-zA-Z0-9_]/.test(head)) {
              
              if (tmpexpression !== '') {
                parsedTokenizedExpression.push(tmpexpression);
                tmpexpression = '';
              }
              
              if (parsedExpression.length === 1) {
                if (id === head) {
                  err.name = 'SelfDependency';
                  err.message = 'failed dependency expression validation (agent self dependency)';
                  return err;
                } else {
                  parentList.push(head); 
                  parsedTokenizedExpression.push(head);
                }
              } else {
                if (i === parsedExpression.length - 1) {
                  tmpparent = tmpparent + head;
                  if (id === tmpparent) {
                    err.name = 'SelfDependency';
                    err.message = 'failed dependency expression validation (agent self dependency)';
                    return err;
                  } else {  
                    parentList.push(tmpparent); 
                    parsedTokenizedExpression.push(tmpparent);
                  }
                } else {
                  tmpparent = tmpparent + head;
                }
              }
              
            } else {
              if (tmpparent !== '') {
                if (id === tmpparent) {
                  err.name = 'SelfDependency';
                  err.message = 'failed dependency expression validation (agent self dependency)';
                  return err;
                } else {
                  parentList.push(tmpparent);
                  parsedTokenizedExpression.push(tmpparent);
                  tmpparent = '';
                }
              }
              tmpexpression = tmpexpression + head;
              if (i === parsedExpression.length - 1) {
                parsedTokenizedExpression.push(tmpexpression);
              }
            }
          
          }
        expression = parsedExpression;
        tokenizedExpression = parsedTokenizedExpression;
        dependencies = arrayGetUniques(parentList); 
      } else {
        err.name = 'InvalidExpression';
        err.message = 'failed dependency expression validation (please use underscore, alphanumeric and logical chars)';
        return err;
      }
    } else {
      expression = '';
      dependencies = []; 
      tokenizedExpression = [];
    }

  }
function parseExpression(expr) {
    var parentList = [];
    var parsedTokenizedExpression = [];
    var parsedExpression = '';
    var testInvalidChar;

    if (expr && expr.trim()) {
      parsedExpression = expr.replace(/\040/g, '');
      testInvalidChar = /[^a-zA-Z0-9_&|!()_]/.test(parsedExpression); //valid characters

      if (!testInvalidChar) {
          var pos = '0';
          var prevprev = '?';
          var prev = '?';
          var head = '';
          var key = '';
          var rbrackets = 0;
          var tmpparent = '';
          var tmpexpression = '';
          
          // parser rules:
          //
          // left hand side of rule determines the rule to apply to the current element of the expression:
          //
          //   first element of key indicates the position of the expression element being evaluated:
          //    1 - first position
          //    n - other position
          //   second element of key represents the position before the previous position:
          //    ? - don't care
          //    & - logical AND
          //    | - logical OR
          //   third element of key represents the previous position on the expression:
          //    ? - don't care
          //    ( - opening parenthesis
          //    # - alpha numeric characters and underscore
          //    ) - closing parenthesis
          //    ! - logical NOT
          //
          // right hand side of rule represents valid symbols for that key
          //
          // example:
          //
          //  parsing expression 'a&&b' (one position at a time):
          //  
          //  - 'a' element is evaluated by first rule:
          //    key: 1st position, before previous and previous positions elements don't care
          //    validation: any alpha numeric character or open parenthesis or underscore or NOT 
          //  - '&' element is evaluated by the third rule:
          //    key: (any position but first, indiferent before previous element, any valid previous element)
          //    validation: any alpha numeric character or closing parenthesis or underscore or AND or OR 
          //  - '&' element is evaluated by sixth rule:
          //    key: any position but first, indiferent before previous element, OR previous element
          //    validation: value has to be '&'
          //  - 'b' element is evaluated by the seventh rule:
          //    key: any position but first, '&' before previous element, '&' previous element
          //    validation: any alpha numeric character or open parenthesis or underscore or NOT or opening parenthesis
          //  
          var rules = {
            '1??': /[a-zA-Z0-9_(!]/,
            'n?(': /[a-zA-Z0-9_(!]/,
            'n?#': /[a-zA-Z0-9_)&|]/,
            'n?!': /[a-zA-Z0-9_(]/,
            'n?)': /[&|)]/,
            'n?&': /[&]/,
            'n&&': /[a-zA-Z0-9_(!]/,
            'n&#': /[a-zA-Z0-9_)&|]/,
            'n&(': /[a-zA-Z0-9_(!]/,
            'n?|': /[|]/,
            'n||': /[a-zA-Z0-9_(!]/,
            'n|(': /[a-zA-Z0-9_(!]/,
            'n|#': /[a-zA-Z0-9_)&|]/,
            'n|&': /[]/,
            'n&|': /[]/,
          };

          for (var i = 0; i < parsedExpression.length; i += 1) {
            pos = (i === 0 ? '1' : 'n');
            head = parsedExpression.charAt(i);
            key = pos + prevprev + prev;

            if (!rules[key].test(head)) {
              err.code = 'InvalidCharacter';
              err.message = 'failed dependency expression validation (invalid character at position ' + (i + 1) + ')'; 
              return err;
            }

            if (head === '(') {
              rbrackets += 1;
            }

            if (head === ')') {
              if (rbrackets <= 0) {
                err.code = 'UnopenedParentheses';
                err.message = 'failed dependency expression validation (unopened parenthesis)'; 
                return err;
              } else {
                rbrackets -= 1;
              }
            }

            // last character
            if (i === parsedExpression.length - 1) {
              // ), # -> expression terminators
              if (/[a-zA-Z0-9)]/.test(head)) {
                if (rbrackets !== 0) {
                  err.code = 'UnclosedParentheses';
                  err.message = 'failed dependency expression validation (unclosed parenthesis)';
                  return err;
                }
              } else {
                err.code = 'InvalidTerminator';
                err.message = 'failed dependency expression validation (invalid expression terminator)';
                return err;
              }
            } else {
              if (prev === '&' || prev === '|') {
                prevprev = prev;
              } else {
                prevprev = '?'; // ? -> don't care
              }

              if (/[a-zA-Z0-9_]/.test(head)) {
                prev = '#'; // # -> valid identifier character
              } else {
                prev = head;
              }

            }
            
            // handle parent list and tokenized expression
            if (/[a-zA-Z0-9_]/.test(head)) {
              
              if (tmpexpression !== '') {
                parsedTokenizedExpression.push(tmpexpression);
                tmpexpression = '';
              }
              
              if (parsedExpression.length === 1) {
                if (id === head) {
                  err.name = 'SelfDependency';
                  err.message = 'failed dependency expression validation (agent self dependency)';
                  return err;
                } else {
                  parentList.push(head); 
                  parsedTokenizedExpression.push(head);
                }
              } else {
                if (i === parsedExpression.length - 1) {
                  tmpparent = tmpparent + head;
                  if (id === tmpparent) {
                    err.name = 'SelfDependency';
                    err.message = 'failed dependency expression validation (agent self dependency)';
                    return err;
                  } else {  
                    parentList.push(tmpparent); 
                    parsedTokenizedExpression.push(tmpparent);
                  }
                } else {
                  tmpparent = tmpparent + head;
                }
              }
              
            } else {
              if (tmpparent !== '') {
                if (id === tmpparent) {
                  err.name = 'SelfDependency';
                  err.message = 'failed dependency expression validation (agent self dependency)';
                  return err;
                } else {
                  parentList.push(tmpparent);
                  parsedTokenizedExpression.push(tmpparent);
                  tmpparent = '';
                }
              }
              tmpexpression = tmpexpression + head;
              if (i === parsedExpression.length - 1) {
                parsedTokenizedExpression.push(tmpexpression);
              }
            }
          
          }
        expression = parsedExpression;
        tokenizedExpression = parsedTokenizedExpression;
        dependencies = arrayGetUniques(parentList); 
      } else {
        err.name = 'InvalidExpression';
        err.message = 'failed dependency expression validation (please use underscore, alphanumeric and logical chars)';
        return err;
      }
    } else {
      expression = '';
      dependencies = []; 
      tokenizedExpression = [];
    }

  }
parses the dependency logical expression of each agent and populates the auxiliary structures used by the agency to control the flow of execution