/*
    Very Simple Compiler (Parser Part)
    Parser part
	$Id: s-compile.c,v 1.1 1996/09/22 11:29:31 kono Exp kono $
 */

%{
#include "s-compile.h"

void error(char *);
void yyerror(char *);

char *ptr,*last_ptr,*before;
int  value,lvalue;
int  last_token;
int  variable[48];

typedef struct node {
    struct node *left;
    struct node *right;
    int type;
    int value;
} node,*nodeptr;
/*typedef node *nodeptr;*/

#define YYSTYPE nodeptr

node *new_node();
%}

%%

top : expr { 
	code_generate($1);
	free_node($1);
	emit_print();
	emit_comment();
    }
    ;

expr :  expr '>' aexpr  { $$ = new_node('>',0,$1,$3); }
    |   expr '=' aexpr  { $$ = new_node('=',0,$1,$3); }
    |   aexpr {$$ = $1; }
    ;

aexpr :  aexpr '+' mexpr { $$ = new_node('+',0,$1,$3); }
    |    aexpr '-' mexpr { $$ = new_node('-',0,$1,$3); }
    |    mexpr {$$ = $1; }
    ;

mexpr :  mexpr '*' term { $$ = new_node('*',0,$1,$3); }
    |    mexpr '/' term { $$ = new_node('/',0,$1,$3); }
    |    term {$$ = $1; }
    ;

term :  '0' { $$ = new_node('0',value,NULL,NULL); }
    |   'v' { $$ = new_node('v',value,NULL,NULL); }
    |   '(' expr ')'  {$$ = $2; }
    ;

%%

void print_node();

node *
new_node(type,value,left,right) 
int type;
int value;
node *left;
node *right;
{
    node *d;
    if ((left  &&  left->type =='0') &&
        (right && right->type =='0')) {
	switch(type) {
	case '>':
	    right->value = (left->value > right->value); 
	    free(left); return right;
	    break;
	case '+':
	    right->value = left->value + right->value; 
	    free(left); return right;
	    break;
	case '-':
	    right->value = left->value - right->value; 
	    free(left); return right;
	    break;
	case '*':
	    right->value = right->value * left->value;
	    free(left); return right;
	    break;
	case '/':
	    if(right->value==0) {
		error("zero divide in compile time");
	    } else {
		right->value = left->value / right->value;
	    }
	    free(left); return right;
	}
    }
    d =  (node *)malloc(sizeof(node));
    d->type = type;
    d->value = value;
    d->left = left;
    d->right = right;
    return d;
}

void
free_node(d)
node *d;
{
    if(d->left) {
	free_node(d->left);
    } 
    if(d->right) {
	free_node(d->right);
    } 
    free(d);
}

void
code_generate(d)
node *d;
{
    int assign;
    if(!d) return; /* compiler internal error.. */
    switch(d->type) {
    case '0':
	emit_value(d->value);
	return;
    case 'v':
	emit_load(d->value);
	return;
    case '=':
	if(!d->left || d->left->type != 'v') {
	    error("Bad assignment");
	    code_generate(d->right);
	    return;
	}
	assign = d->left->value;
	code_generate(d->right);
	emit_store(assign);
	return;
    case '>': 
	code_generate(d->left);
	emit_push();
	code_generate(d->right);
	emit_compare(); 
	break;
    default:   /* calculation */
	code_generate(d->right);
	emit_push();
	code_generate(d->left);
	switch(d->type) {
	case '+': emit_calc(O_ADD); break;
	case '-': emit_calc(O_SUB_R); break;
	case '/': emit_calc(O_DIV_R); break;
	case '*': emit_calc(O_MUL); break;
	default:
	    error("Internal Error, unknown opecode");
	}
	return;
    }
}

int
yylex()
{
    int c,d;

    last_ptr = ptr;  /* for error position */
    c= *ptr;
    if(!c) {
	last_token = EOF;
	return last_token;
    }
    ptr++;
    if (c<=' ') {       /* comment */ 
	while(*ptr++);
	ptr--;
	last_token = EOF;
	last_ptr = ptr;
	return last_token;
    }
    if('0'<=c && c<='9') {     /* Decimal */
	d = c-'0';
	while(c= *ptr++) {
	    if('0'<=c && c<='9') {
		d = d*10 + (c - '0');
	    } else {
		break;
	    }
	}
	c && ptr--;
	value = d;
	last_token = '0';
	return last_token;
    } else if ('a'<=c && c<='z') {    /* variable */
	value = c-'a';                /* return variable reference */
	last_token = 'v';
	return last_token;
    } else {
	last_token = c;
	return last_token;
    }
}


int
main() 
{
    nodeptr d;
    char buf[BUFSIZ];

    emit_intro();
    while (fgets(buf,BUFSIZ,stdin)) {
	ptr = buf;
	before = buf;
	printf("%s %s",comments,buf);
	yyparse();
    }
    emit_ending();
    return 0;
}

void 
yyerror(s)
char *s;
{
    error(s?s:"error");
}

void
error(s)
char *s;
{
    fprintf(stderr,"%s on %s\n",s,last_ptr);
}

/* end */