/*
This file is part of GNU APL, a free implementation of the
ISO/IEC Standard 13751, "Programming Language APL, Extended"
Copyright (C) 2008-2015 Dr. Jürgen Sauermann
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 3 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, see .
*/
#include
#include "Bif_F12_TAKE_DROP.hh"
#include "Command.hh"
#include "Executable.hh"
#include "IndexExpr.hh"
#include "Output.hh"
#include "Prefix.hh"
#include "StateIndicator.hh"
#include "SystemLimits.hh"
#include "UserFunction.hh"
#include "Workspace.hh"
//-----------------------------------------------------------------------------
StateIndicator::StateIndicator(Executable * exec, StateIndicator * _par)
: executable(exec),
safe_execution_count(_par ? _par->safe_execution_count : 0),
level(_par ? 1 + _par->get_level() : 0),
error(E_NO_ERROR, LOC),
current_stack(*this, exec->get_body()),
parent(_par)
{
}
//-----------------------------------------------------------------------------
StateIndicator::~StateIndicator()
{
// flush the FIFO. Do that before delete executable so that values
// copied directly from the body of the executable are not killed.
//
current_stack.clean_up();
// if executable is a user defined function then pop its local vars.
// otherwise delete the body token
//
if (get_parse_mode() == PM_FUNCTION)
{
const UserFunction * ufun = get_executable()->get_ufun();
if (ufun) ufun->pop_local_vars();
}
else
{
Assert1(executable);
delete executable;
executable = 0;
}
}
//-----------------------------------------------------------------------------
void
StateIndicator::goon(Function_Line new_line, const char * loc)
{
const Function_PC pc = get_executable()->get_ufun()->pc_for_line(new_line);
Log(LOG_StateIndicator__push_pop)
CERR << "Continue SI[" << level << "] at line " << new_line
<< " pc=" << pc << " at " << loc << endl;
if (get_executable()->get_body()[pc].get_tag() == TOK_STOP_LINE) // S∆
{
// pc points to a S∆ token. We are jumping back from immediate
// execution, so we don't want to stop again.
//
set_PC(pc + 2);
}
else
{
set_PC(pc);
}
Log(LOG_prefix_parser) CERR << "GOTO [" << get_line() << "]" << endl;
current_stack.reset(LOC);
}
//-----------------------------------------------------------------------------
void
StateIndicator::retry(const char * loc)
{
Log(LOG_StateIndicator__push_pop || LOG_prefix_parser)
CERR << endl << "RETRY " << loc << ")" << endl;
}
//-----------------------------------------------------------------------------
bool
StateIndicator::uses_function(const UserFunction * ufun) const
{
const Executable * uexec = ufun;
// case 1: ufun is the currently executing function
//
if (uexec == get_executable()) return true;
// case 2: ufun is used on the prefix parser stack
//
if (current_stack.uses_function(ufun)) return true;
return false;
}
//-----------------------------------------------------------------------------
UCS_string
StateIndicator::function_name() const
{
Assert(executable);
switch(get_parse_mode())
{
case PM_FUNCTION:
return executable->get_name();
case PM_STATEMENT_LIST:
{
UCS_string ret;
ret.append(UNI_DIAMOND);
return ret;
}
case PM_EXECUTE:
{
UCS_string ret;
ret.append(UNI_EXECUTE);
return ret;
}
}
FIXME;
return UCS_string();
}
//-----------------------------------------------------------------------------
void
StateIndicator::print(ostream & out) const
{
out << "Depth: " << level << endl;
out << "Exec: " << executable << endl;
out << "Safe exec: " << safe_execution_count << endl;
Assert(executable);
switch(get_parse_mode())
{
case PM_FUNCTION:
out << "Pmode: ∇ "
<< executable->get_ufun()->get_name_and_line(get_PC());
break;
case PM_STATEMENT_LIST:
out << "Pmode: ◊ " << " " << executable->get_text(0);
break;
case PM_EXECUTE:
out << "Pmode: ⍎ " << " " << executable->get_text(0);
break;
default:
out << "??? Bad pmode " << get_parse_mode();
}
out << endl;
out << "PC: " << get_PC() << " (" << executable->get_body().size()
<< ")";
out << " " << executable->get_body()[get_PC()] << endl;
out << "Stat: " << executable->statement_text(get_PC());
out << endl;
out << "err_code: " << HEX(error.get_error_code()) << endl;
if (error.get_error_code())
out << "thrown at: " << error.get_throw_loc() << endl
<< "e_msg_1: '" << error.get_error_line_1() << "'" << endl
<< "e_msg_2: '" << error.get_error_line_2() << "'" << endl
<< "e_msg_3: '" << error.get_error_line_3() << "'" << endl;
out << endl;
}
//-----------------------------------------------------------------------------
void
StateIndicator::list(ostream & out, SI_mode mode) const
{
if (mode & SIM_debug) // command ]SI or ]SIS
{
print(out);
return;
}
// pmode column
//
switch(get_parse_mode())
{
case PM_FUNCTION:
Assert(executable);
if (mode == SIM_SI) // )SI
{
out << executable->get_ufun()->get_name_and_line(get_PC());
break;
}
if (mode & SIM_statements) // )SIS
{
if (error.get_error_code())
{
out << error.get_error_line_2() << endl
<< error.get_error_line_3();
}
else
{
const UCS_string name_and_line =
executable->get_ufun()->get_name_and_line(get_PC());
out << name_and_line
<< " " << executable->statement_text(get_PC())
<< endl
<< UCS_string(name_and_line.size(), UNI_ASCII_SPACE)
<< " ^"; // ^^^
}
}
if (mode & SIM_name_list) // )SINL
{
const UCS_string name_and_line =
executable->get_ufun()->get_name_and_line(get_PC());
out << name_and_line << " ";
executable->get_ufun()->print_local_vars(out);
}
break;
case PM_STATEMENT_LIST:
out << "⋆";
if (mode & SIM_statements) // )SIS
{
if (!executable) break;
// )SIS and we have a statement
//
out << " "
<< executable->statement_text(get_PC())
<< endl << " ^"; // ^^^
}
break;
case PM_EXECUTE:
out << "⋆⋆ ";
if (mode & SIM_statements) // )SIS
{
if (!executable) break;
// )SIS and we have a statement
//
if (error.get_error_code())
out << error.get_error_line_2() << endl
<< error.get_error_line_3();
else
out << " "
<< executable->statement_text(get_PC());
}
break;
}
out << endl;
}
//-----------------------------------------------------------------------------
ostream &
StateIndicator::indent(ostream & out) const
{
if (level < 0)
{
CERR << "[negative level " << HEX(level) << "]" << endl;
}
else if (level > 100)
{
CERR << "[huge level " << HEX(level) << "]" << endl;
}
else
{
loop(d, level) out << " ";
}
return out;
}
//-----------------------------------------------------------------------------
Token
StateIndicator::jump(Value_P value)
{
// perform a jump. We either remain in the current function (and then
// return TOK_VOID), or we (want to) jump into back into the calling
// function (and then return TOK_BRANCH.). The jump itself (if any)
// is executed in Prefix.cc.
//
if (value->get_rank() > 1) RANK_ERROR;
if (value->element_count() == 0) // →''
{
// →⍬ in immediate execution means resume (retry) suspended function
// →⍬ on ⍎ or defined functions means do nothing
//
if (get_parse_mode() == PM_STATEMENT_LIST)
return Token(TOK_BRANCH, int64_t(Function_Retry));
return Token(TOK_NOBRANCH); // stay in context
}
const Function_Line line = value->get_line_number();
const UserFunction * ufun = get_executable()->get_ufun();
if (ufun) // →N in user defined function
{
set_PC(ufun->pc_for_line(line)); // →N to valid line in user function
return Token(TOK_VOID); // stay in context
}
// →N in ⍎ or ◊
//
return Token(TOK_BRANCH, int64_t(line < 0 ? Function_Line_0 : line));
}
//-----------------------------------------------------------------------------
void
StateIndicator::escape()
{
}
//-----------------------------------------------------------------------------
Token
StateIndicator::run()
{
Token result = current_stack.reduce_statements();
Log(LOG_prefix_parser)
CERR << "Prefix::reduce_statements(si=" << level << ") returned "
<< result << " in StateIndicator::run()" << endl;
return result;
}
//-----------------------------------------------------------------------------
void
StateIndicator::unmark_all_values() const
{
Assert(executable);
executable->unmark_all_values();
current_stack.unmark_all_values();
}
//-----------------------------------------------------------------------------
int
StateIndicator::show_owners(ostream & out, const Value & value) const
{
int count = 0;
Assert(executable);
char cc[100];
snprintf(cc, sizeof(cc), " SI[%d] ", level);
count += executable->show_owners(cc, out, value);
snprintf(cc, sizeof(cc), " SI[%d] ", level);
count += current_stack.show_owners(cc, out, value);
return count;
}
//-----------------------------------------------------------------------------
void
StateIndicator::info(ostream & out, const char * loc) const
{
out << "SI[" << level << ":" << get_PC() << "] "
<< get_parse_mode_name() << " "
<< executable->get_text(0) << " creator: " << executable->get_loc()
<< " seen at: " << loc << endl;
}
//-----------------------------------------------------------------------------
Value_P
StateIndicator::get_L()
{
Token * tok_L = current_stack.locate_L();
if (tok_L) return tok_L->get_apl_val();
return Value_P();
}
//-----------------------------------------------------------------------------
Value_P
StateIndicator::get_R()
{
Token * tok_R = current_stack.locate_R();
if (tok_R) return tok_R->get_apl_val();
return Value_P();
}
//-----------------------------------------------------------------------------
Value_P
StateIndicator::get_X()
{
Value_P * X = current_stack.locate_X();
if (X) return *X;
return Value_P();
}
//-----------------------------------------------------------------------------
void
StateIndicator::set_L(Value_P new_value)
{
Token * tok_L = current_stack.locate_L();
if (tok_L == 0) return;
Value_P old_value = tok_L->get_apl_val(); // so that
tok_L->move_2(Token(tok_L->get_tag(), new_value), LOC);
}
//-----------------------------------------------------------------------------
void
StateIndicator::set_R(Value_P new_value)
{
Token * tok_R = current_stack.locate_R();
if (tok_R == 0) return;
Value_P old_value = tok_R->get_apl_val(); // so that
tok_R->move_2(Token(tok_R->get_tag(), new_value), LOC);
}
//-----------------------------------------------------------------------------
void
StateIndicator::set_X(Value_P new_value)
{
Value_P * X = current_stack.locate_X();
if (X) *X = new_value;
}
//-----------------------------------------------------------------------------
int
StateIndicator::nth_push(const Symbol * sym, int from_tos) const
{
if (from_tos == 0) return 0;
// collect SI entries in reverse order...
//
std::vector stack;
for (const StateIndicator * si = Workspace::SI_top();
si; si = si->get_parent())
{
stack.push_back(si);
}
loop(d, stack.size())
{
const StateIndicator * si = stack[stack.size() - d - 1];
const Executable * exec = si->get_executable();
Assert(exec);
if (!exec->pushes_sym(sym)) continue;
if (0 == --from_tos) return si->get_level();
}
FIXME;
}
//-----------------------------------------------------------------------------
Function_Line
StateIndicator::get_line() const
{
int pc = get_PC();
if (pc) --pc;
return executable->get_line(Function_PC(pc));
}
//-----------------------------------------------------------------------------
#ifdef WANT_LIBAPL
typedef int (*result_callback)(const Value * result, int committed);
extern "C" result_callback res_callback;
result_callback res_callback = 0;
#endif
#ifdef WANT_PYTHON
extern bool python_result_callback(Token & result);
#endif
void
StateIndicator::statement_result(Token & result, bool trace)
{
Log(LOG_StateIndicator__enter_leave)
CERR << "StateIndicator::statement_result(pmode="
<< get_parse_mode_name() << ", result=" << result << endl;
if (trace)
{
const UserFunction * ufun = executable->get_ufun();
if (ufun && (ufun->get_exec_properties()[0] == 0))
{
const Function_Line line =
executable->get_line(Function_PC(get_PC() - 1));
result.show_trace(COUT, ufun->get_name(), line);
}
}
fun_oper_cache.reset();
// if (get_parse_mode() == PM_EXECUTE) return;
// if result is a value then print it, unless it is a committed value
// (i.e. TOK_APL_VALUE2)
//
if (result.get_ValueType() != TV_VAL)
{
#ifdef WANT_PYTHON
python_result_callback(result);
#endif
return;
}
const TokenTag tag = result.get_tag();
Value_P B(result.get_apl_val());
Assert(!!B);
// print TOK_APL_VALUE and TOK_APL_VALUE1, but not TOK_APL_VALUE2
//
bool print_value = tag == TOK_APL_VALUE1 || tag == TOK_APL_VALUE3;
#ifdef WANT_LIBAPL
if (res_callback) // callback installed
{
// the callback decides whether the value shall be printed (even
// if it was committed)
//
print_value = res_callback(B.get(), !print_value);
}
#endif
#ifdef WANT_PYTHON
print_value = python_result_callback(result);
#endif
if (!print_value) return;
Quad_QUOTE::done(false, LOC);
const int boxing_format = Command::get_boxing_format();
if (boxing_format == 0) // no boxing
{
if (Quad_SYL::print_length_limit &&
B->element_count() >= Quad_SYL::print_length_limit)
{
// the value exceeds print_length_limit.
// We cut the longest dimension in half until we are below the
// limit
//
Shape sh(B->get_shape());
while (sh.get_volume() >= Quad_SYL::print_length_limit)
{
Rank longest = 0;
loop(r, sh.get_rank())
{
if (sh.get_shape_item(r) > sh.get_shape_item(longest))
longest = r;
}
sh.set_shape_item(longest, sh.get_shape_item(longest) / 2);
}
Value_P B1 = Bif_F12_TAKE::do_take(sh, B);
B1->print(COUT);
CERR << " *** display of value was truncated (limit "
"⎕SYL[⎕IO + " << Quad_SYL::SYL_PRINT_LIMIT
<< "] reached) ***" << endl;
}
else // no print length limit or small B
{
B->print(COUT);
}
}
else if (boxing_format < 0)
{
const PrintContext pctx = Workspace::get_PrintContext(PST_NONE);
Value_P B1 = Quad_CR::do_CR(-boxing_format, B.get(), pctx);
if (B1->get_cols() >= Workspace::get_PW()) // too large
B->print(COUT); // don't box
else
B1->print(COUT); // do box
}
else
{
const PrintContext pctx = Workspace::get_PrintContext(PST_NONE);
Value_P B1 = Quad_CR::do_CR(boxing_format, B.get(), pctx);
B1->print(COUT);
}
}
//-----------------------------------------------------------------------------
Unicode
StateIndicator::get_parse_mode_name() const
{
switch(get_parse_mode())
{
case PM_FUNCTION: return UNI_NABLA;
case PM_STATEMENT_LIST: return UNI_DIAMOND;
case PM_EXECUTE: return UNI_EXECUTE;
}
CERR << "pmode = " << get_parse_mode() << endl;
FIXME;
return Invalid_Unicode;
}
//-----------------------------------------------------------------------------