Skip to content

Instantly share code, notes, and snippets.

@jdavidw13
Forked from Zirak/gist:1761880
Created March 22, 2012 06:03
Show Gist options
  • Select an option

  • Save jdavidw13/2156565 to your computer and use it in GitHub Desktop.

Select an option

Save jdavidw13/2156565 to your computer and use it in GitHub Desktop.
A parser for DnD dice rolls, with support for min/max rolls.
//infix operator-precedence parser, supporting d, + and -, where d is a dice
// roll
var parse = (function () {
//the operators we deal with and their precedence
var operators = {
'+' : 1,
'-' : 1,
'd' : 2
};
//object with keys 0-9 set to true
var digits = [
true, true, true, true, true,
true, true, true, true, true
];
//we don't care about whitespace. well, most whitespace
var whitespace = {
' ' : true,
'\t' : true
};
var callbacks = {
'+' : function ( a, b ) {
return a + b;
},
'-' : function ( a, b ) {
return a - b;
},
'd' : function ( rolls, sides ) {
var ret = 0;
while ( rolls-- ) {
ret += Math.floor( Math.random() * sides ) + 1;
}
return ret;
}
};
return function ( source ) {
var numberStack = [],
operatorStack = [];
//remove ALL whitespace!
//could be achieved by:
// source = source.replace( /\s/g, '' )
//but who cares?
Object.keys( whitespace ).forEach(function ( whitey ) {
source = source.replace( whitey, '' );
});
var token, last;
for ( var pos = 0, len = source.length; pos < len; ++pos ) {
token = nextToken();
if ( token.type === 'number' ) {
numberStack.push( token.value );
}
else if ( token.type === 'operator' ) {
last = operatorStack[ operatorStack.length - 1 ];
if (
//previous operator is more important than us
(last && token.precedence < last.precedence) ||
//or, we're about to finish
pos + 1 === len
) {
operate();
}
operatorStack.push( token );
}
}
//by now, operatorStack will only have operators of equal, lowest
// precedence, so we just need to go over the operator stack and execute
while ( operatorStack.length ) {
operate();
}
//the last number in the stack is the result
return numberStack[ 0 ];
//get the next token
function nextToken () {
var ch = source[ pos ];
var ret = {
type : null,
value : ch
},
res;
//have we overflowed, while looking for something else?
if ( pos >= len ) {
throw new Error( 'Unexpected end of input' );
}
//is it a digit?
else if ( digits.hasOwnProperty(ch) ) {
ret.type = 'number';
res = getNumber();
pos += res.length;
ret.value = res.value;
}
//is it an operator?
else if ( operators.hasOwnProperty(ch) ) {
ret.type = 'operator';
ret.precedence = operators[ ch ];
}
return ret;
}
function getNumber () {
var offset = 0, num = 0;
//keep eating digits until we find a non-digit
while ( digits.hasOwnProperty(source[pos+offset]) ) {
num *= 10;
num += Number( source[pos+offset] );
offset++;
}
return {
value : num,
length : offset - 1
};
}
function operate () {
var operator = operatorStack.pop().value,
method = callbacks[ operator ],
couplet = popTwo();
if ( couplet.indexOf(undefined) > -1 ) {
throw new Error( 'Incomplete expression - expected number' );
}
numberStack.push(
callbacks[ operatorStack.pop().value ].apply( null, couplet )
);
}
function popTwo () {
//because we're going left->right, something like:
// 4 + 1
//will be displayed in the stack like:
// [1, 4]
//so after grabbing the topmost numbers, we need to flip them
return [ numberStack.pop(), numberStack.pop() ].reverse();
}
};
}());
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment