diff options
Diffstat (limited to 'src/libixion/formula_parser.cpp')
-rw-r--r-- | src/libixion/formula_parser.cpp | 268 |
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: */ |