diff --git a/CMakeLists.txt b/CMakeLists.txt
index 934802709e84bfda69154d4ca65385429f3d0dda..76493d03aa95d40587a42cde6990f434f083521f 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -3,8 +3,8 @@ set(LIB_TARGET dusklib)
 set(PROJECT_VERSION 1.0.0)
 project(
     "dusk-lang"
-    VERSION ${PROJECT_VERSION}
     DESCRIPTION "Dusk programming language"
+    VERSION ${PROJECT_VERSION}
 )
 
 enable_language(C CXX)
diff --git a/include/dusk/AST/DiagnosticsParse.h b/include/dusk/AST/DiagnosticsParse.h
index dd5a041d5fba9cd13a661b874c06c0f6443ab9cb..b6995f287c8f57618f38b9090e53ab76e3f3617f 100644
--- a/include/dusk/AST/DiagnosticsParse.h
+++ b/include/dusk/AST/DiagnosticsParse.h
@@ -45,6 +45,11 @@ enum DiagID : unsigned {
   
   // Types
   expected_type_annotation,
+  expected_default_initialization,
+  expected_value_type_expression,
+  
+  type_missmatch,
+  undefined_identifier
 };
 
 static StringRef getTextForID(DiagID ID) {
@@ -93,6 +98,15 @@ static StringRef getTextForID(DiagID ID) {
       
   case DiagID::expected_type_annotation:
     return "Expected type annocation ': Type'.";
+  case DiagID::expected_default_initialization:
+    return "Expected default initialization";
+  case DiagID::expected_value_type_expression:
+    return "Expected value type expression.";
+      
+  case DiagID::type_missmatch:
+    return "Type mismatch.";
+  case DiagID::undefined_identifier:
+    return "Use of undefined identifier.";
   }
 }
 
diff --git a/include/dusk/AST/Expr.h b/include/dusk/AST/Expr.h
index 012ea14e7dffb74ea59b1523cb42e0f7190cd942..3e7f9c39b5a08061cfc6e2e7f2607c7866b4b565 100644
--- a/include/dusk/AST/Expr.h
+++ b/include/dusk/AST/Expr.h
@@ -28,6 +28,8 @@ class ExprPattern;
 class Stmt;
 class Pattern;
 class SubscriptPattern;
+class Type;
+class TypeRepr;
 class ASTWalker;
 
 /// Describes expression type.
@@ -46,12 +48,31 @@ enum struct ExprKind {
 class Expr : public ASTNode {
   /// Expression type
   ExprKind Kind;
+  
+  /// Type of declaration
+  Type *Ty;
+  
+  /// Type representation, if present
+  TypeRepr *TyRepr;
 
 public:
   Expr(ExprKind K) : Kind(K) {}
   virtual ~Expr() = default;
 
   ExprKind getKind() const { return Kind; }
+  
+  /// Returns declaration type
+  Type *getType() const { return Ty; }
+  
+  /// Sets declaration type
+  void setType(Type *T) { Ty = T; }
+  
+  /// Returns \c true if declaration has an explicit type specification,
+  /// \c false otherwise.
+  bool hasTypeRepr() const { return TyRepr != nullptr; }
+  
+  /// Returns type representation.
+  TypeRepr *getTypeRepr() const { return TyRepr; }
 };
 
 /// Number literal expression encalsulation.
diff --git a/include/dusk/AST/Pattern.h b/include/dusk/AST/Pattern.h
index 4499d99e363b4c3f7fe3bf55027246746fd4fb1d..1ee108a5911987178b93b47e309dc08824d1d9da 100644
--- a/include/dusk/AST/Pattern.h
+++ b/include/dusk/AST/Pattern.h
@@ -20,6 +20,7 @@ class ASTNode;
 class Decl;
 class Expr;
 class Stmt;
+class Type;
 class ParamDecl;
 
 /// Pattern description.
@@ -28,6 +29,9 @@ enum struct PatternKind { Expr, Variable };
 class Pattern {
   /// Pattern type.
   PatternKind Kind;
+  
+  /// Pattern type
+  Type *Ty;
 
 public:
   Pattern(PatternKind K);
@@ -35,6 +39,10 @@ public:
   
   virtual size_t count() const = 0;
   virtual SMRange getSourceRange() const = 0;
+  
+  void setType(Type *T) { Ty = T; }
+  Type *getType() const { return Ty; }
+  
   SMLoc getLocStart() { return getSourceRange().Start; }
   SMLoc getLocEnd() { return getSourceRange().End; }
 };
diff --git a/include/dusk/AST/Type.h b/include/dusk/AST/Type.h
index 1ff9cc8952e4ce7a404023e70b2289d1d8b37530..0b327c868e1cf8c0277b70c22df31c02dda2cbb9 100644
--- a/include/dusk/AST/Type.h
+++ b/include/dusk/AST/Type.h
@@ -13,15 +13,25 @@
 #include "dusk/Basic/LLVM.h"
 #include "dusk/Parse/Token.h"
 #include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/SmallVector.h"
 #include "llvm/Support/SMLoc.h"
 
 namespace dusk {
 class Type;
+class ValueType;
+class VoidType;
+class IntType;
+class FunctionType;
+class PatternType;
+  
 enum struct TypeKind;
 
 enum struct TypeKind {
   Void,
   Int,
+  Value,
+  Pattern,
   FuncRet
 };
 
@@ -37,6 +47,12 @@ public:
   virtual SMRange getSourceRange() const = 0;
   virtual bool isValueType() const { return false; }
   virtual bool isVoidType() const { return false; }
+  virtual bool isClassOf(const Type *T) const { return false; }
+  virtual bool isClassOf(const ValueType *T) const { return false; }
+  virtual bool isClassOf(const VoidType *T) const { return false; }
+  virtual bool isClassOf(const IntType *T) const { return false; }
+  virtual bool isClassOf(const FunctionType *T) const { return false; }
+  virtual bool isClassOf(const PatternType *T) const { return false; }
 };
   
 class ValueType : public Type {
@@ -50,20 +66,37 @@ class VoidType : public Type {
 public:
   VoidType();
   virtual bool isVoidType() const override { return false; }
+  virtual bool isClassOf(const VoidType *T) const override { return true; }
 };
 
 /// Integer type encapsulation.
 class IntType : public ValueType {
 public:
   IntType();
+  virtual bool isClassOf(const IntType *T) const override { return true; }
 };
   
 class FunctionType : public Type {
-  Type *ArgsType;
-  Type *RetType;
-  SMLoc ArrowLoc;
+  Type *ArgsTy;
+  Type *RetTy;
+  
+public:
+  FunctionType(Type *AT, Type *RT);
+  Type *getArgsType() const { return ArgsTy; }
+  Type *getRetType() const { return RetTy; }
+  virtual bool isClassOf(const FunctionType *T) const override;
 };
 
+
+class PatternType : public Type {
+  SmallVector<Type *, 128> Items;
+public:
+  PatternType(SmallVector<Type *, 128> &&I);
+  
+  ArrayRef<Type *> getItems() const { return Items; }
+  virtual bool isClassOf(const PatternType *T) const override;
+};
+  
   
 class FuncRetType : public Type {
   /// Location of arrow operator
diff --git a/include/dusk/CMakeLists.txt b/include/dusk/CMakeLists.txt
index 2d2097beea23fb8536b9f8822b83072d9ab16862..9b4b9d6d2d113c4f304c2eeed247a61a3572738b 100644
--- a/include/dusk/CMakeLists.txt
+++ b/include/dusk/CMakeLists.txt
@@ -3,6 +3,7 @@ add_subdirectory(Basic)
 add_subdirectory(Frontend)
 add_subdirectory(IRGen)
 add_subdirectory(Parse)
+add_subdirectory(Sema)
 
 set(HEADERS
     ${HEADERS}
diff --git a/include/dusk/Sema/CMakeLists.txt b/include/dusk/Sema/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..3492f65323b373a5375228f80813842f3752953f
--- /dev/null
+++ b/include/dusk/Sema/CMakeLists.txt
@@ -0,0 +1,7 @@
+set(HEADERS
+    ${CMAKE_CURRENT_SOURCE_DIR}/Context.h
+    ${CMAKE_CURRENT_SOURCE_DIR}/Scope.h
+    ${CMAKE_CURRENT_SOURCE_DIR}/Sema.h
+    ${HEADERS}
+    PARENT_SCOPE
+)
diff --git a/include/dusk/Sema/Context.h b/include/dusk/Sema/Context.h
new file mode 100644
index 0000000000000000000000000000000000000000..df3e54ebf43877ed549ae4eb959ef7ee5a00d416
--- /dev/null
+++ b/include/dusk/Sema/Context.h
@@ -0,0 +1,142 @@
+//===--- Context.h - Semantic declaration context ---------------*- C++ -*-===//
+//
+//                                 dusk-lang
+// This source file is part of a dusk-lang project, which is a semestral
+// assignement for BI-PJP course at Czech Technical University in Prague.
+// The software is provided "AS IS", WITHOUT WARRANTY OF ANY KIND.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef DUSK_SEMA_CONTEXT_H
+#define DUSK_SEMA_CONTEXT_H
+
+#include "dusk/Basic/LLVM.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/StringMap.h"
+#include <memory>
+
+namespace dusk {
+class Decl;
+  
+namespace sema {
+
+/// Represents current context declaration level.
+///
+/// Constructors of this struct are private, instances of \c ContextVals class
+/// can be made by only \c Context instances.
+struct ContextImpl {
+  /// Holds pointer to the parent context.
+  std::unique_ptr<ContextImpl> Parent = nullptr;
+  
+  /// Holds constant declarations of current scope.
+  llvm::StringMap<Decl *> Consts;
+  
+  /// Holds variable declarations of current scope.
+  llvm::StringMap<Decl *> Vars;
+  
+public:
+  /// Returns \c true if there is a reachable value delcaration for given name
+  /// the scope, \c false otherwise.
+  bool contains(StringRef Str) const { return get(Str) != nullptr; }
+  
+  /// Returns \c true if a declaration for given name was performed in current
+  /// scope.
+  bool isDeclared(StringRef Str) const;
+  
+  /// Returns variable for given name, if found, \c nullptr otherwise.
+  Decl *getVar(StringRef Str) const;
+  
+  /// Returns variable for given name, if found, \c nullptr otherwise.
+  Decl *getVar(StringRef Str);
+  
+  /// Returns variable for given name, if found, \c nullptr otherwise.
+  Decl *get(StringRef Str) const;
+  
+  /// Pushes a new context to the stack and returns a pointer to the top of the
+  /// stack.
+  ContextImpl *push();
+  
+  /// Pop a top context from the stack and returns a pointer to the top of the
+  /// stack.
+  ///
+  /// \note This method calls \c delete \c this on itself, therefore noone
+  /// should access this object after calling this method.
+  ContextImpl *pop();
+  
+private:
+  friend class Context;
+  ContextImpl() = default;
+  ContextImpl(ContextImpl *P);
+  
+  ContextImpl(const ContextImpl &other) = delete;
+  ContextImpl &operator=(const ContextImpl &other) = delete;
+};
+
+/// Represents a current declaration context.
+///
+/// Holds declaration of variables, constatnts and functions.
+class Context {
+  llvm::StringMap<Decl *> Funcs;
+  ContextImpl *Impl;
+  unsigned Depth = 0;
+  
+public:
+  Context();
+  ~Context();
+  
+  /// Returns current depth of the context.
+  unsigned getDepth() const { return Depth; }
+  
+  /// \brief Declares a variable in current scope.
+  ///
+  /// \return \c true on success, \c false if the current scope is already
+  /// a variable, constatnt or function with the same identifier.
+  bool declareVar(Decl *);
+  
+  /// \brief Declares a param in current scope.
+  ///
+  /// \return \c true on success, \c false if the current scope is already
+  /// a variable, constatnt or function with the same identifier.
+  bool declareLet(Decl *);
+  
+  /// \brief Declares a function.
+  ///
+  /// \note Function can be delcared only in the global scope.
+  ///
+  /// \return \c true on success, \c false if a function with given identifier
+  /// already exists.
+  bool declareFunc(Decl *);
+  
+  /// Returns a value for given identifier. Can be both, reference variable
+  /// and constant.
+  ///
+  /// If no value is found, \c nullptr is returned.
+  Decl *getVal(StringRef Str) const;
+  
+  /// Returns a value for given identifier. Can be both, reference variable
+  /// and constant.
+  ///
+  /// If no value is found, \c nullptr is returned.
+  Decl *getVar(StringRef Str) const;
+  
+  /// Returns function type for given identifier.
+  ///
+  /// If no type is found, \c nullptr is returned.
+  Decl *getFunc(StringRef Str);
+  
+  /// Returns \c true, if in the current scope if a declaration associated with
+  /// given identifier.
+  bool contains(StringRef Str) const;
+  
+  /// Pushes a new scope to the internal stack.
+  void push();
+  
+  /// Pops current scope from the internal stack.
+  void pop();
+};
+
+} // namespace sema
+  
+} // namespace dusk
+
+#endif /* DUSK_SEMA_CONTEXT_H */
diff --git a/include/dusk/Sema/Scope.h b/include/dusk/Sema/Scope.h
new file mode 100644
index 0000000000000000000000000000000000000000..c475ac890323baab8ad4527baa90e5a867b90399
--- /dev/null
+++ b/include/dusk/Sema/Scope.h
@@ -0,0 +1,103 @@
+//===--- Scope.h - Semantic scope -------------------------------*- C++ -*-===//
+//
+//                                 dusk-lang
+// This source file is part of a dusk-lang project, which is a semestral
+// assignement for BI-PJP course at Czech Technical University in Prague.
+// The software is provided "AS IS", WITHOUT WARRANTY OF ANY KIND.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef DUSK_SEMA_SCOPE_H
+#define DUSK_SEMA_SCOPE_H
+
+namespace dusk {
+  
+namespace sema {
+
+class Scope {
+public:
+  enum ScopeFlag : unsigned {
+
+    /// Scope corresponds to the scope of a function. It is valid for return
+    /// statement to exist in this scope.
+    FnScope = 0x01,
+
+    /// This is \c while and \c for loop scope, where it is valid to use a \c
+    /// break keyword.
+    BreakScope = 0x02,
+
+    /// Scope that corresponds to an arbitrary block of code.
+    BlockScope = 0x04,
+
+    /// Scope in \c if, \c while and \c for statements.
+    ControlScope = 0x08,
+
+    /// Scope correspoding to the function parameter delcaration.
+    FnProtoScope = 0x010,
+  };
+
+private:
+  /// A direct parent of current scope.
+  Scope *Parent;
+
+  /// Set of scope flags, which describe current scope.
+  unsigned Flags;
+
+  /// Depth of current scope.
+  unsigned Depth = 0;
+
+  /// Scope within a function definition.
+  Scope *FnParent;
+
+  /// Scope within another break scope.
+  Scope *BreakParent;
+
+  /// Scope within another control scope.
+  Scope *ControlParent;
+
+  /// Scope within another block scope.
+  Scope *BlockParent;
+
+public:
+  Scope();
+  Scope(Scope *Parent, unsigned ScopeFlags);
+
+  /// Returns flags describing current scope.
+  unsigned getFlags() const { return Flags; }
+
+  /// Returns current scope depth.
+  unsigned getDepth() const { return Depth; }
+
+  /// Returns direct parent scope.
+  Scope *getParent() const { return Parent; }
+
+  /// Returns direct \c function parent scope.
+  Scope *getFnParent() const { return FnParent; }
+
+  /// Returns direct \c break parent scope.
+  Scope *getBreakParent() const { return BreakParent; }
+
+  /// Returns direct \c control parent scope.
+  Scope *getControlParent() const { return ControlParent; }
+
+  /// Returns direct \c block parent scope.
+  Scope *getBlockParent() const { return BlockParent; }
+
+  /// Returns \c true if this is a function scope, \c false otherwise.
+  bool isFnScope() const { return Scope::FnScope & Flags; }
+
+  /// Returns \c true if this is a break scope, \c false otherwise.
+  bool isBreakScope() const { return Scope::BreakScope & Flags; }
+
+  /// Returns \c true if this is a control scope, \c false otherwise.
+  bool isControlScope() const { return Scope::ControlScope & Flags; }
+
+  /// Returns \c true if this is a block scope, \c false otherwise.
+  bool isBlockScope() const { return Scope::BlockScope & Flags; }
+};
+
+} // namespace sema
+
+} // namespace dusk
+
+#endif /* DUSK_SEMA_SCOPE_H */
diff --git a/include/dusk/Sema/Sema.h b/include/dusk/Sema/Sema.h
new file mode 100644
index 0000000000000000000000000000000000000000..d4cc86dd60ab835c8446b33af0d4cf60a55f394a
--- /dev/null
+++ b/include/dusk/Sema/Sema.h
@@ -0,0 +1,50 @@
+//===--- Sema.h - Dusk semantic analysis ------------------------*- C++ -*-===//
+//
+//                                 dusk-lang
+// This source file is part of a dusk-lang project, which is a semestral
+// assignement for BI-PJP course at Czech Technical University in Prague.
+// The software is provided "AS IS", WITHOUT WARRANTY OF ANY KIND.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef DUSK_SEMA_SEMA_H
+#define DUSK_SEMA_SEMA_H
+
+#include "dusk/AST/ASTContext.h"
+#include "dusk/AST/ASTNode.h"
+#include "dusk/AST/Diagnostics.h"
+#include "dusk/Sema/Context.h"
+#include "dusk/Sema/Scope.h"
+
+namespace dusk {
+class ASTContext;
+class Decl;
+class Expr;
+class Stmt;
+class Pattern;
+class Type;
+class TypeRepr;
+  
+namespace sema {
+class TypeChecker;
+  
+class Sema {
+  ASTContext &Ctx;
+  DiagnosticEngine &Diag;
+  
+  Context DeclCtx;
+  Scope Scp;
+  
+public:
+  Sema(ASTContext &C, DiagnosticEngine &D);
+  
+private:
+  void declareFuncs();
+  void typeCheck();
+};
+  
+} // namespace sema
+  
+} // namespace dusk
+
+#endif /* DUSK_SEMA_SEMA_H */
diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp
index 7ef3cf6827b9fe90ad51ba70c4730a2493736323..5de91ee114d9a3705369aba2a1e316d60127816d 100644
--- a/lib/AST/Type.cpp
+++ b/lib/AST/Type.cpp
@@ -19,10 +19,34 @@ VoidType::VoidType() : Type(TypeKind::Void) {}
 
 IntType::IntType() : ValueType(TypeKind::Int) {}
 
+// MARK: - Function type
 
+FunctionType::FunctionType(Type *AT, Type *RT)
+    : Type(TypeKind::FuncRet), ArgsTy(AT), RetTy(RT) {}
+
+bool FunctionType::isClassOf(const FunctionType *T) const {
+  return ArgsTy->isClassOf(T->ArgsTy) && RetTy->isClassOf(T->RetTy);
+}
+
+// MARK: - Pattern type
+
+PatternType::PatternType(SmallVector<Type *, 128> &&I)
+    : Type(TypeKind::Pattern), Items(I) {}
+
+bool PatternType::isClassOf(const PatternType *T) const {
+  if (Items.size() != T->Items.size())
+    return false;
+  for (size_t i = 0; i < Items.size(); i++) {
+    if (!Items[i]->isClassOf(T->Items[i]))
+      return false;
+  }
+  return true;
+}
+
+// ====----
 FuncRetType::FuncRetType(SMLoc AL, Token RTy)
     : Type(TypeKind::FuncRet), ArrowLoc(AL), RetType(RTy) {}
 
 SMRange FuncRetType::getSourceRange() const {
-  return { ArrowLoc, RetType.getRange().End };
+  return {ArrowLoc, RetType.getRange().End};
 }
diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt
index 306bd651fe7d5b24205374e338518aae6c895230..fe6191e233d9349418182f60aa1e9bfd114eed3a 100644
--- a/lib/CMakeLists.txt
+++ b/lib/CMakeLists.txt
@@ -3,6 +3,7 @@ add_subdirectory(Basic)
 add_subdirectory(Frontend)
 add_subdirectory(IRGen)
 add_subdirectory(Parser)
+add_subdirectory(Sema)
 
 set(SOURCE
     ${SOURCE}
diff --git a/lib/Sema/CMakeLists.txt b/lib/Sema/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..6199af45166d918b72cc7635d49352d40b41cece
--- /dev/null
+++ b/lib/Sema/CMakeLists.txt
@@ -0,0 +1,12 @@
+set(SOURCE
+    ${CMAKE_CURRENT_SOURCE_DIR}/Context.cpp
+    ${CMAKE_CURRENT_SOURCE_DIR}/Scope.cpp
+    ${CMAKE_CURRENT_SOURCE_DIR}/Sema.cpp
+    ${CMAKE_CURRENT_SOURCE_DIR}/TypeChecker.h
+    ${CMAKE_CURRENT_SOURCE_DIR}/TypeChecker.cpp
+    ${CMAKE_CURRENT_SOURCE_DIR}/TypeCheckDecl.cpp
+    ${CMAKE_CURRENT_SOURCE_DIR}/TypeCheckExpr.cpp
+    ${CMAKE_CURRENT_SOURCE_DIR}/TypeCheckStmt.cpp
+    ${SOURCE}
+    PARENT_SCOPE
+)
diff --git a/lib/Sema/Context.cpp b/lib/Sema/Context.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..6fde8fc8a91ec1d463eb3c81e0a95dbdafcf9d56
--- /dev/null
+++ b/lib/Sema/Context.cpp
@@ -0,0 +1,123 @@
+//===--- Context.cpp ------------------------------------------------------===//
+//
+//                                 dusk-lang
+// This source file is part of a dusk-lang project, which is a semestral
+// assignement for BI-PJP course at Czech Technical University in Prague.
+// The software is provided "AS IS", WITHOUT WARRANTY OF ANY KIND.
+//
+//===----------------------------------------------------------------------===//
+
+#include "dusk/Sema/Context.h"
+#include "dusk/AST/Decl.h"
+
+using namespace dusk;
+using namespace sema;
+
+// MARK: - Context values
+
+ContextImpl::ContextImpl(ContextImpl *P) : Parent(P) {}
+
+ContextImpl *ContextImpl::push() { return new ContextImpl(this); }
+
+ContextImpl *ContextImpl::pop() {
+  auto Ret = Parent.release();
+  delete this;
+  return Ret;
+}
+
+bool ContextImpl::isDeclared(StringRef Str) const {
+  return Vars.find(Str) != Vars.end() || Consts.find(Str) != Consts.end();
+}
+
+Decl *ContextImpl::getVar(StringRef Str) const {
+  auto Var = Vars.find(Str);
+  if (Var != Vars.end())
+    return Var->second;
+  
+  if (Parent != nullptr)
+    return Parent->getVar(Str);
+  return nullptr;
+}
+
+Decl *ContextImpl::getVar(StringRef Str) {
+  auto Var = Vars.find(Str);
+  if (Var != Vars.end())
+    return Var->second;
+  
+  if (Parent != nullptr)
+    return Parent->getVar(Str);
+  return nullptr;
+}
+
+Decl *ContextImpl::get(StringRef Str) const {
+  if (auto Var = getVar(Str))
+    return Var;
+  
+  auto Const = Consts.find(Str);
+  if (Const != Consts.end())
+    return Const->second;
+  
+  if (Parent != nullptr)
+    return Parent->get(Str);
+  return nullptr;
+}
+
+// MARK: - Context
+
+Context::Context(): Impl(new ContextImpl()) {}
+
+Context::~Context() { delete Impl; }
+
+bool Context::declareVar(Decl *D) {
+  // Check if already declared in current scope
+  if (Impl->isDeclared(D->getName()) || Funcs[D->getName()] != nullptr)
+    return false;
+  
+  Impl->Vars[D->getName()] = D;
+  return true;
+}
+
+bool Context::declareLet(Decl *D) {
+  // Check if already declared in current scope
+  if (Impl->isDeclared(D->getName()) || Funcs[D->getName()] != nullptr)
+    return false;
+  Impl->Consts[D->getName()] = D;
+  return true;
+}
+
+bool Context::declareFunc(Decl *Fn) {
+  // Validate that we're in global scope.
+  assert(Depth == 0 && "Function declaration must be declared in global scope");
+  
+  // Check if already declared in current scope
+  if (Funcs[Fn->getName()] != nullptr)
+    return false;
+  Funcs[Fn->getName()] = Fn;
+  return true;
+}
+
+Decl *Context::getVal(StringRef Str) const { return Impl->get(Str); }
+
+Decl *Context::getVar(StringRef Str) const { return Impl->getVar(Str); }
+
+Decl *Context::getFunc(StringRef Str) { return Funcs[Str]; }
+
+
+bool Context::contains(StringRef Str) const {
+  return Funcs.find(Str) != Funcs.end() || Impl->contains(Str);
+}
+
+void Context::push() {
+  ++Depth;
+  // Update the 'virtual' stack.
+  Impl = Impl->push();
+}
+
+void Context::pop() {
+  assert(Depth != 0 && "Cannot pop from global scope");
+  --Depth;
+  // Update the 'virtual' stack.
+  Impl = Impl->pop();
+}
+
+
diff --git a/lib/Sema/Scope.cpp b/lib/Sema/Scope.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..c9f6d28f2689c24c69199054fa002676fe2e1b7b
--- /dev/null
+++ b/lib/Sema/Scope.cpp
@@ -0,0 +1,24 @@
+//===--- Scope.cpp --------------------------------------------------------===//
+//
+//                                 dusk-lang
+// This source file is part of a dusk-lang project, which is a semestral
+// assignement for BI-PJP course at Czech Technical University in Prague.
+// The software is provided "AS IS", WITHOUT WARRANTY OF ANY KIND.
+//
+//===----------------------------------------------------------------------===//
+
+#include "dusk/Sema/Scope.h"
+
+using namespace dusk;
+using namespace sema;
+
+Scope::Scope() : Parent(nullptr), Flags(0) {}
+
+Scope::Scope(Scope *P, unsigned SF)
+    : Parent(P), Flags(Parent->getFlags() | SF), Depth(P->Depth + 1) {
+  FnParent = Parent->isFnScope() ? Parent : Parent->getFnParent();
+  BlockParent = Parent->isBlockScope() ? Parent : Parent->getBlockParent();
+  BreakParent = Parent->isBreakScope() ? Parent : Parent->getBreakParent();
+  ControlParent =
+      Parent->isControlScope() ? Parent : Parent->getControlParent();
+}
diff --git a/lib/Sema/Sema.cpp b/lib/Sema/Sema.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..15ddf46c991196b2faf44047cb6f45fda55d14d3
--- /dev/null
+++ b/lib/Sema/Sema.cpp
@@ -0,0 +1,70 @@
+//===--- Sema.cpp ---------------------------------------------------------===//
+//
+//                                 dusk-lang
+// This source file is part of a dusk-lang project, which is a semestral
+// assignement for BI-PJP course at Czech Technical University in Prague.
+// The software is provided "AS IS", WITHOUT WARRANTY OF ANY KIND.
+//
+//===----------------------------------------------------------------------===//
+
+#include "dusk/Sema/Sema.h"
+
+#include "dusk/AST/ASTWalker.h"
+#include "dusk/AST/Decl.h"
+#include "dusk/AST/Expr.h"
+#include "dusk/AST/Stmt.h"
+
+#include "TypeChecker.h"
+
+using namespace dusk;
+using namespace sema;
+
+namespace {
+  
+class FwdDeclarator: public ASTWalker {
+  Context &Ctx;
+  
+public:
+  FwdDeclarator(Context &C) : Ctx(C) {}
+  
+  virtual bool preWalk(Decl *D) override {
+    if (D->getKind() == DeclKind::Func)
+      return true;
+    else
+      return false;
+  }
+  
+  virtual bool postWalk(Decl *D) override {
+    if (!Ctx.declareFunc(D))
+      return false;
+    return true;
+  }
+  
+  // Skip all expressions.
+  virtual bool preWalk(Expr *E) override { return false; }
+  virtual bool preWalk(Stmt *S) override {
+    switch (S->getKind()) {
+      case StmtKind::Func:
+      case StmtKind::Extern:
+        return true;
+        
+      // Skip all non-func related statements.
+      default:
+        return false;
+    }
+  }
+};
+  
+} // anonymous namespace
+
+
+Sema::Sema(ASTContext &C, DiagnosticEngine &D) : Ctx(C), Diag(D) {}
+
+void Sema::declareFuncs() {
+  FwdDeclarator D{DeclCtx};
+  Ctx.getRootModule()->walk(D);
+}
+
+void Sema::typeCheck() {
+  
+}
diff --git a/lib/Sema/TypeCheckDecl.cpp b/lib/Sema/TypeCheckDecl.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..494da2246e58d27000c9b26df31158d96a82abbd
--- /dev/null
+++ b/lib/Sema/TypeCheckDecl.cpp
@@ -0,0 +1,106 @@
+//===--- TypeCheckStmt.cpp ------------------------------------------------===//
+//
+//                                 dusk-lang
+// This source file is part of a dusk-lang project, which is a semestral
+// assignement for BI-PJP course at Czech Technical University in Prague.
+// The software is provided "AS IS", WITHOUT WARRANTY OF ANY KIND.
+//
+//===----------------------------------------------------------------------===//
+
+#include "TypeChecker.h"
+
+#include "dusk/AST/Diagnostics.h"
+#include "dusk/Sema/Context.h"
+#include "dusk/Sema/Scope.h"
+
+using namespace dusk;
+using namespace sema;
+
+bool TypeChecker::preWalkLetDecl(LetDecl *D) {
+  // Check for initialization value
+  if (!D->hasValue()) {
+    Diag.diagnose(D->getLocEnd(), diag::expected_default_initialization);
+    return false;
+  }
+  
+  if (D->hasTypeRepr())
+    D->setType(nullptr/*typeResolve(D->getTypeRepr()*/);
+  else
+    // Infer type
+    D->setType(nullptr/*typeResolve(D->getValue()->getTypeRepr())*/);
+  return true;
+}
+
+bool TypeChecker::preWalkVarDecl(VarDecl *D) {
+  if (!D->hasValue() && !D->hasTypeRepr()) {
+    Diag.diagnose(D->getLocEnd(), diag::expected_type_specifier);
+    return false;
+  }
+  
+  if (D->hasTypeRepr())
+    D->setType(nullptr /*typeResolve(D->getTypeRepr())*/);
+  else
+    // Infer type
+    D->setType(nullptr /*typeResolve(D->getValue()->getTypeRepr())*/);
+  return true;
+}
+
+bool TypeChecker::preWalkParamDecl(ParamDecl *D) {
+  D->setType(nullptr /*typeResolve(D->getTypeRepr()*/);
+  return false;
+}
+
+bool TypeChecker::preWalkFuncDecl(FuncDecl *D) {
+  D->setType(nullptr /*typeResolve(D->getTypeRepr())*/);
+  return true;
+}
+
+bool TypeChecker::preWalkModuleDecl(ModuleDecl *D) {
+  return false;
+}
+
+
+bool TypeChecker::postWalkLetDecl(LetDecl *D) {
+  // Check if resolved both types
+  if (!D->getType() || !D->getValue()->getType())
+    return false;
+  
+  // Validate types
+  if (D->getType()->isClassOf(D->getValue()->getType()))
+    // If types match, declare
+    return Ctx.declareLet(D);
+  
+  Diag.diagnose(D->getValue()->getLocStart(), diag::type_missmatch);
+  return false;
+}
+
+bool TypeChecker::postWalkVarDecl(VarDecl *D) {
+  // Check if resolved both types
+  if (!D->getType() || !D->getValue()->getType())
+    return false;
+  
+  // Validate types
+  if (D->getType()->isClassOf(D->getValue()->getType()))
+    // If types match, declare
+    return Ctx.declareVar(D);
+  
+  Diag.diagnose(D->getValue()->getLocStart(), diag::type_missmatch);
+  return false;
+}
+
+bool TypeChecker::postWalkParamDecl(ParamDecl *D) {
+  // Check if has resolved type
+  if (!D->getType())
+    return false;
+  
+  // No default value, therefore don't have a reference to match against.
+  return Ctx.declareLet(D);
+}
+
+bool TypeChecker::postWalkFuncDecl(FuncDecl *D) {
+  return (D->getType()) != nullptr;
+}
+
+bool TypeChecker::postWalkModuleDecl(ModuleDecl *D) {
+  return false;
+}
diff --git a/lib/Sema/TypeCheckExpr.cpp b/lib/Sema/TypeCheckExpr.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..5e4556544a9ebb8cc707307390cf1f92c2bd52ba
--- /dev/null
+++ b/lib/Sema/TypeCheckExpr.cpp
@@ -0,0 +1,104 @@
+#include "TypeChecker.h"
+#include "dusk/AST/Diagnostics.h"
+#include "dusk/Sema/Context.h"
+
+using namespace dusk;
+using namespace sema;
+
+bool TypeChecker::preWalkNumberLiteralExpr(NumberLiteralExpr *E) {
+  return false;
+}
+
+bool TypeChecker::preWalkIdentifierExpr(IdentifierExpr *E) {
+  return E->getType() == nullptr;
+}
+
+bool TypeChecker::preWalkParenExpr(ParenExpr *E) {
+  return E->getType() == nullptr;
+}
+
+bool TypeChecker::preWalkAssignExpr(AssignExpr *E) {
+  return E->getType() == nullptr;
+}
+
+bool TypeChecker::preWalkInfixExpr(InfixExpr *E) {
+  return E->getType() == nullptr;
+}
+
+bool TypeChecker::preWalkPrefixExpr(PrefixExpr *E) {
+  return E->getType() == nullptr;
+}
+
+bool TypeChecker::preWalkCallExpr(CallExpr *E) {
+  return E->getType() == nullptr;
+}
+
+bool TypeChecker::preWalkSubscriptExpr(SubscriptExpr *E) { return false; }
+
+bool TypeChecker::postWalkNumberLiteralExpr(NumberLiteralExpr *E) {
+  llvm_unreachable("Not implemented");
+}
+
+bool TypeChecker::postWalkIdentifierExpr(IdentifierExpr *E) {
+  // Check if it's a value type
+  if (auto D = Ctx.getVal(E->getName())) {
+    E->setType(D->getType());
+    return true;
+  }
+  // Check if it's a function reference
+  if (auto Fn = Ctx.getFunc(E->getName())) {
+    E->setType(Fn->getType());
+    return true;
+  }
+  Diag.diagnose(E->getLocStart(), diag::undefined_identifier);
+  return false;
+}
+
+bool TypeChecker::postWalkParenExpr(ParenExpr *E) {
+  E->setType(E->getExpr()->getType());
+  return true;
+}
+
+bool TypeChecker::postWalkAssignExpr(AssignExpr *E) {
+  // Check type match.
+  if (E->getDest()->getType()->isClassOf(E->getSource()->getType())) {
+    E->setType(E->getDest()->getType());
+    return true;
+  }
+  Diag.diagnose(E->getDest()->getLocEnd(), diag::type_missmatch);
+  return false;
+}
+
+bool TypeChecker::postWalkInfixExpr(InfixExpr *E) {
+  if (!E->getLHS()->getType() || !E->getRHS()->getType())
+    return false;
+
+  // Check type match.
+  if (E->getLHS()->getType()->isClassOf(E->getRHS()->getType())) {
+    E->setType(E->getLHS()->getType());
+    return true;
+  }
+  Diag.diagnose(E->getOp().getLoc(), diag::type_missmatch);
+  return false;
+}
+
+bool TypeChecker::postWalkPrefixExpr(PrefixExpr *E) {
+  E->setType(E->getDest()->getType());
+  return true;
+}
+
+bool TypeChecker::postWalkCallExpr(CallExpr *E) {
+  auto FTy = static_cast<FunctionType *>(E->getCalle()->getType());
+  E->setType(FTy->getRetType());
+  
+  if (E->getArgs()->getType()->isClassOf(E->getArgs()->getType())) {
+    E->setType(FTy->getRetType());
+    return true;
+  } else {
+    return false;
+  }
+}
+
+bool TypeChecker::postWalkSubscriptExpr(SubscriptExpr *E) {
+  llvm_unreachable("Not implemented.");
+}
diff --git a/lib/Sema/TypeCheckStmt.cpp b/lib/Sema/TypeCheckStmt.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..4cf8a5669cc78cd8dfc207d2492bd1f20dc59f12
--- /dev/null
+++ b/lib/Sema/TypeCheckStmt.cpp
@@ -0,0 +1,121 @@
+//===--- TypeCheckStmt.cpp ------------------------------------------------===//
+//
+//                                 dusk-lang
+// This source file is part of a dusk-lang project, which is a semestral
+// assignement for BI-PJP course at Czech Technical University in Prague.
+// The software is provided "AS IS", WITHOUT WARRANTY OF ANY KIND.
+//
+//===----------------------------------------------------------------------===//
+
+#include "TypeChecker.h"
+
+#include "dusk/AST/Diagnostics.h"
+#include "dusk/Sema/Context.h"
+#include "dusk/Sema/Scope.h"
+
+using namespace dusk;
+using namespace sema;
+
+
+bool TypeChecker::preWalkBreakStmt(BreakStmt *S) {
+  return false;
+}
+
+bool TypeChecker::preWalkReturnStmt(ReturnStmt *S) {
+  return false;
+}
+
+bool TypeChecker::preWalkRangeStmt(RangeStmt *S) {
+  return false;
+}
+
+bool TypeChecker::preWalkSubscriptStmt(SubscriptStmt *S) {
+  return false;
+}
+
+bool TypeChecker::preWalkBlockStmt(BlockStmt *S) {
+  Ctx.push();
+  return false;
+}
+
+bool TypeChecker::preWalkExternStmt(ExternStmt *S) {
+  return false;
+}
+
+bool TypeChecker::preWalkForStmt(ForStmt *S) {
+  return false;
+}
+
+bool TypeChecker::preWalkFuncStmt(FuncStmt *S) {
+  return false;
+}
+
+bool TypeChecker::preWalkIfStmt(IfStmt *S) {
+  return false;
+}
+
+bool TypeChecker::preWalkWhileStmt(WhileStmt *S) {
+  return false;
+}
+
+
+bool TypeChecker::postWalkBreakStmt(BreakStmt *S) {
+  return true;
+}
+
+bool TypeChecker::postWalkReturnStmt(ReturnStmt *S) {
+  return true;
+}
+
+bool TypeChecker::postWalkRangeStmt(RangeStmt *S) {
+  if (!S->getStart()->getType()->isValueType()) {
+    Diag.diagnose(S->getStart()->getLocStart(),
+                  diag::expected_value_type_expression);
+    return false;
+  }
+  if (!S->getEnd()->getType()->isValueType()) {
+    Diag.diagnose(S->getEnd()->getLocStart(),
+                  diag::expected_value_type_expression);
+    return false;
+  }
+  return true;
+}
+
+bool TypeChecker::postWalkSubscriptStmt(SubscriptStmt *S) {
+  llvm_unreachable("Not implemented yet.");
+}
+
+bool TypeChecker::postWalkBlockStmt(BlockStmt *S) {
+  Ctx.pop();
+  return true;
+}
+
+bool TypeChecker::postWalkExternStmt(ExternStmt *S) {
+  return true;
+}
+
+bool TypeChecker::postWalkForStmt(ForStmt *S) {
+  return true;
+}
+
+bool TypeChecker::postWalkFuncStmt(FuncStmt *S) {
+  return true;
+}
+
+bool TypeChecker::postWalkIfStmt(IfStmt *S) {
+  if (S->getCond()->getType()->isValueType())
+    return true;
+  Diag.diagnose(S->getCond()->getLocStart(),
+                diag::expected_value_type_expression);
+  return false;
+
+}
+
+bool TypeChecker::postWalkWhileStmt(WhileStmt *S) {
+  if (S->getCond()->getType()->isValueType())
+    return true;
+  Diag.diagnose(S->getCond()->getLocStart(),
+                diag::expected_value_type_expression);
+  return false;
+}
+
diff --git a/lib/Sema/TypeChecker.cpp b/lib/Sema/TypeChecker.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..b0fc2ae9f5766fa9a694f9c31ba68a64c595b5a7
--- /dev/null
+++ b/lib/Sema/TypeChecker.cpp
@@ -0,0 +1,142 @@
+//===--- TypeChecker.cpp --------------------------------------------------===//
+//
+//                                 dusk-lang
+// This source file is part of a dusk-lang project, which is a semestral
+// assignement for BI-PJP course at Czech Technical University in Prague.
+// The software is provided "AS IS", WITHOUT WARRANTY OF ANY KIND.
+//
+//===----------------------------------------------------------------------===//
+
+#include "TypeChecker.h"
+
+#include "dusk/AST/Diagnostics.h"
+#include "dusk/Sema/Context.h"
+#include "dusk/Sema/Scope.h"
+
+using namespace dusk;
+using namespace sema;
+
+TypeChecker::TypeChecker(Context &C, Scope &S, DiagnosticEngine &D)
+    : Ctx(C), Scp(S), Diag(D) {}
+
+bool TypeChecker::preWalk(Decl *D) {
+  switch (D->getKind()) {
+  case DeclKind::Let:
+    return preWalkLetDecl(static_cast<LetDecl *>(D));
+  case DeclKind::Func:
+    return preWalkFuncDecl(static_cast<FuncDecl *>(D));
+  case DeclKind::Module:
+    return preWalkModuleDecl(static_cast<ModuleDecl *>(D));
+  case DeclKind::Param:
+    return preWalkParamDecl(static_cast<ParamDecl *>(D));
+  case DeclKind::Var:
+    return preWalkVarDecl(static_cast<VarDecl *>(D));
+  }
+}
+
+bool TypeChecker::postWalk(Decl *D) {
+  switch (D->getKind()) {
+  case DeclKind::Let:
+    return postWalkLetDecl(static_cast<LetDecl *>(D));
+  case DeclKind::Func:
+    return postWalkFuncDecl(static_cast<FuncDecl *>(D));
+  case DeclKind::Module:
+    return postWalkModuleDecl(static_cast<ModuleDecl *>(D));
+  case DeclKind::Param:
+    return postWalkParamDecl(static_cast<ParamDecl *>(D));
+  case DeclKind::Var:
+    return postWalkVarDecl(static_cast<VarDecl *>(D));
+  }
+}
+
+bool TypeChecker::preWalk(Expr *E) {
+  switch (E->getKind()) {
+  case ExprKind::NumberLiteral:
+    return preWalkNumberLiteralExpr(static_cast<NumberLiteralExpr *>(E));
+  case ExprKind::Identifier:
+    return preWalkIdentifierExpr(static_cast<IdentifierExpr *>(E));
+  case ExprKind::Paren:
+    return preWalkParenExpr(static_cast<ParenExpr *>(E));
+  case ExprKind::Assign:
+    return preWalkAssignExpr(static_cast<AssignExpr *>(E));
+  case ExprKind::Infix:
+    return preWalkInfixExpr(static_cast<InfixExpr *>(E));
+  case ExprKind::Prefix:
+    return preWalkPrefixExpr(static_cast<PrefixExpr *>(E));
+  case ExprKind::Call:
+    return preWalkCallExpr(static_cast<CallExpr *>(E));
+  case ExprKind::Subscript:
+    return preWalkSubscriptExpr(static_cast<SubscriptExpr *>(E));
+  }
+}
+
+bool TypeChecker::postWalk(Expr *E) {
+  switch (E->getKind()) {
+  case ExprKind::NumberLiteral:
+    return postWalkNumberLiteralExpr(static_cast<NumberLiteralExpr *>(E));
+  case ExprKind::Identifier:
+    return postWalkIdentifierExpr(static_cast<IdentifierExpr *>(E));
+  case ExprKind::Paren:
+    return postWalkParenExpr(static_cast<ParenExpr *>(E));
+  case ExprKind::Assign:
+    return postWalkAssignExpr(static_cast<AssignExpr *>(E));
+  case ExprKind::Infix:
+    return postWalkInfixExpr(static_cast<InfixExpr *>(E));
+  case ExprKind::Prefix:
+    return postWalkPrefixExpr(static_cast<PrefixExpr *>(E));
+  case ExprKind::Call:
+    return postWalkCallExpr(static_cast<CallExpr *>(E));
+  case ExprKind::Subscript:
+    return postWalkSubscriptExpr(static_cast<SubscriptExpr *>(E));
+  }
+}
+
+bool TypeChecker::preWalk(Stmt *S) {
+  switch (S->getKind()) {
+  case StmtKind::Break:
+    return preWalkBreakStmt(static_cast<BreakStmt *>(S));
+  case StmtKind::Return:
+    return preWalkReturnStmt(static_cast<ReturnStmt *>(S));
+  case StmtKind::Range:
+    return preWalkRangeStmt(static_cast<RangeStmt *>(S));
+  case StmtKind::Subscript:
+    return preWalkSubscriptStmt(static_cast<SubscriptStmt *>(S));
+  case StmtKind::Block:
+    return preWalkBlockStmt(static_cast<BlockStmt *>(S));
+  case StmtKind::Extern:
+    return preWalkExternStmt(static_cast<ExternStmt *>(S));
+  case StmtKind::For:
+    return preWalkForStmt(static_cast<ForStmt *>(S));
+  case StmtKind::Func:
+    return preWalkFuncStmt(static_cast<FuncStmt *>(S));
+  case StmtKind::If:
+    return preWalkIfStmt(static_cast<IfStmt *>(S));
+  case StmtKind::While:
+    return preWalkWhileStmt(static_cast<WhileStmt *>(S));
+  }
+}
+
+bool TypeChecker::postWalk(Stmt *S) {
+  switch (S->getKind()) {
+  case StmtKind::Break:
+    return postWalkBreakStmt(static_cast<BreakStmt *>(S));
+  case StmtKind::Return:
+    return postWalkReturnStmt(static_cast<ReturnStmt *>(S));
+  case StmtKind::Range:
+    return postWalkRangeStmt(static_cast<RangeStmt *>(S));
+  case StmtKind::Subscript:
+    return postWalkSubscriptStmt(static_cast<SubscriptStmt *>(S));
+  case StmtKind::Block:
+    return postWalkBlockStmt(static_cast<BlockStmt *>(S));
+  case StmtKind::Extern:
+    return postWalkExternStmt(static_cast<ExternStmt *>(S));
+  case StmtKind::For:
+    return postWalkForStmt(static_cast<ForStmt *>(S));
+  case StmtKind::Func:
+    return postWalkFuncStmt(static_cast<FuncStmt *>(S));
+  case StmtKind::If:
+    return postWalkIfStmt(static_cast<IfStmt *>(S));
+  case StmtKind::While:
+    return postWalkWhileStmt(static_cast<WhileStmt *>(S));
+  }
+}
diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h
new file mode 100644
index 0000000000000000000000000000000000000000..9ef2d66002c72e4939803b072ef41e3a95a05272
--- /dev/null
+++ b/lib/Sema/TypeChecker.h
@@ -0,0 +1,103 @@
+//===--- TypeChecker.h - Dusk type analyzer ---------------------*- C++ -*-===//
+//
+//                                 dusk-lang
+// This source file is part of a dusk-lang project, which is a semestral
+// assignement for BI-PJP course at Czech Technical University in Prague.
+// The software is provided "AS IS", WITHOUT WARRANTY OF ANY KIND.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef DUSK_SEMA_TYPE_CHECKER_H
+#define DUSK_SEMA_TYPE_CHECKER_H
+
+#include "dusk/AST/Decl.h"
+#include "dusk/AST/Expr.h"
+#include "dusk/AST/Stmt.h"
+#include "dusk/AST/ASTWalker.h"
+
+namespace dusk {
+class DiagnosticEngine;
+  
+namespace sema {
+class Context;
+class Scope;
+
+class TypeChecker : public ASTWalker {
+  Context &Ctx;
+  Scope &Scp;
+  DiagnosticEngine &Diag;
+
+public:
+  TypeChecker(Context &Ctx, Scope &S, DiagnosticEngine &Diag);
+
+  virtual bool preWalk(Decl *D) override;
+  virtual bool postWalk(Decl *D) override;
+
+  virtual bool preWalk(Expr *E) override;
+  virtual bool postWalk(Expr *E) override;
+
+  virtual bool preWalk(Stmt *S) override;
+  virtual bool postWalk(Stmt *S) override;
+
+private:
+  // MARK: - Declarations
+  bool preWalkLetDecl(LetDecl *D);
+  bool preWalkFuncDecl(FuncDecl *D);
+  bool preWalkModuleDecl(ModuleDecl *D);
+  bool preWalkParamDecl(ParamDecl *D);
+  bool preWalkVarDecl(VarDecl *D);
+
+  bool postWalkLetDecl(LetDecl *D);
+  bool postWalkFuncDecl(FuncDecl *D);
+  bool postWalkModuleDecl(ModuleDecl *D);
+  bool postWalkParamDecl(ParamDecl *D);
+  bool postWalkVarDecl(VarDecl *D);
+
+  // MARK: - Expressions
+  bool preWalkNumberLiteralExpr(NumberLiteralExpr *E);
+  bool preWalkIdentifierExpr(IdentifierExpr *E);
+  bool preWalkParenExpr(ParenExpr *E);
+  bool preWalkAssignExpr(AssignExpr *E);
+  bool preWalkInfixExpr(InfixExpr *E);
+  bool preWalkPrefixExpr(PrefixExpr *E);
+  bool preWalkCallExpr(CallExpr *E);
+  bool preWalkSubscriptExpr(SubscriptExpr *E);
+
+  bool postWalkNumberLiteralExpr(NumberLiteralExpr *E);
+  bool postWalkIdentifierExpr(IdentifierExpr *E);
+  bool postWalkParenExpr(ParenExpr *E);
+  bool postWalkAssignExpr(AssignExpr *E);
+  bool postWalkInfixExpr(InfixExpr *E);
+  bool postWalkPrefixExpr(PrefixExpr *E);
+  bool postWalkCallExpr(CallExpr *E);
+  bool postWalkSubscriptExpr(SubscriptExpr *E);
+
+  // MARK: - Statements
+  bool preWalkBreakStmt(BreakStmt *S);
+  bool preWalkReturnStmt(ReturnStmt *S);
+  bool preWalkRangeStmt(RangeStmt *S);
+  bool preWalkSubscriptStmt(SubscriptStmt *S);
+  bool preWalkBlockStmt(BlockStmt *S);
+  bool preWalkExternStmt(ExternStmt *S);
+  bool preWalkForStmt(ForStmt *S);
+  bool preWalkFuncStmt(FuncStmt *S);
+  bool preWalkIfStmt(IfStmt *S);
+  bool preWalkWhileStmt(WhileStmt *S);
+
+  bool postWalkBreakStmt(BreakStmt *S);
+  bool postWalkReturnStmt(ReturnStmt *S);
+  bool postWalkRangeStmt(RangeStmt *S);
+  bool postWalkSubscriptStmt(SubscriptStmt *S);
+  bool postWalkBlockStmt(BlockStmt *S);
+  bool postWalkExternStmt(ExternStmt *S);
+  bool postWalkForStmt(ForStmt *S);
+  bool postWalkFuncStmt(FuncStmt *S);
+  bool postWalkIfStmt(IfStmt *S);
+  bool postWalkWhileStmt(WhileStmt *S);
+};
+
+} // namespace sema
+
+} // namespace dusk
+
+#endif /* DUSK_SEMA_TYPE_CHECKER_H */