/*
 *                            COPYRIGHT
 *
 *  camv-rnd - electronics-related CAM viewer
 *  Copyright (C) 2019,2024 Tibor 'Igor2' Palinkas
 *
 *  This program 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 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program 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 this program; if not, write to the Free Software
 *  Foundation, Inc., 31 Milk Street, # 960789 Boston, MA 02196 USA.
 *
 *  Contact:
 *    Project page: http://repo.hu/projects/camv-rnd
 *    lead developer: http://repo.hu/projects/camv-rnd/contact.html
 *    mailing list: pcb-rnd (at) list.repo.hu (send "subscribe")
 */

#ifndef GCODE_VM_H
#define GCODE_VM_H

#include <genvector/vtd0.h>

typedef enum {
	PUSH_NUM = 1000,
	ADD, SUB, MUL, DIV,
	ASSIGN, PARAM, DO
} gcode_i_t;

typedef struct gcode_inst_s gcode_inst_t;

struct gcode_inst_s {
	int inst; /* a letter or a T_ or gcode_i_t */
	double payload;
	long lineno; /* in the file */
	unsigned do_pop:1;
	gcode_inst_t *next;
};

typedef enum {
	GCP_DELAY_OFF = 0,  /* start collecting instructions on the first (main program) list */
	GCP_DELAY_ON,       /* start collecting instructions on the 2nd (temporary list) */
	GCP_DELAY_APPLY     /* append 2nd list to the main and empty it */
} gcode_delay_t;

typedef struct gcode_prg_s gcode_prg_t;
struct gcode_prg_s {
	struct {
		unsigned laser:1;
	} cfg;

	/*** shared ***/
	void (*error)(gcode_prg_t *ctx, int runtime, const char *msg); /* provided by the caller */
	void (*set_lineno)(gcode_prg_t *ctx, int lineno); /* optional: provided by the caller */
	long ip;
	int lineno;

	/*** lexer states ***/
	int (*get_char)(gcode_prg_t *ctx);   /* provided by the caller */
	int in_eof, pushback, subseq_error;
	long line, col, used, alloced;
	char *buff;

	/*** compiler states ***/
	gcode_inst_t *first, *last; /* main program */
	gcode_inst_t *first2, *last2; /* temporary storage for assignments */
	gcode_delay_t delay;
	long ip2;

	/*** execution states ***/
	int (*execute_code)(gcode_prg_t *ctx, int code, double param); /* provided by the caller; returns 0 on success; code is DO for executing the line */
	double params[5400];
	vtd0_t stack;
	unsigned laser_on:1;

	/*** caller's ***/
	void *user_data;
	void *exec_data;
};


void gcode_delayed(gcode_prg_t *ctx, gcode_delay_t delay);
gcode_inst_t *gcode_append(gcode_prg_t *ctx, gcode_i_t inst, double payload);
void gcode_set_lineno(gcode_prg_t *ctx, int lineno);

void gcode_dump_inst(const char *prefix, gcode_inst_t *inst);
void gcode_dump_prg(const char *prefix, gcode_prg_t *prg);

int gcodeparse(gcode_prg_t *ctx);
int gcode_execute(gcode_prg_t *ctx);

#endif
