Calculator.cc revision 10447:a465576671d4
1#include "Calculator.h"
2
3#include <cctype>
4#include <iostream>
5
6namespace LibUtil
7{
8    using std::cout;
9    using std::endl;
10    using std::scientific;
11
12    Calculator::Calculator()
13    {
14        m_reserved_chars_ = "+-*/;=()\\";
15    }
16
17    Calculator::~Calculator()
18    {}
19
20    void Calculator::reset()
21    {
22        m_var_.clear();
23        return;
24    }
25
26    void Calculator::evaluateString(const String& str_)
27    {
28        istringstream ist(str_);
29        while(ist)
30        {
31            getToken(ist);
32            if(m_curr_token_ == END) break;
33            if(m_curr_token_ == SEP) continue;
34            if((m_curr_token_ == NAME) && (m_value_string_ == "print"))
35            {
36                getToken(ist);
37
38                if(m_curr_token_ == STRING)
39                {
40                    String print_str = m_value_string_;
41
42                    getToken(ist);
43                    if(m_curr_token_ == SEP)
44                    {
45                        cout << print_str << endl;
46                    }
47                    else
48                    {
49                        double v = expr(ist, false);
50                        cout << scientific << print_str << v << endl;
51                    }
52                }
53                else
54                {
55                    double v = expr(ist, false);
56                    cout << scientific << v << endl;
57                }
58            }
59            else
60            {
61                expr(ist, false);
62            }
63        }
64        return;
65    }
66
67    Calculator::Token Calculator::getToken(istringstream& ist_)
68    {
69        char ch;
70        do
71        {
72            ist_.get(ch);
73            if(!ist_)
74            {
75                m_curr_token_ = END;
76                return m_curr_token_;
77            }
78        }
79        while(ch != '\n' && isspace(ch));
80
81        switch(ch)
82        {
83            case '\n':
84                m_curr_token_ = END;
85                return m_curr_token_;
86            case ';':
87                m_curr_token_ = SEP;
88                return m_curr_token_;
89            case '*':
90            case '/':
91            case '+':
92            case '-':
93            case '(':
94            case ')':
95            case '=':
96                m_curr_token_ = Token(ch);
97                return m_curr_token_;
98            case '0': case '1': case '2': case '3': case '4':
99            case '5': case '6': case '7': case '8': case '9':
100            case '.':
101                ist_.putback(ch);
102                ist_ >> m_value_number_;
103                m_curr_token_ = NUMBER;
104                return m_curr_token_;
105            case '"':
106                ist_.get(ch);
107                m_value_string_ = "";
108                while(ist_ && ('"' != ch))
109                {
110                    m_value_string_ += String(1, ch);
111                    ist_.get(ch);
112                }
113                m_curr_token_ = STRING;
114                return m_curr_token_;
115            case '$':
116                ist_.get(ch);
117                ASSERT((ch == '('), "[Error] Bad token: '(' expected");
118                ist_.get(ch);
119                m_value_string_ = "";
120                while(ist_ && (!isspace(ch)) && (')' != ch))
121                {
122                    m_value_string_ += String(1, ch);
123                    ist_.get(ch);
124                }
125                m_curr_token_ = NAME2;
126                return m_curr_token_;
127            default:
128                if(isalpha(ch))
129                {
130                    m_value_string_ = ch;
131                    ist_.get(ch);
132                    while(ist_ && (isalnum(ch) || ('_' == ch)))
133                    {
134                        m_value_string_ += String(1, ch);
135                        ist_.get(ch);
136                    }
137                    ist_.putback(ch);
138                    m_curr_token_ = NAME;
139                    return m_curr_token_;
140                }
141                else
142                {
143                    String error_msg = "[Error] Bad token: '" + String(ch) + "'";
144                    throw Exception(error_msg);
145                }
146        }
147    }
148
149    double Calculator::prim(istringstream& ist_, bool is_get_)
150    {
151        if(is_get_)
152        {
153            getToken(ist_);
154        }
155
156        double v;
157        switch(m_curr_token_)
158        {
159            case NUMBER:
160                v = m_value_number_;
161                getToken(ist_);
162                return v;
163            case NAME:
164                if(getToken(ist_) == ASSIGN)
165                {
166                    String var_name = m_value_string_;
167                    v = expr(ist_, true);
168                    m_var_.set(var_name, v);
169                }
170                else
171                {
172                    v = m_var_.get(m_value_string_);
173                }
174                return v;
175            case NAME2:
176                v = getEnvVar(m_value_string_);
177                getToken(ist_);
178                return v;
179            case MINUS:
180                return -prim(ist_, true);
181            case LP:
182                v = expr(ist_, true);
183                ASSERT((m_curr_token_ == RP), "[Error] ')' expected");
184                getToken(ist_);
185                return v;
186            default:
187                ASSERT(0, "[Error] primary expected, get: '" + String(int(m_curr_token_)) + "'");
188        }
189    }
190
191    double Calculator::term(istringstream& ist_, bool is_get_)
192    {
193        double left = prim(ist_, is_get_);
194
195        while(1)
196        {
197            double d;
198            switch(m_curr_token_)
199            {
200                case MUL:
201                    left *= prim(ist_, true);
202                    break;
203                case DIV:
204                    d = prim(ist_, true);
205                    ASSERT(d, "[Error] divided by 0");
206                    left /= d;
207                    break;
208                default:
209                    return left;
210            }
211        }
212    }
213
214    double Calculator::expr(istringstream& ist_, bool is_get_)
215    {
216        double left = term(ist_, is_get_);
217
218        while(1)
219        {
220            switch(m_curr_token_)
221            {
222                case PLUS:
223                    left += term(ist_, true);
224                    break;
225                case MINUS:
226                    left -= term(ist_, true);
227                    break;
228                default:
229                    return left;
230            }
231        }
232    }
233
234    double Calculator::getEnvVar(const String& var_name_) const
235    {
236        return m_var_.get(var_name_);
237    }
238} // namespace LibUtil
239
240