// 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"

// Utility Functions used to generate code. //////////////////////////////////

FILE *of = stdout;

void iprint(char *s) {
	fprintf(of, "\t%s\n", s);
}

void iprint(char *s, int i) {
	fprintf(of, "\t%s 0x%X\n", s, i);
}

void iprint(char *s, string st) {
	fprintf(of, "\t%s %s\n", s, st.c_str());
}

void iprint(char *s, char *s1, int s2, char *s3) {
	fprintf(of, "\t%s %s%d%s\n", s, s1, s2, s3);
}

void iprint(string s) {
	fprintf(of, "\t%s\n", s.c_str());
}

void ilabel(char *s, int s1, char *s2) {
	fprintf(of, "%s%d%s:\n", s, s1, s2);
}

void ilabel(string s) {
	fprintf(of, "%s:\n", s.c_str());
}

void stprint(char *s) {
	fprintf(of, "\t/// %s\n", s);
}

void stprint(string s) {
	fprintf(of, "\t/// %s\n", s.c_str());
}

void cprint(char *s) {
	fprintf(of, "\t// %s\n", s);
}

void cprint(string s) {
	fprintf(of, "\t// %s\n", s.c_str());
}

void wspace() {
	fprintf(of, "\n");
}

string i2s(int i) {
	char buf[32];
	snprintf(buf, 32, "%d", i);
	return buf;
}
	
string operator + (string s, int i) {
	return s + i2s(i);
}

// Expr //////////////////////////////////////////////////////////////////////

void Expr::code() {
	cprint("code not implemented.");
}

void Expr::gencode() {
	cprint("gencode not implemented.");
}

int Expr::condare(Expr *b) {
	int i;
	int l;
	
	if (rtti != b->rtti) return rtti - b->rtti;

	l = exprs.size();
	if (b->exprs.size() != l) return l - b->exprs.size();

	for (i = 0; i < l; i++) {
		if (exprs[i]->condare(b->exprs[i]))
			return exprs[i]->condare(b->exprs[i]);
	}

	return equal(b);
}

void Expr::dupify() {
	int l;
	int i;
	
	l = exprs.size();
	for (i = 0; i < l; i++) {
		exprs[i]->dupify();
	}
	
	if (l < 2) return;

	for (i = 1; i < l; i++) {
		if (!exprs[i-1]->candup() || !exprs[i]->candup()) continue;
		
		if (exprs[i-1]->condare(exprs[i])) continue;
		delete exprs[i];
		exprs[i] = new EDup(exprs[i - 1]->dupsrc());
	}
}

bool Expr::optimize(string *s) {
	sort(s);
	dupify();
	if (s) return trykos(*s);
	return false;
}

void Expr::sort(string *s) {
	int l;

	l = exprs.size();
	for (int i = 0; i < l; i++) {
		exprs[i]->sort(s);
	}
}

Expr *Expr::dupsrc() {
	return this;
}

bool Expr::candup() {
	int l = exprs.size();
	for (int i = 0; i < l; i++) {
		if (!exprs[i]->candup()) return false;
	}

	return true;	
}

string *Expr::name() {
	return NULL;
}

int Expr::equal(Expr *b) {
	return 0;
}

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

void Expr::didkos() {}

bool Expr::trykos(string s) {
	if (exprs.empty()) return false;
	return exprs[0]->trykos(s);
}

extern int yylineno;

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

Expr::~Expr() {
	int i;
	int l;
	
	l = exprs.size();

	for (i = 0; i < l; i++) {
		delete exprs[i];
	}
}

// EDup //////////////////////////////////////////////////////////////////////

void EDup::code() {
}

void EDup::gencode() {
	iprint("DUP");
}

Expr *EDup::dupsrc() {
	return src;
}

int EDup::condare(Expr *b) {
	return src->condare(b);
}

string EDup::text() {
	return src->text();
}

EDup::EDup(Expr *s) {
	src = s;
	rtti = EXPR_DUP;
}

// EFetch ////////////////////////////////////////////////////////////////////

string *EFetch::kosname() {
	return &var;
}

void EFetch::didkos() {
	keeponstack = true;
}

void EFetch::code() {
	if (keeponstack) gencode();

	if (takefs) {
		// Whoops, this is fairly antisocial. Oh well, this isn't
		// the normal case... it's fairly pathological.
		
		cprint("Expression was optimized out, popping extra " + var + " from stack.");
		iprint("POP");
	}
}

void EFetch::gencode() {
	if (takefs) {
		cprint("Taking " + var + " from stack.");
	} else {
		iprint("ILOAD", var);
	}

	if (keeponstack) {
		iprint("DUP");
		cprint("Leaving " + var + " on stack.");
	}
}
	
string *EFetch::name() {
	return &var;
}

bool EFetch::trykos(string s) {
	if (s == var) {		
		takefs = true;
		return true;
	}

	return false;
}
	

int EFetch::equal(Expr *b) {
	EFetch *efb = (EFetch *) b;
	if (var < efb->var) return -1;
	if (var == efb->var) return 0;
	return 1;
}

string EFetch::text() {
	return var;
}

EFetch::EFetch(string v) {
	keeponstack = false;
	var = v;
	takefs = false;
	rtti = EXPR_FETCH;
}

// EAdd //////////////////////////////////////////////////////////////////////

void EAdd::code() {
	int l = exprs.size();

	for (int i = 0; i < l; i++) {
		exprs[i]->code();	
	}
}

void EAdd::gencode() {
	int i;
	int l = exprs.size();

	for (i = 0; i < l; i++) {
		exprs[i]->gencode();
	}

	for (i = 1; i < l; i++) {
		iprint("IADD");
	}
}
 
void EAdd::sort(string *s) {
	Expr::sort(s);

	int l = exprs.size();

	for (int st = 0; st < l; st++) {
		string *name = exprs[st]->name();
		if (s && name) if (*name == *s) continue;
		
		int maxe = st;
		for (int i = st + 1; i < l; i++) {
			if (s) {
				name = exprs[i]->name();

				if (name && *name == *s) {
					maxe = i;
					break;
				}
			}

			if (exprs[i]->condare(exprs[maxe]) < 0) maxe = i;
		}

		Expr *tmp = exprs[maxe];
		exprs[maxe] = exprs[st];
		exprs[st] = tmp;
	}
}

void EAdd::addterm(Expr *exp) {
	exprs.push_back(exp);
}

string EAdd::text() {
	int l = exprs.size();
	int i;
	string s = "(" + exprs[0]->text();

	for (i = 1; i < l; i++) {
		s += " + " + exprs[i]->text();
	}

	return s + ")";
}

EAdd::EAdd(Expr *e1, Expr *e2) {
	exprs.push_back(e1);
	exprs.push_back(e2);
	rtti = EXPR_ADD;
}

// ESub //////////////////////////////////////////////////////////////////////

void ESub::code() {
	exprs[0]->code();
	exprs[1]->code();
}

void ESub::gencode() {
	exprs[0]->gencode();
	exprs[1]->gencode();
	iprint("ISUB");
}

string ESub::text() {
	return "(" + exprs[0]->text() + " - " + exprs[1]->text() + ")";
}

ESub::ESub(Expr *e1, Expr *e2) {
	exprs.push_back(e1);
	exprs.push_back(e2);
	rtti = EXPR_SUB;
}

// EOr ///////////////////////////////////////////////////////////////////////

void EOr::code() {
	exprs[0]->code();
	exprs[1]->code();
}

void EOr::gencode() {
	exprs[0]->gencode();
	exprs[1]->gencode();
	iprint("IOR");
}

string EOr::text() {
	return "(" + exprs[0]->text() + " | " + exprs[1]->text() + ")";
}

EOr::EOr(Expr *e0, Expr *e1) {
	exprs.push_back(e0);
	exprs.push_back(e1);
	rtti = EXPR_OR;
}

// EAnd ///////////////////////////////////////////////////////////////////////

void EAnd::code() {
	exprs[0]->code();
	exprs[1]->code();
}

void EAnd::gencode() {
	exprs[0]->gencode();
	exprs[1]->gencode();
	iprint("IAND");
}

string EAnd::text() {
	return "(" + exprs[0]->text() + " & " + exprs[1]->text() + ")";
}

EAnd::EAnd(Expr *e0, Expr *e1) {
	exprs.push_back(e0);
	exprs.push_back(e1);
	rtti = EXPR_AND;
}

// ENot //////////////////////////////////////////////////////////////////////

void ENot::code() {
	exprs[0]->code();
}

void ENot::gencode() {
	iprint("BIPUSH -1");
	exprs[0]->gencode();
	iprint("ISUB");
}

string ENot::text() {
	return "~" + exprs[0]->text();
}

ENot::ENot(Expr *e0) {
	exprs.push_back(e0);
	rtti = EXPR_NOT;
}

// EStore ////////////////////////////////////////////////////////////////////

string *EStore::kosname() {
	return &var;	
}

void EStore::didkos() {
	keep = true;
}

void EStore::code() {
	exprs[0]->gencode();
	if (keep) iprint("DUP");
	iprint("ISTORE", var);	
	if (keep) cprint("Left " + var + " on stack.");
}

void EStore::gencode() {
	exprs[0]->gencode();
	iprint("DUP");
	iprint("ISTORE", var);
}
	
bool EStore::candup() {
	return false;
}

string EStore::text() {
	return "(" + var + " = " + exprs[0]->text() + ")";
}

int EStore::equal(Expr *b) {
	EStore *esb = (EStore *) b;
	if (var < esb->var) return -1;
	if (var == esb->var) return 0;
	return 1;
}

EStore::EStore(string v, Expr *exp) {
	var = v;
	keep = 0;
	exprs.push_back(exp);
	rtti = EXPR_STORE;
}       

// EConst ////////////////////////////////////////////////////////////////////

void EConst::code() {
}

void EConst::gencode() {
	iprint("BIPUSH", val);
}

string EConst::text() {
	return i2s(val);
}

int EConst::equal(Expr *b) {
	EConst *ecb = (EConst *) b;
	return val - ecb->val;
}

EConst::EConst(int v) {
	val = v;
	rtti = EXPR_CONST;
}

// ENamedConst ///////////////////////////////////////////////////////////////

void ENamedConst::code() {
}

void ENamedConst::gencode() {
	iprint("LDC_W", name);
}

string ENamedConst::text() {
	return "const " + name;
}

int ENamedConst::equal(Expr *b) {
	ENamedConst *encb = (ENamedConst *) b;
	if (name < encb->name) return -1;
	if (name == encb->name) return 0;
	return 1;
}

ENamedConst::ENamedConst(string n) {
	name = n;
	rtti = EXPR_NAMEDCONST;
}

// EMethod ///////////////////////////////////////////////////////////////////

void EMethod::code() {
	gencode();
	iprint("POP");
}

void EMethod::gencode() {
	int l = exprs.size();
	for (int i = 0; i < l; i++) exprs[i]->gencode();

	iprint("INVOKEVIRTUAL", name);
}

string EMethod::text() {
	bool need_comma = false;
	string s = name + "(";
	int l = exprs.size();

	for (int i = 1; i < l; i++) {
		if (need_comma) s += ", ";
		s += exprs[i]->text();
		need_comma = true;
	}

	return s + ")";
}

int EMethod::equal() {
	return 0; // All methods are created equal... and they can't be duped.
}

bool EMethod::candup() {
	return false;
}

EMethod::EMethod(string n, EXV *exv) {
	while (!exv->empty()) {
		exprs.insert(exprs.begin(), exv->back());
		exv->pop_back();
	}

	delete exv;

	name = n;
	rtti = EXPR_METHOD;
}

// EIn ///////////////////////////////////////////////////////////////////////

bool EIn::candup() {
	return false;
}

void EIn::code() {
	iprint("IN");
	iprint("POP");
}

void EIn::gencode() {
	iprint("IN");
}

string EIn::text() {
	return "in";
}

// EMul //////////////////////////////////////////////////////////////////////

int EMul::equal(Expr *b) {
	EMul *emb = (EMul *) b;
	
	return reps - emb->reps;
}

void EMul::code() {
	exprs[0]->code();
}

void EMul::gencode() {
	if (reps == 0) {
		exprs[0]->code();
		iprint("BIPUSH 0x0");
		return;
	}

	exprs[0]->gencode();

	for (int i = 1; i < reps; i++) iprint ("DUP");
	for (int i = 1; i < reps; i++) iprint ("IADD");
}

string EMul::text() {
	return "(" + exprs[0]->text() + " * " + i2s(reps) + ")";
}

EMul::EMul(Expr *e, int r) {
	reps = r;
	exprs.push_back(e);
	rtti = EXPR_MUL;
}

// EShl //////////////////////////////////////////////////////////////////////

int EShl::equal(Expr *b) {
	EShl *emb = (EShl *) b;
	
	return reps - emb->reps;
}

void EShl::code() {
	exprs[0]->code();
}

void EShl::gencode() {
	exprs[0]->gencode();

	for (int i = 0; i < reps; i++) {
		iprint ("DUP");
		iprint ("IADD");
	}
}

string EShl::text() {
	return "(" + exprs[0]->text() + " << " + i2s(reps) + ")";
}

EShl::EShl(Expr *e, int r) {
	reps = r;
	exprs.push_back(e);
	rtti = EXPR_SHL;
}

// EPop //////////////////////////////////////////////////////////////////////

// Warning... do not use in conjunction with other expressions, in such a
// way that the TOS could change.

void EPop::code() {
	cprint("Popping value from top of stack.");
	iprint("POP");
}

void EPop::gencode() {
	cprint("Pop, leaving value on top of stack.");
}

string EPop::text() {
	return "pop";
}

EPop::EPop() {
	rtti = EXPR_POP;
}

// EAFetch ///////////////////////////////////////////////////////////////////

int EAFetch::equal(Expr *e) {
	EAFetch *eaf = (EAFetch *) e;
	
	int rv = base->equal(eaf->base);
	if (rv) return rv;

	return index->equal(eaf->index);
}

void EAFetch::code() {
	base->code();
	index->code();
}

void EAFetch::gencode() {
	index->gencode();
	base->gencode();
	iprint("BASE");
	iprint("IGET");
}

string EAFetch::text() {
	return base->text() + "[" + index->text() + "]";
}

EAFetch::EAFetch(Expr *b, Expr *i) {
	rtti = EXPR_AFETCH;
	base = b;
	index = i;
}

EAFetch::~EAFetch() {
	delete base;
	delete index;
}

// EAStore ///////////////////////////////////////////////////////////////////

int EAStore::equal(Expr *e) {
	EAStore *eaf = (EAStore *) e;
	
	int rv = base->equal(eaf->base);
	if (rv) return rv;

	return index->equal(eaf->index);
}


void EAStore::code() {
	data->gencode();
	index->gencode();
	base->gencode();
	iprint("BASE");
	iprint("IPUT");
}

void EAStore::gencode() {
	data->gencode();
	iprint("DUP");
	index->gencode();
	base->gencode();
	iprint("BASE");
	iprint("IPUT");
}

string EAStore::text() {
	return "(" + base->text() + "[" + index->text() + "] = " +
		data->text() + ")";
}

EAStore::EAStore(Expr *b, Expr *i, Expr *d) {
	rtti = EXPR_ASTORE;

	base = b;
	index = i;
	data = d;
}

EAStore::~EAStore() {
	delete base;
	delete index;
	delete data;
}

// EAlloca ///////////////////////////////////////////////////////////////////

void EAlloca::code() {
	gencode();
	iprint("POP");
}

void EAlloca::gencode() {
	expr->gencode();
	iprint("ALLOCA");
}

string EAlloca::text() {
	return "alloca(" + expr->text() + ")";
}

EAlloca::EAlloca(Expr *e) {
	rtti = EXPR_ALLOCA;
	expr = e;
}

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

// EAString //////////////////////////////////////////////////////////////////

void EAString::code() {
}

void EAString::gencode() {
	int l;

	l = str.size();

	if (l > 254) {
		fprintf(stderr, "error on line %d: string too long.\n", l);
		exit(-1);
	}

	iprint("BIPUSH", l + 1);
	iprint("ALLOCA");
	iprint("DUP");
	iprint("BASE");
	
	for (int i = 0; i < l; i++) {
		iprint("BIPUSH", str[i]);
		iprint("BIPUSH", i);
		iprint("IPUT");
	}

	iprint("BIPUSH", 0);
	iprint("BIPUSH", l);
	iprint("IPUT");
}

string EAString::text() {
	return escape(str.c_str());
}

EAString::EAString(string s) {
	str = s;
}
	
// Main //////////////////////////////////////////////////////////////////////

int yyparse();
extern int yydebug;

int main(int argc, char **argv) {
	yydebug = 0;

	fprintf(of, "// This program contains code Copyright 2000 Tom Rothamel.\n");
	fprintf(of, "// It is provided without warranty, and may be redistributed\n");
	fprintf(of, "// and used in modified or unmodified form, provided this\n"); 
	fprintf(of, "// copyright notice is retained in the source code.\n\n");

	yyparse();
}
