// joycub - an interpeter for the programming language Joy
// 
// (c) 2004-2006 Sergey Zubkov 
// $Id: symtab.cc,v 1.9 2006/02/27 18:44:01 cubbi Exp $
//
// The Symbol Table
#include <vector>
#include "symtab.h"
#include "exceptions.h"
#include "program.h"

static char RCSID[] ="$Id: symtab.cc,v 1.9 2006/02/27 18:44:01 cubbi Exp $";

JC_symtab_f::JC_symtab_f(const std::string & n, const std::string & b) {
	name=n;
	body=b;
	f=NULL;
}
JC_symtab_f::JC_symtab_f(const std::string & n, void (*fp)(JC_Stack&)) {
	name=n;
	body="";
	f=fp;
}
bool JC_symtab_f::is_userdef(void) {
	return f!=NULL;
}
std::string JC_symtab_f::get_body(void) {
	if (f)
		return name;
	else
		return body;
}
void JC_symtab_f::operator()(JC_Stack & s) {
//	cerr << " operator() called with f="<<f<< "and body=" << body<< endl;
		if(f!=NULL) {
			f(s);
		} else {
			Token_list iline = Token_list(body);
			JC_Program * p = new JC_Program(iline);
			p->execute(s);
			delete p;
		}
}

// Add a new user-defined operator
void JC_symtab::new_userdef(const std::string & name, const std::string & body) {
	contents.push_back(new JC_symtab_f(name, body));
	index_to_contents[name]=contents.size()-1;
	name_to_help[name]=" user-defined function.";
//	cerr << "SYMTAB: added " << body << " as " << name << endl;
}
// Add a new builtin operator 
void JC_symtab::add_to_symtab(void (*f)(JC_Stack&), const std::string & name,
				const std::string & help) {
	contents.push_back(new JC_symtab_f(name, f));
	index_to_contents[name]=contents.size()-1;
	name_to_help[name]=help;
}

// Initialize all builtin words
JC_symtab::JC_symtab(void) {
	add_to_symtab(&op_id,		"id", " :  ->\nIdentity function, does nothing.\nAny program of the form  P id Q  is equivalent to just  P Q.");
	add_to_symtab(&op_pop,		"pop", " :   X  ->\nRemoves X from top of the stack.");
	add_to_symtab(&op_swap,		"swap", " :   X Y  ->   Y X\nInterchanges X and Y on top of the stack.");
	add_to_symtab(&op_rollup,	"rollup", " :  X Y Z  ->  Z X Y\nMoves X and Y up, moves Z down");
	add_to_symtab(&op_rolldown,	"rolldown", " :  X Y Z  ->  Y Z X\nMoves Y and Z down, moves X up");
	add_to_symtab(&op_rotate,	"rotate", " :  X Y Z  ->  Z Y X\nInterchanges X and Z");
	add_to_symtab(&op_sign,		"sign", " :  N1  ->  N2\nInteger N2 is the sign (-1 or 0 or +1) of integer N1,\nor float N2 is the sign (-1.0 or 0.0 or 1.0) of float N1.");
	add_to_symtab(&op_plus,		"+", " :  M I  ->  N\nNumeric N is the result of adding integer I to numeric M.\nAlso supports float.");
	add_to_symtab(&op_minus,	"-", " :  M I  ->  N\nNumeric N is the result of subtracting integer I from numeric M.\nAlso supports float.");
	add_to_symtab(&op_mult,		"*", " :  I J  ->  K\nInteger K is the product of integers I and J.\nAlso supports float.");
	add_to_symtab(&op_divide,	"/", " :  I J  ->  K\nInteger K is the (rounded) ratio of integers I and J.  Also supports float.");
	add_to_symtab(&op_dup,		"dup", " :   X  ->   X X\nPushes an extra copy of X onto stack.");
	add_to_symtab(&op_dip,		"dip", " :  X [P]  ->  ... X\nSaves X, executes P, pushes X back.");
	add_to_symtab(&op_i,		"i", " :  [P]  ->  ...\nExecutes P. So, [P] i  ==  P.");
	add_to_symtab(&op_branch,	"branch", " :  B [T] [F]  ->  ...\nIf B is true, then executes T else executes F.");
	add_to_symtab(&op_small,	"small", " :  X  ->  B\nTests whether aggregate X has 0 or 1 members, or numeric 0 or 1.");
	add_to_symtab(&op_null,		"null", " :  X  ->  B\nTests for empty aggregate X or zero numeric.");
	add_to_symtab(&op_binrec,	"binrec", " :  [B] [T] [R1] [R2]  ->  ...\nExecutes P. If that yields true, executes T.\nElse uses R1 to produce two intermediates, recurses on both,\nthen executes R2 to combines their results.");
	add_to_symtab(&op_linrec,	"linrec", " :  [P] [T] [R1] [R2]  ->  ...\nExecutes P. If that yields true, executes T.\nElse executes R1, recurses, executes R2.");
	add_to_symtab(&op_primrec,	"primrec", " :  X [I] [C]  ->  R\n Executes I to obtain an initial value R0.\nFor integer X uses increasing positive integers to X, combines by C for new R.\nFor aggregate X uses successive members and combines by C for new R.");
	add_to_symtab(&op_size,		"size", " :  A  ->  I\nInteger I is the number of elements of aggregate A.");
	add_to_symtab(&op_ge,		">=", " :  X Y  ->  B\nEither both X and Y are numeric or both are strings or symbols.\n Tests whether X greater than or equal to Y.  Also supports float.");
	add_to_symtab(&op_gt,		">", " :  X Y  ->  B\nEither both X and Y are numeric or both are strings or symbols.\nTests whether X greater than Y.  Also supports float.");
	add_to_symtab(&op_le,		"<=", " :  X Y  ->  B\nEither both X and Y are numeric or both are strings or symbols.\nTests whether X less than or equal to Y.  Also supports float.");
	add_to_symtab(&op_lt,		"<", " :  X Y  ->  B\nEither both X and Y are numeric or both are strings or symbols.\nTests whether X less than Y.  Also supports float.");
	add_to_symtab(&op_ne,		"!=", " :  X Y  ->  B\nEither both X and Y are numeric or both are strings or symbols.\nTests whether X not equal to Y.  Also supports float.");
	add_to_symtab(&op_eq,		"=", " :  X Y  ->  B\nEither both X and Y are numeric or both are strings or symbols.\nTests whether X equal to Y.  Also supports float.");
	add_to_symtab(&op_at,		"at", " :  A I  ->  X\nX (= A[I]) is the member of A at position I.");
	add_to_symtab(&op_cons,		"cons", " :  X A  ->  B\nAggregate B is A with a new member X (first member for sequences).");
	add_to_symtab(&op_uncons,	"uncons", " :  A  ->  F R\nF and R are the first and the rest of non-empty aggregate A.");
	add_to_symtab(&op_unstack,	"unstack", " :  [X Y ..]  ->  ..Y X\nThe list [X Y ..] becomes the new stack.");
	add_to_symtab(&op_stack,	"stack", " :  .. X Y Z  ->  .. X Y Z [Z Y X ..]\nPushes the stack as a list.");
	add_to_symtab(&op_drop,		"drop", " :  A N  ->  B\nAggregate B is the result of deleting the first N elements of A.");
	add_to_symtab(&op_take,		"take", " :  A N  ->  B\nAggregate B is the result of retaining just the first N elements of A.");
	add_to_symtab(&op_concat,	"concat", " :  S T  ->  U\nSequence U is the concatenation of sequences S and T.");
	add_to_symtab(&op_while,	"while", " :  [B] [D]  ->  ...\nWhile executing B yields true executes D.");
	add_to_symtab(&op_times,	"times", " :  N [P]  ->  ...\nN times executes P.");
	add_to_symtab(&op_tailrec,	"tailrec", " :  [P] [T] [R1]  ->  ...\nExecutes P. If that yields true, executes T.\nElse executes R1, recurses.");
	add_to_symtab(&op_condlinrec,"condlinrec", " :  [ [C1] [C2] .. [D] ]  ->  ...\nEach [Ci] is of the forms [[B] [T]] or [[B] [R1] [R2]].\nTries each B. If that yields true and there is just a [T], executes T and exit.\nIf there are [R1] and [R2], executes R1, recurses, executes R2.\nSubsequent case are ignored. If no B yields true, then [D] is used.\nIt is then of the forms [[T]] or [[R1] [R2]]. For the former, executes T.\nFor the latter executes R1, recurses, executes R2.");
	add_to_symtab(&op_condnestrec,"condnestrec", " :  [ [C1] [C2] .. [D] ]  ->  ...\nA generalisation of condlinrec. Each [Ci] is of the form [[B] [R1] [R2] .. [Rn]]\nand [D] is of the form [[R1] [R2] .. [Rn]]. Tries each B, or if all fail, takes\nthe default [D]. For the case taken, executes each [Ri] but recurses between an\ny two consecutive [Ri]. (n > 3 would be exceptional.)\n");
	add_to_symtab(&op_cond,		"cond", " :  [..[[Bi] Ti]..[D]]  ->  ...\nTries each Bi. If that yields true, then executes Ti and exits.\nIf no Bi yields true, executes default D.");
	add_to_symtab(&op_construct,"construct", " :  [P] [[P1] [P2] ..]  ->  R1 R2 ..\nSaves state of stack and then executes [P].\nThen executes each [Pi] to give Ri pushed onto saved stack.");
	add_to_symtab(&op_dot,		".", " :  ...  ->\nexecute the current program and output the top of the stack");
	add_to_symtab(&op_putch,	"putch", " :  N  ->\nN : numeric, writes character whose ASCII is N.");
	add_to_symtab(&op_get,		"get", " :  ->  F\nReads a factor from input and pushes it onto stack.");
	add_to_symtab(&op_put,		"put", " :  X  ->\nWrites X to output, pops X off stack.");
	add_to_symtab(&op_format,	"format", " :  N C I J  ->  S\nS is the formatted version of N in mode C\n('d or 'i = decimal, 'o = octal, 'x or\n'X = hex with lower or upper case letters)\nwith maximum width I and minimum width J. ");
	add_to_symtab(&op_formatf,	"formatf", " :  F C I J  ->  S\nS is the formatted version of F in mode C\n('e or 'E = exponential, 'f = fractional,\n'g or G = general with lower or upper case letters)\nwith maximum width I and precision J.");
	add_to_symtab(&op_rem,		"rem", " :  I J  ->  K\nInteger K is the remainder of dividing I by J.  Also supports float.");
	add_to_symtab(&op_div,		"div", " :  I J  ->  K L\nIntegers K and L are the quotient and remainder of dividing I by J.");
	add_to_symtab(&op_nullary,	"nullary", " :  [P]  ->  R\nExecutes P, which leaves R on top of the stack.\nNo matter how many parameters this consumes, none are removed from the stack.");
	add_to_symtab(&op_unary,	"unary", " :  X [P]  ->  R\nExecutes P, which leaves R on top of the stack.\nNo matter how many parameters this consumes,\nexactly one is removed from the stack.");
	add_to_symtab(&op_app1,		"app1", " :  X [P]  ->  R\nExecutes P, pushes result R on stack without X.");
	add_to_symtab(&op_app11,	"app11", " :  X Y [P]  ->  R\nExecutes P, pushes result R on stack.");
	add_to_symtab(&op_app12,	"app12", " :  X Y1 Y2 [P]  ->  R1 R2\nExecutes P twice, with Y1 and Y2, returns R1 and R2.\n");
	add_to_symtab(&op_unary2,	"unary2", " :  X1 X2 [P]  ->  R1 R2\nExecutes P twice, with X1 and X2 on top of the stack.\nReturns the two values R1 and R2.");
	add_to_symtab(&op_unary3,	"unary3", " :  X1 X2 X3 [P]  ->  R1 R2 R3\nExecutes P three times, with Xi, returns Ri (i = 1..3).");
	add_to_symtab(&op_unary4,	"unary4", " :  X1 X2 X3 X4 [P]  ->  R1 R2 R3 R4\nExecutes P four times, with Xi, returns Ri (i = 1..4).");
	add_to_symtab(&op_step,		"step", " :  A  [P]  ->  ...\nSequentially putting members of aggregate A onto stack,\nexecutes P for each member of A.");
	add_to_symtab(&op_map,		"map", " :  A [P]  ->  B\nExecutes P on each member of aggregate A,\ncollects results in sametype aggregate B.");
	add_to_symtab(&op_fold,		"fold", " :  A V0 [P]  ->  V\nStarting with value V0, sequentially pushes members of aggregate A\nand combines with binary operator P to produce value V.");
	add_to_symtab(&op_filter,	"filter", " :  A [B]  ->  A1\nUses test B to filter aggregate A producing sametype aggregate A1.");
	add_to_symtab(&op_split,	"split", " :  A [B]  ->  A1 A2\nUses test B to split aggregate A into sametype aggregates A1 and A2.");
	add_to_symtab(&op_infra,	"infra", " :  L1 [P]  ->  L2\nUsing list L1 as stack, executes P and returns a new list L2.\nThe first element of L1 is used as the top of stack,\nand after execution of P the top of stack becomes the first element of L2.");
	add_to_symtab(&op_include,	"include", " :  \"filnam.ext\"  ->\nTransfers input to file whose name is \"filnam.ext\".\nOn end-of-file returns to previous input file.");
	add_to_symtab(&op_logical,	"logical", " :  X  ->  B\nTests whether X is a logical.");
	add_to_symtab(&op_integer,	"integer", "help not written. see manual.txt");
	add_to_symtab(&op_char,		"char", "help not written. see manual.txt");
	add_to_symtab(&op_set,		"set", "help not written. see manual.txt");
	add_to_symtab(&op_string,	"string", "help not written. see manual.txt");
	add_to_symtab(&op_list,		"list", "help not written. see manual.txt");
	add_to_symtab(&op_leaf,		"leaf", "help not written. see manual.txt");
	add_to_symtab(&op_user,		"user", "help not written. see manual.txt");
	add_to_symtab(&op_float,	"float", "help not written. see manual.txt");
	add_to_symtab(&op_file,		"file", "help not written. see manual.txt");
	add_to_symtab(&op_time,		"time", "help not written. see manual.txt");
	add_to_symtab(&op_localtime,"localtime", "help not written. see manual.txt");
	add_to_symtab(&op_gmtime,	"gmtime", "help not written. see manual.txt");
	add_to_symtab(&op_mktime,	"mktime", "help not written. see manual.txt");
	add_to_symtab(&op_strftime,	"strftime", "help not written. see manual.txt");
	add_to_symtab(&op_maxint,	"maxint", "help not written. see manual.txt");
	add_to_symtab(&op_intern,	"intern", "help not written. see manual.txt");
	add_to_symtab(&op_name,		"name", "help not written. see manual.txt");
	add_to_symtab(&op_body,		"body", "help not written. see manual.txt");
	add_to_symtab(&op_ord,		"ord", "help not written. see manual.txt");
	add_to_symtab(&op_chr,		"chr", "help not written. see manual.txt");
	add_to_symtab(&op_or,		"or", "help not written. see manual.txt");
	add_to_symtab(&op_xor,		"xor", "help not written. see manual.txt");
	add_to_symtab(&op_and,		"and", "help not written. see manual.txt");
	add_to_symtab(&op_not,		"not", "help not written. see manual.txt");
	add_to_symtab(&op_setsize,	"setsize", "help not written. see manual.txt");
	add_to_symtab(&op_sqrt,		"sqrt", "help not written. see manual.txt");
	add_to_symtab(&op_pow,		"pow", "help not written. see manual.txt");
	add_to_symtab(&op_sin,		"sin", "help not written. see manual.txt");
	add_to_symtab(&op_sinh,		"sinh", "help not written. see manual.txt");
	add_to_symtab(&op_cos,		"cos", "help not written. see manual.txt");
	add_to_symtab(&op_cosh,		"cosh", "help not written. see manual.txt");
	add_to_symtab(&op_tan,		"tan", "help not written. see manual.txt");
	add_to_symtab(&op_tanh,		"tanh", "help not written. see manual.txt");
	add_to_symtab(&op_asin,		"asin", "help not written. see manual.txt");
	add_to_symtab(&op_acos,		"acos", "help not written. see manual.txt");
	add_to_symtab(&op_atan,		"atan", "help not written. see manual.txt");
	add_to_symtab(&op_atan2,	"atan2", "help not written. see manual.txt");
	add_to_symtab(&op_trunc,	"trunc", "help not written. see manual.txt");
	add_to_symtab(&op_setecho,	"setecho", "help not written. see manual.txt");
	add_to_symtab(&op_echo,		"echo", "help not written. see manual.txt");
	add_to_symtab(&op_clock,	"clock", "help not written. see manual.txt");
	add_to_symtab(&op_rand,		"rand", "help not written. see manual.txt");
	add_to_symtab(&op_srand,	"srand", "help not written. see manual.txt");
	add_to_symtab(&op_choice,	"choice", "help not written. see manual.txt");
	add_to_symtab(&op_ceil,		"ceil", "help not written. see manual.txt");
	add_to_symtab(&op_exp,		"exp", "help not written. see manual.txt");
	add_to_symtab(&op_floor,	"floor", "help not written. see manual.txt");
	add_to_symtab(&op_frexp,	"frexp", "help not written. see manual.txt");
	add_to_symtab(&op_ldexp,	"ldexp", "help not written. see manual.txt");
	add_to_symtab(&op_log,		"log", "help not written. see manual.txt");
	add_to_symtab(&op_log10,	"log10", "help not written. see manual.txt");
	add_to_symtab(&op_modf,		"modf", "help not written. see manual.txt");
	add_to_symtab(&op_max,		"max", "help not written. see manual.txt");
	add_to_symtab(&op_min,		"min", "help not written. see manual.txt");
	add_to_symtab(&op_stdin,	"stdin", "help not written. see manual.txt");
	add_to_symtab(&op_stdout,	"stdout", "help not written. see manual.txt");
	add_to_symtab(&op_stderr,	"stderr", "help not written. see manual.txt");
	add_to_symtab(&op_strtol,	"strtol", "help not written. see manual.txt");
	add_to_symtab(&op_strtod,	"strtod", "help not written. see manual.txt");
	add_to_symtab(&op_fclose,	"fclose", "help not written. see manual.txt");
	add_to_symtab(&op_feof,		"feof", "help not written. see manual.txt");
	add_to_symtab(&op_ferror,	"ferror", "help not written. see manual.txt");
	add_to_symtab(&op_fflush,	"fflush", "help not written. see manual.txt");
	add_to_symtab(&op_fgetch,	"fgetch", "help not written. see manual.txt");
	add_to_symtab(&op_fgets,	"fgets", "help not written. see manual.txt");
	add_to_symtab(&op_fopen,	"fopen", "help not written. see manual.txt");
	add_to_symtab(&op_fread,	"fread", "help not written. see manual.txt");
	add_to_symtab(&op_fwrite,	"fwrite", "help not written. see manual.txt");
	add_to_symtab(&op_fremove,	"fremove", "help not written. see manual.txt");
	add_to_symtab(&op_frename,	"frename", "help not written. see manual.txt");
	add_to_symtab(&op_fput,		"fput", "help not written. see manual.txt");
	add_to_symtab(&op_fputch,	"fputch", "help not written. see manual.txt");
	add_to_symtab(&op_fseek,	"fseek", "help not written. see manual.txt");
	add_to_symtab(&op_ftell,	"ftell", "help not written. see manual.txt");
	add_to_symtab(&op_compare,	"compare", "help not written. see manual.txt");
	add_to_symtab(&op_treestep,	"treestep", "help not written. see manual.txt");
	add_to_symtab(&op_treerec,	"treerec", "help not written. see manual.txt");
	add_to_symtab(&op_treegenrec,"treegenrec", "help not written. see manual.txt");
	add_to_symtab(&op_quit,		"quit", "help not written. see manual.txt");
	add_to_symtab(&op_abort,	"abort", "help not written. see manual.txt");
	add_to_symtab(&op_getenv,	"getenv", "help not written. see manual.txt");
	add_to_symtab(&op_system,	"system", "help not written. see manual.txt");
	add_to_symtab(&op_gc,		"gc", "help not written. see manual.txt");
	add_to_symtab(&op_has,		"has", "help not written. see manual.txt");
	add_to_symtab(&op_opcase,	"opcase", "help not written. see manual.txt");
	add_to_symtab(&op_case,		"case", "help not written. see manual.txt");
	add_to_symtab(&op_argc,		"argc", "help not written. see manual.txt");
	add_to_symtab(&op_argv,		"argv", "help not written. see manual.txt");
	add_to_symtab(&op_equal,	"equal", "help not written. see manual.txt");
	add_to_symtab(&op_help,		"help", "help not written. see manual.txt");
	add_to_symtab(&op_helpdetail,"helpdetail", "help not written. see manual.txt");
	add_to_symtab(&op_manual,	"manual", "help not written. see manual.txt");
	add_to_symtab(&op_nonimpl,	"nonimplemented", "help not written. see manual.txt");
	add_to_symtab(&op_undef,	"undefined", "help not written. see manual.txt");
};

// execute the Word from the Symbol Table, referenced by the number op_num
void JC_symtab::execute(int op_num, JC_Stack & js) {
	(*contents[op_num])(js);
}

// check if the word s is in the symbol table
// returns true if it is there, false if not
bool JC_symtab::check(const std::string & s) {
	return (index_to_contents.end() != index_to_contents.find(s));
}

vector<std::string> JC_symtab::get_all_names(void) const {
	vector<std::string> result;
	for(std::map<const std::string, int>::const_iterator i
			= index_to_contents.begin(); i!=index_to_contents.end(); i++)
		result.push_back((*i).first);
	return result;
}

// check if the word s is a user-defined word
// returns true if it is there, false if not or if it is not in the table
bool JC_symtab::is_userdef(int op_num) {
	return contents[op_num]->is_userdef();
}
std::string JC_symtab::get_body(int op_num) {
	return contents[op_num]->get_body();
}

// Get the reference number for a word
int JC_symtab::name_to_opnum(const std::string & s) {
	if (check(s)) return index_to_contents[s];
	else return index_to_contents["undefined"];
}

std::string JC_symtab::get_help(const std::string &s) {
	if (check(s)) return name_to_help[s];
	else return "undefined word.";
}
