507 lines
25 KiB
C
507 lines
25 KiB
C
#include "sim_program.h"
|
|
|
|
#include <ctype.h>
|
|
#include <math.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
static uint64_t fnv1a64(const unsigned char *bytes, size_t length) {
|
|
uint64_t hash = UINT64_C(1469598103934665603);
|
|
size_t index;
|
|
for (index = 0; index < length; ++index) { hash ^= bytes[index]; hash *= UINT64_C(1099511628211); }
|
|
return hash;
|
|
}
|
|
|
|
static int control_line(const char *line) {
|
|
const char *cursor = line;
|
|
while (*cursor != '\0' && isspace((unsigned char)*cursor)) ++cursor;
|
|
if (*cursor == 'O' || *cursor == 'o') return 1;
|
|
if ((cursor[0] == 'M' || cursor[0] == 'm') && (cursor[1] == '8' || cursor[1] == '9')) return 1;
|
|
if ((cursor[0] == 'M' || cursor[0] == 'm') && (cursor[1] == '9' || cursor[1] == '8')) return 1;
|
|
return 0;
|
|
}
|
|
|
|
static uint32_t line_length(const char *line, uint32_t maximum) {
|
|
uint32_t length = 0;
|
|
while (length < maximum && line[length] != '\0' && line[length] != '\n' && line[length] != '\r') ++length;
|
|
return length;
|
|
}
|
|
|
|
static int span_equal(const char *value, size_t length, const char *expected) {
|
|
size_t index;
|
|
if (strlen(expected) != length) return 0;
|
|
for (index = 0; index < length; ++index)
|
|
if (tolower((unsigned char)value[index]) != tolower((unsigned char)expected[index])) return 0;
|
|
return 1;
|
|
}
|
|
|
|
static int parse_oword(const char *line, uint32_t length, char *name, size_t name_capacity,
|
|
const char **verb, size_t *verb_length, const char **arguments) {
|
|
const char *cursor = line;
|
|
const char *end = line + length;
|
|
size_t name_length = 0;
|
|
while (cursor < end && isspace((unsigned char)*cursor)) ++cursor;
|
|
if (cursor >= end || tolower((unsigned char)*cursor++) != 'o' || cursor >= end || *cursor++ != '<') return 0;
|
|
while (cursor < end && *cursor != '>') {
|
|
if (name_length + 1u >= name_capacity) return 0;
|
|
name[name_length++] = (char)tolower((unsigned char)*cursor++);
|
|
}
|
|
if (cursor >= end || *cursor++ != '>' || name_length == 0) return 0;
|
|
name[name_length] = '\0';
|
|
while (cursor < end && isspace((unsigned char)*cursor)) ++cursor;
|
|
*verb = cursor;
|
|
while (cursor < end && isalpha((unsigned char)*cursor)) ++cursor;
|
|
*verb_length = (size_t)(cursor - *verb);
|
|
while (cursor < end && isspace((unsigned char)*cursor)) ++cursor;
|
|
*arguments = cursor;
|
|
return *verb_length > 0;
|
|
}
|
|
|
|
int32_t sim_program_requires_subroutines(const char *source, uint32_t length) {
|
|
uint32_t offset = 0;
|
|
if (source == NULL || length == 0) return SIM_PROGRAM_ERR_ARGUMENT;
|
|
if (length > SIM_PROGRAM_MAX_SOURCE_BYTES) return SIM_PROGRAM_ERR_RANGE;
|
|
while (offset < length) {
|
|
uint32_t available = length - offset;
|
|
uint32_t line_size = line_length(source + offset, available);
|
|
char name[SIM_PROGRAM_SUBROUTINE_NAME_CAPACITY] = {0};
|
|
const char *verb = NULL, *arguments = NULL;
|
|
size_t verb_length = 0;
|
|
if (parse_oword(source + offset, line_size, name, sizeof(name), &verb, &verb_length, &arguments) && span_equal(verb, verb_length, "call")) return 1;
|
|
offset += line_size;
|
|
while (offset < length && (source[offset] == '\n' || source[offset] == '\r')) ++offset;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int ensure_source_capacity(sim_program *program, uint32_t required) {
|
|
uint32_t capacity;
|
|
char *source;
|
|
if (required <= program->source_capacity) return SIM_PROGRAM_OK;
|
|
capacity = program->source_capacity == 0 ? SIM_PROGRAM_SOURCE_CAPACITY : program->source_capacity;
|
|
while (capacity < required) {
|
|
if (capacity >= SIM_PROGRAM_MAX_SOURCE_BYTES / 2u) { capacity = SIM_PROGRAM_MAX_SOURCE_BYTES; break; }
|
|
capacity *= 2u;
|
|
}
|
|
if (capacity < required || capacity > SIM_PROGRAM_MAX_SOURCE_BYTES) return SIM_PROGRAM_ERR_RANGE;
|
|
source = (char *)realloc(program->source, capacity);
|
|
if (source == NULL) return SIM_PROGRAM_ERR_RANGE;
|
|
program->source = source;
|
|
program->source_capacity = capacity;
|
|
return SIM_PROGRAM_OK;
|
|
}
|
|
|
|
static int ensure_line_capacity(sim_program *program, uint32_t required) {
|
|
uint32_t capacity;
|
|
uint32_t *offsets;
|
|
if (required <= program->line_capacity) return SIM_PROGRAM_OK;
|
|
capacity = program->line_capacity == 0 ? SIM_PROGRAM_LINE_CAPACITY + 1u : program->line_capacity;
|
|
while (capacity < required) {
|
|
if (capacity >= SIM_PROGRAM_MAX_LINE_COUNT / 2u) { capacity = SIM_PROGRAM_MAX_LINE_COUNT + 1u; break; }
|
|
capacity *= 2u;
|
|
}
|
|
if (capacity > SIM_PROGRAM_MAX_LINE_COUNT + 1u) return SIM_PROGRAM_ERR_RANGE;
|
|
offsets = (uint32_t *)realloc(program->line_offsets, (size_t)capacity * sizeof(*offsets));
|
|
if (offsets == NULL) return SIM_PROGRAM_ERR_RANGE;
|
|
program->line_offsets = offsets;
|
|
program->line_capacity = capacity;
|
|
return SIM_PROGRAM_OK;
|
|
}
|
|
|
|
static int reindex_source(sim_program *program) {
|
|
uint32_t index;
|
|
if (ensure_line_capacity(program, 2u) != SIM_PROGRAM_OK) return SIM_PROGRAM_ERR_RANGE;
|
|
program->line_count = program->source_length == 0 ? 0 : 1;
|
|
program->line_offsets[0] = 0;
|
|
for (index = 0; index < program->source_length; ++index) if (program->source[index] == '\n' && index + 1u < program->source_length) {
|
|
if (program->line_count >= SIM_PROGRAM_MAX_LINE_COUNT || ensure_line_capacity(program, program->line_count + 2u) != SIM_PROGRAM_OK) return SIM_PROGRAM_ERR_RANGE;
|
|
program->line_offsets[program->line_count++] = index + 1u;
|
|
}
|
|
program->line_offsets[program->line_count] = program->source_length;
|
|
return SIM_PROGRAM_OK;
|
|
}
|
|
|
|
static int append_source(sim_program *program, const char *source, uint32_t length) {
|
|
uint32_t separator;
|
|
uint64_t required;
|
|
if (source == NULL || length == 0) return SIM_PROGRAM_ERR_ARGUMENT;
|
|
separator = program->source_length > 0 && program->source[program->source_length - 1u] != '\n' ? 1u : 0u;
|
|
required = (uint64_t)program->source_length + separator + length + 1u;
|
|
if (required > SIM_PROGRAM_MAX_SOURCE_BYTES + 1ull || ensure_source_capacity(program, (uint32_t)required) != SIM_PROGRAM_OK) return SIM_PROGRAM_ERR_RANGE;
|
|
if (separator) program->source[program->source_length++] = '\n';
|
|
memcpy(program->source + program->source_length, source, length);
|
|
program->source_length += length;
|
|
program->source[program->source_length] = '\0';
|
|
return reindex_source(program);
|
|
}
|
|
|
|
static int scan_subroutines(sim_program *program) {
|
|
uint32_t line;
|
|
program->subroutine_count = 0;
|
|
for (line = program->main_line_count; line < program->line_count; ++line) {
|
|
const char *start = program->source + program->line_offsets[line];
|
|
uint32_t maximum = program->line_offsets[line + 1u] - program->line_offsets[line];
|
|
uint32_t length = line_length(start, maximum);
|
|
char name[SIM_PROGRAM_SUBROUTINE_NAME_CAPACITY] = {0};
|
|
const char *verb = NULL, *arguments = NULL;
|
|
size_t verb_length = 0;
|
|
uint32_t end_line;
|
|
if (!parse_oword(start, length, name, sizeof(name), &verb, &verb_length, &arguments) || !span_equal(verb, verb_length, "sub")) continue;
|
|
for (end_line = line + 1u; end_line < program->line_count; ++end_line) {
|
|
const char *end_start = program->source + program->line_offsets[end_line];
|
|
uint32_t end_maximum = program->line_offsets[end_line + 1u] - program->line_offsets[end_line];
|
|
uint32_t end_length = line_length(end_start, end_maximum);
|
|
char end_name[SIM_PROGRAM_SUBROUTINE_NAME_CAPACITY] = {0};
|
|
const char *end_verb = NULL, *end_arguments = NULL;
|
|
size_t end_verb_length = 0;
|
|
if (parse_oword(end_start, end_length, end_name, sizeof(end_name), &end_verb, &end_verb_length, &end_arguments) && strcmp(name, end_name) == 0 && span_equal(end_verb, end_verb_length, "endsub")) break;
|
|
}
|
|
if (end_line >= program->line_count || program->subroutine_count >= SIM_PROGRAM_SUBROUTINE_CAPACITY) return SIM_PROGRAM_ERR_CONTROL;
|
|
strncpy(program->subroutines[program->subroutine_count].name, name, SIM_PROGRAM_SUBROUTINE_NAME_CAPACITY - 1u);
|
|
program->subroutines[program->subroutine_count].body_line = line + 1u;
|
|
program->subroutines[program->subroutine_count].end_line = end_line;
|
|
program->subroutine_count += 1u;
|
|
line = end_line;
|
|
}
|
|
return SIM_PROGRAM_OK;
|
|
}
|
|
|
|
static const sim_program_subroutine *find_subroutine(const sim_program *program, const char *name) {
|
|
uint32_t index;
|
|
for (index = 0; index < program->subroutine_count; ++index)
|
|
if (strcmp(program->subroutines[index].name, name) == 0) return &program->subroutines[index];
|
|
return NULL;
|
|
}
|
|
|
|
static int evaluate_call_arguments(sim_program *program, const char *cursor, const char *end,
|
|
double *values, uint32_t *count) {
|
|
uint32_t item = 0;
|
|
while (cursor < end) {
|
|
const char *start;
|
|
const char *finish;
|
|
int depth = 0;
|
|
char assignment[512];
|
|
int written;
|
|
while (cursor < end && isspace((unsigned char)*cursor)) ++cursor;
|
|
if (cursor >= end || *cursor == ';' || *cursor == '(') break;
|
|
if (item >= SIM_PROGRAM_CALL_PARAMETER_CAPACITY) return SIM_PROGRAM_ERR_CONTROL;
|
|
start = cursor;
|
|
if (*cursor == '[') {
|
|
do {
|
|
if (*cursor == '[') depth += 1;
|
|
else if (*cursor == ']') depth -= 1;
|
|
++cursor;
|
|
} while (cursor < end && depth > 0);
|
|
if (depth != 0) return SIM_PROGRAM_ERR_CONTROL;
|
|
finish = cursor;
|
|
} else {
|
|
while (cursor < end && !isspace((unsigned char)*cursor) && *cursor != ';' && *cursor != '(') ++cursor;
|
|
finish = cursor;
|
|
}
|
|
written = snprintf(assignment, sizeof(assignment), "#5000=%.*s", (int)(finish - start), start);
|
|
if (written <= 0 || (size_t)written >= sizeof(assignment) || sim_interpreter_execute(&program->interpreter, assignment, (uint32_t)written) != SIM_INTERP_OK || sim_interpreter_parameter_get(&program->interpreter, 5000, &values[item]) != SIM_INTERP_OK) return SIM_PROGRAM_ERR_INTERPRETER;
|
|
item += 1u;
|
|
}
|
|
*count = item;
|
|
return SIM_PROGRAM_OK;
|
|
}
|
|
|
|
static int begin_call(sim_program *program, const char *name, const char *arguments, const char *end) {
|
|
const sim_program_subroutine *subroutine = find_subroutine(program, name);
|
|
sim_program_call_frame *frame;
|
|
double values[SIM_PROGRAM_CALL_PARAMETER_CAPACITY] = {0};
|
|
uint32_t count = 0, index;
|
|
int result;
|
|
if (subroutine == NULL || program->call_depth >= SIM_PROGRAM_CALL_DEPTH) return SIM_PROGRAM_ERR_CONTROL;
|
|
result = evaluate_call_arguments(program, arguments, end, values, &count);
|
|
if (result != SIM_PROGRAM_OK) return result;
|
|
frame = &program->call_stack[program->call_depth++];
|
|
frame->return_line = program->current_line;
|
|
for (index = 0; index < SIM_PROGRAM_CALL_PARAMETER_CAPACITY; ++index) {
|
|
if (sim_interpreter_parameter_get(&program->interpreter, index + 1u, &frame->parameters[index]) != SIM_INTERP_OK) return SIM_PROGRAM_ERR_INTERPRETER;
|
|
if (sim_interpreter_parameter_set(&program->interpreter, index + 1u, index < count ? values[index] : 0.0) != SIM_INTERP_OK) return SIM_PROGRAM_ERR_INTERPRETER;
|
|
}
|
|
program->current_line = subroutine->body_line;
|
|
return SIM_PROGRAM_OK;
|
|
}
|
|
|
|
static int end_call(sim_program *program) {
|
|
sim_program_call_frame *frame;
|
|
uint32_t index;
|
|
if (program->call_depth == 0) return SIM_PROGRAM_ERR_CONTROL;
|
|
frame = &program->call_stack[--program->call_depth];
|
|
for (index = 0; index < SIM_PROGRAM_CALL_PARAMETER_CAPACITY; ++index)
|
|
if (sim_interpreter_parameter_set(&program->interpreter, index + 1u, frame->parameters[index]) != SIM_INTERP_OK) return SIM_PROGRAM_ERR_INTERPRETER;
|
|
program->current_line = frame->return_line;
|
|
return SIM_PROGRAM_OK;
|
|
}
|
|
|
|
static int switch_mcode(const char *block, uint32_t length) {
|
|
const char *cursor = block;
|
|
const char *end = block + length;
|
|
while (cursor < end) {
|
|
if (*cursor == ';') break;
|
|
if (*cursor == '(') { while (cursor < end && *cursor++ != ')') {} continue; }
|
|
if (toupper((unsigned char)*cursor) == 'M') {
|
|
char *number_end = NULL;
|
|
long code = strtol(cursor + 1, &number_end, 10);
|
|
if (number_end != cursor + 1 && (code == 428 || code == 429 || code == 430)) return (int)code;
|
|
}
|
|
++cursor;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int parse_motion_control(sim_program *program, const char *block, uint32_t length) {
|
|
const char *cursor = block;
|
|
const char *end = block + length;
|
|
int active = 0;
|
|
double path = 0.0;
|
|
double naive = 0.0;
|
|
while (cursor < end) {
|
|
char letter;
|
|
char *number_end;
|
|
double value;
|
|
while (cursor < end && isspace((unsigned char)*cursor)) ++cursor;
|
|
if (cursor >= end || *cursor == ';') break;
|
|
if (*cursor == '(') { while (cursor < end && *cursor++ != ')') {} continue; }
|
|
if (!isalpha((unsigned char)*cursor)) { ++cursor; continue; }
|
|
letter = (char)toupper((unsigned char)*cursor++);
|
|
value = strtod(cursor, &number_end);
|
|
if (number_end == cursor) continue;
|
|
cursor = number_end;
|
|
if (letter == 'G') {
|
|
if (fabs(value - 61.0) <= 1e-12) active = SIM_PROGRAM_MOTION_EXACT_PATH;
|
|
else if (fabs(value - 61.1) <= 1e-12) active = SIM_PROGRAM_MOTION_EXACT_STOP;
|
|
else if (fabs(value - 64.0) <= 1e-12) active = SIM_PROGRAM_MOTION_CONTINUOUS;
|
|
else active = 0;
|
|
if (active != 0 || fabs(value - 64.0) <= 1e-12) {
|
|
program->motion_control_mode = active;
|
|
path = 0.0;
|
|
naive = 0.0;
|
|
}
|
|
} else if (active == SIM_PROGRAM_MOTION_CONTINUOUS && (letter == 'P' || letter == 'Q')) {
|
|
if (!isfinite(value) || value < 0.0) return SIM_PROGRAM_ERR_RANGE;
|
|
if (letter == 'P') path = value;
|
|
else naive = value;
|
|
program->path_tolerance = path;
|
|
program->naive_cam_tolerance = naive;
|
|
}
|
|
}
|
|
return SIM_PROGRAM_OK;
|
|
}
|
|
|
|
static void reset_runtime(sim_program *program) {
|
|
sim_interpreter_reset(&program->interpreter);
|
|
program->source_length = 0;
|
|
program->line_count = 0;
|
|
program->main_line_count = 0;
|
|
program->current_line = 0;
|
|
program->status = SIM_PROGRAM_IDLE;
|
|
program->last_error = 0;
|
|
program->error_line = 0;
|
|
program->has_motion = 0;
|
|
program->subroutine_count = 0;
|
|
program->call_depth = 0;
|
|
program->has_control = 0;
|
|
program->control_code = 0;
|
|
program->rapid_velocity = 35.0;
|
|
program->motion_control_mode = SIM_PROGRAM_MOTION_CONTINUOUS;
|
|
program->path_tolerance = 0.0;
|
|
program->naive_cam_tolerance = 0.0;
|
|
memset(&program->motion, 0, sizeof(program->motion));
|
|
memset(program->subroutines, 0, sizeof(program->subroutines));
|
|
memset(program->call_stack, 0, sizeof(program->call_stack));
|
|
}
|
|
|
|
uint32_t sim_program_size(void) { return (uint32_t)sizeof(sim_program); }
|
|
|
|
int32_t sim_program_init(sim_program *program) {
|
|
if (program == NULL) return SIM_PROGRAM_ERR_ARGUMENT;
|
|
memset(program, 0, sizeof(*program));
|
|
if (sim_interpreter_init(&program->interpreter) != SIM_INTERP_OK) return SIM_PROGRAM_ERR_INTERPRETER;
|
|
program->status = SIM_PROGRAM_IDLE;
|
|
return SIM_PROGRAM_OK;
|
|
}
|
|
|
|
int32_t sim_program_reset(sim_program *program) {
|
|
if (program == NULL) return SIM_PROGRAM_ERR_ARGUMENT;
|
|
if (sim_interpreter_reset(&program->interpreter) != SIM_INTERP_OK) return SIM_PROGRAM_ERR_INTERPRETER;
|
|
free(program->source);
|
|
free(program->line_offsets);
|
|
program->source = NULL;
|
|
program->line_offsets = NULL;
|
|
program->source_capacity = 0;
|
|
program->line_capacity = 0;
|
|
reset_runtime(program);
|
|
return SIM_PROGRAM_OK;
|
|
}
|
|
|
|
void sim_program_destroy(sim_program *program) {
|
|
if (program == NULL) return;
|
|
(void)sim_program_reset(program);
|
|
}
|
|
|
|
int32_t sim_program_load(sim_program *program, const char *source, uint32_t length) {
|
|
if (program == NULL || source == NULL) return SIM_PROGRAM_ERR_ARGUMENT;
|
|
if (length == 0 || length > SIM_PROGRAM_MAX_SOURCE_BYTES) return SIM_PROGRAM_ERR_RANGE;
|
|
if (sim_program_reset(program) != SIM_PROGRAM_OK) return SIM_PROGRAM_ERR_INTERPRETER;
|
|
if (append_source(program, source, length) != SIM_PROGRAM_OK) return SIM_PROGRAM_ERR_RANGE;
|
|
program->main_line_count = program->line_count;
|
|
program->status = SIM_PROGRAM_LOADED;
|
|
return SIM_PROGRAM_OK;
|
|
}
|
|
|
|
int32_t sim_program_add_subroutine(sim_program *program, const char *source, uint32_t length) {
|
|
int result;
|
|
if (program == NULL || source == NULL || program->status != SIM_PROGRAM_LOADED) return SIM_PROGRAM_ERR_STATE;
|
|
result = append_source(program, source, length);
|
|
if (result != SIM_PROGRAM_OK) return result;
|
|
return scan_subroutines(program);
|
|
}
|
|
|
|
int32_t sim_program_set_rapid_velocity(sim_program *program, double velocity) {
|
|
if (program == NULL || !isfinite(velocity) || velocity <= 0.0 || velocity > 1e9) return SIM_PROGRAM_ERR_RANGE;
|
|
program->rapid_velocity = velocity;
|
|
return SIM_PROGRAM_OK;
|
|
}
|
|
|
|
int32_t sim_program_start(sim_program *program) {
|
|
if (program == NULL || program->status == SIM_PROGRAM_IDLE) return SIM_PROGRAM_ERR_STATE;
|
|
if (program->status == SIM_PROGRAM_COMPLETED || program->status == SIM_PROGRAM_ABORTED) return SIM_PROGRAM_ERR_STATE;
|
|
program->status = SIM_PROGRAM_RUNNING;
|
|
return SIM_PROGRAM_OK;
|
|
}
|
|
|
|
int32_t sim_program_pause(sim_program *program) {
|
|
if (program == NULL || program->status != SIM_PROGRAM_RUNNING) return SIM_PROGRAM_ERR_STATE;
|
|
program->status = SIM_PROGRAM_PAUSED;
|
|
return SIM_PROGRAM_OK;
|
|
}
|
|
|
|
int32_t sim_program_resume(sim_program *program) {
|
|
if (program == NULL || program->status != SIM_PROGRAM_PAUSED) return SIM_PROGRAM_ERR_STATE;
|
|
program->status = SIM_PROGRAM_RUNNING;
|
|
return SIM_PROGRAM_OK;
|
|
}
|
|
|
|
int32_t sim_program_abort(sim_program *program) {
|
|
if (program == NULL) return SIM_PROGRAM_ERR_ARGUMENT;
|
|
program->status = SIM_PROGRAM_ABORTED;
|
|
program->has_motion = 0;
|
|
return SIM_PROGRAM_OK;
|
|
}
|
|
|
|
int32_t sim_program_execute_block(sim_program *program, const char *block, uint32_t length) {
|
|
sim_posemath_segment segment;
|
|
int result;
|
|
if (program == NULL || block == NULL || length == 0) return SIM_PROGRAM_ERR_ARGUMENT;
|
|
if (control_line(block)) return SIM_PROGRAM_ERR_CONTROL;
|
|
result = parse_motion_control(program, block, length);
|
|
if (result != SIM_PROGRAM_OK) return result;
|
|
program->has_control = 0;
|
|
program->control_code = switch_mcode(block, length);
|
|
if (program->control_code != 0) program->has_control = 1;
|
|
result = sim_interpreter_execute(&program->interpreter, block, length);
|
|
if (result != SIM_INTERP_OK) { program->last_error = result; return SIM_PROGRAM_ERR_INTERPRETER; }
|
|
memset(&segment, 0, sizeof(segment));
|
|
if (sim_interpreter_read_motion(&program->interpreter, &segment) == SIM_INTERP_OK && segment.motion_type != 0) {
|
|
if (program->interpreter.state.motion_mode == 0) segment.velocity = program->rapid_velocity;
|
|
program->motion = segment; program->has_motion = 1;
|
|
} else program->has_motion = 0;
|
|
return SIM_PROGRAM_OK;
|
|
}
|
|
|
|
int32_t sim_program_step(sim_program *program) {
|
|
uint32_t line;
|
|
if (program == NULL) return SIM_PROGRAM_ERR_ARGUMENT;
|
|
if (program->status == SIM_PROGRAM_PAUSED) return SIM_PROGRAM_ERR_STATE;
|
|
if (program->status != SIM_PROGRAM_RUNNING) return SIM_PROGRAM_ERR_STATE;
|
|
program->has_motion = 0;
|
|
program->has_control = 0;
|
|
program->control_code = 0;
|
|
while (program->current_line < program->line_count) {
|
|
const char *line_start = program->source + program->line_offsets[program->current_line];
|
|
uint32_t maximum = program->line_offsets[program->current_line + 1u] - program->line_offsets[program->current_line];
|
|
uint32_t length = line_length(line_start, maximum);
|
|
program->current_line += 1;
|
|
line = program->current_line;
|
|
while (length > 0 && isspace((unsigned char)line_start[length - 1u])) --length;
|
|
if (length == 0 || line_start[0] == ';') continue;
|
|
{
|
|
char name[SIM_PROGRAM_SUBROUTINE_NAME_CAPACITY] = {0};
|
|
const char *verb = NULL, *arguments = NULL;
|
|
size_t verb_length = 0;
|
|
if (parse_oword(line_start, length, name, sizeof(name), &verb, &verb_length, &arguments)) {
|
|
int control_result;
|
|
if (span_equal(verb, verb_length, "call")) control_result = begin_call(program, name, arguments, line_start + length);
|
|
else if (span_equal(verb, verb_length, "endsub")) control_result = end_call(program);
|
|
else if (span_equal(verb, verb_length, "sub")) {
|
|
const sim_program_subroutine *declared = find_subroutine(program, name);
|
|
program->current_line = declared ? declared->end_line + 1u : program->line_count;
|
|
continue;
|
|
}
|
|
else control_result = SIM_PROGRAM_ERR_CONTROL;
|
|
if (control_result != SIM_PROGRAM_OK) { program->last_error = control_result; program->error_line = line; program->status = SIM_PROGRAM_ERROR; return control_result; }
|
|
return SIM_PROGRAM_OK;
|
|
}
|
|
}
|
|
if (control_line(line_start)) { program->last_error = SIM_PROGRAM_ERR_CONTROL; program->error_line = line; program->status = SIM_PROGRAM_ERROR; return SIM_PROGRAM_ERR_CONTROL; }
|
|
if (sim_program_execute_block(program, line_start, length) != SIM_PROGRAM_OK) { program->error_line = line; program->status = SIM_PROGRAM_ERROR; return SIM_PROGRAM_ERR_INTERPRETER; }
|
|
if (program->interpreter.state.program_end || (program->call_depth == 0 && program->current_line >= program->main_line_count)) program->status = SIM_PROGRAM_COMPLETED;
|
|
return SIM_PROGRAM_OK;
|
|
}
|
|
program->status = SIM_PROGRAM_COMPLETED;
|
|
return SIM_PROGRAM_OK;
|
|
}
|
|
|
|
int32_t sim_program_seek(sim_program *program, uint32_t line) {
|
|
if (program == NULL || line > program->line_count) return SIM_PROGRAM_ERR_RANGE;
|
|
if (sim_interpreter_reset(&program->interpreter) != SIM_INTERP_OK) return SIM_PROGRAM_ERR_INTERPRETER;
|
|
program->current_line = 0; program->status = SIM_PROGRAM_PAUSED; program->has_motion = 0;
|
|
while (program->current_line < line) {
|
|
int result = sim_program_step(program);
|
|
if (result != SIM_PROGRAM_OK && result != SIM_PROGRAM_ERR_STATE) return result;
|
|
if (program->status == SIM_PROGRAM_COMPLETED) break;
|
|
program->status = SIM_PROGRAM_PAUSED;
|
|
if (program->current_line >= line) break;
|
|
program->status = SIM_PROGRAM_RUNNING;
|
|
}
|
|
program->status = SIM_PROGRAM_PAUSED;
|
|
return SIM_PROGRAM_OK;
|
|
}
|
|
|
|
int32_t sim_program_read_motion(const sim_program *program, sim_posemath_segment *segment) {
|
|
if (program == NULL || segment == NULL) return SIM_PROGRAM_ERR_ARGUMENT;
|
|
if (!program->has_motion) return SIM_PROGRAM_ERR_STATE;
|
|
*segment = program->motion;
|
|
return SIM_PROGRAM_OK;
|
|
}
|
|
|
|
int32_t sim_program_clear_motion(sim_program *program) {
|
|
if (program == NULL) return SIM_PROGRAM_ERR_ARGUMENT;
|
|
program->has_motion = 0;
|
|
memset(&program->motion, 0, sizeof(program->motion));
|
|
return SIM_PROGRAM_OK;
|
|
}
|
|
|
|
uint32_t sim_program_canonical(const sim_program *program, char *output, uint32_t capacity) {
|
|
char discard[1];
|
|
char *destination = output == NULL ? discard : output;
|
|
uint32_t destination_capacity = output == NULL ? 1 : capacity;
|
|
int written;
|
|
if (program == NULL) return 0;
|
|
written = snprintf(destination, destination_capacity,
|
|
"{\"call_depth\":%u,\"control_code\":%d,\"current_line\":%u,\"error_line\":%u,\"feed\":%.17g,\"has_control\":%u,\"has_motion\":%u,\"last_error\":%d,\"line_count\":%u,\"main_line_count\":%u,\"motion_control_mode\":%d,\"path_tolerance\":%.17g,\"naive_cam_tolerance\":%.17g,\"motion_mode\":%d,\"plane\":%d,\"rapid_velocity\":%.17g,\"schema\":\"sim_program.v1\",\"speed\":%.17g,\"spindle_mode\":%d,\"status\":%u,\"subroutine_count\":%u,\"source_length\":%u,\"units\":%d,\"distance_mode\":%d,\"interpreter_state_hash\":\"%llu\"}",
|
|
program->call_depth, program->control_code, program->current_line, program->error_line, program->interpreter.state.feed, program->has_control, program->has_motion, program->last_error, program->line_count, program->main_line_count, program->motion_control_mode, program->path_tolerance, program->naive_cam_tolerance, program->interpreter.state.motion_mode, program->interpreter.state.plane, program->rapid_velocity, program->interpreter.state.speed, program->interpreter.state.spindle_mode, program->status, program->subroutine_count, program->source_length, program->interpreter.state.units, program->interpreter.state.distance_mode,
|
|
(unsigned long long)sim_interpreter_state_hash(&program->interpreter));
|
|
return written < 0 ? 0 : (uint32_t)written + 1u;
|
|
}
|
|
|
|
uint64_t sim_program_state_hash(const sim_program *program) {
|
|
char canonical[SIM_PROGRAM_CANONICAL_CAPACITY];
|
|
uint32_t length = sim_program_canonical(program, canonical, sizeof(canonical));
|
|
return length == 0 ? 0 : fnv1a64((const unsigned char *)canonical, length - 1u);
|
|
}
|