Compare commits
6 Commits
e3e0cd9f9f
...
4baeaff705
| Author | SHA1 | Date | |
|---|---|---|---|
| 4baeaff705 | |||
| 8314fe2b61 | |||
| 1037024730 | |||
| d6b730945c | |||
| bb4d1c6045 | |||
| 9791c9c8da |
@ -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)
|
||||
|
||||
@ -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
63
src/binops.cpp
Normal 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
39
src/binops.h
Normal 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
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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;
|
||||
};
|
||||
}
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
#include <sstream>
|
||||
|
||||
#include "types.h"
|
||||
#include "binops.h"
|
||||
|
||||
namespace types {
|
||||
int operator_precedence(BinOp& op) {
|
||||
|
||||
11
src/types.h
11
src/types.h
@ -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,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user