Compare commits
No commits in common. "4baeaff70514edfee0209952bf58d8d8233b471b" and "e3e0cd9f9f8e735cb69f521eaf5e3e40601f9fec" have entirely different histories.
4baeaff705
...
e3e0cd9f9f
@ -23,7 +23,6 @@ add_executable(${PROJECT_NAME}
|
|||||||
src/codegen.cpp
|
src/codegen.cpp
|
||||||
src/types.cpp
|
src/types.cpp
|
||||||
src/typechecker.cpp
|
src/typechecker.cpp
|
||||||
src/binops.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)
|
||||||
|
|||||||
@ -7,7 +7,6 @@
|
|||||||
|
|
||||||
#include "codegen.h"
|
#include "codegen.h"
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
#include "binops.h"
|
|
||||||
#include "tokens.h"
|
#include "tokens.h"
|
||||||
#include "typechecker.h"
|
#include "typechecker.h"
|
||||||
|
|
||||||
|
|||||||
@ -1,63 +0,0 @@
|
|||||||
|
|
||||||
#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
39
src/binops.h
@ -1,39 +0,0 @@
|
|||||||
|
|
||||||
#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 {
|
namespace codegen {
|
||||||
Scope Scope::with_lvalue() {
|
Scope Scope::with_lvalue() {
|
||||||
return Scope{ this->binops, this->values, true };
|
return Scope{ this->values, true };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -67,18 +67,27 @@ namespace AST {
|
|||||||
case types::BinOp::Assignment:
|
case types::BinOp::Assignment:
|
||||||
builder.builder->CreateStore(rhs.value, lhs.value, false);
|
builder.builder->CreateStore(rhs.value, lhs.value, false);
|
||||||
return rhs;
|
return rhs;
|
||||||
default:
|
case types::BinOp::Add:
|
||||||
auto binop = types::find_binop(
|
|
||||||
scope.binops,
|
|
||||||
lhs.ty,
|
|
||||||
this->m_binop,
|
|
||||||
rhs.ty);
|
|
||||||
if (binop) {
|
|
||||||
return codegen::StackValue{
|
return codegen::StackValue{
|
||||||
binop->codegen(builder, lhs.value, rhs.value),
|
lhs.ty->add(builder, lhs.value, rhs.value),
|
||||||
binop->result
|
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:
|
||||||
throw CompileError("invalid binop", this->m_meta);
|
throw CompileError("invalid binop", this->m_meta);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,7 +8,6 @@
|
|||||||
|
|
||||||
#include "builder.h"
|
#include "builder.h"
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
#include "binops.h"
|
|
||||||
#include "tokens.h"
|
#include "tokens.h"
|
||||||
|
|
||||||
namespace codegen {
|
namespace codegen {
|
||||||
@ -18,8 +17,6 @@ namespace codegen {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct Scope {
|
struct Scope {
|
||||||
std::vector<types::BinopDefinition>& binops;
|
|
||||||
|
|
||||||
std::map<std::string, StackValue> values;
|
std::map<std::string, StackValue> values;
|
||||||
bool is_lvalue;
|
bool is_lvalue;
|
||||||
|
|
||||||
|
|||||||
@ -98,8 +98,6 @@ std::optional<CompileOutput> compile(std::string_view in_filename) {
|
|||||||
// Perform static analysis
|
// Perform static analysis
|
||||||
|
|
||||||
typecheck::State typecheck_state{};
|
typecheck::State typecheck_state{};
|
||||||
typecheck_state.binops = types::create_binops();
|
|
||||||
|
|
||||||
typecheck::Scope typecheck_scope{};
|
typecheck::Scope typecheck_scope{};
|
||||||
for (auto& tls : statements) {
|
for (auto& tls : statements) {
|
||||||
std::cout << tls->formatted() << std::endl;
|
std::cout << tls->formatted() << std::endl;
|
||||||
@ -116,11 +114,7 @@ std::optional<CompileOutput> compile(std::string_view in_filename) {
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
codegen::Scope cg_scope{
|
codegen::Scope cg_scope{};
|
||||||
.binops = typecheck_state.binops,
|
|
||||||
.values = {},
|
|
||||||
.is_lvalue = false,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Compile parsed output
|
// Compile parsed output
|
||||||
try {
|
try {
|
||||||
|
|||||||
@ -85,34 +85,11 @@ namespace AST {
|
|||||||
auto lhs_ty = this->m_lhs->typecheck(state, scope, {});
|
auto lhs_ty = this->m_lhs->typecheck(state, scope, {});
|
||||||
auto rhs_ty = this->m_rhs->typecheck(state, scope, {});
|
auto rhs_ty = this->m_rhs->typecheck(state, scope, {});
|
||||||
|
|
||||||
if (this->m_binop == types::BinOp::Assignment) {
|
// TODO actually check binop types properly
|
||||||
|
|
||||||
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(
|
std::shared_ptr<types::Type> FunctionCallExpression::typecheck(
|
||||||
typecheck::State& state,
|
typecheck::State& state,
|
||||||
typecheck::Scope& scope,
|
typecheck::Scope& scope,
|
||||||
@ -173,12 +150,10 @@ namespace AST {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void IfStatement::typecheck(typecheck::State& state, typecheck::Scope& scope) {
|
void IfStatement::typecheck(typecheck::State& state, typecheck::Scope& scope) {
|
||||||
auto bool_ty = std::shared_ptr<types::Type>{
|
auto bool_ty_ptr = new types::FundamentalType{ types::FundamentalTypeKind::Bool };
|
||||||
new types::FundamentalType{ types::FundamentalTypeKind::Bool } };
|
this->m_condition->typecheck(state, scope, std::shared_ptr<types::Type>{ bool_ty_ptr });
|
||||||
auto expr_ty = this->m_condition->typecheck(state, scope, bool_ty);
|
|
||||||
|
|
||||||
auto check_res = check_type(expr_ty, bool_ty);
|
// TODO check that condition really is a boolean
|
||||||
this->m_condition = handle_res(std::move(this->m_condition), check_res, state);
|
|
||||||
|
|
||||||
this->m_then->typecheck(state, scope);
|
this->m_then->typecheck(state, scope);
|
||||||
if (this->m_else) {
|
if (this->m_else) {
|
||||||
|
|||||||
@ -4,7 +4,6 @@
|
|||||||
#include <map>
|
#include <map>
|
||||||
|
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
#include "binops.h"
|
|
||||||
#include "errors.h"
|
#include "errors.h"
|
||||||
|
|
||||||
namespace typecheck {
|
namespace typecheck {
|
||||||
@ -14,7 +13,6 @@ namespace typecheck {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct State {
|
struct State {
|
||||||
std::vector<types::BinopDefinition> binops;
|
|
||||||
std::vector<CompileError> errors;
|
std::vector<CompileError> errors;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,7 +2,6 @@
|
|||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
#include "binops.h"
|
|
||||||
|
|
||||||
namespace types {
|
namespace types {
|
||||||
int operator_precedence(BinOp& op) {
|
int operator_precedence(BinOp& op) {
|
||||||
|
|||||||
11
src/types.h
11
src/types.h
@ -7,6 +7,17 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
namespace types {
|
namespace types {
|
||||||
|
enum class BinOp {
|
||||||
|
Assignment,
|
||||||
|
Add,
|
||||||
|
Sub,
|
||||||
|
LessThan,
|
||||||
|
GreaterThan,
|
||||||
|
};
|
||||||
|
|
||||||
|
int operator_precedence(BinOp& op);
|
||||||
|
std::string format_operator(BinOp& op);
|
||||||
|
|
||||||
enum class TypeKind {
|
enum class TypeKind {
|
||||||
Fundamental,
|
Fundamental,
|
||||||
Function,
|
Function,
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user