Commit 432ed93f by Kohei Yoshida

Support named expression parsing with Excel R1C1.

1 parent a726d8d6
Pipeline #6394764 passed
in 6 minutes 9 seconds
......@@ -27,6 +27,29 @@ namespace ixion {
namespace {
bool check_address_by_sheet_bounds(const iface::formula_model_access* cxt, const address_t& pos)
{
sheet_size_t ss(row_upper_bound, column_upper_bound);
if (cxt && pos.sheet >= 0 && size_t(pos.sheet) < cxt->get_sheet_count())
{
// Make sure the address is within the sheet size.
ss = cxt->get_sheet_size(pos.sheet);
}
row_t row_check = pos.row >= 0 ? pos.row : -pos.row;
if (pos.row != row_unset && row_check >= ss.row)
return false;
col_t col_check = pos.column >= 0 ? pos.column : -pos.column;
if (pos.column != column_unset && col_check >= ss.column)
return false;
return true;
}
bool resolve_function(const char* p, size_t n, formula_name_t& ret)
{
formula_function_t func_oc = formula_functions::get_function_opcode(p, n);
......@@ -1078,20 +1101,7 @@ public:
// sheet name is not found in the model. Report back as invalid.
return ret;
row_t row_max = row_upper_bound;
col_t col_max = column_upper_bound;
if (mp_cxt && pos.sheet >= 0 && size_t(pos.sheet) < mp_cxt->get_sheet_count())
{
// Make sure the address is within the sheet size.
sheet_size_t sheet_size = mp_cxt->get_sheet_size(pos.sheet);
row_max = sheet_size.row;
col_max = sheet_size.column;
}
if (parsed_addr.row != row_unset && parsed_addr.row > row_max)
parse_res = invalid;
else if (parsed_addr.column != column_unset && parsed_addr.column > col_max)
if (!check_address_by_sheet_bounds(mp_cxt, parsed_addr))
parse_res = invalid;
}
......@@ -1252,6 +1262,7 @@ public:
if (resolve_function(p, n, ret))
return ret;
const char* p_end = p + n;
const char* p_last = p;
std::advance(p_last, n-1);
......@@ -1259,6 +1270,19 @@ public:
address_t parsed_addr(pos.sheet, 0, 0);
parse_address_result parse_res = parse_address_excel_r1c1(mp_cxt, p, p_last, parsed_addr);
if (parse_res != invalid)
{
// This is a valid R1C1-style address syntax-wise.
if (parsed_addr.sheet == invalid_sheet)
// sheet name is not found in the model. Report back as invalid.
return ret;
if (!check_address_by_sheet_bounds(mp_cxt, parsed_addr))
parse_res = invalid;
}
switch (parse_res)
{
case parse_address_result::valid_address:
......@@ -1266,10 +1290,12 @@ public:
set_cell_reference(ret, parsed_addr);
return ret;
}
break;
case parse_address_result::range_expected:
{
++p; // skip ':'
if (p == p_end)
return ret;
address_t parsed_addr2(0, 0, 0);
parse_address_result parse_res2 = parse_address_excel_r1c1(nullptr, p, p_last, parsed_addr2);
if (parse_res2 != parse_address_result::valid_address)
......@@ -1282,12 +1308,13 @@ public:
set_address(ret.range.first, parsed_addr);
set_address(ret.range.last, parsed_addr2);
ret.type = formula_name_t::range_reference;
return ret;
}
break;
default:
;
}
resolve_function_or_name(p, n, ret);
return ret;
}
......
......@@ -210,26 +210,37 @@ void test_name_resolver_excel_a1()
assert(res.type == formula_name_t::invalid);
}
void test_name_resolver_named_expression_excel_a1()
void test_name_resolver_named_expression()
{
cout << "Testing the Excel A1 name resolver for parsing named expressions." << endl;
cout << "Testing the name resolvers for parsing named expressions." << endl;
model_context cxt;
cxt.append_sheet(IXION_ASCII("Sheet"), 1048576, 16384);
auto resolver = formula_name_resolver::get(formula_name_resolver_t::excel_a1, &cxt);
assert(resolver);
const std::vector<formula_name_resolver_t> resolver_types = {
formula_name_resolver_t::excel_a1,
formula_name_resolver_t::excel_r1c1,
// TODO : add more resolver types.
};
std::vector<std::string> names = {
const std::vector<std::string> names = {
"MyRange",
"MyRange2",
};
for (const std::string& name : names)
for (formula_name_resolver_t rt : resolver_types)
{
cout << "parsing '" << name << "'..." << endl;
formula_name_t res = resolver->resolve(name.data(), name.size(), abs_address_t(0,0,0));
assert(res.type == formula_name_t::name_type::named_expression);
cout << "formula resolver type: " << int(rt) << endl; // TODO : map the enum value to string name.
auto resolver = formula_name_resolver::get(rt, &cxt);
assert(resolver);
for (const std::string& name : names)
{
cout << "parsing '" << name << "'..." << endl;
formula_name_t res = resolver->resolve(name.data(), name.size(), abs_address_t(0,0,0));
assert(res.type == formula_name_t::name_type::named_expression);
}
}
}
......@@ -367,7 +378,7 @@ void test_name_resolver_excel_r1c1()
}
}
// These are supposed to be all invalid.
// These are supposed to be all invalid or named expression.
const char* invalid_address[] = {
"F",
"RR",
......@@ -391,7 +402,7 @@ void test_name_resolver_excel_r1c1()
const char* p = invalid_address[i];
string name_r1c1(p);
formula_name_t res = resolver->resolve(name_r1c1.data(), name_r1c1.size(), abs_address_t());
if (res.type != formula_name_t::invalid)
if (res.type != formula_name_t::invalid && res.type != formula_name_t::named_expression)
{
cerr << "address " << name_r1c1 << " is expected to be invalid." << endl;
assert(false);
......@@ -908,7 +919,7 @@ int main()
test_string_to_double();
test_string_pool();
test_name_resolver_excel_a1();
test_name_resolver_named_expression_excel_a1();
test_name_resolver_named_expression();
test_name_resolver_table_excel_a1();
test_name_resolver_excel_r1c1();
test_name_resolver_odff();
......
......@@ -25,7 +25,7 @@ const char* get_formula_error_name(formula_error_t fe)
{
static const char* default_err_name = "#ERR!";
static std::vector<const char*> names = {
static const std::vector<const char*> names = {
"", // no error
"#REF!", // result not available
"#DIV/0!", // division by zero
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!