// ruri - ruri language compiler
// Copyright 2000 Tom Rothamel <tom-ruri@onegeek.org>
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  

// This is a program that generates another program. As an exemption,
// the output file does not need to be placed under the GPL, provided
// that the following paragraph is placed in the generated output. Please
// note that this is not an authorization to modify this program to cause
// it to output more of itself in order to circumvent my copyright.
//
// 	This program contains code Copyright 2000 Tom Rothamel.
//	It is provided without warranty, and may be redistributed 
//	and used in modified or unmodified form, provided this 
//	copyright notice is retained in the source code.

#include <stdio.h>
#include "parse.h"

int breakto = -1;

string escape(const char *c) {
	string s;

	while (*c) {
		switch (*c) {
		case '\n':
			s += "\\n";
			break;
		case '\t':
			s += "\\t";
			break;
		case '\b':
			s += "\\b";
			break;
		default:
			s += *c;
		}

		c++;
	}

	return "\"" + s + "\"";
}
		
// Statement /////////////////////////////////////////////////////////////////

string *Statement::kosname() { return NULL; };
void Statement::didkos() {}
bool Statement::optimize(string *s) { return false; }

void Statement::error(char *s) {
	fprintf(stderr, "error at line %d: %s\n", lineno, s);
}

extern int yylineno;

Statement::Statement() {
	lineno = yylineno;
}

// SList /////////////////////////////////////////////////////////////////////

void SList::code() {
	int l = vs.size();

	stprint("{");
	
	if (l == 0) {
		stprint("}");
		return;
	}

	vs[0]->optimize(NULL);
	
	if (l == 1) {
		vs[0]->code();
		cprint("}");
		return;
	}

	for (int i = 0; i < l - 1; i++) {
		string *s = vs[i]->kosname();
		if (vs[i+1]->optimize(s)) vs[i]->didkos();
		vs[i]->code();
	}

	vs[l-1]->code();		
	stprint("}");
}

void SList::add(Statement *st) {
	vs.push_back(st);
}

SList::SList() {
}

SList::~SList() {
	for (VSi i = vs.begin(); i < vs.end(); i++) {
		delete (*i);
	}

}
		
// SExprRaw //////////////////////////////////////////////////////////////////

string *SExprRaw::kosname() {
	return expr->kosname();
}

void SExprRaw::didkos() {
	expr->didkos();
}

bool SExprRaw::optimize(string *s) {
	return expr->optimize(s);
}

void SExprRaw::code() {
	expr->optimize(NULL);
	wspace();
	stprint(expr->text() + ";");
	expr->code();
}

SExprRaw::SExprRaw(Expr *e) {
	expr = e;
}

SExprRaw::~SExprRaw() {
	delete expr;
}

// SExpr /////////////////////////////////////////////////////////////////////

void SExpr::gencode() {
	expr->gencode();
	if (needdup) {
		iprint("DUP");
		cprint("Leaving " + *(expr->kosname()) + " on stack.");
	}
}

string *SExpr::kosname() {
	return expr->kosname();
}

void SExpr::didkos() {
	needdup = true;
}

bool SExpr::optimize(string *s) {
	return expr->optimize(s);
}

SExpr::SExpr() {
	needdup = false;
}

SExpr::~SExpr() {
	delete expr;
}

// SOut //////////////////////////////////////////////////////////////////////

void SOut::code() {	
	wspace();
	stprint("out " + expr->text() + ";");
	gencode();
	iprint("OUT");
}

SOut::SOut(Expr *e) {
	expr = e;
}

// SPush /////////////////////////////////////////////////////////////////////

void SPush::code() {	
	wspace();
	stprint("push " + expr->text() + ";");
	gencode();
	cprint("Leaving expression value pushed on stack.");	
}

SPush::SPush(Expr *e) {
	expr = e;
}

// SReturn ///////////////////////////////////////////////////////////////////

// It's pointless to try to keep stuff on the stack through a return.
string *SReturn::kosname() {
	return NULL;
}

void SReturn::code() {	
	wspace();
	stprint("return " + expr->text() + ";");
	gencode();
	iprint("IRETURN");
}

SReturn::SReturn(Expr *e) {
	expr = e;
}

// SIf ///////////////////////////////////////////////////////////////////////

void SIf::code() {
	int n;
	
	wspace();
	stprint("if (" + cond->text() + ")");

	if (cond->alwaystrue()) {
		cprint("Always true. Omitting conditional.");
		state->code();
		return;
	}

	if (cond->alwaysfalse()) {
		cprint("Always false. Omitting conditional and body.");
		cprint("{}");
		return;
	}
		
	n = cnum();
	cond->settrue(n);
	cond->setfalse(n);
	cond->code();

	ilabel("cond", n, "true");
	state->code();
	ilabel("cond", n, "false");
}

SIf::SIf(Condition *c, Statement *s) {
	state = s;
	cond = c;
}

SIf::~SIf() {
	delete state;
	delete cond;
}
	
// SIfElse ///////////////////////////////////////////////////////////////////

void SIfElse::code() {
	wspace();
	stprint("if (" + cond->text() +")");

	if (cond->alwaystrue()) {
		cprint("Condition always true. Supressing condition and else statement.");
		state->code();
		stprint("else {}");
		return;
	}

	if (cond->alwaysfalse()) {
		cprint("Condition always false. Supressing condition and true statement.");
		stprint("{} else");
		elst->code();
		return;
	}

	int num = cnum();

	cond->settrue(num);
	cond->setfalse(num);

	cond->code();
	ilabel("cond", num, "true");
	state->code();
	wspace();
	iprint("GOTO", "cond", num, "done");
	stprint("else");
	ilabel("cond", num, "false");
	elst->code();
	ilabel("cond", num, "done");
}

SIfElse::SIfElse(Condition *c, Statement *s1, Statement *s2) {
	cond = c;
	state = s1;
	elst = s2;
}

SIfElse::~SIfElse() {
	delete cond;
	delete state;
	delete elst;
}

// SWhile ////////////////////////////////////////////////////////////////////


void SWhile::code() {
	wspace();
	
	stprint("while (" + cond->text() + ")");	
	int n = cnum();
	int obreakto = breakto;
	
	if (cond->alwaystrue()) {
		cprint("Condition always true, omitting condition.");
		ilabel("cond", n, "continue");
		breakto = n;
		st->code();
		breakto = obreakto;

		iprint("GOTO", "cond", n, "continue");
		ilabel("cond", n, "break");
		return;
	}

	if (cond->alwaysfalse()) {
		cprint("Condition always false, not coding anything.");
	}

	cond->settrue(n);
	cond->setfalse(n);

	ilabel("cond", n, "continue");
	cond->code();
	ilabel("cond", n, "true");
	breakto = n;
	st->code();
	breakto = obreakto;
	iprint("GOTO", "cond", n, "continue");
	ilabel("cond", n, "break");
	ilabel("cond", n, "false");
	
}

SWhile::SWhile(Condition *c, Statement *s) {
	cond = c;
	st = s;
}

SWhile::~SWhile() {
}

// SContinue /////////////////////////////////////////////////////////////////

void SContinue::code() {
	if (breakto == -1) error("Continue outside of continuable context.");

	wspace();
	stprint("continue");
	iprint("GOTO", "cond", breakto, "continue");
}

// SBreak ////////////////////////////////////////////////////////////////////

void SBreak::code() {
	if (breakto == -1) error("Break outside of breakable context.");

	wspace();
	stprint("break");
	iprint("GOTO", "cond", breakto, "break");
}

// SFor //////////////////////////////////////////////////////////////////////

string *SFor::kosname() {
	return NULL;
}

void SFor::code() {
	int n = cnum();
	int obreak = breakto;
	
	wspace();
	stprint("for (" + expr->text() + "; " + cond->text() + "; " +
	       repexpr->text());

	cprint("for: initalization expression");
	expr->code();

	if (cond->alwaysfalse()) {
		cprint("for: loop condition always false. Not generating rest.");
		return;
	}

	cond->settrue(n);
	cond->setfalse(n);

	ilabel("cond", n, "loop");

	if (cond->alwaystrue()) {
		cprint("for: loop condition always true. Omitting.");
	} else {
		cprint("for: loop condition");
		cond->code();
	}

	cprint("for: loop body");
	ilabel("cond", n, "true");

	breakto = n;
	st->code();
	breakto = obreak;

	wspace();
	ilabel("cond", n, "continue");
	cprint("for: repetition expression");
	repexpr->code();
	iprint("GOTO", "cond", n, "loop");
	ilabel("cond", n, "break");
	ilabel("cond", n, "false");
}

SFor::SFor(Expr *e, Condition *c, Expr *re, Statement *s) {
	expr = e;
	cond = c;
	repexpr = re;
	st = s;
}

SFor::~SFor() {
	// delete expr; (Will be deleted later.)
	delete cond;
	delete repexpr;
	delete st;
}

// SLabel ////////////////////////////////////////////////////////////////////

void SLabel::code() {
	wspace();
	ilabel(name);
}

SLabel::SLabel(string s) {
	name = s;
}

// SGoto /////////////////////////////////////////////////////////////////////

void SGoto::code() {
	wspace();
	stprint("goto " + name + ";");
	iprint("GOTO", name);
}

SGoto::SGoto(string s) {
	name = s;
}

// SAsm //////////////////////////////////////////////////////////////////////

void SAsm::code() {
	wspace();
	iprint(asmi);
}

SAsm::SAsm(string s) {
	asmi = s;
}

// SOutString ////////////////////////////////////////////////////////////////

void SOutString::code() {
	int l = str.length();

	wspace();
	stprint("out " + escape(str.c_str()) + ";");
	
	for (int i = 0; i < l; i++) {
		char c = str[i];
		
		iprint("BIPUSH", c);
		iprint("OUT");
	}
}

SOutString::SOutString(string s) {
	str = s;
}

	
		       
		
	
