rpn/src/program.hpp
2017-06-18 22:01:05 +02:00

608 lines
20 KiB
C++

#ifndef PROGRAM_HPP
#define PROGRAM_HPP
// std c
#include <stdlib.h>
#include <stdint.h>
#include <sys/mman.h>
extern "C" {
#include <readline/readline.h>
#include <readline/history.h>
}
// std c++
#include <iostream>
#include <iomanip>
#include <vector>
#include <sstream>
#include <fstream>
using namespace std;
// external libs
#include <mpfr.h>
#include "linenoise.h"
// internal includes
#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_or_unti_or_repeat(-1),index_else(-1),index_end(-1),is_do_unti(false),is_while_repeat(false) { }
int index_if_or_do_or_while;
int index_then_or_unti_or_repeat;
int index_else;
int index_end;
bool is_do_unti;
bool is_while_repeat;
};
// program
class program : public stack
{
public:
program(program* parent_prog = NULL) { _parent_prog = parent_prog; interrupt_now = false; }
// run this program
ret_value run(stack& stk, heap& hp)
{
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) && (interrupt_now==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++;
}
}
if (interrupt_now)
{
fprintf(stderr, "\nInterrupted\n");
interrupt_now = false;
}
return ret;
}
void stop()
{
interrupt_now = true;
}
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_branch)
{
branch* k = (branch*)seq_obj(i);
if (compare_branch(k, "if", 2))
{
if_layout_t layout;
layout.index_if_or_do_or_while = 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_or_unti_or_repeat != -1)
{
// error: show it
show_syntax_error("duplicate then");
return ret_syntax;
}
vlayout[layout_index].index_then_or_unti_or_repeat = i;
k->arg1 = next;
k->arg3 = vlayout[layout_index].index_if_or_do_or_while;
}
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_or_unti_or_repeat == -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_or_do_or_while;
((branch*)seq_obj(vlayout[layout_index].index_then_or_unti_or_repeat))->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
}
else if (compare_branch(k, "do", 2))
{
if_layout_t layout;
layout.index_if_or_do_or_while = i;
layout.is_do_unti = true;
vlayout.push_back(layout);
layout_index++;
}
else if (compare_branch(k, "until", 4))
{
int next = i + 1;
if (next >= (int)size())
next = -1;
// nothing after 'unti' -> error
if (next == -1)
{
// error: show it
show_syntax_error("missing end");
return ret_syntax;
}
if (layout_index<0 || !vlayout[layout_index].is_do_unti)
{
// error: show it
show_syntax_error("missing do");
return ret_syntax;
}
if (vlayout[layout_index].index_then_or_unti_or_repeat != -1)
{
// error: show it
show_syntax_error("duplicate until");
return ret_syntax;
}
vlayout[layout_index].index_then_or_unti_or_repeat = i;
}
else if (compare_branch(k, "while", 4))
{
if_layout_t layout;
layout.index_if_or_do_or_while = i;
layout.is_while_repeat = true;
vlayout.push_back(layout);
layout_index++;
}
else if (compare_branch(k, "repeat", 5))
{
if (layout_index<0 || !vlayout[layout_index].is_while_repeat)
{
// error: show it
show_syntax_error("missing while");
return ret_syntax;
}
if (vlayout[layout_index].index_then_or_unti_or_repeat != -1)
{
// error: show it
show_syntax_error("duplicate repeat");
return ret_syntax;
}
vlayout[layout_index].index_then_or_unti_or_repeat = i;
}
else if (compare_branch(k, "end", 3))
{
int next = i + 1;
if (next >= (int)size())
next = -1;
if (layout_index<0)
{
// error: show it
show_syntax_error("missing branch instruction before end");
return ret_syntax;
}
else
{
if (vlayout[layout_index].is_do_unti)
{
// this end closes a do..unti
if (vlayout[layout_index].index_end != -1)
{
// error: show it
show_syntax_error("duplicate end");
return ret_syntax;
}
if (vlayout[layout_index].index_then_or_unti_or_repeat == -1)
{
// error: show it
show_syntax_error("missing until");
return ret_syntax;
}
k->arg1 = vlayout[layout_index].index_if_or_do_or_while + 1;
layout_index--;
}
else if (vlayout[layout_index].is_while_repeat)
{
// this end closes a while..repeat
if (vlayout[layout_index].index_end != -1)
{
// error: show it
show_syntax_error("duplicate end");
return ret_syntax;
}
k->arg2 = vlayout[layout_index].index_if_or_do_or_while + 1;
if (vlayout[layout_index].index_then_or_unti_or_repeat == -1)
{
// error: show it
show_syntax_error("missing repeat");
return ret_syntax;
}
//fill 'repeat' arg1 with 'end+1'
((branch*)seq_obj(vlayout[layout_index].index_then_or_unti_or_repeat))->arg1 = i + 1;
layout_index--;
}
else
{
// this end closes an if..then..(else)
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_or_unti_or_repeat != -1)
((branch*)seq_obj(vlayout[layout_index].index_then_or_unti_or_repeat))->arg2 = i;
else
{
// error: show it
show_syntax_error("missing then");
return ret_syntax;
}
}
layout_index--;
}
}
}
}
}
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<<_err_context<<": error "<<_err<<": "<<s_ret_value_string[_err]<<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, bool show_separator = true)
{
if (st.size() == 1)
{
((object*)st.back())->show();
printf("\n");
}
else
{
for (int i = st.size()-1; i>=0; i--)
{
if (show_separator)
printf("%d%s", i+1, SHOW_STACK_SEPARATOR);
((object*)st[i])->show();
printf("\n");
}
}
}
static void apply_default()
{
//default float precision, float mode
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);
// default calc precision for MPFR
floating_t::s_mpfr_prec = (mpfr_prec_t)MPFR_DEFAULT_PREC_BITS;
floating_t::s_mpfr_prec_bytes = MPFR_DEFAULT_STORING_LENGTH_BYTES;
}
private:
bool interrupt_now;
// 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 variables (for..next)
heap _local_heap;
// calc stack internally used by branch and calc commands
stack _calc_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[MAX_COMMAND_LENGTH];
program_fn_t fn;
string comment;
};
static keyword_t s_keywords[];
// keywords implementation
#include "rpn-general.hpp"
#include "rpn-real.hpp"
#include "rpn-complex.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