feat(furc): introduce function call expression and call instruction
This commit is contained in:
@@ -4,6 +4,8 @@
|
|||||||
#include "furc/ast/node.hpp"
|
#include "furc/ast/node.hpp"
|
||||||
#include "furc/ast/statement.hpp"
|
#include "furc/ast/statement.hpp"
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
namespace furc {
|
namespace furc {
|
||||||
namespace ast {
|
namespace ast {
|
||||||
|
|
||||||
@@ -16,6 +18,7 @@ enum class expression_node_t {
|
|||||||
Unaryop, /**< Unary operation expression */
|
Unaryop, /**< Unary operation expression */
|
||||||
Binop, /**< Binary operation expression */
|
Binop, /**< Binary operation expression */
|
||||||
VarAssign, /**< Variable assignment expression */
|
VarAssign, /**< Variable assignment expression */
|
||||||
|
FuncCall, /**< Function call expression. */
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -350,7 +353,55 @@ private:
|
|||||||
expression_node_p m_rhs;
|
expression_node_p m_rhs;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Function call expression AST node.
|
||||||
|
*/
|
||||||
|
class function_call_expression_node final : public expression_node {
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Construct a new function call expression AST node.
|
||||||
|
*
|
||||||
|
* @param location Node location.
|
||||||
|
* @param func Left-hand-side expression.
|
||||||
|
* @param args Function arguments.
|
||||||
|
*/
|
||||||
|
function_call_expression_node(struct location location,
|
||||||
|
expression_node_p&& func,
|
||||||
|
std::vector<expression_node_p>&& args)
|
||||||
|
: expression_node(location), m_func(std::move(func)), m_args(std::move(args)) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns this node's left-hand-side expression.
|
||||||
|
*
|
||||||
|
* @return The left-hand-side expression.
|
||||||
|
*/
|
||||||
|
const expression_node_p& func() const { return m_func; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns this node's argument expressions.
|
||||||
|
*
|
||||||
|
* @return The argument expressions.
|
||||||
|
*/
|
||||||
|
const std::vector<expression_node_p>& args() const { return m_args; }
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Returns this node's expression type.
|
||||||
|
*
|
||||||
|
* @return expression_node_t::FuncCall.
|
||||||
|
*/
|
||||||
|
expression_node_t expression_type() const override { return expression_node_t::FuncCall; }
|
||||||
|
public:
|
||||||
|
void accept(visitor& visitor) const override;
|
||||||
|
|
||||||
|
std::ostream& print(std::ostream& os) const override;
|
||||||
|
protected:
|
||||||
|
bool equal(const node& rhs) const override;
|
||||||
|
private:
|
||||||
|
expression_node_p m_func;
|
||||||
|
std::vector<expression_node_p> m_args;
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace ast
|
} // namespace ast
|
||||||
} // namespace furc
|
} // namespace furc
|
||||||
|
|
||||||
#endif // FURC_AST_EXPRESSION_HPP
|
#endif // FURC_AST_EXPRESSION_HPP
|
||||||
|
|||||||
@@ -151,6 +151,14 @@ using var_assign_expression_node_p =
|
|||||||
using var_assign_expression_node_r =
|
using var_assign_expression_node_r =
|
||||||
node_r<var_assign_expression_node>; /**< Alias for var_assign_expression_node result */
|
node_r<var_assign_expression_node>; /**< Alias for var_assign_expression_node result */
|
||||||
|
|
||||||
|
class function_call_expression_node;
|
||||||
|
|
||||||
|
using function_call_expression_node_p =
|
||||||
|
node_p<function_call_expression_node>; /**< Alias for a shared pointer to function_call_expression_node. */
|
||||||
|
|
||||||
|
using function_call_expression_node_r =
|
||||||
|
node_r<function_call_expression_node>; /**< Alias for function_call_expression_node result. */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief List of statements.
|
* @brief List of statements.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -82,6 +82,14 @@ public:
|
|||||||
*/
|
*/
|
||||||
virtual void visit(const var_assign_expression_node& node) {}
|
virtual void visit(const var_assign_expression_node& node) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Visit a function_call_expression_node.
|
||||||
|
* @see function_call_expression_node
|
||||||
|
*
|
||||||
|
* @param node Node.
|
||||||
|
*/
|
||||||
|
virtual void visit(const function_call_expression_node& node) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Visit a function_declaration_node.
|
* @brief Visit a function_declaration_node.
|
||||||
* @see function_declaration_node
|
* @see function_declaration_node
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ public:
|
|||||||
furlang::ir::mod&& move_module() { return std::move(m_module); }
|
furlang::ir::mod&& move_module() { return std::move(m_module); }
|
||||||
public:
|
public:
|
||||||
void visit(const ast::function_definition_node& funcDef) override;
|
void visit(const ast::function_definition_node& funcDef) override;
|
||||||
|
void visit(const ast::function_declaration_node& funcDecl) override;
|
||||||
void visit(const ast::return_statement_node& returnStmt) override;
|
void visit(const ast::return_statement_node& returnStmt) override;
|
||||||
void visit(const ast::if_statement_node& node) override;
|
void visit(const ast::if_statement_node& node) override;
|
||||||
void visit(const ast::while_statement_node& node) override;
|
void visit(const ast::while_statement_node& node) override;
|
||||||
@@ -35,6 +36,7 @@ public:
|
|||||||
void visit(const ast::unary_op_expression_node& node) override;
|
void visit(const ast::unary_op_expression_node& node) override;
|
||||||
void visit(const ast::binary_op_expression_node& node) override;
|
void visit(const ast::binary_op_expression_node& node) override;
|
||||||
void visit(const ast::var_assign_expression_node& node) override;
|
void visit(const ast::var_assign_expression_node& node) override;
|
||||||
|
void visit(const ast::function_call_expression_node& node) override;
|
||||||
private:
|
private:
|
||||||
template <typename T, typename... Args>
|
template <typename T, typename... Args>
|
||||||
void push(Args&&... args) {
|
void push(Args&&... args) {
|
||||||
@@ -45,7 +47,7 @@ private:
|
|||||||
|
|
||||||
furlang::ir::block_index push_block(bool validate = true);
|
furlang::ir::block_index push_block(bool validate = true);
|
||||||
private:
|
private:
|
||||||
furlang::ir::mod m_module;
|
furlang::ir::mod m_module;
|
||||||
std::unique_ptr<furlang::ir::function> m_currentFunction;
|
std::unique_ptr<furlang::ir::function> m_currentFunction;
|
||||||
std::shared_ptr<furlang::ir::block> m_currentBlock;
|
std::shared_ptr<furlang::ir::block> m_currentBlock;
|
||||||
ir_register m_registerCounter = 0;
|
ir_register m_registerCounter = 0;
|
||||||
|
|||||||
@@ -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;
|
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 {
|
bool declaration_node::equal(const node& rhs) const {
|
||||||
return declaration_type() == dynamic_cast<const declaration_node&>(rhs).declaration_type();
|
return declaration_type() == dynamic_cast<const declaration_node&>(rhs).declaration_type();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -100,7 +100,15 @@ void furvm_generator::generate_instruction(furvm::mod& mod,
|
|||||||
mod.bytecode().push_back(static_cast<furvm::byte>(furvm::instruction_t::ReturnValue));
|
mod.bytecode().push_back(static_cast<furvm::byte>(furvm::instruction_t::ReturnValue));
|
||||||
}
|
}
|
||||||
} break;
|
} 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::Alloca: throw std::runtime_error("unimplemented instruction");
|
||||||
case furlang::ir::instruction_t::Phi: throw std::runtime_error("unreachable");
|
case furlang::ir::instruction_t::Phi: throw std::runtime_error("unreachable");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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) {
|
furlang::ir::block_index ir_generator::push_block(bool validate) {
|
||||||
if (validate && !m_currentFunction->blocks().empty() && !m_currentFunction->blocks().back()->has_exit()) {
|
if (validate && !m_currentFunction->blocks().empty() && !m_currentFunction->blocks().back()->has_exit()) {
|
||||||
throw std::runtime_error(
|
throw std::runtime_error(
|
||||||
|
|||||||
@@ -9,7 +9,6 @@
|
|||||||
#include "furc/front/token.hpp"
|
#include "furc/front/token.hpp"
|
||||||
|
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <iostream>
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
@@ -367,6 +366,7 @@ enum class rhsop_info_t {
|
|||||||
Unaryop,
|
Unaryop,
|
||||||
Binop,
|
Binop,
|
||||||
Assignment,
|
Assignment,
|
||||||
|
FuncCall,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct rhsop_info {
|
struct rhsop_info {
|
||||||
@@ -379,6 +379,8 @@ struct rhsop_info {
|
|||||||
ast::binop_expression_node_t assignment;
|
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) {
|
static rhsop_info create(ast::unaryop_expression_node_t type, std::uint32_t precedence) {
|
||||||
rhsop_info info{};
|
rhsop_info info{};
|
||||||
info.type = rhsop_info_t::Unaryop;
|
info.type = rhsop_info_t::Unaryop;
|
||||||
@@ -407,6 +409,14 @@ struct rhsop_info {
|
|||||||
info.assignment = compound;
|
info.assignment = compound;
|
||||||
return info;
|
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) {
|
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::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::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::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);
|
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;
|
if (current.precedence >= precedence) return lhs;
|
||||||
auto opToken = next_token();
|
auto opToken = next_token();
|
||||||
|
|
||||||
ast::expression_node_p rhs;
|
ast::expression_node_p rhs;
|
||||||
if (current.type != rhsop_info_t::Unaryop) {
|
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
|
auto expr = parse_expression_unary(current.precedence + 1); // unary prefix is always right-associative
|
||||||
if (expr.has_error()) {
|
if (expr.has_error()) {
|
||||||
return ast::expression_node_r(ast::error{ expr.error().location });
|
return ast::expression_node_r(ast::error{ expr.error().location });
|
||||||
}
|
}
|
||||||
rhs = std::move(expr.value());
|
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);
|
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(lhs),
|
||||||
std::move(rhs));
|
std::move(rhs));
|
||||||
break;
|
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;
|
return lhs;
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
#include <optional>
|
#include <optional>
|
||||||
#include <ostream>
|
#include <ostream>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
#include <string>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
@@ -627,6 +628,76 @@ protected:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Function call instruction.
|
||||||
|
*/
|
||||||
|
class call_instruction final : public instruction {
|
||||||
|
public:
|
||||||
|
template <typename NameFwd, typename ArgsFwd>
|
||||||
|
call_instruction(NameFwd&& name, operand&& dst, ArgsFwd&& args)
|
||||||
|
: m_name(std::forward<NameFwd>(name)), m_dst(std::move(dst)), m_args(std::forward<ArgsFwd>(args)) {}
|
||||||
|
|
||||||
|
~call_instruction() override = default;
|
||||||
|
|
||||||
|
call_instruction(call_instruction&&) noexcept = default;
|
||||||
|
call_instruction& operator=(call_instruction&&) noexcept = default;
|
||||||
|
call_instruction(const call_instruction&) = delete;
|
||||||
|
call_instruction& operator=(const call_instruction&) = delete;
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Returns this instruction's type.
|
||||||
|
*
|
||||||
|
* @return instruction_t::Call.
|
||||||
|
*/
|
||||||
|
instruction_t type() const override { return instruction_t::Call; }
|
||||||
|
|
||||||
|
bool has_destination() const override { return true; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns this instruction's destination register.
|
||||||
|
*
|
||||||
|
* @return The register.
|
||||||
|
*/
|
||||||
|
operand& destination() override { return m_dst; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns this instruction's destination register.
|
||||||
|
*
|
||||||
|
* @return The register.
|
||||||
|
*/
|
||||||
|
const operand& destination() const override { return m_dst; }
|
||||||
|
|
||||||
|
std::vector<const operand*> sources() const override {
|
||||||
|
std::vector<const operand*> srcs;
|
||||||
|
srcs.reserve(m_args.size());
|
||||||
|
for (const auto& op : m_args)
|
||||||
|
srcs.push_back(&op);
|
||||||
|
return srcs;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns this instruction's callee name.
|
||||||
|
*
|
||||||
|
* @return The name.
|
||||||
|
*/
|
||||||
|
const std::string& name() const { return m_name; }
|
||||||
|
private:
|
||||||
|
std::string m_name;
|
||||||
|
operand m_dst;
|
||||||
|
std::vector<operand> m_args;
|
||||||
|
protected:
|
||||||
|
std::ostream& print(std::ostream& os) const override {
|
||||||
|
os << "call " << m_name << '(';
|
||||||
|
bool first = true;
|
||||||
|
for (const auto& op : m_args) {
|
||||||
|
if (!first) os << ", ";
|
||||||
|
first = false;
|
||||||
|
os << op;
|
||||||
|
}
|
||||||
|
return os << ") = " << m_dst;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace ir
|
} // namespace ir
|
||||||
} // namespace furlang
|
} // namespace furlang
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user