1 /*
2     This file is part of GNU APL, a free implementation of the
3     ISO/IEC Standard 13751, "Programming Language APL, Extended"
4 
5     Copyright (C) 2008-2015  Dr. Jürgen Sauermann
6 
7     This program is free software: you can redistribute it and/or modify
8     it under the terms of the GNU General Public License as published by
9     the Free Software Foundation, either version 3 of the License, or
10     (at your option) any later version.
11 
12     This program is distributed in the hope that it will be useful,
13     but WITHOUT ANY WARRANTY; without even the implied warranty of
14     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15     GNU General Public License for more details.
16 
17     You should have received a copy of the GNU General Public License
18     along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 */
20 
21 #include <iomanip>
22 
23 #include "Bif_F12_TAKE_DROP.hh"
24 #include "Command.hh"
25 #include "Executable.hh"
26 #include "IndexExpr.hh"
27 #include "Output.hh"
28 #include "Prefix.hh"
29 #include "StateIndicator.hh"
30 #include "SystemLimits.hh"
31 #include "UserFunction.hh"
32 #include "Workspace.hh"
33 
34 //-----------------------------------------------------------------------------
StateIndicator(Executable * exec,StateIndicator * _par)35 StateIndicator::StateIndicator(Executable * exec, StateIndicator * _par)
36    : executable(exec),
37      safe_execution_count(_par ? _par->safe_execution_count : 0),
38      level(_par ? 1 + _par->get_level() : 0),
39      error(E_NO_ERROR, LOC),
40      current_stack(*this, exec->get_body()),
41      parent(_par)
42 {
43 }
44 //-----------------------------------------------------------------------------
~StateIndicator()45 StateIndicator::~StateIndicator()
46 {
47    // flush the FIFO. Do that before delete executable so that values
48    // copied directly from the body of the executable are not killed.
49    //
50    current_stack.clean_up();
51 
52    // if executable is a user defined function then pop its local vars.
53    // otherwise delete the body token
54    //
55    if (get_parse_mode() == PM_FUNCTION)
56       {
57         const UserFunction * ufun = get_executable()->get_ufun();
58         if (ufun)   ufun->pop_local_vars();
59       }
60    else
61       {
62          Assert1(executable);
63          delete executable;
64          executable = 0;
65       }
66 }
67 //-----------------------------------------------------------------------------
68 void
goon(Function_Line new_line,const char * loc)69 StateIndicator::goon(Function_Line new_line, const char * loc)
70 {
71 const Function_PC pc = get_executable()->get_ufun()->pc_for_line(new_line);
72 
73    Log(LOG_StateIndicator__push_pop)
74       CERR << "Continue SI[" << level << "] at line " << new_line
75            << " pc=" << pc << " at " << loc << endl;
76 
77    if (get_executable()->get_body()[pc].get_tag() == TOK_STOP_LINE)   // S∆
78       {
79         // pc points to a S∆ token. We are jumping back from immediate
80         // execution, so we don't want to stop again.
81         //
82         set_PC(pc + 2);
83       }
84    else
85       {
86         set_PC(pc);
87       }
88 
89    Log(LOG_prefix_parser)   CERR << "GOTO [" << get_line() << "]" << endl;
90 
91    current_stack.reset(LOC);
92 }
93 //-----------------------------------------------------------------------------
94 void
retry(const char * loc)95 StateIndicator::retry(const char * loc)
96 {
97    Log(LOG_StateIndicator__push_pop || LOG_prefix_parser)
98       CERR << endl << "RETRY " << loc << ")" << endl;
99 }
100 //-----------------------------------------------------------------------------
101 bool
uses_function(const UserFunction * ufun) const102 StateIndicator::uses_function(const UserFunction * ufun) const
103 {
104 const Executable * uexec = ufun;
105 
106    // case 1: ufun is the currently executing function
107    //
108    if (uexec == get_executable())   return true;
109 
110    // case 2: ufun is used on the prefix parser stack
111    //
112    if (current_stack.uses_function(ufun))   return true;
113    return false;
114 }
115 //-----------------------------------------------------------------------------
116 UCS_string
function_name() const117 StateIndicator::function_name() const
118 {
119    Assert(executable);
120 
121    switch(get_parse_mode())
122       {
123         case PM_FUNCTION:
124              return executable->get_name();
125 
126         case PM_STATEMENT_LIST:
127              {
128                UCS_string ret;
129                ret.append(UNI_DIAMOND);
130                return ret;
131              }
132 
133         case PM_EXECUTE:
134              {
135                UCS_string ret;
136                ret.append(UNI_EXECUTE);
137                return ret;
138              }
139       }
140 
141    FIXME;
142    return UCS_string();
143 }
144 //-----------------------------------------------------------------------------
145 void
print(ostream & out) const146 StateIndicator::print(ostream & out) const
147 {
148    out << "Depth:      " << level                << endl;
149    out << "Exec:       " << executable           << endl;
150    out << "Safe exec:  " << safe_execution_count << endl;
151 
152    Assert(executable);
153 
154    switch(get_parse_mode())
155       {
156         case PM_FUNCTION:
157              out << "Pmode:      ∇ "
158                  << executable->get_ufun()->get_name_and_line(get_PC());
159              break;
160 
161         case PM_STATEMENT_LIST:
162              out << "Pmode:      ◊ " << " " << executable->get_text(0);
163              break;
164 
165         case PM_EXECUTE:
166              out << "Pmode:      ⍎ " << " " << executable->get_text(0);
167              break;
168 
169         default:
170              out << "??? Bad pmode " << get_parse_mode();
171       }
172    out << endl;
173 
174    out << "PC:         " << get_PC() << " (" << executable->get_body().size()
175                        << ")";
176    out << " " << executable->get_body()[get_PC()] << endl;
177    out << "Stat:       " << executable->statement_text(get_PC());
178    out << endl;
179 
180    out << "err_code:   " << HEX(error.get_error_code()) << endl;
181    if (error.get_error_code())
182       out << "thrown at:  " << error.get_throw_loc() << endl
183        << "e_msg_1:    '" << error.get_error_line_1() << "'" << endl
184        << "e_msg_2:    '" << error.get_error_line_2() << "'" << endl
185        << "e_msg_3:    '" << error.get_error_line_3() << "'" << endl;
186 
187    out << endl;
188 }
189 //-----------------------------------------------------------------------------
190 void
list(ostream & out,SI_mode mode) const191 StateIndicator::list(ostream & out, SI_mode mode) const
192 {
193    if (mode & SIM_debug)   // command ]SI or ]SIS
194       {
195         print(out);
196         return;
197       }
198 
199    // pmode column
200    //
201    switch(get_parse_mode())
202       {
203         case PM_FUNCTION:
204              Assert(executable);
205              if (mode == SIM_SI)   // )SI
206                 {
207                   out << executable->get_ufun()->get_name_and_line(get_PC());
208                   break;
209                 }
210 
211              if (mode & SIM_statements)   // )SIS
212                 {
213                   if (error.get_error_code())
214                      {
215                        out << error.get_error_line_2() << endl
216                            << error.get_error_line_3();
217                      }
218                   else
219                      {
220                        const UCS_string name_and_line =
221                             executable->get_ufun()->get_name_and_line(get_PC());
222                        out << name_and_line
223                            << "  " << executable->statement_text(get_PC())
224                            << endl
225                            << UCS_string(name_and_line.size(), UNI_ASCII_SPACE)
226                            << "  ^";   // ^^^
227                      }
228                 }
229 
230              if (mode & SIM_name_list)   // )SINL
231                 {
232                   const UCS_string name_and_line =
233                         executable->get_ufun()->get_name_and_line(get_PC());
234                        out << name_and_line << " ";
235                        executable->get_ufun()->print_local_vars(out);
236                 }
237              break;
238 
239         case PM_STATEMENT_LIST:
240              out << "⋆";
241              if (mode & SIM_statements)   // )SIS
242                 {
243                   if (!executable)      break;
244 
245                   // )SIS and we have a statement
246                   //
247                   out << "  "
248                       << executable->statement_text(get_PC())
249                       << endl << "   ^";   // ^^^
250                 }
251              break;
252 
253         case PM_EXECUTE:
254              out << "⋆⋆  ";
255              if (mode & SIM_statements)   // )SIS
256                 {
257                   if (!executable)   break;
258 
259                   // )SIS and we have a statement
260                   //
261                   if (error.get_error_code())
262                      out << error.get_error_line_2() << endl
263                          << error.get_error_line_3();
264                   else
265                      out << "  "
266                          << executable->statement_text(get_PC());
267                 }
268              break;
269       }
270 
271    out << endl;
272 }
273 //-----------------------------------------------------------------------------
274 ostream &
indent(ostream & out) const275 StateIndicator::indent(ostream & out) const
276 {
277    if (level < 0)
278       {
279          CERR << "[negative level " << HEX(level) << "]" << endl;
280       }
281    else if (level > 100)
282       {
283          CERR << "[huge level " << HEX(level) << "]" << endl;
284       }
285    else
286       {
287          loop(d, level)   out << "   ";
288       }
289 
290    return out;
291 }
292 //-----------------------------------------------------------------------------
293 Token
jump(Value_P value)294 StateIndicator::jump(Value_P value)
295 {
296    // perform a jump. We either remain in the current function (and then
297    // return TOK_VOID), or we (want to) jump into back into the calling
298    // function (and then return TOK_BRANCH.). The jump itself (if any)
299    // is executed in Prefix.cc.
300    //
301    if (value->get_rank() > 1)   RANK_ERROR;
302 
303    if (value->element_count() == 0)     // →''
304       {
305         // →⍬ in immediate execution means resume (retry) suspended function
306         // →⍬ on ⍎ or defined functions means do nothing
307         //
308         if (get_parse_mode() == PM_STATEMENT_LIST)
309            return Token(TOK_BRANCH, int64_t(Function_Retry));
310 
311         return Token(TOK_NOBRANCH);           // stay in context
312       }
313 
314 const Function_Line line = value->get_line_number();
315 
316 const UserFunction * ufun = get_executable()->get_ufun();
317 
318    if (ufun)   // →N in user defined function
319       {
320         set_PC(ufun->pc_for_line(line));   // →N to valid line in user function
321         return Token(TOK_VOID);         // stay in context
322       }
323 
324    // →N in ⍎ or ◊
325    //
326    return Token(TOK_BRANCH, int64_t(line < 0 ? Function_Line_0 : line));
327 }
328 //-----------------------------------------------------------------------------
329 void
escape()330 StateIndicator::escape()
331 {
332 }
333 //-----------------------------------------------------------------------------
334 Token
run()335 StateIndicator::run()
336 {
337 Token result = current_stack.reduce_statements();
338 
339    Log(LOG_prefix_parser)
340       CERR << "Prefix::reduce_statements(si=" << level << ") returned "
341            << result << " in StateIndicator::run()" << endl;
342    return result;
343 }
344 //-----------------------------------------------------------------------------
345 void
unmark_all_values() const346 StateIndicator::unmark_all_values() const
347 {
348    Assert(executable);
349    executable->unmark_all_values();
350 
351    current_stack.unmark_all_values();
352 }
353 //-----------------------------------------------------------------------------
354 int
show_owners(ostream & out,const Value & value) const355 StateIndicator::show_owners(ostream & out, const Value & value) const
356 {
357 int count = 0;
358 
359    Assert(executable);
360 char cc[100];
361    snprintf(cc, sizeof(cc), "    SI[%d] ", level);
362    count += executable->show_owners(cc, out, value);
363 
364    snprintf(cc, sizeof(cc), "    SI[%d] ", level);
365    count += current_stack.show_owners(cc, out, value);
366 
367    return count;
368 }
369 //-----------------------------------------------------------------------------
370 void
info(ostream & out,const char * loc) const371 StateIndicator::info(ostream & out, const char * loc) const
372 {
373    out << "SI[" << level << ":" << get_PC() << "] "
374        << get_parse_mode_name() << " "
375        << executable->get_text(0) << " creator: " << executable->get_loc()
376        << "   seen at: " << loc << endl;
377 }
378 //-----------------------------------------------------------------------------
379 Value_P
get_L()380 StateIndicator::get_L()
381 {
382 Token * tok_L = current_stack.locate_L();
383    if (tok_L)   return tok_L->get_apl_val();
384    return Value_P();
385 }
386 //-----------------------------------------------------------------------------
387 Value_P
get_R()388 StateIndicator::get_R()
389 {
390 Token * tok_R = current_stack.locate_R();
391    if (tok_R)   return tok_R->get_apl_val();
392    return Value_P();
393 }
394 //-----------------------------------------------------------------------------
395 Value_P
get_X()396 StateIndicator::get_X()
397 {
398 Value_P * X = current_stack.locate_X();
399    if (X)   return *X;
400    return Value_P();
401 }
402 //-----------------------------------------------------------------------------
403 void
set_L(Value_P new_value)404 StateIndicator::set_L(Value_P new_value)
405 {
406 Token * tok_L = current_stack.locate_L();
407    if (tok_L == 0)   return;
408 
409 Value_P old_value = tok_L->get_apl_val();   // so that
410    tok_L->move_2(Token(tok_L->get_tag(), new_value), LOC);
411 }
412 //-----------------------------------------------------------------------------
413 void
set_R(Value_P new_value)414 StateIndicator::set_R(Value_P new_value)
415 {
416 Token * tok_R = current_stack.locate_R();
417    if (tok_R == 0)   return;
418 
419 Value_P old_value = tok_R->get_apl_val();   // so that
420    tok_R->move_2(Token(tok_R->get_tag(), new_value), LOC);
421 }
422 //-----------------------------------------------------------------------------
423 void
set_X(Value_P new_value)424 StateIndicator::set_X(Value_P new_value)
425 {
426 Value_P * X = current_stack.locate_X();
427    if (X)   *X = new_value;
428 }
429 //-----------------------------------------------------------------------------
430 int
nth_push(const Symbol * sym,int from_tos) const431 StateIndicator::nth_push(const Symbol * sym, int from_tos) const
432 {
433    if (from_tos == 0)   return 0;
434 
435   // collect SI entries in reverse order...
436    //
437 std::vector<const StateIndicator *> stack;
438 
439    for (const StateIndicator * si = Workspace::SI_top();
440         si; si = si->get_parent())
441       {
442         stack.push_back(si);
443       }
444 
445    loop(d, stack.size())
446        {
447          const StateIndicator * si = stack[stack.size() - d - 1];
448          const Executable * exec = si->get_executable();
449          Assert(exec);
450          if (!exec->pushes_sym(sym))   continue;
451          if (0 == --from_tos)   return si->get_level();
452        }
453 
454    FIXME;
455 }
456 //-----------------------------------------------------------------------------
457 Function_Line
get_line() const458 StateIndicator::get_line() const
459 {
460 int pc = get_PC();
461    if (pc)   --pc;
462    return executable->get_line(Function_PC(pc));
463 }
464 //-----------------------------------------------------------------------------
465 #ifdef WANT_LIBAPL
466 typedef int (*result_callback)(const Value * result, int committed);
467 extern "C" result_callback res_callback;
468 result_callback res_callback = 0;
469 #endif
470 
471 #ifdef WANT_PYTHON
472 extern bool python_result_callback(Token & result);
473 #endif
474 
475 void
statement_result(Token & result,bool trace)476 StateIndicator::statement_result(Token & result, bool trace)
477 {
478    Log(LOG_StateIndicator__enter_leave)
479       CERR << "StateIndicator::statement_result(pmode="
480            << get_parse_mode_name() << ", result=" << result << endl;
481 
482    if (trace)
483       {
484         const UserFunction * ufun = executable->get_ufun();
485         if (ufun && (ufun->get_exec_properties()[0] == 0))
486            {
487              const Function_Line line =
488                    executable->get_line(Function_PC(get_PC() - 1));
489              result.show_trace(COUT, ufun->get_name(), line);
490            }
491       }
492 
493    fun_oper_cache.reset();
494 
495 // if (get_parse_mode() == PM_EXECUTE)   return;
496 
497    // if result is a value then print it, unless it is a committed value
498    // (i.e. TOK_APL_VALUE2)
499    //
500    if (result.get_ValueType() != TV_VAL)
501       {
502 #ifdef WANT_PYTHON
503          python_result_callback(result);
504 #endif
505 
506          return;
507       }
508 
509 const TokenTag tag = result.get_tag();
510 Value_P B(result.get_apl_val());
511    Assert(!!B);
512 
513    // print TOK_APL_VALUE and TOK_APL_VALUE1, but not TOK_APL_VALUE2
514    //
515 bool print_value = tag == TOK_APL_VALUE1 || tag == TOK_APL_VALUE3;
516 
517 #ifdef WANT_LIBAPL
518    if (res_callback)   // callback installed
519       {
520         // the callback decides whether the value shall be printed (even
521         // if it was committed)
522         //
523         print_value = res_callback(B.get(), !print_value);
524       }
525 #endif
526 
527 #ifdef WANT_PYTHON
528    print_value = python_result_callback(result);
529 #endif
530 
531    if (!print_value)   return;
532 
533    Quad_QUOTE::done(false, LOC);
534 
535 const int boxing_format = Command::get_boxing_format();
536    if (boxing_format == 0)   // no boxing
537       {
538         if (Quad_SYL::print_length_limit &&
539             B->element_count() >= Quad_SYL::print_length_limit)
540            {
541              // the value exceeds print_length_limit.
542              // We cut the longest dimension in half until we are below the
543              // limit
544              //
545              Shape sh(B->get_shape());
546              while (sh.get_volume() >= Quad_SYL::print_length_limit)
547                 {
548                   Rank longest = 0;
549                   loop(r, sh.get_rank())
550                      {
551                        if (sh.get_shape_item(r) > sh.get_shape_item(longest))
552                           longest = r;
553                      }
554 
555                   sh.set_shape_item(longest, sh.get_shape_item(longest) / 2);
556                 }
557 
558              Value_P B1 = Bif_F12_TAKE::do_take(sh, B);
559              B1->print(COUT);
560 
561              CERR << "      *** display of value was truncated (limit "
562                      "⎕SYL[⎕IO + " << Quad_SYL::SYL_PRINT_LIMIT
563                   << "] reached)  ***" << endl;
564            }
565         else   // no print length limit or small B
566            {
567              B->print(COUT);
568            }
569       }
570    else if (boxing_format < 0)
571       {
572         const PrintContext pctx = Workspace::get_PrintContext(PST_NONE);
573         Value_P B1 = Quad_CR::do_CR(-boxing_format, B.get(), pctx);
574         if (B1->get_cols() >= Workspace::get_PW())   // too large
575            B->print(COUT);   // don't box
576         else
577            B1->print(COUT);   // do box
578       }
579    else
580       {
581         const PrintContext pctx = Workspace::get_PrintContext(PST_NONE);
582         Value_P B1 = Quad_CR::do_CR(boxing_format, B.get(), pctx);
583         B1->print(COUT);
584       }
585 }
586 //-----------------------------------------------------------------------------
587 Unicode
get_parse_mode_name() const588 StateIndicator::get_parse_mode_name() const
589 {
590    switch(get_parse_mode())
591       {
592         case PM_FUNCTION:       return UNI_NABLA;
593         case PM_STATEMENT_LIST: return UNI_DIAMOND;
594         case PM_EXECUTE:        return UNI_EXECUTE;
595      }
596 
597    CERR << "pmode = " << get_parse_mode() << endl;
598    FIXME;
599    return Invalid_Unicode;
600 }
601 //-----------------------------------------------------------------------------
602 
603