%{
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#ifdef sun
#include <alloca.h>
#endif

extern int      yylineno;
extern char    *yytext;
extern int      yylex();
extern int      yyerror();
extern int      yywarning();

#define STRLEN	256
char            _identifyer[STRLEN];
char           *identifyer = &_identifyer[0];
char            base[STRLEN] = "";
int             bias = -1;
int             foundend = 0;
int             _public = -1;

#define REGS	8

struct gcc_inline {
	char            base[STRLEN];
	int             bias;
	int             _public;
	char            function[STRLEN];
	char            arguments[2 * REGS][STRLEN];
	int             parameters[2 * REGS];
	char            retval[STRLEN];
	char            types[2 * REGS][STRLEN];
	int             argument;
	int             parameter;
	int             type;
	struct gcc_inline *prev;
};

extern struct gcc_inline *curr;
extern struct gcc_inline tmp;
%}

%union {
	char			 	*sval;
	struct gcc_inline	*gval;
}

%type <gval> protofunc
%type <sval> retval argtype struct identifyer builtin ptr varargs

%token	COMMENT
%token	PRIVATE
%token	PUBLIC
%token	BASE
%token	BIAS
%token	END
%token	NUMBER
%token	IDENTIFYER
%token	REGISTER
%token	OPEN
%token	CLOSE
%token	ARGSEP
%token	REGSEP
%token	NEWLINE

%token	STRUCT
%token	BUILTIN
%token	VARARGS
%token	PTR
%token	SEMICOL

%%

fdfile		:	/* empty fdfile */
		|	fdline fdfile
	;

fdline		:	commentline
	{
		if (foundend)
			yyerror("commentline after end", yytext);
	}
		|	privateline
	{
		if (foundend)
			yyerror("privateline after end", yytext);
	}
		|	_publicline
	{
		if (foundend)
			yyerror("_publicline  after end", yytext);
	}
		|	baseline
	{
		if (foundend)
			yyerror("baseline    after end", yytext);
	}
		|	biasline
	{
		if (foundend)
			yyerror("biasline    after end", yytext);
	}
		|	endline
	{
		if (foundend)
			yyerror("endline     after end", yytext);
		foundend = 1;
	}
		|	funcline
	{
		struct gcc_inline *ptr;
		if (!(ptr = (struct gcc_inline *) malloc(sizeof(struct gcc_inline))))
			yyerror("!! out of memory !!", yytext);
		memset(ptr, 0, sizeof(struct gcc_inline));
		if (!strcmp(base, ""))
			yyerror("no base              specified", yytext);
		strcpy(tmp.base, base);
		if (bias == -1)
			yyerror("no bias              specified", yytext);
		tmp.bias = bias;
		if (_public == -1)
			yyerror("no _public or private specified", yytext);
		tmp._public = _public;
		if (foundend)
			yyerror("funcline    after end", yytext);
		if (tmp.argument != tmp.parameter)
			yywarning("#arguments != #parameters", yytext);
		bias += 6;
		memcpy(ptr, &tmp, sizeof(struct gcc_inline));
		ptr->prev = curr;
		curr = ptr;
		memset(&tmp, 0, sizeof(struct gcc_inline));
	}
		|	prototype
	{
		memset(&tmp, 0, sizeof(struct gcc_inline));
	}
	;

prototype :	retval protofunc params SEMICOL
	{
		struct gcc_inline *ptr = $2;
		if (ptr) {
			if (ptr->parameter != tmp.type)
				yywarning("#parameters != #types", yytext);
			strcpy(ptr->retval, $1);
			memcpy(ptr->types, tmp.types, 2 * REGS * STRLEN);
		}
	}
	;

retval : struct identifyer ptr
	{
		$$ = (char *) malloc(strlen($1) + 1 + strlen($2) + 1 + strlen($3) + 1);
		if (!$$)
			yyerror("!! out of memory !!", yytext);
		strcpy($$, $1);
		strcat($$, " ");
		strcat($$, $2);
		strcat($$, " ");
		strcat($$, $3);
		free($3);
		free($2);
		free($1);
	}
	|		struct identifyer
	{
		$$ = (char *) malloc(strlen($1) + 1 + strlen($2) + 1);
		if (!$$)
			yyerror("!! out of memory !!", yytext);
		strcpy($$, $1);
		strcat($$, " ");
		strcat($$, $2);
		free($2);
		free($1);
	}
	|		builtin ptr
	{
		$$ = (char *) malloc(strlen($1) + 1 + strlen($2) + 1);
		if (!$$)
			yyerror("!! out of memory !!", yytext);
		strcpy($$, $1);
		strcat($$, " ");
		strcat($$, $2);
		free($2);
		free($1);
	}
	|		builtin
	{
		$$ = (char *) malloc(strlen($1) + 1);
		if (!$$)
			yyerror("!! out of memory !!", yytext);
		strcpy($$, $1);
		free($1);
	}
	;

protofunc : IDENTIFYER
	{
		struct gcc_inline *ptr = curr;
		int             found = 0;
		while (ptr) {
			if (found = !strcmp(ptr->function, yytext))
				break;
			ptr = ptr->prev;
		}
		if (!found) {
			yywarning("fd for prototype not found", yytext);
			$$ = 0;
		} else
			$$ = ptr;
	}
	;

params	:	OPEN paramlist CLOSE
	;

paramlist	:	/* empty paramlist */
		|	param ARGSEP paramlist
		|	param
	;

param	:	argtype
	{
		strcpy(tmp.types[tmp.type], $1);
		tmp.type++;
		free($1);
	}
	;

argtype : struct identifyer ptr dummy
	{
		$$ = (char *) malloc(strlen($1) + 1 + strlen($2) + 1 + strlen($3) + 1);
		if (!$$)
			yyerror("!! out of memory !!", yytext);
		strcpy($$, $1);
		strcat($$, " ");
		strcat($$, $2);
		strcat($$, " ");
		strcat($$, $3);
		free($3);
		free($2);
		free($1);
	}
	|		struct identifyer dummy
	{
		$$ = (char *) malloc(strlen($1) + 1 + strlen($2) + 1);
		if (!$$)
			yyerror("!! out of memory !!", yytext);
		strcpy($$, $1);
		strcat($$, " ");
		strcat($$, $2);
		free($2);
		free($1);
	}
	|		builtin ptr dummy
	{
		$$ = (char *) malloc(strlen($1) + 1 + strlen($2) + 1);
		if (!$$)
			yyerror("!! out of memory !!", yytext);
		strcpy($$, $1);
		strcat($$, " ");
		strcat($$, $2);
		free($2);
		free($1);
	}
	|		builtin dummy
	{
		$$ = (char *) malloc(strlen($1) + 1);
		if (!$$)
			yyerror("!! out of memory !!", yytext);
		strcpy($$, $1);
		free($1);
	}
	|		varargs
	{
		$$ = (char *) malloc(strlen($1) + 1);
		if (!$$)
			yyerror("!! out of memory !!", yytext);
		strcpy($$, $1);
		free($1);
	}
	;

struct : STRUCT
	{
		$$ = (char *) malloc(strlen(yytext) + 1);
		strcpy($$, yytext);
	}
	;

identifyer : IDENTIFYER
	{
		$$ = (char *) malloc(strlen(identifyer) + 1);
		strcpy($$, identifyer);
	}
	;

builtin : BUILTIN
	{
		$$ = (char *) malloc(strlen(yytext) + 1);
		strcpy($$, yytext);
	}
	;
ptr : PTR
	{
		$$ = (char *) malloc(strlen(yytext) + 1);
		strcpy($$, yytext);
	}
	;
varargs : VARARGS
	{
		$$ = (char *) malloc(strlen(yytext) + 1);
		strcpy($$, yytext);
	}
	;

dummy : /* empty */
	|	IDENTIFYER
	|	OPEN PTR IDENTIFYER CLOSE OPEN dummyparamlist CLOSE
	;

dummyparamlist	:	/* empty dummyparamlist */
		|	dummyparam ARGSEP paramlist
		|	dummyparam
	;

dummyparam : dummystruct dummyidentifyer dummyptr dummy
	|		dummystruct dummyidentifyer dummy
	|		dummybuiltin dummyptr dummy
	|		dummybuiltin dummy
	|		dummyvarargs
	;

dummystruct : STRUCT
	;

dummyidentifyer : IDENTIFYER
	;

dummybuiltin : BUILTIN
	;
dummyptr : PTR
	;
dummyvarargs : VARARGS
	;

commentline	:	COMMENT
	;

privateline	:	PRIVATE NEWLINE
	{
		_public = 0;
	}
	;

_publicline	:	PUBLIC NEWLINE
	{
		_public = 1;
	}
	;

baseline	:	BASE base NEWLINE
	;

base		:	IDENTIFYER
	{
		strcpy(base, yytext);
	}
	;

biasline	:	BIAS bias NEWLINE
	;

bias		:	NUMBER
	{
		bias = atoi(yytext);
	}
	;

endline		:	END NEWLINE
	;

funcline	:	function arguments registers NEWLINE
	;

function	:	IDENTIFYER
	{
		strcpy(tmp.function, identifyer);
	}
	;

arguments	:	OPEN argumentlist CLOSE
	;

argumentlist	:	/* empty argumentlist */
		|	argument ARGSEP argumentlist
		|	argument
	;

argument	:	IDENTIFYER
	{
		strcpy(tmp.arguments[tmp.argument], identifyer);
		tmp.argument++;
	}
	;

registers	:	OPEN registerlist CLOSE
	;

registerlist	:	/* empty registerlist */
		|	register separator registerlist
		|	register
	;

separator	:	REGSEP
		|	ARGSEP
	;

register	:	REGISTER
	{
		if (tmp.parameter < tmp.argument) {
			int             reg = atoi(&yytext[1]);
			if (toupper(yytext[0]) == 'A')
				reg += 8;
			tmp.parameters[tmp.parameter] = reg;
		}
		tmp.parameter++;
	}
	;

%%

/* syntax part of .fd file parsing	 */

yywarning(s, at)
	char           *s;
	char           *at;
{
	fprintf(stderr, "**** yacc WARNING   **** line: %ld, <%s> at [%s]\n",
		yylineno + 1, s, at);
}
yyerror(s, at)
	char           *s;
	char           *at;
{
	fprintf(stderr, "**** yacc ERROR   **** line: %ld, <%s> at [%s]\n",
		yylineno + 1, s, at);
	exit(1);
}

struct gcc_inline *curr = 0;
struct gcc_inline tmp;

void 
findregs(ptr, regs)
	struct gcc_inline *ptr;
	int            *regs;
{
	int             r;
	int             p;
	for (r = 0; r < 2 * REGS; r++)
		*(regs + r) = 0;
	for (p = 0; p < ptr->parameter; p++)
		*(regs + ptr->parameters[p]) = 1;
}
main()
{
	memset(&tmp, 0, sizeof(struct gcc_inline));
	if (!yyparse())
		while (curr) {
			int             r;
			int             p;
			int             regs[2 * REGS];
			struct gcc_inline *ptr = curr;
			if (ptr->_public) {
				int             noretval = (!strcmp(ptr->retval, "void")) || (!strcmp(ptr->retval, "VOID"));
				printf("static __inline %s\n", ptr->retval);
				printf("%s (BASE_PAR_DECL", ptr->function);
				for (p = 0; p < ptr->parameter; p++)
					printf(" %s %s%s", ptr->types[p], ptr->arguments[p], (p < ptr->parameter - 1) ? "," : "");
				printf(")\n");
				printf("{\n");
				printf("  BASE_EXT_DECL\n");
				if (!noretval)
					printf("  register %s _res  __asm(\"d0\");\n", ptr->retval);
				printf("  register struct %s * a6 __asm(\"a6\") = BASE_NAME;\n", &ptr->base[1]);
				for (p = 0; p < ptr->parameter; p++) {
					int             no = ptr->parameters[p];
					int             reg = 'd';
					if (no >= REGS) {
						no -= REGS;
						reg = 'a';
					}
					printf("  register %s %c%d __asm(\"%c%d\") = %s;\n", ptr->types[p], reg, no, reg, no, ptr->arguments[p]);
				}
				printf("  __asm __volatile (\"jsr a6@(-%d)\"\n", ptr->bias);
				printf("  : %s\n", noretval ? "/* no output */" : "\"=r\" (_res)");
				printf("  : \"r\" (a6),");
				for (p = 0; p < ptr->parameter; p++) {
					int             no = ptr->parameters[p];
					int             reg = 'd';
					if (no >= REGS) {
						no -= REGS;
						reg = 'a';
					}
					printf(" \"r\" (%c%d)%s", reg, no, (p < ptr->parameter - 1) ? "," : "");
				}
				printf("\n");
				findregs(ptr, regs);
				printf("  :");
				for (r = REGS; r < 2 * REGS; r++)
					if (regs[r])
						printf(" \"a%d\",", r - REGS);
				for (r = 0; r < REGS; r++)
					if (regs[r])
						printf(" \"d%d\",", r);
				printf("  \"memory\");\n");
				if (!noretval)
					printf("  return _res;\n");
				printf("}\n");
			}
			curr = ptr->prev;
			free(ptr);
		}
}
