Start adding unary operators

This commit is contained in:
Sofia 2026-04-28 00:09:50 +03:00
parent d3d487159a
commit 1103efe6fe
11 changed files with 171 additions and 1 deletions

View File

@ -90,6 +90,21 @@ namespace AST {
return out.str();
}
std::string UnaryExpression::formatted() {
std::stringstream out{ "" };
switch (this->m_unary) {
case types::Unary::AddPostfix:
out << this->m_expr->formatted() << "++";
break;
case types::Unary::AddPrefix:
out << "++" << this->m_expr->formatted();
break;
default:
break;
}
return out.str();
}
std::string ExpressionStatement::formatted() {
std::stringstream out{ "" };
out << this->m_expr->formatted();

View File

@ -322,6 +322,33 @@ namespace AST {
) override;
};
/// @brief Same as [prefix]value[postfix], e.g. value++ or ++value
class UnaryExpression : public Expression {
private:
std::unique_ptr<Expression> m_expr;
types::Unary m_unary;
public:
UnaryExpression(
token::Metadata meta,
std::unique_ptr<Expression> expr,
types::Unary unary)
: Expression{ meta }
, m_expr{ std::move(expr) }
, m_unary{ unary } {
}
virtual ~UnaryExpression() override = default;
virtual std::string formatted() override;
virtual codegen::StackValue codegen(codegen::Builder& builder, codegen::Scope& scope, codegen::StackAllocator& allocator) override;
virtual void codegen_alloca(codegen::StackAllocator& allocator) override;
virtual std::shared_ptr<types::Type> get_codegen_type(codegen::Scope& scope) override;
virtual void typecheck_preprocess(typecheck::Scope& scope) override;
virtual typecheck::ExpressionType typecheck(
typecheck::State& state,
typecheck::Scope& scope,
std::optional<std::shared_ptr<types::Type>> expected_ty
) override;
};
class ReturnStatement : public Statement {
private:

View File

@ -101,4 +101,61 @@ namespace types {
}
return {};
}
std::vector<UnopDefinition> create_unops() {
std::vector<UnopDefinition> definitions{};
auto int_ty = std::shared_ptr<types::Type>{
new types::FundamentalType{ false, types::FundamentalTypeKind::Int } };
auto char_ty = std::shared_ptr<types::Type>{
new types::FundamentalType{ false, types::FundamentalTypeKind::Char } };
// Integer arithmetic binops
for (auto& ty : { int_ty, char_ty }) {
definitions.push_back(UnopDefinition{
ty, types::Unary::AddPostfix, ty,
[](codegen::Builder& builder, std::shared_ptr<Type> ty, llvm::Value* ptr) {
codegen::TypeMap structs {};
auto llvm_ty = ty->codegen(builder, structs);
auto loaded = builder.builder->CreateLoad(llvm_ty, ptr);
auto const_1 = llvm::ConstantInt::get(llvm_ty, 1);
auto result = builder.builder->CreateAdd(loaded, const_1, "add");
builder.builder->CreateStore(result, ptr);
return reinterpret_cast<llvm::Value*>(loaded);
}
});
definitions.push_back(UnopDefinition{
ty, types::Unary::AddPrefix, ty,
[](codegen::Builder& builder, std::shared_ptr<Type> ty, llvm::Value* ptr) {
codegen::TypeMap structs {};
auto llvm_ty = ty->codegen(builder, structs);
auto loaded = builder.builder->CreateLoad(llvm_ty, ptr);
auto const_1 = llvm::ConstantInt::get(llvm_ty, 1);
auto result = builder.builder->CreateAdd(loaded, const_1, "add");
builder.builder->CreateStore(result, ptr);
return result;
}
});
}
return definitions;
}
std::optional<UnopDefinition> find_unop(
std::vector<UnopDefinition>& unops,
std::shared_ptr<types::Type> value,
Unary op) {
for (auto& unop : unops) {
if (unop.op != op) {
continue;
}
if (!types_equal(value, unop.value)) {
continue;
}
return unop;
}
return {};
}
}

View File

@ -15,6 +15,11 @@ namespace types {
GreaterThan,
};
enum class Unary {
AddPostfix,
AddPrefix,
};
int operator_precedence(BinOp& op);
std::string format_operator(BinOp& op);
@ -34,6 +39,20 @@ namespace types {
BinOp op,
std::shared_ptr<types::Type> rhs);
struct UnopDefinition {
std::shared_ptr<Type> value;
Unary op;
std::shared_ptr<Type> res;
llvm::Value* (*codegen)(codegen::Builder& builder, std::shared_ptr<Type> ty, llvm::Value* value);
};
std::vector<UnopDefinition> create_unops();
std::optional<UnopDefinition> find_unop(
std::vector<UnopDefinition>& unops,
std::shared_ptr<types::Type> value,
Unary op);
}
#endif

View File

@ -11,7 +11,7 @@
namespace codegen {
Scope Scope::with_lvalue() {
return Scope{ this->binops, this->casts, this->structs, this->values, true };
return Scope{ this->binops, this->unops, this->casts, this->structs, this->values, true };
}
}
@ -449,6 +449,13 @@ namespace AST {
}
}
std::shared_ptr<types::Type> UnaryExpression::get_codegen_type(codegen::Scope& scope) {
return this->m_expr->get_codegen_type(scope);
}
codegen::StackValue UnaryExpression::codegen(codegen::Builder&, codegen::Scope&, codegen::StackAllocator&) {
throw std::runtime_error("TODO");
}
void ReturnStatement::codegen(codegen::Builder& builder, codegen::Scope& scope, codegen::StackAllocator& allocator) {
if (!builder.block)

View File

@ -20,6 +20,7 @@ namespace codegen {
struct Scope {
std::vector<types::BinopDefinition>& binops;
std::vector<types::UnopDefinition> unops;
std::vector<types::CastDefinition>& casts;
TypeMap structs;

View File

@ -110,6 +110,7 @@ std::optional<CompileOutput> compile(std::string_view in_filename) {
// Actual typechecking
typecheck::State typecheck_state{};
typecheck_state.binops = types::create_binops();
typecheck_state.unops = types::create_unops();
typecheck_state.casts = types::create_casts();
typecheck::Scope typecheck_scope{};
@ -134,6 +135,7 @@ std::optional<CompileOutput> compile(std::string_view in_filename) {
codegen::Scope cg_scope{
.binops = typecheck_state.binops,
.unops = typecheck_state.unops,
.casts = typecheck_state.casts,
.structs = {},
.values = {},

View File

@ -67,6 +67,10 @@ namespace AST {
}
}
void UnaryExpression::codegen_alloca(codegen::StackAllocator& allocator) {
this->m_expr->codegen_alloca(allocator);
}
void ReturnStatement::codegen_alloca(codegen::StackAllocator& allocator) {
this->m_expr->codegen_alloca(allocator);
}

View File

@ -537,6 +537,38 @@ namespace AST {
}
}
void UnaryExpression::typecheck_preprocess(typecheck::Scope& scope) {
this->m_expr->typecheck_preprocess(scope);
}
typecheck::ExpressionType UnaryExpression::typecheck(
typecheck::State& state,
typecheck::Scope& scope,
std::optional<std::shared_ptr<types::Type>> expected_ty
) {
auto expr_res = this->m_expr->typecheck(state, scope, expected_ty);
if (this->m_unary == types::Unary::AddPostfix || this->m_unary == types::Unary::AddPrefix) {
if (!expr_res.lvalue) {
state.errors.push_back(CompileError("Value must be a modifyable l-value", this->m_meta));
}
}
auto unop = types::find_unop(state.unops, expr_res.type, this->m_unary);
if (unop) {
auto check_res = check_type(state, expr_res.type, unop->res);
this->m_expr = handle_res(std::move(this->m_expr), check_res, state);
return {
unop->res,
false,
false,
};
}
else {
state.errors.push_back(CompileError("No suitable unary operator found", this->m_meta));
return { expr_res.type, false, false };
}
}
void ReturnStatement::typecheck_preprocess(typecheck::Scope& scope) {
this->m_expr->typecheck_preprocess(scope);
}

View File

@ -17,6 +17,7 @@ namespace typecheck {
struct State {
std::vector<types::BinopDefinition> binops;
std::vector<types::UnopDefinition> unops;
std::vector<types::CastDefinition> casts;
std::vector<CompileError> errors;
};

5
test.c
View File

@ -51,5 +51,10 @@ int main() {
twod_array[0][0] = 50;
printf("2d array: %d!\n", twod_array[0][0]);
int counter = 0;
printf("counter: %d\n", counter++);
printf("counter: %d\n", counter++);
printf("counter: %d\n", counter++);
return 0;
}