diff --git a/src/builtins/calc.rs b/src/builtins/calc.rs
index 6a32b8522e7f01de7fa232cbd060b58957a6452b..49eb612461b488254753f272c31526b4648b32be 100644
--- a/src/builtins/calc.rs
+++ b/src/builtins/calc.rs
@@ -473,7 +473,7 @@ pub fn parse(tokens: &[Token]) -> Result<String, CalcError> {
     d_expr(tokens).map(|answer| answer.value.to_string())
 }
 
-fn eval(input: &str) -> Result<String, CalcError> {
+pub fn eval(input: &str) -> Result<String, CalcError> {
     tokenize(input).and_then(|x| parse(&x))
 }
 
diff --git a/src/builtins/mod.rs b/src/builtins/mod.rs
index 903f5ab314988cf43f6145f8c2eeed774e0ae868..bbff0c003e4901269062ff715ceace15cbecdeb0 100644
--- a/src/builtins/mod.rs
+++ b/src/builtins/mod.rs
@@ -1,11 +1,11 @@
 pub mod source;
 pub mod variables;
 pub mod functions;
+pub mod calc;
 
 mod test;
 mod time;
 mod echo;
-mod calc;
 mod set;
 
 use self::variables::{alias, drop_alias, drop_variable};
diff --git a/src/parser/shell_expand/mod.rs b/src/parser/shell_expand/mod.rs
index 2f33e745c36ee2539e95a99b46f873114392c223..4218beab3bf5b1b414bc1700e54f892f89c6d271 100644
--- a/src/parser/shell_expand/mod.rs
+++ b/src/parser/shell_expand/mod.rs
@@ -14,6 +14,8 @@ use self::ranges::parse_range;
 pub use self::words::{WordIterator, WordToken, Select, Index, Range};
 use shell::variables::Variables;
 
+use ::builtins::calc;
+
 use std::io::{self, Write};
 use types::*;
 
@@ -269,6 +271,7 @@ pub fn expand_tokens<'a>(token_buffer: &[WordToken], expand_func: &'a ExpanderFu
                             }
                         }
                     },
+                    WordToken::Arithmetic(s) => expand_arithmetic(&mut output, s, &expand_func),
                 }
             }
 
@@ -477,6 +480,7 @@ pub fn expand_tokens<'a>(token_buffer: &[WordToken], expand_func: &'a ExpanderFu
 
                     slice_string(&mut output, &expanded, index);
                 },
+                WordToken::Arithmetic(s) => expand_arithmetic(&mut output, s, expand_func),
             }
         }
         //the is_glob variable can probably be removed, I'm not entirely sure if empty strings are valid in any case- maarten
@@ -488,6 +492,47 @@ pub fn expand_tokens<'a>(token_buffer: &[WordToken], expand_func: &'a ExpanderFu
     expanded_words
 }
 
+/// Expand a string inside an arithmetic expression, for example:
+/// ```ignore
+/// x * 5 + y => 22
+/// ```
+/// if `x=5` and `y=7`
+fn expand_arithmetic(output : &mut String , input : &str, expander : &ExpanderFunctions) {
+    let mut intermediate = String::with_capacity(input.as_bytes().len());
+    let mut varbuf = String::new();
+    let flush = |var : &mut String, out : &mut String| {
+        if ! var.is_empty() {
+            // We have reached the end of a potential variable, so we expand it and push
+            // it onto the result
+            let res = (expander.variable)(&var, false);
+            match res {
+                Some(v) => out.push_str(&v),
+                None => out.push_str(&var),
+            }
+            var.clear();
+        }
+    };
+    for c in input.bytes() {
+        match c {
+            48...57 | 65...90 | 95 | 97...122 => {
+                varbuf.push(c as char);
+            },
+            _ => {
+                flush(&mut varbuf, &mut intermediate);
+                intermediate.push(c as char);
+            }
+        }
+    }
+    flush(&mut varbuf, &mut intermediate);
+    match calc::eval(&intermediate) {
+        Ok(s) => output.push_str(&s),
+        Err(e) => {
+            let err_string : String = e.into();
+            output.push_str(&err_string);
+        }
+    }
+}
+
 // TODO: Write Nested Brace Tests
 
 #[cfg(test)]
@@ -589,4 +634,14 @@ mod test {
             }
         }
     }
+
+    #[test]
+    fn arith_expression() {
+        let line = "$((A * A - (A + A)))";
+        let expected = Array::from_vec(vec!["-1".to_owned()]);
+        assert_eq!(expected, expand_string(line, &functions!(), false));
+        let line = "$((3 * 10 - 27))";
+        let expected = Array::from_vec(vec!["3".to_owned()]);
+        assert_eq!(expected, expand_string(line, &functions!(), false));
+    }
 }
diff --git a/src/parser/shell_expand/words.rs b/src/parser/shell_expand/words.rs
index ab22f788959c1a6b7b4edc160c39480d9f26c010..93c8e1bc0712346c03e32a44ff8e4d480edf3884 100644
--- a/src/parser/shell_expand/words.rs
+++ b/src/parser/shell_expand/words.rs
@@ -344,6 +344,7 @@ pub enum WordToken<'a> {
     Process(&'a str, bool, Select),
     StringMethod(&'a str, &'a str, &'a str, Select),
     ArrayMethod(ArrayMethod<'a>),
+    Arithmetic(&'a str)
     //Glob(&'a str),
 }
 
@@ -853,6 +854,30 @@ impl<'a> WordIterator<'a> {
             false
         }
     }
+
+    fn arithmetic_expression<I : Iterator<Item=u8>>(&mut self, iter : &mut I) -> WordToken<'a> {
+        let mut paren : i8 = 0;
+        let start = self.read;
+        while let Some(character) = iter.next() {
+            match character {
+                b'(' => paren += 1,
+                b')' => {
+                    if paren == 0 {
+                        // Skip the incoming ); we have validated this syntax so it should be OK
+                        let _ = iter.next();
+                        let output = &self.data[start..self.read];
+                        self.read += 2;
+                        return WordToken::Arithmetic(output)
+                    } else {
+                        paren -= 1;
+                    }
+                },
+                _ => ()
+            }
+            self.read += 1;
+        }
+        panic!("ion: fatal syntax error: unterminated arithmetic expression");
+    }
 }
 
 
@@ -940,7 +965,12 @@ impl<'a> Iterator for WordIterator<'a> {
                         match iterator.next() {
                             Some(b'(') => {
                                 self.read += 2;
-                                return if self.flags.contains(EXPAND_PROCESSES) {
+                                return if self.data.as_bytes()[self.read] == b'(' {
+                                    // Pop the incoming left paren
+                                    let _ = iterator.next();
+                                    self.read += 1;
+                                    Some(self.arithmetic_expression(&mut iterator))
+                                } else if self.flags.contains(EXPAND_PROCESSES) {
                                     Some(self.process(&mut iterator))
                                 } else {
                                     Some(WordToken::Normal(&self.data[start..self.read],glob))
@@ -1214,4 +1244,16 @@ mod tests {
         ];
         compare(input, expected);
     }
+
+    #[test]
+    fn test_arithmetic() {
+        let input = "echo $((foo bar baz bing 3 * 2))";
+        let expected = vec![
+            WordToken::Normal("echo", false),
+            WordToken::Whitespace(" "),
+            WordToken::Arithmetic("foo bar baz bing 3 * 2"),
+        ];
+        compare(input, expected);
+    }
+
 }
diff --git a/src/parser/statements.rs b/src/parser/statements.rs
index 25c6439c37b9636134656c11f270e2bc878e3dc5..a3aa8ac0c2137e779050e4f6eb3f39135b46d9dc 100644
--- a/src/parser/statements.rs
+++ b/src/parser/statements.rs
@@ -18,6 +18,9 @@ bitflags! {
         const ARRAY  = 64;
         const VARIAB = 128;
         const METHOD = 256;
+        /// Set while parsing through an inline arithmetic expression, e.g. $((foo * bar / baz))
+        const MATHEXPR = 512;
+        const POST_MATHEXPR = 1024;
     }
 }
 
@@ -30,6 +33,7 @@ pub enum StatementError<'a> {
     UnterminatedBracedVar,
     UnterminatedBrace,
     UnterminatedMethod,
+    UnterminatedArithmetic,
     ExpectedCommandButFound(&'static str)
 }
 
@@ -59,6 +63,9 @@ pub fn check_statement<'a>(statement: Result<&str, StatementError<'a>>) -> State
                 StatementError::UnterminatedMethod => {
                     let _ = writeln!(stderr.lock(), "ion: syntax error: unterminated method");
                 }
+                StatementError::UnterminatedArithmetic => {
+                    let _ = writeln!(stderr.lock(), "ion: syntax error: unterminated arithmetic subexpression");
+                }
                 StatementError::ExpectedCommandButFound(element) => {
                     let _ = writeln!(stderr.lock(), "ion: expected command, but found {}", element);
                 }
@@ -76,6 +83,7 @@ pub struct StatementSplitter<'a> {
     array_process_level: u8,
     process_level: u8,
     brace_level: u8,
+    math_paren_level: i8,
 }
 
 impl<'a> StatementSplitter<'a> {
@@ -87,7 +95,8 @@ impl<'a> StatementSplitter<'a> {
             array_level: 0,
             array_process_level: 0,
             process_level: 0,
-            brace_level: 0
+            brace_level: 0,
+            math_paren_level: 0,
         }
     }
 }
@@ -103,6 +112,7 @@ impl<'a> Iterator for StatementSplitter<'a> {
         for character in self.data.bytes().skip(self.read) {
             self.read += 1;
             match character {
+                _ if self.flags.contains(POST_MATHEXPR) => (),
                 0...47 | 58...64 | 91...94 | 96 | 123...124 | 126...127 if self.flags.contains(VBRACE) => {
                     // If we are just ending the braced section continue as normal
                     if error.is_none() {
@@ -141,14 +151,24 @@ impl<'a> Iterator for StatementSplitter<'a> {
                         self.brace_level -= 1;
                     }
                 },
+                b'(' if self.flags.contains(MATHEXPR) => {
+                    self.math_paren_level += 1;
+                }
                 b'('  if !self.flags.intersects(COMM_1 | VARIAB | ARRAY) => {
                     if error.is_none() && !self.flags.intersects(SQUOTE | DQUOTE) {
                         error = Some(StatementError::InvalidCharacter(character as char, self.read))
                     }
                 },
                 b'(' if self.flags.contains(COMM_1) => {
-                    self.process_level += 1;
                     self.flags -= VARIAB | ARRAY;
+                    if self.data.as_bytes()[self.read] == b'(' {
+                        self.flags -= COMM_1;
+                        self.flags |= MATHEXPR;
+                        // The next character will always be a left paren in this branch;
+                        self.math_paren_level = -1;
+                    } else {
+                        self.process_level += 1;
+                    }
                 },
                 b'(' if self.flags.intersects(VARIAB | ARRAY) => {
                     self.flags -= VARIAB | ARRAY;
@@ -165,6 +185,25 @@ impl<'a> Iterator for StatementSplitter<'a> {
                 },
                 b']' if !self.flags.contains(SQUOTE) && self.array_level != 0 => self.array_level -= 1,
                 b']' if !self.flags.contains(SQUOTE) => self.array_process_level -= 1,
+                b')' if self.flags.contains(MATHEXPR) => {
+                    if self.math_paren_level == 0 {
+                        if self.data.as_bytes().len() <= self.read {
+                            if error.is_none() {
+                                error = Some(StatementError::UnterminatedArithmetic)
+                            }
+                        } else {
+                            let next_character = self.data.as_bytes()[self.read] as char;
+                            if next_character == ')' {
+                                self.flags -= MATHEXPR;
+                                self.flags |= POST_MATHEXPR;
+                            } else if error.is_none() {
+                                error = Some(StatementError::InvalidCharacter(next_character, self.read));
+                            }
+                        }
+                    } else {
+                        self.math_paren_level -= 1;
+                    }
+                },
                 b')' if !self.flags.contains(SQUOTE) && self.flags.contains(METHOD) => {
                     self.flags ^= METHOD;
                 },
@@ -230,6 +269,7 @@ impl<'a> Iterator for StatementSplitter<'a> {
                 None if self.flags.contains(METHOD) => Some(Err(StatementError::UnterminatedMethod)),
                 None if self.flags.contains(VBRACE) => Some(Err(StatementError::UnterminatedBracedVar)),
                 None if self.brace_level != 0    => Some(Err(StatementError::UnterminatedBrace)),
+                None if self.flags.contains(MATHEXPR) => Some(Err(StatementError::UnterminatedArithmetic)),
                 None => {
                     let output = self.data[start..].trim();
                     match output.as_bytes()[0] {
@@ -247,11 +287,11 @@ impl<'a> Iterator for StatementSplitter<'a> {
 
 #[test]
 fn syntax_errors() {
-    let command = "echo (echo one); echo $((echo one); echo ) two; echo $(echo one";
+    let command = "echo (echo one); echo $( (echo one); echo ) two; echo $(echo one";
     let results = StatementSplitter::new(command).collect::<Vec<Result<&str, StatementError>>>();
     assert_eq!(results[0], Err(StatementError::InvalidCharacter('(', 6)));
-    assert_eq!(results[1], Err(StatementError::InvalidCharacter('(', 25)));
-    assert_eq!(results[2], Err(StatementError::InvalidCharacter(')', 42)));
+    assert_eq!(results[1], Err(StatementError::InvalidCharacter('(', 26)));
+    assert_eq!(results[2], Err(StatementError::InvalidCharacter(')', 43)));
     assert_eq!(results[3], Err(StatementError::UnterminatedSubshell));
     assert_eq!(results.len(), 4);
 
@@ -259,6 +299,11 @@ fn syntax_errors() {
     let results = StatementSplitter::new(command).collect::<Vec<Result<&str, StatementError>>>();
     assert_eq!(results[0], Err(StatementError::ExpectedCommandButFound("redirection")));
     assert_eq!(results.len(), 1);
+
+    let command = "echo $((foo bar baz)";
+    let results = StatementSplitter::new(command).collect::<Vec<_>>();
+    assert_eq!(results[0], Err(StatementError::UnterminatedArithmetic));
+    assert_eq!(results.len(), 1);
 }
 
 #[test]