summaryrefslogtreecommitdiffstats
path: root/src/libixion/formula_interpreter.hpp
blob: 73721f603bbf91e1c4d8245570974bb30e3f11f2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
/* -*- 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/.
 */

#ifndef INCLUDED_IXION_FORMULA_INTERPRETER_HPP
#define INCLUDED_IXION_FORMULA_INTERPRETER_HPP

#include "ixion/global.hpp"
#include "ixion/formula_tokens.hpp"
#include "ixion/formula_result.hpp"

#include "formula_value_stack.hpp"

#include <sstream>
#include <unordered_set>
#include <deque>

namespace ixion {

class formula_cell;

namespace iface {

class session_handler;

}

/**
 * The formula interpreter parses a series of formula tokens representing a
 * formula expression, and calculates the result of that expression.
 *
 * <p>Intermediate result of each handler is pushed onto the stack and
 * popped from it for the calling method to retrieve.  By the end of the
 * interpretation there should only be one result left on the stack which is
 * the final result of the interpretation of the expression.  The number of
 * intermediate results (or stack values) on the stack is normally one at
 * the end of each handler, except for the function handler where the number
 * of stack values may be more than one when the function may take more than
 * one argument.</p>
 */
class formula_interpreter
{
    using name_set = std::unordered_set<std::string>;
    using fv_stacks_type = std::deque<formula_value_stack>;

public:
    typedef ::std::vector<const formula_token*> local_tokens_type;

    formula_interpreter() = delete;
    formula_interpreter(const formula_interpreter&) = delete;
    formula_interpreter& operator= (formula_interpreter) = delete;

    formula_interpreter(const formula_cell* cell, model_context& cxt);
    ~formula_interpreter();

    void set_origin(const abs_address_t& pos);
    bool interpret();
    formula_result transfer_result();
    formula_error_t get_error() const;

private:
    /**
     * Expand all named expressions into a flat set of tokens.  This is also
     * where we detect circular referencing of named expressions.
     */
    void init_tokens();

    void pop_result();

    void expand_named_expression(const named_expression_t* expr, name_set& used_names);

    void ensure_token_exists() const;
    bool has_token() const;
    void next();
    const formula_token& token() const;
    const formula_token& token_or_throw() const;
    const formula_token& next_token();
    const std::string& string_or_throw() const;

    // The following methods are handlers.  In each handler, the initial
    // position is always set to the first unprocessed token.  Each handler is
    // responsible for setting the token position to the next unprocessed
    // position when it finishes.

    void expression();
    void term();
    void factor();
    bool sign();
    void paren();
    void single_ref();
    void range_ref();
    void table_ref();
    void constant();
    void literal();
    void array();
    void function();

    void clear_stacks();
    void push_stack();
    void pop_stack();

    formula_value_stack& get_stack();

private:
    const formula_cell* m_parent_cell;
    model_context& m_context;
    std::unique_ptr<iface::session_handler> mp_handler;
    abs_address_t m_pos;

    fv_stacks_type m_stacks;
    local_tokens_type m_tokens;
    local_tokens_type::const_iterator m_cur_token_itr;
    local_tokens_type::const_iterator m_end_token_pos;

    formula_result m_result;
    formula_error_t m_error;
};

}

#endif
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */