Use unique pointers in ExpressionParser

master
Marc Gilleron 2022-04-06 22:00:32 +01:00
parent 0e523f79fe
commit 090d3485e5
14 changed files with 212 additions and 250 deletions

View File

@ -188,9 +188,6 @@ bool VoxelGeneratorGraph::get_expression_variables(std::string_view code, std::v
if (result.root != nullptr) {
ExpressionParser::find_variables(*result.root, vars);
}
if (result.root != nullptr) {
memdelete(result.root);
}
return true;
} else {
return false;
@ -397,7 +394,7 @@ void VoxelGeneratorGraph::gather_indices_and_weights(Span<const WeightOutput> we
for (int rx = rmin.x; rx < rmax.x; ++rx) {
FixedArray<uint8_t, 4> weights;
FixedArray<uint8_t, 4> indices = spare_indices;
weights.fill(0);
fill(weights, uint8_t(0));
for (unsigned int oi = 0; oi < buffers_count; ++oi) {
const float weight = buffers[oi][value_index];
// TODO Optimization: weight output nodes could already multiply by 255 and clamp afterward
@ -450,7 +447,7 @@ void VoxelGeneratorGraph::gather_indices_and_weights(Span<const WeightOutput> we
FixedArray<uint8_t, 4> weights;
FixedArray<uint8_t, 4> indices;
unsigned int skipped_outputs_count = 0;
indices.fill(0);
fill(indices, uint8_t(0));
weights[0] = 255;
weights[1] = 0;
weights[2] = 0;
@ -1005,7 +1002,7 @@ VoxelGraphRuntime::CompilationResult VoxelGeneratorGraph::compile() {
{
FixedArray<bool, 16> used_indices_map;
FixedArray<uint8_t, 4> spare_indices;
used_indices_map.fill(false);
fill(used_indices_map, false);
for (unsigned int i = 0; i < r->weight_outputs.size(); ++i) {
used_indices_map[r->weight_outputs[i].layer_index] = true;
}

View File

@ -152,7 +152,7 @@ static uint32_t expand_node(ProgramGraph &graph, const ExpressionParser::Node &e
// TODO Optimization: per-function shortcuts
for (unsigned int arg_index = 0; arg_index < arg_count; ++arg_index) {
ExpressionParser::Node *arg = fn.args[arg_index];
const ExpressionParser::Node *arg = fn.args[arg_index].get();
CRASH_COND(arg == nullptr);
ERR_FAIL_COND_V(
!expand_input(graph, *arg, pg_node, arg_index, db, to_connect, expanded_node_ids, functions),
@ -183,9 +183,6 @@ static VoxelGraphRuntime::CompilationResult expand_expression_node(ProgramGraph
ExpressionParser::Result parse_result = ExpressionParser::parse(code_utf8.get_data(), functions);
if (parse_result.error.id != ExpressionParser::ERROR_NONE) {
if (parse_result.root != nullptr) {
memdelete(parse_result.root);
}
const std::string error_message_utf8 = ExpressionParser::to_string(parse_result.error);
VoxelGraphRuntime::CompilationResult result;
result.success = false;
@ -207,9 +204,6 @@ static VoxelGraphRuntime::CompilationResult expand_expression_node(ProgramGraph
const uint32_t expanded_root_node_id = expand_node(
graph, *parse_result.root, *VoxelGraphNodeDB::get_singleton(), to_connect, expanded_nodes, functions);
if (expanded_root_node_id == ProgramGraph::NULL_ID) {
if (parse_result.root != nullptr) {
memdelete(parse_result.root);
}
VoxelGraphRuntime::CompilationResult result;
result.success = false;
result.node_id = original_node_id;
@ -224,9 +218,6 @@ static VoxelGraphRuntime::CompilationResult expand_expression_node(ProgramGraph
unsigned int original_port_index;
if (!original_node->find_input_port_by_name(tc.var_name, original_port_index)) {
if (parse_result.root != nullptr) {
memdelete(parse_result.root);
}
VoxelGraphRuntime::CompilationResult result;
result.success = false;
result.node_id = original_node_id;
@ -242,10 +233,6 @@ static VoxelGraphRuntime::CompilationResult expand_expression_node(ProgramGraph
graph.remove_node(original_node_id);
if (parse_result.root != nullptr) {
memdelete(parse_result.root);
}
VoxelGraphRuntime::CompilationResult result;
result.success = true;
return result;

View File

@ -84,7 +84,8 @@ void build_voxel_mesh_as_simple_cubes(
neighbor_offset_d_lut[Vector3i::AXIS_Y] = 1;
neighbor_offset_d_lut[Vector3i::AXIS_Z] = block_size.x * block_size.y;
FixedArray<uint32_t, VoxelMesherCubes::MATERIAL_COUNT> index_offsets(0);
FixedArray<uint32_t, VoxelMesherCubes::MATERIAL_COUNT> index_offsets;
fill(index_offsets, uint32_t(0));
// For each axis
for (unsigned int za = 0; za < Vector3iUtil::AXIS_COUNT; ++za) {
@ -228,7 +229,8 @@ void build_voxel_mesh_as_greedy_cubes(
neighbor_offset_d_lut[Vector3i::AXIS_Y] = 1;
neighbor_offset_d_lut[Vector3i::AXIS_Z] = block_size.x * block_size.y;
FixedArray<uint32_t, VoxelMesherCubes::MATERIAL_COUNT> index_offsets(0);
FixedArray<uint32_t, VoxelMesherCubes::MATERIAL_COUNT> index_offsets;
fill(index_offsets, uint32_t(0));
// For each axis
for (unsigned int za = 0; za < Vector3iUtil::AXIS_COUNT; ++za) {
@ -419,7 +421,8 @@ void build_voxel_mesh_as_greedy_cubes_atlased(
neighbor_offset_d_lut[Vector3i::AXIS_Y] = 1;
neighbor_offset_d_lut[Vector3i::AXIS_Z] = block_size.x * block_size.y;
FixedArray<uint32_t, VoxelMesherCubes::MATERIAL_COUNT> index_offsets(0);
FixedArray<uint32_t, VoxelMesherCubes::MATERIAL_COUNT> index_offsets;
fill(index_offsets, uint32_t(0));
// For each axis
for (unsigned int za = 0; za < Vector3iUtil::AXIS_COUNT; ++za) {

View File

@ -191,7 +191,7 @@ CellTextureDatas<NVoxels> select_textures_4_per_voxel(const FixedArray<unsigned
const FixedArray<uint8_t, 4> weights = weights_sampler.get_weights(data_index);
FixedArray<uint8_t, MAX_TEXTURES> &weights_temp = cell_texture_weights_temp[ci];
weights_temp.fill(0);
fill(weights_temp, uint8_t(0));
for (unsigned int j = 0; j < indices.size(); ++j) {
const unsigned int ti = indices[j];
@ -451,7 +451,8 @@ void build_regular_mesh(Span<const Sdf_T> sdf_data, TextureIndicesData texture_i
const uint8_t triangle_count = regular_cell_data.geometryCounts & 0x0f;
const uint8_t vertex_count = (regular_cell_data.geometryCounts & 0xf0) >> 4;
FixedArray<int, 12> cell_vertex_indices(-1);
FixedArray<int, 12> cell_vertex_indices;
fill(cell_vertex_indices, -1);
const uint8_t cell_border_mask = get_border_mask(pos - min_pos, block_size - Vector3i(1, 1, 1));
@ -958,7 +959,8 @@ void build_transition_mesh(Span<const Sdf_T> sdf_data, TextureIndicesData textur
const bool flip_triangles = ((cell_class & 128) != 0);
const unsigned int vertex_count = cell_data.GetVertexCount();
FixedArray<int, 12> cell_vertex_indices(-1);
FixedArray<int, 12> cell_vertex_indices;
fill(cell_vertex_indices, -1);
CRASH_COND(vertex_count > cell_vertex_indices.size());
const uint8_t direction_validity_mask = (fx > min_fpos_x ? 1 : 0) | ((fy > min_fpos_y ? 1 : 0) << 1);

View File

@ -72,7 +72,7 @@ public:
std::vector<ReuseCell> &deck = _cache[i];
deck.resize(deck_area);
for (size_t j = 0; j < deck.size(); ++j) {
deck[j].vertices.fill(-1);
fill(deck[j].vertices, -1);
}
}
}
@ -82,7 +82,7 @@ public:
std::vector<ReuseTransitionCell> &row = _cache_2d[i];
row.resize(p_block_size.x);
for (size_t j = 0; j < row.size(); ++j) {
row[j].vertices.fill(-1);
fill(row[j].vertices, -1);
}
}
}

View File

@ -188,7 +188,7 @@ inline uint16_t encode_weights_to_packed_u16(uint8_t a, uint8_t b, uint8_t c, ui
// Checks if there are no duplicate indices in any voxel
inline void debug_check_texture_indices(FixedArray<uint8_t, 4> indices) {
FixedArray<bool, 16> checked;
checked.fill(false);
fill(checked, false);
for (unsigned int i = 0; i < indices.size(); ++i) {
unsigned int ti = indices[i];
CRASH_COND(checked[ti]);

View File

@ -114,7 +114,8 @@ static bool load_header(
ERR_FAIL_COND_V(f->get_position() != 0, false);
ERR_FAIL_COND_V(f->get_length() < MAGIC_AND_VERSION_SIZE, false);
FixedArray<char, 5> magic(0);
FixedArray<char, 5> magic;
fill(magic, '\0');
ERR_FAIL_COND_V(f->get_buffer(reinterpret_cast<uint8_t *>(magic.data()), 4) != 4, false);
ERR_FAIL_COND_V(strcmp(magic.data(), FORMAT_REGION_MAGIC) != 0, false);
@ -173,7 +174,7 @@ RegionFile::RegionFile() {
// Defaults
_header.format.block_size_po2 = 4;
_header.format.region_size = Vector3i(16, 16, 16);
_header.format.channel_depths.fill(VoxelBufferInternal::DEPTH_8_BIT);
fill(_header.format.channel_depths, VoxelBufferInternal::DEPTH_8_BIT);
_header.format.sector_size = 512;
}

View File

@ -47,7 +47,7 @@ VoxelStreamRegionFiles::VoxelStreamRegionFiles() {
_meta.region_size_po2 = 4;
_meta.sector_size = 512; // next_power_of_2(_meta.block_size.volume() / 10) // based on compression ratios
_meta.lod_count = 1;
_meta.channel_depths.fill(VoxelBufferInternal::DEFAULT_CHANNEL_DEPTH);
fill(_meta.channel_depths, VoxelBufferInternal::DEFAULT_CHANNEL_DEPTH);
_meta.channel_depths[VoxelBufferInternal::CHANNEL_TYPE] = VoxelBufferInternal::DEFAULT_TYPE_CHANNEL_DEPTH;
_meta.channel_depths[VoxelBufferInternal::CHANNEL_SDF] = VoxelBufferInternal::DEFAULT_SDF_CHANNEL_DEPTH;
_meta.channel_depths[VoxelBufferInternal::CHANNEL_INDICES] = VoxelBufferInternal::DEFAULT_INDICES_CHANNEL_DEPTH;

View File

@ -21,7 +21,7 @@ VoxelStreamBlockFiles::VoxelStreamBlockFiles() {
_meta.block_size_po2 = 4;
_meta.lod_count = 1;
_meta.version = FORMAT_VERSION;
_meta.channel_depths.fill(VoxelBufferInternal::DEFAULT_CHANNEL_DEPTH);
fill(_meta.channel_depths, VoxelBufferInternal::DEFAULT_CHANNEL_DEPTH);
}
// TODO Have configurable block size

View File

@ -1508,9 +1508,8 @@ void test_expression_parser() {
ZYLANN_TEST_ASSERT(result.error.id == ERROR_NONE);
ZYLANN_TEST_ASSERT(result.root != nullptr);
ZYLANN_TEST_ASSERT(result.root->type == Node::NUMBER);
const NumberNode *nn = reinterpret_cast<NumberNode *>(result.root);
ZYLANN_TEST_ASSERT(Math::is_equal_approx(nn->value, 42.f));
memdelete(result.root);
const NumberNode &nn = static_cast<NumberNode &>(*result.root);
ZYLANN_TEST_ASSERT(Math::is_equal_approx(nn.value, 42.f));
}
{
Result result = parse("()", Span<const Function>());
@ -1532,9 +1531,8 @@ void test_expression_parser() {
ZYLANN_TEST_ASSERT(result.error.id == ERROR_NONE);
ZYLANN_TEST_ASSERT(result.root != nullptr);
ZYLANN_TEST_ASSERT(result.root->type == Node::NUMBER);
const NumberNode *nn = reinterpret_cast<NumberNode *>(result.root);
ZYLANN_TEST_ASSERT(Math::is_equal_approx(nn->value, 42.f));
memdelete(result.root);
const NumberNode &nn = static_cast<NumberNode &>(*result.root);
ZYLANN_TEST_ASSERT(Math::is_equal_approx(nn.value, 42.f));
}
{
Result result = parse("(", Span<const Function>());
@ -1571,27 +1569,24 @@ void test_expression_parser() {
ZYLANN_TEST_ASSERT(result.error.id == ERROR_NONE);
ZYLANN_TEST_ASSERT(result.root != nullptr);
ZYLANN_TEST_ASSERT(result.root->type == Node::NUMBER);
const NumberNode *nn = reinterpret_cast<NumberNode *>(result.root);
ZYLANN_TEST_ASSERT(Math::is_equal_approx(nn->value, 0.6f));
memdelete(result.root);
const NumberNode &nn = static_cast<NumberNode &>(*result.root);
ZYLANN_TEST_ASSERT(Math::is_equal_approx(nn.value, 0.6f));
}
{
Result result = parse("1*2-3/4+5", Span<const Function>());
ZYLANN_TEST_ASSERT(result.error.id == ERROR_NONE);
ZYLANN_TEST_ASSERT(result.root != nullptr);
ZYLANN_TEST_ASSERT(result.root->type == Node::NUMBER);
const NumberNode *nn = reinterpret_cast<NumberNode *>(result.root);
ZYLANN_TEST_ASSERT(Math::is_equal_approx(nn->value, 6.25f));
memdelete(result.root);
const NumberNode &nn = static_cast<NumberNode &>(*result.root);
ZYLANN_TEST_ASSERT(Math::is_equal_approx(nn.value, 6.25f));
}
{
Result result = parse("(5 - 3)^2 + 2.5/(4 + 6)", Span<const Function>());
ZYLANN_TEST_ASSERT(result.error.id == ERROR_NONE);
ZYLANN_TEST_ASSERT(result.root != nullptr);
ZYLANN_TEST_ASSERT(result.root->type == Node::NUMBER);
const NumberNode *nn = reinterpret_cast<NumberNode *>(result.root);
ZYLANN_TEST_ASSERT(Math::is_equal_approx(nn->value, 4.25f));
memdelete(result.root);
const NumberNode &nn = static_cast<NumberNode &>(*result.root);
ZYLANN_TEST_ASSERT(Math::is_equal_approx(nn.value, 4.25f));
}
{
/*
@ -1607,17 +1602,22 @@ void test_expression_parser() {
/ \
a b
*/
VariableNode *node_a = memnew(VariableNode("a"));
VariableNode *node_b = memnew(VariableNode("b"));
OperatorNode *node_add = memnew(OperatorNode(OperatorNode::ADD, node_a, node_b));
NumberNode *node_two = memnew(NumberNode(2));
OperatorNode *node_power = memnew(OperatorNode(OperatorNode::POWER, node_add, node_two));
NumberNode *node_four = memnew(NumberNode(4));
OperatorNode *node_mul = memnew(OperatorNode(OperatorNode::MULTIPLY, node_four, node_power));
VariableNode *node_c = memnew(VariableNode("c"));
VariableNode *node_d = memnew(VariableNode("d"));
OperatorNode *node_sub = memnew(OperatorNode(OperatorNode::SUBTRACT, node_c, node_d));
OperatorNode *expected_root = memnew(OperatorNode(OperatorNode::SUBTRACT, node_mul, node_sub));
UniquePtr<VariableNode> node_a = make_unique_instance<VariableNode>("a");
UniquePtr<VariableNode> node_b = make_unique_instance<VariableNode>("b");
UniquePtr<OperatorNode> node_add =
make_unique_instance<OperatorNode>(OperatorNode::ADD, std::move(node_a), std::move(node_b));
UniquePtr<NumberNode> node_two = make_unique_instance<NumberNode>(2);
UniquePtr<OperatorNode> node_power =
make_unique_instance<OperatorNode>(OperatorNode::POWER, std::move(node_add), std::move(node_two));
UniquePtr<NumberNode> node_four = make_unique_instance<NumberNode>(4);
UniquePtr<OperatorNode> node_mul =
make_unique_instance<OperatorNode>(OperatorNode::MULTIPLY, std::move(node_four), std::move(node_power));
UniquePtr<VariableNode> node_c = make_unique_instance<VariableNode>("c");
UniquePtr<VariableNode> node_d = make_unique_instance<VariableNode>("d");
UniquePtr<OperatorNode> node_sub =
make_unique_instance<OperatorNode>(OperatorNode::SUBTRACT, std::move(node_c), std::move(node_d));
UniquePtr<OperatorNode> expected_root =
make_unique_instance<OperatorNode>(OperatorNode::SUBTRACT, std::move(node_mul), std::move(node_sub));
Result result = parse("4*(a+b)^2-(c-d)", Span<const Function>());
ZYLANN_TEST_ASSERT(result.error.id == ERROR_NONE);
@ -1630,8 +1630,6 @@ void test_expression_parser() {
// print_line(String(s2.c_str()));
// }
ZYLANN_TEST_ASSERT(is_tree_equal(*result.root, *expected_root, Span<const Function>()));
memdelete(result.root);
memdelete(expected_root);
}
{
FixedArray<Function, 2> functions;
@ -1661,9 +1659,8 @@ void test_expression_parser() {
ZYLANN_TEST_ASSERT(result.error.id == ERROR_NONE);
ZYLANN_TEST_ASSERT(result.root != nullptr);
ZYLANN_TEST_ASSERT(result.root->type == Node::NUMBER);
const NumberNode *nn = reinterpret_cast<NumberNode *>(result.root);
ZYLANN_TEST_ASSERT(Math::is_equal_approx(nn->value, 4.f));
memdelete(result.root);
const NumberNode &nn = static_cast<NumberNode &>(*result.root);
ZYLANN_TEST_ASSERT(Math::is_equal_approx(nn.value, 4.f));
}
{
FixedArray<Function, 2> functions;

View File

@ -7,23 +7,6 @@
namespace zylann {
namespace ExpressionParser {
OperatorNode::~OperatorNode() {
if (n0 != nullptr) {
memdelete(n0);
}
if (n1 != nullptr) {
memdelete(n1);
}
}
FunctionNode::~FunctionNode() {
for (unsigned int i = 0; i < args.size(); ++i) {
if (args[i] != nullptr) {
memdelete(args[i]);
}
}
}
struct StringView {
const char *ptr;
size_t size;
@ -277,14 +260,13 @@ int get_operator_precedence(OperatorNode::Operation op) {
struct OpEntry {
int precedence;
// TODO Use unique ptr
OperatorNode *node;
UniquePtr<OperatorNode> node;
};
template <typename T>
inline T pop(std::vector<T> &stack) {
CRASH_COND(stack.size() == 0);
T t = stack.back();
T t = std::move(stack.back());
stack.pop_back();
return t;
}
@ -303,11 +285,11 @@ unsigned int get_operator_argument_count(OperatorNode::Operation op_type) {
}
}
ErrorID pop_expression_operator(std::vector<OpEntry> &operations_stack, std::vector<Node *> &operand_stack) {
ErrorID pop_expression_operator(std::vector<OpEntry> &operations_stack, std::vector<UniquePtr<Node>> &operand_stack) {
OpEntry last_op = pop(operations_stack);
CRASH_COND(last_op.node == nullptr);
CRASH_COND(last_op.node->type != Node::OPERATOR);
OperatorNode *last_node = last_op.node;
UniquePtr<OperatorNode> last_node = std::move(last_op.node);
const unsigned int argc = get_operator_argument_count(last_node->op);
CRASH_COND(argc < 1 || argc > 2);
@ -319,14 +301,14 @@ ErrorID pop_expression_operator(std::vector<OpEntry> &operations_stack, std::vec
if (argc == 1) {
last_node->n0 = pop(operand_stack);
} else {
Node *right = pop(operand_stack);
Node *left = pop(operand_stack);
last_node->n0 = left;
last_node->n1 = right;
UniquePtr<Node> right = pop(operand_stack);
UniquePtr<Node> left = pop(operand_stack);
last_node->n0 = std::move(left);
last_node->n1 = std::move(right);
}
// Push result back to stack
operand_stack.push_back(last_node);
operand_stack.push_back(std::move(last_node));
return ERROR_NONE;
}
@ -335,17 +317,13 @@ bool is_operand(const Token &token) {
return token.type == Token::NAME || token.type == Token::NUMBER;
}
Node *operand_to_node(const Token token) {
UniquePtr<Node> operand_to_node(const Token token) {
switch (token.type) {
case Token::NUMBER: {
NumberNode *node = memnew(NumberNode(token.data.number));
return node;
}
case Token::NUMBER:
return make_unique_instance<NumberNode>(token.data.number);
case Token::NAME: {
VariableNode *node = memnew(VariableNode(unpack(token.data.str)));
return node;
}
case Token::NAME:
return make_unique_instance<VariableNode>(unpack(token.data.str));
default:
CRASH_NOW_MSG("Token not handled");
@ -366,83 +344,80 @@ const Function *find_function_by_name(std::string_view name, Span<const Function
Result parse_expression(
Tokenizer &tokenizer, bool in_argument_list, Span<const Function> functions, Token *out_last_token);
Result parse_function(Tokenizer &tokenizer, std::vector<Node *> &operand_stack, Span<const Function> functions) {
Error parse_function(
Tokenizer &tokenizer, std::vector<UniquePtr<Node>> &operand_stack, Span<const Function> functions) {
std::string_view fname;
{
// We'll replace the variable with a function call node
Node *top = operand_stack.back();
UniquePtr<Node> top = pop(operand_stack);
CRASH_COND(top->type != Node::VARIABLE);
VariableNode *node = reinterpret_cast<VariableNode *>(top);
const VariableNode *node = static_cast<VariableNode *>(top.get());
fname = node->name;
memdelete(node);
operand_stack.pop_back();
}
const Function *fn = find_function_by_name(fname, functions);
if (fn == nullptr) {
Result result;
result.error.id = ERROR_UNKNOWN_FUNCTION;
result.error.position = tokenizer.get_position();
result.error.symbol = fname;
return result;
Error error;
error.id = ERROR_UNKNOWN_FUNCTION;
error.position = tokenizer.get_position();
error.symbol = fname;
return error;
}
FunctionNode *fnode = memnew(FunctionNode);
UniquePtr<FunctionNode> fnode = make_unique_instance<FunctionNode>();
fnode->function_id = fn->id;
CRASH_COND(fn->argument_count >= fnode->args.size());
Token last_token;
for (unsigned int arg_index = 0; arg_index < fn->argument_count; ++arg_index) {
Result arg_result = parse_expression(tokenizer, true, functions, &last_token);
if (arg_result.error.id != ERROR_NONE) {
memdelete(fnode);
return arg_result;
return arg_result.error;
}
if (arg_result.root == nullptr) {
Result result;
result.error.id = ERROR_EXPECTED_ARGUMENT;
result.error.position = tokenizer.get_position();
result.error.symbol = fname;
memdelete(fnode);
return result;
Error error;
error.id = ERROR_EXPECTED_ARGUMENT;
error.position = tokenizer.get_position();
error.symbol = fname;
return error;
} else if (last_token.type == Token::PARENTHESIS_CLOSE && arg_index + 1 < fn->argument_count) {
Result result;
result.error.id = ERROR_TOO_FEW_ARGUMENTS;
result.error.position = tokenizer.get_position();
result.error.symbol = fname;
memdelete(arg_result.root);
memdelete(fnode);
return result;
Error error;
error.id = ERROR_TOO_FEW_ARGUMENTS;
error.position = tokenizer.get_position();
error.symbol = fname;
return error;
}
fnode->args[arg_index] = arg_result.root;
fnode->args[arg_index] = std::move(arg_result.root);
}
if (last_token.type != Token::PARENTHESIS_CLOSE) {
Result result;
result.error.id = ERROR_TOO_MANY_ARGUMENTS;
result.error.position = tokenizer.get_position();
result.error.symbol = fname;
memdelete(fnode);
return result;
Error error;
error.id = ERROR_TOO_MANY_ARGUMENTS;
error.position = tokenizer.get_position();
error.symbol = fname;
return error;
}
Result result;
result.root = fnode;
operand_stack.push_back(fnode);
return result;
operand_stack.push_back(std::move(fnode));
return Error();
}
void free_nodes(std::vector<OpEntry> &operations_stack, std::vector<Node *> operand_stack) {
for (unsigned int i = 0; i < operations_stack.size(); ++i) {
memdelete(operations_stack[i].node);
}
for (unsigned int i = 0; i < operand_stack.size(); ++i) {
memdelete(operand_stack[i]);
}
}
// void free_nodes(std::vector<OpEntry> &operations_stack, std::vector<UniquePtr<Node>> operand_stack) {
// operand_stack.clear();
// operations_stack.clear();
// }
Result parse_expression(
Tokenizer &tokenizer, bool in_argument_list, Span<const Function> functions, Token *out_last_token) {
Token token;
std::vector<OpEntry> operations_stack;
// TODO Use unique ptr
std::vector<Node *> operand_stack;
std::vector<UniquePtr<Node>> operand_stack;
int precedence_base = 0;
bool previous_was_operand = false;
@ -458,15 +433,14 @@ Result parse_expression(
OpEntry op;
op.precedence = precedence_base + get_operator_precedence(op_type);
// Operands will be assigned when we pop operations from the stack
op.node = memnew(OperatorNode(op_type, nullptr, nullptr));
op.node = make_unique_instance<OperatorNode>(op_type, nullptr, nullptr);
while (operations_stack.size() > 0) {
const OpEntry last_op = operations_stack.back();
const OpEntry &last_op = operations_stack.back();
// While the current operator has lower precedence, pop last operand
if (op.precedence <= last_op.precedence) {
const ErrorID err = pop_expression_operator(operations_stack, operand_stack);
if (err != ERROR_NONE) {
free_nodes(operations_stack, operand_stack);
Result result;
result.error.id = err;
result.error.position = tokenizer.get_position();
@ -477,11 +451,10 @@ Result parse_expression(
}
}
operations_stack.push_back(op);
operations_stack.push_back(std::move(op));
} else if (is_operand(token)) {
if (previous_was_operand) {
free_nodes(operations_stack, operand_stack);
Result result;
result.error.id = ERROR_MULTIPLE_OPERANDS;
result.error.position = tokenizer.get_position();
@ -492,10 +465,11 @@ Result parse_expression(
} else if (token.type == Token::PARENTHESIS_OPEN) {
if (operand_stack.size() > 0 && operand_stack.back()->type == Node::VARIABLE) {
Result fn_result = parse_function(tokenizer, operand_stack, functions);
if (fn_result.error.id != ERROR_NONE) {
free_nodes(operations_stack, operand_stack);
return fn_result;
Error fn_error = parse_function(tokenizer, operand_stack, functions);
if (fn_error.id != ERROR_NONE) {
Result result;
result.error = fn_error;
return result;
}
} else {
@ -508,7 +482,6 @@ Result parse_expression(
break;
}
if (precedence_base < MAX_PRECEDENCE) {
free_nodes(operations_stack, operand_stack);
Result result;
result.error.id = ERROR_UNEXPECTED_TOKEN;
result.error.position = tokenizer.get_position();
@ -518,7 +491,6 @@ Result parse_expression(
CRASH_COND(precedence_base < 0);
} else {
free_nodes(operations_stack, operand_stack);
Result result;
result.error.id = ERROR_UNEXPECTED_TOKEN;
result.error.position = tokenizer.get_position();
@ -531,13 +503,11 @@ Result parse_expression(
Result result;
result.error.id = tokenizer.get_error();
if (result.error.id != ERROR_NONE) {
free_nodes(operations_stack, operand_stack);
result.error.position = tokenizer.get_position();
return result;
}
if (precedence_base != 0) {
free_nodes(operations_stack, operand_stack);
result.error.id = ERROR_UNCLOSED_PARENTHESIS;
result.error.position = tokenizer.get_position();
return result;
@ -553,7 +523,6 @@ Result parse_expression(
while (operations_stack.size() > 0) {
const ErrorID err = pop_expression_operator(operations_stack, operand_stack);
if (err != ERROR_NONE) {
free_nodes(operations_stack, operand_stack);
Result result;
result.error.id = err;
result.error.position = tokenizer.get_position();
@ -564,7 +533,7 @@ Result parse_expression(
CRASH_COND(operand_stack.size() > 1);
// The stack can be empty if the expression was empty
if (operand_stack.size() > 0) {
result.root = operand_stack.back();
result.root = std::move(operand_stack.back());
}
return result;
@ -609,11 +578,11 @@ void find_variables(const Node &node, std::vector<std::string_view> &variables)
// Returns true if the passed node is constant (or gets changed into a constant).
// `out_number` is the value of the node if it is constant.
bool precompute_constants(Node *&node, float &out_number, Span<const Function> functions) {
bool precompute_constants(UniquePtr<Node> &node, float &out_number, Span<const Function> functions) {
CRASH_COND(node == nullptr);
switch (node->type) {
case Node::NUMBER: {
const NumberNode *nn = reinterpret_cast<NumberNode *>(node);
const NumberNode *nn = reinterpret_cast<NumberNode *>(node.get());
out_number = nn->value;
return true;
}
@ -622,14 +591,14 @@ bool precompute_constants(Node *&node, float &out_number, Span<const Function> f
return false;
case Node::OPERATOR: {
OperatorNode *onode = reinterpret_cast<OperatorNode *>(node);
if (onode->n0 != nullptr && onode->n1 != nullptr) {
OperatorNode &onode = static_cast<OperatorNode &>(*node);
if (onode.n0 != nullptr && onode.n1 != nullptr) {
float n0;
float n1;
const bool constant0 = precompute_constants(onode->n0, n0, functions);
const bool constant1 = precompute_constants(onode->n1, n1, functions);
const bool constant0 = precompute_constants(onode.n0, n0, functions);
const bool constant1 = precompute_constants(onode.n1, n1, functions);
if (constant0 && constant1) {
switch (onode->op) {
switch (onode.op) {
case OperatorNode::ADD:
out_number = n0 + n1;
break;
@ -649,8 +618,7 @@ bool precompute_constants(Node *&node, float &out_number, Span<const Function> f
CRASH_NOW();
}
memdelete(node);
node = memnew(NumberNode(out_number));
node = make_unique_instance<NumberNode>(out_number);
return true;
}
// TODO Unary operators
@ -659,12 +627,12 @@ bool precompute_constants(Node *&node, float &out_number, Span<const Function> f
} break;
case Node::FUNCTION: {
FunctionNode *fnode = reinterpret_cast<FunctionNode *>(node);
FunctionNode &fnode = static_cast<FunctionNode &>(*node);
bool all_constant = true;
FixedArray<float, 4> constant_args;
const Function *f = find_function_by_id(fnode->function_id, functions);
const Function *f = find_function_by_id(fnode.function_id, functions);
for (unsigned int i = 0; i < f->argument_count; ++i) {
if (!precompute_constants(fnode->args[i], constant_args[i], functions)) {
if (!precompute_constants(fnode.args[i], constant_args[i], functions)) {
all_constant = false;
}
}
@ -673,8 +641,7 @@ bool precompute_constants(Node *&node, float &out_number, Span<const Function> f
CRASH_COND(f->func == nullptr);
out_number = f->func(to_span_const(constant_args, f->argument_count));
memdelete(node);
node = memnew(NumberNode(out_number));
node = make_unique_instance<NumberNode>(out_number);
return true;
}
return false;
@ -704,48 +671,48 @@ Result parse(std::string_view text, Span<const Function> functions) {
return result;
}
bool is_tree_equal(const Node *a, const Node *b, Span<const Function> functions) {
if (a->type != b->type) {
bool is_tree_equal(const Node &a, const Node &b, Span<const Function> functions) {
if (a.type != b.type) {
return false;
}
switch (a->type) {
switch (a.type) {
case Node::NUMBER: {
const NumberNode *nn_a = reinterpret_cast<const NumberNode *>(a);
const NumberNode *nn_b = reinterpret_cast<const NumberNode *>(b);
return Math::is_equal_approx(nn_a->value, nn_b->value);
const NumberNode &nn_a = static_cast<const NumberNode &>(a);
const NumberNode &nn_b = static_cast<const NumberNode &>(b);
return Math::is_equal_approx(nn_a.value, nn_b.value);
}
case Node::VARIABLE: {
const VariableNode *va = reinterpret_cast<const VariableNode *>(a);
const VariableNode *vb = reinterpret_cast<const VariableNode *>(b);
return va->name == vb->name;
const VariableNode &va = static_cast<const VariableNode &>(a);
const VariableNode &vb = static_cast<const VariableNode &>(b);
return va.name == vb.name;
}
case Node::OPERATOR: {
const OperatorNode *oa = reinterpret_cast<const OperatorNode *>(a);
const OperatorNode *ob = reinterpret_cast<const OperatorNode *>(b);
if (oa->op != ob->op) {
const OperatorNode &oa = static_cast<const OperatorNode &>(a);
const OperatorNode &ob = static_cast<const OperatorNode &>(b);
if (oa.op != ob.op) {
return false;
}
CRASH_COND(oa->n0 == nullptr);
CRASH_COND(ob->n0 == nullptr);
if (oa->n1 == nullptr && ob->n1 == nullptr) {
return is_tree_equal(oa->n0, ob->n0, functions);
CRASH_COND(oa.n0 == nullptr);
CRASH_COND(ob.n0 == nullptr);
if (oa.n1 == nullptr && ob.n1 == nullptr) {
return is_tree_equal(*oa.n0, *ob.n0, functions);
}
CRASH_COND(oa->n1 == nullptr);
CRASH_COND(ob->n1 == nullptr);
return is_tree_equal(oa->n0, ob->n0, functions) && is_tree_equal(oa->n1, ob->n1, functions);
CRASH_COND(oa.n1 == nullptr);
CRASH_COND(ob.n1 == nullptr);
return is_tree_equal(*oa.n0, *ob.n0, functions) && is_tree_equal(*oa.n1, *ob.n1, functions);
}
case Node::FUNCTION: {
const FunctionNode *fa = reinterpret_cast<const FunctionNode *>(a);
const FunctionNode *fb = reinterpret_cast<const FunctionNode *>(b);
if (fa->function_id != fb->function_id) {
const FunctionNode &fa = static_cast<const FunctionNode &>(a);
const FunctionNode &fb = static_cast<const FunctionNode &>(b);
if (fa.function_id != fb.function_id) {
return false;
}
const Function *f = find_function_by_id(fa->function_id, functions);
const Function *f = find_function_by_id(fa.function_id, functions);
CRASH_COND(f == nullptr);
for (unsigned int i = 0; i < f->argument_count; ++i) {
CRASH_COND(fa->args[i] == nullptr);
CRASH_COND(fb->args[i] == nullptr);
if (!is_tree_equal(fa->args[i], fb->args[i], functions)) {
CRASH_COND(fa.args[i] == nullptr);
CRASH_COND(fb.args[i] == nullptr);
if (!is_tree_equal(*fa.args[i], *fb.args[i], functions)) {
return false;
}
}
@ -757,10 +724,6 @@ bool is_tree_equal(const Node *a, const Node *b, Span<const Function> functions)
}
}
bool is_tree_equal(const Node &root_a, const Node &root_b, Span<const Function> functions) {
return is_tree_equal(&root_a, &root_b, functions);
}
const char *to_string(OperatorNode::Operation op) {
switch (op) {
case OperatorNode::ADD:
@ -779,45 +742,45 @@ const char *to_string(OperatorNode::Operation op) {
}
}
void tree_to_string(const Node *node, int depth, std::stringstream &output, Span<const Function> functions) {
void tree_to_string(const Node &node, int depth, std::stringstream &output, Span<const Function> functions) {
for (int i = 0; i < depth; ++i) {
output << " ";
}
switch (node->type) {
switch (node.type) {
case Node::NUMBER: {
const NumberNode *nn = reinterpret_cast<const NumberNode *>(node);
output << nn->value;
const NumberNode &nn = static_cast<const NumberNode &>(node);
output << nn.value;
} break;
case Node::VARIABLE: {
const VariableNode *vn = reinterpret_cast<const VariableNode *>(node);
output << vn->name;
const VariableNode &vn = static_cast<const VariableNode &>(node);
output << vn.name;
} break;
case Node::OPERATOR: {
const OperatorNode *on = reinterpret_cast<const OperatorNode *>(node);
output << to_string(on->op);
const OperatorNode &on = static_cast<const OperatorNode &>(node);
output << to_string(on.op);
output << '\n';
CRASH_COND(on->n0 == nullptr);
if (on->n1 == nullptr) {
tree_to_string(on->n0, depth + 1, output, functions);
CRASH_COND(on.n0 == nullptr);
if (on.n1 == nullptr) {
tree_to_string(*on.n0, depth + 1, output, functions);
} else {
CRASH_COND(on->n1 == nullptr);
tree_to_string(on->n0, depth + 1, output, functions);
CRASH_COND(on.n1 == nullptr);
tree_to_string(*on.n0, depth + 1, output, functions);
output << '\n';
tree_to_string(on->n1, depth + 1, output, functions);
tree_to_string(*on.n1, depth + 1, output, functions);
}
} break;
case Node::FUNCTION: {
const FunctionNode *fn = reinterpret_cast<const FunctionNode *>(node);
const Function *f = find_function_by_id(fn->function_id, functions);
const FunctionNode &fn = static_cast<const FunctionNode &>(node);
const Function *f = find_function_by_id(fn.function_id, functions);
CRASH_COND(f == nullptr);
output << f->name << "()";
for (unsigned int i = 0; i < f->argument_count; ++i) {
CRASH_COND(fn->args[i] == nullptr);
CRASH_COND(fn.args[i] == nullptr);
output << '\n';
tree_to_string(fn->args[i], depth + 1, output, functions);
tree_to_string(*fn.args[i], depth + 1, output, functions);
}
} break;
@ -828,7 +791,7 @@ void tree_to_string(const Node *node, int depth, std::stringstream &output, Span
std::string tree_to_string(const Node &node, Span<const Function> functions) {
std::stringstream ss;
tree_to_string(&node, 0, ss, functions);
tree_to_string(node, 0, ss, functions);
return ss.str();
}

View File

@ -2,6 +2,7 @@
#define ZYLANN_EXPRESSION_PARSER_H
#include "fixed_array.h"
#include "memory.h"
#include "span.h"
#include <string_view>
@ -50,28 +51,21 @@ struct OperatorNode : Node {
};
Operation op;
// TODO Use unique ptr
Node *n0 = nullptr;
Node *n1 = nullptr;
UniquePtr<Node> n0;
UniquePtr<Node> n1;
OperatorNode(Operation p_op, Node *a, Node *b) : op(p_op), n0(a), n1(b) {
OperatorNode(Operation p_op, UniquePtr<Node> a, UniquePtr<Node> b) : op(p_op), n0(std::move(a)), n1(std::move(b)) {
type = OPERATOR;
}
~OperatorNode();
};
struct FunctionNode : Node {
unsigned int function_id;
// TODO Use unique ptr
FixedArray<Node *, 4> args;
FixedArray<UniquePtr<Node>, 4> args;
FunctionNode() {
type = Node::FUNCTION;
args.fill(nullptr);
}
~FunctionNode();
};
enum ErrorID { //
@ -98,8 +92,7 @@ struct Error {
};
struct Result {
// TODO Use unique ptr
Node *root = nullptr;
UniquePtr<Node> root;
Error error;
};

View File

@ -10,18 +10,6 @@ namespace zylann {
template <typename T, unsigned int N>
class FixedArray {
public:
inline FixedArray() {}
inline FixedArray(T defval) {
fill(defval);
}
inline void fill(T v) {
for (unsigned int i = 0; i < N; ++i) {
_data[i] = v;
}
}
// TODO Optimization: move semantics
inline T &operator[](unsigned int i) {
@ -77,6 +65,15 @@ private:
T _data[N];
};
// Fills array with the same value.
// Not a method because it would not compile with non-copyable types.
template <typename T, unsigned int N>
inline void fill(FixedArray<T, N> &dst, const T v) {
for (unsigned int i = 0; i < dst.size(); ++i) {
dst[i] = v;
}
}
} // namespace zylann
#endif // FIXED_ARRAY_H

22
util/memory.h Normal file
View File

@ -0,0 +1,22 @@
#ifndef ZYLANN_MEMORY_H
#define ZYLANN_MEMORY_H
#include <memory>
namespace zylann {
// Default implementation of unique pointers for this project. Allows to change it in one place.
// Note: array allocations are not used. Containers are preferred.
// TODO Use Godot's allocator?
template <typename T>
using UniquePtr = std::unique_ptr<T>;
template <class T, class... Types, std::enable_if_t<!std::is_array_v<T>, int> = 0>
UniquePtr<T> make_unique_instance(Types &&...args) {
return std::unique_ptr<T>(new T(std::forward<Types>(args)...));
}
} // namespace zylann
#endif // ZYLANN_MEMORY_H