Compare commits

..

No commits in common. "main" and "typechecker" have entirely different histories.

20 changed files with 237 additions and 2102 deletions

View File

@ -24,7 +24,6 @@ add_executable(${PROJECT_NAME}
src/types.cpp
src/typechecker.cpp
src/binops.cpp
src/casting.cpp
)
target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_20)
target_compile_options(${PROJECT_NAME} PRIVATE -Wall -Weffc++ -Wextra -Wpedantic -Werror)

View File

@ -29,11 +29,7 @@ Currently the stages are as follows:
a format that is easier for the computer to process. The AST itself lives in
[`src/ast.h`](src/ast.h), and the code for the parsing phase lives in
[`src/parsing.cpp`](src/parsing.cpp).
4. In the typechecking stage we do static analysis on the generated AST to make
sure expected types match true types, and do other checks (such as checking
that the correct amount of parameters is provided in function calls). The
source code for this stage lives in
[`src/typechecker.cpp`](src/typechecker.cpp).
4. **TODO:** Typechecking phase hasn't yet been developed, but it will go here.
5. Finally the program is **compiled**, or in other words **code-generated**,
hence why this is the **codegen** stage. This is where the AST from the
previous stages is taken and LLVM Intermediate Representation is produced

View File

@ -42,54 +42,6 @@ namespace AST {
return out.str();
}
std::string CastExpression::formatted() {
std::stringstream out{ "" };
out << "(" << this->m_ty->formatted() << ")";
out << this->m_expr->formatted();
return out.str();
}
std::string RefExpression::formatted() {
std::stringstream out{ "" };
out << "&" << this->m_expr->formatted();
return out.str();
}
std::string DerefExpression::formatted() {
std::stringstream out{ "" };
out << "*" << this->m_expr->formatted();
return out.str();
}
std::string IndexAccessExpression::formatted() {
std::stringstream out{ "" };
out << this->m_expr->formatted();
out << "[" << this->m_num << "]";
return out.str();
}
std::string FieldAccessExpression::formatted() {
std::stringstream out{ "" };
out << this->m_expr->formatted();
out << "." << this->m_field;
return out.str();
}
std::string ListInitializerExpression::formatted() {
std::stringstream out{ "" };
out << "{ ";
int counter = 0;
for (auto& expr : this->m_expressions) {
if (counter++ > 0)
out << ", ";
out << expr->formatted();
}
out << " }";
return out.str();
}
std::string ExpressionStatement::formatted() {
std::stringstream out{ "" };
out << this->m_expr->formatted();
@ -156,8 +108,4 @@ namespace AST {
}
return out.str();
}
std::string TopLevelTypedef::formatted() {
return this->m_ty->formatted();
}
}

212
src/ast.h
View File

@ -25,9 +25,7 @@ namespace AST {
public:
Expression(token::Metadata meta) : Node{ meta } {}
virtual codegen::StackValue codegen(codegen::Builder& builder, codegen::Scope& scope) = 0;
virtual std::shared_ptr<types::Type> get_codegen_type(codegen::Scope& scope) = 0;
virtual void typecheck_preprocess(typecheck::Scope& scope) = 0;
virtual typecheck::ExpressionType typecheck(
virtual std::shared_ptr<types::Type> typecheck(
typecheck::State& state,
typecheck::Scope& scope,
std::optional<std::shared_ptr<types::Type>> expected_ty
@ -38,36 +36,24 @@ namespace AST {
public:
Statement(token::Metadata meta) : Node{ meta } {}
virtual void codegen(codegen::Builder& builder, codegen::Scope& scope) = 0;
virtual void typecheck_preprocess(typecheck::Scope& scope) = 0;
virtual void typecheck(typecheck::State& state, typecheck::Scope& scope) = 0;
};
/// @brief Any integer literal
class IntLiteralExpression : public Expression {
private:
int m_value;
std::shared_ptr<types::Type> m_ty;
public:
IntLiteralExpression(token::Metadata meta, int value)
: Expression{ meta }
, m_value{ value }
, m_ty{ { std::shared_ptr<types::Type>{
new types::FundamentalType{true, types::FundamentalTypeKind::Int}
} } } {
}
IntLiteralExpression(token::Metadata meta, int value) : Expression{ meta }, m_value{ value } {}
virtual ~IntLiteralExpression() override = default;
virtual std::string formatted() override;
virtual codegen::StackValue codegen(codegen::Builder& builder, codegen::Scope& scope) override;
virtual std::shared_ptr<types::Type> get_codegen_type(codegen::Scope& scope) override;
virtual void typecheck_preprocess(typecheck::Scope& scope) override;
virtual typecheck::ExpressionType typecheck(
virtual std::shared_ptr<types::Type> typecheck(
typecheck::State& state,
typecheck::Scope& scope,
std::optional<std::shared_ptr<types::Type>> expected_ty
) override;
};
/// @brief i.e. "contents"
class StringLiteralExpression : public Expression {
private:
std::string m_value;
@ -76,16 +62,13 @@ namespace AST {
virtual ~StringLiteralExpression() override = default;
virtual std::string formatted() override;
virtual codegen::StackValue codegen(codegen::Builder& builder, codegen::Scope& scope) override;
virtual std::shared_ptr<types::Type> get_codegen_type(codegen::Scope& scope) override;
virtual void typecheck_preprocess(typecheck::Scope& scope) override;
virtual typecheck::ExpressionType typecheck(
virtual std::shared_ptr<types::Type> typecheck(
typecheck::State& state,
typecheck::Scope& scope,
std::optional<std::shared_ptr<types::Type>> expected_ty
) override;
};
/// @brief Anything that references by value
class ValueReferenceExpression : public Expression {
private:
std::string m_name;
@ -94,16 +77,13 @@ namespace AST {
virtual ~ValueReferenceExpression() override = default;
virtual std::string formatted() override;
virtual codegen::StackValue codegen(codegen::Builder& builder, codegen::Scope& scope) override;
virtual std::shared_ptr<types::Type> get_codegen_type(codegen::Scope& scope) override;
virtual void typecheck_preprocess(typecheck::Scope& scope) override;
virtual typecheck::ExpressionType typecheck(
virtual std::shared_ptr<types::Type> typecheck(
typecheck::State& state,
typecheck::Scope& scope,
std::optional<std::shared_ptr<types::Type>> expected_ty
) override;
};
/// @brief Any binary operation (i.e. lhs + rhs)
class BinaryOperationExpression : public Expression {
private:
std::unique_ptr<Expression> m_lhs;
@ -123,16 +103,13 @@ namespace AST {
virtual ~BinaryOperationExpression() override = default;
virtual std::string formatted() override;
virtual codegen::StackValue codegen(codegen::Builder& builder, codegen::Scope& scope) override;
virtual std::shared_ptr<types::Type> get_codegen_type(codegen::Scope& scope) override;
virtual void typecheck_preprocess(typecheck::Scope& scope) override;
virtual typecheck::ExpressionType typecheck(
virtual std::shared_ptr<types::Type> typecheck(
typecheck::State& state,
typecheck::Scope& scope,
std::optional<std::shared_ptr<types::Type>> expected_ty
) override;
};
/// @brief Same as fn()/// @brief
class FunctionCallExpression : public Expression {
private:
std::unique_ptr<Expression> m_fn_expr;
@ -149,159 +126,7 @@ namespace AST {
virtual ~FunctionCallExpression() override = default;
virtual std::string formatted() override;
virtual codegen::StackValue codegen(codegen::Builder& builder, codegen::Scope& scope) override;
virtual std::shared_ptr<types::Type> get_codegen_type(codegen::Scope& scope) override;
virtual void typecheck_preprocess(typecheck::Scope& scope) override;
virtual typecheck::ExpressionType typecheck(
typecheck::State& state,
typecheck::Scope& scope,
std::optional<std::shared_ptr<types::Type>> expected_ty
) override;
};
/// @brief Same as (type) value
class CastExpression : public Expression {
private:
std::shared_ptr<types::Type> m_ty;
std::unique_ptr<Expression> m_expr;
public:
CastExpression(
token::Metadata meta,
std::shared_ptr<types::Type> type,
std::unique_ptr<Expression> expr)
: Expression{ meta }
, m_ty{ type }
, m_expr{ std::move(expr) } {
}
virtual ~CastExpression() override = default;
virtual std::string formatted() override;
virtual codegen::StackValue codegen(codegen::Builder& builder, codegen::Scope& scope) override;
virtual std::shared_ptr<types::Type> get_codegen_type(codegen::Scope& scope) override;
virtual void typecheck_preprocess(typecheck::Scope& scope) override;
virtual typecheck::ExpressionType typecheck(
typecheck::State& state,
typecheck::Scope& scope,
std::optional<std::shared_ptr<types::Type>> expected_ty
) override;
};
/// @brief Same as &value
class RefExpression : public Expression {
private:
std::unique_ptr<Expression> m_expr;
public:
RefExpression(
token::Metadata meta,
std::unique_ptr<Expression> expr)
: Expression{ meta }
, m_expr{ std::move(expr) } {
}
virtual ~RefExpression() override = default;
virtual std::string formatted() override;
virtual codegen::StackValue codegen(codegen::Builder& builder, codegen::Scope& scope) override;
virtual std::shared_ptr<types::Type> get_codegen_type(codegen::Scope& scope) override;
virtual void typecheck_preprocess(typecheck::Scope& scope) override;
virtual typecheck::ExpressionType typecheck(
typecheck::State& state,
typecheck::Scope& scope,
std::optional<std::shared_ptr<types::Type>> expected_ty
) override;
};
/// @brief Same as *value
class DerefExpression : public Expression {
private:
std::unique_ptr<Expression> m_expr;
public:
DerefExpression(
token::Metadata meta,
std::unique_ptr<Expression> expr)
: Expression{ meta }
, m_expr{ std::move(expr) } {
}
virtual ~DerefExpression() override = default;
virtual std::string formatted() override;
virtual codegen::StackValue codegen(codegen::Builder& builder, codegen::Scope& scope) override;
virtual std::shared_ptr<types::Type> get_codegen_type(codegen::Scope& scope) override;
virtual void typecheck_preprocess(typecheck::Scope& scope) override;
virtual typecheck::ExpressionType typecheck(
typecheck::State& state,
typecheck::Scope& scope,
std::optional<std::shared_ptr<types::Type>> expected_ty
) override;
};
/// @brief Same as value[num]
class IndexAccessExpression : public Expression {
private:
std::unique_ptr<Expression> m_expr;
uint32_t m_num;
public:
IndexAccessExpression(
token::Metadata meta,
std::unique_ptr<Expression> expr,
uint32_t num)
: Expression{ meta }
, m_expr{ std::move(expr) }
, m_num{ num } {
}
virtual ~IndexAccessExpression() override = default;
virtual std::string formatted() override;
virtual codegen::StackValue codegen(codegen::Builder& builder, codegen::Scope& scope) override;
virtual std::shared_ptr<types::Type> get_codegen_type(codegen::Scope& scope) override;
virtual void typecheck_preprocess(typecheck::Scope& scope) override;
virtual typecheck::ExpressionType typecheck(
typecheck::State& state,
typecheck::Scope& scope,
std::optional<std::shared_ptr<types::Type>> expected_ty
) override;
};
/// @brief Same as value.field
class FieldAccessExpression : public Expression {
private:
std::unique_ptr<Expression> m_expr;
std::string m_field;
public:
FieldAccessExpression(
token::Metadata meta,
std::unique_ptr<Expression> expr,
std::string field)
: Expression{ meta }
, m_expr{ std::move(expr) }
, m_field{ field } {
}
virtual ~FieldAccessExpression() override = default;
virtual std::string formatted() override;
virtual codegen::StackValue codegen(codegen::Builder& builder, codegen::Scope& scope) override;
virtual std::shared_ptr<types::Type> get_codegen_type(codegen::Scope& scope) override;
virtual void typecheck_preprocess(typecheck::Scope& scope) override;
virtual typecheck::ExpressionType typecheck(
typecheck::State& state,
typecheck::Scope& scope,
std::optional<std::shared_ptr<types::Type>> expected_ty
) override;
};
/// @brief Same as {value1, value2}
class ListInitializerExpression : public Expression {
private:
std::vector<std::unique_ptr<Expression>> m_expressions;
std::shared_ptr<types::Type> m_ty;
public:
ListInitializerExpression(
token::Metadata meta,
std::vector<std::unique_ptr<Expression>> expressions,
std::shared_ptr<types::Type> type)
: Expression{ meta }
, m_expressions{ std::move(expressions) }
, m_ty{ type } {
}
virtual ~ListInitializerExpression() override = default;
virtual std::string formatted() override;
virtual codegen::StackValue codegen(codegen::Builder& builder, codegen::Scope& scope) override;
virtual std::shared_ptr<types::Type> get_codegen_type(codegen::Scope& scope) override;
virtual void typecheck_preprocess(typecheck::Scope& scope) override;
virtual typecheck::ExpressionType typecheck(
virtual std::shared_ptr<types::Type> typecheck(
typecheck::State& state,
typecheck::Scope& scope,
std::optional<std::shared_ptr<types::Type>> expected_ty
@ -319,7 +144,6 @@ namespace AST {
virtual ~ReturnStatement() override = default;
virtual std::string formatted() override;
virtual void codegen(codegen::Builder& builder, codegen::Scope& scope) override;
virtual void typecheck_preprocess(typecheck::Scope& scope) override;
virtual void typecheck(typecheck::State& state, typecheck::Scope& scope) override;
};
@ -342,7 +166,6 @@ namespace AST {
virtual ~InitializationStatement() override = default;
virtual std::string formatted() override;
virtual void codegen(codegen::Builder& builder, codegen::Scope& scope) override;
virtual void typecheck_preprocess(typecheck::Scope& scope) override;
virtual void typecheck(typecheck::State& state, typecheck::Scope& scope) override;
};
@ -356,7 +179,6 @@ namespace AST {
virtual ~ExpressionStatement() override = default;
virtual std::string formatted() override;
virtual void codegen(codegen::Builder& builder, codegen::Scope& scope) override;
virtual void typecheck_preprocess(typecheck::Scope& scope) override;
virtual void typecheck(typecheck::State& state, typecheck::Scope& scope) override;
};
@ -378,7 +200,6 @@ namespace AST {
virtual ~IfStatement() override = default;
virtual std::string formatted() override;
virtual void codegen(codegen::Builder& builder, codegen::Scope& scope) override;
virtual void typecheck_preprocess(typecheck::Scope& scope) override;
virtual void typecheck(typecheck::State& state, typecheck::Scope& scope) override;
};
@ -386,7 +207,6 @@ namespace AST {
public:
TopLevelStatement(token::Metadata meta) : Node{ meta } {}
virtual void codegen(codegen::Builder& builder, codegen::Scope& scope) = 0;
virtual void typecheck_preprocess(typecheck::Scope& scope) = 0;
virtual void typecheck(typecheck::State& state, typecheck::Scope& scope) = 0;
};
@ -415,24 +235,6 @@ namespace AST {
virtual ~Function() override = default;
virtual std::string formatted() override;
virtual void codegen(codegen::Builder& builder, codegen::Scope& scope) override;
virtual void typecheck_preprocess(typecheck::Scope& scope) override;
virtual void typecheck(typecheck::State& state, typecheck::Scope& scope) override;
};
class TopLevelTypedef : public TopLevelStatement {
private:
std::shared_ptr<types::Type> m_ty;
public:
TopLevelTypedef(
token::Metadata meta,
std::shared_ptr<types::Type> type)
: TopLevelStatement{ meta }
, m_ty{ type } {
}
virtual ~TopLevelTypedef() override = default;
virtual std::string formatted() override;
virtual void codegen(codegen::Builder& builder, codegen::Scope& scope) override;
virtual void typecheck_preprocess(typecheck::Scope& scope) override;
virtual void typecheck(typecheck::State& state, typecheck::Scope& scope) override;
};
}

View File

@ -6,79 +6,38 @@ namespace types {
std::vector<BinopDefinition> definitions{};
auto int_ty = std::shared_ptr<types::Type>{
new types::FundamentalType{ false, types::FundamentalTypeKind::Int } };
new types::FundamentalType{ types::FundamentalTypeKind::Int } };
auto char_ty = std::shared_ptr<types::Type>{
new types::FundamentalType{ false, types::FundamentalTypeKind::Char } };
new types::FundamentalType{ types::FundamentalTypeKind::Char } };
auto bool_ty = std::shared_ptr<types::Type>{
new types::FundamentalType{ false, types::FundamentalTypeKind::Bool } };
new types::FundamentalType{ types::FundamentalTypeKind::Bool } };
// Integer arithmetic binops
for (auto& ty : { int_ty, char_ty, bool_ty }) {
// Arithmetic binops
definitions.push_back(BinopDefinition{
ty, types::BinOp::Add, ty,
[](codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs) {
ty, [](codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs) {
return builder.builder->CreateAdd(lhs, rhs, "add");
},
[](BinopDefinition& def, std::shared_ptr<types::Type>, std::shared_ptr<types::Type>) {return def.lhs;}
});
} });
definitions.push_back(BinopDefinition{
ty, types::BinOp::Sub, ty,
[](codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs) {
ty, [](codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs) {
return builder.builder->CreateSub(lhs, rhs, "sub");
},
[](BinopDefinition& def, std::shared_ptr<types::Type>, std::shared_ptr<types::Type>) {return def.lhs;}
});
}
} });
// Signed comparisons
for (auto& ty : { int_ty }) {
// Comparisons
definitions.push_back(BinopDefinition{
ty, types::BinOp::LessThan, ty,
[](codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs) {
return builder.builder->CreateICmpSLT(lhs, rhs, "icmpslt");
},
[](BinopDefinition&, std::shared_ptr<types::Type>, std::shared_ptr<types::Type>) {
return std::shared_ptr<types::Type>{
new types::FundamentalType{ false, types::FundamentalTypeKind::Bool } };
}
});
bool_ty, [](codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs) {
return builder.builder->CreateICmpSLE(lhs, rhs, "icmpsle");
} });
definitions.push_back(BinopDefinition{
ty, types::BinOp::GreaterThan, ty,
[](codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs) {
bool_ty, [](codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs) {
return builder.builder->CreateICmpSGT(lhs, rhs, "icmpsgt");
},
[](BinopDefinition&, std::shared_ptr<types::Type>, std::shared_ptr<types::Type>) {
return std::shared_ptr<types::Type>{
new types::FundamentalType{ false, types::FundamentalTypeKind::Bool } };
}
});
}
// Unsigned comparisons
for (auto& ty : { bool_ty, char_ty }) {
definitions.push_back(BinopDefinition{
ty, types::BinOp::LessThan, ty,
[](codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs) {
return builder.builder->CreateICmpULT(lhs, rhs, "icmpslt");
},
[](BinopDefinition&, std::shared_ptr<types::Type>, std::shared_ptr<types::Type>) {
return std::shared_ptr<types::Type>{
new types::FundamentalType{ false, types::FundamentalTypeKind::Bool } };
}
});
definitions.push_back(BinopDefinition{
ty, types::BinOp::GreaterThan, ty,
[](codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs) {
return builder.builder->CreateICmpUGT(lhs, rhs, "icmpsgt");
},
[](BinopDefinition&, std::shared_ptr<types::Type>, std::shared_ptr<types::Type>) {
return std::shared_ptr<types::Type>{
new types::FundamentalType{ false, types::FundamentalTypeKind::Bool } };
}
});
} });
}
return definitions;

View File

@ -22,8 +22,8 @@ namespace types {
std::shared_ptr<Type> lhs;
BinOp op;
std::shared_ptr<Type> rhs;
std::shared_ptr<Type> result;
llvm::Value* (*codegen)(codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs);
std::shared_ptr<Type>(*result)(BinopDefinition& def, std::shared_ptr<Type> lhs, std::shared_ptr<Type> rhs);
};
std::vector<BinopDefinition> create_binops();

View File

@ -1,8 +1,6 @@
#ifndef BUILDER_H
#define BUILDER_H
#include <map>
#include <llvm/IR/LLVMContext.h>
#include <llvm/IR/IRBuilder.h>
@ -13,8 +11,6 @@ namespace codegen {
std::unique_ptr<llvm::IRBuilder<>> builder;
llvm::BasicBlock* block;
};
typedef std::map<std::string, llvm::Type*> TypeMap;
}
#endif

View File

@ -1,77 +0,0 @@
#include "casting.h"
#include <iostream>
namespace types {
std::vector<CastDefinition> create_casts() {
std::vector<CastDefinition> casts{};
auto int_ty = std::shared_ptr<Type>{
new FundamentalType{ false, FundamentalTypeKind::Int } };
auto char_ty = std::shared_ptr<Type>{
new FundamentalType{ false, FundamentalTypeKind::Char } };
auto bool_ty = std::shared_ptr<Type>{
new FundamentalType{ false, FundamentalTypeKind::Bool } };
auto numerical_types = { int_ty, char_ty, bool_ty };
for (auto& source_ty : numerical_types) {
for (auto& target_ty : numerical_types) {
if (types::types_equal(source_ty, target_ty)) {
casts.push_back(CastDefinition{ source_ty, target_ty, true,
[](codegen::Builder&, std::shared_ptr<Type>, llvm::Value* value) {
return value;
} });
continue;
}
bool allow_implicit = false;
if (target_ty->size() >= source_ty->size())
allow_implicit = true;
if (target_ty->is_signed()) {
casts.push_back(CastDefinition{ source_ty, target_ty, allow_implicit,
[](codegen::Builder& builder, std::shared_ptr<Type> target, llvm::Value* value) {
codegen::TypeMap empty {};
return builder.builder->CreateSExtOrTrunc(value, target->codegen(builder, empty), "cast");
} });
}
else {
casts.push_back(CastDefinition{ source_ty, target_ty, allow_implicit,
[](codegen::Builder& builder, std::shared_ptr<Type> target, llvm::Value* value) {
codegen::TypeMap empty {};
return builder.builder->CreateZExtOrTrunc(value, target->codegen(builder, empty), "cast");
} });
}
}
}
return casts;
}
std::optional<CastDefinition> find_cast(
std::vector<CastDefinition>& casts,
std::shared_ptr<Type> casted_ty,
std::shared_ptr<Type> target_ty) {
if (casted_ty->m_kind == types::TypeKind::Array && target_ty->m_kind == types::TypeKind::Pointer) {
auto array_ty = dynamic_cast<types::ArrayType*>(casted_ty.get());
auto ptr_ty = dynamic_cast<types::PointerType*>(target_ty.get());
if (!types_equal(array_ty->m_inner, ptr_ty->m_inner))
return {};
return CastDefinition{ casted_ty, target_ty, true,
[](codegen::Builder&, std::shared_ptr<Type>, llvm::Value* value) {
return value;
} };
}
for (auto& cast : casts) {
if (types_equal(cast.casted_ty, casted_ty) && types_equal(cast.target_ty, target_ty)) {
return cast;
}
}
return {};
}
}

View File

@ -1,23 +0,0 @@
#ifndef CASTING_H
#define CASTING_H
#include "types.h"
namespace types {
struct CastDefinition {
std::shared_ptr<Type> casted_ty;
std::shared_ptr<Type> target_ty;
bool allow_implicit;
llvm::Value* (*codegen)(codegen::Builder& builder, std::shared_ptr<types::Type> target, llvm::Value* value);
};
std::vector<CastDefinition> create_casts();
std::optional<CastDefinition> find_cast(
std::vector<CastDefinition>& casts,
std::shared_ptr<Type> casted_ty,
std::shared_ptr<Type> target_ty);
}
#endif

View File

@ -11,71 +11,32 @@
namespace codegen {
Scope Scope::with_lvalue() {
return Scope{ this->binops, this->casts, this->structs, this->values, true };
return Scope{ this->binops, this->values, true };
}
}
namespace AST {
std::shared_ptr<types::Type> IntLiteralExpression::get_codegen_type(codegen::Scope&) {
return this->m_ty;
}
codegen::StackValue IntLiteralExpression::codegen(codegen::Builder& builder, codegen::Scope&) {
auto ty = builder.builder->getInt32Ty();
codegen::StackValue IntLiteralExpression::codegen(codegen::Builder& builder, codegen::Scope& scope) {
auto ty = this->m_ty->codegen(builder, scope.structs);
auto stack_type = new types::FundamentalType{ types::FundamentalTypeKind::Int };
return codegen::StackValue{
llvm::ConstantInt::get(ty, this->m_value),
this->m_ty,
std::unique_ptr<types::Type>{stack_type}
};
}
std::shared_ptr<types::Type> StringLiteralExpression::get_codegen_type(codegen::Scope&) {
auto stack_type = new types::ArrayType{
std::make_shared<types::FundamentalType>(true, types::FundamentalTypeKind::Char),
static_cast<uint32_t>(this->m_value.size()) + 1,
true
};
return std::shared_ptr<types::Type> {stack_type};
}
codegen::StackValue StringLiteralExpression::codegen(codegen::Builder& builder, codegen::Scope&) {
codegen::StackValue StringLiteralExpression::codegen(codegen::Builder& builder, codegen::Scope& scope) {
auto stack_type = new types::ArrayType{
std::make_shared<types::FundamentalType>(true, types::FundamentalTypeKind::Char),
static_cast<uint32_t>(this->m_value.size()) + 1,
true
};
auto stack_type = new types::PointerType{ std::make_unique<types::FundamentalType>(types::FundamentalTypeKind::Char) };
auto str = llvm::StringRef{ this->m_value.c_str() };
auto global_str = builder.builder->CreateGlobalString(str);
if (scope.is_lvalue) {
return codegen::StackValue{
global_str,
std::unique_ptr<types::Type>{
new types::PointerType { false, std::shared_ptr<types::Type> {stack_type } }
},
};
}
else {
return codegen::StackValue{
builder.builder->CreateLoad(stack_type->codegen(builder, scope.structs), global_str, "literal"),
std::unique_ptr<types::Type>{stack_type},
};
}
}
std::shared_ptr<types::Type> ValueReferenceExpression::get_codegen_type(codegen::Scope& scope) {
auto value = scope.values.find(this->m_name);
if (value != scope.values.end()) {
if (value->second.ty->m_kind == types::TypeKind::Pointer) {
auto ptr_ty = dynamic_cast<types::PointerType*>(value->second.ty.get());
return ptr_ty->m_inner;
}
return value->second.ty;
}
else {
throw CompileError("Value " + this->m_name + " not found", this->m_meta);
}
return codegen::StackValue{
builder.builder->CreateGlobalString(str),
std::unique_ptr<types::Type>{stack_type},
};
}
codegen::StackValue ValueReferenceExpression::codegen(codegen::Builder& builder, codegen::Scope& scope) {
@ -85,7 +46,7 @@ namespace AST {
return value->second;
}
else {
auto loaded = value->second.ty->load(builder, value->second.value, scope.structs);
auto loaded = value->second.ty->load(builder, value->second.value);
return codegen::StackValue{
loaded.first,
loaded.second
@ -97,30 +58,6 @@ namespace AST {
}
}
std::shared_ptr<types::Type> BinaryOperationExpression::get_codegen_type(codegen::Scope& scope) {
auto lhs = this->m_lhs->get_codegen_type(scope);
auto rhs = this->m_rhs->get_codegen_type(scope);
try {
switch (this->m_binop) {
case types::BinOp::Assignment:
return rhs;
default:
auto binop = types::find_binop(
scope.binops,
lhs,
this->m_binop,
rhs);
if (binop) {
return binop->result(*binop, lhs, rhs);
}
throw CompileError("invalid binop", this->m_meta);
}
}
catch (std::runtime_error& error) {
throw CompileError(error.what(), this->m_meta);
}
}
codegen::StackValue BinaryOperationExpression::codegen(codegen::Builder& builder, codegen::Scope& scope) {
auto lvalued = scope.with_lvalue();
auto lhs = this->m_lhs->codegen(builder, this->m_binop == types::BinOp::Assignment ? lvalued : scope);
@ -139,7 +76,7 @@ namespace AST {
if (binop) {
return codegen::StackValue{
binop->codegen(builder, lhs.value, rhs.value),
binop->result(*binop, lhs.ty, rhs.ty)
binop->result
};
}
throw CompileError("invalid binop", this->m_meta);
@ -150,11 +87,6 @@ namespace AST {
}
}
std::shared_ptr<types::Type> FunctionCallExpression::get_codegen_type(codegen::Scope& scope) {
auto fn_ty = this->m_fn_expr->get_codegen_type(scope);
return *fn_ty->return_type();
}
codegen::StackValue FunctionCallExpression::codegen(codegen::Builder& builder, codegen::Scope& scope) {
std::vector<llvm::Value*> args{};
for (auto& arg : this->m_args) {
@ -162,294 +94,13 @@ namespace AST {
}
auto function = this->m_fn_expr->codegen(builder, scope);
auto value = builder.builder->CreateCall(llvm::dyn_cast<llvm::FunctionType>(function.ty->codegen(builder, scope.structs)), function.value, args, "call");
auto value = builder.builder->CreateCall(llvm::dyn_cast<llvm::FunctionType>(function.ty->codegen(builder)), function.value, args, "call");
return codegen::StackValue{
value,
*function.ty->return_type(),
};
}
std::shared_ptr<types::Type> CastExpression::get_codegen_type(codegen::Scope&) {
return this->m_ty;
}
codegen::StackValue CastExpression::codegen(codegen::Builder& builder, codegen::Scope& scope) {
auto expr_ty = this->m_expr->get_codegen_type(scope);
if (expr_ty->m_kind == types::TypeKind::Array
&& this->m_ty->m_kind == types::TypeKind::Pointer) {
auto lvalued = scope.with_lvalue();
auto expr = this->m_expr->codegen(builder, lvalued);
auto cast = types::find_cast(scope.casts, expr.ty, this->m_ty);
if (cast) {
return codegen::StackValue{
cast->codegen(builder, cast->target_ty, expr.value),
cast->target_ty
};
}
else {
return expr;
}
}
else {
auto expr = this->m_expr->codegen(builder, scope);
auto cast = types::find_cast(scope.casts, expr.ty, this->m_ty);
if (cast) {
return codegen::StackValue{
cast->codegen(builder, cast->target_ty, expr.value),
cast->target_ty
};
}
return expr;
}
}
std::shared_ptr<types::Type> RefExpression::get_codegen_type(codegen::Scope& scope) {
return std::shared_ptr<types::Type> {
new types::PointerType{ false, this->m_expr->get_codegen_type(scope) }
};
}
codegen::StackValue RefExpression::codegen(codegen::Builder& builder, codegen::Scope& scope) {
auto with_lvalue = scope.with_lvalue();
return this->m_expr->codegen(builder, with_lvalue);
}
std::shared_ptr<types::Type> DerefExpression::get_codegen_type(codegen::Scope& scope) {
auto ty = this->m_expr->get_codegen_type(scope);
if (ty->m_kind == types::TypeKind::Pointer) {
auto ptr_ty = dynamic_cast<types::PointerType*>(ty.get());
return ptr_ty->m_inner;
}
else {
throw new CompileError("Tried to deref a non-pointer!", this->m_meta);
}
}
codegen::StackValue DerefExpression::codegen(codegen::Builder& builder, codegen::Scope& scope) {
auto value = this->m_expr->codegen(builder, scope);
if (value.ty->m_kind == types::TypeKind::Pointer) {
auto loaded = value.ty->load(builder, value.value, scope.structs);
return codegen::StackValue{
loaded.first,
loaded.second
};
}
else {
throw new CompileError("Tried to deref a non-pointer!", this->m_meta);
}
}
std::shared_ptr<types::Type> IndexAccessExpression::get_codegen_type(codegen::Scope& scope) {
auto ty = this->m_expr->get_codegen_type(scope);
if (ty->m_kind == types::TypeKind::Array) {
auto ptr_ty = dynamic_cast<types::ArrayType*>(ty.get());
return ptr_ty->m_inner;
}
else if (ty->m_kind == types::TypeKind::Pointer) {
auto ptr_ty = dynamic_cast<types::PointerType*>(ty.get());
return ptr_ty->m_inner;
}
else {
throw CompileError("Tried indexing a non-pointer", this->m_meta);
}
}
codegen::StackValue IndexAccessExpression::codegen(codegen::Builder& builder, codegen::Scope& scope) {
auto intended_ty = this->m_expr->get_codegen_type(scope);
codegen::StackValue value;
if (intended_ty->m_kind != types::TypeKind::Pointer) {
auto lvalued = scope.with_lvalue();
value = this->m_expr->codegen(builder, lvalued);
}
else {
value = this->m_expr->codegen(builder, scope);
}
std::cout << intended_ty->formatted() << std::endl;
std::vector<llvm::Value*> idx_list{ };
idx_list.push_back(llvm::ConstantInt::get(builder.builder->getInt32Ty(), this->m_num));
if (value.ty->m_kind == types::TypeKind::Pointer) {
auto ptr_ty = dynamic_cast<types::PointerType*>(value.ty.get());
auto gep_value = builder.builder->CreateGEP(ptr_ty->m_inner->codegen(builder, scope.structs), value.value, idx_list, "ptr_gep");
if (scope.is_lvalue) {
return codegen::StackValue{
gep_value,
value.ty,
};
}
else {
auto loaded = ptr_ty->load(builder, gep_value, scope.structs);
return codegen::StackValue{
loaded.first,
loaded.second
};
}
}
else if (value.ty->m_kind == types::TypeKind::Array) {
auto array_ty = dynamic_cast<types::ArrayType*>(value.ty.get());
// if (array_ty->m_raw) {
// throw CompileError("Tried indexing a raw array", this->m_meta);
// }
auto gep_value = builder.builder->CreateGEP(array_ty->m_inner->codegen(builder, scope.structs), value.value, idx_list, "array_gep");
if (scope.is_lvalue) {
return codegen::StackValue{
gep_value,
array_ty->m_inner,
};
}
else {
auto ptr_ty = std::shared_ptr<types::Type>{
new types::PointerType { array_ty->m_inner->m_const, array_ty->m_inner}
};
auto loaded = ptr_ty->load(builder, gep_value, scope.structs);
return codegen::StackValue{
loaded.first,
loaded.second
};
}
}
throw CompileError("Tried indexing a non-pointer", this->m_meta);
}
std::shared_ptr<types::Type> FieldAccessExpression::get_codegen_type(codegen::Scope& scope) {
auto ty = this->m_expr->get_codegen_type(scope);
if (ty->m_kind == types::TypeKind::Struct) {
auto struct_ty = dynamic_cast<types::StructType*>(ty.get());
if (struct_ty->m_fields) {
for (auto& field : *struct_ty->m_fields) {
if (field.first == this->m_field)
return field.second;
}
throw CompileError("Unknown field", this->m_meta);
}
throw CompileError("Cannot access a field of opaque struct", this->m_meta);
}
else {
throw CompileError("Tried accessing a non-struct", this->m_meta);
}
}
codegen::StackValue FieldAccessExpression::codegen(codegen::Builder& builder, codegen::Scope& scope) {
auto lvalued = scope.with_lvalue();
auto struct_ptr = this->m_expr->codegen(builder, lvalued);
if (struct_ptr.ty->m_kind == types::TypeKind::Pointer) {
auto ptr_ty = dynamic_cast<types::PointerType*>(struct_ptr.ty.get());
if (ptr_ty->m_inner->m_kind == types::TypeKind::Struct) {
auto struct_ty = dynamic_cast<types::StructType*>(ptr_ty->m_inner.get());
int idx = -1;
auto field_ty = std::shared_ptr<types::Type>{};
for (int i = 0; i < static_cast<int>(struct_ty->m_fields->size()); i++) {
auto field = (*struct_ty->m_fields)[i];
if (field.first == this->m_field) {
idx = i;
field_ty = field.second;
break;
}
}
auto gep = builder.builder->CreateStructGEP(
ptr_ty->m_inner->codegen(builder, scope.structs), struct_ptr.value, idx);
if (scope.is_lvalue) {
return codegen::StackValue{
gep,
field_ty
};
}
else {
auto ptr_ty = std::shared_ptr<types::Type>{
new types::PointerType { field_ty->m_const, field_ty }
};
auto loaded = ptr_ty->load(builder, gep, scope.structs);
return codegen::StackValue{ loaded.first, loaded.second };
}
}
else {
throw CompileError("Tried field-accessing a non-struct-pointer", this->m_meta);
}
}
else {
throw CompileError("Tried field-accessing a non-pointer", this->m_meta);
}
}
std::shared_ptr<types::Type> ListInitializerExpression::get_codegen_type(codegen::Scope&) {
return this->m_ty;
}
codegen::StackValue ListInitializerExpression::codegen(codegen::Builder& builder, codegen::Scope& scope) {
auto value_ptr = builder.builder->CreateAlloca(this->m_ty->codegen(builder, scope.structs));
if (this->m_ty->m_kind == types::TypeKind::Array) {
auto array_ty = dynamic_cast<types::ArrayType*>(this->m_ty.get());
int counter = 0;
for (auto& expr : this->m_expressions) {
std::vector<llvm::Value*> indices{};
indices.push_back(llvm::ConstantInt::get(builder.builder->getInt32Ty(), counter++));
auto gep = builder.builder->CreateGEP(
array_ty->m_inner->codegen(builder, scope.structs), value_ptr, indices, "GEP");
builder.builder->CreateStore(expr->codegen(builder, scope).value, gep);
}
auto ptr_ty = std::shared_ptr<types::Type>{
new types::PointerType{this->m_ty->m_const, this->m_ty}
};
if (scope.is_lvalue) {
return codegen::StackValue{ value_ptr, ptr_ty };
}
else {
auto loaded = ptr_ty->load(builder, value_ptr, scope.structs);
return codegen::StackValue{ loaded.first, loaded.second };
}
}
else if (this->m_ty->m_kind == types::TypeKind::Struct) {
auto struct_ty = dynamic_cast<types::StructType*>(this->m_ty.get());
for (int i = 0; i < static_cast<int>(this->m_expressions.size()); i++) {
std::vector<llvm::Value*> indices{};
auto gep = builder.builder->CreateStructGEP(
struct_ty->codegen(builder, scope.structs), value_ptr, i, "struct_gep"
);
builder.builder->CreateStore(
this->m_expressions[i]->codegen(builder, scope).value, gep);
}
auto ptr_ty = std::shared_ptr<types::Type>{
new types::PointerType{this->m_ty->m_const, this->m_ty}
};
if (scope.is_lvalue) {
return codegen::StackValue{ value_ptr, ptr_ty };
}
else {
auto loaded = ptr_ty->load(builder, value_ptr, scope.structs);
return codegen::StackValue{ loaded.first, loaded.second };
}
}
else {
throw CompileError("Tried to list-initialize a non-array!", this->m_meta);
}
}
void ReturnStatement::codegen(codegen::Builder& builder, codegen::Scope& scope) {
if (!builder.block)
return;
@ -474,30 +125,14 @@ namespace AST {
builder.builder->SetInsertPoint(builder.block);
if (this->m_type->m_kind == types::TypeKind::Array) {
auto raw_llvm_ty = this->m_type->codegen(builder, scope.structs);
auto ptr = builder.builder->CreateAlloca(raw_llvm_ty);
if (this->m_expr.has_value()) {
auto value = this->m_expr->get()->codegen(builder, scope);
builder.builder->CreateStore(value.value, ptr, false);
}
scope.values[this->m_name] = codegen::StackValue{ ptr, this->m_type };
return;
}
auto ty = this->m_type->codegen(builder, scope.structs);
auto ty = this->m_type->codegen(builder);
auto ptr = builder.builder->CreateAlloca(ty);
if (this->m_expr.has_value()) {
auto value = this->m_expr->get()->codegen(builder, scope);
builder.builder->CreateStore(value.value, ptr, false);
}
auto ptr_ty = std::shared_ptr<types::Type>{
new types::PointerType{ this->m_type->m_const, this->m_type }
};
scope.values[this->m_name] = codegen::StackValue{ ptr, ptr_ty };
scope.values[this->m_name] = codegen::StackValue{ ptr, this->m_type };
}
void IfStatement::codegen(codegen::Builder& builder, codegen::Scope& scope) {
@ -544,11 +179,9 @@ namespace AST {
param_ty_ptrs.push_back(param.second);
}
auto fn_ty_ptr = std::shared_ptr<types::Type>{
new types::FunctionType{ true, ret_ty_ptr, param_ty_ptrs, this->m_is_vararg }
};
auto fn_ty_ptr = std::shared_ptr<types::Type>{ new types::FunctionType{ ret_ty_ptr, param_ty_ptrs, this->m_is_vararg } };
auto fn_ty = fn_ty_ptr->codegen(builder, scope.structs);
auto fn_ty = fn_ty_ptr->codegen(builder);
auto function = llvm::Function::Create(
llvm::dyn_cast<llvm::FunctionType>(fn_ty),
llvm::GlobalValue::LinkageTypes::ExternalLinkage,
@ -566,32 +199,12 @@ namespace AST {
int counter = 0;
for (auto& param : this->m_params) {
if (param.second->m_kind == types::TypeKind::Array) {
auto array_ty = dynamic_cast<types::ArrayType*>(param.second.get());
if (!array_ty->m_raw) {
auto ty = param_ty_ptrs[counter];
auto arg = function->getArg(counter++);
if (param.first) {
arg->setName(*param.first);
inner_scope.values[*param.first] = codegen::StackValue{
arg,
ty,
};
}
continue;
}
}
builder.builder->SetInsertPoint(BB);
auto arg_ptr = builder.builder->CreateAlloca(param_ty_ptrs[counter]->codegen(builder, scope.structs));
auto param_ty_ptr = std::shared_ptr<types::Type>{
new types::PointerType { true, param_ty_ptrs[counter]}
};
auto param_ty_ptr = param_ty_ptrs[counter];
auto arg = function->getArg(counter++);
builder.builder->CreateStore(arg, arg_ptr);
if (param.first) {
arg->setName(*param.first);
inner_scope.values[*param.first] = codegen::StackValue{
arg_ptr,
arg,
param_ty_ptr,
};
}
@ -602,26 +215,14 @@ namespace AST {
}
}
builder.builder->CreateRetVoid();
llvm::verifyFunction(*function);
builder.block = nullptr;
}
void TopLevelTypedef::codegen(codegen::Builder& builder, codegen::Scope& scope) {
auto ty = this->m_ty->codegen(builder, scope.structs);
if (this->m_ty->m_kind == types::TypeKind::Struct) {
auto struct_ty = dynamic_cast<types::StructType*>(this->m_ty.get());
if (struct_ty->m_name) {
scope.structs[*struct_ty->m_name] = ty;
}
}
}
}
namespace types {
llvm::Type* FundamentalType::codegen(codegen::Builder& builder, codegen::TypeMap&) {
llvm::Type* FundamentalType::codegen(codegen::Builder& builder) {
switch (this->m_ty) {
case FundamentalTypeKind::Int:
return builder.builder->getInt32Ty();
@ -636,57 +237,19 @@ namespace types {
}
}
llvm::Type* FunctionType::codegen(codegen::Builder& builder, codegen::TypeMap& structs) {
llvm::Type* FunctionType::codegen(codegen::Builder& builder) {
std::vector<llvm::Type*> params{};
for (auto& param : this->m_param_tys) {
params.push_back(param->codegen(builder, structs));
params.push_back(param->codegen(builder));
}
auto ret_ty = this->m_ret_ty->codegen(builder, structs);
auto ret_ty = this->m_ret_ty->codegen(builder);
return llvm::FunctionType::get(ret_ty, params, this->m_vararg);
}
llvm::Type* PointerType::codegen(codegen::Builder& builder, codegen::TypeMap&) {
llvm::Type* PointerType::codegen(codegen::Builder& builder) {
return llvm::PointerType::get(*builder.context, 0);
}
llvm::Type* ArrayType::codegen(codegen::Builder& builder, codegen::TypeMap& structs) {
if (this->m_raw)
return llvm::ArrayType::get(this->m_inner->codegen(builder, structs), this->m_size);
return llvm::PointerType::get(*builder.context, 0);
}
llvm::Type* StructType::codegen(codegen::Builder& builder, codegen::TypeMap& structs) {
if (this->m_is_ref) {
if (this->m_name) {
return structs[*this->m_name];
}
else {
throw CompileError("reference to nonexistant struct", {});
}
}
if (this->m_fields) {
std::vector<llvm::Type*> fields{};
for (auto& field : *this->m_fields) {
fields.push_back(field.second->codegen(builder, structs));
}
auto ty = llvm::StructType::create(*builder.context, fields);
if (this->m_name)
ty->setName(*this->m_name);
return ty;
}
else {
auto ty = llvm::StructType::create(*builder.context);
if (this->m_name)
ty->setName(*this->m_name);
return ty;
}
}
// llvm::Type* StructRef::codegen(codegen::Builder&, codegen::TypeMap& structs) {
// return structs[this->m_name];
// }
}

View File

@ -9,7 +9,6 @@
#include "builder.h"
#include "types.h"
#include "binops.h"
#include "casting.h"
#include "tokens.h"
namespace codegen {
@ -20,11 +19,8 @@ namespace codegen {
struct Scope {
std::vector<types::BinopDefinition>& binops;
std::vector<types::CastDefinition>& casts;
TypeMap structs;
std::map<std::string, StackValue> values;
bool is_lvalue;
Scope with_lvalue();

View File

@ -70,14 +70,12 @@ std::optional<CompileOutput> compile(std::string_view in_filename) {
}
// Parse tokens
parsing::Scope parse_scope{};
auto stream = token::TokenStream{ tokens };
std::vector<std::unique_ptr<AST::TopLevelStatement>> statements;
auto statement = parsing::parse_top_level_statement(stream, parse_scope);
auto statement = parsing::parse_top_level_statement(stream);
while (statement.ok()) {
statements.push_back(statement.unwrap());
statement = parsing::parse_top_level_statement(stream, parse_scope);
statement = parsing::parse_top_level_statement(stream);
}
if (stream.peek().type != token::Type::Eof) {
std::cerr << statement.unwrap_err() << std::endl;
@ -99,27 +97,15 @@ std::optional<CompileOutput> compile(std::string_view in_filename) {
// Perform static analysis
typecheck::Scope preprocess_scope{};
// Preprocess
for (auto& tls : statements) {
std::cout << tls->formatted() << std::endl;
tls->typecheck_preprocess(preprocess_scope);
}
// Actual typechecking
typecheck::State typecheck_state{};
typecheck_state.binops = types::create_binops();
typecheck_state.casts = types::create_casts();
typecheck::Scope typecheck_scope{};
for (auto& tls : statements) {
std::cout << tls->formatted() << std::endl;
tls->typecheck(typecheck_state, typecheck_scope);
}
// Error checking
if (typecheck_state.errors.size() > 0) {
std::cerr << "Errors while typechecking:" << std::endl;
for (auto& error : typecheck_state.errors) {
@ -130,16 +116,13 @@ std::optional<CompileOutput> compile(std::string_view in_filename) {
return {};
}
// Compile parsed output
codegen::Scope cg_scope{
.binops = typecheck_state.binops,
.casts = typecheck_state.casts,
.structs = {},
.values = {},
.is_lvalue = false,
};
// Compile parsed output
try {
for (auto& tls : statements) {
std::cout << tls->formatted() << std::endl;
@ -176,8 +159,6 @@ std::optional<CompileOutput> compile(std::string_view in_filename) {
builder.mod->print(llvm_ir_dest, nullptr);
llvm_ir_dest.flush();
std::cout << llvm_ir_string << std::endl;
// Print output to obj-file
std::error_code EC;
std::string obj_string;

View File

@ -5,154 +5,37 @@
namespace parsing {
namespace {
static uint32_t struct_id_counter = 0;
Result<std::unique_ptr<AST::Expression>, std::string> parse_expression(token::TokenStream& stream);
Result<std::unique_ptr<AST::Expression>, std::string> parse_expression(token::TokenStream& stream, Scope& scope);
Result<std::optional<uint32_t>, std::string> parse_array_postfix(token::TokenStream& stream, bool allow_empty, Scope&);
Result<std::shared_ptr<types::Type>, std::string> parse_type(token::TokenStream& stream, Scope& scope) {
Result<std::shared_ptr<types::Type>, std::string> parse_type(token::TokenStream& stream) {
token::TokenStream inner{ stream };
try {
bool is_const = false;
if (inner.peek().type == token::Type::Ident && inner.peek().content == "const") {
is_const = true;
inner.next();
}
auto token = inner.expect(token::Type::Ident);
// TODO eventually make this be potentially more than one word
std::string type_name = token.content;
std::shared_ptr<types::Type> returned{};
if (token.content == "struct") {
std::optional<std::string> struct_name{};
if (inner.peek().type == token::Type::Ident) {
struct_name = inner.expect(token::Type::Ident).content;
}
std::optional<std::vector<types::StructField>> maybe_fields{};
if (inner.peek().content == "{") {
std::vector<types::StructField> fields{};
inner.expect(token::Type::Symbol, "{");
int counter = 0;
while (inner.peek().content != "}") {
if (counter++ > 0)
inner.expect(token::Type::Symbol, ";");
auto ty_res = parse_type(inner, scope);
if (!ty_res.ok())
break;
auto ty = ty_res.unwrap();
auto field_name = inner.expect(token::Type::Ident);
auto array_postfix = parse_array_postfix(inner, false, scope);
while (array_postfix.ok()) {
auto postfix = array_postfix.unwrap();
ty = std::shared_ptr<types::Type>{
new types::ArrayType(ty, *postfix, true)
};
array_postfix = parse_array_postfix(inner, false, scope);
}
fields.push_back(types::StructField{ field_name.content, ty });
}
inner.expect(token::Type::Symbol, "}");
maybe_fields = fields;
}
if (!struct_name && !maybe_fields) {
throw std::runtime_error("Struct must have a name or fields!");
}
if (struct_name && !maybe_fields && scope.structs.find(*struct_name) != scope.structs.end()) {
auto original_ty = scope.structs[*struct_name];
auto original_struct_ty = dynamic_cast<types::StructType*>(original_ty.get());
auto ty = new types::StructType{
is_const,
struct_name,
original_struct_ty->m_fields,
true,
false,
original_struct_ty->m_id
};
returned = std::shared_ptr<types::Type>{ ty };
}
else {
if (scope.structs.find(*struct_name) != scope.structs.end()) {
auto original_ty = scope.structs[*struct_name];
auto original_struct_ty = dynamic_cast<types::StructType*>(original_ty.get());
if (!original_struct_ty->m_fields.has_value()) {
auto ty = new types::StructType{
is_const,
struct_name,
maybe_fields,
false,
true,
original_struct_ty->m_id
};
returned = std::shared_ptr<types::Type>{ ty };
}
else {
auto ty = new types::StructType{
is_const,
struct_name,
maybe_fields,
false,
false,
struct_id_counter++
};
returned = std::shared_ptr<types::Type>{ ty };
}
}
else {
auto ty = new types::StructType{
is_const,
struct_name,
maybe_fields,
false,
false,
struct_id_counter++
};
returned = std::shared_ptr<types::Type>{ ty };
}
}
if (type_name == "int") {
auto ty = new types::FundamentalType{ types::FundamentalTypeKind::Int };
returned = std::shared_ptr<types::Type>{ ty };
}
else if (type_name == "char") {
auto ty = new types::FundamentalType{ types::FundamentalTypeKind::Char };
returned = std::shared_ptr<types::Type>{ ty };
}
else if (type_name == "void") {
auto ty = new types::FundamentalType{ types::FundamentalTypeKind::Void };
returned = std::shared_ptr<types::Type>{ ty };
}
else {
// TODO eventually make this be potentially more than one word
std::string type_name = token.content;
if (type_name == "int") {
auto ty = new types::FundamentalType{ is_const, types::FundamentalTypeKind::Int };
returned = std::shared_ptr<types::Type>{ ty };
}
else if (type_name == "char") {
auto ty = new types::FundamentalType{ is_const, types::FundamentalTypeKind::Char };
returned = std::shared_ptr<types::Type>{ ty };
}
else if (type_name == "void") {
auto ty = new types::FundamentalType{ is_const, types::FundamentalTypeKind::Void };
returned = std::shared_ptr<types::Type>{ ty };
}
else {
throw std::runtime_error("Expected type name, got " + type_name);
}
throw std::runtime_error("Expected type name, got " + type_name);
}
while (inner.peek().type == token::Type::Symbol && inner.peek().content == "*") {
inner.next();
auto ptr_const = false;
if (inner.peek().type == token::Type::Ident && inner.peek().content == "const") {
inner.next();
ptr_const = true;
}
auto ty = new types::PointerType{ ptr_const, std::move(returned) };
auto ty = new types::PointerType{ std::move(returned) };
returned = std::shared_ptr<types::Type>{ ty };
}
@ -166,71 +49,9 @@ namespace parsing {
}
}
Result<std::optional<uint32_t>, std::string> parse_array_postfix(token::TokenStream& stream, bool allow_empty, Scope&) {
Result<std::unique_ptr<AST::Expression>, std::string> parse_plain_expression(token::TokenStream& stream) {
token::TokenStream inner{ stream };
try {
std::optional<uint32_t> returned{};
inner.expect(token::Type::Symbol, "[");
if (inner.peek().type == token::Type::LiteralInt) {
returned = std::stoi(inner.next().content);
}
if (!allow_empty && !returned.has_value()) {
throw std::runtime_error("Expected array size");
}
inner.expect(token::Type::Symbol, "]");
stream.m_position = inner.m_position;
return returned;
}
catch (std::runtime_error& error) {
return std::string{ error.what() };
}
}
Result<std::unique_ptr<AST::Expression>, std::string> parse_list_initializer(token::TokenStream& stream, Scope& scope) {
token::TokenStream inner{ stream };
try {
auto before_meta = inner.metadata();
inner.expect(token::Type::Symbol, "{");
std::vector<std::unique_ptr<AST::Expression>> expressions{};
int counter = 0;
while (inner.peek().content != "}") {
if (counter++ > 0) {
inner.expect(token::Type::Symbol, ",");
}
expressions.push_back(parse_expression(inner, scope).unwrap());
}
inner.expect(token::Type::Symbol, "}");
stream.m_position = inner.m_position;
return std::unique_ptr<AST::Expression> {
new AST::ListInitializerExpression(
before_meta + inner.metadata(),
std::move(expressions),
std::shared_ptr<types::Type> {
new types::FundamentalType{ true, types::FundamentalTypeKind::Any }
})
};
}
catch (std::runtime_error& error) {
return std::string{ error.what() };
}
}
Result<std::unique_ptr<AST::Expression>, std::string> parse_plain_expression(token::TokenStream& stream, Scope& scope) {
token::TokenStream inner{ stream };
try {
if (auto list_init = parse_list_initializer(inner, scope); list_init.ok()) {
stream.m_position = inner.m_position;
return std::unique_ptr<AST::Expression> { list_init.unwrap() };
}
auto token = inner.next();
if (token.type == token::Type::LiteralInt) {
stream.m_position = inner.m_position;
@ -259,95 +80,27 @@ namespace parsing {
}
}
Result<std::shared_ptr<types::Type>, std::string> parse_cast(token::TokenStream& stream, Scope& scope) {
token::TokenStream inner{ stream };
try {
inner.expect(token::Type::Symbol, "(");
auto ty = parse_type(inner, scope).unwrap();
inner.expect(token::Type::Symbol, ")");
stream.m_position = inner.m_position;
return ty;
}
catch (std::runtime_error& error) {
return std::string{ error.what() };
}
}
Result<std::unique_ptr<AST::Expression>, std::string> parse_primary_expression(token::TokenStream& stream, Scope& scope) {
Result<std::unique_ptr<AST::Expression>, std::string> parse_primary_expression(token::TokenStream& stream) {
token::TokenStream inner{ stream };
try {
auto before_meta = inner.metadata();
if (auto cast = parse_cast(inner, scope); cast.ok()) {
auto expr = parse_primary_expression(inner, scope).unwrap();
stream.m_position = inner.m_position;
return std::unique_ptr<AST::Expression>{
new AST::CastExpression{
before_meta + inner.metadata(),
cast.unwrap(),
std::move(expr)
}
};
}
else if (inner.peek().content == "(") {
auto plain_expr = parse_plain_expression(inner);
while (inner.peek().content == "(") {
inner.next();
auto expr = parse_expression(inner, scope).unwrap();
std::vector<std::unique_ptr<AST::Expression>> args{};
int counter = 0;
while (inner.peek().content != ")") {
if (counter++ > 0)
inner.expect(token::Type::Symbol, ",");
args.push_back(parse_expression(inner).unwrap());
}
inner.expect(token::Type::Symbol, ")");
stream.m_position = inner.m_position;
return expr;
}
else if (inner.peek().content == "&") {
inner.next();
auto expr = parse_primary_expression(inner, scope).unwrap();
stream.m_position = inner.m_position;
return std::unique_ptr<AST::Expression> {
new AST::RefExpression(before_meta + inner.metadata(), std::move(expr))
};
}
else if (inner.peek().content == "*") {
inner.next();
auto expr = parse_primary_expression(inner, scope).unwrap();
stream.m_position = inner.m_position;
return std::unique_ptr<AST::Expression> {
new AST::DerefExpression(before_meta + inner.metadata(), std::move(expr))
};
}
auto plain_expr = parse_plain_expression(inner, scope);
while (inner.peek().content == "(" || inner.peek().content == "[" || inner.peek().content == ".") {
if (inner.peek().content == "(") {
inner.next();
std::vector<std::unique_ptr<AST::Expression>> args{};
int counter = 0;
while (inner.peek().content != ")") {
if (counter++ > 0)
inner.expect(token::Type::Symbol, ",");
args.push_back(parse_expression(inner, scope).unwrap());
}
inner.expect(token::Type::Symbol, ")");
auto fn_call = new AST::FunctionCallExpression{ before_meta + inner.metadata(), plain_expr.unwrap(), std::move(args) };
plain_expr = std::unique_ptr<AST::Expression>{ fn_call };
}
else if (auto postfix = parse_array_postfix(inner, false, scope); postfix.ok()) {
auto idx_expr = new AST::IndexAccessExpression{
before_meta + inner.metadata(), plain_expr.unwrap(), *postfix.unwrap() };
plain_expr = std::unique_ptr<AST::Expression>{ idx_expr };
}
else if (inner.peek().content == ".") {
inner.next();
std::string field_name = inner.expect(token::Type::Ident).content;
plain_expr = std::unique_ptr<AST::Expression>{
new AST::FieldAccessExpression{before_meta + inner.metadata(), plain_expr.unwrap(), field_name}
};
}
auto fn_call = new AST::FunctionCallExpression{ before_meta + inner.metadata(), plain_expr.unwrap(), std::move(args) };
plain_expr = std::unique_ptr<AST::Expression>{ fn_call };
}
@ -359,7 +112,7 @@ namespace parsing {
}
}
Result<types::BinOp, std::string> parse_binop(token::TokenStream& stream, Scope&) {
Result<types::BinOp, std::string> parse_binop(token::TokenStream& stream) {
token::TokenStream inner{ stream };
try {
auto token = inner.next();
@ -395,32 +148,32 @@ namespace parsing {
}
std::unique_ptr<AST::Expression> parse_rhs(
token::TokenStream& stream, std::unique_ptr<AST::Expression> lhs, int prev_precedence, Scope& scope) {
token::TokenStream& stream, std::unique_ptr<AST::Expression> lhs, int prev_precedence) {
auto before = stream.metadata();
auto binop_res = parse_binop(stream, scope);
auto binop_res = parse_binop(stream);
while (binop_res.ok()) {
auto binop = binop_res.unwrap();
auto rhs = parse_primary_expression(stream, scope).unwrap();
auto rhs = parse_primary_expression(stream).unwrap();
if (types::operator_precedence(binop) > prev_precedence) {
rhs = parse_rhs(stream, std::move(rhs), types::operator_precedence(binop), scope);
rhs = parse_rhs(stream, std::move(rhs), types::operator_precedence(binop));
}
auto binop_expr = new AST::BinaryOperationExpression{ before + stream.metadata(), std::move(lhs), binop, std::move(rhs) };
lhs = std::unique_ptr<AST::Expression>{ binop_expr };
binop_res = parse_binop(stream, scope);
binop_res = parse_binop(stream);
}
return lhs;
}
Result<std::unique_ptr<AST::Expression>, std::string> parse_expression(token::TokenStream& stream, Scope& scope) {
Result<std::unique_ptr<AST::Expression>, std::string> parse_expression(token::TokenStream& stream) {
try {
auto lhs = parse_primary_expression(stream, scope).unwrap();
return std::unique_ptr{ parse_rhs(stream, std::move(lhs), 0, scope) };
auto lhs = parse_primary_expression(stream).unwrap();
return std::unique_ptr{ parse_rhs(stream, std::move(lhs), 0) };
}
catch (std::runtime_error& error) {
return std::string{ error.what() };
@ -429,28 +182,19 @@ namespace parsing {
Result<std::unique_ptr<AST::InitializationStatement>, std::string> parse_init_statement(token::TokenStream& stream, Scope& scope) {
Result<std::unique_ptr<AST::InitializationStatement>, std::string> parse_init_statement(token::TokenStream& stream) {
token::TokenStream inner{ stream };
auto before_meta = inner.metadata();
try {
auto ty = parse_type(inner, scope).unwrap();
auto ty = parse_type(inner).unwrap();
auto name = inner.expect(token::Type::Ident);
auto array_postfix = parse_array_postfix(inner, false, scope);
while (array_postfix.ok()) {
auto postfix = array_postfix.unwrap();
ty = std::shared_ptr<types::Type>{
new types::ArrayType(ty, *postfix, true)
};
array_postfix = parse_array_postfix(inner, false, scope);
}
std::optional<std::unique_ptr<AST::Expression>> expr = {};
if (inner.peek().type == token::Type::Symbol && inner.peek().content == "=") {
inner.expect(token::Type::Symbol, "=");
expr = parse_expression(inner, scope).unwrap();
expr = parse_expression(inner).unwrap();
}
inner.expect(token::Type::Symbol, ";");
@ -464,13 +208,13 @@ namespace parsing {
}
}
Result<std::unique_ptr<AST::Statement>, std::string> parse_statement(token::TokenStream& stream, Scope& scope) {
Result<std::unique_ptr<AST::Statement>, std::string> parse_statement(token::TokenStream& stream) {
token::TokenStream inner{ stream };
auto before_meta = inner.metadata();
try {
if (inner.peek().type == token::Type::ReturnKeyword) {
inner.next();
auto expression = parse_expression(inner, scope).unwrap();
auto expression = parse_expression(inner).unwrap();
inner.expect(token::Type::Symbol, ";");
stream.m_position = inner.m_position;
@ -481,14 +225,14 @@ namespace parsing {
else if (inner.peek().type == token::Type::IfKeyword) {
inner.next();
inner.expect(token::Type::Symbol, "(");
auto expression = parse_expression(inner, scope).unwrap();
auto expression = parse_expression(inner).unwrap();
inner.expect(token::Type::Symbol, ")");
auto then_statement = parse_statement(inner, scope).unwrap();
auto then_statement = parse_statement(inner).unwrap();
std::optional<std::unique_ptr<AST::Statement>> else_statement{};
if (inner.peek().type == token::Type::ElseKeyword) {
inner.next();
else_statement = parse_statement(inner, scope).unwrap();
else_statement = parse_statement(inner).unwrap();
}
stream.m_position = inner.m_position;
@ -501,11 +245,11 @@ namespace parsing {
};
return std::unique_ptr<AST::Statement>{ statement };
}
else if (auto init = parse_init_statement(inner, scope); init.ok()) {
else if (auto init = parse_init_statement(inner); init.ok()) {
stream.m_position = inner.m_position;
return std::unique_ptr<AST::Statement>{ init.unwrap() };
}
else if (auto expr = parse_expression(inner, scope); expr.ok()) {
else if (auto expr = parse_expression(inner); expr.ok()) {
stream.m_position = inner.m_position;
stream.expect(token::Type::Symbol, ";");
auto expr_statement = new AST::ExpressionStatement{ before_meta + stream.metadata(), expr.unwrap() };
@ -522,11 +266,11 @@ namespace parsing {
}
}
Result<std::unique_ptr<AST::TopLevelStatement>, std::string> parse_function(token::TokenStream& stream, Scope& scope) {
Result<std::unique_ptr<AST::TopLevelStatement>, std::string> parse_top_level_statement(token::TokenStream& stream) {
token::TokenStream inner{ stream };
auto before_meta = inner.metadata();
try {
auto type = parse_type(inner, scope).unwrap();
auto type = parse_type(inner).unwrap();
auto name_token = inner.expect(token::Type::Ident);
inner.expect(token::Type::Symbol, "(");
@ -545,45 +289,26 @@ namespace parsing {
break;
}
auto param_ty = parse_type(inner, scope).unwrap();
auto param_ty = parse_type(inner).unwrap();
std::optional<std::string> param_name{};
if (inner.peek().type == token::Type::Ident) {
param_name = inner.expect(token::Type::Ident).content;
auto postfix = parse_array_postfix(inner, true, scope);
while (postfix.ok()) {
auto array_postfix = postfix.unwrap();
if (array_postfix) {
param_ty = std::shared_ptr<types::Type>{
new types::ArrayType(param_ty, *array_postfix, false)
};
}
else {
param_ty = std::shared_ptr<types::Type>{
new types::PointerType(true, param_ty)
};
}
postfix = parse_array_postfix(inner, true, scope);
}
}
params.push_back(std::pair(param_name, std::move(param_ty)));
}
inner.expect(token::Type::Symbol, ")");
auto inner_scope = parsing::Scope{ scope };
std::optional<std::vector<std::unique_ptr<AST::Statement>>> statements{};
if (inner.peek().content == "{") {
inner.expect(token::Type::Symbol, "{");
std::vector<std::unique_ptr<AST::Statement>> statement_list{};
auto statement = parse_statement(inner, inner_scope);
auto statement = parse_statement(inner);
while (statement.ok()) {
statement_list.push_back(statement.unwrap());
statement = parse_statement(inner, inner_scope);
statement = parse_statement(inner);
}
statements = std::optional{ std::move(statement_list) };
@ -609,56 +334,4 @@ namespace parsing {
return std::string(error.what());
}
}
Result<std::unique_ptr<AST::TopLevelStatement>, std::string> parse_tl_typedef(token::TokenStream& stream, Scope& scope) {
token::TokenStream inner{ stream };
auto before_meta = inner.metadata();
try {
auto ty = parse_type(inner, scope).unwrap();
inner.expect(token::Type::Symbol, ";");
if (ty->m_kind == types::TypeKind::Struct) {
auto struct_ty = dynamic_cast<types::StructType*>(ty.get());
if (!struct_ty->m_is_ref && struct_ty->m_name) {
if (scope.structs.find(*struct_ty->m_name) != scope.structs.end() && struct_ty->m_is_def) {
auto true_ty = dynamic_cast<types::StructType*>(scope.structs[*struct_ty->m_name].get());
true_ty->m_fields = struct_ty->m_fields;
}
else {
scope.structs[*struct_ty->m_name] = ty;
}
}
}
stream.m_position = inner.m_position;
auto tl_typedef = new AST::TopLevelTypedef{
before_meta + stream.metadata(), ty
};
return std::unique_ptr<AST::TopLevelStatement>{tl_typedef};
}
catch (std::runtime_error& error) {
return std::string(error.what());
}
}
Result<std::unique_ptr<AST::TopLevelStatement>, std::string> parse_top_level_statement(token::TokenStream& stream, Scope& scope) {
token::TokenStream inner{ stream };
auto before_meta = inner.metadata();
try {
if (auto func = parse_function(inner, scope); func.ok()) {
stream.m_position = inner.m_position;
return func.unwrap();
}
else if (auto tl_typedef = parse_tl_typedef(inner, scope); tl_typedef.ok()) {
stream.m_position = inner.m_position;
return tl_typedef.unwrap();
}
else {
throw std::runtime_error("Expected top-level statement, got " + inner.peek().formatted());
}
}
catch (std::runtime_error& error) {
return std::string(error.what());
}
}
}

View File

@ -1,19 +1,12 @@
#ifndef PARSING_H
#define PARSING_H
#include <map>
#include "ast.h"
#include "result.h"
#include "tokens.h"
namespace parsing {
struct Scope {
std::map<std::string, std::shared_ptr<types::Type>> structs;
};
Result<std::unique_ptr<AST::TopLevelStatement>, std::string> parse_top_level_statement(
token::TokenStream& stream, Scope& scope);
Result<std::unique_ptr<AST::TopLevelStatement>, std::string> parse_top_level_statement(token::TokenStream& stream);
}
#endif

View File

@ -5,7 +5,7 @@
#include <vector>
#include <iostream>
#include <sstream>
#include <optional>
static bool iswhitespace(char& character) {
return character == ' '
@ -14,33 +14,6 @@ static bool iswhitespace(char& character) {
|| character == '\r';
}
static std::optional<char> get_escaped(std::string_view inspected) {
if (inspected == "n")
return '\n';
if (inspected == "r")
return '\r';
if (inspected == "t")
return '\t';
if (inspected == "\\")
return '\\';
if (inspected.size() <= 3) {
for (char c : inspected) {
std::cout << c << std::endl;
if (!std::isdigit(c))
return {};
if (std::stoi(std::string{ c }) > 8)
return {};
}
unsigned int x;
std::stringstream ss;
ss << std::oct << inspected;
ss >> x;
return static_cast<char>(x);
}
return {};
}
namespace token {
std::string type_name(Type& type) {
switch (type) {
@ -179,38 +152,20 @@ namespace token {
std::string content{};
c = text[++i]; // Skip initial "
do {
if (c == '\\') {
std::string escaped_content{};
if ((i + 1) >= text_length) break;
auto potential = get_escaped(escaped_content + text[++i]);
while (potential.has_value() && (i + 1) < text_length) {
escaped_content += text[i];
potential = get_escaped(escaped_content + text[++i]);
}
if (escaped_content.size() > 0) {
auto escaped = get_escaped(escaped_content);
if (escaped.has_value())
content += *escaped;
}
c = text[i];
}
else {
content += c;
if ((i + 1) >= text_length) break;
c = text[++i];
}
content += c;
if ((i + 1) >= text_length) break;
c = text[++i];
} while (c != '\"');
i++; // Skip second "
tokens.push_back(token::Token{ token::Type::LiteralStr, content, meta + (content.size() + 2) });
}
else if (std::isalpha(c) || c == '_') {
else if (std::isalpha(c)) {
std::string content{};
do {
content += c;
if ((i + 1) >= text_length) break;
c = text[++i];
} while (std::isalnum(c) || c == '_');
} while (std::isalnum(c));
token::Type type = token::Type::Ident;
if (content == "return") {

View File

@ -5,31 +5,14 @@
#include "result.h"
namespace {
enum class TypecheckResKind {
enum class TypecheckRes {
Ok,
Castable,
};
struct TypecheckRes {
TypecheckResKind kind;
std::shared_ptr<types::Type> result;
};
Result<TypecheckRes, std::string> check_type(
typecheck::State& state,
std::shared_ptr<types::Type> checked,
std::shared_ptr<types::Type> target) {
auto potential_cast = types::find_cast(state.casts, checked, target);
Result<TypecheckRes, std::string> check_type(std::shared_ptr<types::Type> checked, std::shared_ptr<types::Type> target) {
if (types::types_equal(checked, target)) {
return TypecheckRes{ TypecheckResKind::Ok, target };
}
else if (potential_cast.has_value()) {
if (potential_cast->allow_implicit)
return TypecheckRes{ TypecheckResKind::Castable, target };
return std::string{ "Type " + checked->formatted() + " not implicitly castable to " + target->formatted() };
return TypecheckRes::Ok;
}
return std::string{ "Types " + checked->formatted() + " and " + target->formatted() + " incompatible" };
@ -41,13 +24,12 @@ namespace {
typecheck::State& state) {
if (res.ok()) {
auto result = res.unwrap();
if (result.kind == TypecheckResKind::Ok) {
if (result == TypecheckRes::Ok) {
return expr;
}
else {
return std::unique_ptr<AST::Expression> {
new AST::CastExpression{ expr->m_meta, result.result, std::move(expr) }
};
state.errors.push_back(CompileError("Casting not yet implemented", expr->m_meta));
return expr;
}
}
else {
@ -55,140 +37,58 @@ namespace {
return expr;
}
}
std::shared_ptr<types::Type> refresh_type(typecheck::Scope& scope, std::shared_ptr<types::Type> ty) {
if (ty->m_kind == types::TypeKind::Fundamental) {
return ty;
}
else if (ty->m_kind == types::TypeKind::Array) {
auto array_ty = dynamic_cast<types::ArrayType*>(ty.get());
array_ty->m_inner = refresh_type(scope, array_ty->m_inner);
return ty;
}
else if (ty->m_kind == types::TypeKind::Function) {
auto function_ty = dynamic_cast<types::FunctionType*>(ty.get());
function_ty->m_ret_ty = refresh_type(scope, function_ty->m_ret_ty);
for (int i = 0; i < static_cast<int>(function_ty->m_param_tys.size()); i++) {
function_ty->m_param_tys[i] = refresh_type(scope, function_ty->m_param_tys[i]);
}
return ty;
}
else if (ty->m_kind == types::TypeKind::Pointer) {
auto ptr_ty = dynamic_cast<types::PointerType*>(ty.get());
ptr_ty->m_inner = refresh_type(scope, ptr_ty->m_inner);
return ty;
}
else if (ty->m_kind == types::TypeKind::Struct) {
auto struct_ty = dynamic_cast<types::StructType*>(ty.get());
if (struct_ty->m_is_ref || struct_ty->m_is_def) {
if (scope.structs.find(*struct_ty->m_name) != scope.structs.end()) {
auto pre_existing = dynamic_cast<types::StructType*>(scope.structs[*struct_ty->m_name].get());
struct_ty->m_fields = pre_existing->m_fields;
}
}
if (struct_ty->m_fields) {
for (int i = 0; i < static_cast<int>((*struct_ty->m_fields).size()); i++) {
(*struct_ty->m_fields)[i].second = refresh_type(scope, (*struct_ty->m_fields)[i].second);
}
}
return ty;
}
else {
return ty;
}
}
}
namespace AST {
void IntLiteralExpression::typecheck_preprocess(typecheck::Scope& scope) {
this->m_ty = refresh_type(scope, this->m_ty);
}
typecheck::ExpressionType IntLiteralExpression::typecheck(
std::shared_ptr<types::Type> IntLiteralExpression::typecheck(
typecheck::State&,
typecheck::Scope&,
std::optional<std::shared_ptr<types::Type>> expected_ty
std::optional<std::shared_ptr<types::Type>>
) {
// Allow implicitly converting IntLiteralExpression to other types
// representable by integers.
if (expected_ty) {
if ((*expected_ty)->m_kind == types::TypeKind::Fundamental) {
auto ty = dynamic_cast<types::FundamentalType*>((*expected_ty).get());
if (
ty->m_ty == types::FundamentalTypeKind::Bool
|| ty->m_ty == types::FundamentalTypeKind::Char
|| ty->m_ty == types::FundamentalTypeKind::Int
) {
this->m_ty = *expected_ty;
}
}
}
return { this->m_ty, false, false };
return std::shared_ptr<types::Type>{
new types::FundamentalType{ types::FundamentalTypeKind::Int }
};
}
void StringLiteralExpression::typecheck_preprocess(typecheck::Scope&) {}
typecheck::ExpressionType StringLiteralExpression::typecheck(
std::shared_ptr<types::Type> StringLiteralExpression::typecheck(
typecheck::State&,
typecheck::Scope&,
std::optional<std::shared_ptr<types::Type>>
) {
auto char_ty = std::shared_ptr<types::Type>{
new types::FundamentalType{ true, types::FundamentalTypeKind::Char }
new types::FundamentalType{ types::FundamentalTypeKind::Char }
};
auto ptr_ty = new types::ArrayType{ char_ty, static_cast<uint32_t>(this->m_value.size()) + 1, true };
return { std::shared_ptr<types::Type>{ptr_ty}, true, false };
auto ptr_ty = new types::PointerType{ char_ty };
return std::shared_ptr<types::Type>{ptr_ty};
}
void ValueReferenceExpression::typecheck_preprocess(typecheck::Scope&) {}
typecheck::ExpressionType ValueReferenceExpression::typecheck(
std::shared_ptr<types::Type> ValueReferenceExpression::typecheck(
typecheck::State& state,
typecheck::Scope& scope,
std::optional<std::shared_ptr<types::Type>>
) {
if (scope.symbols.find(this->m_name) != scope.symbols.end()) {
return { scope.symbols[this->m_name], false, true };
return scope.symbols[this->m_name];
}
state.errors.push_back(CompileError("Value " + this->m_name + " not defined", this->m_meta));
return { std::shared_ptr<types::Type>{
new types::FundamentalType{ false, types::FundamentalTypeKind::Void }
}, false, true };
return std::shared_ptr<types::Type>{
new types::FundamentalType{ types::FundamentalTypeKind::Void }
};
}
void BinaryOperationExpression::typecheck_preprocess(typecheck::Scope& scope) {
this->m_lhs->typecheck_preprocess(scope);
this->m_rhs->typecheck_preprocess(scope);
}
typecheck::ExpressionType BinaryOperationExpression::typecheck(
std::shared_ptr<types::Type> BinaryOperationExpression::typecheck(
typecheck::State& state,
typecheck::Scope& scope,
std::optional<std::shared_ptr<types::Type>> expected_ty
std::optional<std::shared_ptr<types::Type>>
) {
auto lhs_res = this->m_lhs->typecheck(state, scope, {});
auto lhs_ty = lhs_res.type;
auto rhs_ty = this->m_rhs->typecheck(state, scope, {}).type;
auto lhs_ty = this->m_lhs->typecheck(state, scope, {});
auto rhs_ty = this->m_rhs->typecheck(state, scope, {});
if (this->m_binop == types::BinOp::Assignment) {
if (!lhs_res.lvalue) {
state.errors.push_back(CompileError("Value must be a modifiable l-value", this->m_lhs->m_meta));
}
else if (lhs_ty->m_const) {
state.errors.push_back(CompileError("Value must be a modifiable l-value", this->m_lhs->m_meta));
}
// Re-typecheck rhs to actually match lhs
auto rhs_ty = this->m_rhs->typecheck(state, scope, lhs_ty).type;
auto rhs_ty_res = check_type(state, rhs_ty, lhs_ty);
this->m_rhs = handle_res(std::move(this->m_rhs), rhs_ty_res, state);
return { lhs_ty, false, false };
return lhs_ty;
}
// Try to find a binop that matches exactly
auto binop = types::find_binop(
state.binops,
lhs_ty,
@ -197,56 +97,11 @@ namespace AST {
);
if (binop) {
return { binop->result(*binop, lhs_ty, rhs_ty), false, false };
return binop->result;
}
// If that fails, try to find binop that matches on one side perfectly
// and is castable on the other side, and would also be perfectly
// assignable to the expected value.
for (auto& binop : state.binops) {
if (expected_ty) {
// Skip any binops that would not be immediately assignable to
// the expected type
if (!types::types_equal(binop.result(binop, lhs_ty, rhs_ty), *expected_ty)) {
continue;
}
}
if (types::types_equal(binop.lhs, lhs_ty)) {
auto rhs_res = check_type(state, rhs_ty, binop.rhs);
if (!rhs_res.ok())
// Skip if not implicitly castable to lhs
continue;
this->m_rhs = handle_res(std::move(this->m_rhs), rhs_res, state);
return { binop.result(binop, lhs_ty, rhs_ty), false, false };
}
else if (types::types_equal(binop.rhs, rhs_ty)) {
auto lhs_res = check_type(state, lhs_ty, binop.lhs);
if (!lhs_res.ok())
// Skip if not implicitly castable to rhs
continue;
this->m_lhs = handle_res(std::move(this->m_lhs), lhs_res, state);
return { binop.result(binop, lhs_ty, rhs_ty), false, false };
}
}
// TODO check for binops that may be implicitly castable
// Finally check for any binop that allows the result to be implicitly
// casted to the result
for (auto& binop : state.binops) {
if (expected_ty) {
// Skip any binops that would not even be implicitly castable to
// the expected result
auto result_res = check_type(state, binop.result(binop, lhs_ty, rhs_ty), *expected_ty);
if (!result_res.ok())
continue;
}
auto lhs_result = check_type(state, lhs_ty, binop.lhs);
auto rhs_result = check_type(state, rhs_ty, binop.rhs);
this->m_lhs = handle_res(std::move(this->m_lhs), lhs_result, state);
this->m_rhs = handle_res(std::move(this->m_rhs), lhs_result, state);
return { binop.result(binop, lhs_ty, rhs_ty), false, false };
}
// No suitable binops found :(
state.errors.push_back(CompileError(
"No suitable binop between "
+ lhs_ty->formatted() + " "
@ -254,29 +109,22 @@ namespace AST {
+ rhs_ty->formatted(),
this->m_meta));
return { std::shared_ptr<types::Type>{
new types::FundamentalType{ false, types::FundamentalTypeKind::Void } }, false, false };
return std::shared_ptr<types::Type>{
new types::FundamentalType{ types::FundamentalTypeKind::Void } };
}
void FunctionCallExpression::typecheck_preprocess(typecheck::Scope& scope) {
this->m_fn_expr->typecheck_preprocess(scope);
for (auto& expr : this->m_args) {
expr->typecheck_preprocess(scope);
}
}
typecheck::ExpressionType FunctionCallExpression::typecheck(
std::shared_ptr<types::Type> FunctionCallExpression::typecheck(
typecheck::State& state,
typecheck::Scope& scope,
std::optional<std::shared_ptr<types::Type>>
) {
auto expr_ty = this->m_fn_expr->typecheck(state, scope, {}).type;
auto expr_ty = this->m_fn_expr->typecheck(state, scope, {});
if (expr_ty->m_kind != types::TypeKind::Function) {
state.errors.push_back(CompileError("Tried calling a non-function", this->m_meta));
return { std::shared_ptr<types::Type> {
new types::FundamentalType{ false, types::FundamentalTypeKind::Void }
}, false, false };
return std::shared_ptr<types::Type> {
new types::FundamentalType{ types::FundamentalTypeKind::Void }
};
}
auto fn_ty = dynamic_cast<types::FunctionType*>(expr_ty.get());
@ -291,9 +139,9 @@ namespace AST {
for (int i = 0; i < static_cast<int>(this->m_args.size()); i++) {
if (i < static_cast<int>(fn_ty->m_param_tys.size())) {
auto expected_param_ty = fn_ty->m_param_tys[i];
auto param_ty = this->m_args[i]->typecheck(state, scope, expected_param_ty).type;
auto param_ty = this->m_args[i]->typecheck(state, scope, expected_param_ty);
auto check_res = check_type(state, param_ty, expected_param_ty);
auto check_res = check_type(param_ty, expected_param_ty);
this->m_args[i] = handle_res(std::move(this->m_args[i]), check_res, state);
}
else {
@ -302,294 +150,34 @@ namespace AST {
}
}
return { fn_ty->m_ret_ty, false, false };
}
void CastExpression::typecheck_preprocess(typecheck::Scope& scope) {
this->m_ty = refresh_type(scope, this->m_ty);
this->m_expr->typecheck_preprocess(scope);
}
typecheck::ExpressionType CastExpression::typecheck(
typecheck::State& state,
typecheck::Scope& scope,
std::optional<std::shared_ptr<types::Type>>
) {
auto expr_ty = this->m_expr->typecheck(state, scope, {}).type;
auto cast = types::find_cast(state.casts, expr_ty, this->m_ty);
if (cast) {
return { cast->target_ty, false, false };
}
state.errors.push_back(CompileError("Cast from type "
+ expr_ty->formatted() + "to type " + this->m_ty->formatted()
+ " is not permitted", this->m_meta));
return { std::shared_ptr<types::Type> { new types::FundamentalType{
false,
types::FundamentalTypeKind::Void
} }, false, false };
}
void RefExpression::typecheck_preprocess(typecheck::Scope& scope) {
this->m_expr->typecheck_preprocess(scope);
}
typecheck::ExpressionType RefExpression::typecheck(
typecheck::State& state,
typecheck::Scope& scope,
std::optional<std::shared_ptr<types::Type>>
) {
auto expr_ty = this->m_expr->typecheck(state, scope, {}).type;
return { std::shared_ptr<types::Type> {
new types::PointerType{ false, expr_ty }
}, false, false };
}
void DerefExpression::typecheck_preprocess(typecheck::Scope& scope) {
this->m_expr->typecheck_preprocess(scope);
}
typecheck::ExpressionType DerefExpression::typecheck(
typecheck::State& state,
typecheck::Scope& scope,
std::optional<std::shared_ptr<types::Type>>
) {
auto expr_ty = this->m_expr->typecheck(state, scope, {});
if (expr_ty.type->m_kind != types::TypeKind::Pointer) {
state.errors.push_back(
CompileError("Tried to deref " + expr_ty.type->formatted(), this->m_meta));
return { std::shared_ptr<types::Type> {
new types::FundamentalType{ false, types::FundamentalTypeKind::Void }
}, false, expr_ty.lvalue };
}
auto ptr_ty = dynamic_cast<types::PointerType*>(expr_ty.type.get());
return { ptr_ty->m_inner, false, expr_ty.lvalue };
}
void IndexAccessExpression::typecheck_preprocess(typecheck::Scope& scope) {
this->m_expr->typecheck_preprocess(scope);
}
typecheck::ExpressionType IndexAccessExpression::typecheck(
typecheck::State& state,
typecheck::Scope& scope,
std::optional<std::shared_ptr<types::Type>>
) {
auto expr_ty = this->m_expr->typecheck(state, scope, {});
if (expr_ty.type->m_kind != types::TypeKind::Pointer && expr_ty.type->m_kind != types::TypeKind::Array) {
state.errors.push_back(
CompileError("Tried to index " + expr_ty.type->formatted(), this->m_meta));
return { std::shared_ptr<types::Type> {
new types::FundamentalType{ false, types::FundamentalTypeKind::Void }
}, false, expr_ty.lvalue };
}
if (expr_ty.type->m_kind == types::TypeKind::Pointer) {
auto ptr_ty = dynamic_cast<types::PointerType*>(expr_ty.type.get());
return { ptr_ty->m_inner, false, expr_ty.lvalue };
}
else if (expr_ty.type->m_kind == types::TypeKind::Array) {
auto ptr_ty = dynamic_cast<types::ArrayType*>(expr_ty.type.get());
return { ptr_ty->m_inner, false, expr_ty.lvalue };
}
// Default return type
return { std::shared_ptr<types::Type> {
new types::FundamentalType{ false, types::FundamentalTypeKind::Void }
}, false, expr_ty.lvalue };
}
void FieldAccessExpression::typecheck_preprocess(typecheck::Scope& scope) {
this->m_expr->typecheck_preprocess(scope);
}
typecheck::ExpressionType FieldAccessExpression::typecheck(
typecheck::State& state,
typecheck::Scope& scope,
std::optional<std::shared_ptr<types::Type>>
) {
auto expr_ty = this->m_expr->typecheck(state, scope, {});
if (expr_ty.type->m_kind != types::TypeKind::Struct) {
state.errors.push_back(
CompileError("Tried to access " + expr_ty.type->formatted() + "." + this->m_field, this->m_meta));
return { std::shared_ptr<types::Type> {
new types::FundamentalType{ false, types::FundamentalTypeKind::Void }
}, false, expr_ty.lvalue };
}
auto struct_ty = dynamic_cast<types::StructType*>(expr_ty.type.get());
if (struct_ty->m_fields) {
for (auto& field : *struct_ty->m_fields) {
if (field.first == this->m_field) {
return { field.second, false, expr_ty.lvalue };
}
}
state.errors.push_back(CompileError("No such field", this->m_meta));
return { std::shared_ptr<types::Type> {
new types::FundamentalType{ false, types::FundamentalTypeKind::Void }
}, false, expr_ty.lvalue };
}
state.errors.push_back(CompileError("Cannot access fields of opaque struct", this->m_meta));
return { std::shared_ptr<types::Type> {
new types::FundamentalType{ false, types::FundamentalTypeKind::Void }
}, false, expr_ty.lvalue };
}
void ListInitializerExpression::typecheck_preprocess(typecheck::Scope& scope) {
this->m_ty = refresh_type(scope, this->m_ty);
for (auto& expr : this->m_expressions) {
expr->typecheck_preprocess(scope);
}
}
typecheck::ExpressionType ListInitializerExpression::typecheck(
typecheck::State& state,
typecheck::Scope& scope,
std::optional<std::shared_ptr<types::Type>> expected_ty
) {
if (expected_ty) {
if ((*expected_ty)->m_kind == types::TypeKind::Array) {
auto array_ty = dynamic_cast<types::ArrayType*>(expected_ty->get());
for (auto& expr : this->m_expressions) {
auto expr_ty = expr->typecheck(state, scope, array_ty->m_inner).type;
auto expr_res = check_type(state, expr_ty, array_ty->m_inner);
expr = handle_res(std::move(expr), expr_res, state);
}
this->m_ty = std::shared_ptr<types::Type>{
new types::ArrayType{
array_ty->m_inner,
static_cast<uint32_t>(this->m_expressions.size()),
true
}
};
}
else if ((*expected_ty)->m_kind == types::TypeKind::Struct) {
auto struct_ty = dynamic_cast<types::StructType*>(expected_ty->get());
if (struct_ty->m_fields) {
if (this->m_expressions.size() > struct_ty->m_fields->size()) {
state.errors.push_back(CompileError(
"Too many initializer values for " + struct_ty->formatted(),
this->m_meta));
return { *expected_ty, true, false };
}
for (int i = 0; i < static_cast<int>(this->m_expressions.size()); i++) {
auto expected_field = (*struct_ty->m_fields)[i];
auto expr_ty = this->m_expressions[i]->typecheck(state, scope, expected_field.second).type;
auto res = check_type(state, expr_ty, expected_field.second);
this->m_expressions[i] = handle_res(std::move(this->m_expressions[i]), res, state);
}
this->m_ty = *expected_ty;
return { this->m_ty, true, false };
}
else {
if (this->m_expressions.size() > 0) {
state.errors.push_back(CompileError(
"Too many initializer values for " + struct_ty->formatted(),
this->m_meta));
return { *expected_ty, true, false };
}
else {
this->m_ty = *expected_ty;
return { this->m_ty, true, false };
}
}
}
else {
return { std::shared_ptr<types::Type> {
new types::FundamentalType{ false, types::FundamentalTypeKind::Void }
}, true, false };
}
return { this->m_ty, true, false };
}
// No expected ty, try to infer array type from elements
if (this->m_expressions.size() == 0) {
this->m_ty = std::shared_ptr<types::Type>{
new types::ArrayType{
std::make_shared<types::FundamentalType>(true, types::FundamentalTypeKind::Void),
0,
true
}
};
return { this->m_ty, true, false };
}
else {
auto first_expr_ty = this->m_expressions[0]->typecheck(state, scope, {}).type;
for (int i = 1; i < static_cast<int>(this->m_expressions.size()); i++) {
auto expr_ty = this->m_expressions[i]->typecheck(state, scope, first_expr_ty).type;
auto expr_res = check_type(state, expr_ty, first_expr_ty);
this->m_expressions[i] = handle_res(std::move(this->m_expressions[i]), expr_res, state);
}
this->m_ty = std::shared_ptr<types::Type>{
new types::ArrayType{
first_expr_ty,
static_cast<uint32_t>(this->m_expressions.size()),
true
}
};
return { this->m_ty, true, false };
}
}
void ReturnStatement::typecheck_preprocess(typecheck::Scope& scope) {
this->m_expr->typecheck_preprocess(scope);
return fn_ty->m_ret_ty;
}
void ReturnStatement::typecheck(typecheck::State& state, typecheck::Scope& scope) {
auto res_ty = this->m_expr->typecheck(state, scope, scope.return_ty).type;
auto res_ty = this->m_expr->typecheck(state, scope, scope.return_ty);
if (scope.return_ty) {
auto check_res = check_type(state, res_ty, *scope.return_ty);
auto check_res = check_type(res_ty, *scope.return_ty);
this->m_expr = handle_res(std::move(this->m_expr), check_res, state);
}
}
void InitializationStatement::typecheck_preprocess(typecheck::Scope& scope) {
this->m_type = refresh_type(scope, this->m_type);
if (this->m_expr)
(*this->m_expr)->typecheck_preprocess(scope);
}
void InitializationStatement::typecheck(typecheck::State& state, typecheck::Scope& scope) {
if (this->m_expr) {
auto expr_ty = (*this->m_expr)->typecheck(state, scope, this->m_type);
if (this->m_type->m_kind == types::TypeKind::Array && !expr_ty.array_initializer) {
state.errors.push_back(
CompileError("Arrays can only be initialized with list-initializers or strings", this->m_meta)
);
}
auto check_res = check_type(state, expr_ty.type, this->m_type);
this->m_expr = handle_res(std::move(*this->m_expr), check_res, state);
(*this->m_expr)->typecheck(state, scope, this->m_type);
}
scope.symbols[this->m_name] = this->m_type;
}
void ExpressionStatement::typecheck_preprocess(typecheck::Scope& scope) {
this->m_expr->typecheck_preprocess(scope);
}
void ExpressionStatement::typecheck(typecheck::State& state, typecheck::Scope& scope) {
this->m_expr->typecheck(state, scope, {});
}
void IfStatement::typecheck_preprocess(typecheck::Scope& scope) {
this->m_condition->typecheck_preprocess(scope);
this->m_then->typecheck_preprocess(scope);
if (this->m_else)
(*this->m_else)->typecheck_preprocess(scope);
}
void IfStatement::typecheck(typecheck::State& state, typecheck::Scope& scope) {
auto bool_ty = std::shared_ptr<types::Type>{
new types::FundamentalType{ false, types::FundamentalTypeKind::Bool } };
auto expr_ty = this->m_condition->typecheck(state, scope, bool_ty).type;
new types::FundamentalType{ types::FundamentalTypeKind::Bool } };
auto expr_ty = this->m_condition->typecheck(state, scope, bool_ty);
auto check_res = check_type(state, expr_ty, bool_ty);
auto check_res = check_type(expr_ty, bool_ty);
this->m_condition = handle_res(std::move(this->m_condition), check_res, state);
this->m_then->typecheck(state, scope);
@ -598,19 +186,6 @@ namespace AST {
}
}
void Function::typecheck_preprocess(typecheck::Scope& scope) {
this->m_return_ty = refresh_type(scope, this->m_return_ty);
for (auto& param : this->m_params) {
param.second = refresh_type(scope, param.second);
}
if (this->m_statements) {
for (auto& statement : *this->m_statements) {
statement->typecheck_preprocess(scope);
}
}
}
void Function::typecheck(typecheck::State& state, typecheck::Scope& scope) {
auto return_ty = this->m_return_ty;
std::vector<std::shared_ptr<types::Type>> param_tys{};
@ -618,7 +193,7 @@ namespace AST {
param_tys.push_back(param.second);
}
auto function_ty = new types::FunctionType{ true, return_ty, param_tys, this->m_is_vararg };
auto function_ty = new types::FunctionType{ return_ty, param_tys, this->m_is_vararg };
scope.symbols[this->m_name] = std::shared_ptr<types::Type>{ function_ty };
typecheck::Scope inner{ scope };
@ -636,28 +211,4 @@ namespace AST {
}
}
}
void TopLevelTypedef::typecheck_preprocess(typecheck::Scope& scope) {
if (this->m_ty->m_kind == types::TypeKind::Struct) {
auto struct_ty = dynamic_cast<types::StructType*>(this->m_ty.get());
if (struct_ty->m_is_ref || struct_ty->m_is_def) {
return;
}
if (struct_ty->m_name) {
scope.structs[*struct_ty->m_name] = this->m_ty;
}
}
}
void TopLevelTypedef::typecheck(typecheck::State&, typecheck::Scope& scope) {
if (this->m_ty->m_kind == types::TypeKind::Struct) {
auto struct_ty = dynamic_cast<types::StructType*>(this->m_ty.get());
if (struct_ty->m_is_ref || struct_ty->m_is_def) {
return;
}
if (struct_ty->m_name) {
scope.structs[*struct_ty->m_name] = this->m_ty;
}
}
}
}

View File

@ -5,27 +5,18 @@
#include "types.h"
#include "binops.h"
#include "casting.h"
#include "errors.h"
namespace typecheck {
struct Scope {
std::map<std::string, std::shared_ptr<types::Type>> symbols;
std::map<std::string, std::shared_ptr<types::Type>> structs;
std::optional<std::shared_ptr<types::Type>> return_ty;
};
struct State {
std::vector<types::BinopDefinition> binops;
std::vector<types::CastDefinition> casts;
std::vector<CompileError> errors;
};
struct ExpressionType {
std::shared_ptr<types::Type> type;
bool array_initializer;
bool lvalue;
};
}
#endif

View File

@ -1,10 +1,8 @@
#include <sstream>
#include <iostream>
#include "types.h"
#include "binops.h"
#include "builder.h"
namespace types {
int operator_precedence(BinOp& op) {
@ -42,62 +40,85 @@ namespace types {
}
std::string FundamentalType::formatted() {
std::stringstream out{ "" };
if (this->m_const)
out << "const ";
switch (this->m_ty) {
case FundamentalTypeKind::Int:
out << "Int";
break;
return "Int";
case FundamentalTypeKind::Bool:
out << "Bool";
break;
return "Bool";
case FundamentalTypeKind::Char:
out << "Char";
break;
return "Char";
case FundamentalTypeKind::Void:
out << "Void";
break;
return "Void";
default:
out << "Unknown";
break;
return "Unknown";
}
return out.str();
}
llvm::Value* Type::add(codegen::Builder&, llvm::Value*, llvm::Value*) {
throw std::runtime_error("Invalid operation for this type");
}
llvm::Value* Type::sub(codegen::Builder&, llvm::Value*, llvm::Value*) {
throw std::runtime_error("Invalid operation for this type");
}
llvm::Value* Type::lt(codegen::Builder&, llvm::Value*, llvm::Value*) {
throw std::runtime_error("Invalid operation for this type");
}
llvm::Value* Type::gt(codegen::Builder&, llvm::Value*, llvm::Value*) {
throw std::runtime_error("Invalid operation for this type");
}
std::optional<std::shared_ptr<Type>> Type::return_type() {
return {};
}
bool Type::is_signed() {
return false;
}
std::pair<llvm::Value*, std::shared_ptr<Type>> FundamentalType::load(codegen::Builder&, llvm::Value* ptr, codegen::TypeMap&) {
std::pair<llvm::Value*, std::shared_ptr<Type>> FundamentalType::load(codegen::Builder&, llvm::Value* ptr) {
auto self = std::make_shared<FundamentalType>(*this);
return std::pair(ptr, self);
}
bool FundamentalType::is_signed() {
llvm::Value* FundamentalType::add(codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs) {
switch (this->m_ty) {
case FundamentalTypeKind::Int:
return true;
case FundamentalTypeKind::Bool:
case FundamentalTypeKind::Char:
return false;
return builder.builder->CreateAdd(lhs, rhs, "add");
default:
throw std::runtime_error("Invalid type for add");
}
}
llvm::Value* FundamentalType::sub(codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs) {
switch (this->m_ty) {
case FundamentalTypeKind::Int:
case FundamentalTypeKind::Bool:
case FundamentalTypeKind::Char:
return builder.builder->CreateSub(lhs, rhs, "sub");
default:
throw std::runtime_error("Invalid type");
}
}
uint32_t FundamentalType::size() {
llvm::Value* FundamentalType::lt(codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs) {
switch (this->m_ty) {
case FundamentalTypeKind::Int:
return 32;
case FundamentalTypeKind::Bool:
return 1;
case FundamentalTypeKind::Char:
return 8;
return builder.builder->CreateCmp(llvm::CmpInst::Predicate::ICMP_SLT, lhs, rhs, "cmp");
default:
throw std::runtime_error("Invalid type");
}
}
llvm::Value* FundamentalType::gt(codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs) {
switch (this->m_ty) {
case FundamentalTypeKind::Int:
case FundamentalTypeKind::Bool:
case FundamentalTypeKind::Char:
return builder.builder->CreateCmp(llvm::CmpInst::Predicate::ICMP_SGT, lhs, rhs);
default:
throw std::runtime_error("Invalid type");
}
@ -105,9 +126,6 @@ namespace types {
std::string FunctionType::formatted() {
std::stringstream out{ "" };
if (this->m_const)
out << "const ";
out << "(";
int counter = 0;
@ -131,96 +149,24 @@ namespace types {
return this->m_ret_ty;
}
std::pair<llvm::Value*, std::shared_ptr<Type>> FunctionType::load(codegen::Builder&, llvm::Value* ptr, codegen::TypeMap&) {
std::pair<llvm::Value*, std::shared_ptr<Type>> FunctionType::load(codegen::Builder&, llvm::Value* ptr) {
auto self = std::make_shared<FunctionType>(*this);
return std::pair(ptr, self);
}
uint32_t FunctionType::size() {
return 64;
}
std::string PointerType::formatted() {
std::stringstream out{ "" };
out << this->m_inner->formatted() << "*";
if (this->m_const)
out << " const";
return out.str();
}
std::pair<llvm::Value*, std::shared_ptr<Type>> PointerType::load(codegen::Builder& builder, llvm::Value* ptr, codegen::TypeMap& structs) {
std::pair<llvm::Value*, std::shared_ptr<Type>> PointerType::load(codegen::Builder& builder, llvm::Value* ptr) {
return std::pair(
builder.builder->CreateLoad(this->m_inner->codegen(builder, structs), ptr),
builder.builder->CreateLoad(this->m_inner->codegen(builder), ptr),
this->m_inner
);
}
uint32_t PointerType::size() {
return 64;
}
std::string ArrayType::formatted() {
std::stringstream out{ "" };
// Arrays are always constant, no reason to format it.
out << this->m_inner->formatted();
out << "[" << this->m_size << "]";
return out.str();
}
std::pair<llvm::Value*, std::shared_ptr<Type>> ArrayType::load(codegen::Builder&, llvm::Value* ptr, codegen::TypeMap&) {
auto self = std::make_shared<ArrayType>(*this);
return std::pair(ptr, self);
}
uint32_t ArrayType::size() {
return this->m_size * this->m_inner->size();
}
std::string StructType::formatted() {
std::stringstream out{ "" };
if (this->m_const)
out << "const ";
out << "struct(" << this->m_id << ")";
if (this->m_is_ref)
out << "(ref)";
out << " ";
if (this->m_name) {
out << *this->m_name << " ";
}
if (this->m_fields) {
out << "{ ";
int counter = 0;
for (auto& field : *this->m_fields) {
if (counter++ > 0)
out << ", ";
out << field.first << ": " << field.second->formatted() << " ";
}
out << "}";
}
return out.str();
}
std::pair<llvm::Value*, std::shared_ptr<Type>> StructType::load(codegen::Builder&, llvm::Value* ptr, codegen::TypeMap&) {
auto self = std::make_shared<StructType>(*this);
return std::pair(ptr, self);
}
uint32_t StructType::size() {
uint32_t size{ 0 };
if (this->m_fields) {
for (auto& field : *this->m_fields) {
size += field.second->size();
}
}
return size;
}
bool types_equal(std::shared_ptr<types::Type> type1, std::shared_ptr<types::Type> type2) {
if (type1->m_kind != type2->m_kind)
return false;
@ -256,38 +202,6 @@ namespace types {
return types_equal(ty1->m_inner, ty2->m_inner);
}
else if (type1->m_kind == TypeKind::Array) {
auto ty1 = dynamic_cast<ArrayType*>(type1.get());
auto ty2 = dynamic_cast<ArrayType*>(type2.get());
return types_equal(ty1->m_inner, ty2->m_inner) && ty1->m_size == ty2->m_size;
}
else if (type1->m_kind == TypeKind::Struct) {
auto ty1 = dynamic_cast<StructType*>(type1.get());
auto ty2 = dynamic_cast<StructType*>(type2.get());
if (ty1->m_is_ref || ty2->m_is_ref)
return ty1->m_id == ty2->m_id;
if (ty1->m_fields.has_value() != ty2->m_fields.has_value())
return false;
if (ty1->m_fields) {
if (ty1->m_fields->size() != ty2->m_fields->size())
return false;
for (int i = 0; i < static_cast<int>(ty1->m_fields->size()); i++) {
auto field1 = (*ty1->m_fields)[i];
auto field2 = (*ty2->m_fields)[i];
if (!types_equal(field1.second, field2.second))
return false;
}
}
return true;
}
else {
return false;
}

View File

@ -11,8 +11,6 @@ namespace types {
Fundamental,
Function,
Pointer,
Array,
Struct,
};
enum FundamentalTypeKind {
@ -20,35 +18,35 @@ namespace types {
Bool,
Char,
Void,
/// @brief Mainly used for binop resolution
Any,
};
class Type {
public:
TypeKind m_kind;
bool m_const;
Type(TypeKind kind, bool is_const) : m_kind{ kind }, m_const{ is_const } {}
Type(TypeKind kind) : m_kind{ kind } {}
virtual ~Type() = default;
virtual std::string formatted() = 0;
virtual llvm::Type* codegen(codegen::Builder& builder, codegen::TypeMap& structs) = 0;
virtual std::pair<llvm::Value*, std::shared_ptr<Type>> load(codegen::Builder& builder, llvm::Value* ptr, codegen::TypeMap& structs) = 0;
virtual llvm::Type* codegen(codegen::Builder& builder) = 0;
virtual std::pair<llvm::Value*, std::shared_ptr<Type>> load(codegen::Builder& builder, llvm::Value* ptr) = 0;
virtual std::optional<std::shared_ptr<Type>> return_type();
virtual bool is_signed();
virtual uint32_t size() = 0;
virtual llvm::Value* add(codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs);
virtual llvm::Value* sub(codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs);
virtual llvm::Value* lt(codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs);
virtual llvm::Value* gt(codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs);
};
class FundamentalType : public Type {
public:
FundamentalTypeKind m_ty;
FundamentalType(bool is_const, FundamentalTypeKind kind) : Type(TypeKind::Fundamental, is_const), m_ty{ kind } {}
FundamentalType(FundamentalTypeKind kind) : Type(TypeKind::Fundamental), m_ty{ kind } {}
virtual ~FundamentalType() override = default;
virtual std::string formatted() override;
virtual llvm::Type* codegen(codegen::Builder& builder, codegen::TypeMap& structs) override;
virtual std::pair<llvm::Value*, std::shared_ptr<Type>> load(codegen::Builder& builder, llvm::Value* ptr, codegen::TypeMap& structs) override;
virtual bool is_signed() override;
virtual uint32_t size() override;
virtual llvm::Type* codegen(codegen::Builder& builder) override;
virtual std::pair<llvm::Value*, std::shared_ptr<Type>> load(codegen::Builder& builder, llvm::Value* ptr) override;
virtual llvm::Value* add(codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs) override;
virtual llvm::Value* sub(codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs) override;
virtual llvm::Value* lt(codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs) override;
virtual llvm::Value* gt(codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs) override;
};
@ -57,68 +55,31 @@ namespace types {
std::shared_ptr<Type> m_ret_ty;
std::vector<std::shared_ptr<Type>> m_param_tys;
bool m_vararg;
FunctionType(bool is_const, std::shared_ptr<Type> ret_ty, std::vector<std::shared_ptr<Type>> param_tys, bool vararg)
: Type(TypeKind::Function, is_const)
FunctionType(std::shared_ptr<Type> ret_ty, std::vector<std::shared_ptr<Type>> param_tys, bool vararg)
: Type(TypeKind::Function)
, m_ret_ty{ std::move(ret_ty) }
, m_param_tys{ std::move(param_tys) }
, m_vararg{ vararg } {
}
virtual ~FunctionType() override = default;
virtual std::string formatted() override;
virtual llvm::Type* codegen(codegen::Builder& builder, codegen::TypeMap& structs) override;
virtual std::pair<llvm::Value*, std::shared_ptr<Type>> load(codegen::Builder& builder, llvm::Value* ptr, codegen::TypeMap& structs) override;
virtual llvm::Type* codegen(codegen::Builder& builder) override;
virtual std::pair<llvm::Value*, std::shared_ptr<Type>> load(codegen::Builder& builder, llvm::Value* ptr) override;
virtual std::optional<std::shared_ptr<Type>> return_type() override;
virtual uint32_t size() override;
};
class PointerType : public Type {
public:
std::shared_ptr<Type> m_inner;
PointerType(bool is_const, std::shared_ptr<Type> inner)
: Type(TypeKind::Pointer, is_const), m_inner{ std::move(inner) } {
PointerType(std::shared_ptr<Type> inner)
: Type(TypeKind::Pointer), m_inner{ std::move(inner) } {
}
virtual ~PointerType() override = default;
virtual std::string formatted() override;
virtual llvm::Type* codegen(codegen::Builder& builder, codegen::TypeMap& structs) override;
virtual std::pair<llvm::Value*, std::shared_ptr<Type>> load(codegen::Builder& builder, llvm::Value* ptr, codegen::TypeMap& structs) override;
virtual uint32_t size() override;
};
class ArrayType : public Type {
public:
std::shared_ptr<Type> m_inner;
uint32_t m_size;
bool m_raw;
ArrayType(std::shared_ptr<Type> inner, uint32_t size, bool raw)
: Type(TypeKind::Array, true), m_inner{ std::move(inner) }, m_size{ size }, m_raw{ raw } {
}
virtual ~ArrayType() override = default;
virtual std::string formatted() override;
virtual llvm::Type* codegen(codegen::Builder& builder, codegen::TypeMap& structs) override;
virtual std::pair<llvm::Value*, std::shared_ptr<Type>> load(codegen::Builder& builder, llvm::Value* ptr, codegen::TypeMap& structs) override;
virtual uint32_t size() override;
};
typedef std::pair<std::string, std::shared_ptr<types::Type>> StructField;
class StructType : public Type {
public:
std::optional<std::string> m_name;
std::optional<std::vector<StructField>> m_fields;
bool m_is_ref;
bool m_is_def;
uint32_t m_id;
StructType(bool is_const, std::optional<std::string> name, std::optional<std::vector<StructField>> fields, bool is_ref, bool is_def, uint32_t id)
: Type(TypeKind::Struct, is_const), m_name{ name }, m_fields{ fields }, m_is_ref{ is_ref }, m_is_def{ is_def }, m_id{ id } {
}
virtual ~StructType() override = default;
virtual std::string formatted() override;
virtual llvm::Type* codegen(codegen::Builder& builder, codegen::TypeMap& structs) override;
virtual std::pair<llvm::Value*, std::shared_ptr<Type>> load(codegen::Builder& builder, llvm::Value* ptr, codegen::TypeMap& structs) override;
virtual uint32_t size() override;
virtual llvm::Type* codegen(codegen::Builder& builder) override;
virtual std::pair<llvm::Value*, std::shared_ptr<Type>> load(codegen::Builder& builder, llvm::Value* ptr) override;
};
bool types_equal(std::shared_ptr<types::Type> type1, std::shared_ptr<types::Type> type2);

45
test.c
View File

@ -6,50 +6,7 @@ int fibonacci(int n) {
return fibonacci(n - 1) + fibonacci(n - 2);
}
void change_first(char otus[5]) {
otus[0] = 115;
}
struct Otus;
struct Otus {
int field;
int second[2];
};
void update(struct Otus potus) {
potus.field = 20;
}
void update_ptr(char* ptr) {
*ptr = 50;
}
int main() {
char text[30] = "10th fibonacci number is %d!\n";
printf(text, fibonacci(10));
char somelist[5] = { 1, 2, 3, 4, 5 };
char* somelist_ptr = somelist;
change_first(somelist);
printf("first element: %d!\n", somelist[0]);
printf("first element via ptr: %d!\n", somelist_ptr[0]);
struct Otus otus = { 5, { 7, 3 } };
update(otus);
printf("first field: %d!\n", otus.field);
printf("second field's second element: %d!\n", otus.second[1]);
char hello = 10;
update_ptr(&hello);
printf("hello: %d!\n", hello);
char twod_array[5][5];
twod_array[0][0] = 50;
printf("2d array: %d!\n", twod_array[0][0]);
printf("10th fibonacci number is %d!", fibonacci(10));
return 0;
}