diff --git a/forge-script-lang/src/parser/grammar.rs b/forge-script-lang/src/parser/grammar.rs
index e4fd9f4f7c1cf1c36e655d52230969f18f9ee8f3..6cf95b30b34002fa815892970be78551c48922b3 100644
--- a/forge-script-lang/src/parser/grammar.rs
+++ b/forge-script-lang/src/parser/grammar.rs
@@ -10,7 +10,7 @@ peg::parser! {
 			= ex:expression_list() eof() { Program(ex) }
 
 		pub rule expression() -> Expression
-			= ex:value_expression() { Expression::Value(ex) }
+			= ex:precedence_expression() { Expression::Value(ex) }
 			/ ex:void_expression() { Expression::Void(ex) }
 
 		rule void_expression() -> VoidExpression
@@ -21,14 +21,16 @@ peg::parser! {
 
 		#[cache_left_rec]
 		rule value_expression() -> ValueExpression
-			= co:conditional_statement() { ValueExpression::ConditionalBlock(co) }
+			=
+			co:conditional_statement() { ValueExpression::ConditionalBlock(co) }
 			/ t:type_of() { ValueExpression::Typeof(t) }
+			/ precedence_expression()
 			/ decl:declare_variable() { ValueExpression::DeclareIdentifier(decl) }
 			/ decl:declare_function() { ValueExpression::DeclareFunction(decl) }
 			/ name:simple_identifier() "(" params:param_list()? ")"
 				{ ValueExpression::FunctionCall(FunctionCall { name, params: params.unwrap_or_default() }) }
-			/ left:value_expression() op:binary_operator() right:value_expression()
-				{ ValueExpression::Binary { lhs: Box::new(left), rhs: Box::new(right), operator: op } }
+			// / left:value_expression() op:binary_operator() right:value_expression()
+			// 	{ ValueExpression::Binary { lhs: Box::new(left), rhs: Box::new(right), operator: op } }
 			/ op:unary_operator() operand:value_expression()
 				{ ValueExpression::Unary { operator: op, operand: Box::new(operand) } }
 			/ grouped()
@@ -82,15 +84,37 @@ peg::parser! {
 		rule expression_list() -> ExpressionList
 			= ex:(expression() ** ";") term:";"? { ExpressionList { expressions: ex, is_void: term.is_some() } }
 
-		rule binary_operator() -> BinaryOp
-			= "+" { BinaryOp::Add }
-			/ "-" { BinaryOp::Subtract }
-			/ "*" { BinaryOp::Multiply }
-			/ "/" { BinaryOp::Divide }
-			/ "%" { BinaryOp::Modulo }
-			/ "==" { BinaryOp::Equals }
-			/ "&&" { BinaryOp::BoolAnd }
-			/ "||" { BinaryOp::BoolOr }
+		#[cache_left_rec]
+		rule precedence_expression() -> ValueExpression
+			= precedence! {
+				"-" z:(@) { ValueExpression::Unary { operator: UnaryOp::Negate, operand: Box::new(z) } }
+				--
+				x:(@) "+" y:@ { ValueExpression::Binary { operator: BinaryOp::Add, lhs: Box::new(x), rhs: Box::new(y) } }
+				x:(@) "-" y:@ { ValueExpression::Binary { operator: BinaryOp::Subtract, lhs: Box::new(x), rhs: Box::new(y) } }
+				--
+				x:(@) "*" y:@ { ValueExpression::Binary { operator: BinaryOp::Multiply, lhs: Box::new(x), rhs: Box::new(y) } }
+				x:(@) "/" y:@ { ValueExpression::Binary { operator: BinaryOp::Divide, lhs: Box::new(x), rhs: Box::new(y) } }
+				x:(@) "%" y:@ { ValueExpression::Binary { operator: BinaryOp::Modulo, lhs: Box::new(x), rhs: Box::new(y) } }
+				--
+				x:(@) "==" y:@ { ValueExpression::Binary { operator: BinaryOp::Equals, lhs: Box::new(x), rhs: Box::new(y) } }
+				x:(@) "&&" y:@ { ValueExpression::Binary { operator: BinaryOp::BoolAnd, lhs: Box::new(x), rhs: Box::new(y) } }
+				x:(@) "||" y:@ { ValueExpression::Binary { operator: BinaryOp::BoolOr, lhs: Box::new(x), rhs: Box::new(y) } }
+				--
+				"!" z:(@) { ValueExpression::Unary { operator: UnaryOp::Not, operand: Box::new(z) } }
+				--
+				"(" ex:precedence_expression() ")" { ValueExpression::Grouped(GroupedExpression { inner: Box::new(ex) }) }
+				val:value_expression() { val }
+			}
+
+		// rule binary_operator() -> BinaryOp
+		// 	= "+" { BinaryOp::Add }
+		// 	/ "-" { BinaryOp::Subtract }
+		// 	/ "*" { BinaryOp::Multiply }
+		// 	/ "/" { BinaryOp::Divide }
+		// 	/ "%" { BinaryOp::Modulo }
+		// 	/ "==" { BinaryOp::Equals }
+		// 	/ "&&" { BinaryOp::BoolAnd }
+		// 	/ "||" { BinaryOp::BoolOr }
 
 		rule unary_operator() -> UnaryOp
 			= "!" { UnaryOp::Not }
diff --git a/forge-script-lang/src/runtime/vm/chunk_builder.rs b/forge-script-lang/src/runtime/vm/chunk_builder.rs
new file mode 100644
index 0000000000000000000000000000000000000000..d67f6d1c723f2fcabca3ef88b081aee2fe565b3e
--- /dev/null
+++ b/forge-script-lang/src/runtime/vm/chunk_builder.rs
@@ -0,0 +1,138 @@
+use crate::format_forge_error;
+use crate::parse::ast::{BinaryOp, Program, UnaryOp, ValueExpression, VoidExpression};
+use crate::parser::ast::GroupedExpression;
+use crate::parser::parse_program;
+use crate::runtime::executor::Visitor;
+use crate::runtime::value::ForgeValue;
+use crate::runtime::vm::{Chunk, ChunkRef, OpCode, VmError};
+
+pub struct ChunkBuilder {
+	current_chunk: Chunk,
+}
+
+impl ChunkBuilder {
+	pub fn new() -> Self {
+		Self {
+			current_chunk: Chunk::default(),
+		}
+	}
+
+	pub fn parse(code: &str) -> Result<Chunk, VmError> {
+		let mut builder = ChunkBuilder::new();
+		let program =
+			parse_program(code).map_err(|fe| VmError::ast_parser(format_forge_error(code, &fe)))?;
+		builder.evaluate_program(&program)?;
+		Ok(builder.take_chunk())
+	}
+
+	pub fn chunk_ref(&self) -> ChunkRef {
+		self.current_chunk.as_ref()
+	}
+
+	pub fn take_chunk(&mut self) -> Chunk {
+		std::mem::take(&mut self.current_chunk)
+	}
+}
+
+impl Visitor for ChunkBuilder {
+	type Output = ();
+	type Error = VmError;
+
+	fn evaluate_value_expression(
+		&mut self,
+		expression: &ValueExpression,
+	) -> Result<Self::Output, Self::Error> {
+		match expression {
+			ValueExpression::Unary { operator, operand } => {
+				self.evaluate_value_expression(operand.as_ref())?;
+				match operator {
+					UnaryOp::Not => Err(VmError::chunk_parser("Unsupported expression")),
+					UnaryOp::Negate => {
+						self.current_chunk.push_op(OpCode::Invert);
+						Ok(())
+					}
+				}
+			}
+			ValueExpression::Binary { lhs, rhs, operator } => {
+				self.evaluate_value_expression(lhs.as_ref())?;
+				self.evaluate_value_expression(rhs.as_ref())?;
+				match operator {
+					BinaryOp::Add => {
+						self.current_chunk.push_op(OpCode::Add);
+						Ok(())
+					}
+					BinaryOp::Subtract => {
+						self.current_chunk.push_op(OpCode::Subtract);
+						Ok(())
+					}
+					BinaryOp::Multiply => {
+						self.current_chunk.push_op(OpCode::Multiply);
+						Ok(())
+					}
+					BinaryOp::Divide => {
+						self.current_chunk.push_op(OpCode::Divide);
+						Ok(())
+					}
+					BinaryOp::Modulo => Err(VmError::chunk_parser("Unsupported expression")),
+					BinaryOp::Equals => Err(VmError::chunk_parser("Unsupported expression")),
+					BinaryOp::BoolAnd => Err(VmError::chunk_parser("Unsupported expression")),
+					BinaryOp::BoolOr => Err(VmError::chunk_parser("Unsupported expression")),
+					BinaryOp::NotEquals => Err(VmError::chunk_parser("Unsupported expression")),
+					BinaryOp::LessThan => Err(VmError::chunk_parser("Unsupported expression")),
+					BinaryOp::GreaterThan => Err(VmError::chunk_parser("Unsupported expression")),
+					BinaryOp::LessThanEqual => Err(VmError::chunk_parser("Unsupported expression")),
+					BinaryOp::GreaterThanEqual => {
+						Err(VmError::chunk_parser("Unsupported expression"))
+					}
+				}
+			}
+			ValueExpression::Grouped(GroupedExpression { inner }) => {
+				self.evaluate_value_expression(inner.as_ref())
+			}
+			ValueExpression::Block(_) => Err(VmError::chunk_parser("Unsupported expression")),
+			ValueExpression::Literal(val) => {
+				self.current_chunk
+					.op_constant(ForgeValue::from(val.clone()));
+				Ok(())
+			}
+			ValueExpression::DeclareIdentifier(_) => {
+				Err(VmError::chunk_parser("Unsupported expression"))
+			}
+			ValueExpression::Assignment(_) => Err(VmError::chunk_parser("Unsupported expression")),
+			ValueExpression::ConditionalBlock(_) => {
+				Err(VmError::chunk_parser("Unsupported expression"))
+			}
+			ValueExpression::Identifier(_) => Err(VmError::chunk_parser("Unsupported expression")),
+			ValueExpression::FunctionCall(_) => {
+				Err(VmError::chunk_parser("Unsupported expression"))
+			}
+			ValueExpression::DeclareFunction(_) => {
+				Err(VmError::chunk_parser("Unsupported expression"))
+			}
+			ValueExpression::Typeof(_) => Err(VmError::chunk_parser("Unsupported expression")),
+		}
+	}
+
+	fn evaluate_void_expression(
+		&mut self,
+		expression: &VoidExpression,
+	) -> Result<Self::Output, Self::Error> {
+		match expression {
+			VoidExpression::ConditionLoop(_) => {
+				Err(VmError::chunk_parser("Unsupported expression"))
+			}
+			VoidExpression::Import(_) => Err(VmError::chunk_parser("Unsupported expression")),
+			VoidExpression::Export(_) => Err(VmError::chunk_parser("Unsupported expression")),
+			VoidExpression::Print(_) => Err(VmError::chunk_parser("Unsupported expression")),
+		}
+	}
+
+	fn evaluate_program(&mut self, program: &Program) -> Result<Self::Output, Self::Error> {
+		for expr in program.0.iter() {
+			self.evaluate_expression(expr)?;
+		}
+
+		self.current_chunk.push_op(OpCode::Return);
+		Ok(())
+	}
+}
diff --git a/forge-script-lang/src/runtime/vm/machine.rs b/forge-script-lang/src/runtime/vm/machine.rs
index 5159ba38466001a82663eb68f52a5a789f77086b..2be97ec3b72f21d8e517ff906cd58e9ae531f47f 100644
--- a/forge-script-lang/src/runtime/vm/machine.rs
+++ b/forge-script-lang/src/runtime/vm/machine.rs
@@ -1,8 +1,12 @@
 use crate::parser::ast::BinaryOp;
+use crate::parser::parse_program;
 use crate::runtime::value::{ForgeValue, UnsupportedOperation};
+use crate::runtime::vm::chunk_builder::ChunkBuilder;
 use crate::runtime::vm::chunks::Chunk;
 use crate::runtime::vm::{ChunkOps, OpCode};
+use crate::{format_forge_error, ForgeErrorKind};
 use std::collections::VecDeque;
+use std::error::Error;
 use std::fmt::{Display, Formatter};
 
 const INITIAL_STACK: usize = 512;
@@ -29,10 +33,25 @@ pub enum RuntimeErrorKind {
 	UnsupportedOperation(BinaryOp),
 }
 
-#[derive(Copy, Clone, Eq, PartialEq, Debug)]
+#[derive(Clone, Eq, PartialEq, Debug)]
+pub enum CompilerErrorKind {
+	AstParser(String),
+	Chunkbuilder(String),
+}
+
+#[derive(Clone, Eq, PartialEq, Debug)]
 pub enum VmError {
 	RuntimeError(RuntimeErrorKind),
-	CompilerError,
+	CompilerError(CompilerErrorKind),
+}
+
+impl VmError {
+	pub fn ast_parser(reason: impl ToString) -> VmError {
+		VmError::CompilerError(CompilerErrorKind::AstParser(reason.to_string()))
+	}
+	pub fn chunk_parser(reason: impl ToString) -> VmError {
+		VmError::CompilerError(CompilerErrorKind::Chunkbuilder(reason.to_string()))
+	}
 }
 
 impl From<UnsupportedOperation> for VmError {
@@ -44,12 +63,40 @@ impl From<UnsupportedOperation> for VmError {
 impl Display for VmError {
 	fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
 		match self {
-			Self::RuntimeError(kind) => write!(f, "{:?}", kind),
-			Self::CompilerError => write!(f, "Compiler Error"),
+			Self::RuntimeError(kind) => write!(f, "[Runtime Error] {}", kind),
+			Self::CompilerError(kind) => write!(f, "[Compiler Error] {}", kind),
 		}
 	}
 }
 
+impl Display for RuntimeErrorKind {
+	fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+		match self {
+			RuntimeErrorKind::MissingEof => write!(f, "Unexpected end of file"),
+			RuntimeErrorKind::InvalidConstData => {
+				write!(f, "Tried to access invalid constant data")
+			}
+			RuntimeErrorKind::InvalidStackData => write!(f, "Tried to access invalid stack data"),
+			RuntimeErrorKind::UnsupportedOperation(op) => write!(f, "Unsupported op: {:?}", op),
+		}
+	}
+}
+
+impl Display for CompilerErrorKind {
+	fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+		write!(
+			f,
+			"{}",
+			match self {
+				CompilerErrorKind::AstParser(err) => err.as_str(),
+				CompilerErrorKind::Chunkbuilder(err) => err.as_str(),
+			}
+		)
+	}
+}
+
+impl Error for VmError {}
+
 pub type VmResult = Result<ForgeValue, VmError>;
 
 impl Forge {
@@ -105,6 +152,16 @@ impl Forge {
 		Ok((second, first))
 	}
 
+	pub fn compile(value: impl ToString) -> Result<Chunk, VmError> {
+		let value = value.to_string();
+		ChunkBuilder::parse(value.as_str())
+	}
+
+	pub fn compile_and_run(value: impl ToString) -> VmResult {
+		let chunk = Forge::compile(value)?;
+		Forge::exec(chunk)
+	}
+
 	/// Execute a chunk of code in a new VM instance
 	pub fn exec(chunk: impl ChunkOps) -> VmResult {
 		let mut vm = Forge::from_chunk(chunk.as_chunk());
@@ -128,7 +185,6 @@ impl Forge {
 				OpCode::Return => return Ok(self.peek_stack().cloned().unwrap_or_default()),
 				OpCode::Constant(idx) => {
 					if let Some(value) = self.get_const(idx) {
-						println!("Load Const {:?}", &value);
 						self.push_stack(value.clone());
 					} else {
 						return Err(VmError::RuntimeError(RuntimeErrorKind::InvalidConstData));
diff --git a/forge-script-lang/src/runtime/vm/mod.rs b/forge-script-lang/src/runtime/vm/mod.rs
index d1d1b4336caa5632865ccc4c73061803c3cda1c3..3486e51db982c37d2ee167f2017bd183dbb9918a 100644
--- a/forge-script-lang/src/runtime/vm/mod.rs
+++ b/forge-script-lang/src/runtime/vm/mod.rs
@@ -1,3 +1,4 @@
+mod chunk_builder;
 mod chunks;
 mod const_data;
 mod machine;
diff --git a/forge-script/src/repl.rs b/forge-script/src/repl.rs
index e23a11c4ab0102a6bc2d35fc8b0264c7f8253cc5..1535fa84aa6e5f0f805c8354277c6d703429cf3c 100644
--- a/forge-script/src/repl.rs
+++ b/forge-script/src/repl.rs
@@ -64,8 +64,14 @@ impl Repl {
 					println!("=============\n");
 				}
 				other => {
-					println!("{}", other);
 					self.buffer.push(String::from(other));
+					println!(
+						"{}",
+						match Forge::compile_and_run(other) {
+							Ok(val) => format!("{}", val),
+							Err(e) => format!("{}", e),
+						}
+					);
 				}
 			}
 		}