From 1103efe6fe3693f7df10c37d19d1f1c88ed870c3 Mon Sep 17 00:00:00 2001 From: Sofia Date: Tue, 28 Apr 2026 00:09:50 +0300 Subject: [PATCH] Start adding unary operators --- src/ast.cpp | 15 +++++++++++ src/ast.h | 27 +++++++++++++++++++ src/binops.cpp | 57 +++++++++++++++++++++++++++++++++++++++++ src/binops.h | 19 ++++++++++++++ src/codegen.cpp | 9 ++++++- src/codegen.h | 1 + src/main.cpp | 2 ++ src/stack_allocator.cpp | 4 +++ src/typechecker.cpp | 32 +++++++++++++++++++++++ src/typechecker.h | 1 + test.c | 5 ++++ 11 files changed, 171 insertions(+), 1 deletion(-) diff --git a/src/ast.cpp b/src/ast.cpp index 4284ded..955f0ce 100644 --- a/src/ast.cpp +++ b/src/ast.cpp @@ -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(); diff --git a/src/ast.h b/src/ast.h index cc10f50..aefaa36 100644 --- a/src/ast.h +++ b/src/ast.h @@ -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 m_expr; + types::Unary m_unary; + public: + UnaryExpression( + token::Metadata meta, + std::unique_ptr 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 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> expected_ty + ) override; + }; + class ReturnStatement : public Statement { private: diff --git a/src/binops.cpp b/src/binops.cpp index dcbf0d2..c91779d 100644 --- a/src/binops.cpp +++ b/src/binops.cpp @@ -101,4 +101,61 @@ namespace types { } return {}; } + + std::vector create_unops() { + std::vector definitions{}; + + auto int_ty = std::shared_ptr{ + new types::FundamentalType{ false, types::FundamentalTypeKind::Int } }; + auto char_ty = std::shared_ptr{ + 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 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(loaded); + } + }); + + definitions.push_back(UnopDefinition{ + ty, types::Unary::AddPrefix, ty, + [](codegen::Builder& builder, std::shared_ptr 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 find_unop( + std::vector& unops, + std::shared_ptr value, + Unary op) { + for (auto& unop : unops) { + if (unop.op != op) { + continue; + } + if (!types_equal(value, unop.value)) { + continue; + } + + return unop; + } + return {}; + } } \ No newline at end of file diff --git a/src/binops.h b/src/binops.h index 801474a..c920f06 100644 --- a/src/binops.h +++ b/src/binops.h @@ -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 rhs); + struct UnopDefinition { + std::shared_ptr value; + Unary op; + std::shared_ptr res; + llvm::Value* (*codegen)(codegen::Builder& builder, std::shared_ptr ty, llvm::Value* value); + }; + + std::vector create_unops(); + + std::optional find_unop( + std::vector& unops, + std::shared_ptr value, + Unary op); + } #endif \ No newline at end of file diff --git a/src/codegen.cpp b/src/codegen.cpp index 560eb16..a34b6cf 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -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 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) diff --git a/src/codegen.h b/src/codegen.h index 611f670..321905a 100644 --- a/src/codegen.h +++ b/src/codegen.h @@ -20,6 +20,7 @@ namespace codegen { struct Scope { std::vector& binops; + std::vector unops; std::vector& casts; TypeMap structs; diff --git a/src/main.cpp b/src/main.cpp index f9ff086..8808b27 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -110,6 +110,7 @@ std::optional 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 compile(std::string_view in_filename) { codegen::Scope cg_scope{ .binops = typecheck_state.binops, + .unops = typecheck_state.unops, .casts = typecheck_state.casts, .structs = {}, .values = {}, diff --git a/src/stack_allocator.cpp b/src/stack_allocator.cpp index 302a370..2e3b142 100644 --- a/src/stack_allocator.cpp +++ b/src/stack_allocator.cpp @@ -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); } diff --git a/src/typechecker.cpp b/src/typechecker.cpp index 2f351be..5499c15 100644 --- a/src/typechecker.cpp +++ b/src/typechecker.cpp @@ -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> 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); } diff --git a/src/typechecker.h b/src/typechecker.h index fab0cb5..9701577 100644 --- a/src/typechecker.h +++ b/src/typechecker.h @@ -17,6 +17,7 @@ namespace typecheck { struct State { std::vector binops; + std::vector unops; std::vector casts; std::vector errors; }; diff --git a/test.c b/test.c index 439473d..5e5eced 100644 --- a/test.c +++ b/test.c @@ -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; } \ No newline at end of file