%{
/* vi:set sw=4: */

/***********************************************************************\
*									*
*	VELPON: GLUEING C AND CLEAN TOGETHER				*
*									*
\***********************************************************************/

#include <stdio.h>
#include <stddef.h>
#include "global.h"
#define	YYDEBUG 1

static	char *newident = "_";

%}

%union{
	char *string;
	char bad;
	struct {
		char *definition;
		char *impl;
/*		char *instr; */
		char *abc;
	} output;
}

%token <string> C_IDENT CLEAN_IDENT
%token SYM_FOREIGN SYM_MODULE SYM_CLEAN_STRING SYM_INT SYM_BOOL SYM_DOUBLE 
%token SYM_CLEAN_FILE SYM_VOID
%token SYM_UNQ_CLEAN_STRING SYM_UNQ_INT SYM_UNQ_BOOL SYM_UNQ_DOUBLE 
%token SYM_UNQ_CLEAN_FILE
%token <bad> BAD_CHAR

%type <output> c_input_type c_output_type c_result_type empty_parameter_pack parameter_pack
%type <output> input_parameter output_parameter specification specification_list
%type <output> c_function_declaration input_parameter_list file_output_parameter
%type <output> output_parameter_list tuple_parameter_list
%type <string> module_ident procedure_ident

%start specfile

%%

specfile:
	SYM_FOREIGN SYM_MODULE module_ident ';' specification_list
	{
		fprintf(definition,"system module %s;\n\n",$3);
		fprintf(definition,"%s",$5.definition);

		fprintf(impl,"implementation module %s;\n\n",$3);
		fprintf(impl,"%s",$5.impl);
	}
	;

specification_list:
	specification_list specification
	{
		$$.definition = concat($1.definition,$2.definition,CEND);
		$$.impl = concat($1.impl, $2.impl, CEND);
	}
	|
	specification
	;

specification: c_function_declaration CLEAN_IDENT ';'
    {
		char *v;
	
		v=concat ($2," :: ",$1.definition,";\n",CEND);
	
		$$.definition=v;
		v=concat (v,$2," ",$1.impl,
			"\n\t= code {\n",
				"\t\t.inline ",$2,
				   "\n\t\t\tccall ",$1.abc,
				"\t\t.end",
			"\n\t};\n\n", CEND);

		$$.impl=v;
		
    }
    ;

c_function_declaration:
    c_result_type procedure_ident parameter_pack
    {
		$$.definition=concat ($3.definition," -> ",$1.definition,CEND);
		$$.impl=$3.impl;
		$$.abc=concat ($2," \"",$3.abc,"-",$1.abc,"\"\n",CEND);
    }
	|
    c_result_type procedure_ident empty_parameter_pack
    {
		$$.definition=concat ($3.definition," ",$1.definition,CEND);
		$$.impl=$3.impl;
		$$.abc=concat ($2," \"",$3.abc,"-",$1.abc,"\"\n",CEND);
    }
	|
	SYM_VOID procedure_ident '(' input_parameter_list ',' file_output_parameter ')'
    {
        $$.definition=concat ($4.definition," -> ",$6.definition,CEND);
        $$.impl=$4.impl;
        $$.abc=concat ($2," \"",$4.abc,"-",$6.abc,"\"\n",CEND);
    }
	|
	SYM_VOID procedure_ident '(' input_parameter_list ',' tuple_parameter_list ')'
	{
		$$.definition=concat ($4.definition," -> (!",$6.definition,")",CEND);
		$$.impl=$4.impl;
		$$.abc=concat ($2," \"",$4.abc,"-",$6.abc,"\"\n",CEND);
	}
	;

c_result_type:
    SYM_INT
	{
		$$.definition = "Int";
		$$.abc = "I";
	}
	|
	SYM_BOOL
	{
		$$.definition = "Bool";
		$$.abc = "I";
	}
	|
	SYM_DOUBLE
	{
		$$.definition = "Real";
		$$.abc = "R";
	}
	|
	SYM_CLEAN_STRING
	{
		$$.definition = "{#Char}";
		$$.abc = "S";
	}
	|
    SYM_UNQ_INT
	{
		$$.definition = "*Int";
		$$.abc = "I";
	}
	|
	SYM_UNQ_BOOL
	{
		$$.definition = "*Bool";
		$$.abc = "I";
	}
	|
	SYM_UNQ_DOUBLE
	{
		$$.definition = "*Real";
		$$.abc = "R";
	}
	|
	SYM_UNQ_CLEAN_STRING
	{
		$$.definition = "*{#Char}";
		$$.abc = "S";
	}
	;

procedure_ident	:
	C_IDENT
	;

empty_parameter_pack:
	'(' ')'
	{
	    $$.definition = "";
	    $$.impl = "";
	    $$.abc = "";
	}
	|
	'(' SYM_VOID ')'
	{
	    $$.definition = "";
	    $$.impl = "";
	    $$.abc = "";
	}
	;

parameter_pack:
	'(' input_parameter_list ')'
	{
	   $$ = $2;
	}
	;

input_parameter_list:
	input_parameter_list ',' input_parameter
	{
		$$.definition=concat ($1.definition," ",$3.definition,CEND);
		$$.impl = concat($1.impl, " ", $3.impl,CEND);
		$$.abc = concat($1.abc, $3.abc, CEND);
	}
	|
	input_parameter
	;

tuple_parameter_list:
    output_parameter_list ',' output_parameter
    {
            $$.definition=concat ($1.definition,",!",$3.definition,CEND);
            $$.abc = concat($1.abc, $3.abc, CEND);
    }
    ;

output_parameter_list:
	output_parameter_list ',' output_parameter
	{
		$$.definition=concat ($1.definition,",!",$3.definition,CEND);
		$$.abc = concat($1.abc, $3.abc, CEND);
	}
	|
	output_parameter
	;

input_parameter:
	c_input_type
	|
	c_input_type C_IDENT
	{
	    $$ = $1;
	}
	;

output_parameter:
	c_output_type
	|
	c_output_type C_IDENT
	{
	    $$ = $1;
	}
	;

file_output_parameter:
	SYM_CLEAN_FILE '*'
	{
		$$.definition="File";
        $$.abc="F";
	}
	|
	SYM_CLEAN_FILE '*' C_IDENT
	{
		$$.definition="File";
		$$.abc="F";
	}
	;

c_input_type:
    SYM_INT
    {
		$$.definition = "!Int";
		$$.impl = newident;
		$$.abc = "I";
    }
	|
	SYM_BOOL
	{
		$$.definition = "!Bool";
        $$.impl = newident;
        $$.abc = "I";
    }
    |
    SYM_DOUBLE
    {
		$$.definition = "!Real";
	    $$.impl = newident;
	    $$.abc = "R";
	}
	|
	SYM_CLEAN_STRING
	{
		$$.definition = "!{#Char}";
		$$.impl = newident;
		$$.abc = "S";
	}
	|
	SYM_CLEAN_FILE
	{
		$$.definition = "!File";
		$$.impl = newident;
		$$.abc = "F";
	}
	|
    SYM_UNQ_INT
    {
		$$.definition = "!*Int";
		$$.impl = newident;
		$$.abc = "I";
    }
	|
	SYM_UNQ_BOOL
	{
		$$.definition = "!*Bool";
        $$.impl = newident;
        $$.abc = "I";
    }
    |
    SYM_UNQ_DOUBLE
    {
		$$.definition = "!*Real";
	    $$.impl = newident;
	    $$.abc = "R";
	}
	|
	SYM_UNQ_CLEAN_STRING
	{
		$$.definition = "!*{#Char}";
		$$.impl = newident;
		$$.abc = "S";
	}
	|
	SYM_UNQ_CLEAN_FILE
	{
		$$.definition = "!*File";
		$$.impl = newident;
		$$.abc = "F";
	}
	;

c_output_type:
    SYM_INT '*'
    {
		$$.definition = "Int";
		$$.abc = "I";
    }
	|
	SYM_BOOL '*'
    {
        $$.definition = "Bool";
        $$.abc = "I";
    }
    |
    SYM_DOUBLE '*'
    {
		$$.definition = "Real";
	    $$.abc = "R";
	}
	|
	SYM_CLEAN_STRING '*'
	{
		$$.definition = "{#Char}";
		$$.abc = "S";
	}
	|
	SYM_CLEAN_FILE '*'
	{
		$$.definition = "File";
		$$.abc = "F";
	}
	|
    SYM_UNQ_INT '*'
    {
		$$.definition = "*Int";
		$$.abc = "I";
    }
	|
	SYM_UNQ_BOOL '*'
    {
        $$.definition = "*Bool";
        $$.abc = "I";
    }
    |
    SYM_UNQ_DOUBLE '*'
    {
		$$.definition = "*Real";
	    $$.abc = "R";
	}
	|
	SYM_UNQ_CLEAN_STRING '*'
	{
		$$.definition = "*{#Char}";
		$$.abc = "S";
	}
	|
	SYM_UNQ_CLEAN_FILE '*'
	{
		$$.definition = "*File";
		$$.abc = "F";
	}
	;

module_ident:
	C_IDENT
	;

%%
