Add FieldAccessExpression

This commit is contained in:
Sofia 2026-04-15 20:01:20 +03:00
parent 9ed753a238
commit a12cf52c48
5 changed files with 134 additions and 0 deletions

View File

@ -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 << "{ ";

View File

@ -245,6 +245,32 @@ namespace AST {
) override;
};
/// @brief Same as value.field
class FieldAccessExpression : public Expression {
private:
std::unique_ptr<Expression> m_expr;
std::string m_field;
public:
FieldAccessExpression(
token::Metadata meta,
std::unique_ptr<Expression> 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<types::Type> get_codegen_type(codegen::Scope& scope) override;
virtual std::shared_ptr<types::Type> typecheck(
typecheck::State& state,
typecheck::Scope& scope,
std::optional<std::shared_ptr<types::Type>> expected_ty
) override;
};
/// @brief Same as {value1, value2}
class ListInitializerExpression : public Expression {
private:
std::vector<std::unique_ptr<Expression>> m_expressions;

View File

@ -323,6 +323,72 @@ namespace AST {
}
std::shared_ptr<types::Type> 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<types::StructType*>(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<types::PointerType*>(struct_ptr.ty.get());
if (ptr_ty->m_inner->m_kind == types::TypeKind::Struct) {
auto struct_ty = dynamic_cast<types::StructType*>(ptr_ty->m_inner.get());
int idx = -1;
auto field_ty = std::shared_ptr<types::Type>{};
for (int i = 0; i < static_cast<int>(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<types::Type>{
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<types::Type> ListInitializerExpression::get_codegen_type(codegen::Scope&) {
return this->m_ty;

View File

@ -309,6 +309,39 @@ namespace AST {
};
}
std::shared_ptr<types::Type> FieldAccessExpression::typecheck(
typecheck::State& state,
typecheck::Scope& scope,
std::optional<std::shared_ptr<types::Type>>
) {
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<types::Type> {
new types::FundamentalType{ types::FundamentalTypeKind::Void }
};
}
auto struct_ty = dynamic_cast<types::StructType*>(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<types::Type> {
new types::FundamentalType{ types::FundamentalTypeKind::Void }
};
}
state.errors.push_back(CompileError("Cannot access fields of opaque struct", this->m_meta));
return std::shared_ptr<types::Type> {
new types::FundamentalType{ types::FundamentalTypeKind::Void }
};
}
std::shared_ptr<types::Type> ListInitializerExpression::typecheck(
typecheck::State& state,
typecheck::Scope& scope,

2
test.c
View File

@ -26,5 +26,7 @@ int main() {
struct Otus otus = { 5 };
printf(" first field: %d!", otus.field);
return 0;
}