Add compilation of casts
This commit is contained in:
parent
dab4d47b8e
commit
fa01b22b81
@ -24,6 +24,7 @@ add_executable(${PROJECT_NAME}
|
|||||||
src/types.cpp
|
src/types.cpp
|
||||||
src/typechecker.cpp
|
src/typechecker.cpp
|
||||||
src/binops.cpp
|
src/binops.cpp
|
||||||
|
src/casting.cpp
|
||||||
)
|
)
|
||||||
target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_20)
|
target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_20)
|
||||||
target_compile_options(${PROJECT_NAME} PRIVATE -Wall -Weffc++ -Wextra -Wpedantic -Werror)
|
target_compile_options(${PROJECT_NAME} PRIVATE -Wall -Weffc++ -Wextra -Wpedantic -Werror)
|
||||||
|
|||||||
@ -42,6 +42,13 @@ namespace AST {
|
|||||||
return out.str();
|
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 ExpressionStatement::formatted() {
|
std::string ExpressionStatement::formatted() {
|
||||||
std::stringstream out{ "" };
|
std::stringstream out{ "" };
|
||||||
out << this->m_expr->formatted();
|
out << this->m_expr->formatted();
|
||||||
|
|||||||
23
src/ast.h
23
src/ast.h
@ -140,6 +140,29 @@ namespace AST {
|
|||||||
) override;
|
) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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> typecheck(
|
||||||
|
typecheck::State& state,
|
||||||
|
typecheck::Scope& scope,
|
||||||
|
std::optional<std::shared_ptr<types::Type>> expected_ty
|
||||||
|
) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
class ReturnStatement : public Statement {
|
class ReturnStatement : public Statement {
|
||||||
private:
|
private:
|
||||||
|
|||||||
41
src/casting.cpp
Normal file
41
src/casting.cpp
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
|
||||||
|
#include "casting.h"
|
||||||
|
|
||||||
|
namespace types {
|
||||||
|
std::vector<CastDefinition> create_casts() {
|
||||||
|
std::vector<CastDefinition> casts{};
|
||||||
|
|
||||||
|
auto int_ty = std::shared_ptr<Type>{
|
||||||
|
new FundamentalType{ FundamentalTypeKind::Int } };
|
||||||
|
auto char_ty = std::shared_ptr<Type>{
|
||||||
|
new FundamentalType{ FundamentalTypeKind::Char } };
|
||||||
|
auto bool_ty = std::shared_ptr<Type>{
|
||||||
|
new FundamentalType{ FundamentalTypeKind::Bool } };
|
||||||
|
|
||||||
|
for (auto& source_ty : { int_ty, char_ty, bool_ty }) {
|
||||||
|
for (auto& target_ty : { int_ty, char_ty, bool_ty }) {
|
||||||
|
casts.push_back(CastDefinition{ source_ty, target_ty, false,
|
||||||
|
[](codegen::Builder& builder, std::shared_ptr<Type> target, llvm::Value* value) {
|
||||||
|
return builder.builder->CreateSExtOrTrunc(value, target->codegen(builder), "cast");
|
||||||
|
} });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return casts;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::optional<CastDefinition> find_cast(
|
||||||
|
std::vector<CastDefinition>& casts,
|
||||||
|
std::shared_ptr<Type> casted_ty,
|
||||||
|
std::shared_ptr<Type> target_ty) {
|
||||||
|
|
||||||
|
for (auto& cast : casts) {
|
||||||
|
if (types_equal(cast.casted_ty, casted_ty) && types_equal(cast.target_ty, target_ty)) {
|
||||||
|
return cast;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
23
src/casting.h
Normal file
23
src/casting.h
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
#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
|
||||||
@ -11,7 +11,7 @@
|
|||||||
|
|
||||||
namespace codegen {
|
namespace codegen {
|
||||||
Scope Scope::with_lvalue() {
|
Scope Scope::with_lvalue() {
|
||||||
return Scope{ this->binops, this->values, true };
|
return Scope{ this->binops, this->casts, this->values, true };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -99,6 +99,18 @@ namespace AST {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
codegen::StackValue CastExpression::codegen(codegen::Builder& builder, codegen::Scope& scope) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
void ReturnStatement::codegen(codegen::Builder& builder, codegen::Scope& scope) {
|
void ReturnStatement::codegen(codegen::Builder& builder, codegen::Scope& scope) {
|
||||||
if (!builder.block)
|
if (!builder.block)
|
||||||
return;
|
return;
|
||||||
|
|||||||
@ -9,6 +9,7 @@
|
|||||||
#include "builder.h"
|
#include "builder.h"
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
#include "binops.h"
|
#include "binops.h"
|
||||||
|
#include "casting.h"
|
||||||
#include "tokens.h"
|
#include "tokens.h"
|
||||||
|
|
||||||
namespace codegen {
|
namespace codegen {
|
||||||
@ -19,6 +20,7 @@ namespace codegen {
|
|||||||
|
|
||||||
struct Scope {
|
struct Scope {
|
||||||
std::vector<types::BinopDefinition>& binops;
|
std::vector<types::BinopDefinition>& binops;
|
||||||
|
std::vector<types::CastDefinition>& casts;
|
||||||
|
|
||||||
std::map<std::string, StackValue> values;
|
std::map<std::string, StackValue> values;
|
||||||
bool is_lvalue;
|
bool is_lvalue;
|
||||||
|
|||||||
@ -99,6 +99,7 @@ std::optional<CompileOutput> compile(std::string_view in_filename) {
|
|||||||
|
|
||||||
typecheck::State typecheck_state{};
|
typecheck::State typecheck_state{};
|
||||||
typecheck_state.binops = types::create_binops();
|
typecheck_state.binops = types::create_binops();
|
||||||
|
typecheck_state.casts = types::create_casts();
|
||||||
|
|
||||||
typecheck::Scope typecheck_scope{};
|
typecheck::Scope typecheck_scope{};
|
||||||
for (auto& tls : statements) {
|
for (auto& tls : statements) {
|
||||||
@ -118,6 +119,7 @@ std::optional<CompileOutput> compile(std::string_view in_filename) {
|
|||||||
|
|
||||||
codegen::Scope cg_scope{
|
codegen::Scope cg_scope{
|
||||||
.binops = typecheck_state.binops,
|
.binops = typecheck_state.binops,
|
||||||
|
.casts = typecheck_state.casts,
|
||||||
.values = {},
|
.values = {},
|
||||||
.is_lvalue = false,
|
.is_lvalue = false,
|
||||||
};
|
};
|
||||||
|
|||||||
@ -166,6 +166,25 @@ namespace AST {
|
|||||||
return fn_ty->m_ret_ty;
|
return fn_ty->m_ret_ty;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::shared_ptr<types::Type> CastExpression::typecheck(
|
||||||
|
typecheck::State& state,
|
||||||
|
typecheck::Scope& scope,
|
||||||
|
std::optional<std::shared_ptr<types::Type>>
|
||||||
|
) {
|
||||||
|
auto expr_ty = this->m_expr->typecheck(state, scope, {});
|
||||||
|
auto cast = types::find_cast(state.casts, expr_ty, this->m_ty);
|
||||||
|
if (cast) {
|
||||||
|
return cast->target_ty;
|
||||||
|
}
|
||||||
|
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{
|
||||||
|
types::FundamentalTypeKind::Void
|
||||||
|
} };
|
||||||
|
}
|
||||||
|
|
||||||
void ReturnStatement::typecheck(typecheck::State& state, typecheck::Scope& scope) {
|
void ReturnStatement::typecheck(typecheck::State& state, typecheck::Scope& scope) {
|
||||||
auto res_ty = this->m_expr->typecheck(state, scope, scope.return_ty);
|
auto res_ty = this->m_expr->typecheck(state, scope, scope.return_ty);
|
||||||
if (scope.return_ty) {
|
if (scope.return_ty) {
|
||||||
|
|||||||
@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
#include "binops.h"
|
#include "binops.h"
|
||||||
|
#include "casting.h"
|
||||||
#include "errors.h"
|
#include "errors.h"
|
||||||
|
|
||||||
namespace typecheck {
|
namespace typecheck {
|
||||||
@ -15,6 +16,7 @@ namespace typecheck {
|
|||||||
|
|
||||||
struct State {
|
struct State {
|
||||||
std::vector<types::BinopDefinition> binops;
|
std::vector<types::BinopDefinition> binops;
|
||||||
|
std::vector<types::CastDefinition> casts;
|
||||||
std::vector<CompileError> errors;
|
std::vector<CompileError> errors;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user