Compare commits

...

8 Commits

15 changed files with 249 additions and 15 deletions

View File

@ -24,6 +24,7 @@ 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

@ -42,6 +42,13 @@ 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 ExpressionStatement::formatted() {
std::stringstream out{ "" };
out << this->m_expr->formatted();

View File

@ -42,8 +42,15 @@ namespace AST {
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 } {}
IntLiteralExpression(token::Metadata meta, int value)
: Expression{ meta }
, m_value{ value }
, m_ty{ { std::shared_ptr<types::Type>{
new types::FundamentalType{types::FundamentalTypeKind::Int}
} } } {
}
virtual ~IntLiteralExpression() override = default;
virtual std::string formatted() override;
virtual codegen::StackValue codegen(codegen::Builder& builder, codegen::Scope& scope) override;
@ -133,6 +140,29 @@ namespace AST {
) 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 {
private:

View File

@ -12,8 +12,8 @@ namespace types {
auto bool_ty = std::shared_ptr<types::Type>{
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,
ty, [](codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs) {
@ -25,12 +25,14 @@ namespace types {
ty, [](codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs) {
return builder.builder->CreateSub(lhs, rhs, "sub");
} });
}
// Comparisons
// Signed comparisons
for (auto& ty : { int_ty }) {
definitions.push_back(BinopDefinition{
ty, types::BinOp::LessThan, ty,
bool_ty, [](codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs) {
return builder.builder->CreateICmpSLE(lhs, rhs, "icmpsle");
return builder.builder->CreateICmpSLT(lhs, rhs, "icmpslt");
} });
definitions.push_back(BinopDefinition{
@ -40,6 +42,21 @@ namespace types {
} });
}
// Unsigned comparisons
for (auto& ty : { bool_ty, char_ty }) {
definitions.push_back(BinopDefinition{
ty, types::BinOp::LessThan, ty,
bool_ty, [](codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs) {
return builder.builder->CreateICmpULT(lhs, rhs, "icmpslt");
} });
definitions.push_back(BinopDefinition{
ty, types::BinOp::GreaterThan, ty,
bool_ty, [](codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs) {
return builder.builder->CreateICmpUGT(lhs, rhs, "icmpsgt");
} });
}
return definitions;
}

55
src/casting.cpp Normal file
View File

@ -0,0 +1,55 @@
#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 }) {
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;
} });
}
else if (target_ty->is_signed()) {
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");
} });
}
else {
casts.push_back(CastDefinition{ source_ty, target_ty, false,
[](codegen::Builder& builder, std::shared_ptr<Type> target, llvm::Value* value) {
return builder.builder->CreateZExtOrTrunc(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
View 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

View File

@ -11,19 +11,17 @@
namespace codegen {
Scope Scope::with_lvalue() {
return Scope{ this->binops, this->values, true };
return Scope{ this->binops, this->casts, this->values, true };
}
}
namespace AST {
codegen::StackValue IntLiteralExpression::codegen(codegen::Builder& builder, codegen::Scope&) {
auto ty = builder.builder->getInt32Ty();
auto stack_type = new types::FundamentalType{ types::FundamentalTypeKind::Int };
auto ty = this->m_ty->codegen(builder);
return codegen::StackValue{
llvm::ConstantInt::get(ty, this->m_value),
std::unique_ptr<types::Type>{stack_type}
this->m_ty,
};
}
@ -101,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) {
if (!builder.block)
return;
@ -132,7 +142,9 @@ namespace AST {
builder.builder->CreateStore(value.value, ptr, false);
}
scope.values[this->m_name] = codegen::StackValue{ ptr, this->m_type };
auto ptr_ty = std::shared_ptr<types::Type>{ new types::PointerType{ this->m_type } };
scope.values[this->m_name] = codegen::StackValue{ ptr, ptr_ty };
}
void IfStatement::codegen(codegen::Builder& builder, codegen::Scope& scope) {

View File

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

View File

@ -99,6 +99,7 @@ std::optional<CompileOutput> compile(std::string_view in_filename) {
typecheck::State typecheck_state{};
typecheck_state.binops = types::create_binops();
typecheck_state.casts = types::create_casts();
typecheck::Scope typecheck_scope{};
for (auto& tls : statements) {
@ -118,6 +119,7 @@ std::optional<CompileOutput> compile(std::string_view in_filename) {
codegen::Scope cg_scope{
.binops = typecheck_state.binops,
.casts = typecheck_state.casts,
.values = {},
.is_lvalue = false,
};

View File

@ -80,10 +80,39 @@ namespace parsing {
}
}
Result<std::shared_ptr<types::Type>, std::string> parse_cast(token::TokenStream& stream) {
token::TokenStream inner{ stream };
try {
inner.expect(token::Type::Symbol, "(");
auto ty = parse_type(inner).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) {
token::TokenStream inner{ stream };
try {
auto before_meta = inner.metadata();
if (auto cast = parse_cast(inner); cast.ok()) {
auto expr = parse_expression(inner).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)
}
};
}
auto plain_expr = parse_plain_expression(inner);
while (inner.peek().content == "(") {
inner.next();

View File

@ -43,11 +43,24 @@ namespace AST {
std::shared_ptr<types::Type> IntLiteralExpression::typecheck(
typecheck::State&,
typecheck::Scope&,
std::optional<std::shared_ptr<types::Type>>
std::optional<std::shared_ptr<types::Type>> expected_ty
) {
return std::shared_ptr<types::Type>{
new types::FundamentalType{ types::FundamentalTypeKind::Int }
};
// 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;
}
std::shared_ptr<types::Type> StringLiteralExpression::typecheck(
@ -86,6 +99,8 @@ namespace AST {
auto rhs_ty = this->m_rhs->typecheck(state, scope, {});
if (this->m_binop == types::BinOp::Assignment) {
// Re-typecheck rhs to actually match lhs
auto rhs_ty = this->m_rhs->typecheck(state, scope, lhs_ty);
return lhs_ty;
}
@ -153,6 +168,25 @@ namespace AST {
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) {
auto res_ty = this->m_expr->typecheck(state, scope, scope.return_ty);
if (scope.return_ty) {

View File

@ -5,6 +5,7 @@
#include "types.h"
#include "binops.h"
#include "casting.h"
#include "errors.h"
namespace typecheck {
@ -15,6 +16,7 @@ namespace typecheck {
struct State {
std::vector<types::BinopDefinition> binops;
std::vector<types::CastDefinition> casts;
std::vector<CompileError> errors;
};
}

View File

@ -75,6 +75,10 @@ namespace types {
return {};
}
bool Type::is_signed() {
false;
}
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);
@ -124,6 +128,18 @@ namespace types {
}
}
bool FundamentalType::is_signed() {
switch (this->m_ty) {
case FundamentalTypeKind::Int:
return true;
case FundamentalTypeKind::Bool:
case FundamentalTypeKind::Char:
return false;
default:
throw std::runtime_error("Invalid type");
}
}
std::string FunctionType::formatted() {
std::stringstream out{ "" };
out << "(";

View File

@ -33,6 +33,7 @@ namespace types {
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);
virtual bool is_signed();
};
class FundamentalType : public Type {
@ -47,6 +48,7 @@ namespace types {
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;
virtual bool is_signed() override;
};

4
test.c
View File

@ -8,5 +8,7 @@ int fibonacci(int n) {
int main() {
printf("10th fibonacci number is %d!", fibonacci(10));
return 0;
char res = 0;
res = 15;
return (int)res;
}