110448Snilay@cs.wisc.edu/* Copyright (c) 2012 Massachusetts Institute of Technology
210448Snilay@cs.wisc.edu *
310448Snilay@cs.wisc.edu * Permission is hereby granted, free of charge, to any person obtaining a copy
410448Snilay@cs.wisc.edu * of this software and associated documentation files (the "Software"), to deal
510448Snilay@cs.wisc.edu * in the Software without restriction, including without limitation the rights
610448Snilay@cs.wisc.edu * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
710448Snilay@cs.wisc.edu * copies of the Software, and to permit persons to whom the Software is
810448Snilay@cs.wisc.edu * furnished to do so, subject to the following conditions:
910448Snilay@cs.wisc.edu *
1010448Snilay@cs.wisc.edu * The above copyright notice and this permission notice shall be included in
1110448Snilay@cs.wisc.edu * all copies or substantial portions of the Software.
1210448Snilay@cs.wisc.edu *
1310448Snilay@cs.wisc.edu * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1410448Snilay@cs.wisc.edu * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1510448Snilay@cs.wisc.edu * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1610448Snilay@cs.wisc.edu * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1710448Snilay@cs.wisc.edu * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
1810448Snilay@cs.wisc.edu * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
1910448Snilay@cs.wisc.edu * THE SOFTWARE.
2010448Snilay@cs.wisc.edu */
2110448Snilay@cs.wisc.edu
2210447Snilay@cs.wisc.edu#include "Calculator.h"
2310447Snilay@cs.wisc.edu
2410447Snilay@cs.wisc.edu#include <cctype>
2510447Snilay@cs.wisc.edu#include <iostream>
2610447Snilay@cs.wisc.edu
2710447Snilay@cs.wisc.edunamespace LibUtil
2810447Snilay@cs.wisc.edu{
2910447Snilay@cs.wisc.edu    using std::cout;
3010447Snilay@cs.wisc.edu    using std::endl;
3110447Snilay@cs.wisc.edu    using std::scientific;
3210447Snilay@cs.wisc.edu
3310447Snilay@cs.wisc.edu    Calculator::Calculator()
3410447Snilay@cs.wisc.edu    {
3510447Snilay@cs.wisc.edu        m_reserved_chars_ = "+-*/;=()\\";
3610447Snilay@cs.wisc.edu    }
3710447Snilay@cs.wisc.edu
3810447Snilay@cs.wisc.edu    Calculator::~Calculator()
3910447Snilay@cs.wisc.edu    {}
4010447Snilay@cs.wisc.edu
4110447Snilay@cs.wisc.edu    void Calculator::reset()
4210447Snilay@cs.wisc.edu    {
4310447Snilay@cs.wisc.edu        m_var_.clear();
4410447Snilay@cs.wisc.edu        return;
4510447Snilay@cs.wisc.edu    }
4610447Snilay@cs.wisc.edu
4710448Snilay@cs.wisc.edu    void Calculator::evaluateString(const String& str_,
4810448Snilay@cs.wisc.edu                                    const map<String, String> &config,
4910448Snilay@cs.wisc.edu                                    DSENT::Model *ms_model,
5010448Snilay@cs.wisc.edu                                    map<string, double> &outputs)
5110447Snilay@cs.wisc.edu    {
5210447Snilay@cs.wisc.edu        istringstream ist(str_);
5310448Snilay@cs.wisc.edu
5410447Snilay@cs.wisc.edu        while(ist)
5510447Snilay@cs.wisc.edu        {
5610447Snilay@cs.wisc.edu            getToken(ist);
5710447Snilay@cs.wisc.edu            if(m_curr_token_ == END) break;
5810447Snilay@cs.wisc.edu            if(m_curr_token_ == SEP) continue;
5910447Snilay@cs.wisc.edu            if((m_curr_token_ == NAME) && (m_value_string_ == "print"))
6010447Snilay@cs.wisc.edu            {
6110447Snilay@cs.wisc.edu                getToken(ist);
6210447Snilay@cs.wisc.edu
6310447Snilay@cs.wisc.edu                if(m_curr_token_ == STRING)
6410447Snilay@cs.wisc.edu                {
6510447Snilay@cs.wisc.edu                    String print_str = m_value_string_;
6610447Snilay@cs.wisc.edu
6710447Snilay@cs.wisc.edu                    getToken(ist);
6810447Snilay@cs.wisc.edu                    if(m_curr_token_ == SEP)
6910447Snilay@cs.wisc.edu                    {
7010448Snilay@cs.wisc.edu                        outputs[print_str] = 0;
7110447Snilay@cs.wisc.edu                        cout << print_str << endl;
7210447Snilay@cs.wisc.edu                    }
7310447Snilay@cs.wisc.edu                    else
7410447Snilay@cs.wisc.edu                    {
7510448Snilay@cs.wisc.edu                        double v = expr(ist, false, config, ms_model);
7610448Snilay@cs.wisc.edu                        outputs[print_str] = v;
7710448Snilay@cs.wisc.edu                        cout << print_str << v << endl;
7810447Snilay@cs.wisc.edu                    }
7910447Snilay@cs.wisc.edu                }
8010447Snilay@cs.wisc.edu                else
8110447Snilay@cs.wisc.edu                {
8210448Snilay@cs.wisc.edu                    double v = expr(ist, false, config, ms_model);
8310448Snilay@cs.wisc.edu                    outputs["Missing Expression"] = v;
8410448Snilay@cs.wisc.edu                    cout << v << endl;
8510447Snilay@cs.wisc.edu                }
8610447Snilay@cs.wisc.edu            }
8710447Snilay@cs.wisc.edu            else
8810447Snilay@cs.wisc.edu            {
8910448Snilay@cs.wisc.edu                expr(ist, false, config, ms_model);
9010447Snilay@cs.wisc.edu            }
9110447Snilay@cs.wisc.edu        }
9210447Snilay@cs.wisc.edu    }
9310447Snilay@cs.wisc.edu
9410447Snilay@cs.wisc.edu    Calculator::Token Calculator::getToken(istringstream& ist_)
9510447Snilay@cs.wisc.edu    {
9610447Snilay@cs.wisc.edu        char ch;
9710447Snilay@cs.wisc.edu        do
9810447Snilay@cs.wisc.edu        {
9910447Snilay@cs.wisc.edu            ist_.get(ch);
10010447Snilay@cs.wisc.edu            if(!ist_)
10110447Snilay@cs.wisc.edu            {
10210447Snilay@cs.wisc.edu                m_curr_token_ = END;
10310447Snilay@cs.wisc.edu                return m_curr_token_;
10410447Snilay@cs.wisc.edu            }
10510447Snilay@cs.wisc.edu        }
10610447Snilay@cs.wisc.edu        while(ch != '\n' && isspace(ch));
10710447Snilay@cs.wisc.edu
10810447Snilay@cs.wisc.edu        switch(ch)
10910447Snilay@cs.wisc.edu        {
11010447Snilay@cs.wisc.edu            case '\n':
11110447Snilay@cs.wisc.edu                m_curr_token_ = END;
11210447Snilay@cs.wisc.edu                return m_curr_token_;
11310447Snilay@cs.wisc.edu            case ';':
11410447Snilay@cs.wisc.edu                m_curr_token_ = SEP;
11510447Snilay@cs.wisc.edu                return m_curr_token_;
11610447Snilay@cs.wisc.edu            case '*':
11710447Snilay@cs.wisc.edu            case '/':
11810447Snilay@cs.wisc.edu            case '+':
11910447Snilay@cs.wisc.edu            case '-':
12010447Snilay@cs.wisc.edu            case '(':
12110447Snilay@cs.wisc.edu            case ')':
12210447Snilay@cs.wisc.edu            case '=':
12310447Snilay@cs.wisc.edu                m_curr_token_ = Token(ch);
12410447Snilay@cs.wisc.edu                return m_curr_token_;
12510447Snilay@cs.wisc.edu            case '0': case '1': case '2': case '3': case '4':
12610447Snilay@cs.wisc.edu            case '5': case '6': case '7': case '8': case '9':
12710447Snilay@cs.wisc.edu            case '.':
12810447Snilay@cs.wisc.edu                ist_.putback(ch);
12910447Snilay@cs.wisc.edu                ist_ >> m_value_number_;
13010447Snilay@cs.wisc.edu                m_curr_token_ = NUMBER;
13110447Snilay@cs.wisc.edu                return m_curr_token_;
13210447Snilay@cs.wisc.edu            case '"':
13310447Snilay@cs.wisc.edu                ist_.get(ch);
13410447Snilay@cs.wisc.edu                m_value_string_ = "";
13510447Snilay@cs.wisc.edu                while(ist_ && ('"' != ch))
13610447Snilay@cs.wisc.edu                {
13710447Snilay@cs.wisc.edu                    m_value_string_ += String(1, ch);
13810447Snilay@cs.wisc.edu                    ist_.get(ch);
13910447Snilay@cs.wisc.edu                }
14010447Snilay@cs.wisc.edu                m_curr_token_ = STRING;
14110447Snilay@cs.wisc.edu                return m_curr_token_;
14210447Snilay@cs.wisc.edu            case '$':
14310447Snilay@cs.wisc.edu                ist_.get(ch);
14410447Snilay@cs.wisc.edu                ASSERT((ch == '('), "[Error] Bad token: '(' expected");
14510447Snilay@cs.wisc.edu                ist_.get(ch);
14610447Snilay@cs.wisc.edu                m_value_string_ = "";
14710447Snilay@cs.wisc.edu                while(ist_ && (!isspace(ch)) && (')' != ch))
14810447Snilay@cs.wisc.edu                {
14910447Snilay@cs.wisc.edu                    m_value_string_ += String(1, ch);
15010447Snilay@cs.wisc.edu                    ist_.get(ch);
15110447Snilay@cs.wisc.edu                }
15210447Snilay@cs.wisc.edu                m_curr_token_ = NAME2;
15310447Snilay@cs.wisc.edu                return m_curr_token_;
15410447Snilay@cs.wisc.edu            default:
15510447Snilay@cs.wisc.edu                if(isalpha(ch))
15610447Snilay@cs.wisc.edu                {
15710447Snilay@cs.wisc.edu                    m_value_string_ = ch;
15810447Snilay@cs.wisc.edu                    ist_.get(ch);
15910447Snilay@cs.wisc.edu                    while(ist_ && (isalnum(ch) || ('_' == ch)))
16010447Snilay@cs.wisc.edu                    {
16110447Snilay@cs.wisc.edu                        m_value_string_ += String(1, ch);
16210447Snilay@cs.wisc.edu                        ist_.get(ch);
16310447Snilay@cs.wisc.edu                    }
16410447Snilay@cs.wisc.edu                    ist_.putback(ch);
16510447Snilay@cs.wisc.edu                    m_curr_token_ = NAME;
16610447Snilay@cs.wisc.edu                    return m_curr_token_;
16710447Snilay@cs.wisc.edu                }
16810447Snilay@cs.wisc.edu                else
16910447Snilay@cs.wisc.edu                {
17010447Snilay@cs.wisc.edu                    String error_msg = "[Error] Bad token: '" + String(ch) + "'";
17110447Snilay@cs.wisc.edu                    throw Exception(error_msg);
17210447Snilay@cs.wisc.edu                }
17310447Snilay@cs.wisc.edu        }
17410447Snilay@cs.wisc.edu    }
17510447Snilay@cs.wisc.edu
17610448Snilay@cs.wisc.edu    double Calculator::prim(istringstream& ist_, bool is_get_,
17710448Snilay@cs.wisc.edu                            const map<String, String> &config,
17810448Snilay@cs.wisc.edu                            DSENT::Model *ms_model)
17910447Snilay@cs.wisc.edu    {
18010447Snilay@cs.wisc.edu        if(is_get_)
18110447Snilay@cs.wisc.edu        {
18210447Snilay@cs.wisc.edu            getToken(ist_);
18310447Snilay@cs.wisc.edu        }
18410447Snilay@cs.wisc.edu
18510447Snilay@cs.wisc.edu        double v;
18610447Snilay@cs.wisc.edu        switch(m_curr_token_)
18710447Snilay@cs.wisc.edu        {
18810447Snilay@cs.wisc.edu            case NUMBER:
18910447Snilay@cs.wisc.edu                v = m_value_number_;
19010447Snilay@cs.wisc.edu                getToken(ist_);
19110447Snilay@cs.wisc.edu                return v;
19210447Snilay@cs.wisc.edu            case NAME:
19310447Snilay@cs.wisc.edu                if(getToken(ist_) == ASSIGN)
19410447Snilay@cs.wisc.edu                {
19510447Snilay@cs.wisc.edu                    String var_name = m_value_string_;
19610448Snilay@cs.wisc.edu                    v = expr(ist_, true, config, ms_model);
19710447Snilay@cs.wisc.edu                    m_var_.set(var_name, v);
19810447Snilay@cs.wisc.edu                }
19910447Snilay@cs.wisc.edu                else
20010447Snilay@cs.wisc.edu                {
20110447Snilay@cs.wisc.edu                    v = m_var_.get(m_value_string_);
20210447Snilay@cs.wisc.edu                }
20310447Snilay@cs.wisc.edu                return v;
20410447Snilay@cs.wisc.edu            case NAME2:
20510448Snilay@cs.wisc.edu                v = getEnvVar(m_value_string_, config, ms_model);
20610447Snilay@cs.wisc.edu                getToken(ist_);
20710447Snilay@cs.wisc.edu                return v;
20810447Snilay@cs.wisc.edu            case MINUS:
20910448Snilay@cs.wisc.edu                return -prim(ist_, true, config, ms_model);
21010447Snilay@cs.wisc.edu            case LP:
21110448Snilay@cs.wisc.edu                v = expr(ist_, true, config, ms_model);
21210447Snilay@cs.wisc.edu                ASSERT((m_curr_token_ == RP), "[Error] ')' expected");
21310447Snilay@cs.wisc.edu                getToken(ist_);
21410447Snilay@cs.wisc.edu                return v;
21510447Snilay@cs.wisc.edu            default:
21610447Snilay@cs.wisc.edu                ASSERT(0, "[Error] primary expected, get: '" + String(int(m_curr_token_)) + "'");
21710447Snilay@cs.wisc.edu        }
21810447Snilay@cs.wisc.edu    }
21910447Snilay@cs.wisc.edu
22010448Snilay@cs.wisc.edu    double Calculator::term(istringstream& ist_, bool is_get_,
22110448Snilay@cs.wisc.edu                            const map<String, String> &config,
22210448Snilay@cs.wisc.edu                            DSENT::Model *ms_model)
22310447Snilay@cs.wisc.edu    {
22410448Snilay@cs.wisc.edu        double left = prim(ist_, is_get_, config, ms_model);
22510447Snilay@cs.wisc.edu
22610447Snilay@cs.wisc.edu        while(1)
22710447Snilay@cs.wisc.edu        {
22810447Snilay@cs.wisc.edu            double d;
22910447Snilay@cs.wisc.edu            switch(m_curr_token_)
23010447Snilay@cs.wisc.edu            {
23110447Snilay@cs.wisc.edu                case MUL:
23210448Snilay@cs.wisc.edu                    left *= prim(ist_, true, config, ms_model);
23310447Snilay@cs.wisc.edu                    break;
23410447Snilay@cs.wisc.edu                case DIV:
23510448Snilay@cs.wisc.edu                    d = prim(ist_, true, config, ms_model);
23610447Snilay@cs.wisc.edu                    ASSERT(d, "[Error] divided by 0");
23710447Snilay@cs.wisc.edu                    left /= d;
23810447Snilay@cs.wisc.edu                    break;
23910447Snilay@cs.wisc.edu                default:
24010447Snilay@cs.wisc.edu                    return left;
24110447Snilay@cs.wisc.edu            }
24210447Snilay@cs.wisc.edu        }
24310447Snilay@cs.wisc.edu    }
24410447Snilay@cs.wisc.edu
24510448Snilay@cs.wisc.edu    double Calculator::expr(istringstream& ist_, bool is_get_,
24610448Snilay@cs.wisc.edu                            const map<String, String> &config,
24710448Snilay@cs.wisc.edu                            DSENT::Model *ms_model)
24810447Snilay@cs.wisc.edu    {
24910448Snilay@cs.wisc.edu        double left = term(ist_, is_get_, config, ms_model);
25010447Snilay@cs.wisc.edu
25110447Snilay@cs.wisc.edu        while(1)
25210447Snilay@cs.wisc.edu        {
25310447Snilay@cs.wisc.edu            switch(m_curr_token_)
25410447Snilay@cs.wisc.edu            {
25510447Snilay@cs.wisc.edu                case PLUS:
25610448Snilay@cs.wisc.edu                    left += term(ist_, true, config, ms_model);
25710447Snilay@cs.wisc.edu                    break;
25810447Snilay@cs.wisc.edu                case MINUS:
25910448Snilay@cs.wisc.edu                    left -= term(ist_, true, config, ms_model);
26010447Snilay@cs.wisc.edu                    break;
26110447Snilay@cs.wisc.edu                default:
26210447Snilay@cs.wisc.edu                    return left;
26310447Snilay@cs.wisc.edu            }
26410447Snilay@cs.wisc.edu        }
26510447Snilay@cs.wisc.edu    }
26610447Snilay@cs.wisc.edu
26710448Snilay@cs.wisc.edu    double Calculator::getEnvVar(const String& var_name_,
26810448Snilay@cs.wisc.edu                                 const map<String, String> &config,
26910448Snilay@cs.wisc.edu                                 DSENT::Model *ms_model) const
27010447Snilay@cs.wisc.edu    {
27110447Snilay@cs.wisc.edu        return m_var_.get(var_name_);
27210447Snilay@cs.wisc.edu    }
27310447Snilay@cs.wisc.edu} // namespace LibUtil
274