/*
 * Copyright, 2001-2003, Astra Network Inc.  All Rights Reserved
 *
 * This source code has been published by Astra Network Inc. However, any
 * use, reproduction, modification, distribution or transfer of this
 * software, or any software which includes or is based upon any of this
 * code, is only permitted if expressly authorized by a written license
 * agreement from Astra. Contact your Astra representative directly for
 * more information.
 */

%{
#include <strings.h>
#include <string.h>
#include "pcode.h"
#include "tables.h"
#include "sixfourbit.h"

void p_add(char p_op);
void p_str(const char * p_str);
void p_push(int64_t p_num);
void p_extend(size_t len);
void p_ref(const char * p_str);
void p_mark(const char * p_str);
void p_ctx(const char * p_str);
void p_mod(const char * p_str);

char *p_code = NULL;
unsigned long p_code_len = 0; /* allocated length */
unsigned long p_code_cur = 0; /* current offset */
unsigned long olc_version = 0; /* version of the overlord compiler */
unsigned long stack_size = 256; /* size of stack to allocate */
unsigned long stack_death = 0; /* zero for grow, one for die */

static long lineno = 1;
static signed int ifdepth = 0;
static signed int fordepth = 0;

%}

nspcbrk [^ \t\n/@#()%&]

%s STRING

%%

;.*\n	{ /* comments */ }

:proto:[^:]*:	{ /* prototype, ignored for now */ }
:olc_ver:[0-9]*: { olc_version = atol(strchr(yytext+1,':')+1); }
:stack:[0-9]*,die: { stack_size = atol(strchr(yytext+1,':')+1);
			stack_death = 1; }
:stack:[0-9]*,grow: { stack_size = atol(strchr(yytext+1,':')+1);
			stack_death = 0; }

\(		{ p_add('('); BEGIN STRING; }
<STRING>[^)\\]*	{ p_str(yytext); }
<STRING>\)	{ p_add(0); BEGIN 0; }
<STRING>\\.	{ p_add(yytext[1]); }

\/{nspcbrk}+	{ p_ref(yytext+1); }
@{nspcbrk}+	{ p_mark(yytext+1); }
\%{nspcbrk}+	{ p_ctx(yytext+1); }
&{nspcbrk}+	{ p_mod(yytext+1); }
#-?[0-9]+	{ p_push(STRTOQ(yytext+1)); }

call	{ p_add(pCALL); }
alarm	{ p_add(pALARM); }
signal	{ p_add(pSIGNAL); }
sigchild { p_add(pSIGCHILD); }
get	{ p_add(pGET); }
set	{ p_add(pSET); }
getpid	{ p_add(pGETPID); }
setpid	{ p_add(pSETPID); }
return	{ p_add(pRETURN); }
inst	{ p_add(pINST); }
dinst	{ p_add(pDINST); }
drop	{ p_add(pDROP); }
dup	{ p_add(pDUP); }

"\{"	{ p_add(pIF); ifdepth++; }
"\}"	{ p_add(pFI); ifdepth--;
		if (ifdepth < 0) {
			printf("More '}'s than '{'s, line %ld\n", lineno);
			exit(1);
		}
	}

"\["	{ p_add(pFOR); fordepth++; 
		if (fordepth > 63) {
			printf("[] loop nesting too deep (max 64), line %ld\n", lineno);
			exit(1);
		}
			
	}
"\]"	{ p_add(pROF); fordepth--;
		if (fordepth < 0) {
			printf("More ']'s than '['s, line %ld\n", lineno);
			exit(1);
		}
	}

"<"	{ p_add(pLT); }
">"	{ p_add(pGT); }
"!"	{ p_add(pNOT); }
"="	{ p_add(pEQ); }
"&"	{ p_add(pAND); }
"|"	{ p_add(pOR); }

"+"	{ p_add(pPLUS); }
"-"	{ p_add(pMINUS); }
"*"	{ p_add(pMULT); }
"/"	{ p_add(pDIV); }
"~"	{ p_add(pNEG); }

[ \t]+	;
\n	lineno++;

.	printf("Garbage on line %ld: %s\n", lineno, yytext);

%%

int yywrap() {
        return 1;
}

void p_add(char p_op) {
	p_extend(sizeof(char));
	p_code[p_code_cur] = p_op;
	p_code_cur++;
}

void p_str(const char * p_str) {
	int l = strlen(p_str);
	p_extend(l);
	memcpy(&p_code[p_code_cur], p_str, l);
	p_code_cur += l;
}

void p_push(int64_t p_num) {
	p_add(pNUM);
	p_extend(sizeof(int64_t));
	p_num = HTOFQ(p_num);
	memcpy(&p_code[p_code_cur], &p_num, sizeof(int64_t));
	p_code_cur += sizeof(int64_t);
}

void p_extend(size_t len) {
	if (p_code == NULL) {
		p_code = malloc(4096);
		p_code_len = 4096;
	}
	if ((p_code_cur + len) > p_code_len) {
		p_code = realloc(p_code, p_code_len * 2);
		p_code_len *= 2;
	}
	if (p_code == NULL) {
		printf("Out of Memory! line %ld\n", lineno);
		exit(1);
	}
}

void p_ref(const char * p_str) {
	int i;
	for (i=0;i<256;i++) {
		if(tpcode_table[i].name[0] == 0xff) {
			/* reached the end */
			strcpy(tpcode_table[i].name, p_str);
			break;
		}
		if(strcasecmp(tpcode_table[i].name, p_str) == 0)
			/* found a match */
			break;
	}
	if (i == 255){
		printf("Too many segments! /%s at line %ld\n", p_str, lineno);
	}
	p_push(LTOQ(i));
}

void p_mark(const char * p_str) {
	int i;
	for (i=0;i<256;i++) {
		if (tpcode_table[i].name[0] == 0xff) {
			/* reached the end */
			strcpy(tpcode_table[i].name, p_str);
			tpcode_table[i].p_offset = p_code_cur;
			break;
		}
		if (strcasecmp(tpcode_table[i].name, p_str) == 0) {
			if (tpcode_table[i].p_offset != 0xffffffff)
				printf("Duplicate definition of %s, line %ld!\n", p_str, lineno);
			tpcode_table[i].p_offset = p_code_cur;
			break;
		}
	}
	if (i == 255) {
		printf("Too many segments! @%s at line %ld\n", p_str, lineno);
		exit(1);
	}
}

void p_ctx(const char * p_str) {
	int i;
	for (i=0;i<256;i++) {
		if (ctx_table[i][0] == 0) {
			/* reached the end */
			strcpy(ctx_table[i], p_str);
			break;
		}
		if (strcasecmp(ctx_table[i], p_str) == 0)
			break;
	}
	if (i == 255) {
		printf("Too many contexts! %%%s at line %ld\n", p_str, lineno);
		exit(1);
	}
	p_push(LTOQ(i));
	p_add(pCONTEXT);
}

void p_mod(const char * p_str) {
	int i;
	for (i=0;i<256;i++) {
		if (mod_table[i][0] == 0) {
			/* reached the end */
			strcpy(mod_table[i], p_str);
			break;
		}
		if (strcasecmp(mod_table[i], p_str) == 0)
			break;
	}
	if (i == 255) {
		printf("Too many modules! &%s at line %ld\n", p_str, lineno);
		exit(1);
	}
	p_push(LTOQ(i));
}
