Show:
/**
 * @module proact-dsl
 */

/**
 * Contains {{#crossLink "ProAct.DSL"}}{{/crossLink}} operation logic definitions.
 * <p>
 *  Every operation has
 *  <ol>
 *    <li><b>sym</b> - A symbol used to identify the right operation in a DSL string or object.</li>
 *    <li><b>match method</b> - A method used for identifying the operation, usually it uses the <i>sym</i></li>
 *    <li>
 *      <b>toOptions</b> - A method which is able to turn a DSL string with the operation,
 *      into an actual array of options containing all the functions to be executed by the DSL and their arguments.
 *    </li>
 *    <li><b>action</b> - The operation logic. The options object of the above method should be passed to it, as well as the targed on which the DSL should be run.</li>
 *  </ol>
 * </p>
 *
 * @namespace ProAct
 * @class OpStore
 * @static
 */
ProAct.OpStore = {

  all: {

    /**
     * Can generate a simple operation definition.
     * <p>
     *  It is used for defining all the simple operations, like <i>map</i> or <i>filter</i>.
     * </p>
     *
     * @for ProAct.OpStore.all
     * @static
     * @param {String} name
     *      The name of the operation to define.
     * @param {String} sym
     *      The symbol of the operation that shoul dbe used to identify it from within a DSL string.
     * @return {Object}
     *      <ol>
     *        <li><b>sym</b> - The symbol used to identify the operation in a DSL string or object.</li>
     *        <li><b>match method</b> - A method using the <i>sym</i> for identifying the operation in a DSL string.</li>
     *        <li>
     *          <b>toOptions</b> - A method which is able to turn a DSL string with the operation,
     *          into the actual array of options containing all the functions to be executed by the DSL and their arguments.
     *          <p>
     *            This method is able to fetch predefined operation functions.
     *          </p>
     *        </li>
     *        <li>
     *          <b>action</b> - The operation logic.
     *          The options object of the above method should be passed to it, as well as the targed on which the DSL should be run.
     *          <p>
     *            It just calls method named as the passed <i>name</i> parameter on the targed <i>object</i>, passing it as arguments,
     *            the argument array generated from the <i>toOptions</i> method.
     *          </p>
     *        </li>
     *      </ol>
     */
    simpleOp: function(name, sym) {
      return {
        sym: sym,
        match: function (op) {
          return op.substring(0, sym.length) === sym;
        },
        setupArgument: function (arg, realArguments, predefined, opArguments) {
          var i, k, ln, actions;
          if (arg.charAt(0) === '$') {
            arg = realArguments[parseInt(arg.substring(1), 10) - 1];
          } else if (predefined && arg.charAt(0) === '&') {
            i = arg.lastIndexOf('&');
            k = arg.substring(0, i);
            if (predefined[k]) {
              arg = predefined[k].call(null, arg.substring(i + 1));
            }
          } else if (predefined && arg.charAt(0) === '!') {
            arg = this.setupArgument(arg.substring(1), realArguments, predefined, opArguments);
            if (arg) {
              k = arg;
              arg = function () {
                return !k.apply(null, arguments);
              };
            }
          } else if (predefined && predefined[arg]) {
            arg = predefined[arg];

            if (P.U.isArray(arg)) {
              opArguments.push.apply(opArguments, arg);
              arg = undefined;
            }
          }

          return arg;
        },
        toOptions: function (actionObject, op) {
          var reg = new RegExp(dslOps[name].sym + "(\\w*)\\(([\\s\\S]*)\\)"),
              matched = reg.exec(op),
              action = matched[1], args = matched[2],
              opArguments = [],
              realArguments = slice.call(arguments, 2),
              predefined = dsl.predefined[name],
              arg, i , ln, k;
          if (action) {
            opArguments.push(action);
          }

          if (args) {
            args = args.split(',');
            ln = args.length;
            for (i = 0; i < ln; i++) {
              arg = args[i].trim();
              arg = this.setupArgument(arg, realArguments, predefined, opArguments);

              if (arg !== undefined) {
                opArguments.push(arg);
              }
            }
          }

          if (!actionObject[name]) {
            actionObject[name] = opArguments;
          } else {
            if (!P.U.isArray(actionObject[name][0])) {
              actionObject[name] = [actionObject[name], opArguments];
            } else {
              actionObject[name].push(opArguments);
            }
          }

          actionObject.order = actionObject.order || [];
          actionObject.order.push(name);
        },
        action: function (object, actionObject) {
          if (!actionObject || !actionObject[name]) {
            return object;
          }

          var args = actionObject[name];
          if (!P.U.isArray(args)) {
            args = [args];
          }

          if (name === 'accumulation' && P.U.isArray(args[0]) && args[0].length == 2 && P.U.isFunction(args[0][1])) {
            args = args[0];
          }

          return object[name].apply(object, args);
        }
      };
    }
  }
};
opStoreAll = P.OpStore.all;

/**
 * Contains implementation of the `ProAct.js DSL`.
 * <p>
 *  The idea of the DSL is to define {{#crossLink "ProAct.Actor"}}{{/crossLink}}s and their dependencies on each other in a declarative and simple way.
 * </p>
 * <p>
 *  The {{#crossLink "ProAct.Registry"}}{{/crossLink}} is used to store these actors.
 * </p>
 * <p>
 *  For example if we want to have a stream configured to write in a property, it is very easy done using the DSL:
 *  <pre>
 *    ProAct.registry.prob('val', 0, '<<(s:data)');
 *  </pre>
 *  This tells the {{#crossLink "ProAct.Registry"}}{{/crossLink}} to create a {{#crossLink "ProAct.Property"}}{{/crossLink}} with the value of zero, and to point the previously,
 *  stored 'data' stream to it.
 * </p>
 *
 * @namespace ProAct
 * @class DSL
 * @static
 */
ProAct.DSL = {

  /**
   * A separator which can be used to separate multiple DSL expressions in one string.
   *
   * @for ProAct.DSL
   * @type String
   * @property separator
   * @final
   */
  separator: '|',

  /**
   * The operation definitions of the DSL.
   * <p>
   *  All of the available and executable operations defined in the ProAct.DSL.
   * </p>
   * <p>
   *  Users of ProAct.js can add their own operation to it.
   *  <pre>
   *    ProAct.DSL.ops.myOp = ProAct.OpStore.all.simpleOp('foo', 'foo');
   *  </pre>
   * </p>
   *
   * @namespace ProAct.DSL
   * @class ops
   * @static
   */
  ops: {

    /**
     * DSL operation for defining sources of {{#crossLink "ProAct.Actor"}}{{/crossLink}}s.
     * <p>
     *  For example
     *  <pre>
     *    '<<(s:bla)'
     *  </pre>
     *  means that the source of the targed of the DSL should be a stream stored in the {{#crossLink "ProAct.Registry"}}{{/crossLink}} by the key 'bla'.
     * </p>
     * <p>
     *  or
     *  <pre>
     *    '<<($1)'
     *  </pre>
     *  means that the source of the targed of the DSL should be an {{#crossLink "ProAct.Actor"}}{{/crossLink}} passed to the {{#crossLink "ProAct.DSL/run:method"}}{{/crossLink}}
     *  method as the first argument after the targed object, the DSL data and the registry.
     * </p>
     *
     * @for ProAct.DSL.ops
     * @final
     * @property into
     * @type Object
     */
    into: opStoreAll.simpleOp('into', '<<'),

    /**
     * DSL operation for setting the targed of the DSL as sources of another {{#crossLink "ProAct.Actor"}}{{/crossLink}}s.
     * <p>
     *  For example
     *  <pre>
     *    '>>(s:bla)'
     *  </pre>
     *  means that the targed of the DSL should become a source for a stream stored in the {{#crossLink "ProAct.Registry"}}{{/crossLink}} by the key 'bla'.
     * </p>
     * <p>
     *  or
     *  <pre>
     *    '>>($1)'
     *  </pre>
     *  means that the targed of the DSL should become a source for an {{#crossLink "ProAct.Actor"}}{{/crossLink}} passed to the {{#crossLink "ProAct.DSL/run:method"}}{{/crossLink}}
     *  method as the first argument after the targed object, the DSL data and the registry.
     * </p>
     *
     * @for ProAct.DSL.ops
     * @final
     * @property out
     * @type Object
     */
    out: opStoreAll.simpleOp('out', '>>'),

    /**
     * DSL operation for attaching listener to the target {{#crossLink "ProAct.Actor"}}{{/crossLink}} of the DSL.
     * <p>
     *  For example
     *  <pre>
     *    '@(f:bla)'
     *  </pre>
     *  means that listener function, stored in the {{#crossLink "ProAct.Registry"}}{{/crossLink}} as 'bla'
     *  should be attached as a listener to the targed {{#crossLink "ProAct.Actor"}}{{/crossLink}} of the DSL.
     * </p>
     *
     * @for ProAct.DSL.ops
     * @final
     * @property on
     * @type Object
     */
    on: opStoreAll.simpleOp('on', '@'),

    /**
     * DSL operation for adding mapping to the target {{#crossLink "ProAct.Actor"}}{{/crossLink}} of the DSL.
     * <p>
     *  For example
     *  <pre>
     *    'map(f:bla)'
     *  </pre>
     *  means that mapping function, stored in the {{#crossLink "ProAct.Registry"}}{{/crossLink}} as 'bla'
     *  should be mapped to the targed {{#crossLink "ProAct.Actor"}}{{/crossLink}} of the DSL.
     * </p>
     * <p>
     *  or
     *  <pre>
     *    'map($2)'
     *  </pre>
     *  means that mapping function passed to the {{#crossLink "ProAct.DSL/run:method"}}{{/crossLink}}
     *  method as the second argument after the targed object, the DSL data and the registry
     *  should be mapped to the targed {{#crossLink "ProAct.Actor"}}{{/crossLink}} of the DSL.
     * </p>
     *
     * @for ProAct.DSL.ops
     * @final
     * @property mapping
     * @type Object
     */
    mapping: opStoreAll.simpleOp('mapping', 'map'),

    /**
     * DSL operation for adding filters to the target {{#crossLink "ProAct.Actor"}}{{/crossLink}} of the DSL.
     * <p>
     *  For example
     *  <pre>
     *    'filter(f:bla)'
     *  </pre>
     *  means that filtering function, stored in the {{#crossLink "ProAct.Registry"}}{{/crossLink}} as 'bla'
     *  should be add as filter to the targed {{#crossLink "ProAct.Actor"}}{{/crossLink}} of the DSL.
     * </p>
     * <p>
     *  or
     *  <pre>
     *    'filter($1)'
     *  </pre>
     *  means that filtering function passed to the {{#crossLink "ProAct.DSL/run:method"}}{{/crossLink}}
     *  method as the first argument after the targed object, the DSL data and the registry
     *  should be added as filter to the targed {{#crossLink "ProAct.Actor"}}{{/crossLink}} of the DSL.
     * </p>
     *
     * @for ProAct.DSL.ops
     * @final
     * @property filtering
     * @type Object
     */
    filtering: opStoreAll.simpleOp('filtering', 'filter'),

    /**
     * DSL operation for adding accumulation to the target {{#crossLink "ProAct.Actor"}}{{/crossLink}} of the DSL.
     * <p>
     *  For example
     *  <pre>
     *    'acc($1, f:bla)'
     *  </pre>
     *  means that accumulating function, stored in the {{#crossLink "ProAct.Registry"}}{{/crossLink}} as 'bla'
     *  should be added as accumulation to the targed {{#crossLink "ProAct.Actor"}}{{/crossLink}} of the DSL,
     *  and the first argument passed to {{#crossLink "ProAct.DSL/run:method"}}{{/crossLink}} after the targed object, the DSL data and the registry should
     *  be used as initial value for the accumulation.
     * </p>
     *
     * @for ProAct.DSL.ops
     * @final
     * @property accumulation
     * @type Object
     */
    accumulation: opStoreAll.simpleOp('accumulation', 'acc')
  },

  /**
   * A set of predefined operations to be used by the DSL.
   *
   * @namespace ProAct.DSL
   * @class predefined
   * @static
   */
  predefined: {

    /**
     * A set of predefined mapping operations to be used by the DSL.
     *
     * @class mapping
     * @namespace ProAct.DSL.predefined
     * @static
     */
    mapping: {

      /**
       * Mapping operation for changing the sign of a number to the oposite.
       * <p>
       *  For example 4 becomes -4 and -5 becomes 5.
       * </p>
       * <p>
       *  Usage in a DSL expression:
       *  <pre>
       *    map(-)
       *  </pre>
       * </p>
       *
       * @for ProAct.DSL.predefined.mapping
       * @final
       * @static
       * @method -
       * @param {Number} n
       *      The number which will have its sign inverted.
       * @return {Number}
       *      The same number as `n`, but with opposite sign.
       */
      '-': function (el) { return -el; },

      /**
       * Mapping operation for computing the square of a number.
       * <p>
       *  For example 4 becomes 16.
       * </p>
       * <p>
       *  Usage in a DSL expression:
       *  <pre>
       *    map(pow)
       *  </pre>
       * </p>
       *
       * @for ProAct.DSL.predefined.mapping
       * @static
       * @method pow
       * @param {Number} n
       *      The number to power.
       * @return {Number}
       *      The square of `n`.
       */
      'pow': function (el) { return el * el; },

      /**
       * Mapping operation for computing the square root of a number.
       * <p>
       *  For example 4 becomes 2.
       * </p>
       * <p>
       *  Usage in a DSL expression:
       *  <pre>
       *    map(sqrt)
       *  </pre>
       * </p>
       *
       * @for ProAct.DSL.predefined.mapping
       * @static
       * @method sqrt
       * @param {Number} n
       *      The number to compute the square root for.
       * @return {Number}
       *      The square root of `n`.
       */
      'sqrt': function (el) { return Math.sqrt(el); },

      /**
       * Mapping operation for turning an string to a decimal Number - integer.
       * <p>
       *  For example '4' becomes 4.
       * </p>
       * <p>
       *  Usage in a DSL expression:
       *  <pre>
       *    map(int)
       *  </pre>
       * </p>
       *
       * @for ProAct.DSL.predefined.mapping
       * @static
       * @method int
       * @param {String} str
       *      The string to convert to integer.
       * @return {Number}
       *      The integer representation of `str`.
       */
      'int': function (el) { return parseInt(el, 10); },

      /**
       * Mapping operation for calling a method of an object.
       * <p>
       *  Usage in a DSL expression:
       *  <pre>
       *    map(&.&go)
       *  </pre>
       *  This will call the 'target.go' method and use its result.
       * </p>
       *
       * @for ProAct.DSL.predefined.mapping
       * @static
       * @method &.
       * @param {String} methodName
       *      The method name to call.
       * @return {Object}
       *      The result of the method call.
       */
      '&.': function (arg) {
        return function (el) {
          var p = el[arg];
          if (!p) {
            return el;
          } else if (P.U.isFunction(p)) {
            return p.call(el);
          } else {
            return p;
          }
        };
      },

      /**
       * Mapping operation for turning value in an
       * ProAct.Array pop event.
       * <p>
       *  Usage in a DSL expression:
       *  <pre>
       *    map(pop)
       *  </pre>
       * </p>
       *
       * @for ProAct.DSL.predefined.mapping
       * @static
       * @method pop
       * @return {Event}
       *      Pop event.
       */
      pop: function () {
        return P.E.simple('array', 'pop');
      },

      /**
       * Mapping operation for turning value in an
       * ProAct.Array shift event.
       * <p>
       *  Usage in a DSL expression:
       *  <pre>
       *    map(shift)
       *  </pre>
       * </p>
       *
       * @for ProAct.DSL.predefined.mapping
       * @static
       * @method shift
       * @return {Event}
       *      Shift event.
       */
      shift: function () {
        return P.E.simple('array', 'shift');
      },

      /**
       * Mapping operation for turning value event in its value.
       * <p>
       *  Usage in a DSL expression:
       *  <pre>
       *    map(eventToVal)
       *  </pre>
       * </p>
       *
       * @for ProAct.DSL.predefined.mapping
       * @static
       * @method eventToVal
       * @param {Event} event
       *      The value event to get the new value from.
       * @return {Object}
       *      The value.
       */
      eventToVal: function (event) {
        return event.args[0][event.target];
      },

      /**
       * Maps anything to the constant true.
       * <p>
       *  Usage in a DSL expression:
       *  <pre>
       *    map(true)
       *  </pre>
       * </p>
       *
       * @for ProAct.DSL.predefined.mapping
       * @static
       * @method true
       * @param {Object} value
       *      Arbitrary value.
       * @return {Boolean}
       *      Just the `true` constant.
       */
      'true': function (event) {
        return true;
      },

      /**
       * Toggles a boolean value. If the value is `true` it becomes `false` and vice versa.
       * <p>
       *  Usage in a DSL expression:
       *  <pre>
       *    map(!)
       *  </pre>
       * </p>
       *
       * @for ProAct.DSL.predefined.mapping
       * @static
       * @method !
       * @param {Boolean} value
       *      A boolean value.
       * @return {Boolean}
       *      The opposite of `value`.
       */
      '!': function (value) {
        return !value;
      },

      /**
       * Adds the current time to the object value, called upon
       * If the value is not an object (for example it is a Number), it is returned as it is.
       *
       * <p>
       *  Usage in a DSL expression:
       *  <pre>
       *    map(time)
       *  </pre>
       * </p>
       *
       * @for ProAct.DSL.predefined.mapping
       * @static
       * @method time
       * @param {Object} value
       *      The object to modify with time.
       * @return {Object}
       *      The modified value.
       */
      'time': function (value) {
        if (P.U.isObject(value)) {
          value.time = new Date().getTime();
        }
        return value;
      }
    },

    /**
     * A set of predefined filtering operations to be used by the DSL.
     *
     * @class filtering
     * @namespace ProAct.DSL.predefined
     * @static
     */
    filtering: {

      /**
       * Filtering operation for filtering only odd Numbers.
       * <p>
       *  Usage in a DSL expression:
       *  <pre>
       *    filter(odd)
       *  </pre>
       * </p>
       *
       * @for ProAct.DSL.predefined.filtering
       * @static
       * @method odd
       * @param {Number} n
       *      The number to check if it is odd.
       * @return {Boolean}
       *      True, if the number is odd.
       */
      'odd': function (el) { return el % 2 !== 0; },

      /**
       * Filtering operation for filtering only even Numbers.
       * <p>
       *  Usage in a DSL expression:
       *  <pre>
       *    filter(even)
       *  </pre>
       * </p>
       *
       * @for ProAct.DSL.predefined.filtering
       * @static
       * @method even
       * @param {Number} n
       *      The number to check if it is even.
       * @return {Boolean}
       *      True, if the number is even.
       */
      'even': function (el) { return el % 2 === 0; },

      /**
       * Filtering operation for filtering only positive Numbers.
       * <p>
       *  Usage in a DSL expression:
       *  <pre>
       *    filter(+)
       *  </pre>
       * </p>
       *
       * @for ProAct.DSL.predefined.filtering
       * @static
       * @method +
       * @param {Number} n
       *      The number to check if it is positive.
       * @return {Boolean}
       *      True, if the number is positive or zero.
       */
      '+': function (el) { return el >= 0; },

      /**
       * Filtering operation for filtering only negative Numbers.
       * <p>
       *  Usage in a DSL expression:
       *  <pre>
       *    filter(-)
       *  </pre>
       * </p>
       *
       * @for ProAct.DSL.predefined.filtering
       * @static
       * @method -
       * @param {Number} n
       *      The number to check if it is negative.
       * @return {Boolean}
       *      True, if the number is negative or zero.
       */
      '-': function (el) { return el <= 0; },

      /**
       * Flitering operation for using a method of an object as a filter.
       * <p>
       *  Usage in a DSL expression:
       *  <pre>
       *    filter(&.&boolFunc)
       *  </pre>
       *  This will call the 'target.boolFunc' method and use its result as a filter.
       * </p>
       *
       * @for ProAct.DSL.predefined.filtering
       * @static
       * @method &.
       * @param {String} methodName
       *      The name of the method to use for filtering.
       * @return {Boolean}
       *      The result of the method call.
       */
      '&.': function (arg) {
        return function (el) {
          if (this.action) {
            return this.action.call(this.context, el);
          }

          var p = el[arg];
          if (!p) {
            return el;
          } else if (P.U.isFunction(p)) {
            this.action = p;
            this.context = el;
          } else {
            return p;
          }
        };
      },

      /**
       * Filtering operation for filtering only values different from undefined.
       * <p>
       *  Usage in a DSL expression:
       *  <pre>
       *    filter(defined)
       *  </pre>
       * </p>
       *
       * @for ProAct.DSL.predefined.filtering
       * @static
       * @method defined
       * @param {Event} event
       *      The value event to check if its value is defined.
       * @return {Boolean}
       *      True if the value in the event is not `undefined`.
       */
      defined: function (event) {
        return event.args[0][event.target] !== undefined;
      },

      /**
       * Filtering operation for filtering only events
       * that have null/undefined as a source.
       * <p>
       *  Usage in a DSL expression:
       *  <pre>
       *    filter(originalEvent)
       *  </pre>
       * </p>
       *
       * @for ProAct.DSL.predefined.filtering
       * @static
       * @method originalEvent
       * @param {Event} event
       *      The value event to check if it has a source or not.
       * @return {Boolean}
       *      True if the `event` passed has no source.
       */
      originalEvent: function (event) {
        return event.source === undefined || event.source === null;
      },

      /**
       * Filtering operation for passing everything.
       * <p>
       *  Usage in a DSL expression:
       *  <pre>
       *    filter(all)
       *  </pre>
       * </p>
       *
       * @for ProAct.DSL.predefined.filtering
       * @static
       * @method all
       * @param {Object} val
       *      Anything.
       * @return {Boolean}
       *      True.
       */
      all: function () {
        return true;
      }
    },

    /**
     * A set of predefined accumulation operations to be used by the DSL.
     *
     * @class accumulation
     * @namespace ProAct.DSL.predefined
     * @static
     */
    accumulation: {

      /**
       * Accumulation operation representing a sum of numbers.
       * <p>
       *  Usage in a DSL expression:
       *  <pre>
       *    acc(+)
       *  </pre>
       * </p>
       *
       * @for ProAct.DSL.predefined.accumulation
       * @static
       * @property +
       * @type Array
       */
      '+': [0, function (x, y) { return x + y; }],

      /**
       * Accumulation operation representing a product of numbers.
       * <p>
       *  Usage in a DSL expression:
       *  <pre>
       *    acc(*)
       *  </pre>
       * </p>
       *
       * @for ProAct.DSL.predefined.accumulation
       * @static
       * @constant
       * @property *
       * @type Array
       */
      '*': [1, function (x, y) { return x * y; }],

      /**
       * Accumulation operation representing string concatenation.
       * <p>
       *  Usage in a DSL expression:
       *  <pre>
       *    acc(+str)
       *  </pre>
       * </p>
       *
       * @for ProAct.DSL.predefined.accumulation
       * @static
       * @property +str
       * @type Array
       */
      '+str': ['', function (x, y) { return x + y; }],
    }
  },

  /**
   * Defines a new predefined function to be reused in the DSL.
   *
   * For example:
   * ```
   *   ProAct.DSL.defPredefined('filter', 'enter', function (event) {
   *    return event.keyCode === 13;
   *   });
   *
   * ```
   * creates a new `filtering` function, which can be used like this:
   * ```
   *   actor2 = actor1.filter('enter');
   * ```
   * the `actor2` in this case will recieve only the events with keyCode of `13`.
   *
   * @for ProAct.DSL
   * @static
   * @method defPredefined
   * @param {String} type
   *      One of the three -> `mapping`, `filtering` and `accumulation` types.
   * @param {String} id
   *      The identificator of the predefined function to be passed to trasfromation or filtering operations.
   * @param {Function|Array} operation
   *      The implementation of the operation.
   */
  defPredefined: function(type, id, operation) {
    if (type === 'm' || type === 'map') {
      type = 'mapping';
    }
    if (type === 'f' || type === 'filter') {
      type = 'filtering';
    }
    if (type === 'a' || type === 'acc' || type === 'accumulate') {
      type = 'accumulation';
    }

    ProAct.DSL.predefined[type][id] = operation;
  },

  /**
   * Extracts DSL actions and options from a string.
   * <p>
   *  Splits the passed <i>optionString</i> using {{#crossLink "ProAct.DSL/separator:property"}}{{/crossLink}} as saparator
   *  and calls {{#crossLink "ProAct.DSL/optionsFromArray:method"}}{{/crossLink}} on the result.
   * </p>
   *
   * @for ProAct.DSL
   * @static
   * @method optionsFromString
   * @param {String} optionString
   *      The string to use to extract options from.
   * @param [...]
   *      Parameters for the extracted actions/functions/operations.
   *      <p>
   *        For example if the string contains 'map($1)', the first argument passed after the <i>optionString</i> argument
   *        is passed to the 'map' operation.
   *      </p>
   * @return {Object}
   *      Object containing operations as fields and options(arguments) for these operations as values.
   *      <p>
   *        'map($1)|filter(+)|@($2)' becomes:
   *        <pre>
   *          {
   *            mapping: {first-argument-to-this-function-after-the-optionString-arg},
   *            filtering: ProAct.DSL.predefined.filtering['+'],
   *            on: {second-argument-to-this-function-after-the-optionString-arg}
   *          }
   *        </pre>
   *      </p>
   */
  optionsFromString: function (optionString) {
    return dsl.optionsFromArray.apply(null, [optionString.split(dsl.separator)].concat(slice.call(arguments, 1)));
  },

  /**
   * Extracts DSL actions and options from an array of strings.
   * <p>
   *  Example <i>optionArray</i> is ['map($1)', 'filter(+)', @($2)'] and it will become options object of functions and arguments to
   *  be applied on a target {{#crossLink "ProAct.Actor"}}{{/crossLink}} passed to the {{#crossLink "ProAct.DSL/run:method"}}{{/crossLink}} method.
   * </p>
   *
   * @for ProAct.DSL
   * @static
   * @method optionsFromArray
   * @param {Array} optionArray
   *      The array of strings to use to extract options from.
   * @param [...]
   *      Parameters for the extracted actions/functions/operations.
   *      <p>
   *        For example if the array contains 'map($1)', the first argument passed after the <i>optionArray</i> argument
   *        is passed to the 'map' operation.
   *      </p>
   * @return {Object}
   *      Object containing operations as fields and options(arguments) for these operations as values.
   *      <p>
   *        ['map($1)', 'filter(+)', @($2)'] becomes:
   *        <pre>
   *          {
   *            mapping: {first-argument-to-this-function-after-the-optionString-arg},
   *            filtering: ProAct.DSL.predefined.filtering['+'],
   *            on: {second-argument-to-this-function-after-the-optionString-arg}
   *          }
   *        </pre>
   *      </p>
   */
  optionsFromArray: function (optionArray) {
    var result = {}, i, ln = optionArray.length,
        ops = P.R.ops, op, opType;
    for (i = 0; i < ln; i++) {
      op = optionArray[i];
      for (opType in P.DSL.ops) {
        opType = P.DSL.ops[opType];
        if (opType.match(op)) {
          opType.toOptions.apply(opType, [result, op].concat(slice.call(arguments, 1)));
          break;
        }
      }
    }
    return result;
  },

  /**
   * Configures an {{#crossLink "ProAct.Actor"}}{{/crossLink}} using the DSL passed with the <i>options</i> argument.
   * <p>
   *  Uses the passed {{#crossLink "ProAct.Registry"}}{{/crossLink}} to read stored values from.
   * </p>
   *
   * @for ProAct.DSL
   * @static
   * @method
   * @param {ProAct.Actor} actor
   *      The target of the DSL operations.
   * @param {ProAct.Actor|String|Object} options
   *      The DSL formatted options to be used for the configuration.
   *      <p>
   *        If the value of this parameter is instance of {{#crossLink "ProAct.Actor"}}{{/crossLink}} it is set as a source to the <i>target actor</i>.
   *      </p>
   *      <p>
   *        If the value ot this parameter is String - {{#crossLink "ProAct.DSL/optionsFromString:method"}}{{/crossLink}} is used to be turned to an options object.
   *      </p>
   *      <p>
   *        If the values of this parameter is object, it is used to configure the <i>targed actor</i>.
   *      </p>
   *      <p>
   *        The format of the object should be something like:
   *        <pre>
   *          {
   *            dsl-operation: function|array-of-functions-and-arguments,
   *            dsl-operation: function|array-of-functions-and-arguments,
   *            dsl-operation: function|array-of-functions-and-arguments,
   *            ...
   *          }
   *        </pre>
   *      </p>
   * @param {ProAct.Registry} registry
   *      The registry to read stored values for the DSL operations.
   *      <p>
   *        For example if there is 'map(f:foo)', the mapping function is read from the registry at the key 'foo'.
   *      </p>
   * @param [...]
   *      Parameters for the DSL operations.
   *      <p>
   *        For example if the array contains 'map($1)', the first argument passed after the <i>actor</i>, <i>options</i> and <i>registry</i> arguments
   *        is passed to the 'map' operation.
   *      </p>
   * @return {ProAct.Actor}
   *      The configured actor.
   */
  run: function (actor, options, registry) {
    var isS = P.U.isString,
        args = slice.call(arguments, 3),
        option, i, ln, opType, oldOption,
        multiple = {};

    if (options && isS(options)) {
      options = dsl.optionsFromString.apply(null, [options].concat(args));
    }

    if (options && options instanceof P.Actor) {
      options = {into: options};
    }

    if (options && options.order) {
      ln = options.order.length;
      for (i = 0; i < ln; i++) {
        option = options.order[i];
        if (opType = dslOps[option]) {
          if (registry) {
            if (options.order.indexOf(option) !== options.order.lastIndexOf(option)) {
              if (multiple[option] === undefined) {
                multiple[option] = -1;
              }
              multiple[option] = multiple[option] + 1;
              oldOption = options[option];
              options[option] = options[option][multiple[option]];
            }
            options[option] = registry.toObjectArray(options[option]);
          }

          opType.action(actor, options);
          if (oldOption) {
            options[option] = oldOption;
            oldOption = undefined;

            if (multiple[option] >= options[option].length - 1) {
              delete options[option];
            }
          } else {
            delete options[option];
          }
        }
      }
    }

    for (opType in dslOps) {
      if (options && (option = options[opType])) {
        options[opType] = registry.toObjectArray(option);
      }
      opType = dslOps[opType];
      opType.action(actor, options);
    }

    return actor;
  }
};

dsl = P.DSL;
dslOps = dsl.ops;