GCC Code Coverage Report
Directory: emc/rs274ngc/ Exec Total Coverage
File: emc/rs274ngc/interp_o_word.cc Lines: 262 418 62.7 %
Date: 2016-10-27 Branches: 191 714 26.8 %

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