feat: introduce pointerof operation

Introduce pointerof operation and do some side-quests along the way.

Refs: #34
Closes: #17
This commit is contained in:
2026-07-05 14:54:16 +02:00
parent 9690ac3617
commit 642d57622a
13 changed files with 290 additions and 141 deletions
+2
View File
@@ -37,6 +37,7 @@ std::ostream& operator<<(std::ostream& os, unaryop_expression_node_t type) {
case unaryop_expression_node_t::PostfixIncrement: return os << "++";
case unaryop_expression_node_t::PrefixDecrement:
case unaryop_expression_node_t::PostfixDecrement: return os << "--";
case unaryop_expression_node_t::Pointerof: return os << "pointerof";
}
return os;
}
@@ -54,6 +55,7 @@ std::ostream& unary_op_expression_node::print(std::ostream& os) const {
case unaryop_expression_node_t::PrefixDecrement: return os << '(' << m_type << *m_node << ')';
case unaryop_expression_node_t::PostfixIncrement:
case unaryop_expression_node_t::PostfixDecrement: return os << '(' << *m_node << m_type << ')';
case unaryop_expression_node_t::Pointerof: return os << "pointerof " << *m_node;
}
return os;
}
+42 -17
View File
@@ -2,7 +2,6 @@
#include "furlang/ir/function.hpp"
#include "furlang/ir/instruction.hpp"
#include "furvm/function.hpp"
#include "furvm/fwd.hpp"
#include <furvm/instruction.hpp>
@@ -56,21 +55,26 @@ void furvm_generator::generate_function(furvm::mod& mod, const furlang::ir::func
}
}
static inline furvm::instruction_t binary_op_type(furlang::ir::binary_op_instruction_t type) {
static inline furvm::instruction_t op_type(furlang::ir::instruction_t type) {
switch (type) {
case furlang::ir::binary_op_instruction_t::Add: return furvm::instruction_t::Add;
case furlang::ir::binary_op_instruction_t::Sub: return furvm::instruction_t::Sub;
case furlang::ir::binary_op_instruction_t::Mul: return furvm::instruction_t::Mul;
case furlang::ir::binary_op_instruction_t::Div: return furvm::instruction_t::Div;
case furlang::ir::binary_op_instruction_t::Mod: return furvm::instruction_t::Mod;
case furlang::ir::binary_op_instruction_t::Eq: return furvm::instruction_t::Equals;
case furlang::ir::binary_op_instruction_t::NotEq: return furvm::instruction_t::NotEquals;
case furlang::ir::binary_op_instruction_t::LessThan: return furvm::instruction_t::LessThan;
case furlang::ir::binary_op_instruction_t::GreaterThan: return furvm::instruction_t::GreaterThan;
case furlang::ir::binary_op_instruction_t::LessEq: return furvm::instruction_t::LessEqual;
case furlang::ir::binary_op_instruction_t::GreaterEq: return furvm::instruction_t::GreaterEqual;
// Unary
case furlang::ir::instruction_t::Pointerof: return furvm::instruction_t::Pointerof;
// Binary
case furlang::ir::instruction_t::Add: return furvm::instruction_t::Add;
case furlang::ir::instruction_t::Sub: return furvm::instruction_t::Sub;
case furlang::ir::instruction_t::Mul: return furvm::instruction_t::Mul;
case furlang::ir::instruction_t::Div: return furvm::instruction_t::Div;
case furlang::ir::instruction_t::Mod: return furvm::instruction_t::Mod;
case furlang::ir::instruction_t::Eq: return furvm::instruction_t::Equals;
case furlang::ir::instruction_t::NotEq: return furvm::instruction_t::NotEquals;
case furlang::ir::instruction_t::LessThan: return furvm::instruction_t::LessThan;
case furlang::ir::instruction_t::GreaterThan: return furvm::instruction_t::GreaterThan;
case furlang::ir::instruction_t::LessEq: return furvm::instruction_t::LessEqual;
case furlang::ir::instruction_t::GreaterEq: return furvm::instruction_t::GreaterEqual;
default: throw std::runtime_error("unreachable");
}
throw std::runtime_error("unreachable");
}
void furvm_generator::generate_instruction(furvm::mod& mod,
@@ -90,9 +94,30 @@ void furvm_generator::generate_instruction(furvm::mod& mod,
mod.bytecode().push_back((var >> 8) & 0xFF);
static_assert(sizeof(var) == 2, "sizeof(furvm::variable_t) has changed");
} break;
case furlang::ir::instruction_t::BinaryOp: {
const auto& op = dynamic_cast<const furlang::ir::binary_op_instruction&>(instr);
mod.bytecode().push_back(static_cast<furvm::byte>(binary_op_type(op.op_type())));
case furlang::ir::instruction_t::Add:
case furlang::ir::instruction_t::Sub:
case furlang::ir::instruction_t::Mul:
case furlang::ir::instruction_t::Div:
case furlang::ir::instruction_t::Mod:
case furlang::ir::instruction_t::Eq:
case furlang::ir::instruction_t::NotEq:
case furlang::ir::instruction_t::LessThan:
case furlang::ir::instruction_t::GreaterThan:
case furlang::ir::instruction_t::LessEq:
case furlang::ir::instruction_t::GreaterEq: {
mod.bytecode().push_back(static_cast<furvm::byte>(op_type(instr.type())));
if (ctx.variables.find(instr.destination().reg()) == ctx.variables.end()) {
ctx.variables[instr.destination().reg()] = ctx.variableCounter++;
}
auto var = ctx.variables[instr.destination().reg()];
mod.bytecode().push_back(static_cast<furvm::byte>(furvm::instruction_t::Store));
mod.bytecode().push_back((var >> 0) & 0xFF);
mod.bytecode().push_back((var >> 8) & 0xFF);
static_assert(sizeof(var) == 2, "sizeof(furvm::variable_t) has changed");
} break;
case furlang::ir::instruction_t::Pointerof: {
mod.bytecode().push_back(static_cast<furvm::byte>(op_type(instr.type())));
if (ctx.variables.find(instr.destination().reg()) == ctx.variables.end()) {
ctx.variables[instr.destination().reg()] = ctx.variableCounter++;
+28 -16
View File
@@ -132,23 +132,35 @@ void ir_generator::visit(const ast::var_read_expression_node& node) {
}
}
void ir_generator::visit(const ast::unary_op_expression_node& node) {
throw std::runtime_error("unimplemented");
static inline furlang::ir::instruction_t unary_op_instruction_t(ast::unaryop_expression_node_t type) {
switch (type) {
case ast::unaryop_expression_node_t::Pointerof: return furlang::ir::instruction_t::Pointerof;
default: throw std::runtime_error("unimplemented");
}
}
static inline furlang::ir::binary_op_instruction_t binary_op_instruction_t(ast::binop_expression_node_t type) {
void ir_generator::visit(const ast::unary_op_expression_node& node) {
node.get_node()->accept(*this);
ir_register src = m_registerCounter - 1;
ir_register dst = m_registerCounter++;
push<furlang::ir::unary_instruction>(unary_op_instruction_t(node.type()),
ir::operand::new_reg(src),
ir::operand::new_reg(dst));
}
static inline furlang::ir::instruction_t binary_op_instruction_t(ast::binop_expression_node_t type) {
switch (type) {
case ast::binop_expression_node_t::Add: return furlang::ir::binary_op_instruction_t::Add;
case ast::binop_expression_node_t::Sub: return furlang::ir::binary_op_instruction_t::Sub;
case ast::binop_expression_node_t::Mul: return furlang::ir::binary_op_instruction_t::Mul;
case ast::binop_expression_node_t::Div: return furlang::ir::binary_op_instruction_t::Div;
case ast::binop_expression_node_t::Mod: return furlang::ir::binary_op_instruction_t::Mod;
case ast::binop_expression_node_t::Equal: return furlang::ir::binary_op_instruction_t::Eq;
case ast::binop_expression_node_t::NotEqual: return furlang::ir::binary_op_instruction_t::NotEq;
case ast::binop_expression_node_t::LessThan: return furlang::ir::binary_op_instruction_t::LessThan;
case ast::binop_expression_node_t::GreaterThan: return furlang::ir::binary_op_instruction_t::GreaterThan;
case ast::binop_expression_node_t::LessEqual: return furlang::ir::binary_op_instruction_t::LessEq;
case ast::binop_expression_node_t::GreaterEqual: return furlang::ir::binary_op_instruction_t::GreaterEq;
case ast::binop_expression_node_t::Add: return furlang::ir::instruction_t::Add;
case ast::binop_expression_node_t::Sub: return furlang::ir::instruction_t::Sub;
case ast::binop_expression_node_t::Mul: return furlang::ir::instruction_t::Mul;
case ast::binop_expression_node_t::Div: return furlang::ir::instruction_t::Div;
case ast::binop_expression_node_t::Mod: return furlang::ir::instruction_t::Mod;
case ast::binop_expression_node_t::Equal: return furlang::ir::instruction_t::Eq;
case ast::binop_expression_node_t::NotEqual: return furlang::ir::instruction_t::NotEq;
case ast::binop_expression_node_t::LessThan: return furlang::ir::instruction_t::LessThan;
case ast::binop_expression_node_t::GreaterThan: return furlang::ir::instruction_t::GreaterThan;
case ast::binop_expression_node_t::LessEqual: return furlang::ir::instruction_t::LessEq;
case ast::binop_expression_node_t::GreaterEqual: return furlang::ir::instruction_t::GreaterEq;
case ast::binop_expression_node_t::None:
default: throw std::runtime_error("unreachable");
}
@@ -160,7 +172,7 @@ void ir_generator::visit(const ast::binary_op_expression_node& node) {
node.rhs()->accept(*this);
ir_register rhs = m_registerCounter - 1;
ir_register dst = m_registerCounter++;
push<furlang::ir::binary_op_instruction>(binary_op_instruction_t(node.type()),
push<furlang::ir::binary_instruction>(binary_op_instruction_t(node.type()),
ir::operand::new_reg(lhs),
ir::operand::new_reg(rhs),
ir::operand::new_reg(dst));
@@ -176,7 +188,7 @@ void ir_generator::visit(const ast::var_assign_expression_node& node) {
auto compound = node.compound();
if (compound != ast::binop_expression_node_t::None) {
push<ir::binary_op_instruction>(binary_op_instruction_t(compound),
push<ir::binary_instruction>(binary_op_instruction_t(compound),
ir::operand::new_reg(reg),
ir::operand::new_reg(rhs),
ir::operand::new_reg(reg));
+1
View File
@@ -104,6 +104,7 @@ token_r lexer::next_token() {
{ "native", keyword_token::Native },
{ "public", keyword_token::Public },
{ "private", keyword_token::Private },
{ "pointerof", keyword_token::Pointerof },
{ "int32", keyword_token::Int32 },
};
+26 -10
View File
@@ -9,6 +9,7 @@
#include "furc/front/token.hpp"
#include <fstream>
#include <optional>
#include <string>
#include <unordered_map>
#include <vector>
@@ -313,27 +314,42 @@ struct unaryop_info {
std::uint32_t precedence;
};
ast::expression_node_r parser::parse_expression_unary(std::uint32_t precedence) {
static inline std::optional<unaryop_info> get_unaryop_info(const token_r& token) {
static std::unordered_map<token_t, unaryop_info> s_prefixes = {
{ token_t::Plus, unaryop_info{ ast::unaryop_expression_node_t::Positive, 3 } },
{ token_t::Minus, unaryop_info{ ast::unaryop_expression_node_t::Negative, 3 } },
{ token_t::Plus, unaryop_info{ ast::unaryop_expression_node_t::Positive, 2 } },
{ token_t::Minus, unaryop_info{ ast::unaryop_expression_node_t::Negative, 2 } },
{ token_t::DPlus, unaryop_info{ ast::unaryop_expression_node_t::PrefixIncrement, 2 } },
{ token_t::DMinus, unaryop_info{ ast::unaryop_expression_node_t::PrefixDecrement, 2 } },
};
static std::unordered_map<keyword_token, unaryop_info> s_keywords = {
{ keyword_token::Pointerof, unaryop_info{ ast::unaryop_expression_node_t::Pointerof, 2 } },
};
if (token->type == token_t::Keyword) {
auto it = s_keywords.find(token->value.keyword);
if (it == s_keywords.end()) return {};
return it->second;
}
auto it = s_prefixes.find(token->type);
if (it == s_prefixes.end()) return {};
return it->second;
}
ast::expression_node_r parser::parse_expression_unary(std::uint32_t precedence) {
std::shared_ptr<ast::unary_op_expression_node> result;
while (true) {
auto it = s_prefixes.find(peek_token()->type);
if (it == s_prefixes.end()) break;
auto current = it->second;
auto opt = get_unaryop_info(peek_token());
if (!opt.has_value()) break;
unaryop_info current = opt.value();
if (current.precedence >= precedence) break;
auto token = next_token();
ast::expression_node_p expression;
auto nextIt = s_prefixes.find(peek_token()->type);
if (nextIt != s_prefixes.end()) {
auto next = nextIt->second;
opt = get_unaryop_info(peek_token());
if (opt.has_value()) {
auto next = opt.value();
auto expr = parse_expression_unary(current.precedence + 1);
if (expr.has_error()) {
return ast::expression_node_r(ast::error{ expr.error().location });
@@ -464,7 +480,7 @@ ast::expression_node_r parser::parse_expression_rhs(ast::expression_node_p&& ini
} 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);
auto expr = parse_expression_unary(16);
if (expr.has_error()) {
return ast::expression_node_r(ast::error{ expr.error().location });
}
+25 -23
View File
@@ -11,6 +11,7 @@
#include <queue>
#include <set>
#include <stack>
#include <stdexcept>
#include <unordered_map>
#include <unordered_set>
#include <utility>
@@ -427,7 +428,17 @@ static void sccp_stage(function_context& ctx) {
case furlang::ir::instruction_t::Assign: {
newLat = sccp_stage_get_lattice(latticeValues, *instr->sources().front());
} break;
case furlang::ir::instruction_t::BinaryOp: {
case furlang::ir::instruction_t::Add:
case furlang::ir::instruction_t::Sub:
case furlang::ir::instruction_t::Mul:
case furlang::ir::instruction_t::Div:
case furlang::ir::instruction_t::Mod:
case furlang::ir::instruction_t::Eq:
case furlang::ir::instruction_t::NotEq:
case furlang::ir::instruction_t::LessThan:
case furlang::ir::instruction_t::GreaterThan:
case furlang::ir::instruction_t::LessEq:
case furlang::ir::instruction_t::GreaterEq: {
lattice lhs = sccp_stage_get_lattice(latticeValues, *instr->sources()[0]);
lattice rhs = sccp_stage_get_lattice(latticeValues, *instr->sources()[1]);
@@ -435,40 +446,31 @@ static void sccp_stage(function_context& ctx) {
newLat.type = lattice::Bottom;
} else if (lhs.type == lattice::Constant && rhs.type == lattice::Constant) {
newLat.type = lattice::Constant;
switch (dynamic_cast<const furlang::ir::binary_op_instruction&>(*instr).op_type()) {
case furlang::ir::binary_op_instruction_t::Add:
newLat.constant = lhs.constant + rhs.constant;
break;
case furlang::ir::binary_op_instruction_t::Sub:
newLat.constant = lhs.constant - rhs.constant;
break;
case furlang::ir::binary_op_instruction_t::Mul:
newLat.constant = lhs.constant * rhs.constant;
break;
case furlang::ir::binary_op_instruction_t::Div:
newLat.constant = lhs.constant / rhs.constant;
break;
case furlang::ir::binary_op_instruction_t::Mod:
newLat.constant = lhs.constant % rhs.constant;
break;
case furlang::ir::binary_op_instruction_t::Eq:
switch (instr->type()) {
case furlang::ir::instruction_t::Add: newLat.constant = lhs.constant + rhs.constant; break;
case furlang::ir::instruction_t::Sub: newLat.constant = lhs.constant - rhs.constant; break;
case furlang::ir::instruction_t::Mul: newLat.constant = lhs.constant * rhs.constant; break;
case furlang::ir::instruction_t::Div: newLat.constant = lhs.constant / rhs.constant; break;
case furlang::ir::instruction_t::Mod: newLat.constant = lhs.constant % rhs.constant; break;
case furlang::ir::instruction_t::Eq:
newLat.constant = (lhs.constant == rhs.constant) ? 1 : 0;
break;
case furlang::ir::binary_op_instruction_t::NotEq:
case furlang::ir::instruction_t::NotEq:
newLat.constant = (lhs.constant != rhs.constant) ? 1 : 0;
break;
case furlang::ir::binary_op_instruction_t::LessThan:
case furlang::ir::instruction_t::LessThan:
newLat.constant = (lhs.constant < rhs.constant) ? 1 : 0;
break;
case furlang::ir::binary_op_instruction_t::GreaterThan:
case furlang::ir::instruction_t::GreaterThan:
newLat.constant = (lhs.constant > rhs.constant) ? 1 : 0;
break;
case furlang::ir::binary_op_instruction_t::LessEq:
case furlang::ir::instruction_t::LessEq:
newLat.constant = (lhs.constant <= rhs.constant) ? 1 : 0;
break;
case furlang::ir::binary_op_instruction_t::GreaterEq:
case furlang::ir::instruction_t::GreaterEq:
newLat.constant = (lhs.constant >= rhs.constant) ? 1 : 0;
break;
default: throw std::runtime_error("unreachable");
}
}
} break;
+1 -1
View File
@@ -23,7 +23,7 @@ int main(void) {
while (x < y) {
x = x + z;
}
print(x);
print(pointerof x);
}
)";
furlang::arena arena{};