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