diff --git a/src/interpreter.rs b/src/interpreter.rs
new file mode 100644
index 0000000..ddffbb5
--- /dev/null
+++ b/src/interpreter.rs
@@ -0,0 +1,98 @@
+use crate::{
+    ast::Expr,
+    token::{LiteralType, Token, TokenType},
+};
+
+#[derive(Debug)]
+pub struct RuntimeError {
+    pub token: Token,
+    pub message: String,
+}
+
+impl RuntimeError {
+    pub fn new(token: &Token, message: &str) -> RuntimeError {
+        RuntimeError {
+            token: token.clone(),
+            message: message.to_string(),
+        }
+    }
+}
+
+pub fn interpret(expr: &Expr) -> Result<LiteralType, RuntimeError> {
+    match expr {
+        Expr::Ternary {
+            first,
+            second,
+            third,
+            ..
+        } => ternary(first, second, third),
+        Expr::Binary { left, op, right } => binary(&interpret(left)?, &interpret(right)?, op),
+        Expr::Grouping { expression } => interpret(expression),
+        Expr::Literal { value } => Ok(value.clone()),
+        Expr::Unary { op, right } => Ok(unary(&interpret(right)?, op)),
+    }
+}
+
+fn ternary(first: &Expr, second: &Expr, third: &Expr) -> Result<LiteralType, RuntimeError> {
+    let first = interpret(first)?;
+    if is_truthy(&first) {
+        return interpret(second);
+    }
+    interpret(third)
+}
+
+fn binary(
+    left: &LiteralType,
+    right: &LiteralType,
+    op: &Token,
+) -> Result<LiteralType, RuntimeError> {
+    use LiteralType::{Bool, Number, String};
+    use TokenType::{
+        BangEqual, Comma, EqualEqual, Greater, GreaterEqual, Less, LessEqual, Minus, Plus, Slash,
+        Star,
+    };
+
+    match (op.t_type, &left, &right) {
+        (Greater, Number(left), Number(right)) => Ok(Bool(left > right)),
+        (GreaterEqual, Number(left), Number(right)) => Ok(Bool(left >= right)),
+        (Less, Number(left), Number(right)) => Ok(Bool(left < right)),
+        (LessEqual, Number(left), Number(right)) => Ok(Bool(left <= right)),
+        (BangEqual, _, _) => Ok(Bool(!is_equal(left, right))),
+        (EqualEqual, _, _) => Ok(Bool(is_equal(left, right))),
+        (Minus, Number(left), Number(right)) => Ok(Number(left - right)),
+        (Plus, Number(left), Number(right)) => Ok(Number(left + right)),
+        (Plus, String(left), String(right)) => Ok(String(format!("{left}{right}"))),
+        (Slash, Number(left), Number(right)) => Ok(Number(left / right)),
+        (Star, Number(left), Number(right)) => Ok(Number(left * right)),
+        (Comma, _,_) => Ok(right.clone()),
+        (Greater | GreaterEqual | Less | LessEqual | Minus | Slash | Star, _, _) => Err(RuntimeError::new(op, "Operands must be numbers")),
+        (Plus, _, _) => Err(RuntimeError::new(op, "Operands must be two numbers or two strings")),
+        /* comma operator discard the left operand, so we just return the evaluation of the right operand */
+
+        _ => unreachable!("Shouldn't happen. Expr::Binary for interpret. Some case is a binary operation that wasn't matched")
+    }
+}
+
+fn unary(right: &LiteralType, op: &Token) -> LiteralType {
+    match (op.t_type, &right) {
+        (TokenType::Minus, LiteralType::Number(num)) => LiteralType::Number(-num),
+        (TokenType::Bang, _) => LiteralType::Bool(!is_truthy(right)),
+        _ => unreachable!("Shouldn't happen. Expr::Unary for interpret"),
+    }
+}
+
+fn is_truthy(literal: &LiteralType) -> bool {
+    match literal {
+        LiteralType::Nil => false,
+        LiteralType::Bool(val) => *val,
+        _ => true,
+    }
+}
+
+fn is_equal(left: &LiteralType, right: &LiteralType) -> bool {
+    match (left, right) {
+        (LiteralType::Nil, LiteralType::Nil) => true,
+        (LiteralType::Nil, _) => false,
+        _ => left == right,
+    }
+}
diff --git a/src/lib.rs b/src/lib.rs
index 54ac438..20220c8 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,43 +1,55 @@
 use std::{
     error::Error,
-    fmt::Display,
     fs,
     io::{self, Write},
 };
 
-use parser::{ParseError, Parser};
-use printer::pretty_print;
+use interpreter::RuntimeError;
+use parser::Parser;
 use scanner::Scanner;
 use token::{Token, TokenType};
 
 mod ast;
+mod interpreter;
 mod parser;
 mod printer;
 mod scanner;
 mod token;
 mod utils;
 
-pub fn run_file(path: &str) -> Result<(), Box<dyn Error>> {
-    let file = fs::read_to_string(path)?;
+#[derive(Debug)]
+pub enum RunError {
+    FileReadError(io::Error),
+    OtherError(Box<dyn Error>), // to be added,
+    RuntimeError(RuntimeError),
+}
+
+impl<E: Error + 'static> From<E> for RunError {
+    fn from(value: E) -> Self {
+        Self::OtherError(Box::new(value))
+    }
+}
+
+pub fn run_file(path: &str) -> Result<(), RunError> {
+    let file = fs::read_to_string(path).map_err(RunError::FileReadError)?;
 
     run(&file)?;
     Ok(())
 }
 
-pub fn run(src: &str) -> Result<(), Box<dyn Error>> {
+pub fn run(src: &str) -> Result<(), RunError> {
     let mut scanner = Scanner::new(src.to_string());
     let tokens = scanner.scan_tokens()?;
 
     let mut parser = Parser::new(tokens);
 
-    let expression = parser.parse();
+    let expression = parser.parse().inspect_err(|e| error(&e.token, &e.msg))?;
 
-    match expression {
-        Ok(expr) => println!("{}", pretty_print(&expr)),
-        Err(e) => {
-            error(e.token, &e.msg);
-        }
-    }
+    let interpreted_value = interpreter::interpret(&expression)
+        .inspect_err(runtime_error)
+        .map_err(RunError::RuntimeError)?;
+
+    println!("{interpreted_value}");
 
     Ok(())
 }
@@ -64,9 +76,13 @@ pub fn report(line: usize, location: &str, message: &str) {
     eprintln!("[line {line}] Error {location}: {message}");
 }
 
-fn error(token: Token, message: &str) {
+fn error(token: &Token, message: &str) {
     match token.t_type {
         TokenType::EOF => report(token.line, " at end", message),
         _ => report(token.line, &format!(" at '{}'", token.lexeme), message),
     }
 }
+
+fn runtime_error(err: &RuntimeError) {
+    eprintln!("{}\n[line {}]", err.message, err.token.line);
+}
diff --git a/src/main.rs b/src/main.rs
index 5422fa7..a291031 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,6 +1,6 @@
 use std::{env::args_os, ffi::OsString, process::ExitCode};
 
-use izanami::{run_file, run_prompt};
+use izanami::{run_file, run_prompt, RunError};
 
 fn main() -> ExitCode {
     let args: Vec<OsString> = args_os().collect();
@@ -11,10 +11,18 @@ fn main() -> ExitCode {
     } else if args.len() == 2 {
         let result = run_file(args[1].to_str().unwrap());
 
-        if let Err(res) = result {
-            println!("Couldn't read the file. Reason: {}", &*res);
+        if let Err(RunError::FileReadError(e)) = result {
+            println!("Couldn't read the file. Reason: {}", e);
             return ExitCode::from(1);
         }
+        if let Err(RunError::OtherError(e)) = result {
+            println!("Error occured. Error: {}", e);
+            return ExitCode::from(75);
+        }
+
+        if let Err(RunError::RuntimeError(r)) = result {
+            return ExitCode::from(70);
+        }
     } else {
         let result = run_prompt();
 
diff --git a/src/parser.rs b/src/parser.rs
index 7a016f6..f4b6ce2 100644
--- a/src/parser.rs
+++ b/src/parser.rs
@@ -1,3 +1,5 @@
+use std::fmt::Display;
+
 use crate::{
     ast::Expr,
     token::{LiteralType, Token, TokenType},
@@ -14,6 +16,14 @@ pub struct ParseError {
     pub msg: String,
 }
 
+impl Display for ParseError {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        write!(f, "ParseError: {} {}", self.token, self.msg)
+    }
+}
+
+impl std::error::Error for ParseError {}
+
 impl Parser<'_> {
     pub fn new(tokens: &Vec<Token>) -> Parser<'_> {
         Parser { tokens, current: 0 }
diff --git a/src/token.rs b/src/token.rs
index c674f81..5c2cae9 100644
--- a/src/token.rs
+++ b/src/token.rs
@@ -68,6 +68,17 @@ impl LiteralType {
     }
 }
 
+impl Display for LiteralType {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        match self {
+            LiteralType::String(v) => write!(f, "{v}"),
+            LiteralType::Number(v) => write!(f, "{v:.2}"),
+            LiteralType::Bool(v) => write!(f, "{v}"),
+            LiteralType::Nil => write!(f, "nil"),
+        }
+    }
+}
+
 #[derive(Debug, Clone)]
 pub struct Token {
     pub t_type: TokenType,