Commit defbbe20 authored by Jeremy Soller's avatar Jeremy Soller
Browse files

Merge branch 'add-dice' into 'master'

Add support for the dice operator.

See merge request !21
parents 26bce399 c1ad0705
Pipeline #2141 passed with stage
in 2 minutes and 4 seconds
......@@ -65,6 +65,7 @@ dependencies = [
"failure 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"liner 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"num 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
......@@ -91,6 +92,14 @@ dependencies = [
"vec_map 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "cloudabi"
version = "0.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bitflags 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "decimal"
version = "2.0.0"
......@@ -131,6 +140,15 @@ dependencies = [
"fuchsia-zircon-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "fuchsia-zircon"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bitflags 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "fuchsia-zircon-sys"
version = "0.2.0"
......@@ -139,6 +157,11 @@ dependencies = [
"bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "fuchsia-zircon-sys"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "gcc"
version = "0.3.54"
......@@ -253,6 +276,71 @@ dependencies = [
"libc 0.2.32 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rand"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
"fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.32 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_chacha 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_pcg 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_xorshift 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rand_chacha"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rand_core"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "rand_hc"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rand_isaac"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rand_pcg"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rand_xorshift"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "redox_syscall"
version = "0.1.31"
......@@ -276,6 +364,27 @@ name = "rustc-serialize"
version = "0.3.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "rustc_version"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "semver"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "semver-parser"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "serde"
version = "1.0.16"
......@@ -387,11 +496,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum cc 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)" = "8b9d2900f78631a5876dc5d6c9033ede027253efcd33dd36b1309fc6cab97ee0"
"checksum cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d4c819a1287eb618df47cc647173c5c4c66ba19d888a6e50d605672aed3140de"
"checksum clap 2.27.0 (registry+https://github.com/rust-lang/crates.io-index)" = "260aa209e1e7165920b6925d508cd73674a4adeb4086197385968c70ecd14947"
"checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
"checksum decimal 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a8e3bfe070d7876527c8f901afc27b4190a715b6e52c8961f72fb35430960e80"
"checksum failure 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "934799b6c1de475a012a02dab0ace1ace43789ee4b99bcfbf1a2e3e8ced5de82"
"checksum failure_derive 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c7cdda555bb90c9bb67a3b670a0f42de8e73f5981524123ad8578aafec8ddb8b"
"checksum fuchsia-zircon 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f6c0581a4e363262e52b87f59ee2afe3415361c6ec35e665924eb08afe8ff159"
"checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82"
"checksum fuchsia-zircon-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "43f3795b4bae048dc6123a6b972cadde2e676f9ded08aef6bb77f5f157684a82"
"checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
"checksum gcc 0.3.54 (registry+https://github.com/rust-lang/crates.io-index)" = "5e33ec290da0d127825013597dbdfc28bee4964690c7ce1166cbc2a7bd08b1bb"
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
"checksum libc 0.2.32 (registry+https://github.com/rust-lang/crates.io-index)" = "56cce3130fd040c28df6f495c8492e5ec5808fb4c9093c310df02b0c8f030148"
......@@ -406,10 +518,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum ord_subset 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bdb8b8c275824bc609c8294f20a55d37d808a1b071cec596da746ce4df0405e8"
"checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a"
"checksum rand 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)" = "6475140dfd8655aeb72e1fd4b7a1cc1c202be65d71669476e392fe62532b9edd"
"checksum rand 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ae9d223d52ae411a33cf7e54ec6034ec165df296ccd23533d671a28252b6f66a"
"checksum rand_chacha 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "771b009e3a508cb67e8823dda454aaa5368c7bc1c16829fb77d3e980440dd34a"
"checksum rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0905b6b7079ec73b314d4c748701f6931eb79fd97c668caa3f1899b22b32c6db"
"checksum rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4"
"checksum rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08"
"checksum rand_pcg 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "086bd09a33c7044e56bb44d5bdde5a60e7f119a9e95b0775f545de759a32fe05"
"checksum rand_xorshift 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "effa3fcaa47e18db002bdde6060944b6d2f9cfd8db471c30e873448ad9187be3"
"checksum redox_syscall 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)" = "8dde11f18c108289bef24469638a04dce49da56084f2d50618b226e47eb04509"
"checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76"
"checksum rustc-demangle 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "11fb43a206a04116ffd7cfcf9bcb941f8eb6cc7ff667272246b0a1c74259a3cb"
"checksum rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)" = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda"
"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
"checksum serde 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)" = "e11a631f964d4e6572712ea12075fb1d65eeef42b0058884195b430ac1e26809"
"checksum strsim 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b4d15c810519a91cf877e7e36e63fe068815c678181439f2f29e2562147c3694"
"checksum syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad"
......
......@@ -31,6 +31,7 @@ decimal = "2.0.0"
failure = "0.1.1"
liner = "0.4.2"
num = "0.1"
rand = "0.6.1"
[lib]
name = "calc"
......
......@@ -74,6 +74,7 @@ pub enum CalcError {
WouldOverflow(PartialComp),
WouldTruncate(PartialComp),
RecursionLimitReached,
ImpossibleDice,
}
use CalcError::*;
......@@ -107,6 +108,7 @@ impl fmt::Display for CalcError {
UnexpectedEndOfInput => write!(f, "unexpected end of input"),
UnmatchedParenthesis => write!(f, "unmatched patenthesis"),
RecursionLimitReached => write!(f, "recursion limit reached"),
ImpossibleDice => write!(f, "impossible dice"),
}
}
}
......
......@@ -7,6 +7,7 @@ extern crate failure;
extern crate num;
#[cfg(test)]
extern crate test;
extern crate rand;
#[cfg(test)]
mod bench;
......
use error::CalcError;
use token::*;
use value::{Value, IR};
use rand::Rng;
const RECURSION_LIMIT: usize = 10;
......@@ -137,6 +138,21 @@ where
f1.value = (f1.value % f2.value)?;
f1.tokens += f2.tokens + 1;
}
Token::Dice => {
let f2 = f_expr(&token_list[index + 1..], env)?;
let dice_rolls:i32 = f1.value.as_float()?.into();
let dice_max:i32 = f2.value.as_float()?.into();
if dice_rolls < 1 || dice_max < 1 {
return Err(CalcError::ImpossibleDice);
}
let mut dice_result = 0;
let mut rng = rand::thread_rng();
for _i in 0..dice_rolls {
dice_result += rng.gen_range(1, dice_max+1);
}
f1.value = Value::dec(dice_result);
f1.tokens += f2.tokens + 1;
}
Token::Number(ref n) => {
return Err(CalcError::UnexpectedToken(
n.to_string(),
......@@ -334,6 +350,19 @@ mod tests {
assert_eq!(super::parse(&expr, &mut env), Ok(expected));
}
#[test]
fn dice_parse() {
let expr = [
Token::Number(Value::dec(3)),
Token::Dice,
Token::Number(Value::dec(6)),
];
let mut env = DefaultEnvironment::new();
let out = super::parse(&expr, &mut env);
let out_float = out.unwrap().as_float().unwrap();
assert!(out_float >= d128!(3.0) && out_float <= d128!(18.0));
}
#[test]
fn function_binding() {
let expr = [
......
......@@ -25,6 +25,7 @@ pub enum Token {
Modulo,
OpenParen,
CloseParen,
Dice,
Number(Value),
Atom(String),
}
......@@ -48,6 +49,7 @@ impl fmt::Display for Token {
Token::Modulo => write!(f, "Modulo"),
Token::OpenParen => write!(f, "OpenParen"),
Token::CloseParen => write!(f, "CloseParen"),
Token::Dice => write!(f, "Dice"),
Token::Number(ref n) => write!(f, "'{}'", n),
Token::Atom(ref s) => write!(f, "'{}'", s),
}
......@@ -68,7 +70,7 @@ impl IsOperator for char {
fn is_operator(self) -> bool {
match self {
'+' | '-' | '/' | '^' | '²' | '³' | '&' | '|' | '~' | '>'
| '%' | '(' | ')' | '*' | '<' => true,
| '%' | '(' | ')' | '*' | '<' | 'd' => true,
_ => false,
}
}
......@@ -82,7 +84,7 @@ impl CheckOperator for char {
fn check_operator(self) -> OperatorState {
match self {
'+' | '-' | '/' | '^' | '²' | '³' | '&' | '|' | '~' | '%'
| '(' | ')' => OperatorState::Complete,
| '(' | ')' | 'd' => OperatorState::Complete,
'*' | '<' | '>' => OperatorState::PotentiallyIncomplete,
_ => OperatorState::NotAnOperator,
}
......@@ -123,6 +125,7 @@ impl OperatorMatch for char {
'%' => Some(Token::Modulo),
'(' => Some(Token::OpenParen),
')' => Some(Token::CloseParen),
'd' => Some(Token::Dice),
_ => None,
}
}
......@@ -139,46 +142,45 @@ pub fn tokenize(input: &str) -> Result<Vec<Token>, CalcError> {
let mut chars = input.chars().peekable();
while let Some(&c) = chars.peek() {
if c.is_alphabetic() {
tokens.push(Token::Atom(consume_atom(&mut chars)));
} else if c.is_digit(16) || c == '.' {
tokens.push(Token::Number(consume_number(&mut chars)?));
} else {
match c.check_operator() {
OperatorState::Complete => {
tokens.push(
c.operator_type().ok_or_else(|| InvalidOperator(c))?,
);
chars.next();
}
OperatorState::PotentiallyIncomplete => {
chars.next();
match chars.peek() {
Some(&next_char) if next_char.is_operator() => {
tokens.push(
[c, next_char]
.operator_type()
.ok_or_else(|| InvalidOperator(c))?,
);
chars.next();
}
_ => {
tokens.push(
c.operator_type()
.ok_or_else(|| InvalidOperator(c))?,
);
}
}
}
OperatorState::NotAnOperator => {
if c.is_whitespace() {
match c.check_operator() {
OperatorState::Complete => {
tokens.push(
c.operator_type().ok_or_else(|| InvalidOperator(c))?,
);
chars.next();
}
OperatorState::PotentiallyIncomplete => {
chars.next();
match chars.peek() {
Some(&next_char) if next_char.is_operator() => {
tokens.push(
[c, next_char]
.operator_type()
.ok_or_else(|| InvalidOperator(c))?,
);
chars.next();
} else {
let token_string = consume_until_new_token(&mut chars);
return Err(CalcError::UnrecognizedToken(token_string));
}
_ => {
tokens.push(
c.operator_type()
.ok_or_else(|| InvalidOperator(c))?,
);
}
}
}
OperatorState::NotAnOperator => {
if c.is_whitespace() {
chars.next();
} else if c.is_alphabetic() {
tokens.push(Token::Atom(consume_atom(&mut chars)));
} else if c.is_digit(16) || c == '.' {
tokens.push(Token::Number(consume_number(&mut chars)?));
} else {
let token_string = consume_until_new_token(&mut chars);
return Err(CalcError::UnrecognizedToken(token_string));
}
}
}
}
Ok(tokens)
......@@ -222,61 +224,59 @@ pub fn tokenize_polish(input: &str) -> Result<Vec<Token>, CalcError> {
'outer: loop {
// Collect the operators until a number is found.
while let Some(&c) = chars.peek() {
if c.is_alphabetic() {
values.push(PolishValue::Atom(consume_atom(&mut chars)));
break;
} else if c.is_digit(16) || c == '.' {
values.push(PolishValue::Number(consume_number(&mut chars)?));
break;
} else {
match c.check_operator() {
OperatorState::Complete => {
let token = c
.operator_type()
.ok_or_else(|| InvalidOperator(c))?;
if token != Token::OpenParen
&& token != Token::CloseParen
{
operators.push(token);
}
chars.next();
match c.check_operator() {
OperatorState::Complete => {
let token = c
.operator_type()
.ok_or_else(|| InvalidOperator(c))?;
if token != Token::OpenParen
&& token != Token::CloseParen
{
operators.push(token);
}
OperatorState::PotentiallyIncomplete => {
chars.next();
match chars.peek() {
Some(&next_char) if next_char.is_operator() => {
let token = [c, next_char]
.operator_type()
.ok_or_else(|| InvalidOperator(c))?;
if token != Token::OpenParen
&& token != Token::CloseParen
{
operators.push(token);
}
chars.next();
chars.next();
}
OperatorState::PotentiallyIncomplete => {
chars.next();
match chars.peek() {
Some(&next_char) if next_char.is_operator() => {
let token = [c, next_char]
.operator_type()
.ok_or_else(|| InvalidOperator(c))?;
if token != Token::OpenParen
&& token != Token::CloseParen
{
operators.push(token);
}
_ => {
let token = c
.operator_type()
.ok_or_else(|| InvalidOperator(c))?;
if token != Token::OpenParen
&& token != Token::CloseParen
{
operators.push(token);
}
chars.next();
}
_ => {
let token = c
.operator_type()
.ok_or_else(|| InvalidOperator(c))?;
if token != Token::OpenParen
&& token != Token::CloseParen
{
operators.push(token);
}
}
}
OperatorState::NotAnOperator => {
if c.is_whitespace() {
chars.next();
} else {
let token_string =
consume_until_new_token(&mut chars);
return Err(CalcError::UnrecognizedToken(
token_string,
));
}
}
OperatorState::NotAnOperator => {
if c.is_whitespace() {
chars.next();
} else if c.is_alphabetic() {
values.push(PolishValue::Atom(consume_atom(&mut chars)));
break;
} else if c.is_digit(16) || c == '.' {
values.push(PolishValue::Number(consume_number(&mut chars)?));
break;
} else {
let token_string =
consume_until_new_token(&mut chars);
return Err(CalcError::UnrecognizedToken(
token_string,
));
}
}
}
......@@ -479,6 +479,28 @@ mod tests {
assert_eq!(tokenize(line), Ok(expected));
}
#[test]
fn dice() {
let line = "3d6";
let expected = vec![
Token::Number(Value::dec(3)),
Token::Dice,
Token::Number(Value::dec(6)),
];
assert_eq!(tokenize(line), Ok(expected));
}
#[test]
fn dice_polish() {
let line = "d 3 6";
let expected = vec![
Token::Number(Value::dec(3)),
Token::Dice,
Token::Number(Value::dec(6)),
];
assert_eq!(tokenize_polish(line), Ok(expected));
}
#[test]
fn function_chaining() {
let line = "log 4 / log 2";
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment