Compare commits

...

6 Commits

Author SHA1 Message Date
4baeaff705 Add rest of typechecking 2026-04-13 18:26:47 +03:00
8314fe2b61 Use binops for codegen as well 2026-04-13 18:24:07 +03:00
1037024730 Add binops for typechecker 2026-04-13 18:18:25 +03:00
d6b730945c Check binops 2026-04-13 18:17:33 +03:00
bb4d1c6045 Add binops 2026-04-13 18:11:54 +03:00
9791c9c8da Add binops.h and binops.cpp 2026-04-13 18:00:47 +03:00
11 changed files with 159 additions and 38 deletions

View File

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

View File

@ -7,6 +7,7 @@
#include "codegen.h"
#include "types.h"
#include "binops.h"
#include "tokens.h"
#include "typechecker.h"

63
src/binops.cpp Normal file
View File

@ -0,0 +1,63 @@
#include "binops.h"
namespace types {
std::vector<BinopDefinition> create_binops() {
std::vector<BinopDefinition> definitions{};
auto int_ty = std::shared_ptr<types::Type>{
new types::FundamentalType{ types::FundamentalTypeKind::Int } };
auto char_ty = std::shared_ptr<types::Type>{
new types::FundamentalType{ types::FundamentalTypeKind::Char } };
auto bool_ty = std::shared_ptr<types::Type>{
new types::FundamentalType{ types::FundamentalTypeKind::Bool } };
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) {
return builder.builder->CreateAdd(lhs, rhs, "add");
} });
definitions.push_back(BinopDefinition{
ty, types::BinOp::Sub, ty,
ty, [](codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs) {
return builder.builder->CreateSub(lhs, rhs, "sub");
} });
// Comparisons
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");
} });
definitions.push_back(BinopDefinition{
ty, types::BinOp::GreaterThan, ty,
bool_ty, [](codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs) {
return builder.builder->CreateICmpSGT(lhs, rhs, "icmpsgt");
} });
}
return definitions;
}
std::optional<BinopDefinition> find_binop(
std::vector<BinopDefinition>& binops,
std::shared_ptr<types::Type> lhs,
BinOp op,
std::shared_ptr<types::Type> rhs) {
for (auto& binop : binops) {
if (binop.op != op) {
continue;
}
if (!types_equal(lhs, binop.lhs) || !types_equal(rhs, binop.rhs)) {
continue;
}
return binop;
}
return {};
}
}

39
src/binops.h Normal file
View File

@ -0,0 +1,39 @@
#ifndef BINOP_H
#define BINOP_H
#include <memory>
#include "types.h"
namespace types {
enum class BinOp {
Assignment,
Add,
Sub,
LessThan,
GreaterThan,
};
int operator_precedence(BinOp& op);
std::string format_operator(BinOp& op);
struct BinopDefinition {
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::vector<BinopDefinition> create_binops();
std::optional<BinopDefinition> find_binop(
std::vector<BinopDefinition>& binops,
std::shared_ptr<types::Type> lhs,
BinOp op,
std::shared_ptr<types::Type> rhs);
}
#endif

View File

@ -11,7 +11,7 @@
namespace codegen {
Scope Scope::with_lvalue() {
return Scope{ this->values, true };
return Scope{ this->binops, this->values, true };
}
}
@ -67,27 +67,18 @@ namespace AST {
case types::BinOp::Assignment:
builder.builder->CreateStore(rhs.value, lhs.value, false);
return rhs;
case types::BinOp::Add:
return codegen::StackValue{
lhs.ty->add(builder, lhs.value, rhs.value),
lhs.ty
};
case types::BinOp::Sub:
return codegen::StackValue{
lhs.ty->sub(builder, lhs.value, rhs.value),
lhs.ty
};
case types::BinOp::LessThan:
return codegen::StackValue{
lhs.ty->lt(builder, lhs.value, rhs.value),
std::make_shared<types::FundamentalType>(types::FundamentalTypeKind::Bool),
};
case types::BinOp::GreaterThan:
return codegen::StackValue{
lhs.ty->gt(builder, lhs.value, rhs.value),
std::make_shared<types::FundamentalType>(types::FundamentalTypeKind::Bool),
};
default:
auto binop = types::find_binop(
scope.binops,
lhs.ty,
this->m_binop,
rhs.ty);
if (binop) {
return codegen::StackValue{
binop->codegen(builder, lhs.value, rhs.value),
binop->result
};
}
throw CompileError("invalid binop", this->m_meta);
}
}

View File

@ -8,6 +8,7 @@
#include "builder.h"
#include "types.h"
#include "binops.h"
#include "tokens.h"
namespace codegen {
@ -17,6 +18,8 @@ namespace codegen {
};
struct Scope {
std::vector<types::BinopDefinition>& binops;
std::map<std::string, StackValue> values;
bool is_lvalue;

View File

@ -98,6 +98,8 @@ std::optional<CompileOutput> compile(std::string_view in_filename) {
// Perform static analysis
typecheck::State typecheck_state{};
typecheck_state.binops = types::create_binops();
typecheck::Scope typecheck_scope{};
for (auto& tls : statements) {
std::cout << tls->formatted() << std::endl;
@ -114,7 +116,11 @@ std::optional<CompileOutput> compile(std::string_view in_filename) {
return {};
}
codegen::Scope cg_scope{};
codegen::Scope cg_scope{
.binops = typecheck_state.binops,
.values = {},
.is_lvalue = false,
};
// Compile parsed output
try {

View File

@ -85,9 +85,32 @@ namespace AST {
auto lhs_ty = this->m_lhs->typecheck(state, scope, {});
auto rhs_ty = this->m_rhs->typecheck(state, scope, {});
// TODO actually check binop types properly
if (this->m_binop == types::BinOp::Assignment) {
return lhs_ty;
}
return lhs_ty;
auto binop = types::find_binop(
state.binops,
lhs_ty,
this->m_binop,
rhs_ty
);
if (binop) {
return binop->result;
}
// TODO check for binops that may be implicitly castable
state.errors.push_back(CompileError(
"No suitable binop between "
+ lhs_ty->formatted() + " "
+ types::format_operator(this->m_binop) + " "
+ rhs_ty->formatted(),
this->m_meta));
return std::shared_ptr<types::Type>{
new types::FundamentalType{ types::FundamentalTypeKind::Void } };
}
std::shared_ptr<types::Type> FunctionCallExpression::typecheck(
@ -150,10 +173,12 @@ namespace AST {
}
void IfStatement::typecheck(typecheck::State& state, typecheck::Scope& scope) {
auto bool_ty_ptr = new types::FundamentalType{ types::FundamentalTypeKind::Bool };
this->m_condition->typecheck(state, scope, std::shared_ptr<types::Type>{ bool_ty_ptr });
auto bool_ty = std::shared_ptr<types::Type>{
new types::FundamentalType{ types::FundamentalTypeKind::Bool } };
auto expr_ty = this->m_condition->typecheck(state, scope, bool_ty);
// TODO check that condition really is a boolean
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);
if (this->m_else) {

View File

@ -4,6 +4,7 @@
#include <map>
#include "types.h"
#include "binops.h"
#include "errors.h"
namespace typecheck {
@ -13,6 +14,7 @@ namespace typecheck {
};
struct State {
std::vector<types::BinopDefinition> binops;
std::vector<CompileError> errors;
};
}

View File

@ -2,6 +2,7 @@
#include <sstream>
#include "types.h"
#include "binops.h"
namespace types {
int operator_precedence(BinOp& op) {

View File

@ -7,17 +7,6 @@
#include <memory>
namespace types {
enum class BinOp {
Assignment,
Add,
Sub,
LessThan,
GreaterThan,
};
int operator_precedence(BinOp& op);
std::string format_operator(BinOp& op);
enum class TypeKind {
Fundamental,
Function,