diff --git a/dust-lang/src/disassembler.rs b/dust-lang/src/disassembler.rs
index 619e39c..9e4bfef 100644
--- a/dust-lang/src/disassembler.rs
+++ b/dust-lang/src/disassembler.rs
@@ -50,8 +50,8 @@ use crate::{value::ConcreteValue, Chunk, Local};
 const INSTRUCTION_HEADER: [&str; 4] = [
     "Instructions",
     "------------",
-    " i   POSITION    OPERATION                 TYPE                             INFO              ",
-    "--- ---------- ------------- -------------------------------- --------------------------------",
+    " i   POSITION    OPERATION        TYPE                    INFO              ",
+    "--- ---------- ------------- -------------- --------------------------------",
 ];
 
 const CONSTANT_HEADER: [&str; 4] = [
@@ -259,10 +259,18 @@ impl<'a> Disassembler<'a> {
         {
             let position = position.to_string();
             let operation = instruction.operation().to_string();
-            let r#type = r#type.to_string();
+            let type_display = {
+                let mut type_string = r#type.to_string();
+
+                if type_string.len() > 14 {
+                    type_string = format!("{type_string:.11}...");
+                }
+
+                type_string
+            };
             let info = instruction.disassembly_info();
             let instruction_display =
-                format!("{index:^3} {position:^10} {operation:13} {type:^32} {info:^32}");
+                format!("{index:^3} {position:^10} {operation:13} {type_display:^14} {info:^32}");
 
             self.push_details(&instruction_display);
         }
diff --git a/dust-lang/src/lexer.rs b/dust-lang/src/lexer.rs
index 8811d2b..604b907 100644
--- a/dust-lang/src/lexer.rs
+++ b/dust-lang/src/lexer.rs
@@ -30,9 +30,7 @@ use crate::{dust_error::AnnotatedError, CompileError, DustError, Span, Token};
 ///     ]
 /// );
 /// ```
-pub fn lex<'tokens, 'src: 'tokens>(
-    source: &'src str,
-) -> Result<Vec<(Token<'src>, Span)>, DustError> {
+pub fn lex(source: &str) -> Result<Vec<(Token, Span)>, DustError> {
     let mut lexer = Lexer::new(source);
     let mut tokens = Vec::new();
 
diff --git a/dust-lang/src/optimize.rs b/dust-lang/src/optimize.rs
index 2d454c5..fecc510 100644
--- a/dust-lang/src/optimize.rs
+++ b/dust-lang/src/optimize.rs
@@ -18,10 +18,10 @@ use crate::{instruction::SetLocal, Chunk, Operation};
 /// ```
 ///
 /// The instructions must be in the following order:
-///     - `Operation::Equal` | `Operation::Less` | `Operation::LessEqual` | `Operation::Test`
-///     - `Operation::Jump`
-///     - `Operation::LoadBoolean` | `Operation::LoadConstant`
-///     - `Operation::LoadBoolean` | `Operation::LoadConstant`
+///     - `Equal`, `Less`, `LessEqual` or `Test`
+///     - `Jump`
+///     - `LoadBoolean` or `LoadConstant`
+///     - `LoadBoolean` or `LoadConstant`
 pub fn optimize_control_flow(chunk: &mut Chunk) {
     if !matches!(
         chunk.get_last_operations(),
@@ -49,6 +49,24 @@ pub fn optimize_control_flow(chunk: &mut Chunk) {
     *second_loader = second_loader_new;
 }
 
+/// Optimizes a math instruction followed by a SetLocal instruction.
+///
+/// The SetLocal instruction is removed and the math instruction is modified to use the local as
+/// its destination. This makes the following two code snippets compile to the same bytecode:
+///
+/// ```dust
+/// let a = 0;
+/// a = a + 1;
+/// ```
+///
+/// ```dust
+/// let a = 0;
+/// a += 1;
+/// ```
+///
+/// The instructions must be in the following order:
+///     - `Add`, `Subtract`, `Multiply`, `Divide` or `Modulo`
+///     - `SetLocal`
 pub fn optimize_set_local(chunk: &mut Chunk) {
     if !matches!(
         chunk.get_last_operations(),
diff --git a/dust-lang/src/scope.rs b/dust-lang/src/scope.rs
index fd9f5c0..4e84988 100644
--- a/dust-lang/src/scope.rs
+++ b/dust-lang/src/scope.rs
@@ -34,13 +34,11 @@ impl Scope {
         }
     }
 
-    #[inline]
     pub fn begin(&mut self, block_index: u8) {
         self.depth += 1;
         self.block_index = block_index;
     }
 
-    #[inline]
     pub fn end(&mut self, block_index: u8) {
         self.depth -= 1;
         self.block_index = block_index;
diff --git a/dust-lang/src/value/abstract_value.rs b/dust-lang/src/value/abstract_value.rs
index 6b91958..6d59096 100644
--- a/dust-lang/src/value/abstract_value.rs
+++ b/dust-lang/src/value/abstract_value.rs
@@ -1,6 +1,6 @@
 use std::fmt::{self, Display, Formatter};
 
-use crate::{vm::Pointer, ConcreteValue, Vm, VmError};
+use crate::{vm::Pointer, ConcreteValue, Value, ValueRef, Vm, VmError};
 
 #[derive(Debug, PartialEq, PartialOrd)]
 pub enum AbstractValue {
@@ -9,6 +9,14 @@ pub enum AbstractValue {
 }
 
 impl AbstractValue {
+    pub fn to_value(self) -> Value {
+        Value::Abstract(self)
+    }
+
+    pub fn to_value_ref(&self) -> ValueRef {
+        ValueRef::Abstract(self)
+    }
+
     pub fn to_concrete_owned(&self, vm: &Vm) -> Result<ConcreteValue, VmError> {
         match self {
             AbstractValue::FunctionSelf => Ok(ConcreteValue::Function(vm.chunk().clone())),
diff --git a/dust-lang/src/value/concrete_value.rs b/dust-lang/src/value/concrete_value.rs
index e23abd3..fae26c0 100644
--- a/dust-lang/src/value/concrete_value.rs
+++ b/dust-lang/src/value/concrete_value.rs
@@ -2,7 +2,7 @@ use std::fmt::{self, Display, Formatter};
 
 use serde::{Deserialize, Serialize};
 
-use crate::{Chunk, Type, ValueError};
+use crate::{Chunk, Type, Value, ValueError, ValueRef};
 
 use super::RangeValue;
 
@@ -20,6 +20,14 @@ pub enum ConcreteValue {
 }
 
 impl ConcreteValue {
+    pub fn to_value(self) -> Value {
+        Value::Concrete(self)
+    }
+
+    pub fn to_value_ref(&self) -> ValueRef {
+        ValueRef::Concrete(self)
+    }
+
     pub fn list<T: Into<Vec<ConcreteValue>>>(into_list: T) -> Self {
         ConcreteValue::List(into_list.into())
     }
@@ -62,7 +70,12 @@ impl ConcreteValue {
             (Float(left), Float(right)) => ConcreteValue::Float(*left + *right),
             (Integer(left), Integer(right)) => ConcreteValue::Integer(left.saturating_add(*right)),
             (String(left), String(right)) => ConcreteValue::string(format!("{}{}", left, right)),
-            _ => return Err(ValueError::CannotAdd(self.clone(), other.clone())),
+            _ => {
+                return Err(ValueError::CannotAdd(
+                    self.clone().to_value(),
+                    other.clone().to_value(),
+                ))
+            }
         };
 
         Ok(sum)
@@ -75,7 +88,12 @@ impl ConcreteValue {
             (Byte(left), Byte(right)) => ConcreteValue::Byte(left.saturating_sub(*right)),
             (Float(left), Float(right)) => ConcreteValue::Float(left - right),
             (Integer(left), Integer(right)) => ConcreteValue::Integer(left.saturating_sub(*right)),
-            _ => return Err(ValueError::CannotSubtract(self.clone(), other.clone())),
+            _ => {
+                return Err(ValueError::CannotSubtract(
+                    self.clone().to_value(),
+                    other.clone().to_value(),
+                ))
+            }
         };
 
         Ok(difference)
@@ -88,7 +106,12 @@ impl ConcreteValue {
             (Byte(left), Byte(right)) => ConcreteValue::Byte(left.saturating_mul(*right)),
             (Float(left), Float(right)) => ConcreteValue::Float(left * right),
             (Integer(left), Integer(right)) => ConcreteValue::Integer(left.saturating_mul(*right)),
-            _ => return Err(ValueError::CannotMultiply(self.clone(), other.clone())),
+            _ => {
+                return Err(ValueError::CannotMultiply(
+                    self.clone().to_value(),
+                    other.clone().to_value(),
+                ))
+            }
         };
 
         Ok(product)
@@ -101,7 +124,12 @@ impl ConcreteValue {
             (Byte(left), Byte(right)) => ConcreteValue::Byte(left.saturating_div(*right)),
             (Float(left), Float(right)) => ConcreteValue::Float(left / right),
             (Integer(left), Integer(right)) => ConcreteValue::Integer(left.saturating_div(*right)),
-            _ => return Err(ValueError::CannotMultiply(self.clone(), other.clone())),
+            _ => {
+                return Err(ValueError::CannotMultiply(
+                    self.clone().to_value(),
+                    other.clone().to_value(),
+                ))
+            }
         };
 
         Ok(quotient)
@@ -116,7 +144,12 @@ impl ConcreteValue {
             (Integer(left), Integer(right)) => {
                 ConcreteValue::Integer(left.wrapping_rem_euclid(*right))
             }
-            _ => return Err(ValueError::CannotMultiply(self.clone(), other.clone())),
+            _ => {
+                return Err(ValueError::CannotMultiply(
+                    self.clone().to_value(),
+                    other.clone().to_value(),
+                ))
+            }
         };
 
         Ok(product)
@@ -130,7 +163,7 @@ impl ConcreteValue {
             Byte(value) => ConcreteValue::Byte(value.wrapping_neg()),
             Float(value) => ConcreteValue::Float(-value),
             Integer(value) => ConcreteValue::Integer(value.wrapping_neg()),
-            _ => return Err(ValueError::CannotNegate(self.clone())),
+            _ => return Err(ValueError::CannotNegate(self.clone().to_value())),
         };
 
         Ok(negated)
@@ -141,7 +174,7 @@ impl ConcreteValue {
 
         let not = match self {
             Boolean(value) => ConcreteValue::Boolean(!value),
-            _ => return Err(ValueError::CannotNot(self.clone())),
+            _ => return Err(ValueError::CannotNot(self.clone().to_value())),
         };
 
         Ok(not)
@@ -160,7 +193,12 @@ impl ConcreteValue {
             (List(left), List(right)) => ConcreteValue::Boolean(left == right),
             (Range(left), Range(right)) => ConcreteValue::Boolean(left == right),
             (String(left), String(right)) => ConcreteValue::Boolean(left == right),
-            _ => return Err(ValueError::CannotCompare(self.clone(), other.clone())),
+            _ => {
+                return Err(ValueError::CannotCompare(
+                    self.clone().to_value(),
+                    other.clone().to_value(),
+                ))
+            }
         };
 
         Ok(equal)
@@ -179,7 +217,12 @@ impl ConcreteValue {
             (List(left), List(right)) => ConcreteValue::Boolean(left < right),
             (Range(left), Range(right)) => ConcreteValue::Boolean(left < right),
             (String(left), String(right)) => ConcreteValue::Boolean(left < right),
-            _ => return Err(ValueError::CannotCompare(self.clone(), other.clone())),
+            _ => {
+                return Err(ValueError::CannotCompare(
+                    self.clone().to_value(),
+                    other.clone().to_value(),
+                ))
+            }
         };
 
         Ok(less_than)
@@ -198,7 +241,12 @@ impl ConcreteValue {
             (List(left), List(right)) => ConcreteValue::Boolean(left <= right),
             (Range(left), Range(right)) => ConcreteValue::Boolean(left <= right),
             (String(left), String(right)) => ConcreteValue::Boolean(left <= right),
-            _ => return Err(ValueError::CannotCompare(self.clone(), other.clone())),
+            _ => {
+                return Err(ValueError::CannotCompare(
+                    self.clone().to_value(),
+                    other.clone().to_value(),
+                ))
+            }
         };
 
         Ok(less_than_or_equal)
diff --git a/dust-lang/src/value/mod.rs b/dust-lang/src/value/mod.rs
index 5378c36..5d8b04a 100644
--- a/dust-lang/src/value/mod.rs
+++ b/dust-lang/src/value/mod.rs
@@ -18,6 +18,13 @@ pub enum Value {
 }
 
 impl Value {
+    pub fn to_ref(&self) -> ValueRef {
+        match self {
+            Value::Abstract(abstract_value) => ValueRef::Abstract(abstract_value),
+            Value::Concrete(concrete_value) => ValueRef::Concrete(concrete_value),
+        }
+    }
+
     pub fn to_concrete_owned(&self, vm: &Vm) -> Result<ConcreteValue, VmError> {
         match self {
             Value::Abstract(abstract_value) => abstract_value.to_concrete_owned(vm),
@@ -33,13 +40,29 @@ impl Value {
     }
 }
 
-#[derive(Clone, Debug, PartialEq, PartialOrd)]
+impl Display for Value {
+    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+        match self {
+            Value::Abstract(abstract_value) => write!(f, "{}", abstract_value),
+            Value::Concrete(concrete_value) => write!(f, "{}", concrete_value),
+        }
+    }
+}
+
+#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
 pub enum ValueRef<'a> {
     Abstract(&'a AbstractValue),
     Concrete(&'a ConcreteValue),
 }
 
 impl ValueRef<'_> {
+    pub fn to_owned(&self) -> Value {
+        match self {
+            ValueRef::Abstract(abstract_value) => Value::Abstract((*abstract_value).clone()),
+            ValueRef::Concrete(concrete_value) => Value::Concrete((*concrete_value).clone()),
+        }
+    }
+
     pub fn to_concrete_owned(&self, vm: &Vm) -> Result<ConcreteValue, VmError> {
         match self {
             ValueRef::Abstract(abstract_value) => abstract_value.to_concrete_owned(vm),
@@ -53,20 +76,125 @@ impl ValueRef<'_> {
             ValueRef::Concrete(concrete_value) => Ok(concrete_value.to_string()),
         }
     }
+
+    pub fn add(&self, other: ValueRef) -> Result<Value, ValueError> {
+        match (self, other) {
+            (ValueRef::Concrete(left), ValueRef::Concrete(right)) => {
+                left.add(right).map(|result| result.to_value())
+            }
+            _ => Err(ValueError::CannotAdd(self.to_owned(), other.to_owned())),
+        }
+    }
+
+    pub fn subtract(&self, other: ValueRef) -> Result<Value, ValueError> {
+        match (self, other) {
+            (ValueRef::Concrete(left), ValueRef::Concrete(right)) => {
+                left.subtract(right).map(|result| result.to_value())
+            }
+            _ => Err(ValueError::CannotSubtract(
+                self.to_owned(),
+                other.to_owned(),
+            )),
+        }
+    }
+
+    pub fn multiply(&self, other: ValueRef) -> Result<Value, ValueError> {
+        match (self, other) {
+            (ValueRef::Concrete(left), ValueRef::Concrete(right)) => {
+                left.multiply(right).map(|result| result.to_value())
+            }
+            _ => Err(ValueError::CannotMultiply(
+                self.to_owned(),
+                other.to_owned(),
+            )),
+        }
+    }
+
+    pub fn divide(&self, other: ValueRef) -> Result<Value, ValueError> {
+        match (self, other) {
+            (ValueRef::Concrete(left), ValueRef::Concrete(right)) => {
+                left.divide(right).map(|result| result.to_value())
+            }
+            _ => Err(ValueError::CannotDivide(self.to_owned(), other.to_owned())),
+        }
+    }
+
+    pub fn modulo(&self, other: ValueRef) -> Result<Value, ValueError> {
+        match (self, other) {
+            (ValueRef::Concrete(left), ValueRef::Concrete(right)) => {
+                left.modulo(right).map(|result| result.to_value())
+            }
+            _ => Err(ValueError::CannotModulo(self.to_owned(), other.to_owned())),
+        }
+    }
+
+    pub fn negate(&self) -> Result<Value, ValueError> {
+        match self {
+            ValueRef::Concrete(concrete_value) => {
+                concrete_value.negate().map(|result| result.to_value())
+            }
+            _ => Err(ValueError::CannotNegate(self.to_owned())),
+        }
+    }
+
+    pub fn not(&self) -> Result<Value, ValueError> {
+        match self {
+            ValueRef::Concrete(concrete_value) => {
+                concrete_value.not().map(|result| result.to_value())
+            }
+            _ => Err(ValueError::CannotNot(self.to_owned())),
+        }
+    }
+
+    pub fn equal(&self, other: ValueRef) -> Result<Value, ValueError> {
+        match (self, other) {
+            (ValueRef::Concrete(left), ValueRef::Concrete(right)) => {
+                left.equal(right).map(|result| result.to_value())
+            }
+            _ => Err(ValueError::CannotCompare(self.to_owned(), other.to_owned())),
+        }
+    }
+
+    pub fn less_than(&self, other: ValueRef) -> Result<Value, ValueError> {
+        match (self, other) {
+            (ValueRef::Concrete(left), ValueRef::Concrete(right)) => {
+                left.less_than(right).map(|result| result.to_value())
+            }
+            _ => Err(ValueError::CannotCompare(self.to_owned(), other.to_owned())),
+        }
+    }
+
+    pub fn less_than_or_equal(&self, other: ValueRef) -> Result<Value, ValueError> {
+        match (self, other) {
+            (ValueRef::Concrete(left), ValueRef::Concrete(right)) => left
+                .less_than_or_equal(right)
+                .map(|result| result.to_value()),
+            _ => Err(ValueError::CannotCompare(self.to_owned(), other.to_owned())),
+        }
+    }
+}
+
+impl Display for ValueRef<'_> {
+    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+        match self {
+            ValueRef::Abstract(abstract_value) => write!(f, "{}", abstract_value),
+            ValueRef::Concrete(concrete_value) => write!(f, "{}", concrete_value),
+        }
+    }
 }
 
 #[derive(Clone, Debug, PartialEq)]
 pub enum ValueError {
-    CannotAdd(ConcreteValue, ConcreteValue),
-    CannotAnd(ConcreteValue, ConcreteValue),
-    CannotCompare(ConcreteValue, ConcreteValue),
-    CannotDivide(ConcreteValue, ConcreteValue),
-    CannotModulo(ConcreteValue, ConcreteValue),
-    CannotMultiply(ConcreteValue, ConcreteValue),
-    CannotNegate(ConcreteValue),
-    CannotNot(ConcreteValue),
-    CannotSubtract(ConcreteValue, ConcreteValue),
-    CannotOr(ConcreteValue, ConcreteValue),
+    CannotAdd(Value, Value),
+    CannotAnd(Value, Value),
+    CannotCompare(Value, Value),
+    CannotDivide(Value, Value),
+    CannotModulo(Value, Value),
+    CannotMultiply(Value, Value),
+    CannotNegate(Value),
+    CannotNot(Value),
+    CannotSubtract(Value, Value),
+    CannotOr(Value, Value),
 }
 
 impl Display for ValueError {
diff --git a/dust-lang/src/vm.rs b/dust-lang/src/vm.rs
index 9c726d0..0103b47 100644
--- a/dust-lang/src/vm.rs
+++ b/dust-lang/src/vm.rs
@@ -6,8 +6,11 @@ use std::{
 };
 
 use crate::{
-    compile, AbstractValue, AnnotatedError, Chunk, ChunkError, ConcreteValue, DustError,
-    Instruction, NativeFunction, NativeFunctionError, Operation, Span, Value, ValueError, ValueRef,
+    compile,
+    instruction::{Negate, Not},
+    AbstractValue, AnnotatedError, Argument, Chunk, ChunkError, ConcreteValue, Destination,
+    DustError, Instruction, NativeFunction, NativeFunctionError, Operation, Span, Value,
+    ValueError, ValueRef,
 };
 
 pub fn run(source: &str) -> Result<Option<ConcreteValue>, DustError> {
@@ -98,9 +101,9 @@ impl<'a> Vm<'a> {
                     let to_register = instruction.a();
                     let boolean = instruction.b_as_boolean();
                     let jump = instruction.c_as_boolean();
-                    let boolean = ConcreteValue::Boolean(boolean);
+                    let boolean = ConcreteValue::Boolean(boolean).to_value();
 
-                    self.set_register(to_register, Register::ConcreteValue(boolean))?;
+                    self.set_register(to_register, Register::Value(boolean))?;
 
                     if jump {
                         self.ip += 1;
@@ -135,14 +138,14 @@ impl<'a> Vm<'a> {
                         pointers.push(pointer);
                     }
 
-                    self.set_register(
-                        to_register,
-                        Register::AbstractValue(AbstractValue::List { items: pointers }),
-                    )?;
+                    let register =
+                        Register::Value(AbstractValue::List { items: pointers }.to_value());
+
+                    self.set_register(to_register, register)?;
                 }
                 Operation::LoadSelf => {
                     let to_register = instruction.a();
-                    let register = Register::AbstractValue(AbstractValue::FunctionSelf);
+                    let register = Register::Value(AbstractValue::FunctionSelf.to_value());
 
                     self.set_register(to_register, register)?;
                 }
@@ -179,7 +182,7 @@ impl<'a> Vm<'a> {
                         position: self.current_position,
                     })?;
 
-                    self.set_register(to_register, Register::ConcreteValue(sum))?;
+                    self.set_register(to_register, Register::Value(sum))?;
                 }
                 Operation::Subtract => {
                     let to_register = instruction.a();
@@ -189,7 +192,7 @@ impl<'a> Vm<'a> {
                         position: self.current_position,
                     })?;
 
-                    self.set_register(to_register, Register::ConcreteValue(difference))?;
+                    self.set_register(to_register, Register::Value(difference))?;
                 }
                 Operation::Multiply => {
                     let to_register = instruction.a();
@@ -199,7 +202,7 @@ impl<'a> Vm<'a> {
                         position: self.current_position,
                     })?;
 
-                    self.set_register(to_register, Register::ConcreteValue(product))?;
+                    self.set_register(to_register, Register::Value(product))?;
                 }
                 Operation::Divide => {
                     let to_register = instruction.a();
@@ -209,7 +212,7 @@ impl<'a> Vm<'a> {
                         position: self.current_position,
                     })?;
 
-                    self.set_register(to_register, Register::ConcreteValue(quotient))?;
+                    self.set_register(to_register, Register::Value(quotient))?;
                 }
                 Operation::Modulo => {
                     let to_register = instruction.a();
@@ -219,7 +222,7 @@ impl<'a> Vm<'a> {
                         position: self.current_position,
                     })?;
 
-                    self.set_register(to_register, Register::ConcreteValue(remainder))?;
+                    self.set_register(to_register, Register::Value(remainder))?;
                 }
                 Operation::Test => {
                     let test_register = instruction.b();
@@ -230,7 +233,7 @@ impl<'a> Vm<'a> {
                         *boolean
                     } else {
                         return Err(VmError::ExpectedBoolean {
-                            found: value.to_concrete_owned(self)?,
+                            found: value.to_owned(),
                             position: self.current_position,
                         });
                     };
@@ -249,7 +252,7 @@ impl<'a> Vm<'a> {
                         *boolean
                     } else {
                         return Err(VmError::ExpectedBoolean {
-                            found: value.to_concrete_owned(self)?,
+                            found: value.to_owned(),
                             position: self.current_position,
                         });
                     };
@@ -270,14 +273,15 @@ impl<'a> Vm<'a> {
                         error,
                         position: self.current_position,
                     })?;
-                    let is_equal = if let ConcreteValue::Boolean(boolean) = equal_result {
-                        boolean
-                    } else {
-                        return Err(VmError::ExpectedBoolean {
-                            found: equal_result.clone(),
-                            position: self.current_position,
-                        });
-                    };
+                    let is_equal =
+                        if let Value::Concrete(ConcreteValue::Boolean(boolean)) = equal_result {
+                            boolean
+                        } else {
+                            return Err(VmError::ExpectedBoolean {
+                                found: equal_result,
+                                position: self.current_position,
+                            });
+                        };
 
                     if is_equal == compare_to {
                         self.ip += 1;
@@ -294,14 +298,15 @@ impl<'a> Vm<'a> {
                         error,
                         position: self.current_position,
                     })?;
-                    let is_less_than = if let ConcreteValue::Boolean(boolean) = less_result {
-                        boolean
-                    } else {
-                        return Err(VmError::ExpectedBoolean {
-                            found: less_result.clone(),
-                            position: self.current_position,
-                        });
-                    };
+                    let is_less_than =
+                        if let Value::Concrete(ConcreteValue::Boolean(boolean)) = less_result {
+                            boolean
+                        } else {
+                            return Err(VmError::ExpectedBoolean {
+                                found: less_result,
+                                position: self.current_position,
+                            });
+                        };
 
                     if is_less_than == compare_to {
                         self.ip += 1;
@@ -321,11 +326,13 @@ impl<'a> Vm<'a> {
                                 position: self.current_position,
                             })?;
                     let is_less_than_or_equal =
-                        if let ConcreteValue::Boolean(boolean) = less_or_equal_result {
+                        if let Value::Concrete(ConcreteValue::Boolean(boolean)) =
+                            less_or_equal_result
+                        {
                             boolean
                         } else {
                             return Err(VmError::ExpectedBoolean {
-                                found: less_or_equal_result.clone(),
+                                found: less_or_equal_result,
                                 position: self.current_position,
                             });
                         };
@@ -339,22 +346,34 @@ impl<'a> Vm<'a> {
                     }
                 }
                 Operation::Negate => {
-                    let value = self.get_argument(instruction.b(), instruction.b_is_constant())?;
+                    let Negate {
+                        destination,
+                        argument,
+                    } = Negate::from(&instruction);
+                    let value = self.get_argument(argument)?;
                     let negated = value.negate().map_err(|error| VmError::Value {
                         error,
                         position: self.current_position,
                     })?;
+                    let register_index = self.get_destination(destination)?;
+                    let register = Register::Value(negated);
 
-                    self.set_register(instruction.a(), Register::ConcreteValue(negated))?;
+                    self.set_register(register_index, register)?;
                 }
                 Operation::Not => {
-                    let value = self.get_argument(instruction.b(), instruction.b_is_constant())?;
+                    let Not {
+                        destination,
+                        argument,
+                    } = Not::from(&instruction);
+                    let value = self.get_argument(argument)?;
                     let not = value.not().map_err(|error| VmError::Value {
                         error,
                         position: self.current_position,
                     })?;
+                    let register_index = self.get_destination(destination)?;
+                    let register = Register::Value(not);
 
-                    self.set_register(instruction.a(), Register::ConcreteValue(not))?;
+                    self.set_register(register_index, register)?;
                 }
                 Operation::Jump => self.jump(instruction),
                 Operation::Call => {
@@ -382,13 +401,17 @@ impl<'a> Vm<'a> {
                         function_vm.set_register(
                             argument_index as u16,
                             Register::Pointer(Pointer::ParentStack(argument_register_index)),
-                        )?
+                        )?;
+
+                        function_vm.local_definitions[argument_index] = Some(argument_index as u16);
                     }
 
                     let return_value = function_vm.run()?;
 
-                    if let Some(value) = return_value {
-                        self.set_register(to_register, Register::ConcreteValue(value))?;
+                    if let Some(concrete_value) = return_value {
+                        let register = Register::Value(concrete_value.to_value());
+
+                        self.set_register(to_register, register)?;
                     }
                 }
                 Operation::CallNative => {
@@ -397,15 +420,7 @@ impl<'a> Vm<'a> {
 
                     if let Some(value) = return_value {
                         let to_register = instruction.a();
-
-                        let register = match value {
-                            Value::Abstract(abstract_value) => {
-                                Register::AbstractValue(abstract_value)
-                            }
-                            Value::Concrete(concrete_value) => {
-                                Register::ConcreteValue(concrete_value)
-                            }
-                        };
+                        let register = Register::Value(value);
 
                         self.set_register(to_register, register)?;
                     }
@@ -480,9 +495,8 @@ impl<'a> Vm<'a> {
         log::trace!("Open R{register_index} to {register}");
 
         match register {
-            Register::ConcreteValue(value) => Ok(ValueRef::Concrete(value)),
+            Register::Value(value) => Ok(value.to_ref()),
             Register::Pointer(pointer) => self.follow_pointer(*pointer),
-            Register::AbstractValue(abstract_value) => Ok(ValueRef::Abstract(abstract_value)),
             Register::Empty => Err(VmError::EmptyRegister {
                 index: register_index,
                 position: self.current_position,
@@ -506,9 +520,8 @@ impl<'a> Vm<'a> {
         log::trace!("Open R{register_index} to {register}");
 
         match register {
-            Register::ConcreteValue(value) => Ok(Some(ValueRef::Concrete(value))),
+            Register::Value(value) => Ok(Some(value.to_ref())),
             Register::Pointer(pointer) => self.follow_pointer(*pointer).map(Some),
-            Register::AbstractValue(abstract_value) => Ok(Some(ValueRef::Abstract(abstract_value))),
             Register::Empty => Ok(None),
         }
     }
@@ -525,34 +538,43 @@ impl<'a> Vm<'a> {
         self.ip = new_ip;
     }
 
-    /// DRY helper to get a constant or register values
-    fn get_argument(&self, index: u16, is_constant: bool) -> Result<&ConcreteValue, VmError> {
-        let argument = if is_constant {
-            self.get_constant(index)?
-        } else {
-            match self.open_register(index)? {
-                ValueRef::Concrete(concrete_value) => concrete_value,
-                ValueRef::Abstract(abstract_value) => {
-                    return Err(VmError::ExpectedConcreteValue {
-                        found: abstract_value.clone(),
-                        position: self.current_position,
-                    })
-                }
-            }
+    fn get_destination(&self, destination: Destination) -> Result<u16, VmError> {
+        let index = match destination {
+            Destination::Register(register_index) => register_index,
+            Destination::Local(local_index) => self
+                .local_definitions
+                .get(local_index as usize)
+                .copied()
+                .flatten()
+                .ok_or_else(|| VmError::UndefinedLocal {
+                    local_index,
+                    position: self.current_position,
+                })?,
         };
 
-        Ok(argument)
+        Ok(index)
+    }
+
+    /// DRY helper to get a constant or register values
+    fn get_argument(&self, argument: Argument) -> Result<ValueRef, VmError> {
+        let value_ref = match argument {
+            Argument::Constant(constant_index) => {
+                ValueRef::Concrete(self.get_constant(constant_index)?)
+            }
+            Argument::Register(register_index) => self.open_register(register_index)?,
+            Argument::Local(local_index) => self.get_local(local_index)?,
+        };
+
+        Ok(value_ref)
     }
 
     /// DRY helper to get two arguments for binary operations
-    fn get_arguments(
-        &self,
-        instruction: Instruction,
-    ) -> Result<(&ConcreteValue, &ConcreteValue), VmError> {
-        let left = self.get_argument(instruction.b(), instruction.b_is_constant())?;
-        let right = self.get_argument(instruction.c(), instruction.c_is_constant())?;
+    fn get_arguments(&self, instruction: Instruction) -> Result<(ValueRef, ValueRef), VmError> {
+        let (left, right) = instruction.b_and_c_as_arguments();
+        let left_value = self.get_argument(left)?;
+        let right_value = self.get_argument(right)?;
 
-        Ok((left, right))
+        Ok((left_value, right_value))
     }
 
     fn set_register(&mut self, to_register: u16, register: Register) -> Result<(), VmError> {
@@ -600,15 +622,31 @@ impl<'a> Vm<'a> {
         }
     }
 
-    fn get_constant(&self, index: u16) -> Result<&ConcreteValue, VmError> {
+    fn get_constant(&self, constant_index: u16) -> Result<&ConcreteValue, VmError> {
         self.chunk
-            .get_constant(index)
+            .get_constant(constant_index)
             .map_err(|error| VmError::Chunk {
                 error,
                 position: self.current_position,
             })
     }
 
+    fn get_local(&self, local_index: u16) -> Result<ValueRef, VmError> {
+        let register_index = self
+            .local_definitions
+            .get(local_index as usize)
+            .ok_or_else(|| VmError::UndefinedLocal {
+                local_index,
+                position: self.current_position,
+            })?
+            .ok_or_else(|| VmError::UndefinedLocal {
+                local_index,
+                position: self.current_position,
+            })?;
+
+        self.open_register(register_index)
+    }
+
     fn read(&mut self) -> Result<Instruction, VmError> {
         let (instruction, _type, position) =
             self.chunk
@@ -628,8 +666,7 @@ impl<'a> Vm<'a> {
 #[derive(Clone, Debug, PartialEq)]
 pub enum Register {
     Empty,
-    ConcreteValue(ConcreteValue),
-    AbstractValue(AbstractValue),
+    Value(Value),
     Pointer(Pointer),
 }
 
@@ -637,9 +674,8 @@ impl Display for Register {
     fn fmt(&self, f: &mut Formatter) -> fmt::Result {
         match self {
             Self::Empty => write!(f, "empty"),
-            Self::ConcreteValue(value) => write!(f, "{}", value),
+            Self::Value(value) => write!(f, "{}", value),
             Self::Pointer(pointer) => write!(f, "{}", pointer),
-            Self::AbstractValue(value) => write!(f, "{}", value),
         }
     }
 }
@@ -699,7 +735,7 @@ pub enum VmError {
 
     // Execution errors
     ExpectedBoolean {
-        found: ConcreteValue,
+        found: Value,
         position: Span,
     },
     ExpectedFunction {