diff --git a/furc/include/furc/ast/expression.hpp b/furc/include/furc/ast/expression.hpp index 617d0a8..2eef710 100644 --- a/furc/include/furc/ast/expression.hpp +++ b/furc/include/furc/ast/expression.hpp @@ -4,6 +4,8 @@ #include "furc/ast/node.hpp" #include "furc/ast/statement.hpp" +#include + namespace furc { namespace ast { @@ -16,6 +18,7 @@ enum class expression_node_t { Unaryop, /**< Unary operation expression */ Binop, /**< Binary operation expression */ VarAssign, /**< Variable assignment expression */ + FuncCall, /**< Function call expression. */ }; /** @@ -350,7 +353,55 @@ private: 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&& 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& 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 m_args; +}; + } // namespace ast } // namespace furc -#endif // FURC_AST_EXPRESSION_HPP \ No newline at end of file +#endif // FURC_AST_EXPRESSION_HPP diff --git a/furc/include/furc/ast/fwd.hpp b/furc/include/furc/ast/fwd.hpp index 7844c17..1879464 100644 --- a/furc/include/furc/ast/fwd.hpp +++ b/furc/include/furc/ast/fwd.hpp @@ -151,6 +151,14 @@ using var_assign_expression_node_p = using var_assign_expression_node_r = node_r; /**< Alias for var_assign_expression_node result */ +class function_call_expression_node; + +using function_call_expression_node_p = + node_p; /**< Alias for a shared pointer to function_call_expression_node. */ + +using function_call_expression_node_r = + node_r; /**< Alias for function_call_expression_node result. */ + /** * @brief List of statements. */ diff --git a/furc/include/furc/ast/visitor.hpp b/furc/include/furc/ast/visitor.hpp index ede00aa..dece12f 100644 --- a/furc/include/furc/ast/visitor.hpp +++ b/furc/include/furc/ast/visitor.hpp @@ -82,6 +82,14 @@ public: */ 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. * @see function_declaration_node diff --git a/furc/include/furc/front/ir_generator.hpp b/furc/include/furc/front/ir_generator.hpp index ec745cf..e882505 100644 --- a/furc/include/furc/front/ir_generator.hpp +++ b/furc/include/furc/front/ir_generator.hpp @@ -25,6 +25,7 @@ public: furlang::ir::mod&& move_module() { return std::move(m_module); } public: 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::if_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::binary_op_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: template void push(Args&&... args) { @@ -45,7 +47,7 @@ private: furlang::ir::block_index push_block(bool validate = true); private: - furlang::ir::mod m_module; + furlang::ir::mod m_module; std::unique_ptr m_currentFunction; std::shared_ptr m_currentBlock; ir_register m_registerCounter = 0; diff --git a/furc/src/ast.cpp b/furc/src/ast.cpp index 1185ab0..2b6932b 100644 --- a/furc/src/ast.cpp +++ b/furc/src/ast.cpp @@ -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(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(rhs).declaration_type(); } diff --git a/furc/src/back/furvm.cpp b/furc/src/back/furvm.cpp index 7dd73f8..67871c5 100644 --- a/furc/src/back/furvm.cpp +++ b/furc/src/back/furvm.cpp @@ -100,7 +100,15 @@ void furvm_generator::generate_instruction(furvm::mod& mod, mod.bytecode().push_back(static_cast(furvm::instruction_t::ReturnValue)); } } break; - case furlang::ir::instruction_t::Call: + case furlang::ir::instruction_t::Call: { + const auto& call = dynamic_cast(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::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"); } diff --git a/furc/src/front/ir_generator.cpp b/furc/src/front/ir_generator.cpp index a173a0d..c37cb43 100644 --- a/furc/src/front/ir_generator.cpp +++ b/furc/src/front/ir_generator.cpp @@ -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 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(dynamic_cast(*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( diff --git a/furc/src/front/parser.cpp b/furc/src/front/parser.cpp index fbba3e2..ee98695 100644 --- a/furc/src/front/parser.cpp +++ b/furc/src/front/parser.cpp @@ -9,7 +9,6 @@ #include "furc/front/token.hpp" #include -#include #include #include #include @@ -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 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(opToken->location, + std::move(lhs), + std::move(params)); + break; } } return lhs; diff --git a/furlang/include/furlang/ir/instruction.hpp b/furlang/include/furlang/ir/instruction.hpp index 4517e09..bf626a6 100644 --- a/furlang/include/furlang/ir/instruction.hpp +++ b/furlang/include/furlang/ir/instruction.hpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include @@ -627,6 +628,76 @@ protected: } }; +/** + * @brief Function call instruction. + */ +class call_instruction final : public instruction { +public: + template + call_instruction(NameFwd&& name, operand&& dst, ArgsFwd&& args) + : m_name(std::forward(name)), m_dst(std::move(dst)), m_args(std::forward(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 sources() const override { + std::vector 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 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 furlang