feat(furc): introduce function call expression and call instruction

This commit is contained in:
2026-06-28 13:14:46 +02:00
parent 0bcbe1d7d1
commit 7b609f87c9
9 changed files with 220 additions and 6 deletions
+20
View File
@@ -108,6 +108,26 @@ bool var_assign_expression_node::equal(const node& rhsNode) const {
return expression_node::equal(rhsNode) && m_compound == rhs.m_compound && m_lhs == rhs.m_lhs && m_rhs == rhs.m_rhs;
}
void function_call_expression_node::accept(visitor& visitor) const {
visitor.visit(*this);
}
std::ostream& function_call_expression_node::print(std::ostream& os) const {
os << *m_func << '(';
bool first = true;
for (const auto& arg : m_args) {
if (!first) os << ", ";
first = false;
os << *arg;
}
return os << ')';
}
bool function_call_expression_node::equal(const node& rhsNode) const {
const auto& rhs = dynamic_cast<const function_call_expression_node&>(rhsNode);
return expression_node::equal(rhsNode) && m_func == rhs.m_func && m_args == rhs.m_args;
}
bool declaration_node::equal(const node& rhs) const {
return declaration_type() == dynamic_cast<const declaration_node&>(rhs).declaration_type();
}
+9 -1
View File
@@ -100,7 +100,15 @@ void furvm_generator::generate_instruction(furvm::mod& mod,
mod.bytecode().push_back(static_cast<furvm::byte>(furvm::instruction_t::ReturnValue));
}
} break;
case furlang::ir::instruction_t::Call:
case furlang::ir::instruction_t::Call: {
const auto& call = dynamic_cast<const furlang::ir::call_instruction&>(instr);
// TODO: Implement a queue for unknown functions
furvm::function_id func = mod.get_function_id(call.name());
mod.bytecode().push_back(static_cast<furvm::byte>(furvm::instruction_t::Call));
mod.bytecode().push_back((func >> 0) & 0xFF);
mod.bytecode().push_back((func >> 8) & 0xFF);
} break;
case furlang::ir::instruction_t::Alloca: throw std::runtime_error("unimplemented instruction");
case furlang::ir::instruction_t::Phi: throw std::runtime_error("unreachable");
}
+15
View File
@@ -167,6 +167,21 @@ void ir_generator::visit(const ast::var_assign_expression_node& node) {
}
}
void ir_generator::visit(const ast::function_call_expression_node& node) {
std::vector<ir::operand> args;
args.reserve(node.args().size());
for (const auto& arg : node.args()) {
arg->accept(*this);
args.push_back(ir::operand::new_reg(m_registerCounter - 1));
}
if (node.func()->expression_type() != ast::expression_node_t::VarRead)
throw std::runtime_error("invalid function call left-hand-side expression");
push<ir::call_instruction>(dynamic_cast<const ast::var_read_expression_node&>(*node.func()).get_name(),
ir::operand::new_reg(m_registerCounter++),
std::move(args));
}
furlang::ir::block_index ir_generator::push_block(bool validate) {
if (validate && !m_currentFunction->blocks().empty() && !m_currentFunction->blocks().back()->has_exit()) {
throw std::runtime_error(
+34 -3
View File
@@ -9,7 +9,6 @@
#include "furc/front/token.hpp"
#include <fstream>
#include <iostream>
#include <string>
#include <unordered_map>
#include <vector>
@@ -367,6 +366,7 @@ enum class rhsop_info_t {
Unaryop,
Binop,
Assignment,
FuncCall,
};
struct rhsop_info {
@@ -379,6 +379,8 @@ struct rhsop_info {
ast::binop_expression_node_t assignment;
};
bool has_rhs() const { return type == rhsop_info_t::Binop || type == rhsop_info_t::Assignment; }
static rhsop_info create(ast::unaryop_expression_node_t type, std::uint32_t precedence) {
rhsop_info info{};
info.type = rhsop_info_t::Unaryop;
@@ -407,6 +409,14 @@ struct rhsop_info {
info.assignment = compound;
return info;
}
static rhsop_info create_function_call() {
rhsop_info info{};
info.type = rhsop_info_t::FuncCall;
info.precedence = 1;
info.associativity = associativity::Left;
return info;
}
};
ast::expression_node_r parser::parse_expression_rhs(ast::expression_node_p&& init, std::uint32_t precedence) {
@@ -431,6 +441,7 @@ ast::expression_node_r parser::parse_expression_rhs(ast::expression_node_p&& ini
{ token_t::GreaterThan, rhsop_info::create(ast::binop_expression_node_t::GreaterThan, 9, associativity::Left) },
{ token_t::LessEq, rhsop_info::create(ast::binop_expression_node_t::LessEqual, 9, associativity::Left) },
{ token_t::GreaterEq, rhsop_info::create(ast::binop_expression_node_t::GreaterEqual, 9, associativity::Left) },
{ token_t::LParen, rhsop_info::create_function_call() },
};
ast::expression_node_p lhs = std::move(init);
@@ -442,13 +453,28 @@ ast::expression_node_r parser::parse_expression_rhs(ast::expression_node_p&& ini
if (current.precedence >= precedence) return lhs;
auto opToken = next_token();
ast::expression_node_p rhs;
if (current.type != rhsop_info_t::Unaryop) {
ast::expression_node_p rhs;
std::vector<ast::expression_node_p> params;
if (current.has_rhs()) {
auto expr = parse_expression_unary(current.precedence + 1); // unary prefix is always right-associative
if (expr.has_error()) {
return ast::expression_node_r(ast::error{ expr.error().location });
}
rhs = std::move(expr.value());
} else if (current.type == rhsop_info_t::FuncCall && peek_token().has_value()) {
if (peek_token()->type != token_t::RParen) {
while (true) {
auto expr = parse_expression_unary(current.precedence + 1);
if (expr.has_error()) {
return ast::expression_node_r(ast::error{ expr.error().location });
}
params.emplace_back(std::move(expr.value()));
if (eat_token(token_t::Comma).has_error()) break;
}
}
auto enclosing = eat_token(token_t::RParen);
if (enclosing.has_error()) return ast::expression_node_r(ast::error{ enclosing.error().location });
}
auto nextIt = s_rhsops.find(peek_token()->type);
@@ -485,6 +511,11 @@ ast::expression_node_r parser::parse_expression_rhs(ast::expression_node_p&& ini
std::move(lhs),
std::move(rhs));
break;
case rhsop_info_t::FuncCall:
lhs = m_arena->allocate_shared<ast::function_call_expression_node>(opToken->location,
std::move(lhs),
std::move(params));
break;
}
}
return lhs;