
%{

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

FILE *codefile = NULL;
FILE *protofile = NULL;
char *pfilename = NULL;
int have_funk_main = 0;

enum cmd {
	C_INC, C_SUB, C_MUL, 
	C_CDR, C_CAR, C_STA,
	C_OP_IF, C_CL_IF,
	C_INP, C_OUP, C_DBG
};

char *commands[] = {
	"++acc;", "acc -= cur->val;", "acc *= cur->val;",
	"if ( !cur->n ) cur->n = new_cons(); cur = cur->n;", 
	"if ( !cur->c ) cur->c = new_cons(); cur = cur->c;", 
	"cur->val = acc;", "if ( acc ) {", "}",
	"acc = getchar(); if ( acc == EOF ) acc = 0;", "putchar( acc );",

	"fprintf( stderr, \"acc=%d, cur=(%p,%p,%d)\\n\", acc,"
	"cur->c, cur->n, cur->val);"
};

void e_const( int c )
{
	fprintf( codefile, "acc = %d;\n", c );
}

void e_func( char *name )
{
	name += 2; /* skip '::' at yytext */
	if ( strcmp( name, "main" ) == 0 ) have_funk_main = 1;
	fprintf( codefile, "return acc;\n}\n\n"
		 "int funk_%s( consp cur, int acc ) {\n", name );
	if ( protofile ) 
		fprintf( protofile, "int funk_%s( consp cur, int acc );\n", 
			 name );
}

void e_call( char *func )
{
	fprintf( codefile, "acc = funk_%s( cur, acc );\n", func );
}

void e_cmd( enum cmd type )
{
	fprintf( codefile, "%s\n", commands[type] );
}

void yyerror( char *s )
{
	fprintf( stderr, "parse error: %s", s );
	exit( 1 );
}

%}

%option noyywrap

%%

"/*".*"*/"	/* comments */
[ \t\n\r]+	/* whitespace */
[0-9]+		e_const( atoi( yytext ));
"/".		e_const( yytext[1] );
"::"[a-z]+	e_func( yytext );
[a-z]+		e_call( yytext );
"+"		e_cmd( C_INC );
"-"		e_cmd( C_SUB );
"X"		e_cmd( C_MUL );
">"		e_cmd( C_CDR );
"V"		e_cmd( C_CAR );
"("		e_cmd( C_OP_IF );
")"		e_cmd( C_CL_IF );
"!"		e_cmd( C_STA );
","		e_cmd( C_INP );
"."		e_cmd( C_OUP );
"##"		e_cmd( C_DBG );

.		yyerror( yytext );

%%

void parse_options( int ac, char *av[] )
{
	int i;
	for ( i = 1; i < ac; ++i ) {
		if ( strcmp( av[i], "-o" ) == 0 ) {
			if ( ++i == ac ) continue;
			codefile = fopen( av[i], "w" );
			if ( !codefile ) perror( "open" ), exit( 1 );
		} else if ( strcmp( av[i], "-d" ) == 0 || 
			    strcmp( av[i], "-p" ) == 0 ) {
			if ( ++i == ac ) continue;
			protofile = fopen( av[i],
					   ( av[i-1][1] == 'd' ? "w" : "a" ));
			if ( !protofile ) perror( "open" ), exit( 1 );
			pfilename = av[i];
		} else {
			yyin = fopen( av[i], "r" );
			if ( !yyin ) perror( "open" ), exit( 1 );
		}
	}
}

void print_header( void )
{
	fprintf( codefile, "#include <stdio.h>\n#include <stdlib.h>\n" );
	fprintf( codefile, "struct cons {\n"
		 "struct cons *n;\nstruct cons *c;\nint val;\n};\n\n"
		 "typedef struct cons *consp;\n\n" );
	if ( pfilename ) fprintf( codefile, "#include \"%s\"\n", pfilename );
	fprintf( codefile, "static consp new_cons( void ) {\n"
		 "consp c = malloc( sizeof(struct cons) );\n"
		 "c->n = c->c = NULL;\nc->val = 0;\nreturn c;\n}\n\n" );
	fprintf( codefile, "int funky_unknown( consp cur, int acc ){\n" );
}

void print_footer( void )
{
	fprintf( codefile, "return acc;\n}\n\n"
		 "int main(void){\nreturn %s(new_cons(),0);\n}\n",
		 ( have_funk_main ? "funk_main" : "funky_unknown" ) );
}

int main( int ac, char *av[] )
{
	codefile = stdout;
	yyin = stdin;
	parse_options( ac, av );
	print_header();
	yylex();
	print_footer();
	return 0;
}

