Compare commits
3 Commits
8702298cd2
...
8466902281
| Author | SHA1 | Date | |
|---|---|---|---|
|
8466902281
|
|||
|
7b609f87c9
|
|||
|
0bcbe1d7d1
|
@@ -26,6 +26,16 @@ private:
|
||||
std::string m_name;
|
||||
};
|
||||
|
||||
enum class declaration_access_t {
|
||||
Implicit = 0, /**< Implicit access. */
|
||||
Public, /**< Public access. */
|
||||
Private, /**< Private access. */
|
||||
};
|
||||
|
||||
static inline bool same_access(declaration_access_t lhs, declaration_access_t rhs) {
|
||||
return lhs == declaration_access_t::Implicit || lhs == rhs;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Declaration node type.
|
||||
*/
|
||||
@@ -43,9 +53,10 @@ public:
|
||||
* @brief Construct a new declaration AST node.
|
||||
*
|
||||
* @param location Node location.
|
||||
* @param access Declaration access.
|
||||
*/
|
||||
declaration_node(struct location location)
|
||||
: abstract_node(location) {}
|
||||
declaration_node(struct location location, declaration_access_t access)
|
||||
: abstract_node(location), p_access(access) {}
|
||||
public:
|
||||
/**
|
||||
* @brief Returns this node's category.
|
||||
@@ -67,8 +78,17 @@ public:
|
||||
* @return The declaration type.
|
||||
*/
|
||||
virtual declaration_node_t declaration_type() const = 0;
|
||||
|
||||
/**
|
||||
* @brief Returns the declaration's access.
|
||||
*
|
||||
* @return Access.
|
||||
*/
|
||||
declaration_access_t access() const { return p_access; }
|
||||
protected:
|
||||
bool equal(const node& rhs) const override;
|
||||
protected:
|
||||
declaration_access_t p_access;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -99,11 +119,12 @@ public:
|
||||
*/
|
||||
template <typename T, typename ParamsFwd>
|
||||
function_declaration_node(struct location location,
|
||||
declaration_access_t access,
|
||||
T&& name,
|
||||
std::optional<type>&& returnType,
|
||||
ParamsFwd&& params,
|
||||
function_declaration_node_t type = function_declaration_node_t::Normal)
|
||||
: declaration_node(location),
|
||||
: declaration_node(location, access),
|
||||
p_name(std::forward<T>(name)),
|
||||
p_returnType(std::move(returnType)),
|
||||
p_params(std::forward<ParamsFwd>(params)),
|
||||
@@ -186,11 +207,16 @@ public:
|
||||
*/
|
||||
template <typename T, typename ParamsFwd>
|
||||
function_definition_node(struct location location,
|
||||
declaration_access_t access,
|
||||
T&& name,
|
||||
std::optional<class type>&& type,
|
||||
ParamsFwd&& params,
|
||||
body&& body)
|
||||
: function_declaration_node(location, std::forward<T>(name), std::move(type), std::forward<ParamsFwd>(params)),
|
||||
: function_declaration_node(location,
|
||||
access,
|
||||
std::forward<T>(name),
|
||||
std::move(type),
|
||||
std::forward<ParamsFwd>(params)),
|
||||
m_body(std::move(body)) {}
|
||||
public:
|
||||
/**
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
#include "furc/ast/node.hpp"
|
||||
#include "furc/ast/statement.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
||||
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<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 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 =
|
||||
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.
|
||||
*/
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 <typename T, typename... Args>
|
||||
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<furlang::ir::function> m_currentFunction;
|
||||
std::shared_ptr<furlang::ir::block> m_currentBlock;
|
||||
ir_register m_registerCounter = 0;
|
||||
|
||||
@@ -147,14 +147,16 @@ static inline std::string operator+(const std::string& str, token_t type) {
|
||||
* @brief Keyword token.
|
||||
*/
|
||||
enum class keyword_token {
|
||||
None, /**< None */
|
||||
Func, /**< `func` */
|
||||
Return, /**< `return` */
|
||||
If, /**< `if` */
|
||||
Else, /**< `else` */
|
||||
While, /**< `while` */
|
||||
Import, /**< `import` */
|
||||
Native, /**< `native` */
|
||||
None, /**< None */
|
||||
Func, /**< `func` */
|
||||
Return, /**< `return` */
|
||||
If, /**< `if` */
|
||||
Else, /**< `else` */
|
||||
While, /**< `while` */
|
||||
Import, /**< `import` */
|
||||
Native, /**< `native` */
|
||||
Public, /**< `public` */
|
||||
Private, /**< `private` */
|
||||
|
||||
Int32, /**< `int32` */
|
||||
};
|
||||
@@ -169,6 +171,8 @@ static inline std::ostream& operator<<(std::ostream& os, keyword_token keyword)
|
||||
case keyword_token::While: return os << "while";
|
||||
case keyword_token::Import: return os << "import";
|
||||
case keyword_token::Native: return os << "native";
|
||||
case keyword_token::Public: return os << "public";
|
||||
case keyword_token::Private: return os << "private";
|
||||
case keyword_token::Int32: return os << "int32";
|
||||
}
|
||||
return os;
|
||||
@@ -184,6 +188,8 @@ static inline std::string operator+(const std::string& str, keyword_token keywor
|
||||
case keyword_token::While: return str + "while";
|
||||
case keyword_token::Import: return str + "import";
|
||||
case keyword_token::Native: return str + "native";
|
||||
case keyword_token::Public: return str + "public";
|
||||
case keyword_token::Private: return str + "private";
|
||||
case keyword_token::Int32: return str + "int32";
|
||||
}
|
||||
return str;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
+41
-14
@@ -1,6 +1,9 @@
|
||||
#include "furc/back/furvm.hpp"
|
||||
|
||||
#include "furlang/ir/function.hpp"
|
||||
#include "furlang/ir/instruction.hpp"
|
||||
#include "furvm/function.hpp"
|
||||
#include "furvm/fwd.hpp"
|
||||
|
||||
#include <furvm/instruction.hpp>
|
||||
#include <stdexcept>
|
||||
@@ -18,22 +21,38 @@ furvm::mod furvm_generator::generate(furlang::ir::mod& mod) {
|
||||
}
|
||||
|
||||
void furvm_generator::generate_function(furvm::mod& mod, const furlang::ir::function& function) {
|
||||
auto func = mod.emplace_function(function.name(), mod.bytecode().size());
|
||||
func.dispatch();
|
||||
switch (function.type()) {
|
||||
case furlang::ir::function_t::Normal: {
|
||||
if (function.access() == furlang::ir::function_access_t::Public)
|
||||
mod.emplace_function(function.name(), function.param_count(), mod.bytecode().size()).dispatch();
|
||||
else
|
||||
mod.emplace_function_private(function.name(), function.param_count(), mod.bytecode().size()).dispatch();
|
||||
|
||||
function_context ctx;
|
||||
for (furlang::ir::block_index idx = 0; idx < function.blocks().size(); ++idx) {
|
||||
if (auto it = ctx.incompleteJumps.find(idx); it != ctx.incompleteJumps.end()) {
|
||||
for (std::size_t offset : it->second) {
|
||||
mod.bytecode()[offset] = mod.bytecode().size() - offset - 1;
|
||||
function_context ctx;
|
||||
for (furlang::ir::block_index idx = 0; idx < function.blocks().size(); ++idx) {
|
||||
if (auto it = ctx.incompleteJumps.find(idx); it != ctx.incompleteJumps.end()) {
|
||||
for (std::size_t offset : it->second) {
|
||||
mod.bytecode()[offset] = mod.bytecode().size() - offset - 1;
|
||||
}
|
||||
ctx.incompleteJumps.erase(it);
|
||||
}
|
||||
ctx.incompleteJumps.erase(it);
|
||||
}
|
||||
|
||||
ctx.blockOffsets[idx] = mod.bytecode().size();
|
||||
for (const auto& instr : function.blocks()[idx]->instructions())
|
||||
generate_instruction(mod, ctx, *instr);
|
||||
generate_instruction(mod, ctx, *function.blocks()[idx]->exit());
|
||||
ctx.blockOffsets[idx] = mod.bytecode().size();
|
||||
for (const auto& instr : function.blocks()[idx]->instructions())
|
||||
generate_instruction(mod, ctx, *instr);
|
||||
generate_instruction(mod, ctx, *function.blocks()[idx]->exit());
|
||||
}
|
||||
} break;
|
||||
case furlang::ir::function_t::Import: {
|
||||
throw std::runtime_error("unimplemented");
|
||||
// mod.emplace_function_private(function.name(), function.param_count(), mod.bytecode().size()).dispatch();
|
||||
} break;
|
||||
case furlang::ir::function_t::Native: {
|
||||
if (function.access() == furlang::ir::function_access_t::Public)
|
||||
mod.emplace_function(function.name(), function.param_count(), function.name()).dispatch();
|
||||
else
|
||||
mod.emplace_function_private(function.name(), function.param_count(), function.name()).dispatch();
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,7 +119,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");
|
||||
}
|
||||
|
||||
@@ -4,10 +4,14 @@
|
||||
#include "furc/ast/expression.hpp" // IWYU pragma: keep
|
||||
#include "furc/ast/literal.hpp" // IWYU pragma: keep
|
||||
#include "furc/ast/statement.hpp" // IWYU pragma: keep
|
||||
#include "furlang/ir/function.hpp"
|
||||
#include "furlang/ir/instruction.hpp"
|
||||
#include "furlang/ir/operand.hpp"
|
||||
|
||||
#include <cassert>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <vector>
|
||||
|
||||
namespace furc::front {
|
||||
|
||||
@@ -16,7 +20,11 @@ namespace ir = furlang::ir;
|
||||
}
|
||||
|
||||
void ir_generator::visit(const ast::function_definition_node& funcDef) {
|
||||
m_currentFunction = std::make_unique<furlang::ir::function>(std::string(funcDef.name()));
|
||||
furlang::ir::function_access_t access = (funcDef.access() == ast::declaration_access_t::Public)
|
||||
? furlang::ir::function_access_t::Public
|
||||
: furlang::ir::function_access_t::Private;
|
||||
|
||||
m_currentFunction = std::make_unique<furlang::ir::function>(std::string(funcDef.name()), access, 0);
|
||||
|
||||
push_block();
|
||||
for (const auto& stmt : funcDef.body().statements) {
|
||||
@@ -28,6 +36,21 @@ void ir_generator::visit(const ast::function_definition_node& funcDef) {
|
||||
m_module.push(std::move(m_currentFunction));
|
||||
}
|
||||
|
||||
void ir_generator::visit(const ast::function_declaration_node& funcDecl) {
|
||||
if (funcDecl.type() == ast::function_declaration_node_t::Normal) return;
|
||||
|
||||
furlang::ir::function_t type = funcDecl.type() == ast::function_declaration_node_t::Import
|
||||
? furlang::ir::function_t::Import
|
||||
: furlang::ir::function_t::Native;
|
||||
|
||||
furlang::ir::function_access_t access = (funcDecl.access() == ast::declaration_access_t::Public)
|
||||
? furlang::ir::function_access_t::Public
|
||||
: furlang::ir::function_access_t::Private;
|
||||
|
||||
m_module.push(
|
||||
std::make_unique<furlang::ir::function>(std::string(funcDecl.name()), access, funcDecl.params().size(), type));
|
||||
}
|
||||
|
||||
void ir_generator::visit(const ast::return_statement_node& returnStmt) {
|
||||
if (returnStmt.value().has_value()) {
|
||||
returnStmt.value().value()->accept(*this);
|
||||
@@ -149,12 +172,7 @@ void ir_generator::visit(const ast::var_assign_expression_node& node) {
|
||||
assert(node.lhs()->expression_type() == ast::expression_node_t::VarRead);
|
||||
auto lhs = std::dynamic_pointer_cast<ast::var_read_expression_node>(node.lhs());
|
||||
|
||||
ir_register reg = 0;
|
||||
if (auto it = m_variables.find(lhs->get_name()); it != m_variables.end()) {
|
||||
reg = it->second;
|
||||
} else {
|
||||
m_variables[lhs->get_name()] = reg = m_registerCounter++;
|
||||
}
|
||||
ir_register reg = m_registerCounter++;
|
||||
|
||||
auto compound = node.compound();
|
||||
if (compound != ast::binop_expression_node_t::None) {
|
||||
@@ -165,6 +183,27 @@ void ir_generator::visit(const ast::var_assign_expression_node& node) {
|
||||
} else {
|
||||
push<ir::assign_instruction>(ir::operand::new_reg(rhs), ir::operand::new_reg(reg));
|
||||
}
|
||||
|
||||
if (auto it = m_variables.find(lhs->get_name()); it != m_variables.end()) {
|
||||
push<ir::assign_instruction>(ir::operand::new_reg(reg), ir::operand::new_reg(it->second));
|
||||
} else {
|
||||
m_variables[lhs->get_name()] = reg;
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
|
||||
@@ -102,6 +102,8 @@ token_r lexer::next_token() {
|
||||
{ "while", keyword_token::While },
|
||||
{ "import", keyword_token::Import },
|
||||
{ "native", keyword_token::Native },
|
||||
{ "public", keyword_token::Public },
|
||||
{ "private", keyword_token::Private },
|
||||
{ "int32", keyword_token::Int32 },
|
||||
};
|
||||
|
||||
|
||||
+72
-14
@@ -9,7 +9,6 @@
|
||||
#include "furc/front/token.hpp"
|
||||
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
@@ -54,21 +53,40 @@ ast::type_r parser::parse_type() {
|
||||
|
||||
ast::declaration_node_r parser::parse_declaration() {
|
||||
const auto& first = peek_token();
|
||||
if (first.has_error()) return ast::declaration_node_r(ast::error{ first.error().location });
|
||||
switch (first->type) {
|
||||
case token_t::Keyword: {
|
||||
token firstToken = *first;
|
||||
|
||||
ast::declaration_access_t accessOverride = ast::declaration_access_t::Implicit;
|
||||
switch ((*first)->keyword) {
|
||||
default: break;
|
||||
case keyword_token::Public:
|
||||
case keyword_token::Private: {
|
||||
if ((*first)->keyword == keyword_token::Public) accessOverride = ast::declaration_access_t::Public;
|
||||
if ((*first)->keyword == keyword_token::Private) accessOverride = ast::declaration_access_t::Private;
|
||||
auto kw = eat_token(token_t::Keyword);
|
||||
if (kw.has_error()) return ast::declaration_node_r(ast::error{ kw.error().location });
|
||||
firstToken = *kw;
|
||||
} break;
|
||||
}
|
||||
|
||||
ast::function_declaration_node_t funcDeclType{};
|
||||
|
||||
auto first = next_token();
|
||||
switch ((*first)->keyword) {
|
||||
auto kw = next_token();
|
||||
if (kw.has_error()) return ast::declaration_node_r(ast::error{ kw.error().location });
|
||||
firstToken = *kw;
|
||||
switch (firstToken->keyword) {
|
||||
case keyword_token::Import:
|
||||
case keyword_token::Native: {
|
||||
funcDeclType = ((*first)->keyword == keyword_token::Import) ? ast::function_declaration_node_t::Import
|
||||
: ast::function_declaration_node_t::Native;
|
||||
funcDeclType = (firstToken->keyword == keyword_token::Import) ? ast::function_declaration_node_t::Import
|
||||
: ast::function_declaration_node_t::Native;
|
||||
|
||||
first = eat_token(token_t::Keyword);
|
||||
if (first.has_error()) return ast::declaration_node_r(ast::error{ first.error().location });
|
||||
if (first->value.keyword != keyword_token::Func)
|
||||
return ast::declaration_node_r(ast::error{ first->location });
|
||||
auto kw = eat_token(token_t::Keyword);
|
||||
if (kw.has_error()) return ast::declaration_node_r(ast::error{ kw.error().location });
|
||||
firstToken = *kw;
|
||||
if (firstToken.value.keyword != keyword_token::Func)
|
||||
return ast::declaration_node_r(ast::error{ firstToken.location });
|
||||
}
|
||||
case keyword_token::Func: {
|
||||
auto name = eat_token(token_t::Identifier);
|
||||
@@ -106,6 +124,12 @@ ast::declaration_node_r parser::parse_declaration() {
|
||||
returnType = *type;
|
||||
}
|
||||
|
||||
auto access = (funcDeclType == ast::function_declaration_node_t::Import)
|
||||
? ast::declaration_access_t::Private
|
||||
: accessOverride;
|
||||
if (access == ast::declaration_access_t::Implicit) access = ast::declaration_access_t::Public;
|
||||
if (!ast::same_access(accessOverride, access)) return ast::declaration_node_r(ast::error{ tok->location });
|
||||
|
||||
const auto& peek = peek_token();
|
||||
if (peek.has_error()) return ast::declaration_node_r(ast::error{ peek.error().location });
|
||||
switch (peek->type) {
|
||||
@@ -114,7 +138,8 @@ ast::declaration_node_r parser::parse_declaration() {
|
||||
if (body.has_error()) return ast::declaration_node_r(ast::error{ body.error().location });
|
||||
if (funcDeclType != ast::function_declaration_node_t::Normal)
|
||||
return ast::declaration_node_r(ast::error{ body->begin });
|
||||
return m_arena->allocate_shared<ast::function_definition_node>(first->location,
|
||||
return m_arena->allocate_shared<ast::function_definition_node>(firstToken.location,
|
||||
access,
|
||||
name->value.string,
|
||||
std::move(returnType),
|
||||
std::move(params),
|
||||
@@ -122,7 +147,8 @@ ast::declaration_node_r parser::parse_declaration() {
|
||||
}
|
||||
case token_t::Semicolon: {
|
||||
m_peekBuffer.clear();
|
||||
return m_arena->allocate_shared<ast::function_declaration_node>(first->location,
|
||||
return m_arena->allocate_shared<ast::function_declaration_node>(firstToken.location,
|
||||
access,
|
||||
name->value.string,
|
||||
std::move(returnType),
|
||||
std::move(params),
|
||||
@@ -131,7 +157,7 @@ ast::declaration_node_r parser::parse_declaration() {
|
||||
default: return ast::declaration_node_r(ast::error{ tok->location });
|
||||
}
|
||||
}
|
||||
default: return ast::declaration_node_r(ast::error{ first->location });
|
||||
default: return ast::declaration_node_r(ast::error{ firstToken.location });
|
||||
}
|
||||
}
|
||||
case token_t::None:
|
||||
@@ -340,6 +366,7 @@ enum class rhsop_info_t {
|
||||
Unaryop,
|
||||
Binop,
|
||||
Assignment,
|
||||
FuncCall,
|
||||
};
|
||||
|
||||
struct rhsop_info {
|
||||
@@ -352,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;
|
||||
@@ -380,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) {
|
||||
@@ -404,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);
|
||||
@@ -415,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);
|
||||
@@ -458,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;
|
||||
|
||||
@@ -546,6 +546,10 @@ static void adce_stage(function_context& ctx) {
|
||||
if (instr->has_destination() && instr->destination().type() == furlang::ir::operand_t::Register) {
|
||||
defMap[instr->destination().reg()] = instr.get();
|
||||
}
|
||||
if (instr->type() == furlang::ir::instruction_t::Call) {
|
||||
// TODO: Check if the function has side effects
|
||||
if (alive.insert(instr.get()).second) worklist.push(instr.get());
|
||||
}
|
||||
}
|
||||
|
||||
auto* exit = block->exit().get();
|
||||
|
||||
+26
-2
@@ -8,12 +8,13 @@
|
||||
#include "furlang/arena.hpp"
|
||||
|
||||
#include <fstream>
|
||||
#include <furvm/furvm.hpp>
|
||||
#include <iostream>
|
||||
|
||||
int main(void) {
|
||||
try {
|
||||
std::string programStr = R"(
|
||||
native func print(value: int32);
|
||||
private native func print(value: int32);
|
||||
|
||||
func main() -> int32 {
|
||||
x = 0;
|
||||
@@ -22,6 +23,7 @@ int main(void) {
|
||||
while (x < y) {
|
||||
x = x + z;
|
||||
}
|
||||
print(x);
|
||||
}
|
||||
)";
|
||||
furlang::arena arena{};
|
||||
@@ -58,8 +60,30 @@ int main(void) {
|
||||
}
|
||||
}
|
||||
|
||||
auto context = std::make_shared<furvm::context>();
|
||||
auto furvmMod = context->emplace_module("main", furc::back::furvm_generator::generate(mod));
|
||||
|
||||
std::ofstream file("./a.fmod", std::ios::binary);
|
||||
furc::back::furvm_generator::generate(mod).serialize(file);
|
||||
furvmMod->serialize(file);
|
||||
file.close();
|
||||
|
||||
furvmMod->set_native_function("print", [](furvm::executor& executor) {
|
||||
furvm::thing_h thing = executor.load_thing(0);
|
||||
switch (thing->type()) {
|
||||
case furvm::thing_t::Int32: {
|
||||
std::cout << thing->int32() << '\n';
|
||||
} break;
|
||||
}
|
||||
});
|
||||
|
||||
furvm::executor_h executor = context->emplace_executor(context);
|
||||
executor->push_frame(furvmMod, *furvmMod->function_at("main"));
|
||||
|
||||
std::cout << "--- Interpreting:\n";
|
||||
|
||||
while ((executor->flags() & furvm::executor_flags::Done) != furvm::executor_flags::Done) {
|
||||
executor->step();
|
||||
}
|
||||
|
||||
return 0;
|
||||
} catch (...) {
|
||||
|
||||
@@ -3,12 +3,25 @@
|
||||
|
||||
#include "furlang/ir/block.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
namespace furlang {
|
||||
namespace ir {
|
||||
|
||||
enum class function_t : std::uint8_t {
|
||||
Normal = 0,
|
||||
Import,
|
||||
Native,
|
||||
};
|
||||
|
||||
enum class function_access_t : std::uint8_t {
|
||||
Public = 0,
|
||||
Private,
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief IR function.
|
||||
*
|
||||
@@ -22,18 +35,11 @@ public:
|
||||
/**
|
||||
* @brief Construct a new IR function.
|
||||
*
|
||||
* @param name Name to copy.
|
||||
* @param name Name to forward.
|
||||
*/
|
||||
function(const std::string& name)
|
||||
: m_name(name) {}
|
||||
|
||||
/**
|
||||
* @brief Construct a new IR function.
|
||||
*
|
||||
* @param name Name to move.
|
||||
*/
|
||||
function(std::string&& name)
|
||||
: m_name(std::move(name)) {}
|
||||
template <typename StringFwd, typename = std::enable_if_t<std::is_constructible_v<std::string, StringFwd>>>
|
||||
function(StringFwd&& name, function_access_t access, std::uint32_t paramCount, function_t type = function_t::Normal)
|
||||
: m_name(std::forward<StringFwd>(name)), m_access(access), m_paramCount(paramCount), m_type(type) {}
|
||||
public:
|
||||
/**
|
||||
* @brief Returns this function's name.
|
||||
@@ -42,6 +48,27 @@ public:
|
||||
*/
|
||||
const std::string& name() const { return m_name; }
|
||||
|
||||
/**
|
||||
* @brief Returns the function's access.
|
||||
*
|
||||
* @return The access.
|
||||
*/
|
||||
function_access_t access() const { return m_access; }
|
||||
|
||||
/**
|
||||
* @brief Returns this function's parameter count.
|
||||
*
|
||||
* @return The parameter count.
|
||||
*/
|
||||
std::uint32_t param_count() const { return m_paramCount; }
|
||||
|
||||
/**
|
||||
* @brief Returns this function's type.
|
||||
*
|
||||
* @return The type.
|
||||
*/
|
||||
function_t type() const { return m_type; }
|
||||
|
||||
/**
|
||||
* @brief Pushes and returns a new IR block.
|
||||
*
|
||||
@@ -57,10 +84,13 @@ public:
|
||||
const std::vector<value_type>& blocks() const { return m_blocks; }
|
||||
private:
|
||||
std::string m_name;
|
||||
function_access_t m_access;
|
||||
function_t m_type;
|
||||
std::uint32_t m_paramCount;
|
||||
std::vector<value_type> m_blocks;
|
||||
};
|
||||
|
||||
} // namespace ir
|
||||
} // namespace furlang
|
||||
|
||||
#endif // FURLANG_IR_FUNCTION_HPP
|
||||
#endif // FURLANG_IR_FUNCTION_HPP
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <optional>
|
||||
#include <ostream>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#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 furlang
|
||||
|
||||
|
||||
Reference in New Issue
Block a user