summaryrefslogtreecommitdiffstats
path: root/src/libixion/formula_parser.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/libixion/formula_parser.cpp')
-rw-r--r--src/libixion/formula_parser.cpp268
1 files changed, 268 insertions, 0 deletions
diff --git a/src/libixion/formula_parser.cpp b/src/libixion/formula_parser.cpp
new file mode 100644
index 0000000..4a8e6f5
--- /dev/null
+++ b/src/libixion/formula_parser.cpp
@@ -0,0 +1,268 @@
+/* -*- 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_parser.hpp"
+#include "ixion/formula_name_resolver.hpp"
+
+#include "formula_functions.hpp"
+
+#include <iostream>
+#include <sstream>
+
+namespace ixion {
+
+namespace {
+
+class ref_error : public general_error
+{
+public:
+ ref_error(const std::string& msg) :
+ general_error(msg) {}
+};
+
+}
+
+// ----------------------------------------------------------------------------
+
+formula_parser::parse_error::parse_error(const std::string& msg) :
+ general_error(msg) {}
+
+// ----------------------------------------------------------------------------
+
+formula_parser::formula_parser(
+ const lexer_tokens_t& tokens, model_context& cxt, const formula_name_resolver& resolver) :
+ m_itr_cur(tokens.end()),
+ m_itr_end(tokens.end()),
+ m_tokens(tokens),
+ m_context(cxt),
+ m_resolver(resolver)
+{
+}
+
+formula_parser::~formula_parser()
+{
+}
+
+void formula_parser::set_origin(const abs_address_t& pos)
+{
+ m_pos = pos;
+}
+
+void formula_parser::parse()
+{
+ for (m_itr_cur = m_tokens.begin(); has_token(); next())
+ {
+ switch (get_token().opcode)
+ {
+ case lexer_opcode_t::array_open:
+ case lexer_opcode_t::array_close:
+ case lexer_opcode_t::open:
+ case lexer_opcode_t::close:
+ case lexer_opcode_t::plus:
+ case lexer_opcode_t::minus:
+ case lexer_opcode_t::multiply:
+ case lexer_opcode_t::exponent:
+ case lexer_opcode_t::concat:
+ case lexer_opcode_t::equal:
+ case lexer_opcode_t::divide:
+ case lexer_opcode_t::sep:
+ case lexer_opcode_t::array_row_sep:
+ primitive();
+ break;
+ case lexer_opcode_t::name:
+ name();
+ break;
+ case lexer_opcode_t::string:
+ literal();
+ break;
+ case lexer_opcode_t::value:
+ value();
+ break;
+ case lexer_opcode_t::less:
+ less();
+ break;
+ case lexer_opcode_t::greater:
+ greater();
+ break;
+ default:
+ ;
+ }
+ }
+}
+
+formula_tokens_t& formula_parser::get_tokens()
+{
+ return m_formula_tokens;
+}
+
+void formula_parser::primitive()
+{
+ fopcode_t foc = fop_unknown;
+ switch (get_token().opcode)
+ {
+ case lexer_opcode_t::close:
+ foc = fop_close;
+ break;
+ case lexer_opcode_t::divide:
+ foc = fop_divide;
+ break;
+ case lexer_opcode_t::minus:
+ foc = fop_minus;
+ break;
+ case lexer_opcode_t::multiply:
+ foc = fop_multiply;
+ break;
+ case lexer_opcode_t::exponent:
+ foc = fop_exponent;
+ break;
+ case lexer_opcode_t::concat:
+ foc = fop_concat;
+ break;
+ case lexer_opcode_t::equal:
+ foc = fop_equal;
+ break;
+ case lexer_opcode_t::open:
+ foc = fop_open;
+ break;
+ case lexer_opcode_t::plus:
+ foc = fop_plus;
+ break;
+ case lexer_opcode_t::sep:
+ foc = fop_sep;
+ break;
+ case lexer_opcode_t::array_row_sep:
+ foc = fop_array_row_sep;
+ break;
+ case lexer_opcode_t::array_open:
+ foc = fop_array_open;
+ break;
+ case lexer_opcode_t::array_close:
+ foc = fop_array_close;
+ break;
+ default:
+ throw parse_error("unknown primitive token received");
+ }
+ m_formula_tokens.emplace_back(foc);
+}
+
+void formula_parser::name()
+{
+ std::string_view name = std::get<std::string_view>(get_token().value);
+
+ formula_name_t fn = m_resolver.resolve(name, m_pos);
+
+ switch (fn.type)
+ {
+ case formula_name_t::cell_reference:
+ m_formula_tokens.emplace_back(std::get<address_t>(fn.value));
+ break;
+ case formula_name_t::range_reference:
+ {
+ m_formula_tokens.emplace_back(std::get<range_t>(fn.value));
+ break;
+ }
+ case formula_name_t::table_reference:
+ {
+ table_t table;
+ formula_name_t::table_type src_table = std::get<formula_name_t::table_type>(fn.value);
+ table.name = m_context.add_string(src_table.name);
+ table.column_first = m_context.add_string(src_table.column_first);
+ table.column_last = m_context.add_string(src_table.column_last);
+ table.areas = src_table.areas;
+ m_formula_tokens.emplace_back(table);
+ break;
+ }
+ case formula_name_t::function:
+ m_formula_tokens.emplace_back(std::get<formula_function_t>(fn.value));
+ break;
+ case formula_name_t::named_expression:
+ m_formula_tokens.emplace_back(std::string{name});
+ break;
+ default:
+ {
+ std::ostringstream os;
+ os << "failed to resolve a name token '" << name << "'.";
+ throw parse_error(os.str());
+ }
+ }
+}
+
+void formula_parser::literal()
+{
+ string_id_t sid = m_context.add_string(std::get<std::string_view>(get_token().value));
+ m_formula_tokens.emplace_back(sid);
+}
+
+void formula_parser::value()
+{
+ m_formula_tokens.emplace_back(std::get<double>(get_token().value));
+}
+
+void formula_parser::less()
+{
+ if (has_next())
+ {
+ next();
+ switch (get_token().opcode)
+ {
+ case lexer_opcode_t::equal:
+ m_formula_tokens.emplace_back(fop_less_equal);
+ return;
+ case lexer_opcode_t::greater:
+ m_formula_tokens.emplace_back(fop_not_equal);
+ return;
+ default:
+ ;
+ }
+ prev();
+ }
+ m_formula_tokens.emplace_back(fop_less);
+}
+
+void formula_parser::greater()
+{
+ if (has_next())
+ {
+ next();
+ if (get_token().opcode == lexer_opcode_t::equal)
+ {
+ m_formula_tokens.emplace_back(fop_greater_equal);
+ return;
+ }
+ prev();
+ }
+ m_formula_tokens.emplace_back(fop_greater);
+
+}
+
+const lexer_token& formula_parser::get_token() const
+{
+ return *m_itr_cur;
+}
+
+bool formula_parser::has_token() const
+{
+ return m_itr_cur != m_itr_end;
+}
+
+bool formula_parser::has_next() const
+{
+ return (m_itr_cur+1) != m_itr_end;
+}
+
+void formula_parser::next()
+{
+ ++m_itr_cur;
+}
+
+void formula_parser::prev()
+{
+ --m_itr_cur;
+}
+
+}
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */