dice = {
	version: "0.5.0",

	roll: function(str, scope){
		var parsed = dice.parse.parse(dice.correctInput(str));
		var evaled = dice.eval.eval(parsed, scope);
		return evaled;
	},

	correctInput: function(input) {
		if (input.charAt(0).match(/[dw]/i)) {
			input = 1 + input;   
		}

		var match;
		while ((match = input.match(/\D[dw]\d*/i)) !== null) {
		var str = match.toString();
		var edit = str.charAt(0) + 1 + str.substring(1,str.length);
			input = input.replace(/\D[dw]\d*/i, edit);
		}
		input = input.replace(/D/g,'d').replace(/W/g,'w');
		return input;
	},

	stringify_expression: function(evaled_op){
		var sub = dice.stringify(evaled_op.expression);
		var prefix = evaled_op.op[0];
		if(prefix === 'p'){
			prefix = '';
		}
		
		return prefix + "( " + sub + " )";
	},

	stringify_op: function(evaled_op){
		var rs = dice.stringify(evaled_op.rightSide);
		var ls = dice.stringify(evaled_op.leftSide);
		return rs + ' ' + evaled_op.op + ' ' + ls;
	},

	stringify_rolls: function(evaled_roll){
		var minStr = evaled_roll.min > 1 ? evaled_roll.min + '..' : '';
		var preamble = evaled_roll.x + evaled_roll.mode + minStr + evaled_roll.max + ':[';
		return preamble + evaled_roll.rolls.join(', ') + ']';
	},

	stringify: function(evaled){
		if(evaled.expression){
			return dice.stringify_expression(evaled);
		}

		if(evaled.op){
			return dice.stringify_op(evaled);
		}

		if(evaled.rolls){
			return dice.stringify_rolls(evaled);
		}

		return evaled.toString();
	}

};
dice.parse = (function(){
  /*
   * Generated by PEG.js 0.7.0.
   *
   * http://pegjs.majda.cz/
   */
  
  function quote(s) {
    /*
     * ECMA-262, 5th ed., 7.8.4: All characters may appear literally in a
     * string literal except for the closing quote character, backslash,
     * carriage return, line separator, paragraph separator, and line feed.
     * Any character may appear in the form of an escape sequence.
     *
     * For portability, we also escape escape all control and non-ASCII
     * characters. Note that "\0" and "\v" escape sequences are not used
     * because JSHint does not like the first and IE the second.
     */
     return '"' + s
      .replace(/\\/g, '\\\\')  // backslash
      .replace(/"/g, '\\"')    // closing quote character
      .replace(/\x08/g, '\\b') // backspace
      .replace(/\t/g, '\\t')   // horizontal tab
      .replace(/\n/g, '\\n')   // line feed
      .replace(/\f/g, '\\f')   // form feed
      .replace(/\r/g, '\\r')   // carriage return
      .replace(/[\x00-\x07\x0B\x0E-\x1F\x80-\uFFFF]/g, escape)
      + '"';
  }
  
  var result = {
    /*
     * Parses the input with a generated parser. If the parsing is successfull,
     * returns a value explicitly or implicitly specified by the grammar from
     * which the parser was generated (see |PEG.buildParser|). If the parsing is
     * unsuccessful, throws |PEG.parser.SyntaxError| describing the error.
     */
    parse: function(input, startRule) {
      var parseFunctions = {
        "dicerolls": parse_dicerolls,
        "parenExpress": parse_parenExpress,
        "multiplicationOp": parse_multiplicationOp,
        "multiplicationSeq": parse_multiplicationSeq,
        "additionSeq": parse_additionSeq,
        "additionOp": parse_additionOp,
        "diceroll": parse_diceroll,
        "rollmode": parse_rollmode,
        "maybe_minmax": parse_maybe_minmax,
        "minmax": parse_minmax,
        "ws": parse_ws,
        "intval": parse_intval,
        "mathit": parse_mathit,
        "lookup": parse_lookup,
        "variable": parse_variable,
        "integer": parse_integer
      };
      
      if (startRule !== undefined) {
        if (parseFunctions[startRule] === undefined) {
          throw new Error("Invalid rule name: " + quote(startRule) + ".");
        }
      } else {
        startRule = "dicerolls";
      }
      
      var pos = { offset: 0, line: 1, column: 1, seenCR: false };
      var reportFailures = 0;
      var rightmostFailuresPos = { offset: 0, line: 1, column: 1, seenCR: false };
      var rightmostFailuresExpected = [];
      
      function padLeft(input, padding, length) {
        var result = input;
        
        var padLength = length - input.length;
        for (var i = 0; i < padLength; i++) {
          result = padding + result;
        }
        
        return result;
      }
      
      function escape(ch) {
        var charCode = ch.charCodeAt(0);
        var escapeChar;
        var length;
        
        if (charCode <= 0xFF) {
          escapeChar = 'x';
          length = 2;
        } else {
          escapeChar = 'u';
          length = 4;
        }
        
        return '\\' + escapeChar + padLeft(charCode.toString(16).toUpperCase(), '0', length);
      }
      
      function clone(object) {
        var result = {};
        for (var key in object) {
          result[key] = object[key];
        }
        return result;
      }
      
      function advance(pos, n) {
        var endOffset = pos.offset + n;
        
        for (var offset = pos.offset; offset < endOffset; offset++) {
          var ch = input.charAt(offset);
          if (ch === "\n") {
            if (!pos.seenCR) { pos.line++; }
            pos.column = 1;
            pos.seenCR = false;
          } else if (ch === "\r" || ch === "\u2028" || ch === "\u2029") {
            pos.line++;
            pos.column = 1;
            pos.seenCR = true;
          } else {
            pos.column++;
            pos.seenCR = false;
          }
        }
        
        pos.offset += n;
      }
      
      function matchFailed(failure) {
        if (pos.offset < rightmostFailuresPos.offset) {
          return;
        }
        
        if (pos.offset > rightmostFailuresPos.offset) {
          rightmostFailuresPos = clone(pos);
          rightmostFailuresExpected = [];
        }
        
        rightmostFailuresExpected.push(failure);
      }
      
      function parse_dicerolls() {
        var result0, result1, result2;
        var pos0, pos1;
        
        pos0 = clone(pos);
        pos1 = clone(pos);
        result0 = parse_multiplicationSeq();
        if (result0 !== null) {
          result1 = parse_additionOp();
          if (result1 !== null) {
            result2 = parse_additionSeq();
            if (result2 !== null) {
              result0 = [result0, result1, result2];
            } else {
              result0 = null;
              pos = clone(pos1);
            }
          } else {
            result0 = null;
            pos = clone(pos1);
          }
        } else {
          result0 = null;
          pos = clone(pos1);
        }
        if (result0 !== null) {
          result0 = (function(offset, line, column, v1, op, v2) { return {'op':op, args: [v1, v2] }; })(pos0.offset, pos0.line, pos0.column, result0[0], result0[1], result0[2]);
        }
        if (result0 === null) {
          pos = clone(pos0);
        }
        if (result0 === null) {
          pos0 = clone(pos);
          result0 = parse_additionSeq();
          if (result0 !== null) {
            result0 = (function(offset, line, column, out) { return out; })(pos0.offset, pos0.line, pos0.column, result0);
          }
          if (result0 === null) {
            pos = clone(pos0);
          }
          if (result0 === null) {
            pos0 = clone(pos);
            result0 = parse_parenExpress();
            if (result0 !== null) {
              result0 = (function(offset, line, column, out) { return out; })(pos0.offset, pos0.line, pos0.column, result0);
            }
            if (result0 === null) {
              pos = clone(pos0);
            }
          }
        }
        return result0;
      }
      
      function parse_parenExpress() {
        var result0, result1, result2, result3, result4;
        var pos0, pos1;
        
        pos0 = clone(pos);
        pos1 = clone(pos);
        if (input.charCodeAt(pos.offset) === 40) {
          result0 = "(";
          advance(pos, 1);
        } else {
          result0 = null;
          if (reportFailures === 0) {
            matchFailed("\"(\"");
          }
        }
        if (result0 !== null) {
          result1 = parse_ws();
          if (result1 !== null) {
            result2 = parse_dicerolls();
            if (result2 !== null) {
              result3 = parse_ws();
              if (result3 !== null) {
                if (input.charCodeAt(pos.offset) === 41) {
                  result4 = ")";
                  advance(pos, 1);
                } else {
                  result4 = null;
                  if (reportFailures === 0) {
                    matchFailed("\")\"");
                  }
                }
                if (result4 !== null) {
                  result0 = [result0, result1, result2, result3, result4];
                } else {
                  result0 = null;
                  pos = clone(pos1);
                }
              } else {
                result0 = null;
                pos = clone(pos1);
              }
            } else {
              result0 = null;
              pos = clone(pos1);
            }
          } else {
            result0 = null;
            pos = clone(pos1);
          }
        } else {
          result0 = null;
          pos = clone(pos1);
        }
        if (result0 !== null) {
          result0 = (function(offset, line, column, rolls) { return {'op':'paren_express', args: [rolls]} })(pos0.offset, pos0.line, pos0.column, result0[2]);
        }
        if (result0 === null) {
          pos = clone(pos0);
        }
        return result0;
      }
      
      function parse_multiplicationOp() {
        var result0, result1, result2;
        var pos0, pos1;
        
        pos0 = clone(pos);
        pos1 = clone(pos);
        result0 = parse_ws();
        if (result0 !== null) {
          if (input.charCodeAt(pos.offset) === 42) {
            result1 = "*";
            advance(pos, 1);
          } else {
            result1 = null;
            if (reportFailures === 0) {
              matchFailed("\"*\"");
            }
          }
          if (result1 !== null) {
            result2 = parse_ws();
            if (result2 !== null) {
              result0 = [result0, result1, result2];
            } else {
              result0 = null;
              pos = clone(pos1);
            }
          } else {
            result0 = null;
            pos = clone(pos1);
          }
        } else {
          result0 = null;
          pos = clone(pos1);
        }
        if (result0 !== null) {
          result0 = (function(offset, line, column) { return '*'; })(pos0.offset, pos0.line, pos0.column);
        }
        if (result0 === null) {
          pos = clone(pos0);
        }
        if (result0 === null) {
          pos0 = clone(pos);
          pos1 = clone(pos);
          result0 = parse_ws();
          if (result0 !== null) {
            if (input.charCodeAt(pos.offset) === 47) {
              result1 = "/";
              advance(pos, 1);
            } else {
              result1 = null;
              if (reportFailures === 0) {
                matchFailed("\"/\"");
              }
            }
            if (result1 !== null) {
              result2 = parse_ws();
              if (result2 !== null) {
                result0 = [result0, result1, result2];
              } else {
                result0 = null;
                pos = clone(pos1);
              }
            } else {
              result0 = null;
              pos = clone(pos1);
            }
          } else {
            result0 = null;
            pos = clone(pos1);
          }
          if (result0 !== null) {
            result0 = (function(offset, line, column) { return '/'; })(pos0.offset, pos0.line, pos0.column);
          }
          if (result0 === null) {
            pos = clone(pos0);
          }
        }
        return result0;
      }
      
      function parse_multiplicationSeq() {
        var result0, result1, result2;
        var pos0, pos1;
        
        pos0 = clone(pos);
        pos1 = clone(pos);
        result0 = parse_diceroll();
        if (result0 !== null) {
          result1 = parse_multiplicationOp();
          if (result1 !== null) {
            result2 = parse_multiplicationSeq();
            if (result2 !== null) {
              result0 = [result0, result1, result2];
            } else {
              result0 = null;
              pos = clone(pos1);
            }
          } else {
            result0 = null;
            pos = clone(pos1);
          }
        } else {
          result0 = null;
          pos = clone(pos1);
        }
        if (result0 !== null) {
          result0 = (function(offset, line, column, v1, op, v2) { return {'op':op, args:[v1, v2] }; })(pos0.offset, pos0.line, pos0.column, result0[0], result0[1], result0[2]);
        }
        if (result0 === null) {
          pos = clone(pos0);
        }
        if (result0 === null) {
          pos0 = clone(pos);
          pos1 = clone(pos);
          result0 = parse_parenExpress();
          if (result0 !== null) {
            result1 = parse_multiplicationOp();
            if (result1 !== null) {
              result2 = parse_multiplicationSeq();
              if (result2 !== null) {
                result0 = [result0, result1, result2];
              } else {
                result0 = null;
                pos = clone(pos1);
              }
            } else {
              result0 = null;
              pos = clone(pos1);
            }
          } else {
            result0 = null;
            pos = clone(pos1);
          }
          if (result0 !== null) {
            result0 = (function(offset, line, column, v1, op, v2) { return {'op':op, args:[v1, v2] }; })(pos0.offset, pos0.line, pos0.column, result0[0], result0[1], result0[2]);
          }
          if (result0 === null) {
            pos = clone(pos0);
          }
          if (result0 === null) {
            pos0 = clone(pos);
            result0 = parse_diceroll();
            if (result0 !== null) {
              result0 = (function(offset, line, column, d) { return d; })(pos0.offset, pos0.line, pos0.column, result0);
            }
            if (result0 === null) {
              pos = clone(pos0);
            }
            if (result0 === null) {
              pos0 = clone(pos);
              result0 = parse_parenExpress();
              if (result0 !== null) {
                result0 = (function(offset, line, column, paren) { return paren; })(pos0.offset, pos0.line, pos0.column, result0);
              }
              if (result0 === null) {
                pos = clone(pos0);
              }
            }
          }
        }
        return result0;
      }
      
      function parse_additionSeq() {
        var result0, result1, result2;
        var pos0, pos1;
        
        pos0 = clone(pos);
        pos1 = clone(pos);
        result0 = parse_multiplicationSeq();
        if (result0 !== null) {
          result1 = parse_additionOp();
          if (result1 !== null) {
            result2 = parse_additionSeq();
            if (result2 !== null) {
              result0 = [result0, result1, result2];
            } else {
              result0 = null;
              pos = clone(pos1);
            }
          } else {
            result0 = null;
            pos = clone(pos1);
          }
        } else {
          result0 = null;
          pos = clone(pos1);
        }
        if (result0 !== null) {
          result0 = (function(offset, line, column, v1, op, v2) { return {'op':op, args:[v1, v2]}; })(pos0.offset, pos0.line, pos0.column, result0[0], result0[1], result0[2]);
        }
        if (result0 === null) {
          pos = clone(pos0);
        }
        if (result0 === null) {
          pos0 = clone(pos);
          result0 = parse_multiplicationSeq();
          if (result0 !== null) {
            result0 = (function(offset, line, column, op) { return op; })(pos0.offset, pos0.line, pos0.column, result0);
          }
          if (result0 === null) {
            pos = clone(pos0);
          }
          if (result0 === null) {
            pos0 = clone(pos);
            result0 = parse_parenExpress();
            if (result0 !== null) {
              result0 = (function(offset, line, column, op) { return op; })(pos0.offset, pos0.line, pos0.column, result0);
            }
            if (result0 === null) {
              pos = clone(pos0);
            }
          }
        }
        return result0;
      }
      
      function parse_additionOp() {
        var result0, result1, result2;
        var pos0, pos1;
        
        pos0 = clone(pos);
        pos1 = clone(pos);
        result0 = parse_ws();
        if (result0 !== null) {
          if (input.charCodeAt(pos.offset) === 43) {
            result1 = "+";
            advance(pos, 1);
          } else {
            result1 = null;
            if (reportFailures === 0) {
              matchFailed("\"+\"");
            }
          }
          if (result1 !== null) {
            result2 = parse_ws();
            if (result2 !== null) {
              result0 = [result0, result1, result2];
            } else {
              result0 = null;
              pos = clone(pos1);
            }
          } else {
            result0 = null;
            pos = clone(pos1);
          }
        } else {
          result0 = null;
          pos = clone(pos1);
        }
        if (result0 !== null) {
          result0 = (function(offset, line, column) { return "+"; })(pos0.offset, pos0.line, pos0.column);
        }
        if (result0 === null) {
          pos = clone(pos0);
        }
        if (result0 === null) {
          pos0 = clone(pos);
          pos1 = clone(pos);
          result0 = parse_ws();
          if (result0 !== null) {
            if (input.charCodeAt(pos.offset) === 45) {
              result1 = "-";
              advance(pos, 1);
            } else {
              result1 = null;
              if (reportFailures === 0) {
                matchFailed("\"-\"");
              }
            }
            if (result1 !== null) {
              result2 = parse_ws();
              if (result2 !== null) {
                result0 = [result0, result1, result2];
              } else {
                result0 = null;
                pos = clone(pos1);
              }
            } else {
              result0 = null;
              pos = clone(pos1);
            }
          } else {
            result0 = null;
            pos = clone(pos1);
          }
          if (result0 !== null) {
            result0 = (function(offset, line, column) { return "-"; })(pos0.offset, pos0.line, pos0.column);
          }
          if (result0 === null) {
            pos = clone(pos0);
          }
        }
        return result0;
      }
      
      function parse_diceroll() {
        var result0, result1, result2;
        var pos0, pos1;
        
        pos0 = clone(pos);
        pos1 = clone(pos);
        result0 = parse_intval();
        if (result0 !== null) {
          result1 = parse_rollmode();
          if (result1 !== null) {
            result2 = parse_maybe_minmax();
            if (result2 !== null) {
              result0 = [result0, result1, result2];
            } else {
              result0 = null;
              pos = clone(pos1);
            }
          } else {
            result0 = null;
            pos = clone(pos1);
          }
        } else {
          result0 = null;
          pos = clone(pos1);
        }
        if (result0 !== null) {
          result0 = (function(offset, line, column, x, mode, mm) { return {'op':mode, 'args':[x, mm]} })(pos0.offset, pos0.line, pos0.column, result0[0], result0[1], result0[2]);
        }
        if (result0 === null) {
          pos = clone(pos0);
        }
        if (result0 === null) {
          pos0 = clone(pos);
          pos1 = clone(pos);
          result0 = parse_rollmode();
          if (result0 !== null) {
            result1 = parse_maybe_minmax();
            if (result1 !== null) {
              result0 = [result0, result1];
            } else {
              result0 = null;
              pos = clone(pos1);
            }
          } else {
            result0 = null;
            pos = clone(pos1);
          }
          if (result0 !== null) {
            result0 = (function(offset, line, column, mode, mm) { return {'op':mode, 'args':[1, mm]}; })(pos0.offset, pos0.line, pos0.column, result0[0], result0[1]);
          }
          if (result0 === null) {
            pos = clone(pos0);
          }
          if (result0 === null) {
            pos0 = clone(pos);
            result0 = parse_minmax();
            if (result0 !== null) {
              result0 = (function(offset, line, column, mm) { return { 'op':'d', 'args':[ {'op':'static', 'value':1}, mm]}; })(pos0.offset, pos0.line, pos0.column, result0);
            }
            if (result0 === null) {
              pos = clone(pos0);
            }
            if (result0 === null) {
              pos0 = clone(pos);
              result0 = parse_intval();
              if (result0 !== null) {
                result0 = (function(offset, line, column, x) { return x; })(pos0.offset, pos0.line, pos0.column, result0);
              }
              if (result0 === null) {
                pos = clone(pos0);
              }
            }
          }
        }
        return result0;
      }
      
      function parse_rollmode() {
        var result0;
        
        if (input.charCodeAt(pos.offset) === 100) {
          result0 = "d";
          advance(pos, 1);
        } else {
          result0 = null;
          if (reportFailures === 0) {
            matchFailed("\"d\"");
          }
        }
        if (result0 === null) {
          if (input.charCodeAt(pos.offset) === 119) {
            result0 = "w";
            advance(pos, 1);
          } else {
            result0 = null;
            if (reportFailures === 0) {
              matchFailed("\"w\"");
            }
          }
        }
        return result0;
      }
      
      function parse_maybe_minmax() {
        var result0;
        var pos0;
        
        pos0 = clone(pos);
        result0 = parse_minmax();
        if (result0 !== null) {
          result0 = (function(offset, line, column, mm) { return mm; })(pos0.offset, pos0.line, pos0.column, result0);
        }
        if (result0 === null) {
          pos = clone(pos0);
        }
        if (result0 === null) {
          pos0 = clone(pos);
          result0 = parse_intval();
          if (result0 !== null) {
            result0 = (function(offset, line, column, max) { return {'op':'random', 'args':[{'op':'static', 'args':[], 'value':1}, max]}; })(pos0.offset, pos0.line, pos0.column, result0);
          }
          if (result0 === null) {
            pos = clone(pos0);
          }
        }
        return result0;
      }
      
      function parse_minmax() {
        var result0, result1, result2;
        var pos0, pos1;
        
        pos0 = clone(pos);
        pos1 = clone(pos);
        result0 = parse_intval();
        if (result0 !== null) {
          if (input.substr(pos.offset, 2) === "..") {
            result1 = "..";
            advance(pos, 2);
          } else {
            result1 = null;
            if (reportFailures === 0) {
              matchFailed("\"..\"");
            }
          }
          if (result1 !== null) {
            result2 = parse_intval();
            if (result2 !== null) {
              result0 = [result0, result1, result2];
            } else {
              result0 = null;
              pos = clone(pos1);
            }
          } else {
            result0 = null;
            pos = clone(pos1);
          }
        } else {
          result0 = null;
          pos = clone(pos1);
        }
        if (result0 !== null) {
          result0 = (function(offset, line, column, min, max) { return {'op':'random', 'args':[min, max]}; })(pos0.offset, pos0.line, pos0.column, result0[0], result0[2]);
        }
        if (result0 === null) {
          pos = clone(pos0);
        }
        return result0;
      }
      
      function parse_ws() {
        var result0, result1;
        
        result0 = [];
        if (/^[ ]/.test(input.charAt(pos.offset))) {
          result1 = input.charAt(pos.offset);
          advance(pos, 1);
        } else {
          result1 = null;
          if (reportFailures === 0) {
            matchFailed("[ ]");
          }
        }
        while (result1 !== null) {
          result0.push(result1);
          if (/^[ ]/.test(input.charAt(pos.offset))) {
            result1 = input.charAt(pos.offset);
            advance(pos, 1);
          } else {
            result1 = null;
            if (reportFailures === 0) {
              matchFailed("[ ]");
            }
          }
        }
        return result0;
      }
      
      function parse_intval() {
        var result0, result1;
        var pos0, pos1;
        
        pos0 = clone(pos);
        result0 = parse_integer();
        if (result0 !== null) {
          result0 = (function(offset, line, column, i) {
        		return {'op': 'static', value: i};
        	})(pos0.offset, pos0.line, pos0.column, result0);
        }
        if (result0 === null) {
          pos = clone(pos0);
        }
        if (result0 === null) {
          pos0 = clone(pos);
          result0 = parse_lookup();
          if (result0 !== null) {
            result0 = (function(offset, line, column, l) {
          		return l;
          	})(pos0.offset, pos0.line, pos0.column, result0);
          }
          if (result0 === null) {
            pos = clone(pos0);
          }
          if (result0 === null) {
            pos0 = clone(pos);
            pos1 = clone(pos);
            result0 = parse_mathit();
            if (result0 !== null) {
              result1 = parse_lookup();
              if (result1 !== null) {
                result0 = [result0, result1];
              } else {
                result0 = null;
                pos = clone(pos1);
              }
            } else {
              result0 = null;
              pos = clone(pos1);
            }
            if (result0 !== null) {
              result0 = (function(offset, line, column, f, v) {
            		return {'op':f, args:[v]};
            	})(pos0.offset, pos0.line, pos0.column, result0[0], result0[1]);
            }
            if (result0 === null) {
              pos = clone(pos0);
            }
            if (result0 === null) {
              pos0 = clone(pos);
              pos1 = clone(pos);
              result0 = parse_mathit();
              if (result0 !== null) {
                result1 = parse_parenExpress();
                if (result1 !== null) {
                  result0 = [result0, result1];
                } else {
                  result0 = null;
                  pos = clone(pos1);
                }
              } else {
                result0 = null;
                pos = clone(pos1);
              }
              if (result0 !== null) {
                result0 = (function(offset, line, column, f, ex) {
                  return {'op':f, args:ex.args};
              	})(pos0.offset, pos0.line, pos0.column, result0[0], result0[1]);
              }
              if (result0 === null) {
                pos = clone(pos0);
              }
            }
          }
        }
        return result0;
      }
      
      function parse_mathit() {
        var result0;
        var pos0;
        
        pos0 = clone(pos);
        if (input.charCodeAt(pos.offset) === 102) {
          result0 = "f";
          advance(pos, 1);
        } else {
          result0 = null;
          if (reportFailures === 0) {
            matchFailed("\"f\"");
          }
        }
        if (result0 !== null) {
          result0 = (function(offset, line, column) { return 'floor'; })(pos0.offset, pos0.line, pos0.column);
        }
        if (result0 === null) {
          pos = clone(pos0);
        }
        if (result0 === null) {
          pos0 = clone(pos);
          if (input.charCodeAt(pos.offset) === 114) {
            result0 = "r";
            advance(pos, 1);
          } else {
            result0 = null;
            if (reportFailures === 0) {
              matchFailed("\"r\"");
            }
          }
          if (result0 !== null) {
            result0 = (function(offset, line, column) { return 'round'; })(pos0.offset, pos0.line, pos0.column);
          }
          if (result0 === null) {
            pos = clone(pos0);
          }
          if (result0 === null) {
            pos0 = clone(pos);
            if (input.charCodeAt(pos.offset) === 99) {
              result0 = "c";
              advance(pos, 1);
            } else {
              result0 = null;
              if (reportFailures === 0) {
                matchFailed("\"c\"");
              }
            }
            if (result0 !== null) {
              result0 = (function(offset, line, column) { return 'ceil'; })(pos0.offset, pos0.line, pos0.column);
            }
            if (result0 === null) {
              pos = clone(pos0);
            }
          }
        }
        return result0;
      }
      
      function parse_lookup() {
        var result0;
        var pos0;
        
        pos0 = clone(pos);
        result0 = parse_variable();
        if (result0 !== null) {
          result0 = (function(offset, line, column, v) { return {'op':'lookup', 'value':v}; })(pos0.offset, pos0.line, pos0.column, result0);
        }
        if (result0 === null) {
          pos = clone(pos0);
        }
        return result0;
      }
      
      function parse_variable() {
        var result0, result1, result2;
        var pos0, pos1;
        
        pos0 = clone(pos);
        pos1 = clone(pos);
        if (input.charCodeAt(pos.offset) === 91) {
          result0 = "[";
          advance(pos, 1);
        } else {
          result0 = null;
          if (reportFailures === 0) {
            matchFailed("\"[\"");
          }
        }
        if (result0 !== null) {
          if (/^[a-zA-Z 0-9]/.test(input.charAt(pos.offset))) {
            result2 = input.charAt(pos.offset);
            advance(pos, 1);
          } else {
            result2 = null;
            if (reportFailures === 0) {
              matchFailed("[a-zA-Z 0-9]");
            }
          }
          if (result2 !== null) {
            result1 = [];
            while (result2 !== null) {
              result1.push(result2);
              if (/^[a-zA-Z 0-9]/.test(input.charAt(pos.offset))) {
                result2 = input.charAt(pos.offset);
                advance(pos, 1);
              } else {
                result2 = null;
                if (reportFailures === 0) {
                  matchFailed("[a-zA-Z 0-9]");
                }
              }
            }
          } else {
            result1 = null;
          }
          if (result1 !== null) {
            if (input.charCodeAt(pos.offset) === 93) {
              result2 = "]";
              advance(pos, 1);
            } else {
              result2 = null;
              if (reportFailures === 0) {
                matchFailed("\"]\"");
              }
            }
            if (result2 !== null) {
              result0 = [result0, result1, result2];
            } else {
              result0 = null;
              pos = clone(pos1);
            }
          } else {
            result0 = null;
            pos = clone(pos1);
          }
        } else {
          result0 = null;
          pos = clone(pos1);
        }
        if (result0 !== null) {
          result0 = (function(offset, line, column, varname) { return varname.join(""); })(pos0.offset, pos0.line, pos0.column, result0[1]);
        }
        if (result0 === null) {
          pos = clone(pos0);
        }
        return result0;
      }
      
      function parse_integer() {
        var result0, result1, result2;
        var pos0, pos1;
        
        reportFailures++;
        pos0 = clone(pos);
        if (/^[0-9]/.test(input.charAt(pos.offset))) {
          result1 = input.charAt(pos.offset);
          advance(pos, 1);
        } else {
          result1 = null;
          if (reportFailures === 0) {
            matchFailed("[0-9]");
          }
        }
        if (result1 !== null) {
          result0 = [];
          while (result1 !== null) {
            result0.push(result1);
            if (/^[0-9]/.test(input.charAt(pos.offset))) {
              result1 = input.charAt(pos.offset);
              advance(pos, 1);
            } else {
              result1 = null;
              if (reportFailures === 0) {
                matchFailed("[0-9]");
              }
            }
          }
        } else {
          result0 = null;
        }
        if (result0 !== null) {
          result0 = (function(offset, line, column, digits) { return parseInt(digits.join(""), 10); })(pos0.offset, pos0.line, pos0.column, result0);
        }
        if (result0 === null) {
          pos = clone(pos0);
        }
        if (result0 === null) {
          pos0 = clone(pos);
          pos1 = clone(pos);
          if (input.charCodeAt(pos.offset) === 45) {
            result0 = "-";
            advance(pos, 1);
          } else {
            result0 = null;
            if (reportFailures === 0) {
              matchFailed("\"-\"");
            }
          }
          if (result0 !== null) {
            if (/^[0-9]/.test(input.charAt(pos.offset))) {
              result2 = input.charAt(pos.offset);
              advance(pos, 1);
            } else {
              result2 = null;
              if (reportFailures === 0) {
                matchFailed("[0-9]");
              }
            }
            if (result2 !== null) {
              result1 = [];
              while (result2 !== null) {
                result1.push(result2);
                if (/^[0-9]/.test(input.charAt(pos.offset))) {
                  result2 = input.charAt(pos.offset);
                  advance(pos, 1);
                } else {
                  result2 = null;
                  if (reportFailures === 0) {
                    matchFailed("[0-9]");
                  }
                }
              }
            } else {
              result1 = null;
            }
            if (result1 !== null) {
              result0 = [result0, result1];
            } else {
              result0 = null;
              pos = clone(pos1);
            }
          } else {
            result0 = null;
            pos = clone(pos1);
          }
          if (result0 !== null) {
            result0 = (function(offset, line, column, digits) { return parseInt(digits.join(""), 10) * -1; })(pos0.offset, pos0.line, pos0.column, result0[1]);
          }
          if (result0 === null) {
            pos = clone(pos0);
          }
        }
        reportFailures--;
        if (reportFailures === 0 && result0 === null) {
          matchFailed("integer");
        }
        return result0;
      }
      
      
      function cleanupExpected(expected) {
        expected.sort();
        
        var lastExpected = null;
        var cleanExpected = [];
        for (var i = 0; i < expected.length; i++) {
          if (expected[i] !== lastExpected) {
            cleanExpected.push(expected[i]);
            lastExpected = expected[i];
          }
        }
        return cleanExpected;
      }
      
      
      
      var result = parseFunctions[startRule]();
      
      /*
       * The parser is now in one of the following three states:
       *
       * 1. The parser successfully parsed the whole input.
       *
       *    - |result !== null|
       *    - |pos.offset === input.length|
       *    - |rightmostFailuresExpected| may or may not contain something
       *
       * 2. The parser successfully parsed only a part of the input.
       *
       *    - |result !== null|
       *    - |pos.offset < input.length|
       *    - |rightmostFailuresExpected| may or may not contain something
       *
       * 3. The parser did not successfully parse any part of the input.
       *
       *   - |result === null|
       *   - |pos.offset === 0|
       *   - |rightmostFailuresExpected| contains at least one failure
       *
       * All code following this comment (including called functions) must
       * handle these states.
       */
      if (result === null || pos.offset !== input.length) {
        var offset = Math.max(pos.offset, rightmostFailuresPos.offset);
        var found = offset < input.length ? input.charAt(offset) : null;
        var errorPosition = pos.offset > rightmostFailuresPos.offset ? pos : rightmostFailuresPos;
        
        throw new this.SyntaxError(
          cleanupExpected(rightmostFailuresExpected),
          found,
          offset,
          errorPosition.line,
          errorPosition.column
        );
      }
      
      return result;
    },
    
    /* Returns the parser source code. */
    toSource: function() { return this._source; }
  };
  
  /* Thrown when a parser encounters a syntax error. */
  
  result.SyntaxError = function(expected, found, offset, line, column) {
    function buildMessage(expected, found) {
      var expectedHumanized, foundHumanized;
      
      switch (expected.length) {
        case 0:
          expectedHumanized = "end of input";
          break;
        case 1:
          expectedHumanized = expected[0];
          break;
        default:
          expectedHumanized = expected.slice(0, expected.length - 1).join(", ")
            + " or "
            + expected[expected.length - 1];
      }
      
      foundHumanized = found ? quote(found) : "end of input";
      
      return "Expected " + expectedHumanized + " but " + foundHumanized + " found.";
    }
    
    this.name = "SyntaxError";
    this.expected = expected;
    this.found = found;
    this.message = buildMessage(expected, found);
    this.offset = offset;
    this.line = line;
    this.column = column;
  };
  
  result.SyntaxError.prototype = Error.prototype;
  
  return result;
})();
dice.eval = (function(){

	function makeSeq(endIndex){
		var seq = [];
		seq[endIndex] = true;
		for(var i = 0; i < seq.length; i++){
			seq[i] = true;
		}
		return seq;
	};

	var ops = {

		'static': function(){
			var outValue = this.value;
			return function(){
				return outValue;
			};
		},

		'lookup': function(){
			var variableName = this.value;
			return function(scope){
				return scope[variableName];
			}
		},

		'floor': function(value){
			return function(scope){
				var floorable = value(scope);
				var tots = new Number(Math.floor(floorable));
				tots.op = 'floor';
				tots.expression = floorable;
				return tots;
			}
		},

		'ceil': function(value){
			return function(scope){
				var ceilable = value(scope);
				var tots = new Number(Math.ceil(ceilable));
				tots.op = 'ceil';
				tots.expression = ceilable;
				return tots;
			}
		},

		'round': function(value){
			return function(scope){
				var roundable = value(scope);
				var tots = new Number(Math.round(roundable));
				tots.op = 'round';
				tots.expression = roundable;
				return tots;
			}
		},

		'd': function(numRolls, minMax){
			return function(scope){
				var x = numRolls(scope);
				var seq = makeSeq(x - 1);
				var outMin, outMax;
				var rolled = seq.map(function(){
					var rolledRet = minMax(scope);
					outMin = rolledRet.min;
					outMax = rolledRet.max;
					return rolledRet;
				});
				var out = rolled.reduce(function(sum, val){
					return sum + val;
				}, 0);
				out = new Number(out);
				out.rolls = rolled;
				out.min = outMin;
				out.max = outMax;
				out.x = x;
				out.mode = 'd';
				return out;
			};
		},

		'w': function(numRolls, minMax){
			return function(scope){
				var x = numRolls(scope);
				var seq = makeSeq(x - 1);
				var outMin, outMax;
				var rolled = seq.map(function(){
					var lastRolled = minMax(scope);
					var wildrolled = 0;
					outMin = minMax.min;
					outMax = minMax.max;
					if(minMax.min === minMax.max){
						return lastRolled;
					}
					while(lastRolled === minMax.max){
						wildrolled += lastRolled;
						lastRolled = mimMax(scope);
					}
					return wildrolled;
				});
				var out = rolled.reduce(function(sum, val){
					return sum + val;
				}, 0);
				out = new Number(out);
				out.rolls = rolled;
				out.min = outMin;
				out.max = outMax;
				out.x = x;
				out.mode = 'w';
				return out;
			};
		},

		'random': function(minFun, maxFun){
			return function(scope){
				var rawRandom = Math.random();
				var max = maxFun(scope);
				var min = minFun(scope);
				var diff = max - min;
				var rawRandom = diff * rawRandom;
				var rndNumber = Math.round(rawRandom + min);
				rndNumber = new Number(rndNumber);
				rndNumber.min = min;
				rndNumber.max = max;
				return rndNumber;
			};
		},

		'+': function(v1, v2){
			return function(scope){
				var rightSide = v1(scope);
				var leftSide = v2(scope);
				var sum = rightSide + leftSide;
				sum = new Number(sum);
				sum.op = '+';
				sum.rightSide = rightSide;
				sum.leftSide = leftSide;
				return sum;
			};
		},

		'-': function(v1, v2){
			return function(scope){
				var rightSide = v1(scope);
				var leftSide = v2(scope);
				var sum = rightSide - leftSide;
				sum = new Number(sum);
				sum.op = '-';
				sum.rightSide = rightSide;
				sum.leftSide = leftSide;
				return sum;
			};
		},

		'*': function(v1, v2){
			return function(scope){
				var rightSide = v1(scope);
				var leftSide = v2(scope);
				var tots = rightSide * leftSide;
				tots = new Number(tots);
				tots.op = '*';
				tots.rightSide = rightSide;
				tots.leftSide = leftSide;
				return tots;
			};
		},

		'/': function(v1, v2){
			return function(scope){
				var rightSide = v1(scope);
				var leftSide = v2(scope);
				var tots = rightSide / leftSide;
				tots = new Number(tots);
				tots.op = '/';
				tots.rightSide = rightSide;
				tots.leftSide = leftSide;
				return tots;
			};
		},

		'paren_express': function(op){
			return function(scope){
				var tots = op(scope);
				outtots = new Number(tots);
				outtots.op = 'paren_express';
				outtots.expression = tots;
				return outtots;
			};
		}

	};

	function resolve_ops(args){
		args = args || [];
		return args.map(resolve_op);
	};

	function resolve_op(opObj){
		var subArgs = resolve_ops(opObj.args);
		return ops[opObj.op].apply(opObj, subArgs);
	};

	var result = {

		eval: function(parsed, scope){
			scope = scope || {};
			var ops = resolve_op(parsed)
			return ops(scope);

			/*var acc = {sum: 0, mode: "+", rolls: [], 'scope':scope}
			var reduced = parsed.reduce(reduceThemBones, acc);
			return {sum: reduced.sum, rolls: reduced.rolls};*/
		},

		'ops':ops
	}

	return result;

})();
