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 */