summaryrefslogtreecommitdiffstats
path: root/src/libixion/formula_value_stack.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/libixion/formula_value_stack.cpp')
-rw-r--r--src/libixion/formula_value_stack.cpp625
1 files changed, 625 insertions, 0 deletions
diff --git a/src/libixion/formula_value_stack.cpp b/src/libixion/formula_value_stack.cpp
new file mode 100644
index 0000000..9985c77
--- /dev/null
+++ b/src/libixion/formula_value_stack.cpp
@@ -0,0 +1,625 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include "formula_value_stack.hpp"
+#include "debug.hpp"
+
+#include <ixion/address.hpp>
+#include <ixion/cell.hpp>
+#include <ixion/cell_access.hpp>
+#include <ixion/matrix.hpp>
+#include <ixion/formula_result.hpp>
+#include <ixion/config.hpp>
+#include <ixion/exceptions.hpp>
+
+#include <string>
+#include <sstream>
+
+namespace ixion {
+
+namespace {
+
+bool get_boolean_value(const model_context& cxt, const stack_value& v)
+{
+ switch (v.get_type())
+ {
+ case stack_value_t::boolean:
+ return v.get_boolean();
+ case stack_value_t::value:
+ case stack_value_t::matrix:
+ return v.get_value() != 0.0;
+ case stack_value_t::single_ref:
+ {
+ // reference to a single cell.
+ const abs_address_t& addr = v.get_address();
+ auto ca = cxt.get_cell_access(addr);
+ switch (ca.get_value_type())
+ {
+ case cell_value_t::numeric:
+ case cell_value_t::boolean:
+ return ca.get_boolean_value();
+ case cell_value_t::empty:
+ return false;
+ default:;
+ }
+ break;
+ }
+ default:;
+ }
+
+ throw formula_error(formula_error_t::invalid_value_type);
+}
+
+double get_numeric_value(const model_context& cxt, const stack_value& v)
+{
+ switch (v.get_type())
+ {
+ case stack_value_t::boolean:
+ return v.get_boolean() ? 1.0 : 0.0;
+ case stack_value_t::value:
+ case stack_value_t::matrix:
+ return v.get_value();
+ case stack_value_t::string:
+ return 0.0;
+ case stack_value_t::single_ref:
+ {
+ // reference to a single cell.
+ const abs_address_t& addr = v.get_address();
+ return cxt.get_numeric_value(addr);
+ }
+ default:;
+ }
+
+ throw formula_error(formula_error_t::invalid_value_type);
+}
+
+} // anonymous namespace
+
+std::ostream& operator<<(std::ostream& os, stack_value_t sv)
+{
+ static constexpr std::string_view names[] = {
+ "boolean",
+ "error",
+ "value",
+ "string",
+ "single_ref",
+ "range_ref",
+ "matrix",
+ };
+
+ auto pos = static_cast<std::size_t>(sv);
+ if (pos < std::size(names))
+ os << names[pos];
+ else
+ os << "???";
+
+ return os;
+}
+
+stack_value::stack_value(bool b) :
+ m_type(stack_value_t::boolean), m_value(b) {}
+
+stack_value::stack_value(double val) :
+ m_type(stack_value_t::value), m_value(val) {}
+
+stack_value::stack_value(std::string str) :
+ m_type(stack_value_t::string), m_value(std::move(str)) {}
+
+stack_value::stack_value(const abs_address_t& val) :
+ m_type(stack_value_t::single_ref), m_value(val) {}
+
+stack_value::stack_value(const abs_range_t& val) :
+ m_type(stack_value_t::range_ref), m_value(val) {}
+
+stack_value::stack_value(formula_error_t err) :
+ m_type(stack_value_t::error), m_value(err) {}
+
+stack_value::stack_value(matrix mtx) :
+ m_type(stack_value_t::matrix), m_value(std::move(mtx)) {}
+
+stack_value::stack_value(stack_value&& other) :
+ m_type(other.m_type), m_value(std::move(other.m_value)) {}
+
+stack_value::~stack_value() = default;
+
+stack_value& stack_value::operator= (stack_value&& other)
+{
+ m_type = other.m_type;
+ m_value = std::move(other.m_value);
+ return *this;
+}
+
+stack_value_t stack_value::get_type() const
+{
+ return m_type;
+}
+
+bool stack_value::get_boolean() const
+{
+ switch (m_type)
+ {
+ case stack_value_t::boolean:
+ return std::get<bool>(m_value);
+ case stack_value_t::value:
+ return std::get<double>(m_value) != 0.0;
+ case stack_value_t::matrix:
+ return std::get<matrix>(m_value).get_boolean(0, 0);
+ default:;
+ }
+
+ return false;
+}
+
+double stack_value::get_value() const
+{
+ switch (m_type)
+ {
+ case stack_value_t::boolean:
+ return std::get<bool>(m_value) ? 1.0 : 0.0;
+ case stack_value_t::value:
+ return std::get<double>(m_value);
+ case stack_value_t::matrix:
+ return std::get<matrix>(m_value).get_numeric(0, 0);
+ default:
+ ;
+ }
+
+ return 0.0;
+}
+
+const std::string& stack_value::get_string() const
+{
+ return std::get<std::string>(m_value);
+}
+
+const abs_address_t& stack_value::get_address() const
+{
+ return std::get<abs_address_t>(m_value);
+}
+
+const abs_range_t& stack_value::get_range() const
+{
+ return std::get<abs_range_t>(m_value);
+}
+
+formula_error_t stack_value::get_error() const
+{
+ return std::get<formula_error_t>(m_value);
+}
+
+const matrix& stack_value::get_matrix() const
+{
+ return std::get<matrix>(m_value);
+}
+
+matrix stack_value::pop_matrix()
+{
+ switch (m_type)
+ {
+ case stack_value_t::boolean:
+ {
+ matrix mtx(1, 1);
+ mtx.set(0, 0, std::get<bool>(m_value));
+ return mtx;
+ }
+ case stack_value_t::value:
+ {
+ matrix mtx(1, 1);
+ mtx.set(0, 0, std::get<double>(m_value));
+ return mtx;
+ }
+ case stack_value_t::matrix:
+ {
+ matrix mtx;
+ mtx.swap(std::get<matrix>(m_value));
+ return mtx;
+ }
+ default:
+ throw formula_error(formula_error_t::stack_error);
+ }
+}
+
+formula_value_stack::formula_value_stack(const model_context& cxt) : m_context(cxt) {}
+
+formula_value_stack::iterator formula_value_stack::begin()
+{
+ return m_stack.begin();
+}
+
+formula_value_stack::iterator formula_value_stack::end()
+{
+ return m_stack.end();
+}
+
+formula_value_stack::const_iterator formula_value_stack::begin() const
+{
+ return m_stack.begin();
+}
+
+formula_value_stack::const_iterator formula_value_stack::end() const
+{
+ return m_stack.end();
+}
+
+formula_value_stack::value_type formula_value_stack::release(iterator pos)
+{
+ auto tmp = std::move(*pos);
+ m_stack.erase(pos);
+ return tmp;
+}
+
+formula_value_stack::value_type formula_value_stack::release_back()
+{
+ assert(!m_stack.empty());
+ auto tmp = std::move(m_stack.back());
+ m_stack.pop_back();
+ return tmp;
+}
+
+bool formula_value_stack::empty() const
+{
+ return m_stack.empty();
+}
+
+size_t formula_value_stack::size() const
+{
+ return m_stack.size();
+}
+
+void formula_value_stack::clear()
+{
+ return m_stack.clear();
+}
+
+void formula_value_stack::swap(formula_value_stack& other)
+{
+ m_stack.swap(other.m_stack);
+}
+
+stack_value& formula_value_stack::back()
+{
+ return m_stack.back();
+}
+
+const stack_value& formula_value_stack::back() const
+{
+ return m_stack.back();
+}
+
+const stack_value& formula_value_stack::operator[](size_t pos) const
+{
+ return m_stack[pos];
+}
+
+double formula_value_stack::get_value(size_t pos) const
+{
+ const stack_value& v = m_stack[pos];
+ return get_numeric_value(m_context, v);
+}
+
+void formula_value_stack::push_back(value_type&& val)
+{
+ IXION_TRACE("push_back");
+ m_stack.push_back(std::move(val));
+}
+
+void formula_value_stack::push_boolean(bool b)
+{
+ IXION_TRACE("b=" << std::boolalpha << b);
+ m_stack.emplace_back(b);
+}
+
+void formula_value_stack::push_value(double val)
+{
+ IXION_TRACE("val=" << val);
+ m_stack.emplace_back(val);
+}
+
+void formula_value_stack::push_string(std::string str)
+{
+ IXION_TRACE("str='" << str << "'");
+ m_stack.emplace_back(std::move(str));
+}
+
+void formula_value_stack::push_single_ref(const abs_address_t& val)
+{
+ IXION_TRACE("val=" << val.get_name());
+ m_stack.emplace_back(val);
+}
+
+void formula_value_stack::push_range_ref(const abs_range_t& val)
+{
+ assert(val.valid());
+ IXION_TRACE("start=" << val.first.get_name() << "; end=" << val.last.get_name());
+ m_stack.emplace_back(val);
+}
+
+void formula_value_stack::push_matrix(matrix mtx)
+{
+ IXION_TRACE("push_matrix");
+ m_stack.emplace_back(std::move(mtx));
+}
+
+void formula_value_stack::push_error(formula_error_t err)
+{
+ IXION_TRACE("err=" << short(err) << " (" << get_formula_error_name(err) << ")");
+ m_stack.emplace_back(err);
+}
+
+bool formula_value_stack::pop_boolean()
+{
+ if (m_stack.empty())
+ throw formula_error(formula_error_t::stack_error);
+
+ const stack_value& v = m_stack.back();
+ bool ret = get_boolean_value(m_context, v);
+ m_stack.pop_back();
+ IXION_TRACE("ret=" << std::boolalpha << ret);
+ return ret;
+}
+
+double formula_value_stack::pop_value()
+{
+ double ret = 0.0;
+ if (m_stack.empty())
+ throw formula_error(formula_error_t::stack_error);
+
+ const stack_value& v = m_stack.back();
+ ret = get_numeric_value(m_context, v);
+ m_stack.pop_back();
+ IXION_TRACE("ret=" << ret);
+ return ret;
+}
+
+std::string formula_value_stack::pop_string()
+{
+ IXION_TRACE("pop_string");
+
+ if (m_stack.empty())
+ throw formula_error(formula_error_t::stack_error);
+
+ const stack_value& v = m_stack.back();
+ switch (v.get_type())
+ {
+ case stack_value_t::string:
+ {
+ const std::string str = v.get_string();
+ m_stack.pop_back();
+ return str;
+ }
+ case stack_value_t::boolean:
+ {
+ std::ostringstream os;
+ os << std::boolalpha << v.get_boolean();
+ m_stack.pop_back();
+ return os.str();
+ }
+ case stack_value_t::value:
+ {
+ std::ostringstream os;
+ os << v.get_value();
+ m_stack.pop_back();
+ return os.str();
+ }
+ case stack_value_t::single_ref:
+ {
+ // reference to a single cell.
+ abs_address_t addr = v.get_address();
+ m_stack.pop_back();
+
+ switch (m_context.get_celltype(addr))
+ {
+ case celltype_t::empty:
+ return std::string();
+ case celltype_t::formula:
+ {
+ formula_result res = m_context.get_formula_result(addr);
+
+ switch (res.get_type())
+ {
+ case formula_result::result_type::error:
+ throw formula_error(res.get_error());
+ case formula_result::result_type::string:
+ return res.get_string();
+ case formula_result::result_type::boolean:
+ {
+ std::ostringstream os;
+ os << std::boolalpha << res.get_boolean();
+ return os.str();
+ }
+ case formula_result::result_type::value:
+ {
+ std::ostringstream os;
+ os << res.get_value();
+ return os.str();
+ }
+ default:
+ throw formula_error(formula_error_t::stack_error);
+ }
+ }
+ break;
+ case celltype_t::numeric:
+ {
+ std::ostringstream os;
+ os << m_context.get_numeric_value(addr);
+ return os.str();
+ }
+ case celltype_t::string:
+ {
+ const std::string* ps = m_context.get_string(m_context.get_string_identifier(addr));
+ if (!ps)
+ throw formula_error(formula_error_t::stack_error);
+ return *ps;
+ }
+ break;
+ default:
+ throw formula_error(formula_error_t::stack_error);
+ }
+
+ break;
+ }
+ default:
+ {
+ IXION_DEBUG("unhandled type: " << v.get_type());
+ }
+ }
+ throw formula_error(formula_error_t::stack_error);
+}
+
+matrix formula_value_stack::pop_matrix()
+{
+ if (auto mtx = maybe_pop_matrix(); mtx)
+ return *mtx;
+
+ throw formula_error(formula_error_t::stack_error);
+}
+
+std::optional<matrix> formula_value_stack::maybe_pop_matrix()
+{
+ if (m_stack.empty())
+ throw formula_error(formula_error_t::stack_error);
+
+ stack_value& v = m_stack.back();
+ switch (v.get_type())
+ {
+ case stack_value_t::matrix:
+ {
+ auto mtx = v.pop_matrix();
+ m_stack.pop_back();
+ return mtx;
+ }
+ case stack_value_t::range_ref:
+ return pop_range_value();
+ default:;
+ }
+
+ return {};
+}
+
+abs_address_t formula_value_stack::pop_single_ref()
+{
+ IXION_TRACE("pop_single_ref");
+
+ if (m_stack.empty())
+ throw formula_error(formula_error_t::stack_error);
+
+ const stack_value& v = m_stack.back();
+
+ switch (v.get_type())
+ {
+ case stack_value_t::single_ref:
+ {
+ abs_address_t addr = v.get_address();
+ m_stack.pop_back();
+ return addr;
+ }
+ case stack_value_t::range_ref:
+ {
+ abs_range_t range = v.get_range();
+ m_stack.pop_back();
+ return range.first;
+ }
+ default:;
+ }
+
+ throw formula_error(formula_error_t::stack_error);
+}
+
+abs_range_t formula_value_stack::pop_range_ref()
+{
+ IXION_TRACE("pop_range_ref");
+
+ if (m_stack.empty())
+ throw formula_error(formula_error_t::stack_error);
+
+ const stack_value& v = m_stack.back();
+
+ switch (v.get_type())
+ {
+ case stack_value_t::single_ref:
+ {
+ abs_address_t addr = v.get_address();
+ m_stack.pop_back();
+ return addr;
+ }
+ case stack_value_t::range_ref:
+ {
+ abs_range_t range = v.get_range();
+ m_stack.pop_back();
+ return range;
+ }
+ default:
+ throw formula_error(formula_error_t::stack_error);
+ }
+}
+
+matrix formula_value_stack::pop_range_value()
+{
+ IXION_TRACE("pop_range_value");
+
+ if (m_stack.empty())
+ throw formula_error(formula_error_t::stack_error);
+
+ const stack_value& v = m_stack.back();
+ if (v.get_type() != stack_value_t::range_ref)
+ throw formula_error(formula_error_t::stack_error);
+
+ matrix ret = m_context.get_range_value(v.get_range());
+ m_stack.pop_back();
+ return ret;
+}
+
+formula_error_t formula_value_stack::pop_error()
+{
+ IXION_TRACE("pop_error");
+
+ if (m_stack.empty())
+ throw formula_error(formula_error_t::stack_error);
+
+ const stack_value& v = m_stack.back();
+ if (v.get_type() != stack_value_t::error)
+ throw formula_error(formula_error_t::stack_error);
+
+ formula_error_t ret = v.get_error();
+ m_stack.pop_back();
+ return ret;
+}
+
+resolved_stack_value formula_value_stack::pop_matrix_or_numeric()
+{
+ if (auto mtx = maybe_pop_matrix(); mtx)
+ return *mtx;
+
+ // fall back to numeric value
+ return pop_value();
+}
+
+resolved_stack_value formula_value_stack::pop_matrix_or_string()
+{
+ if (auto mtx = maybe_pop_matrix(); mtx)
+ return *mtx;
+
+ // fall back to string value
+ return pop_string();
+}
+
+void formula_value_stack::pop_back()
+{
+ m_stack.pop_back();
+}
+
+stack_value_t formula_value_stack::get_type() const
+{
+ if (m_stack.empty())
+ throw formula_error(formula_error_t::stack_error);
+
+ return m_stack.back().get_type();
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */