Compare commits

..

5 Commits

Author SHA1 Message Date
941e19d4dd Codegen for-loops 2026-04-28 01:45:29 +03:00
6247bc91f6 Add for-loop parsing 2026-04-28 01:35:39 +03:00
52e43ea1e2 Move ; parsing upper down the line 2026-04-28 01:34:52 +03:00
2d004cc5d8 Make for-loop parts optional 2026-04-28 01:19:11 +03:00
8d826326fa Add for-loop AST 2026-04-28 01:15:57 +03:00
9 changed files with 184 additions and 15 deletions

View File

@ -137,6 +137,23 @@ namespace AST {
return out.str();
}
std::string ForStatement::formatted() {
std::stringstream out{ "" };
out << "for (";
if (this->m_init)
out << (*this->m_init)->formatted();
out << ";";
if (this->m_cond)
out << (*this->m_cond)->formatted();
out << ";";
if (this->m_after)
out << (*this->m_after)->formatted();
out << ")";
out << "\n then " << this->m_loop->formatted();
return out.str();
}
std::string ReturnStatement::formatted() {
std::stringstream out{ "" };
out << "return ";

View File

@ -427,6 +427,32 @@ namespace AST {
virtual void typecheck(typecheck::State& state, typecheck::Scope& scope) override;
};
class ForStatement : public Statement {
private:
std::optional<std::unique_ptr<Statement>> m_init;
std::optional<std::unique_ptr<Expression>> m_cond;
std::optional<std::unique_ptr<Expression>> m_after;
std::unique_ptr<Statement> m_loop;
public:
ForStatement(token::Metadata meta,
std::optional<std::unique_ptr<Statement>> init,
std::optional<std::unique_ptr<Expression>> cond,
std::optional<std::unique_ptr<Expression>> after,
std::unique_ptr<Statement> loop)
: Statement{ meta }
, m_init{ std::move(init) }
, m_cond{ std::move(cond) }
, m_after{ std::move(after) }
, m_loop{ std::move(loop) } {
}
virtual ~ForStatement() override = default;
virtual std::string formatted() override;
virtual void codegen(codegen::Builder& builder, codegen::Scope& scope, codegen::StackAllocator& allocator) override;
virtual void codegen_alloca(codegen::StackAllocator& allocator) override;
virtual void typecheck_preprocess(typecheck::Scope& scope) override;
virtual void typecheck(typecheck::State& state, typecheck::Scope& scope) override;
};
class TopLevelStatement : public Node {
public:
TopLevelStatement(token::Metadata meta) : Node{ meta } {}

View File

@ -557,6 +557,45 @@ namespace AST {
builder.builder->SetInsertPoint(after_block);
}
void ForStatement::codegen(codegen::Builder& builder, codegen::Scope& scope, codegen::StackAllocator& allocator) {
if (!builder.block)
return;
builder.builder->SetInsertPoint(builder.block);
if (this->m_init)
(*this->m_init)->codegen(builder, scope, allocator);
auto function = builder.block->getParent();
auto cond_bb = llvm::BasicBlock::Create(*builder.context, "for-cond", function);
auto inner_bb = llvm::BasicBlock::Create(*builder.context, "for-inner", function);
auto after_bb = llvm::BasicBlock::Create(*builder.context, "for-after", function);
builder.builder->CreateBr(cond_bb);
// Loop conditional
builder.builder->SetInsertPoint(cond_bb);
if (this->m_cond) {
auto cond_res = (*this->m_cond)->codegen(builder, scope, allocator);
builder.builder->CreateCondBr(cond_res.value, inner_bb, after_bb);
}
else {
builder.builder->CreateBr(inner_bb);
}
// Loop inner block
builder.block = inner_bb;
builder.builder->SetInsertPoint(inner_bb);
this->m_loop->codegen(builder, scope, allocator);
if (this->m_after)
(*this->m_after)->codegen(builder, scope, allocator);
builder.builder->CreateBr(cond_bb);
// Loop after
builder.block = after_bb;
builder.builder->SetInsertPoint(after_bb);
}
void Function::codegen(codegen::Builder& builder, codegen::Scope& scope) {
std::shared_ptr<types::Type> ret_ty_ptr{ this->m_return_ty };

View File

@ -510,8 +510,6 @@ namespace parsing {
expr = parse_expression(inner, scope).unwrap();
}
inner.expect(token::Type::Symbol, ";");
stream.m_position = inner.m_position;
auto init = new AST::InitializationStatement{ before_meta + inner.metadata(), std::move(ty), name.content, std::move(expr) };
return std::unique_ptr<AST::InitializationStatement>{ init };
@ -521,19 +519,18 @@ namespace parsing {
}
}
Result<std::unique_ptr<AST::Statement>, std::string> parse_statement(token::TokenStream& stream, Scope& scope) {
Result<std::pair<std::unique_ptr<AST::Statement>, bool>, std::string> parse_statement(token::TokenStream& stream, Scope& scope) {
token::TokenStream inner{ stream };
auto before_meta = inner.metadata();
try {
if (inner.peek().type == token::Type::ReturnKeyword) {
inner.next();
auto expression = parse_expression(inner, scope).unwrap();
inner.expect(token::Type::Symbol, ";");
stream.m_position = inner.m_position;
auto ret = new AST::ReturnStatement{ before_meta + stream.metadata(),std::move(expression) };
return std::unique_ptr<AST::Statement>{ ret };
return std::pair{ std::unique_ptr<AST::Statement>{ ret }, true };
}
else if (inner.peek().type == token::Type::IfKeyword) {
inner.next();
@ -541,11 +538,18 @@ namespace parsing {
auto expression = parse_expression(inner, scope).unwrap();
inner.expect(token::Type::Symbol, ")");
auto then_statement = parse_statement(inner, scope).unwrap();
auto then_res = parse_statement(inner, scope).unwrap();
if (then_res.second)
inner.expect(token::Type::Symbol, ";");
auto then_statement = std::move(then_res.first);
std::optional<std::unique_ptr<AST::Statement>> else_statement{};
if (inner.peek().type == token::Type::ElseKeyword) {
inner.next();
else_statement = parse_statement(inner, scope).unwrap();
auto else_res = parse_statement(inner, scope).unwrap();
if (else_res.second)
inner.expect(token::Type::Symbol, ";");
else_statement = std::move(else_res.first);
}
stream.m_position = inner.m_position;
@ -556,17 +560,52 @@ namespace parsing {
std::move(then_statement),
std::move(else_statement)
};
return std::unique_ptr<AST::Statement>{ statement };
return std::pair{ std::unique_ptr<AST::Statement>{ statement }, false };
}
else if (inner.peek().type == token::Type::ForKeyword) {
inner.next();
inner.expect(token::Type::Symbol, "(");
std::optional<std::unique_ptr<AST::Statement>> init;
std::optional<std::unique_ptr<AST::Expression>> cond;
std::optional<std::unique_ptr<AST::Expression>> after;
if (inner.peek().content != ";")
init = parse_statement(inner, scope).unwrap().first;
inner.expect(token::Type::Symbol, ";");
if (inner.peek().content != ";")
cond = parse_expression(inner, scope).unwrap();
inner.expect(token::Type::Symbol, ";");
if (inner.peek().content != ")")
after = parse_expression(inner, scope).unwrap();
inner.expect(token::Type::Symbol, ")");
auto loop_res = parse_statement(inner, scope).unwrap();
if (loop_res.second)
inner.expect(token::Type::Symbol, ";");
auto loop = std::move(loop_res.first);
stream.m_position = inner.m_position;
auto statement = new AST::ForStatement{
before_meta + stream.metadata(),
std::move(init),
std::move(cond),
std::move(after),
std::move(loop),
};
return std::pair{ std::unique_ptr<AST::Statement>{ statement }, false };
}
else if (auto init = parse_init_statement(inner, scope); init.ok()) {
stream.m_position = inner.m_position;
return std::unique_ptr<AST::Statement>{ init.unwrap() };
return std::pair{ std::unique_ptr<AST::Statement>{ init.unwrap() }, true };
}
else if (auto expr = parse_expression(inner, scope); expr.ok()) {
stream.m_position = inner.m_position;
stream.expect(token::Type::Symbol, ";");
auto expr_statement = new AST::ExpressionStatement{ before_meta + stream.metadata(), expr.unwrap() };
return std::unique_ptr<AST::Statement>{ expr_statement };
return std::pair{ std::unique_ptr<AST::Statement>{ expr_statement }, true };
}
else {
throw std::runtime_error("Expected return-keyword");
@ -639,7 +678,10 @@ namespace parsing {
auto statement = parse_statement(inner, inner_scope);
while (statement.ok()) {
statement_list.push_back(statement.unwrap());
auto statement_res = statement.unwrap();
if (statement_res.second)
inner.expect(token::Type::Symbol, ";");
statement_list.push_back(std::move(statement_res.first));
statement = parse_statement(inner, inner_scope);
}

View File

@ -93,4 +93,14 @@ namespace AST {
(*this->m_else)->codegen_alloca(allocator);
}
}
void ForStatement::codegen_alloca(codegen::StackAllocator& allocator) {
if (this->m_init)
(*this->m_init)->codegen_alloca(allocator);
if (this->m_cond)
(*this->m_cond)->codegen_alloca(allocator);
if (this->m_after)
(*this->m_after)->codegen_alloca(allocator);
this->m_loop->codegen_alloca(allocator);
}
}

View File

@ -59,6 +59,8 @@ namespace token {
return "If";
case token::Type::ElseKeyword:
return "Else";
case token::Type::ForKeyword:
return "For";
case token::Type::Whitespace:
return "Whitespace";
@ -222,6 +224,9 @@ namespace token {
else if (content == "else") {
type = token::Type::ElseKeyword;
}
else if (content == "for") {
type = token::Type::ForKeyword;
}
tokens.push_back(token::Token{ type, content, meta + content.size() });
}
else if (iswhitespace(c)) {

View File

@ -16,6 +16,7 @@ namespace token {
ReturnKeyword,
IfKeyword,
ElseKeyword,
ForKeyword,
Whitespace,

View File

@ -631,6 +631,36 @@ namespace AST {
}
}
void ForStatement::typecheck_preprocess(typecheck::Scope& scope) {
if (this->m_init)
(*this->m_init)->typecheck_preprocess(scope);
if (this->m_cond)
(*this->m_cond)->typecheck_preprocess(scope);
if (this->m_after)
(*this->m_after)->typecheck_preprocess(scope);
this->m_loop->typecheck_preprocess(scope);
}
void ForStatement::typecheck(typecheck::State& state, typecheck::Scope& scope) {
auto bool_ty = std::shared_ptr<types::Type>{
new types::FundamentalType{ false, types::FundamentalTypeKind::Bool } };
if (this->m_init)
(*this->m_init)->typecheck(state, scope);
if (this->m_cond) {
auto cond_ty = (*this->m_cond)->typecheck(state, scope, bool_ty).type;
auto check_res = check_type(state, cond_ty, bool_ty);
this->m_cond = handle_res(std::move(*this->m_cond), check_res, state);
}
if (this->m_after)
(*this->m_after)->typecheck(state, scope, {});
this->m_loop->typecheck(state, scope);
}
void Function::typecheck_preprocess(typecheck::Scope& scope) {
this->m_return_ty = refresh_type(scope, this->m_return_ty);
for (auto& param : this->m_params) {

5
test.c
View File

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