/* * rpn.cpp * * Copyright 2013 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301, USA. * * */ #include #ifdef WIN32 #include #else #define _TCHAR char #define _tmain main #endif #include #include #include #include #include using namespace std; #include "stack.h" static const char CURSOR[] = "> "; static const char SHOW_STACK_SEPARATOR[] = ":\t"; static int g_verbose = 0; typedef enum { ret_ok, ret_unknown_err, ret_missing_operand, ret_bad_operand_type, ret_unknown_variable, ret_internal, ret_deadly, ret_good_bye, ret_not_impl, ret_nop, ret_syntax, ret_max } ret_value; const char* ret_value_string[ret_max] = { "ok", "unknown command", "missing operand", "bad operand type", "unknown variable", "internal error, aborting", "deadly", "goodbye", "not implemented", "no operation", "syntax" }; typedef enum { cmd_undef, cmd_number,/* floating value to put in stack */ cmd_symbol,/* symbol value to put in stack */ cmd_keyword,/* langage keyword */ cmd_branch,/* langage branch keyword */ cmd_max } cmd_type_t; const char* cmd_type_string[cmd_max] = { "undef", "number", "symbol", "keyword", "keyword" }; // typedef long double floating_t; class program; class object; class branch; typedef void (program::*program_fn_t)(void); typedef union { program_fn_t _fn; } operand; typedef int (program::*branch_fn_t)(branch&); // class object { public: cmd_type_t _type;// object type object(cmd_type_t type = cmd_undef):_type(type) { } virtual void show(ostream& stream = cout) { } }; class number : public object { public: number(floating_t value) : object(cmd_number) { _value = value; } virtual void show(ostream& stream = cout) { stream << _value; } floating_t _value; }; class symbol : public object { public: symbol(string& name, cmd_type_t type = cmd_symbol) : object(type), _name(name),_auto_eval(false) { } virtual void show(ostream& stream = cout) { stream << "'" << _name << "'"; } string _name; bool _auto_eval; }; class keyword : public symbol { public: keyword(program_fn_t fn, string& name, cmd_type_t type = cmd_keyword) : symbol(name, type) { _fn = fn; } program_fn_t _fn; virtual void show(ostream& stream = cout) { stream << _name; } }; class branch : public keyword { public: branch(branch_fn_t fn, string& name) : keyword(NULL, name, cmd_branch), arg1(-1), arg2(-1), arg3(-1), arg_bool(false) { _type = cmd_branch; _fn = fn; } // branch function branch_fn_t _fn; // args used by cmd_branch cmds int arg1, arg2, arg3; floating_t farg1, farg2; bool arg_bool; }; class program : public stack { public: program() { } // run this program ret_value run(stack& stk, heap& hp) { bool go_out = false; ret_value ret = ret_ok; cmd_type_t type; _stack = &stk; _heap = &hp; _err = ret_ok; _err_context = ""; // branches for 'if' ret = preprocess(); if (ret != ret_ok) return ret; // iterate commands for(int i = 0; (go_out==false) && (i<(int)size());) { type = (cmd_type_t)seq_type(i); // if (g_verbose >= 1) { cout << "(" << i << ") "; ((object*)seq_obj(i))->show(); cout << endl; } // not a command, but a stack entry, manage it if (type == cmd_number) { stk.push_back(seq_obj(i), seq_len(i), type); i++; } // could be an auto-evaluated symbol else if (type == cmd_symbol) { auto_rcl((symbol*)seq_obj(i)); i++; } // a keyword else if (type == cmd_keyword) { keyword* k = (keyword*)seq_obj(i); // call matching function (this->*(k->_fn))(); switch(_err) { // no pb -> go on case ret_ok: break; // explicit go out software case ret_good_bye: go_out = true; ret = ret_good_bye; break; default: // error: abort prog go_out = true; // error: show it if (show_error(_err, _err_context) == ret_deadly) { // pb showing error -> go out software ret = ret_good_bye; } break; } i++; } // a branch keyword else if (type == cmd_branch) { // call matching function branch* b = (branch*)seq_obj(i); int tmp = (this->*(b->_fn))(*b); if (tmp == -1) i++; else i = tmp; } } return ret; } ret_value preprocess(void) { struct if_layout_t { if_layout_t():index_then(-1),index_else(-1),index_end(-1) { } int index_if; int index_then; int index_else; int index_end; }; // for if-then-else-end vector vlayout; int layout_index=-1;// TODO remplaçable par vlayout.size()-1 //for start-end-step vector vstartindex; // analyse if-then-else-end branches // analyse start-{next, step} branches for(int i=0; i<(int)size(); i++) { int type = seq_type(i); if (type == cmd_keyword) { keyword* k = (keyword*)seq_obj(i); if(k->_name.compare("end") == 0) { int next = i + 1; if (next >= (int)size()) next = -1; if (layout_index<0) { // error: show it show_syntax_error("missing if before end"); return ret_syntax; } if (vlayout[layout_index].index_end != -1) { // error: show it show_syntax_error("duplicate end"); return ret_syntax; } ((branch*)seq_obj(i))->arg1 = next;//fill branch1 if (vlayout[layout_index].index_else != -1) //fill 'end' branch of 'else' ((branch*)seq_obj(vlayout[layout_index].index_else))->arg2 = i; else //fill 'end' branch of 'then' ((branch*)seq_obj(vlayout[layout_index].index_then))->arg2 = i; layout_index--; } } else if (type == cmd_branch) { branch* k = (branch*)seq_obj(i); if (k->_name.compare("if") == 0) { if_layout_t layout; layout.index_if = i; vlayout.push_back(layout); layout_index++; } else if(k->_name.compare("then") == 0) { int next = i + 1; if (next >= (int)size()) next = -1; // nothing after 'then' -> error if (next == -1) { // error: show it show_syntax_error("missing end after then"); return ret_syntax; } if (layout_index<0) { // error: show it show_syntax_error("missing if before then"); return ret_syntax; } if (vlayout[layout_index].index_then != -1) { // error: show it show_syntax_error("duplicate then"); return ret_syntax; } vlayout[layout_index].index_then = i; k->arg1 = next; k->arg3 = vlayout[layout_index].index_if; } else if(k->_name.compare("else") == 0) { int next = i + 1; if (next >= (int)size()) next = -1; // nothing after 'else' -> error if (next == -1) { // error: show it show_syntax_error("missing end after else"); return ret_syntax; } if (layout_index<0) { // error: show it show_syntax_error("missing if before else"); return ret_syntax; } if (vlayout[layout_index].index_then == -1) { // error: show it show_syntax_error("missing then before else"); return ret_syntax; } if (vlayout[layout_index].index_else != -1) { // error: show it show_syntax_error("duplicate else"); return ret_syntax; } vlayout[layout_index].index_else = i; k->arg1 = next;// fill branch1 (if was false) of 'else' k->arg3 = vlayout[layout_index].index_if; ((branch*)seq_obj(vlayout[layout_index].index_then))->arg2 = next;// fill branch2 (if was false) of 'then' } else if(k->_name.compare("start") == 0) { vstartindex.push_back(i); } else if(k->_name.compare("for") == 0) { vstartindex.push_back(i); k->arg1 = i + 1;// arg1 points on symbol variable } else if(k->_name.compare("next") == 0) { if (vstartindex.size() == 0) { // error: show it show_syntax_error("missing start or for before next"); return ret_syntax; } k->arg1 = vstartindex[vstartindex.size() - 1];// fill 'next' branch1 = 'start' index vstartindex.pop_back(); } else if(k->_name.compare("step") == 0) { if (vstartindex.size() == 0) { // error: show it show_syntax_error("missing start or for before step"); return ret_syntax; } k->arg1 = vstartindex[vstartindex.size() - 1];// fill 'step' branch1 = 'start' index vstartindex.pop_back(); } } } if (layout_index >= 0) { // error: show it show_syntax_error("missing end"); return ret_syntax; } if (vstartindex.size() > 0) { // error: show it show_syntax_error("missing next or step after for or start"); return ret_syntax; } return ret_ok; } static ret_value show_error(ret_value err, string& context) { cerr<0) && (fn_name == _keywords[i].name)) { fn = _keywords[i].fn; type = _keywords[i].type; return ret_ok; } } return ret_unknown_err; } // interactive entry and decoding static ret_value entry(program& prog) { ret_value ret; string entry; // show cursor cout<> sub; isub.str(sub); // check whether it is a number isub >> num; if ( (!isub.fail()) && (!isub.bad()) ) { // found a number prog.push_back(&number(num), sizeof(number), cmd_number); if (isub.good()) { // plus another command without space program_fn_t fn; cmd_type_t type; isub >> sub; if (prog.get_fn(sub, fn, type) != ret_ok) program::show_error(ret_unknown_err, sub); else { if (type == cmd_keyword) prog.push_back(&keyword(fn, sub), sizeof(keyword), cmd_keyword); else if (type == cmd_branch) prog.push_back(&branch((branch_fn_t)fn, sub), sizeof(branch), cmd_branch); } } } else { if (sub.size()>0) { // not a number ? // could be a symbol if (sub.substr(0,1) == "'") { // syntax should be 'symbol' if (sub.substr(sub.size()-1, 1) == "'") prog.push_back(&symbol(sub.substr(1, sub.size()-2)), sizeof(symbol), cmd_symbol); // or 'symbol without ending ' only if last entry else { char c; do { stream.get(c); if (stream.eof()) break; sub += c; } while(c != '\''); if (stream.eof()) prog.push_back(&symbol(sub.substr(1, sub.size()-1)), sizeof(symbol), cmd_symbol); else prog.push_back(&symbol(sub.substr(1, sub.size()-2)), sizeof(symbol), cmd_symbol); } } else { program_fn_t fn; cmd_type_t type; // could be a command if (prog.get_fn(sub, fn, type) == ret_ok) { if (type == cmd_keyword) prog.push_back(&keyword(fn, sub), sizeof(keyword), cmd_keyword); else if (type == cmd_branch) prog.push_back(&branch((branch_fn_t)fn, sub), sizeof(branch), cmd_branch); } else { // no, so it is counted as an auto-evaluated symbol symbol sym(sub); sym._auto_eval = true; prog.push_back(&sym, sizeof(symbol), cmd_symbol); } } } } } while (!stream.eof()); // particular : check 'for' command is followed by a symbol, with auto-evaluated syntax (i.e. without ') //TODO /* for(int i = 0; i < (int)prog.size(); i++) { if ((prog.seq_type(i) == cmd_branch) && ()) } */ return ret; } static void show_stack(stack& st) { if (st.size() == 1) { ((object*)st.back())->show(); cout<=0; i--) { cout<show(); cout<back())->_value; _stack->pop_back(); return a; } void putf(floating_t value) { /* warning, caller must check object type before */ _stack->push_back(&number(value), sizeof(number), cmd_number); } string getn() { /* warning, caller must check object type before */ string a = ((symbol*)_stack->back())->_name; _stack->pop_back(); return a; } void putn(string& a) { /* warning, caller must check object type before */ _stack->push_back(&symbol(a), sizeof(symbol), cmd_symbol); } int stack_size() { return _stack->size(); } private: // carefull : some of these macros modify program flow #define ERR_CONTEXT(err) do { _err = (err); _err_context = __FUNCTION__; } while(0) #define MIN_ARGUMENTS(num) do { if (stack_size()<(num)) { ERR_CONTEXT(ret_missing_operand); return; } } while(0) #define MIN_ARGUMENTS_RET(num, ret) do { if (stack_size()<(num)) { ERR_CONTEXT(ret_missing_operand); return (ret); } } while(0) #define ARG_IS_OF_TYPE(num, type) do { if (_stack->get_type(num) != (type)) { ERR_CONTEXT(ret_bad_operand_type); return; } } while(0) #define ARG_IS_OF_TYPE_RET(num, type, ret) do { if (_stack->get_type(num) != (type)) { ERR_CONTEXT(ret_bad_operand_type); return (ret); } } while(0) // keywords implementation #include "rpn-general.h" #include "rpn-algebra.h" #include "rpn-test.h" #include "rpn-stack.h" #include "rpn-branch.h" #include "rpn-store.h" #include "rpn-trig.h" #include "rpn-logs.h" }; //keywords declaration #include "rpn-cmd.h" #include "rpn-general-core.h" // int _tmain(int argc, _TCHAR* argv[]) { heap hp; stack st; // cout << setprecision(16); // for (;;) { program prog; if (program::entry(prog) == ret_good_bye) break; else { if (prog.run(st, hp) == ret_good_bye) break; else program::show_stack(st); } } return 0; }