PageRenderTime 56ms CodeModel.GetById 23ms RepoModel.GetById 1ms app.codeStats 0ms

/src/emc/rs274ngc/interp_o_word.cc

https://github.com/narogon/linuxcnc
C++ | 1078 lines | 788 code | 119 blank | 171 comment | 214 complexity | 5676a8777c0b695247a10dfe287d03a2 MD5 | raw file
Possible License(s): 0BSD, LGPL-2.1, LGPL-3.0, MPL-2.0-no-copyleft-exception, LGPL-2.0
  1. /********************************************************************
  2. * Description: interp_o_word.cc
  3. *
  4. *
  5. * Author: Kenneth Lerman
  6. * License: GPL Version 2
  7. * System: Linux
  8. *
  9. * Copyright 2005 All rights reserved.
  10. *
  11. * Last change: Michael Haberler 7/2011
  12. *
  13. ********************************************************************/
  14. #include <boost/python.hpp>
  15. #include <unistd.h>
  16. #include <stdio.h>
  17. #include <stdlib.h>
  18. #include <math.h>
  19. #include <string.h>
  20. #include <ctype.h>
  21. #include <sys/types.h>
  22. #include <sys/stat.h>
  23. #include <dirent.h>
  24. #include "rs274ngc.hh"
  25. #include "rs274ngc_return.hh"
  26. #include "interp_return.hh"
  27. #include "interp_internal.hh"
  28. #include "rs274ngc_interp.hh"
  29. namespace bp = boost::python;
  30. //========================================================================
  31. // Functions for control stuff (O-words)
  32. //========================================================================
  33. /*
  34. Given the root of a directory tree and a file name,
  35. find the path to the file, if any.
  36. */
  37. int Interp::findFile( // ARGUMENTS
  38. char *direct, // the directory to start looking in
  39. char *target, // the name of the file to find
  40. char *foundFileDirect) // where to store the result
  41. {
  42. FILE *file;
  43. DIR *aDir;
  44. struct dirent *aFile;
  45. char targetPath[PATH_MAX+1];
  46. snprintf(targetPath, PATH_MAX, "%s/%s", direct, target);
  47. file = fopen(targetPath, "r");
  48. if (file) {
  49. strncpy(foundFileDirect, direct, PATH_MAX);
  50. fclose(file);
  51. return INTERP_OK;
  52. }
  53. aDir = opendir(direct);
  54. if (!aDir) {
  55. ERS(NCE_FILE_NOT_OPEN);
  56. }
  57. while ((aFile = readdir(aDir))) {
  58. if (aFile->d_type == DT_DIR &&
  59. (0 != strcmp(aFile->d_name, "..")) &&
  60. (0 != strcmp(aFile->d_name, "."))) {
  61. char path[PATH_MAX+1];
  62. snprintf(path, PATH_MAX, "%s/%s", direct, aFile->d_name);
  63. if (INTERP_OK == findFile(path, target, foundFileDirect)) {
  64. closedir(aDir);
  65. return INTERP_OK;
  66. }
  67. }
  68. }
  69. closedir(aDir);
  70. ERS(NCE_FILE_NOT_OPEN);
  71. }
  72. /*
  73. * this now uses STL maps for offset access
  74. */
  75. int Interp::control_save_offset(block_pointer block, /* pointer to a block of RS274/NGC instructions */
  76. setup_pointer settings) /* pointer to machine settings */
  77. {
  78. static char name[] = "control_save_offset";
  79. offset_pointer op = NULL;
  80. logOword("Entered:%s for o_name:|%s|", name, block->o_name);
  81. if (control_find_oword(block, settings, &op) == INTERP_OK) {
  82. // already exists
  83. ERS(_("File:%s line:%d redefining sub: o|%s| already defined in file:%s"),
  84. settings->filename, settings->sequence_number,
  85. block->o_name,
  86. op->filename);
  87. }
  88. offset new_offset;
  89. new_offset.type = block->o_type;
  90. new_offset.offset = block->offset;
  91. new_offset.filename = strstore(settings->filename);
  92. new_offset.repeat_count = -1;
  93. // the sequence number has already been bumped, so save
  94. // the proper value
  95. new_offset.sequence_number = settings->sequence_number - 1;
  96. settings->offset_map[block->o_name] = new_offset;
  97. return INTERP_OK;
  98. }
  99. int Interp::control_find_oword(block_pointer block, // pointer to block
  100. setup_pointer settings, // pointer to machine settings
  101. offset_pointer *op) // pointer to offset descriptor
  102. {
  103. static char name[] = "control_find_oword";
  104. offset_map_iterator it;
  105. it = settings->offset_map.find(block->o_name);
  106. if (it != settings->offset_map.end()) {
  107. *op = &it->second;
  108. return INTERP_OK;
  109. } else {
  110. logOword("%s: Unknown oword name: |%s|", name, block->o_name);
  111. ERS(NCE_UNKNOWN_OWORD_NUMBER);
  112. }
  113. }
  114. const char *o_ops[] = {
  115. "O_none",
  116. "O_sub",
  117. "O_endsub",
  118. "O_call",
  119. "O_do",
  120. "O_while",
  121. "O_if",
  122. "O_elseif",
  123. "O_else",
  124. "O_endif",
  125. "O_break",
  126. "O_continue",
  127. "O_endwhile",
  128. "O_return",
  129. "O_repeat",
  130. "O_endrepeat",
  131. "O_continue_call",
  132. "O_pyreturn",
  133. };
  134. const char *call_statenames[] = {
  135. "CS_NORMAL",
  136. "CS_REEXEC_PROLOG",
  137. "CS_REEXEC_PYBODY",
  138. "CS_REEXEC_EPILOG",
  139. "CS_REEXEC_PYOSUB",
  140. };
  141. const char *call_typenames[] = {
  142. "CT_NGC_OWORD_SUB",
  143. "CT_PYTHON_OWORD_SUB",
  144. "CT_REMAP",
  145. };
  146. int Interp::execute_call(setup_pointer settings,
  147. context_pointer current_frame,
  148. int call_type)
  149. {
  150. int status = INTERP_OK;
  151. int i;
  152. bp::list plist;
  153. context_pointer previous_frame = &settings->sub_context[settings->call_level-1];
  154. block_pointer eblock = &EXECUTING_BLOCK(*settings);
  155. logOword("execute_call %s type=%s state=%s cl=%d rl=%d",
  156. current_frame->subName,
  157. call_typenames[call_type],
  158. call_statenames[settings->call_state],
  159. settings->call_level,settings->remap_level);
  160. switch (call_type) {
  161. case CT_NGC_OWORD_SUB:
  162. // copy parameters from context
  163. // save old values of parameters
  164. // save current file position in context
  165. // if we were skipping, no longer
  166. if (settings->skipping_o) {
  167. logOword("case O_call -- no longer skipping to:|%s|",
  168. settings->skipping_o);
  169. settings->skipping_o = NULL;
  170. }
  171. for(i = 0; i < INTERP_SUB_PARAMS; i++) {
  172. previous_frame->saved_params[i] =
  173. settings->parameters[i + INTERP_FIRST_SUBROUTINE_PARAM];
  174. settings->parameters[i + INTERP_FIRST_SUBROUTINE_PARAM] =
  175. eblock->params[i];
  176. }
  177. // if the previous file was NULL, mark positon as -1 so as not to
  178. // reopen it on return.
  179. if (settings->file_pointer == NULL) {
  180. previous_frame->position = -1;
  181. } else {
  182. previous_frame->position = ftell(settings->file_pointer);
  183. }
  184. // save return location
  185. previous_frame->filename = strstore(settings->filename);
  186. previous_frame->sequence_number = settings->sequence_number;
  187. logOword("saving return location[cl=%d]: %s:%d offset=%ld",
  188. settings->call_level-1,
  189. previous_frame->filename,
  190. previous_frame->sequence_number,
  191. previous_frame->position);
  192. if (FEATURE(OWORD_N_ARGS)) {
  193. // let any Oword sub know the number of parameters
  194. CHP(add_named_param("n_args", PA_READONLY));
  195. CHP(store_named_param(settings, "n_args",
  196. (double )eblock->param_cnt,
  197. OVERRIDE_READONLY));
  198. }
  199. // transfer control
  200. if (control_back_to(eblock, settings) == INTERP_ERROR) {
  201. settings->call_level--;
  202. ERS(NCE_UNABLE_TO_OPEN_FILE,eblock->o_name);
  203. return INTERP_ERROR;
  204. }
  205. break;
  206. case CT_PYTHON_OWORD_SUB:
  207. switch (settings->call_state) {
  208. case CS_NORMAL:
  209. settings->return_value = 0.0;
  210. settings->value_returned = 0;
  211. previous_frame->sequence_number = settings->sequence_number;
  212. previous_frame->filename = strstore(settings->filename);
  213. plist.append(settings->pythis); // self
  214. for(int i = 0; i < eblock->param_cnt; i++)
  215. plist.append(eblock->params[i]); // positonal args
  216. current_frame->tupleargs = bp::tuple(plist);
  217. current_frame->kwargs = bp::dict();
  218. case CS_REEXEC_PYOSUB:
  219. if (settings->call_state == CS_REEXEC_PYOSUB)
  220. CHP(read_inputs(settings));
  221. status = pycall(settings, current_frame, OWORD_MODULE,
  222. current_frame->subName,
  223. settings->call_state == CS_NORMAL ? PY_OWORDCALL : PY_FINISH_OWORDCALL);
  224. CHKS(status == INTERP_ERROR, "pycall(%s.%s) failed", OWORD_MODULE, current_frame->subName) ;
  225. switch (status = handler_returned(settings, current_frame, current_frame->subName, true)) {
  226. case INTERP_EXECUTE_FINISH:
  227. settings->call_state = CS_REEXEC_PYOSUB;
  228. break;
  229. default:
  230. settings->call_state = CS_NORMAL;
  231. settings->sequence_number = previous_frame->sequence_number;
  232. CHP(status);
  233. // M73 auto-restore is of dubious value in a Python subroutine
  234. CHP(leave_context(settings,false));
  235. }
  236. break;
  237. }
  238. break;
  239. case CT_REMAP:
  240. block_pointer cblock = &CONTROLLING_BLOCK(*settings);
  241. remap_pointer remap = cblock->executing_remap;
  242. switch (settings->call_state) {
  243. case CS_NORMAL:
  244. if (remap->remap_py || remap->prolog_func || remap->epilog_func) {
  245. CHKS(!PYUSABLE, "%s (remapped) uses Python functions, but the Python plugin is not available",
  246. remap->name);
  247. plist.append(settings->pythis); //self
  248. current_frame->tupleargs = bp::tuple(plist);
  249. current_frame->kwargs = bp::dict();
  250. }
  251. if (remap->argspec && (strchr(remap->argspec, '@') == NULL)) {
  252. // add_parameters will decorate kwargs as per argspec
  253. // if named local parameters specified
  254. CHP(add_parameters(settings, cblock, NULL));
  255. }
  256. // fall through
  257. case CS_REEXEC_PROLOG:
  258. if (remap->prolog_func) {
  259. status = pycall(settings, current_frame, REMAP_MODULE,remap->prolog_func,
  260. settings->call_state == CS_NORMAL ? PY_PROLOG : PY_FINISH_PROLOG);
  261. CHKS(status == INTERP_ERROR, "pycall(%s.%s) failed", REMAP_MODULE, remap->prolog_func);
  262. switch (status = handler_returned(settings, current_frame, current_frame->subName, false)) {
  263. case INTERP_EXECUTE_FINISH:
  264. settings->call_state = CS_REEXEC_PROLOG;
  265. return status;
  266. default:
  267. settings->call_state = CS_NORMAL;
  268. //settings->sequence_number = previous_frame->sequence_number;
  269. CHP(status);
  270. }
  271. }
  272. // fall through
  273. case CS_REEXEC_PYBODY:
  274. if (remap->remap_py) {
  275. status = pycall(settings, current_frame, REMAP_MODULE, remap->remap_py,
  276. settings->call_state == CS_NORMAL ? PY_BODY : PY_FINISH_BODY);
  277. CHP(status);
  278. switch (status = handler_returned(settings, current_frame, current_frame->subName, false)) {
  279. case INTERP_EXECUTE_FINISH:
  280. settings->call_state = CS_REEXEC_PYBODY;
  281. return status;
  282. default:
  283. settings->call_state = CS_NORMAL;
  284. settings->sequence_number = previous_frame->sequence_number;
  285. CHP(status);
  286. // epilog is not supported on python body - makes no sense
  287. CHP(leave_context(settings,false));
  288. ERP(remap_finished(-cblock->phase));
  289. }
  290. }
  291. // call the NGC remap procedure
  292. assert(settings->call_state == CS_NORMAL);
  293. if (remap->remap_ngc) {
  294. CHP(execute_call(settings, current_frame,
  295. CT_NGC_OWORD_SUB));
  296. }
  297. }
  298. }
  299. return status;
  300. }
  301. // this is executed only for NGC subs, either normal ones or part of a remap
  302. // subs whose name is a Py callable are handled inline in execute_call()
  303. // since there is no corresponding O_return/O_endsub to execute.
  304. int Interp::execute_return(setup_pointer settings, context_pointer current_frame,int call_type)
  305. {
  306. int status = INTERP_OK;
  307. logOword("execute_return %s type=%s state=%s",
  308. current_frame->subName,
  309. call_typenames[call_type],
  310. call_statenames[settings->call_state]);
  311. block_pointer cblock = &CONTROLLING_BLOCK(*settings);
  312. block_pointer eblock = &EXECUTING_BLOCK(*settings);
  313. context_pointer previous_frame = &settings->sub_context[settings->call_level - 1];
  314. // if level is not zero, in a call
  315. // otherwise in a defn
  316. // if we were skipping, no longer
  317. if (settings->skipping_o && (eblock->o_type == O_endsub)) {
  318. logOword("case O_%s -- no longer skipping to:|%s|",
  319. (eblock->o_type == O_endsub) ? "endsub" : "return",
  320. settings->skipping_o);
  321. settings->skipping_o = NULL;
  322. }
  323. switch (call_type) {
  324. case CT_REMAP:
  325. switch (settings->call_state) {
  326. case CS_NORMAL:
  327. case CS_REEXEC_EPILOG:
  328. if (cblock->executing_remap && cblock->executing_remap->epilog_func) {
  329. if (settings->call_state == CS_REEXEC_EPILOG)
  330. CHP(read_inputs(settings));
  331. status = pycall(settings, current_frame, REMAP_MODULE,
  332. cblock->executing_remap->epilog_func,
  333. settings->call_state == CS_NORMAL ? PY_EPILOG : PY_FINISH_EPILOG);
  334. CHP(status);
  335. switch (status = handler_returned(settings, current_frame, current_frame->subName, false)) {
  336. case INTERP_EXECUTE_FINISH:
  337. settings->call_state = CS_REEXEC_EPILOG;
  338. break;
  339. default:
  340. settings->call_state = CS_NORMAL;
  341. settings->sequence_number = previous_frame->sequence_number;
  342. CHP(status);
  343. // leave_context() is done by falling through into CT_NGC_OWORD_SUB code
  344. }
  345. }
  346. }
  347. // fall through to normal NGC return handling
  348. case CT_NGC_OWORD_SUB:
  349. if (settings->call_level != 0) {
  350. // restore subroutine parameters.
  351. for(int i = 0; i < INTERP_SUB_PARAMS; i++) {
  352. settings->parameters[i+INTERP_FIRST_SUBROUTINE_PARAM] =
  353. previous_frame->saved_params[i];
  354. }
  355. // file at this level was marked as closed, so dont reopen.
  356. if (previous_frame->position == -1) {
  357. settings->file_pointer = NULL;
  358. strcpy(settings->filename, "");
  359. } else {
  360. if(settings->file_pointer == NULL) {
  361. ERS(NCE_FILE_NOT_OPEN);
  362. }
  363. //!!!KL must open the new file, if changed
  364. if (0 != strcmp(settings->filename, previous_frame->filename)) {
  365. fclose(settings->file_pointer);
  366. settings->file_pointer = fopen(previous_frame->filename, "r");
  367. strcpy(settings->filename, previous_frame->filename);
  368. }
  369. fseek(settings->file_pointer, previous_frame->position, SEEK_SET);
  370. settings->sequence_number = previous_frame->sequence_number;
  371. logOword("endsub/return: %s:%d pos=%ld",
  372. settings->filename,previous_frame->sequence_number,
  373. previous_frame->position);
  374. }
  375. // cleanups on return:
  376. CHP(leave_context(settings, true));
  377. // if this was a remap frame we're done
  378. if (current_frame->context_status & REMAP_FRAME) {
  379. CHP(remap_finished(-cblock->phase));
  380. }
  381. settings->sub_name = 0;
  382. if (previous_frame->subName) {
  383. settings->sub_name = previous_frame->subName;
  384. } else {
  385. settings->sub_name = NULL;
  386. }
  387. } else { // call_level == 0
  388. // a definition
  389. if (eblock->o_type == O_endsub) {
  390. CHKS((settings->defining_sub != 1), NCE_NOT_IN_SUBROUTINE_DEFN);
  391. // no longer skipping or defining
  392. if (settings->skipping_o) {
  393. logOword("case O_endsub in defn -- no longer skipping to:|%s|",
  394. settings->skipping_o);
  395. settings->skipping_o = NULL;
  396. }
  397. settings->defining_sub = 0;
  398. settings->sub_name = NULL;
  399. }
  400. }
  401. }
  402. return status;
  403. }
  404. //
  405. // TESTME!!! MORE THOROUGHLY !!!KL
  406. //
  407. // In the past, calls had to be to predefined subs
  408. //
  409. // Now they don't. Do things in the following sequence:
  410. // 1 -- if o_word is already defined, just go back to it, else
  411. // 2 -- if there is a file with the name of the o_word,
  412. // open it and start skipping (as in 3, below)
  413. // 3 -- skip to the o_word (will be an error if not found)
  414. //
  415. int Interp::control_back_to( block_pointer block, // pointer to block
  416. setup_pointer settings) // pointer to machine settings
  417. {
  418. static char name[] = "control_back_to";
  419. char newFileName[PATH_MAX+1];
  420. char tmpFileName[PATH_MAX+1];
  421. FILE *newFP;
  422. offset_map_iterator it;
  423. offset_pointer op;
  424. logOword("Entered:%s %s", name,block->o_name);
  425. it = settings->offset_map.find(block->o_name);
  426. if (it != settings->offset_map.end()) {
  427. op = &it->second;
  428. if ((settings->filename[0] != 0) &
  429. (settings->file_pointer == NULL)) {
  430. ERS(NCE_FILE_NOT_OPEN);
  431. }
  432. if (0 != strcmp(settings->filename,
  433. op->filename)) {
  434. // open the new file...
  435. newFP = fopen(op->filename, "r");
  436. // set the line number
  437. settings->sequence_number = 0;
  438. strncpy(settings->filename, op->filename, sizeof(settings->filename));
  439. if (settings->filename[sizeof(settings->filename)-1] != '\0') {
  440. fclose(settings->file_pointer);
  441. logOword("filename too long: %s", op->filename);
  442. ERS(NCE_UNABLE_TO_OPEN_FILE, op->filename);
  443. }
  444. if (newFP) {
  445. // close the old file...
  446. if (settings->file_pointer) // only close if it was open
  447. fclose(settings->file_pointer);
  448. settings->file_pointer = newFP;
  449. } else {
  450. logOword("Unable to open file: %s", settings->filename);
  451. ERS(NCE_UNABLE_TO_OPEN_FILE,settings->filename);
  452. }
  453. }
  454. if (settings->file_pointer) { // only seek if it was open
  455. fseek(settings->file_pointer,
  456. op->offset, SEEK_SET);
  457. }
  458. settings->sequence_number = op->sequence_number;
  459. return INTERP_OK;
  460. }
  461. newFP = find_ngc_file(settings, block->o_name, newFileName);
  462. if (newFP) {
  463. logOword("fopen: |%s| OK", newFileName);
  464. settings->sequence_number = 0;
  465. // close the old file...
  466. if (settings->file_pointer)
  467. fclose(settings->file_pointer);
  468. settings->file_pointer = newFP;
  469. strncpy(settings->filename, newFileName, sizeof(settings->filename));
  470. if (settings->filename[sizeof(settings->filename)-1] != '\0') {
  471. logOword("new filename '%s' is too long (max len %zu)\n", newFileName, sizeof(settings->filename)-1);
  472. settings->filename[sizeof(settings->filename)-1] = '\0'; // oh well, truncate the filename
  473. }
  474. } else {
  475. char *dirname = get_current_dir_name();
  476. logOword("fopen: |%s| failed CWD:|%s|", newFileName,
  477. dirname);
  478. free(dirname);
  479. ERS(NCE_UNABLE_TO_OPEN_FILE,tmpFileName);
  480. }
  481. settings->skipping_o = block->o_name; // start skipping
  482. settings->skipping_to_sub = block->o_name; // start skipping
  483. settings->skipping_start = settings->sequence_number;
  484. return INTERP_OK;
  485. }
  486. int Interp::handler_returned( setup_pointer settings, context_pointer active_frame,
  487. const char *name, bool osub)
  488. {
  489. int status = INTERP_OK;
  490. switch (active_frame->py_return_type) {
  491. case RET_YIELD:
  492. // yield <integer> was executed
  493. CHP(active_frame->py_returned_int);
  494. break;
  495. case RET_STOPITERATION: // a bare 'return' in a generator - treat as INTERP_OK
  496. case RET_NONE:
  497. break;
  498. case RET_DOUBLE:
  499. if (osub) { // float values are ok for osubs
  500. settings->return_value = active_frame->py_returned_double;
  501. settings->value_returned = 1;
  502. } else {
  503. ERS("handler_returned: %s returned double: %f - invalid",
  504. name, active_frame->py_returned_double);
  505. }
  506. break;
  507. case RET_INT:
  508. if (osub) { // let's be liberal with types - widen to double return value
  509. settings->return_value = (double) active_frame->py_returned_int;
  510. settings->value_returned = 1;
  511. } else
  512. return active_frame->py_returned_int;
  513. case RET_ERRORMSG:
  514. status = INTERP_ERROR;
  515. break;
  516. }
  517. return status;
  518. }
  519. // prepare a new call frame.
  520. int Interp::enter_context(setup_pointer settings, block_pointer block)
  521. {
  522. logOword("enter_context cl=%d->%d type=%s",
  523. settings->call_level, settings->call_level+1,
  524. call_typenames[block->call_type]);
  525. settings->call_level++;
  526. if (settings->call_level >= INTERP_SUB_ROUTINE_LEVELS) {
  527. ERS(NCE_TOO_MANY_SUBROUTINE_LEVELS);
  528. }
  529. context_pointer frame = &settings->sub_context[settings->call_level];
  530. // mark frame for finishing remap
  531. frame->context_status = (block->call_type == CT_REMAP) ? REMAP_FRAME : 0;
  532. frame->subName = block->o_name;
  533. frame->py_returned_int = 0;
  534. frame->py_returned_double = 0.0;
  535. frame->py_return_type = -1;
  536. frame->call_type = block->call_type; // distinguish call frames: oword,python,remap
  537. return INTERP_OK;
  538. }
  539. int Interp::leave_context(setup_pointer settings, bool restore)
  540. {
  541. context_pointer leaving_frame = &settings->sub_context[settings->call_level];
  542. if (settings->call_level < 1) {
  543. ERS(NCE_CALL_STACK_UNDERRUN);
  544. }
  545. logOword("leave_context cl=%d->%d type=%s state=%s" ,
  546. settings->call_level, settings->call_level-1,
  547. call_typenames[leaving_frame->call_type],
  548. call_statenames[settings->call_state]);
  549. free_named_parameters(leaving_frame);
  550. leaving_frame->subName = NULL;
  551. settings->call_level--; // drop back
  552. if (restore && ((leaving_frame->context_status &
  553. (CONTEXT_RESTORE_ON_RETURN|CONTEXT_VALID)) ==
  554. (CONTEXT_RESTORE_ON_RETURN|CONTEXT_VALID))) {
  555. // a valid previous context was marked by an M73 as auto-restore
  556. // NB: this means an M71 invalidate context will prevent an
  557. // auto-restore on return/endsub
  558. CHP(restore_settings(settings, settings->call_level + 1));
  559. }
  560. return INTERP_OK;
  561. }
  562. /************************************************************************/
  563. /* convert_control_functions
  564. Returned Value: int (INTERP_OK)
  565. Side effects:
  566. Changes the flow of control.
  567. Called by: execute
  568. Calls: control_skip_to
  569. control_back_to
  570. control_save_offset
  571. */
  572. int Interp::convert_control_functions(block_pointer block, // pointer to a block of RS274/NGC instructions
  573. setup_pointer settings) // pointer to machine settings
  574. {
  575. int status = INTERP_OK;
  576. context_pointer current_frame;
  577. offset_pointer op = NULL;
  578. logOword("convert_control_functions %s", o_ops[block->o_type]);
  579. // must skip if skipping
  580. if (settings->skipping_o && (0 != strcmp(settings->skipping_o, block->o_name))) {
  581. logOword("skipping to line: |%s|", settings->skipping_o);
  582. return INTERP_OK;
  583. }
  584. if (settings->skipping_to_sub && (block->o_type != O_sub)) {
  585. logOword("skipping to sub: |%s|", settings->skipping_to_sub);
  586. return INTERP_OK;
  587. }
  588. // if skipping_o was set, we are now on a line which contains that O-word.
  589. // if skipping_to_sub was set, we are now on the 'O-name sub' definition line.
  590. switch (block->o_type) {
  591. case O_none:
  592. // not an error because we use this to signal that we
  593. // are not evaluating functions
  594. break;
  595. case O_sub:
  596. // if the level is not zero, this is a call
  597. // not the definition
  598. // if we were skipping, no longer
  599. if (settings->skipping_o) {
  600. logOword("sub(o_|%s|) was skipping to here", settings->skipping_o);
  601. // skipping to a sub means that we must define this now
  602. CHP(control_save_offset( block, settings));
  603. logOword("no longer skipping to:|%s|", settings->skipping_o);
  604. settings->skipping_o = NULL; // this IS our block number
  605. }
  606. settings->skipping_to_sub = NULL; // this IS our block number
  607. if (settings->call_level != 0) {
  608. logOword("call:%f:%f:%f",
  609. settings->parameters[1],
  610. settings->parameters[2],
  611. settings->parameters[3]);
  612. } else {
  613. // a definition. We're on the O<name> sub line.
  614. logOword("started a subroutine defn: %s",block->o_name);
  615. CHKS((settings->defining_sub == 1), NCE_NESTED_SUBROUTINE_DEFN);
  616. CHP(control_save_offset( block, settings));
  617. // start skipping to the corresponding ensub.
  618. settings->skipping_o = block->o_name;
  619. settings->skipping_start = settings->sequence_number;
  620. settings->defining_sub = 1;
  621. settings->sub_name = block->o_name;
  622. logOword("will now skip to: |%s|", settings->sub_name);
  623. }
  624. break;
  625. case O_endsub:
  626. case O_return:
  627. if ((settings->call_level == 0) &&
  628. (settings->sub_name == NULL)) {
  629. // detect a standalone 'o<label> return|endsub'
  630. OERR(_("%d: not in a subroutine definition: '%s'"),
  631. settings->sequence_number, settings->linetext);
  632. }
  633. // proper label semantics (only refer to defined sub, within sub defn etc)
  634. // is handled in read_o() for return & endsub
  635. current_frame = &settings->sub_context[settings->call_level];
  636. CHP(execute_return(settings, current_frame,
  637. current_frame->call_type));
  638. break;
  639. case O_call:
  640. // only enter new frame if not reexecuting a Python handler
  641. // which returned INTERP_EXECUTE_FINISH
  642. if (settings->call_state == CS_NORMAL) {
  643. CHP(enter_context(settings, block));
  644. }
  645. current_frame = &settings->sub_context[settings->call_level];
  646. CHP(execute_call(settings, current_frame,
  647. current_frame->call_type));
  648. break;
  649. case O_do:
  650. // if we were skipping, no longer
  651. settings->skipping_o = NULL;
  652. // save the loop point
  653. // we hit this again on loop back -- so test first
  654. if(INTERP_OK != control_find_oword(block, settings, &op)) // &index))
  655. {
  656. // save offset if not found in offset table
  657. CHP(control_save_offset( block, settings));
  658. }
  659. break;
  660. case O_repeat:
  661. if (control_find_oword(block, settings, &op) == INTERP_OK) {
  662. if (settings->sequence_number != (op->sequence_number + 1))
  663. OERR(_("%d: duplicate O-word label: '%s' - defined in line %d"),
  664. settings->sequence_number,
  665. settings->linetext, op->sequence_number + 1);
  666. } else
  667. CHP(control_save_offset(block, settings));
  668. // if we were skipping, no longer
  669. settings->skipping_o = NULL;
  670. status = control_find_oword(block, settings, &op); // &index);
  671. // test if not already seen OR
  672. // if seen and this is a repeat
  673. if ((status != INTERP_OK) ||
  674. (op->type == block->o_type)) {
  675. // this is the beginning of a 'repeat' loop
  676. // add it to the table if not already there
  677. if(status != INTERP_OK)
  678. CHP(control_save_offset( block, settings));
  679. // note the repeat count. it should only be calculated at the
  680. // start of the repeat loop.
  681. control_find_oword(block, settings, &op); // &index);
  682. if(op->repeat_count == -1)
  683. op->repeat_count =
  684. round_to_int(settings->test_value);
  685. // are we still repeating?
  686. if(op->repeat_count > 0) {
  687. // execute forward
  688. logOword("executing forward: [%s] in 'repeat' test value-- %g",
  689. block->o_name, settings->test_value);
  690. // one less repeat remains
  691. op->repeat_count--;
  692. } else {
  693. // skip forward
  694. logOword("skipping forward: [%s] in 'repeat'",
  695. block->o_name);
  696. settings->skipping_o = block->o_name;
  697. settings->skipping_start = settings->sequence_number;
  698. // cause the repeat count to be recalculated
  699. // if we do this loop again
  700. op->repeat_count = -1;
  701. }
  702. }
  703. break;
  704. case O_while:
  705. if (control_find_oword(block, settings, &op) == INTERP_OK) {
  706. if ((op->type != O_do) &&
  707. (settings->sequence_number != (op->sequence_number + 1)))
  708. OERR(_("%d: duplicate O-word label: '%s' - defined in line %d"),
  709. settings->sequence_number,
  710. settings->linetext, op->sequence_number + 1);
  711. } else
  712. // record only if this is a while/endwhile loop
  713. CHP(control_save_offset(block, settings));
  714. // if we were skipping, no longer
  715. settings->skipping_o = NULL;
  716. status = control_find_oword(block, settings, &op); // &index);
  717. // test if not already seen OR
  718. // if seen and this is a while (alternative is that it is a do)
  719. if ((status != INTERP_OK) ||
  720. (op->type == block->o_type)) {
  721. // this is the beginning of a 'while' loop
  722. // add it to the table if not already there
  723. if (status != INTERP_OK)
  724. CHP(control_save_offset( block, settings));
  725. // test the condition
  726. if (settings->test_value != 0.0) // true - execute forward
  727. logOword("executing forward: [%s] in 'while'",
  728. block->o_name);
  729. else {
  730. // false - skip forward
  731. logOword("skipping forward: [%s] in 'while'",
  732. block->o_name);
  733. settings->skipping_o = block->o_name;
  734. settings->skipping_start = settings->sequence_number;
  735. }
  736. } else {
  737. // this is the end of a 'do'
  738. // test the condition
  739. if ((settings->test_value != 0.0) && !settings->doing_break) {
  740. // true - loop on back
  741. logOword("looping back to: [%s] in 'do while'",
  742. block->o_name);
  743. CHP(control_back_to(block, settings));
  744. } else {
  745. // false
  746. logOword("not looping back to: [%s] in 'do while'",
  747. block->o_name);
  748. settings->doing_break = 0;
  749. }
  750. }
  751. break;
  752. case O_if:
  753. if (control_find_oword(block, settings, &op) == INTERP_OK) {
  754. if (settings->sequence_number != (op->sequence_number + 1))
  755. OERR(_("%d: duplicate O-word label - already defined in line %d: '%s'"),
  756. settings->sequence_number, op->sequence_number + 1,
  757. settings->linetext);
  758. } else
  759. CHP(control_save_offset(block, settings));
  760. if (settings->test_value != 0.0) {
  761. //true
  762. logOword("executing forward: [%s] in 'if'",
  763. block->o_name);
  764. settings->skipping_o = NULL;
  765. settings->executed_if = 1;
  766. } else {
  767. //false
  768. logOword("skipping forward: [%s] in 'if'",
  769. block->o_name);
  770. settings->skipping_o = block->o_name;
  771. settings->skipping_start = settings->sequence_number;
  772. settings->executed_if = 0;
  773. }
  774. break;
  775. case O_elseif:
  776. if (control_find_oword(block, settings, &op) != INTERP_OK)
  777. OERR(_("%d: undefined O-word label: '%s'"),
  778. settings->sequence_number, settings->linetext);
  779. if (op->type != O_if)
  780. OERR(_("%d: no matching 'if' label: '%s' (found '%s' in line %d)"),
  781. settings->sequence_number, settings->linetext,
  782. o_ops[op->type] + 2, op->sequence_number + 1);
  783. if ((settings->skipping_o) &&
  784. (0 != strcmp(settings->skipping_o, block->o_name))) {
  785. //!!!KL -- the if conditions here say that we were skipping
  786. //!!!KL but that the target o_name is not ours.
  787. //!!!KL so we should continue skipping -- that's not what
  788. //!!!KL the code below says.
  789. // mah: so?
  790. #if 0
  791. // we were not skipping -- start skipping
  792. logOword("start skipping forward: [%s] in 'elseif'",
  793. block->o_name);
  794. settings->skipping_o = block->o_name;
  795. settings->skipping_start = settings->sequence_number;
  796. return INTERP_OK;
  797. #else
  798. // we were skipping -- continue skipping
  799. logOword("continue skipping forward: [%s] in 'elseif'",
  800. block->o_name);
  801. return INTERP_OK;
  802. #endif
  803. }
  804. // we were skipping
  805. // but were we ever not skipping
  806. if (settings->executed_if) {
  807. // we have already executed, keep on skipping
  808. logOword("already executed, continue "
  809. "skipping forward: [%s] in 'elseif'",
  810. block->o_name);
  811. settings->skipping_o = block->o_name;
  812. settings->skipping_start = settings->sequence_number;
  813. return INTERP_OK;
  814. }
  815. if (settings->test_value != 0.0) {
  816. //true -- start executing
  817. logOword("start executing forward: [%s] in 'elseif'",
  818. block->o_name);
  819. settings->skipping_o = NULL;
  820. settings->executed_if = 1;
  821. } else {
  822. //false
  823. logOword("continue skipping forward: [%s] in 'elseif'",
  824. block->o_name);
  825. }
  826. break;
  827. case O_else:
  828. if (control_find_oword(block, settings, &op) != INTERP_OK)
  829. OERR(_("%d: undefined O-word label: '%s'"),
  830. settings->sequence_number, settings->linetext);
  831. if (op->type != O_if)
  832. OERR(_("%d: no matching 'if' label: '%s' (found '%s' in line %d)"),
  833. settings->sequence_number, settings->linetext,
  834. o_ops[op->type] + 2, op->sequence_number + 1);
  835. // were we ever not skipping
  836. if (settings->executed_if) {
  837. // we have already executed, skip
  838. logOword("already executed, "
  839. "skipping forward: [%s] in 'else'",
  840. block->o_name);
  841. settings->skipping_o = block->o_name;
  842. settings->skipping_start = settings->sequence_number;
  843. return INTERP_OK;
  844. }
  845. if ((settings->skipping_o) &&
  846. (0 == strcmp(settings->skipping_o, block->o_name))) {
  847. // we were skipping so stop skipping
  848. logOword("stop skipping forward: [%s] in 'else'",
  849. block->o_name);
  850. settings->executed_if = 1;
  851. settings->skipping_o = NULL;
  852. } else {
  853. // we were not skipping -- so skip
  854. logOword("start skipping forward: [%s] in 'else'",
  855. block->o_name);
  856. }
  857. break;
  858. case O_endif:
  859. if (control_find_oword(block, settings, &op) != INTERP_OK)
  860. OERR(_("%d: undefined O-word label: '%s'"),
  861. settings->sequence_number, settings->linetext);
  862. if (op->type != O_if)
  863. OERR(_("%d: no matching label: '%s' (found '%s' in line %d): '%s'"),
  864. settings->sequence_number, block->o_name,
  865. o_ops[op->type] + 2, op->sequence_number + 1, settings->linetext);
  866. // stop skipping if we were
  867. settings->skipping_o = NULL;
  868. logOword("stop skipping forward: [%s] in 'endif'",
  869. block->o_name);
  870. // the KEY -- outside if clearly must have executed
  871. // or this would not have executed
  872. settings->executed_if = 1;
  873. break;
  874. case O_break:
  875. if (control_find_oword(block, settings, &op) != INTERP_OK)
  876. OERR(_("%d: undefined O-word label: '%s'"),
  877. settings->sequence_number, settings->linetext);
  878. if ((op->type != O_while) && (op->type != O_do))
  879. OERR(_("%d: no matching while/do label: '%s' (found '%s' in line %d)"),
  880. settings->sequence_number, settings->linetext,
  881. o_ops[op->type] + 2, op->sequence_number + 1);
  882. // start skipping
  883. settings->skipping_o = block->o_name;
  884. settings->skipping_start = settings->sequence_number;
  885. settings->doing_break = 1;
  886. logOword("start skipping forward: [%s] in 'break'",
  887. block->o_name);
  888. break;
  889. case O_continue:
  890. if (control_find_oword(block, settings, &op) != INTERP_OK)
  891. OERR(_("%d: undefined O-word label: '%s'"),
  892. settings->sequence_number, settings->linetext);
  893. if ((op->type != O_while) && (op->type != O_do))
  894. OERR(_("%d: no matching while/do label: '%s' (found '%s' in line %d)"),
  895. settings->sequence_number, settings->linetext,
  896. o_ops[op->type] + 2, op->sequence_number + 1);
  897. // if already skipping, do nothing
  898. if ((settings->skipping_o) &&
  899. (0 == strcmp(settings->skipping_o, block->o_name))) {
  900. logOword("already skipping: [%s] in 'continue'",
  901. block->o_name);
  902. return INTERP_OK;
  903. }
  904. // start skipping
  905. settings->skipping_o = block->o_name;
  906. settings->skipping_start = settings->sequence_number;
  907. settings->doing_continue = 1;
  908. logOword("start skipping forward: [%s] in 'continue'",
  909. block->o_name);
  910. break;
  911. case O_endrepeat:
  912. case O_endwhile:
  913. if (control_find_oword(block, settings, &op) != INTERP_OK)
  914. OERR(_("%d: undefined O-word label: '%s'"),
  915. settings->sequence_number, settings->linetext);
  916. if (((block->o_type == O_endrepeat) && (op->type != O_repeat)) ||
  917. ((block->o_type == O_endwhile) && (op->type != O_while)))
  918. OERR(_("%d: no matching label: '%s' (found '%s' in line %d)"),
  919. settings->sequence_number, settings->linetext,
  920. o_ops[op->type] + 2, op->sequence_number + 1);
  921. // end of a while loop
  922. logOword("endwhile: skipping_o:%s", settings->skipping_o);
  923. if ((settings->skipping_o) &&
  924. (0 == strcmp(settings->skipping_o, block->o_name))) {
  925. // we were skipping, so this is the end
  926. settings->skipping_o = NULL;
  927. if (settings->doing_continue) {
  928. settings->doing_continue = 0;
  929. // loop on back
  930. logOword("looping back (continue) to: [%s] in while/repeat",
  931. block->o_name);
  932. CHP(control_back_to(block, settings));
  933. } else {
  934. // not doing continue, we are done
  935. logOword("falling thru the complete while/repeat: [%s]",
  936. block->o_name);
  937. return INTERP_OK;
  938. }
  939. } else {
  940. // loop on back
  941. logOword("looping back to: [%s] in 'endwhile/endrepeat'",
  942. block->o_name);
  943. CHP(control_back_to(block, settings));
  944. }
  945. break;
  946. default:
  947. // FIXME !!!KL should probably be an error
  948. return INTERP_ERROR;
  949. break;
  950. }
  951. // return status;
  952. return INTERP_OK;
  953. }
  954. //========================================================================
  955. // End of functions for control stuff (O-words)
  956. //========================================================================