mirror of
https://github.com/grblHAL/core.git
synced 2026-02-06 09:02:33 +08:00
Added named parameters for getting absolute (G53) position: _abs_x, abs_y, ... Available when expression support is enabled. Changed stepper enable HAL signature to allow current reduction when idle. Added reference id to spindle registration in order to allow configuring default spindle, and possibly additional spindles, at compile time.
609 lines
22 KiB
C
609 lines
22 KiB
C
/*
|
|
ngc_flowctrl.c - An embedded CNC Controller with rs274/ngc (g-code) support
|
|
|
|
Program flow control, for filesystem macros
|
|
|
|
Part of grblHAL
|
|
|
|
Copyright (c) 2023-2024 Terje Io
|
|
|
|
grblHAL is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
grblHAL is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with grblHAL. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "hal.h"
|
|
|
|
#if NGC_EXPRESSIONS_ENABLE
|
|
|
|
#include <string.h>
|
|
|
|
#include "errors.h"
|
|
#include "ngc_expr.h"
|
|
#include "ngc_params.h"
|
|
|
|
#ifndef NGC_STACK_DEPTH
|
|
#define NGC_STACK_DEPTH 20
|
|
#endif
|
|
|
|
typedef enum {
|
|
NGCFlowCtrl_NoOp = 0,
|
|
NGCFlowCtrl_If,
|
|
NGCFlowCtrl_ElseIf,
|
|
NGCFlowCtrl_Else,
|
|
NGCFlowCtrl_EndIf,
|
|
NGCFlowCtrl_Do,
|
|
NGCFlowCtrl_Continue,
|
|
NGCFlowCtrl_Break,
|
|
NGCFlowCtrl_While,
|
|
NGCFlowCtrl_EndWhile,
|
|
NGCFlowCtrl_Repeat,
|
|
NGCFlowCtrl_EndRepeat,
|
|
NGCFlowCtrl_Sub,
|
|
NGCFlowCtrl_EndSub,
|
|
NGCFlowCtrl_Call,
|
|
NGCFlowCtrl_Return,
|
|
NGCFlowCtrl_RaiseAlarm,
|
|
NGCFlowCtrl_RaiseError
|
|
} ngc_cmd_t;
|
|
|
|
typedef struct ngc_sub {
|
|
uint32_t o_label;
|
|
vfs_file_t *file;
|
|
size_t file_pos;
|
|
struct ngc_sub *next;
|
|
} ngc_sub_t;
|
|
|
|
typedef struct {
|
|
uint32_t o_label;
|
|
ngc_cmd_t operation;
|
|
ngc_sub_t *sub;
|
|
vfs_file_t *file;
|
|
size_t file_pos;
|
|
char *expr;
|
|
uint32_t repeats;
|
|
bool skip;
|
|
bool handled;
|
|
bool brk;
|
|
} ngc_stack_entry_t;
|
|
|
|
static volatile int_fast8_t stack_idx = -1;
|
|
static bool skip_sub = false;
|
|
static ngc_sub_t *subs = NULL, *exec_sub = NULL;
|
|
static ngc_stack_entry_t stack[NGC_STACK_DEPTH] = {0};
|
|
|
|
static status_code_t read_command (char *line, uint_fast8_t *pos, ngc_cmd_t *operation)
|
|
{
|
|
char c = line[*pos];
|
|
status_code_t status = Status_OK;
|
|
|
|
(*pos)++;
|
|
|
|
switch(c) {
|
|
|
|
case 'A':
|
|
if (!strncmp(line + *pos, "LARM", 4)) {
|
|
*operation = NGCFlowCtrl_RaiseAlarm;
|
|
*pos += 4;
|
|
} else
|
|
status = Status_FlowControlSyntaxError; // Unknown statement name starting with A
|
|
break;
|
|
|
|
case 'B':
|
|
if (!strncmp(line + *pos, "REAK", 4)) {
|
|
*operation = NGCFlowCtrl_Break;
|
|
*pos += 4;
|
|
} else
|
|
status = Status_FlowControlSyntaxError; // Unknown statement name starting with B
|
|
break;
|
|
|
|
case 'C':
|
|
if (!strncmp(line + *pos, "ONTINUE", 7)) {
|
|
*operation = NGCFlowCtrl_Continue;
|
|
*pos += 7;
|
|
} else if (!strncmp(line + *pos, "ALL", 3)) {
|
|
*operation = NGCFlowCtrl_Call;
|
|
*pos += 3;
|
|
} else
|
|
status = Status_FlowControlSyntaxError; // Unknown statement name starting with C
|
|
break;
|
|
|
|
case 'D':
|
|
if (line[*pos] == 'O') {
|
|
*operation = NGCFlowCtrl_Do;
|
|
(*pos)++;
|
|
} else
|
|
status = Status_FlowControlSyntaxError; // Unknown statement name starting with D
|
|
break;
|
|
|
|
case 'E':
|
|
if (!strncmp(line + *pos, "LSEIF", 5)) {
|
|
*operation = NGCFlowCtrl_ElseIf;
|
|
*pos += 5;
|
|
} else if (!strncmp(line + *pos, "LSE", 3)) {
|
|
*operation = NGCFlowCtrl_Else;
|
|
*pos += 3;
|
|
} else if (!strncmp(line + *pos, "NDIF", 4)) {
|
|
*operation = NGCFlowCtrl_EndIf;
|
|
*pos += 4;
|
|
} else if (!strncmp(line + *pos, "NDWHILE", 7)) {
|
|
*operation = NGCFlowCtrl_EndWhile;
|
|
*pos += 7;
|
|
} else if (!strncmp(line + *pos, "NDREPEAT", 8)) {
|
|
*operation = NGCFlowCtrl_EndRepeat;
|
|
*pos += 8;
|
|
} else if (!strncmp(line + *pos, "NDSUB", 5)) {
|
|
*operation = NGCFlowCtrl_EndSub;
|
|
*pos += 5;
|
|
} else if (!strncmp(line + *pos, "RROR", 4)) {
|
|
*operation = NGCFlowCtrl_RaiseError;
|
|
*pos += 4;
|
|
} else
|
|
status = Status_FlowControlSyntaxError; // Unknown statement name starting with E
|
|
break;
|
|
|
|
case 'I':
|
|
if(line[*pos] == 'F') {
|
|
*operation = NGCFlowCtrl_If;
|
|
(*pos)++;
|
|
} else
|
|
status = Status_FlowControlSyntaxError; // Unknown statement name starting with F
|
|
break;
|
|
|
|
case 'R':
|
|
if (!strncmp(line + *pos, "EPEAT", 5)) {
|
|
*operation = NGCFlowCtrl_Repeat;
|
|
*pos += 5;
|
|
} else if (!strncmp(line + *pos, "ETURN", 5)) {
|
|
*operation = NGCFlowCtrl_Return;
|
|
*pos += 5;
|
|
} else
|
|
status = Status_FlowControlSyntaxError; // Unknown statement name starting with R
|
|
break;
|
|
|
|
case 'S':
|
|
if (!strncmp(line + *pos, "UB", 2)) {
|
|
*operation = NGCFlowCtrl_Sub;
|
|
*pos += 2;
|
|
} else
|
|
status = Status_FlowControlSyntaxError; // Unknown statement name starting with S
|
|
break;
|
|
|
|
case 'W':
|
|
if (!strncmp(line + *pos, "HILE", 4)) {
|
|
*operation = NGCFlowCtrl_While;
|
|
*pos += 4;
|
|
} else
|
|
status = Status_FlowControlSyntaxError; // Unknown statement name starting with W
|
|
break;
|
|
|
|
default:
|
|
status = Status_FlowControlSyntaxError; // Unknown statement
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
static void clear_subs (vfs_file_t *file)
|
|
{
|
|
ngc_sub_t *current = subs, *prev = NULL, *next;
|
|
|
|
subs = NULL;
|
|
|
|
while(current) {
|
|
next = current->next;
|
|
if(file == NULL || file == current->file) {
|
|
free(current);
|
|
if(prev)
|
|
prev->next = next;
|
|
} else {
|
|
if(subs == NULL)
|
|
subs = current;
|
|
prev = current;
|
|
}
|
|
current = next;
|
|
}
|
|
}
|
|
|
|
static status_code_t stack_push (uint32_t o_label, ngc_cmd_t operation)
|
|
{
|
|
if(stack_idx < (NGC_STACK_DEPTH - 1) && (operation != NGCFlowCtrl_Call || ngc_call_push(&stack[stack_idx + 1]))) {
|
|
stack[++stack_idx].o_label = o_label;
|
|
stack[stack_idx].file = hal.stream.file;
|
|
stack[stack_idx].operation = operation;
|
|
stack[stack_idx].sub = exec_sub;
|
|
return Status_OK;
|
|
}
|
|
|
|
return Status_FlowControlStackOverflow;
|
|
}
|
|
|
|
static bool stack_pull (void)
|
|
{
|
|
bool ok;
|
|
|
|
if((ok = stack_idx >= 0)) {
|
|
if(stack[stack_idx].expr)
|
|
free(stack[stack_idx].expr);
|
|
if(stack[stack_idx].operation == NGCFlowCtrl_Call)
|
|
ngc_call_pop();
|
|
memset(&stack[stack_idx], 0, sizeof(ngc_stack_entry_t));
|
|
stack_idx--;
|
|
}
|
|
|
|
return ok;
|
|
}
|
|
|
|
static void stack_unwind_sub (uint32_t o_label)
|
|
{
|
|
while(stack_idx >= 0 && stack[stack_idx].o_label != o_label)
|
|
stack_pull();
|
|
|
|
if(stack_idx >= 0) {
|
|
vfs_seek(stack[stack_idx].file, stack[stack_idx].file_pos);
|
|
stack_pull();
|
|
}
|
|
|
|
exec_sub = stack_idx >= 0 ? stack[stack_idx].sub : NULL;
|
|
}
|
|
|
|
// Public functions
|
|
|
|
void ngc_flowctrl_unwind_stack (vfs_file_t *file)
|
|
{
|
|
clear_subs(file);
|
|
while(stack_idx >= 0 && stack[stack_idx].file == file)
|
|
stack_pull();
|
|
}
|
|
|
|
void ngc_flowctrl_init (void)
|
|
{
|
|
clear_subs(NULL);
|
|
while(stack_idx >= 0)
|
|
stack_pull();
|
|
}
|
|
|
|
status_code_t ngc_flowctrl (uint32_t o_label, char *line, uint_fast8_t *pos, bool *skip)
|
|
{
|
|
float value;
|
|
bool skipping;
|
|
ngc_cmd_t operation, last_op;
|
|
|
|
status_code_t status;
|
|
|
|
if((status = read_command(line, pos, &operation)) != Status_OK)
|
|
return status;
|
|
|
|
skipping = skip_sub || (stack_idx >= 0 && stack[stack_idx].skip);
|
|
last_op = stack_idx >= 0 ? stack[stack_idx].operation : (skip_sub ? NGCFlowCtrl_Sub : NGCFlowCtrl_NoOp);
|
|
|
|
switch(operation) {
|
|
|
|
case NGCFlowCtrl_If:
|
|
if(!skipping && (status = ngc_eval_expression(line, pos, &value)) == Status_OK) {
|
|
if((status = stack_push(o_label, operation)) == Status_OK) {
|
|
stack[stack_idx].skip = value == 0.0f;
|
|
stack[stack_idx].handled = !stack[stack_idx].skip;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case NGCFlowCtrl_ElseIf:
|
|
if(last_op == NGCFlowCtrl_If || last_op == NGCFlowCtrl_ElseIf) {
|
|
if(o_label == stack[stack_idx].o_label &&
|
|
!(stack[stack_idx].skip = stack[stack_idx].handled) && !stack[stack_idx].handled &&
|
|
(status = ngc_eval_expression(line, pos, &value)) == Status_OK) {
|
|
if(!(stack[stack_idx].skip = value == 0.0f)) {
|
|
stack[stack_idx].operation = operation;
|
|
stack[stack_idx].handled = true;
|
|
}
|
|
}
|
|
} else if(!skipping)
|
|
status = Status_FlowControlSyntaxError;
|
|
break;
|
|
|
|
case NGCFlowCtrl_Else:
|
|
if(last_op == NGCFlowCtrl_If || last_op == NGCFlowCtrl_ElseIf) {
|
|
if(o_label == stack[stack_idx].o_label) {
|
|
if(!(stack[stack_idx].skip = stack[stack_idx].handled))
|
|
stack[stack_idx].operation = operation;
|
|
}
|
|
} else if(!skipping)
|
|
status = Status_FlowControlSyntaxError;
|
|
break;
|
|
|
|
case NGCFlowCtrl_EndIf:
|
|
if(last_op == NGCFlowCtrl_If || last_op == NGCFlowCtrl_ElseIf || last_op == NGCFlowCtrl_Else) {
|
|
if(o_label == stack[stack_idx].o_label)
|
|
stack_pull();
|
|
} else if(!skipping)
|
|
status = Status_FlowControlSyntaxError;
|
|
break;
|
|
|
|
case NGCFlowCtrl_Do:
|
|
if(hal.stream.file) {
|
|
if(!skipping && (status = stack_push(o_label, operation)) == Status_OK) {
|
|
stack[stack_idx].file_pos = vfs_tell(hal.stream.file);
|
|
stack[stack_idx].skip = false;
|
|
}
|
|
} else
|
|
status = Status_FlowControlNotExecutingMacro;
|
|
break;
|
|
|
|
case NGCFlowCtrl_While:
|
|
if(hal.stream.file) {
|
|
char *expr = line + *pos;
|
|
if(stack_idx >= 0 && stack[stack_idx].brk) {
|
|
if(last_op == NGCFlowCtrl_Do && o_label == stack[stack_idx].o_label)
|
|
stack_pull();
|
|
} else if(!skipping && (status = ngc_eval_expression(line, pos, &value)) == Status_OK) {
|
|
if(last_op == NGCFlowCtrl_Do) {
|
|
if(o_label == stack[stack_idx].o_label) {
|
|
if(value != 0.0f)
|
|
vfs_seek(stack[stack_idx].file, stack[stack_idx].file_pos);
|
|
else
|
|
stack_pull();
|
|
}
|
|
} else if((status = stack_push(o_label, operation)) == Status_OK) {
|
|
if(!(stack[stack_idx].skip = value == 0.0f)) {
|
|
if((stack[stack_idx].expr = malloc(strlen(expr) + 1))) {
|
|
strcpy(stack[stack_idx].expr, expr);
|
|
stack[stack_idx].file = hal.stream.file;
|
|
stack[stack_idx].file_pos = vfs_tell(hal.stream.file);
|
|
} else
|
|
status = Status_FlowControlOutOfMemory;
|
|
}
|
|
}
|
|
}
|
|
} else
|
|
status = Status_FlowControlNotExecutingMacro;
|
|
break;
|
|
|
|
case NGCFlowCtrl_EndWhile:
|
|
if(hal.stream.file) {
|
|
if(last_op == NGCFlowCtrl_While) {
|
|
if(!skipping && o_label == stack[stack_idx].o_label) {
|
|
uint_fast8_t pos = 0;
|
|
if(!stack[stack_idx].skip && (status = ngc_eval_expression(stack[stack_idx].expr, &pos, &value)) == Status_OK) {
|
|
if(!(stack[stack_idx].skip = value == 0))
|
|
vfs_seek(stack[stack_idx].file, stack[stack_idx].file_pos);
|
|
}
|
|
if(stack[stack_idx].skip)
|
|
stack_pull();
|
|
}
|
|
} else if(!skipping)
|
|
status = Status_FlowControlSyntaxError;
|
|
} else
|
|
status = Status_FlowControlNotExecutingMacro;
|
|
break;
|
|
|
|
case NGCFlowCtrl_Repeat:
|
|
if(hal.stream.file) {
|
|
if(!skipping && (status = ngc_eval_expression(line, pos, &value)) == Status_OK) {
|
|
if((status = stack_push(o_label, operation)) == Status_OK) {
|
|
if(!(stack[stack_idx].skip = value == 0.0f)) {
|
|
stack[stack_idx].file = hal.stream.file;
|
|
stack[stack_idx].file_pos = vfs_tell(hal.stream.file);
|
|
stack[stack_idx].repeats = (uint32_t)value;
|
|
}
|
|
}
|
|
}
|
|
} else
|
|
status = Status_FlowControlNotExecutingMacro;
|
|
break;
|
|
|
|
case NGCFlowCtrl_EndRepeat:
|
|
if(hal.stream.file) {
|
|
if(last_op == NGCFlowCtrl_Repeat) {
|
|
if(o_label == stack[stack_idx].o_label) {
|
|
if(!skipping && stack[stack_idx].repeats && --stack[stack_idx].repeats)
|
|
vfs_seek(stack[stack_idx].file, stack[stack_idx].file_pos);
|
|
else
|
|
stack_pull();
|
|
}
|
|
} else if(!skipping)
|
|
status = Status_FlowControlSyntaxError;
|
|
} else
|
|
status = Status_FlowControlNotExecutingMacro;
|
|
break;
|
|
|
|
case NGCFlowCtrl_Break:
|
|
if(hal.stream.file) {
|
|
if(!skipping) {
|
|
while(o_label != stack[stack_idx].o_label && stack_pull());
|
|
last_op = stack_idx >= 0 ? stack[stack_idx].operation : NGCFlowCtrl_NoOp;
|
|
if(last_op == NGCFlowCtrl_Do || last_op == NGCFlowCtrl_While || last_op == NGCFlowCtrl_Repeat) {
|
|
if(o_label == stack[stack_idx].o_label) {
|
|
stack[stack_idx].repeats = 0;
|
|
stack[stack_idx].brk = stack[stack_idx].skip = stack[stack_idx].handled = true;
|
|
}
|
|
} else
|
|
status = Status_FlowControlSyntaxError;
|
|
}
|
|
} else
|
|
status = Status_FlowControlNotExecutingMacro;
|
|
break;
|
|
|
|
case NGCFlowCtrl_Continue:
|
|
if(hal.stream.file) {
|
|
if(!skipping) {
|
|
while(o_label != stack[stack_idx].o_label && stack_pull());
|
|
if(stack_idx >= 0 && o_label == stack[stack_idx].o_label) switch(stack[stack_idx].operation) {
|
|
|
|
case NGCFlowCtrl_Repeat:
|
|
if(stack[stack_idx].repeats && --stack[stack_idx].repeats)
|
|
vfs_seek(stack[stack_idx].file, stack[stack_idx].file_pos);
|
|
else
|
|
stack_pull();
|
|
break;
|
|
|
|
case NGCFlowCtrl_Do:
|
|
vfs_seek(stack[stack_idx].file, stack[stack_idx].file_pos);
|
|
break;
|
|
|
|
case NGCFlowCtrl_While:
|
|
{
|
|
uint_fast8_t pos = 0;
|
|
if(!stack[stack_idx].skip && (status = ngc_eval_expression(stack[stack_idx].expr, &pos, &value)) == Status_OK) {
|
|
if(!(stack[stack_idx].skip = value == 0))
|
|
vfs_seek(stack[stack_idx].file, stack[stack_idx].file_pos);
|
|
}
|
|
if(stack[stack_idx].skip) {
|
|
if(stack[stack_idx].expr) {
|
|
free(stack[stack_idx].expr);
|
|
stack[stack_idx].expr = NULL;
|
|
}
|
|
stack_pull();
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
status = Status_FlowControlSyntaxError;
|
|
break;
|
|
} else
|
|
status = Status_FlowControlSyntaxError;
|
|
}
|
|
} else
|
|
status = Status_FlowControlNotExecutingMacro;
|
|
break;
|
|
|
|
case NGCFlowCtrl_RaiseAlarm:
|
|
if(!skipping && ngc_eval_expression(line, pos, &value) == Status_OK)
|
|
system_raise_alarm((alarm_code_t)value);
|
|
break;
|
|
|
|
case NGCFlowCtrl_RaiseError:
|
|
if(!skipping && ngc_eval_expression(line, pos, &value) == Status_OK)
|
|
status = (status_code_t)value;
|
|
break;
|
|
|
|
case NGCFlowCtrl_Sub:
|
|
if(hal.stream.file) {
|
|
ngc_sub_t *sub;
|
|
if((skip_sub = (sub = malloc(sizeof(ngc_sub_t))) != NULL)) {
|
|
sub->o_label = o_label;
|
|
sub->file = hal.stream.file;
|
|
sub->file_pos = vfs_tell(hal.stream.file);
|
|
sub->next = NULL;
|
|
if(subs == NULL)
|
|
subs = sub;
|
|
else {
|
|
ngc_sub_t *last = subs;
|
|
while(last->next)
|
|
last = last->next;
|
|
last->next = sub;
|
|
}
|
|
} // else out of memory
|
|
} else
|
|
status = Status_FlowControlNotExecutingMacro;
|
|
break;
|
|
|
|
case NGCFlowCtrl_EndSub:
|
|
if(hal.stream.file) {
|
|
if(!skip_sub)
|
|
stack_unwind_sub(o_label);
|
|
skip_sub = false;
|
|
} else
|
|
status = Status_FlowControlNotExecutingMacro;
|
|
break;
|
|
|
|
case NGCFlowCtrl_Call:
|
|
if(hal.stream.file) {
|
|
if(!skipping) {
|
|
|
|
ngc_sub_t *sub = subs;
|
|
do {
|
|
if(sub->o_label == o_label && sub->file == hal.stream.file)
|
|
break;
|
|
} while((sub = sub->next));
|
|
|
|
if(sub == NULL)
|
|
status = Status_FlowControlSyntaxError;
|
|
else {
|
|
|
|
float params[30];
|
|
ngc_param_id_t param_id = 1;
|
|
|
|
while(line[*pos] && status == Status_OK && param_id <= 30) {
|
|
status = ngc_eval_expression(line, pos, ¶ms[param_id - 1]);
|
|
param_id++;
|
|
}
|
|
|
|
if(status == Status_OK && param_id < 30) do {
|
|
ngc_param_get(param_id, ¶ms[param_id - 1]);
|
|
} while(++param_id <= 30);
|
|
|
|
if(status == Status_OK && (status = stack_push(o_label, operation)) == Status_OK) {
|
|
|
|
stack[stack_idx].sub = exec_sub = sub;
|
|
stack[stack_idx].file = hal.stream.file;
|
|
stack[stack_idx].file_pos = vfs_tell(hal.stream.file);
|
|
stack[stack_idx].repeats = 1;
|
|
|
|
for(param_id = 1; param_id <= 30; param_id++) {
|
|
if(params[param_id - 1] != 0.0f) {
|
|
if(!ngc_param_set(param_id, params[param_id - 1]))
|
|
status = Status_FlowControlOutOfMemory;
|
|
}
|
|
}
|
|
|
|
if(status == Status_OK)
|
|
vfs_seek(sub->file, sub->file_pos);
|
|
}
|
|
}
|
|
}
|
|
} else
|
|
status = Status_FlowControlNotExecutingMacro;
|
|
break;
|
|
|
|
case NGCFlowCtrl_Return:
|
|
if(hal.stream.file) {
|
|
if(!skipping) {
|
|
|
|
bool g65_return = false;
|
|
|
|
if(exec_sub)
|
|
stack_unwind_sub(o_label);
|
|
else if((g65_return = !!grbl.on_macro_return))
|
|
ngc_flowctrl_unwind_stack(stack[stack_idx].file);
|
|
|
|
if(ngc_eval_expression(line, pos, &value) == Status_OK) {
|
|
ngc_named_param_set("_value", value);
|
|
ngc_named_param_set("_value_returned", 1.0f);
|
|
} else
|
|
ngc_named_param_set("_value_returned", 0.0f);
|
|
|
|
if(g65_return)
|
|
grbl.on_macro_return();
|
|
}
|
|
} else
|
|
status = Status_FlowControlNotExecutingMacro;
|
|
break;
|
|
|
|
default:
|
|
status = Status_FlowControlSyntaxError;
|
|
}
|
|
|
|
if(status != Status_OK) {
|
|
ngc_flowctrl_init();
|
|
*skip = false;
|
|
if(settings.flags.ngc_debug_out)
|
|
report_message(line, Message_Plain);
|
|
} else
|
|
*skip = skip_sub || (stack_idx >= 0 && stack[stack_idx].skip);
|
|
|
|
return status;
|
|
}
|
|
|
|
#endif // NGC_EXPRESSIONS_ENABLE
|