From 4f0d9c0d27ff2725182ca6c0e1844a5c0935da1d Mon Sep 17 00:00:00 2001
From: Louis Rubet <louis@rubet.fr>
Date: Thu, 25 May 2017 19:00:44 +0200
Subject: [PATCH] #72: added by refactoring

---
 src/main.cpp    |  63 +++++++
 src/object.cpp  |  55 ++++++
 src/object.hpp  | 290 +++++++++++++++++++++++++++++
 src/program.cpp | 141 ++++++++++++++
 src/program.hpp | 479 ++++++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 1028 insertions(+)
 create mode 100644 src/main.cpp
 create mode 100644 src/object.cpp
 create mode 100644 src/object.hpp
 create mode 100644 src/program.cpp
 create mode 100644 src/program.hpp

diff --git a/src/main.cpp b/src/main.cpp
new file mode 100644
index 0000000..00aae22
--- /dev/null
+++ b/src/main.cpp
@@ -0,0 +1,63 @@
+#include "program.hpp"
+
+static heap s_global_heap;
+static stack s_global_stack;
+
+//
+int main(int argc, char* argv[])
+{
+    int ret = 0;
+
+    // apply default configuration
+    program::apply_default();
+
+    // run with interactive prompt
+    if (argc == 1)
+    {
+        //
+        for (;;)
+        {
+            // make program from interactive entry
+            program prog;
+            if (program::entry(prog) == ret_good_bye)
+                break;
+            else
+            {
+                // run it
+                if (prog.run(s_global_stack, s_global_heap) == ret_good_bye)
+                    break;
+                else
+                    program::show_stack(s_global_stack);
+            }
+        }
+    }
+    // run with cmd line arguments
+    else
+    {
+        program prog;
+        string entry;
+        int i;
+
+        // make one string from entry
+        for (i=1; i<argc; i++)
+        {
+            entry += argv[i];
+            entry += ' ';
+        }
+
+        // make program
+        ret = program::parse(entry.c_str(), prog);
+        if (ret == ret_ok)
+        {
+            string separator = "";
+
+            // run it
+            ret = prog.run(s_global_stack, s_global_heap);
+            program::show_stack(s_global_stack, separator);
+        }
+    }
+ 
+    mpfr_free_cache();
+ 
+    return ret;
+}
diff --git a/src/object.cpp b/src/object.cpp
new file mode 100644
index 0000000..3afc89d
--- /dev/null
+++ b/src/object.cpp
@@ -0,0 +1,55 @@
+#include <string>
+using namespace std;
+
+#include "constant.h"
+#include "object.hpp"
+
+// floating_t statics
+mpfr_prec_t floating_t::s_mpfr_prec = MPFR_DEFAULT_PREC_BITS;
+mpfr_rnd_t floating_t::s_mpfr_rnd = MPFR_DEFAULT_RND;
+unsigned int floating_t::s_mpfr_prec_bytes = MPFR_DEFAULT_STORING_LENGTH_BYTES;
+const char* floating_t::s_mpfr_rnd_str[5] = MPFR_RND_STRINGS;
+
+// number statics
+number::mode_enum number::s_mode = DEFAULT_MODE;
+int number::s_current_precision = DEFAULT_PRECISION;
+string number::s_mpfr_printf_format = string(MPFR_DEFAULT_FORMAT);
+
+//
+const char* object::s_cmd_type_string[cmd_max] = CMD_TYPE_STRINGS;
+
+void object::show(FILE* stream)
+{
+    switch(_type)
+    {
+    case cmd_number:
+        switch(((number*)this)->_representation)
+        {
+            case number::dec:
+                mpfr_fprintf(stream, number::s_mpfr_printf_format.c_str(), ((number*)this)->_value.mpfr);
+                break;
+            case number::hex:
+                mpfr_fprintf(stream, string(MPFR_FORMAT_HEX).c_str(), ((number*)this)->_value.mpfr);                
+                break;
+            case number::bin:
+                fprintf(stream, "<binary representation TODO>\n");
+        }
+        break;
+    case cmd_string:
+        fprintf(stream, "\"%s\"", ((ostring*)this)->_value);
+        break;
+    case cmd_program:
+        fprintf(stream, "<<%s>>", ((oprogram*)this)->_value);
+        break;
+    case cmd_symbol:
+        fprintf(stream, "'%s'", ((symbol*)this)->_value);
+        break;
+    case cmd_keyword:
+    case cmd_branch:
+        fprintf(stream, "%s", ((keyword*)this)->_value);
+        break;
+    default:
+        fprintf(stream, "< unknown object representation >");
+        break;
+    }
+}
diff --git a/src/object.hpp b/src/object.hpp
new file mode 100644
index 0000000..425c3af
--- /dev/null
+++ b/src/object.hpp
@@ -0,0 +1,290 @@
+#ifndef OBJECT_HPP
+#define OBJECT_HPP
+
+#include <mpfr.h>
+#include <string.h>
+
+// definitions for objects
+////
+typedef enum {
+    cmd_undef,
+    cmd_number,/* floating value to put in stack */
+    cmd_string,/* string value to put in stack */
+    cmd_symbol,/* symbol value to put in stack */
+    cmd_program,/* program */
+    cmd_keyword,/* langage keyword */
+    cmd_branch,/* langage branch keyword */
+    cmd_max
+} cmd_type_t;
+
+class program;
+class branch;
+
+typedef void (program::*program_fn_t)(void);
+typedef int (program::*branch_fn_t)(branch&);
+
+// MPFR object
+////
+struct floating_t
+{
+    mpfr_t mpfr;
+
+    void init(void* significand)
+    {
+        mpfr_custom_init(significand, MPFR_DEFAULT_PREC_BITS);
+        mpfr_custom_init_set(mpfr, MPFR_ZERO_KIND, 0, s_mpfr_prec, significand);
+    }
+
+    void set_significand(void* significand)
+    {
+        mpfr->_mpfr_d = (mp_limb_t*)significand;
+    }
+
+    floating_t& operator=(const long int val)
+    {
+        mpfr_set_si(mpfr, val, s_mpfr_rnd);
+    }
+
+    floating_t& operator=(const unsigned long val)
+    {
+        mpfr_set_ui(mpfr, val, s_mpfr_rnd);
+    }
+
+    operator int()
+    {
+        return (int)mpfr_get_si(mpfr, s_mpfr_rnd);
+    }
+    
+    bool operator>(const floating_t right)
+    {
+        return mpfr_cmp(mpfr, right.mpfr) > 0;
+    }
+
+    bool operator<(const floating_t right)
+    {
+        return mpfr_cmp(mpfr, right.mpfr) < 0;
+    }
+
+    // default precision in bits, precision length in bytes, rounding mode
+    static mpfr_prec_t s_mpfr_prec;
+    static unsigned int s_mpfr_prec_bytes;
+    static mpfr_rnd_t s_mpfr_rnd;
+    static const char* s_mpfr_rnd_str[5];
+};
+
+// object - a generic stack object
+////
+struct object
+{
+    // object type
+    cmd_type_t _type;
+    unsigned int _size;
+
+    //
+    unsigned int size() { return _size; }
+
+    void show(FILE* stream = stdout);
+
+    //
+    static const char* s_cmd_type_string[cmd_max];
+};
+
+// stack objects derived from object
+////
+struct number : public object
+{
+    number() { _type = cmd_number; }
+    floating_t _value;
+
+    void init(void* significand)
+    {
+        _type = cmd_number;
+        _representation = dec;
+        _value.init(significand);
+    }
+
+    void copy(number& op)
+    {
+        _value = op._value;
+        memcpy(_value.mpfr->_mpfr_d, op._value.mpfr->_mpfr_d, floating_t::s_mpfr_prec_bytes);
+    }
+
+    //
+    void set(const floating_t& value)
+    {
+        _type = cmd_number;
+        _value.mpfr->_mpfr_d = value.mpfr->_mpfr_d;
+    }
+
+    void set(long value)
+    {
+        _type = cmd_number;
+        _value = value;
+    }
+
+    void set(unsigned long value)
+    {
+        _type = cmd_number;
+        _value = value;
+    }
+
+    static unsigned int calc_size()
+    {
+        return (unsigned int)(sizeof(number)+floating_t::s_mpfr_prec_bytes);
+    }
+
+    //
+    number operator=(const number& op)
+    {
+        number num;
+        num.set((const floating_t&)op._value);
+        return num;
+    }
+
+    // representation mode
+    typedef enum {
+        std,
+        fix,
+        sci
+    } mode_enum;
+    static mode_enum s_mode;
+    
+    enum {
+        dec,
+        hex,
+        bin
+    } _representation;
+
+    // precision
+    static int s_current_precision;
+    static string s_mpfr_printf_format;
+};
+
+struct ostring : public object
+{
+    //
+    void set(const char* value, unsigned int len)
+    {
+        _type = cmd_string;
+        if (value != NULL)
+        {
+            if (len>0)
+                (void)memcpy(_value, value, len);
+            _value[len] = 0;
+            _len = len;
+        }
+        else
+            len = 0;
+    }
+
+    //
+    unsigned int _len;
+    char _value[0];
+};
+
+struct oprogram : public object
+{
+    //
+    void set(const char* value, unsigned int len)
+    {
+        _type = cmd_program;
+        if (value != NULL)
+        {
+            if (len>0)
+                (void)memcpy(_value, value, len);
+            _value[len] = 0;
+            _len = len;
+        }
+        else
+            len = 0;
+    }
+
+    //
+    unsigned int _len;
+    char _value[0];
+};
+
+struct symbol : public object
+{
+    //
+    void set(const char* value, unsigned int len, bool auto_eval)
+    {
+        _type = cmd_symbol;
+        _auto_eval = auto_eval;
+
+        if (value != NULL)
+        {
+            if (len>0)
+                (void)memcpy(_value, value, len);
+            _value[len] = 0;
+            _len = len;
+        }
+        else
+            len = 0;
+    }
+
+    //
+    bool _auto_eval;
+    unsigned int _len;
+    char _value[0];
+};
+
+struct keyword : public object
+{
+    //
+    void set(program_fn_t fn, const char* value, unsigned int len)
+    {
+        _type = cmd_keyword;
+        _fn = fn;
+        if (value != NULL)
+        {
+            if (len>0)
+                (void)memcpy(_value, value, len);
+            _value[len] = 0;
+            _len = len;
+        }
+        else
+            len = 0;
+    }
+
+    //
+    program_fn_t _fn;
+    unsigned int _len;
+    char _value[0];
+};
+
+struct branch : public object
+{
+    //
+    void set(branch_fn_t fn, const char* value, unsigned int len)
+    {
+        _type = cmd_branch;
+        _fn = fn;
+        arg1 = -1;
+        arg2 = -1;
+        arg3 = -1;
+        farg1 = NULL;
+        farg2 = NULL;
+        arg_bool = 0;
+        if (value != NULL)
+        {
+            if (len>0)
+                (void)memcpy(_value, value, len);
+            _value[len] = 0;
+            _len = len;
+        }
+        else
+            len = 0;
+    }
+
+    // branch function
+    branch_fn_t _fn;
+    // args used by cmd_branch cmds
+    int arg1, arg2, arg3;
+    number *farg1, *farg2;
+    bool arg_bool;
+    unsigned int _len;
+    char _value[0];
+};
+
+#endif
diff --git a/src/program.cpp b/src/program.cpp
new file mode 100644
index 0000000..cdad770
--- /dev/null
+++ b/src/program.cpp
@@ -0,0 +1,141 @@
+#include "program.hpp"
+
+//
+const char* program::s_ret_value_string[ret_max] = RET_VALUE_STRINGS;
+
+//
+program::keyword_t program::s_keywords[] =
+{
+    //GENERAL
+    { cmd_undef, "", NULL, "\nGENERAL"},
+    { cmd_keyword, "nop", &program::nop, "no operation"},
+    { cmd_keyword, "?", &program::help, "" },
+    { cmd_keyword, "h", &program::help, "" },
+    { cmd_keyword, "help", &program::help, "(or h or ?) this help message" },
+    { cmd_keyword, "q", &program::good_bye, "" },
+    { cmd_keyword, "quit", &program::good_bye, "(or q or exit) quit software" },
+    { cmd_keyword, "exit", &program::good_bye, "" },
+    { cmd_keyword, "test", &program::test, "" }, //not seen by user
+    { cmd_keyword, "version", &program::rpn_version, "show rpn version" },
+    { cmd_keyword, "uname", &program::rpn_uname, "show rpn complete identification string" },
+    { cmd_keyword, "type", &program::type, "show type of stack first entry" },
+    { cmd_keyword, "default", &program::rpn_default, "set float representation and precision to default" },
+    { cmd_keyword, "prec", &program::precision, "get float precision in bits when first stack is not a number\n\t"
+                                                "set float precision in bits when first stack entry is a number. ex: 256 prec\n\t" },
+    { cmd_keyword, "round", &program::round, "set float rounding mode.\n\tex: [\"nearest\", \"toward zero\", \"toward +inf\", \"toward -inf\", \"away from zero\"] round" },
+
+    //REAL
+    { cmd_undef, "", NULL, "\nREAL"},
+    { cmd_keyword, "+", &program::plus, "addition" },
+    { cmd_keyword, "-", &program::minus, "substraction" },
+    { cmd_keyword, "neg", &program::neg , "negation" },
+    { cmd_keyword, "*", &program::mul, "multiplication" },
+    { cmd_keyword, "/", &program::div, "division" },
+    { cmd_keyword, "inv", &program::inv, "inverse" },
+    { cmd_keyword, "%", &program::purcent, "purcent" },
+    { cmd_keyword, "%CH", &program::purcentCH, "inverse purcent" },
+    { cmd_keyword, "^", &program::power, "(or pow) power" },
+    { cmd_keyword, "pow", &program::power, "" },
+    { cmd_keyword, "sqrt", &program::squareroot, "square root" },
+    { cmd_keyword, "sq", &program::square, "(or sqr) square" },
+    { cmd_keyword, "sqr", &program::square, "" },
+    { cmd_keyword, "mod", &program::modulo, "modulo" },
+    { cmd_keyword, "abs", &program::rpn_abs, "absolute value" },
+
+    //REAL representation
+    { cmd_undef, "", NULL, "\nREAL REPRESENTATION"},
+    { cmd_keyword, "dec", &program::dec, "decimal representation" },
+    { cmd_keyword, "hex", &program::hex, "hexadecimal representation" },
+    { cmd_keyword, "bin", &program::bin, "binary representation" },
+    { cmd_keyword, "std", &program::std, "standard floating numbers representation. ex: [25] std" },
+    { cmd_keyword, "fix", &program::fix, "fixed point representation. ex: 6 fix" },
+    { cmd_keyword, "sci", &program::sci, "scientific floating point representation. ex: 20 sci" },
+
+    //TEST
+    { cmd_undef, "", NULL, "\nTEST"},
+    { cmd_keyword, ">", &program::sup, "binary operator >" },
+    { cmd_keyword, ">=", &program::sup_eq, "binary operator >=" },
+    { cmd_keyword, "<", &program::inf, "binary operator <" },
+    { cmd_keyword, "<=", &program::inf_eq, "binary operator <=" },
+    { cmd_keyword, "!=", &program::diff, "binary operator != (different)" },
+    { cmd_keyword, "==", &program::eq , "binary operator == (equal)" },
+    { cmd_keyword, "and", &program::test_and , "boolean operator and" },
+    { cmd_keyword, "or", &program::test_or , "boolean operator or" },
+    { cmd_keyword, "xor", &program::test_xor , "boolean operator xor" },
+    { cmd_keyword, "not", &program::test_not , "boolean operator not" },
+    { cmd_keyword, "same", &program::same , "boolean operator same (equal)" },
+
+    //STACK
+    { cmd_undef, "", NULL, "\nSTACK"},
+    { cmd_keyword, "swap", &program::swap, "swap 2 first stack entries" },
+    { cmd_keyword, "drop", &program::drop, "drop first stack entry" },
+    { cmd_keyword, "drop2", &program::drop2, "drop 2 first stack entries" },
+    { cmd_keyword, "erase", &program::erase, "drop all stack entries" },
+    { cmd_keyword, "rot", &program::rot, "rotate 3 first stack entries" },
+    { cmd_keyword, "dup", &program::dup, "duplicate first stack entry" },
+    { cmd_keyword, "dup2", &program::dup2, "duplicate 2 first stack entries" },
+    { cmd_keyword, "pick", &program::pick, "push a copy of  the given stack level onto the stack" },
+    { cmd_keyword, "depth", &program::depth, "give stack depth" },
+
+    //STRING
+    { cmd_undef, "", NULL, "\nSTRING"},
+    { cmd_keyword, "->str", &program::instr, "convert an object into a string" },
+    { cmd_keyword, "str->", &program::strout, "convert a string into an object" },
+
+    //BRANCH
+    { cmd_undef, "", NULL, "\nBRANCH"},
+    { cmd_branch, "if", (program_fn_t)&program::rpn_if, "<test-instructions>" },
+    { cmd_branch, "then", (program_fn_t)&program::rpn_then, "<true-instructions>" },
+    { cmd_branch, "else", (program_fn_t)&program::rpn_else, "<false-instructions>" },
+    { cmd_keyword, "end", &program::rpn_end, "(end of if structure)" },
+    { cmd_branch, "start", (program_fn_t)&program::rpn_start, "repeat instructions several times" },
+    { cmd_branch, "for", (program_fn_t)&program::rpn_for, "repeat instructions several times with variable" },
+    { cmd_branch, "next", (program_fn_t)&program::rpn_next, "ex: 1 10 start <instructions> next" },
+    { cmd_branch, "step", (program_fn_t)&program::rpn_step, "ex: 1 100 start <instructions> 4 step" },
+
+    //STORE
+    { cmd_undef, "", NULL, "\nSTORE"},
+    { cmd_keyword, "sto", &program::sto, "store a variable. ex: 1 'name' sto" },
+    { cmd_keyword, "rcl", &program::rcl, "recall a variable. ex: 'name' rcl" },
+    { cmd_keyword, "purge", &program::purge, "delete a variable. ex: 'name' purge" },
+    { cmd_keyword, "vars", &program::vars, "list all variables" },
+    { cmd_keyword, "edit", &program::edit, "edit a variable content" },
+
+    //PROGRAM
+    { cmd_undef, "", NULL, "\nPROGRAM"},
+    { cmd_keyword, "eval", &program::eval, "evaluate (run) a program, or recall a variable. ex: 'my_prog' eval" },
+    { cmd_branch, "->", (program_fn_t)&program::inprog, "load program local variables. ex: << -> n m << 0 n m for i i + next >> >>" },
+
+    //TRIG
+    { cmd_undef, "", NULL, "\nTRIG"},
+    { cmd_keyword, "pi", &program::pi, "pi constant" },
+    { cmd_keyword, "sin", &program::rpn_sin, "sinus" },
+    { cmd_keyword, "asin", &program::rpn_asin, "arg sinus" },
+    { cmd_keyword, "cos", &program::rpn_cos , "cosinus" },
+    { cmd_keyword, "acos", &program::rpn_acos, "arg cosinus" },
+    { cmd_keyword, "tan", &program::rpn_tan, "tangent" },
+    { cmd_keyword, "atan", &program::rpn_atan, "arg tangent" },
+    { cmd_keyword, "d->r", &program::d2r, "convert degrees to radians" },
+    { cmd_keyword, "r->d", &program::r2d, "convert radians to degrees" },    
+
+    //LOGS
+    { cmd_undef, "", NULL, "\nLOGS"},
+    { cmd_keyword, "e", &program::rpn_e, "exp(1) constant" },
+    { cmd_keyword, "log", &program::rpn_log, "logarithm base 10" },
+    { cmd_keyword, "alog", &program::rpn_alog, "(or exp10) exponential base 10" },
+    { cmd_keyword, "exp10", &program::rpn_alog, "" },
+    { cmd_keyword, "log2", &program::rpn_log2, "logarithm base 2" },
+    { cmd_keyword, "alog2", &program::rpn_alog2, "(or exp2) exponential base 2" },
+    { cmd_keyword, "exp2", &program::rpn_alog2, "" },
+    { cmd_keyword, "ln", &program::rpn_ln, "logarithm base e" },
+    { cmd_keyword, "exp", &program::rpn_exp, "exponential" },
+    { cmd_keyword, "sinh", &program::rpn_sinh, "hyperbolic sine" },
+    { cmd_keyword, "asinh", &program::rpn_asinh, "inverse hyperbolic sine" },
+    { cmd_keyword, "cosh", &program::rpn_sinh, "hyperbolic cosine" },
+    { cmd_keyword, "acosh", &program::rpn_acosh, "inverse hyperbolic cosine" },
+    { cmd_keyword, "tanh", &program::rpn_tanh, "hyperbolic tangent" },
+    { cmd_keyword, "atanh", &program::rpn_atanh, "inverse hyperbolic tangent" },
+
+    // end
+    { cmd_max, "", NULL, "" },
+};
diff --git a/src/program.hpp b/src/program.hpp
new file mode 100644
index 0000000..2300323
--- /dev/null
+++ b/src/program.hpp
@@ -0,0 +1,479 @@
+#ifndef PROGRAM_HPP
+#define PROGRAM_HPP
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <mpfr.h>
+#include <math.h>
+
+extern "C" {
+#include <readline/readline.h>
+#include <readline/history.h>
+}
+
+#include <iostream>
+#include <iomanip>
+#include <vector>
+#include <sstream>
+#include <fstream>
+using namespace std;
+
+#include "constant.h"
+#include "escape.h"
+#include "version.h"
+#include "debug.h"
+#include "object.hpp"
+#include "stack.hpp"
+
+//
+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;
+};
+
+// program
+class program : public stack
+{
+public:
+    program(program* parent_prog = NULL) { _parent_prog = parent_prog; }
+
+    // run this program
+    ret_value run(stack& stk, heap& hp, heap* parent_local_hp = NULL)
+    {
+        bool go_out = false;
+        ret_value ret = ret_ok;
+        cmd_type_t type;
+
+        // stack comes from outside
+        _stack = &stk;
+
+        // global heap comes from outside
+        _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);
+
+            // could be an auto-evaluated symbol
+            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 next_cmd = (this->*(b->_fn))(*b);
+                switch (next_cmd)
+                {
+                    case -1:
+                        i++; // meaning 'next command'
+                        break;
+                    case -(int)ret_runtime_error:
+                        // error: show it
+                        (void)show_error(_err, _err_context);
+                        go_out = true;// end of run
+                        break;
+                    default:
+                        i = next_cmd;// new direction
+                        break;
+                }
+            }
+
+            // not a command, but a stack entry, manage it
+            else
+            {
+                // copy the program stack entry to the running stack
+                stack::copy_and_push_back(*this, i, stk);
+                i++;
+            }
+        }
+
+        return ret;
+    }
+
+    bool compare_keyword(keyword* k, const char* str_to_compare, int len)
+    {
+        if (k->_len >= len)
+            return strncmp(k->_value, str_to_compare, len) == 0;
+        else
+            return false;
+    }
+
+    bool compare_branch(branch* b, const char* str_to_compare, int len)
+    {
+        if (b->_len >= len)
+            return strncmp(b->_value, str_to_compare, len) == 0;
+        else
+            return false;
+    }
+
+    ret_value preprocess(void)
+    {
+        // for if-then-else-end
+        vector<struct if_layout_t> vlayout;
+        int layout_index=-1;
+        // for start-end-step
+        vector<int> 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 (compare_keyword(k, "end", 3))
+                {
+                    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;
+                    }
+                    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'
+                        if (vlayout[layout_index].index_then != -1)
+                            ((branch*)seq_obj(vlayout[layout_index].index_then))->arg2 = i;
+                        else
+                        {
+                            // error: show it
+                            show_syntax_error("missing then before end");
+                            return ret_syntax;
+                        }
+                    }
+                    layout_index--;
+                }
+            }
+            else if (type == cmd_branch)
+            {
+                branch* k = (branch*)seq_obj(i);
+                if (compare_branch(k, "if", 2))
+                {
+                    if_layout_t layout;
+                    layout.index_if = i;
+                    vlayout.push_back(layout);
+                    layout_index++;
+                }
+                else if (compare_branch(k, "then", 4))
+                {
+                    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 (compare_branch(k, "else", 4))
+                {
+                    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 (compare_branch(k, "start", 5))
+                {
+                    vstartindex.push_back(i);
+                }
+                else if (compare_branch(k, "for", 3))
+                {
+                    vstartindex.push_back(i);
+                    k->arg1 = i + 1;// arg1 points on symbol variable
+                }
+                else if(compare_branch(k, "next", 4))
+                {
+                    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]; // 'next' arg1 = 'start' index
+                    ((branch*)seq_obj(vstartindex[vstartindex.size() - 1]))->arg2 = i; // 'for' or 'start' arg2 = 'next' index
+                    vstartindex.pop_back();
+                }
+                else if (compare_branch(k, "step", 4))
+                {
+                    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
+                    ((branch*)seq_obj(vstartindex[vstartindex.size() - 1]))->arg2 = i; // 'for' or 'start' arg2 = 'next' index
+                    vstartindex.pop_back();
+                }
+                else if (compare_branch(k, "->", 2))
+                {
+                    k->arg1 = i;// arg1 is '->' command index in program
+                }
+            }
+        }
+        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;
+    }
+
+    ret_value show_error()
+    {
+        ret_value ret;
+
+        // show last recorded error
+        cerr<<s_ret_value_string[_err]<<"("<<_err<<"): "<<_err_context<<endl;
+        switch(_err)
+        {
+            case ret_internal:
+            case ret_deadly:
+                ret = ret_deadly;
+            default:
+                ret = ret_ok;
+        }
+        
+        return ret;
+    }
+
+    ret_value show_error(ret_value err, string& context)
+    {
+        // record error
+        _err = err;
+        _err_context = context;
+        return show_error();
+    }
+
+    ret_value show_error(ret_value err, const char* context)
+    {
+        // record error
+        _err = err;
+        _err_context = context;
+        return show_error();
+    }
+
+    void show_syntax_error(const char* context)
+    {
+        // record error
+        _err = ret_syntax;
+        _err_context = context;
+        (void)show_error();
+    }
+
+    ret_value get_err(void) { return _err; }
+
+    static void show_stack(stack& st, const string& separator = g_show_stack_separator)
+    {
+        if (st.size() == 1)
+        {
+            ((object*)st.back())->show();
+            printf("\n");
+        }
+        else
+        {
+            bool show_sep = (! separator.empty());
+            for (int i = st.size()-1; i>=0; i--)
+            {
+                if (show_sep)
+                    printf("%d%s", i+1, separator.c_str());
+                ((object*)st[i])->show();
+                printf("\n");
+            }
+        }
+    }
+    
+    static void apply_default()
+    {
+        //default float precision, float mode, verbosity
+        number::s_mode = DEFAULT_MODE;
+        number::s_current_precision = DEFAULT_PRECISION;
+
+        // format for mpfr_printf 
+        stringstream ss;
+        ss << number::s_current_precision;
+        number::s_mpfr_printf_format = string(MPFR_FORMAT_BEG) + ss.str() + string(MPFR_FORMAT_STD);
+    }
+
+private:
+    // current error and its context
+    ret_value _err;
+    string _err_context;
+
+    // global stack holding results for user
+    stack* _stack;
+
+    // global heap (sto, rcl)
+    heap* _heap;
+
+    // local heap for local loop vairables (for..next)
+    heap _local_heap;
+
+    // local stack internally used by branch commands (start, for, next, ..)
+    stack _branch_stack;
+
+    // parent prog for inheriting heaps
+    program* _parent_prog;
+
+    int stack_size()
+    {
+        return _stack->size();
+    }
+    
+private:
+    static const char* s_ret_value_string[ret_max];
+
+    // 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_MUST_BE_OF_TYPE(num, type) do { if (_stack->get_type(num) != (type)) { ERR_CONTEXT(ret_bad_operand_type); return; } } while(0)
+    #define ARG_MUST_BE_OF_TYPE_RET(num, type, ret) do { if (_stack->get_type(num) != (type)) { ERR_CONTEXT(ret_bad_operand_type); return (ret); } } while(0)
+    #define IS_ARG_TYPE(num, type) (_stack->get_type(num) == (type))
+    #define CHECK_MPFR(op) do { (void)(op); } while(0)
+
+    // keywords
+    struct keyword_t
+    {
+        cmd_type_t type;
+        char name[24];
+        program_fn_t fn;
+        string comment;
+    };
+    static keyword_t s_keywords[];
+
+    // keywords implementation
+    #include "rpn-general.hpp"
+    #include "rpn-real.hpp"
+    #include "rpn-test.hpp"
+    #include "rpn-stack.hpp"
+    #include "rpn-string.hpp"
+    #include "rpn-branch.hpp"
+    #include "rpn-store.hpp"
+    #include "rpn-program.hpp"
+    #include "rpn-trig.hpp"
+    #include "rpn-logs.hpp"
+    #include "rpn-test-core.hpp"
+
+    // parser
+    #include "parse.hpp"
+};
+
+#endif