From a12cf52c48ed1bd1b5f42789276b0aa2035a148e Mon Sep 17 00:00:00 2001 From: Sofia Date: Wed, 15 Apr 2026 20:01:20 +0300 Subject: [PATCH] Add FieldAccessExpression --- src/ast.cpp | 7 +++++ src/ast.h | 26 ++++++++++++++++++ src/codegen.cpp | 66 +++++++++++++++++++++++++++++++++++++++++++++ src/typechecker.cpp | 33 +++++++++++++++++++++++ test.c | 2 ++ 5 files changed, 134 insertions(+) diff --git a/src/ast.cpp b/src/ast.cpp index 3e68664..4284ded 100644 --- a/src/ast.cpp +++ b/src/ast.cpp @@ -68,6 +68,13 @@ namespace AST { return out.str(); } + std::string FieldAccessExpression::formatted() { + std::stringstream out{ "" }; + out << this->m_expr->formatted(); + out << "." << this->m_field; + return out.str(); + } + std::string ListInitializerExpression::formatted() { std::stringstream out{ "" }; out << "{ "; diff --git a/src/ast.h b/src/ast.h index 5538827..13f0d35 100644 --- a/src/ast.h +++ b/src/ast.h @@ -245,6 +245,32 @@ namespace AST { ) override; }; + /// @brief Same as value.field + class FieldAccessExpression : public Expression { + private: + std::unique_ptr m_expr; + std::string m_field; + public: + FieldAccessExpression( + token::Metadata meta, + std::unique_ptr expr, + std::string field) + : Expression{ meta } + , m_expr{ std::move(expr) } + , m_field{ field } { + } + virtual ~FieldAccessExpression() override = default; + virtual std::string formatted() override; + virtual codegen::StackValue codegen(codegen::Builder& builder, codegen::Scope& scope) override; + virtual std::shared_ptr get_codegen_type(codegen::Scope& scope) override; + virtual std::shared_ptr typecheck( + typecheck::State& state, + typecheck::Scope& scope, + std::optional> expected_ty + ) override; + }; + + /// @brief Same as {value1, value2} class ListInitializerExpression : public Expression { private: std::vector> m_expressions; diff --git a/src/codegen.cpp b/src/codegen.cpp index 4182ee6..37b6533 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -323,6 +323,72 @@ namespace AST { } + std::shared_ptr FieldAccessExpression::get_codegen_type(codegen::Scope& scope) { + auto ty = this->m_expr->get_codegen_type(scope); + if (ty->m_kind == types::TypeKind::Struct) { + auto struct_ty = dynamic_cast(ty.get()); + if (struct_ty->m_fields) { + for (auto& field : *struct_ty->m_fields) { + if (field.first == this->m_field) + return field.second; + } + throw CompileError("Unknown field", this->m_meta); + } + throw CompileError("Cannot access a field of opaque struct", this->m_meta); + } + else { + throw CompileError("Tried accessing a non-struct", this->m_meta); + } + } + + codegen::StackValue FieldAccessExpression::codegen(codegen::Builder& builder, codegen::Scope& scope) { + auto lvalued = scope.with_lvalue(); + auto struct_ptr = this->m_expr->codegen(builder, lvalued); + if (struct_ptr.ty->m_kind == types::TypeKind::Pointer) { + auto ptr_ty = dynamic_cast(struct_ptr.ty.get()); + if (ptr_ty->m_inner->m_kind == types::TypeKind::Struct) { + auto struct_ty = dynamic_cast(ptr_ty->m_inner.get()); + + int idx = -1; + auto field_ty = std::shared_ptr{}; + + for (int i = 0; i < static_cast(struct_ty->m_fields->size()); i++) { + auto field = (*struct_ty->m_fields)[i]; + if (field.first == this->m_field) { + idx = i; + field_ty = field.second; + break; + } + } + + auto gep = builder.builder->CreateStructGEP( + ptr_ty->m_inner->codegen(builder, scope.structs), struct_ptr.value, idx); + + auto ptr_ty = std::shared_ptr{ + new types::PointerType { field_ty } + }; + + if (scope.is_lvalue) { + return codegen::StackValue{ + gep, + ptr_ty + }; + } + else { + auto loaded = ptr_ty->load(builder, gep, scope.structs); + return codegen::StackValue{ loaded.first, loaded.second }; + } + } + else { + throw CompileError("Tried field-accessing a non-struct-pointer", this->m_meta); + } + } + else { + throw CompileError("Tried field-accessing a non-pointer", this->m_meta); + } + } + + std::shared_ptr ListInitializerExpression::get_codegen_type(codegen::Scope&) { return this->m_ty; diff --git a/src/typechecker.cpp b/src/typechecker.cpp index 91630f6..c0fb498 100644 --- a/src/typechecker.cpp +++ b/src/typechecker.cpp @@ -309,6 +309,39 @@ namespace AST { }; } + std::shared_ptr FieldAccessExpression::typecheck( + typecheck::State& state, + typecheck::Scope& scope, + std::optional> + ) { + auto expr_ty = this->m_expr->typecheck(state, scope, {}); + if (expr_ty->m_kind != types::TypeKind::Struct) { + state.errors.push_back( + CompileError("Tried to access " + expr_ty->formatted() + "." + this->m_field, this->m_meta)); + return std::shared_ptr { + new types::FundamentalType{ types::FundamentalTypeKind::Void } + }; + } + + auto struct_ty = dynamic_cast(expr_ty.get()); + if (struct_ty->m_fields) { + for (auto& field : *struct_ty->m_fields) { + if (field.first == this->m_field) { + return field.second; + } + } + state.errors.push_back(CompileError("No such field", this->m_meta)); + return std::shared_ptr { + new types::FundamentalType{ types::FundamentalTypeKind::Void } + }; + } + + state.errors.push_back(CompileError("Cannot access fields of opaque struct", this->m_meta)); + return std::shared_ptr { + new types::FundamentalType{ types::FundamentalTypeKind::Void } + }; + } + std::shared_ptr ListInitializerExpression::typecheck( typecheck::State& state, typecheck::Scope& scope, diff --git a/test.c b/test.c index 2efd44c..04f99bc 100644 --- a/test.c +++ b/test.c @@ -26,5 +26,7 @@ int main() { struct Otus otus = { 5 }; + printf(" first field: %d!", otus.field); + return 0; } \ No newline at end of file