diff --git a/grimoire/src/expr/mod.rs b/grimoire/src/ast/expr.rs
similarity index 87%
rename from grimoire/src/expr/mod.rs
rename to grimoire/src/ast/expr.rs
index fa12c7701d34e8d5ff0cf3c289b17f43b8642f83..7c4028c92b57fcf06416d6841e21e1359e928f6a 100644
--- a/grimoire/src/expr/mod.rs
+++ b/grimoire/src/ast/expr.rs
@@ -1,11 +1,8 @@
+use crate::ast::Location;
+
 #[derive(Debug)]
 pub enum VarOrConstant {
     Var(String),
-    Constant(Constant),
-}
-
-#[derive(Debug)]
-pub enum Constant {
     Int(i32),
     Flt(f32),
     Bool(bool),
@@ -14,9 +11,9 @@ pub enum Constant {
 
 #[derive(Debug)]
 pub enum Expression {
-    Binary(Box<Expression>, BinOp, Box<Expression>),
-    Unary(UnOp, Box<Expression>),
-    Leaf(VarOrConstant),
+    Binary(Location, Location, Box<Expression>, BinOp, Box<Expression>),
+    Unary(Location, Location, UnOp, Box<Expression>),
+    Leaf(Location, Location, VarOrConstant),
 }
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
diff --git a/grimoire/src/ast/location.rs b/grimoire/src/ast/location.rs
new file mode 100644
index 0000000000000000000000000000000000000000..32a6f608d8f91e47acdf9763df87009c8524692b
--- /dev/null
+++ b/grimoire/src/ast/location.rs
@@ -0,0 +1,5 @@
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub struct Location {
+    offset: usize,
+    line: u32,
+}
diff --git a/grimoire/src/ast/mod.rs b/grimoire/src/ast/mod.rs
new file mode 100644
index 0000000000000000000000000000000000000000..598d762a1ff5cadf9465ccbc9609da1615fd63d0
--- /dev/null
+++ b/grimoire/src/ast/mod.rs
@@ -0,0 +1,4 @@
+pub mod expr;
+mod location;
+
+pub use location::Location;
diff --git a/grimoire/src/lib.rs b/grimoire/src/lib.rs
index 1dc5c4853e9ff3780577039ea9243d8048585b9a..c3d4d7b87a891e57886afddc4cf32a442e1b8df6 100644
--- a/grimoire/src/lib.rs
+++ b/grimoire/src/lib.rs
@@ -1,5 +1,5 @@
+pub mod ast;
 pub mod error;
-pub mod expr;
 pub mod parser;
 pub mod spell;
 pub mod state;
diff --git a/grimoire/src/parser/error.rs b/grimoire/src/parser/error.rs
index 6c129bcce1f571c12b06517abf15206a8336acea..9ac891d8d7d94635892d374fec3c7c698891e13a 100644
--- a/grimoire/src/parser/error.rs
+++ b/grimoire/src/parser/error.rs
@@ -1,4 +1,4 @@
-use crate::parser::span::{Location, Span};
+use crate::{ast::Location, parser::Span};
 use nom::{IResult, error::*};
 use std::num::{ParseFloatError, ParseIntError};
 
diff --git a/grimoire/src/parser/expr.rs b/grimoire/src/parser/expr.rs
index 2f0b272801955f26e8c3674de085f5adfc94f2eb..ee6c32d5454fd7ccebc6ff9d69a2481ff59d2270 100644
--- a/grimoire/src/parser/expr.rs
+++ b/grimoire/src/parser/expr.rs
@@ -1,20 +1,37 @@
 use crate::{
-    expr::{BinOp, Expression, UnOp},
+    ast::{Location, expr::*},
     parser::*,
 };
 
 use nom::{
-    Parser, branch::alt, bytes::complete::tag, character::complete::multispace0, combinator::map,
-    multi::many0, sequence::preceded,
+    Parser,
+    branch::alt,
+    bytes::complete::tag,
+    character::complete::{digit1, multispace0},
+    combinator::map,
+    multi::many0,
+    sequence::preceded,
 };
 
+fn var_or_constant(s: Span) -> ParserResult<Expression> {
+    utils::map_with_locaiton(
+        alt((
+            map(digit1, |digits| todo!()),
+            map(utils::identifier("true"), |_| todo!()),
+            map(utils::identifier("false"), |_| todo!()),
+        )),
+        |begin, end, var_or_constant| todo!(),
+    )
+    .parse(s)
+}
+
 fn leaf(s: Span) -> ParserResult<Expression> {
+    // Note: the order is important!
     alt((
-        // 1
         unop(UnOp::Not, expression),
         unop(UnOp::Neg, expression),
-        // 2
         utils::parse_paren(expression),
+        var_or_constant,
     ))
     .parse(s)
 }
@@ -23,27 +40,37 @@ fn unop<'a>(
     op: UnOp,
     next: impl Parser<Span<'a>, Output = Expression, Error = ParserError>,
 ) -> impl Parser<Span<'a>, Output = Expression, Error = ParserError> {
-    map((multispace0, tag(op.as_str()), next), move |(.., expr)| {
-        Expression::Unary(op, Box::new(expr))
-    })
+    utils::map_with_locaiton(
+        (multispace0, tag(op.as_str()), next),
+        move |begin, end, (.., expr)| Expression::Unary(begin, end, op, Box::new(expr)),
+    )
 }
 
 fn binop<'a>(
     op: BinOp,
     next: impl Parser<Span<'a>, Output = Expression, Error = ParserError>,
-) -> impl Parser<Span<'a>, Output = (BinOp, Expression), Error = ParserError> {
-    preceded(multispace0, (map(tag(op.as_str()), move |_| op), next))
+) -> impl Parser<Span<'a>, Output = (Location, Location, BinOp, Expression), Error = ParserError> {
+    preceded(
+        multispace0,
+        utils::with_location((map(tag(op.as_str()), move |_| op), next))
+            .map(|(begin, end, (op, expr))| (begin, end, op, expr)),
+    )
 }
 
-fn fold_exprs(initial: Expression, remainder: Vec<(BinOp, Expression)>) -> Expression {
-    remainder.into_iter().fold(initial, |acc, (op, expr)| {
-        Expression::Binary(Box::new(acc), op, Box::new(expr))
-    })
+fn fold_exprs(
+    initial: Expression,
+    remainder: Vec<(Location, Location, BinOp, Expression)>,
+) -> Expression {
+    remainder
+        .into_iter()
+        .fold(initial, |acc, (begin, end, op, expr)| {
+            Expression::Binary(begin, end, Box::new(acc), op, Box::new(expr))
+        })
 }
 
 fn terms(s: Span) -> ParserResult<Expression> {
     let (s, initial) = leaf(s)?;
-    let (s, remainder): (Span, Vec<(BinOp, Expression)>) = many0(alt((
+    let (s, remainder): (Span, Vec<_>) = many0(alt((
         binop(BinOp::Mul, leaf),
         binop(BinOp::Div, leaf),
         binop(BinOp::Mod, leaf),
diff --git a/grimoire/src/parser/span.rs b/grimoire/src/parser/span.rs
index 3bfe1ad4778a7b51b29279d1fdd8997b9b9937ac..ae08a5fff924cca8609d0eddf086aba0ded5dbe1 100644
--- a/grimoire/src/parser/span.rs
+++ b/grimoire/src/parser/span.rs
@@ -1,11 +1,6 @@
+use crate::ast::Location;
 use nom::{Compare, Input, Offset as _};
 
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
-pub struct Location {
-    offset: usize,
-    line: u32,
-}
-
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 pub struct Span<'a> {
     /// The offset represents the position of the fragment relatively to
diff --git a/grimoire/src/parser/utils.rs b/grimoire/src/parser/utils.rs
index ca8516ddc4b9d7192e1738eb4f9050db8121c3aa..075a319901162720459b3e05212907b56e5d5177 100644
--- a/grimoire/src/parser/utils.rs
+++ b/grimoire/src/parser/utils.rs
@@ -1,4 +1,7 @@
-use crate::parser::{error::*, span::*};
+use crate::{
+    ast::Location,
+    parser::{error::*, span::*},
+};
 use nom::{
     Parser,
     bytes::complete::tag,
@@ -11,3 +14,33 @@ pub fn parse_paren<'a, T>(
 ) -> impl Parser<Span<'a>, Output = T, Error = ParserError> {
     preceded(multispace0, delimited(tag("("), inner, tag(")")))
 }
+
+pub fn with_location<'a, O>(
+    mut parser: impl Parser<Span<'a>, Output = O, Error = ParserError>,
+) -> impl Parser<Span<'a>, Output = (Location, Location, O), Error = ParserError> {
+    move |span: Span<'a>| {
+        let begin_s = multispace0(span)?.0;
+        let (end_s, res) = parser.parse(span)?;
+        Ok((end_s, (begin_s.get_location(), end_s.get_location(), res)))
+    }
+}
+
+pub fn map_with_locaiton<'a, F, O>(
+    mut parser: F,
+    mut cb: impl FnMut(Location, Location, <F as Parser<Span<'a>>>::Output) -> O,
+) -> impl Parser<Span<'a>, Output = O, Error = ParserError>
+where
+    F: Parser<Span<'a>, Error = ParserError>,
+{
+    move |span: Span<'a>| {
+        let begin_s = multispace0(span)?.0;
+        let (end_s, res) = parser.parse(span)?;
+        Ok((end_s, cb(begin_s.get_location(), end_s.get_location(), res)))
+    }
+}
+
+pub fn identifier<'a>(
+    ident: &'static str,
+) -> impl Parser<Span<'a>, Output = Span<'a>, Error = ParserError> {
+    todo!()
+}
diff --git a/grimoire/src/spell/arguments.rs b/grimoire/src/spell/arguments.rs
index 32807b726c1512e748d092d49cd6a1db5931ba14..bf623816ddc4d3f2411d2f038ac945d30138da0e 100644
--- a/grimoire/src/spell/arguments.rs
+++ b/grimoire/src/spell/arguments.rs
@@ -1,4 +1,4 @@
-use crate::expr::Expression;
+use crate::ast::expr::Expression;
 use std::collections::HashMap;
 
 /// Allowed types for arguments.