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 = [];
    }

  }
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
parses the dependency logical expression of each agent and populates the auxiliary structures used by the agency to control the flow of execution