var CLASSES, PROG;
var SEM_CHECK = false; //used as flag to perform semantic validation checks and ignore control flow restrictions, ie look at every statement once
var SEM_FUN_RET = "", SEM_RET_CHECK;
var PRIM_TYPES = ["boolean","byte","char","short","int","long","double","float"]
var thread = false
var SEM_CLASS_ID = []
var CLS_EXT_LIST = []
var OUTPUT = ""
var OUT_TIMER = 0
var RUNNING = false
var AUTO_BOX = []
var LIST_CLS = {}
var AUTOMATE = false
var CURRENT_FILENAME;

//THREAD
self.onmessage = function (e){
	if(e.data.resize != null) runResize(e.data.w, e.data.h)
	else if(e.data.init){
		if(e.data.automate) AUTOMATE = true
		thread = true;
		P_ERRS = 0; S_ERRS = 0
		printCmd = function (txt){
			if(RUNNING){
				OUTPUT+=txt
				if(new Date().getTime() - OUT_TIMER > 1){
					self.postMessage({cmd:true, id:CURRENT_FILE, str:OUTPUT})
					OUT_TIMER = new Date().getTime()
					OUTPUT = ""
				}
			}else self.postMessage({cmd:true, id:CURRENT_FILE, str:txt})
		}
		runtimeError = function (txt){self.postMessage(txt)}
		semError = function (msg,obj,force){debugger;msg="["+arguments.callee.caller.caller.name+"()] "+msg;S_ERRS++;if(S_ERRS > 20) throw "too many errors";self.postMessage({semerr:true, msg:msg, obj:obj, force:force})}
		compileError = function (obj,force){obj.msg="["+arguments.callee.caller.caller.name+"()] "+obj.msg;if(P_ERRS > 20) throw "too many errors";P_ERRS++;self.postMessage({comerr:true, obj:obj, force:force})}
		canvasRequest = function (req){
			if(req.type == "set-color") CANVAS_COLOR = "rgba("+req.r+","+req.g+","+req.b+","+req.a/255+")"
			else if(req.type == "set-font"){
				CANVAS_FONT_FAMILY = req.family;
				CANVAS_FONT_SIZE = req.size;
			}else{
				req.id = CURRENT_FILE
				req.color = CANVAS_COLOR
				req.family = CANVAS_FONT_FAMILY
				req.size = CANVAS_FONT_SIZE
				self.postMessage(req)
			}
		}
		importScripts('lib/js_proto.js', 'AST.js', 'java/java.js', 'lexer.js', 'parser.js', 'lib/db_functions.js', 'lib/taffy-min.js', 'lib/base64.js', 'lib/md5-min.js', 'lib/underscore.js', 'lib/longData.js')
		
		CURRENT_PROJECT = e.data.proj
		CURRENT_FILE = e.data.file;
		if(e.data.files) {
			DB_FILES = TAFFY(e.data.files)
			CURRENT_FILENAME = DB_FILES().filter({id:CURRENT_FILE}).first().name
		}
		self.postMessage({init:true})
		APPLET_HEIGHT = e.data.h
		APPLET_WIDTH = e.data.w
	}else if(e.data.sem){ //builds AST and semantic analysis
		var time = getTime()
		var tokens = LEX(e.data.code)
		printCmd("LEX Time = "+(getTime()-time).toFixed(2)+"\n")
		time = getTime()
		PROGRAM = PARSE(tokens)
		printCmd("PARSE Time = "+(getTime()-time).toFixed(2)+"\n")
		self.postMessage({parser:true, tree:PROGRAM})
		
		semanticValidate(PROGRAM)
		self.postMessage({sem:true, errs:(P_ERRS > 0 || S_ERRS > 0 ? true : false), id:CURRENT_FILE})
	}else if(e.data.click){ //sends a MouseEvent to the applet if has listener
		var ml = null
		if(e.data.type == 'click') ml = getVal(new varSet(APPLET_CLASS,[],[]),"ml")
		if(e.data.type == 'move') ml = getVal(new varSet(APPLET_CLASS,[],[]),"mml")
		if(ml && ml!=JNULL){ //applet has a mouse listener
			var me = mouseEventClass(e.data.x,e.data.y)
			var args = doArgs(idPack,[new J_VALUE("MouseEvent",me)])
			var fun_name = "mouseClicked"
			if(e.data.type == 'move') fun_name = "mouseMoved"
			var fun = funSearch(ml,fun_name,false,"void",args)
			args = wrapArgs(fun.fun,args)
			var res = runFunction(fun,args,new packSet(new varSet(ml,[],[])))
		}
	}else if(e.data.keyboard){ //sends a KeyEvent to the applet if has listener
		var ml = getVal(new varSet(APPLET_CLASS,[],[]),"kl")
		if(ml!=JNULL){ //applet has a mouse listener
			var me = keyEventClass(e.data.code)
			var args = doArgs(idPack,[new J_VALUE("KeyEvent",me)])
			var fun = funSearch(ml,"keyTyped",false,"void",args)
			args = wrapArgs(fun.fun,args)
			var res = runFunction(fun,args,new packSet(new varSet(ml,[],[])))
		}
	}else if(e.data.action){ //sends ActionEvent (button press) to applet is ActionListener
		var cl = LIST_CLS[e.data.uid]
		if(!cl) return;
		var al = getVal(new varSet(cl,[],[]),"al")
		if(al!=JNULL){ //applet has a mouse listener
			var ae = actionEventClass(cl,e.data.cmd,cl)
			var args = doArgs(idPack,[new J_VALUE("ActionEvent",ae)])
			var fun = funSearch(al,"actionPerformed",false,"void",args)
			args = wrapArgs(fun.fun,args)
			var res = runFunction(fun,args,new packSet(new varSet(al,[],[])))
		}
	}else if(e.data.update){ //updates the text in a TextComponent class
		var cl = LIST_CLS[e.data.uid]
		var type = e.data.type
		
		if(cl){
			if(type == "text"){
				setVar(new varSet(cl,[],[]),'txt',false,'',newString(e.data.content.text))
			}else if(type == "slider"){
				setVar(new varSet(cl,[],[]),'value',false,'',e.data.content.value);
				var al = getVal(new varSet(cl,[],[]),"cl")
				if(al!=JNULL){ //applet has a mouse listener
					var ae = changeEventClass(cl)
					var args = doArgs(idPack,[new J_VALUE("ChangeEvent",ae)])
					var fun = funSearch(al,"stateChanged",false,"void",args)
					args = wrapArgs(fun.fun,args)
					var res = runFunction(fun,args,new packSet(new varSet(al,[],[])))
				}
			}
		}
	}else{ //runs the code
		if(e.data.files) DB_FILES = TAFFY(e.data.files)
		ByteCode.structInst()
		var RUN_TIME = getTime()
		SEM_CHECK = false
		//RUNNING = true
		AUTO_BOX = []
		var run
		try{RUN(e.data.code,e.data.args)}catch(error){}
		setTimeout(function (){
			printCmd("")
			if(run && run.thr) self.postMessage({end:true, error:true, id:CURRENT_FILE, time:getTime()-RUN_TIME})
			else self.postMessage({end:true, error:false, id:CURRENT_FILE, time:getTime()-RUN_TIME})
		},20)
	}
}

//CANVAS
var CANVAS_COLOR = "#0000ff"
var CANVAS_FONT_FAMILY = "Arial"
var CANVAS_FONT_SIZE = 12
function mouseEventClass(x,y){
	var args = [new J_VARIABLE("x","int",true,x), new J_VARIABLE("y","int",true,y)]
	return newClass("MouseEvent",args,clsPack(APPLET_CLASS))
}
function keyEventClass(code){
	var args = [new J_VARIABLE("source","Object",true,JNULL), new J_VARIABLE("id","int",true,0),new J_VARIABLE("when","long",true,"0"),new J_VARIABLE("modifiers","int",true,0),new J_VARIABLE("code","int",true,code),new J_VARIABLE("keyChar","char",true,code)]
	return newClass("KeyEvent",args,clsPack(APPLET_CLASS))
}
function actionEventClass(cl,cmd,src){
	var args = [new J_VARIABLE("source","Object",true,src), new J_VARIABLE("id","int",true,0), new J_VARIABLE("command","String",true,newString(cmd))]
	return newClass("ActionEvent",args,clsPack(cl))
}
function changeEventClass(src){
	var args = [new J_VARIABLE("source","Object",true,src)]
	return newClass("ChangeEvent",args,clsPack(APPLET_CLASS_SUB))
}
function runResize(w,h){ //redraws the applet when it has been resized by calling repaint()
	setVar(new varSet(APPLET_CLASS,[],[]),"WIDTH",true,"int",w)
	setVar(new varSet(APPLET_CLASS,[],[]),"HEIGHT",true,"int",h)
	var fun = funSearch(APPLET_CLASS,"repaint",true,"void",[])
	runFunction(fun,[])
}
//END

//SEMANTIC VALIDATE
function semanticValidate(p){
	SEM_CHECK = true;
	CLASSES = []
	SEM_CLASS_ID = []
	PROG=p.concat([])
	var total = p.length
	var count = 0;
	var time = getTime()
	var matchedFilename = (CURRENT_FILENAME == null ? true : false);
	if(!AUTOMATE) appendLocalClasses(PROG,CURRENT_PROJECT)
	for(a in lib.java.lang) PROG.push(lib.java.lang[a])
	ATRACE_STACK = [];
	//printCmd("semantic validating...\n")
	p.each(function (c,cls){ //loop through each class
		if(count >= total) return true;
		if(!matchedFilename && (cls.name + ".java") == CURRENT_FILENAME) matchedFilename = true;
		//printCmd("class --> "+cls.name+"\n")
		var gen
		if(cls.generic){ // class has generics but none specified in new statement -> set all to Object
			gen = {}
			for(var i = 0; i < cls.generic.length; i++) gen[cls.generic[i]] = "Object"
		}
		/*var c_inst = getSemClass(cls,false,gen)
		var c_stat = getSemClass(cls,true,gen)*/
		if(cls.interface){
			_.each(cls.funs, function(group){
				group.each(function(i,fun){
					if(fun.type != "void"){
						var clsType = classSearch(fun.type.replace(/\[\]/gi, ""))
						if(!clsType) semError("Class not function return type", fun)
					}
					fun.args.each(function(a, arg){
						var clsType = classSearch(arg.type.replace(/\[\]/gi, ""))
						if(!clsType) semError("Class not found for argument", arg)
					})
				})
			})
			return;
		}
		var c_inst = newClass(cls.name,[],clsPack(cls))
		var c_stat = newClass(cls.name)
		FUNS = []
		FUN_IMPS = generateImps(c_inst,cls)
		for(var x in cls.funs){
			var group = cls.funs[x]
			group.each(function (f,fun){ //and each function to check semantically valid
				var fun_str = fun.name+getFunArgs(fun.args)
				
				//printCmd("fun   --> "+(fun.static?"static ":"")+fun_str+"\n")
				//check if function already exists in class
				if(FUNS.indexOf(fun_str) != -1) semError("duplicate function in class '"+cls.name+"' - "+fun_str,fun)
				else FUNS.push(fun_str)
				duplicateVars(fun.vars,fun_str)
				
				SEM_FUN_RET = fun.type
				if(!fun.type) return;
				var v = copyFunVars(fun.args);
				for(var x = 0; x < v.length; x++){ //set each value of class to default value
					getSemDef(c_inst,v[x])
				}
				runFunction({cl:(fun.static?c_stat:c_inst), fun:fun}, v)
				//check if fun in implementation list
				var found_imp = FUN_IMPS.indexOf((fun.static?"static ":"")+fun_str)
				if(found_imp > -1) FUN_IMPS.splice(found_imp,1)
			})
		}
		
		duplicateVars(cls.vars,cls.name)
		if(FUN_IMPS.length > 0) semError(FUN_IMPS.length+" unimplemented methods in class '"+cls.name+"'",cls.tok)
		count++
	})
	if(!matchedFilename) semError("No class found for given filename '" + CURRENT_FILENAME.substring(0, CURRENT_FILENAME.length - 5) + "'", null)
	
	if(!AUTOMATE) printCmd("SEMANTIC Time = "+(getTime()-time).toFixed(2)+"\n")
	SEM_CHECK = false;
}
function duplicateVars(set,name){ //checks for duplicate variable definitions 
	VARS = []
	set.each(function (i,v){
		if(VARS.indexOf(v.name) != -1) semError("duplicate variable in '"+name+"' - "+v.name,v)
		else VARS.push(v.name)
	})
}
function generateImps(cl,def){
	var res = []
	def.implement.each(function (i,item){
		var c = classSearch(item,clsPack(cl))
		if(!c) {
			semError("interface '"+item+"' not found for class '"+def.name+"'")
			return
		}else if(!c.interface) {
			semError("class is not an interface: '"+c.name+"'")
			return;
		}
		_.each(c.funs,function (fun,j){
			for(var x = 0; x < fun.length; x++){
				if(j==0) return;
				var fun_str = (fun[x].static?"static ":"")+fun[x].name+getFunArgs(fun[x].args)
				if(res.indexOf(fun_str) > -1) return;
				res.push(fun_str)
			}
		})
	})
	return res
}
//END

function RUN(p,ar){
	CLASSES = []
	ATRACE_STACK = []
	PROG = p.concat([]);
	
	for(a in lib.java.lang) PROG.push(lib.java.lang[a]) //import all java.lang.* package
	var args = new Array(ar.length)
	ar.each(function (i,item){
		args[i] = new J_VALUE("String",newString(item))
	})
	
	var app = checkIsApplet(PROG)
	if(app) {
		app = appletLoader(app.name)
		PROG.unshift(app)
		self.postMessage({applet:true})
	}
	
	args = new J_VARIABLE("args","[]String",false,newArray(new J_ARR_ACT(args,"[]String"),idPack))
	var main = findMain(args)
	if(main == null) {
		printCmd("Main method not found in program")
		throw "Main not found"
	}
	PROG=[]
	if(!DB_FILES) PROG = PROG.concat(p)
	if(app) PROG.unshift(app)
	
	
	if(!AUTOMATE && DB_FILES) appendLocalClasses(PROG,CURRENT_PROJECT)
	else PROG = PROG.concat(p)
	for(a in lib.java.lang) PROG.push(lib.java.lang[a]) //import all java.lang.* package
	main.cl = staticClass(main.cl.name)
	
	var result = runFunction(main,[args]);
	return result
	if(result.thr) printCmd("uncaught exception in main '"+result.value.name+"'")
}
//concat java library classes from import
var imp_str=""
function importClasses(cl,s){
	cl.imports.each(function (i,item){
		imp_str=item.name
		subImp(item.sub,lib[item.name],cl)
	})
	cl.impcls.push(s)
}
function subImp(imp,f,cl){
	imp_str+="."+imp.name
	if(!f || (!imp.all && !f[imp.name])) runtimeError("error importing '"+imp_str+"'")
	if(imp.sub==null && !imp.all) cl.impcls.push(f[imp.name])
	else if(imp.sub==null){
		for(a in f) {
			if(f[a] instanceof J_FOLDER) continue;
			cl.impcls.push(f[a])
		}
	}else subImp(imp.sub,f[imp.name],cl)
}
//Other stuff
function runFunction(pair, args, call){
	return runBlock(pair.fun.stats,new varSet(pair.cl,copyFunVars(pair.fun.vars,pair.fun.bcode),args,call), (pair.fun.name!=pair.cl.name?pair.fun:null))
}
function runBlock(stats, vars, fun){
	var r = new packSet(vars)
	if(fun) SEM_RET_CHECK = [null]
	else if(SEM_RET_CHECK) SEM_RET_CHECK.push(null)
	for(var i = 0; i < stats.length; i++){ //go through list of statements
		var s = stats[i]
		var call = vars.caller
		r = doStatement(s, new packSet(vars))
		vars.caller = call
		if(r.ret && !SEM_CHECK) break;
		else if(r.ret && SEM_CHECK && !r.thr && r.brk==null){
			if(r.type == null){
				if(SEM_FUN_RET != "void") semError("not expected void return, got "+ty, s)
				
			}else if(r.type == "?"){
				semError("Type of returning value unknown, can't type check method ", s)
				SEM_RET_CHECK[SEM_RET_CHECK.length - 1] = true
			}else{
				var over = getOverload([r])
				var ty = SEM_FUN_RET
				
				SEM_RET_CHECK[SEM_RET_CHECK.length - 1] = true
				if(vars.cl.generic && vars.cl.generic[ty]) ty = vars.cl.generic[ty]
				if(r.type && ((r.type=="void" && ty != "void") || (r.type!="void" && over[0].indexOf(ty)==-1)))
					semError("return of wrong type ("+r.type+"), expected "+ty, s)
				if(fun && stats.length-i-1 > 0) semError("unreachable code after return statement in function '"+fun.name+"'",s)
			}
			
		}
	}
	if(SEM_CHECK){
		if(fun && SEM_FUN_RET!="void" && SEM_RET_CHECK[0] == null) semError("function '"+fun.name+"' does not always return a statement")
		else if(SEM_RET_CHECK){
			var x = SEM_RET_CHECK.pop()
			if(x) r.ret = true
		}
	}
	
	return r
}
function doStatement(s, p){
	var r;
	//ATRACE_STACK.push({v:p.vars, s:s})
	switch(s.is){
		case IS_CALL: r = doCall(s,p); break;
		case IS_LR: r = doLR(s,p); break;
		case IS_VAR: r = doValue(s,p); break;
		case IS_OP: r = doOp(s,p); break;
		case IS_NEW: r = doNew(s,p); break;
		case IS_IF: r = doIf(s,p); break;
		case IS_WHILE: r = doWhile(s,p); break;
		case IS_FOR: r = doFor(s,p); break;
		case IS_SWITCH: r = doSwitch(s,p); break;
		case IS_TRY: r = doTry(s,p); break;
		case IS_RETURN: r = doReturn(s,p); break;
		case IS_BREAK: r = doBreak(s,p); break;
		case IS_CONV: r = doConv(s,p); break;
		case IS_DEF: r = doDef(s,p); break;
		case IS_BLOCK: r = doBlock(s,p); break;
	}
	if(s.subcall != null) r = doStatement(s.subcall, r)
	//ATRACE_STACK.pop()
	return r
}
function doValue(s,p){
	var r = new packSet(p.vars,s.value,s.type)
	if(r.type == 'long') r.value = goog.math.Long.fromString(s.value)
	return r
}
function doCall(s,p){
	var res;
	if(p.value == JNULL) {
		return newException("Exception","NullPointerException: attempting to call '"+s.fun+"' on null value of type '"+p.type+"'")
	}
	if(!s.args && s.arr) {res = doArray(s,p);}
	else if(s.args==null){ //call for variable
		if(s.value) {
			if(s.value.is == IS_VAR) return new packSet(p.vars,s.value.value,s.value.type,s.value.name)
			else return new packSet(p.vars,s.value,s.value.name)
		}
		var v,t;
		var statClass=false
		var cl = p.vars.cl
		if(p.value==null){
			v = getVar(p.vars,s.fun,false,s.type)
			if(v==null) {v = staticClass(s.fun,p,true);statClass=true}
			if(v==null) {
				t = getVar(p.vars,s.fun,false,s.type,true)
				if(!t) semError("variable does not exist: "+s.fun,s)
				else semError("variable has not yet been defined: "+s.fun,s)
			}
		}else{
			v = getVar(new varSet(p.value,[],[]),s.fun,!p.ths,s.type)
			if(v==null) {
				t =  getVar(new varSet(p.value,[],[]),s.fun,!p.ths,s.type,true)
				if(!t) semError("could not find var '"+s.fun+"' in class "+p.value.name+(p.value.static?"(static)":"(inst)"),s)
				else semError("variable has not yet been defined: "+s.fun,s)
			}
			cl=p.value
		}
		if(!v) {
			if(t) return new packSet(p.vars,null,t.type,s.fun)
			else return new packSet(p.vars,null,"?",s.fun)
		}else if(!SEM_CHECK && statClass && p.value==null){
			s.value = v
		}
		var val = v
		var type = v.name
		if(primType(v.type) || !statClass) {
			val = boundVal(v)
			type = v.type
		}
		p.vars.caller = val
		if(cl.generic && cl.generic[type]) type = cl.generic[type]
		res = new packSet(p.vars,val,type,v.name)
		res.arr_cl = p.value
		if(p.value == null && v.name == "this") res.ths = true
	}else if(s.custom == false){ //call for java function
		if(s.fun == "super") return superClass(s,p)
		
		var cl = p.vars.cl
		var ext = false
		if(p.value!=null) {
			cl = p.value
			ext = true
		}
		var call = p.vars.caller
		var args = doArgs(p,s.args)
		if(args.thr) return args
		var fun = funSearch(cl,s.fun,ext,"void",args)
		p.vars.caller = call
		if(fun==null) {
			semError("Function not found: "+s.fun+getFunArgs(args)+" in class "+cl.name + (cl.static?"(static)":"(inst)"),s)
			return new packSet(p.vars,null,null,"")
		}
		
		if(SEM_CHECK){ //gettting the value as though function has been called
			var tmp = new J_VALUE(fun.fun.type)
			if(cl.generic && cl.generic[tmp.type]) tmp.type = cl.generic[tmp.type]
			if(tmp.type != "void") getSemDef(p.vars.cl,tmp)
			res = new packSet(p.vars,tmp.value,fun.fun.type)
		}else{ //call the function
			args = wrapArgs(fun.fun,args)
			res = runFunction(fun,args,p.vars.caller)
			p.vars.caller = call
			if(!res.thr) res.ret = null;
		}
	}else{  //call for custom JS code
		if(SEM_CHECK) return new packSet(p.vars,JNULL,"")
		if(s.bcode){
			if(typeof s.fun == "string") s.fun = ByteCode.process(s.fun)
			var fin = ByteCode.run(s.fun, p)
			return new packSet(p.vars,fin.value,fin.type)
		}else{
			if(typeof s.fun == "string") s.fun = new Function("vars",s.fun)
			var fin = s.fun(p.vars)
			return new packSet(p.vars,fin.value,fin.type)
		}
		
	}
	return res
}
function doArgs(p,args){
	var res = []
	args.each(function (i,item){
		var r = doStatement(item,new packSet(p.vars))
		if(r.thr) {
			res = r
			return r
		}
		res.push(new J_VARIABLE(r.name,r.type,false,r.value))
	})
	return res
}
function wrapArgs(f,args){
	var res = new Array(args.length)
	f = f.args
	var i = args.length
	while(i--){
		res[i] = new J_VARIABLE(f[i].name,f[i].type,false,args[i].value)
	}
	return res
}
function doArray(s,p){
	var v = p.vars;
	var val;
	if (p.value) v = new varSet(p.value,[],[])
	
	var res = getVar(v,s.fun,false,"1_array");
	if(!res) runtimeError("Array not found: "+s.fun+" in class "+p.vars.cl.name + (p.vars.cl.static?"(static)":"(inst)"))
	var arr = s.arr.arr
	arr.each(function (i,item){ //perform .get(i) on each thingy
		var index = doStatement(item,new packSet(p.vars));
		if(!SEM_CHECK){
			if(index.thr) return index
			if(index.value < 0 || index.value >= res.value.array.length){
				res = newException("Exception","index out of bounds: "+index.value+" (length "+res.value.array.length+")")
				return res
			}
		}
		var parent = res.value
		var type = res.type.substring(2)//res.value.name.substring(2);printCmd(res.value.type)
		if(!SEM_CHECK) val = res.value.array[index.value]
		else{
			var tmp = new J_VALUE(type)
			getSemDef(v.cl,tmp)
			val = tmp.value
		}
		res = new packSet(new varSet(v.cl,v.fun,v.args,val),val,type)
		res.arr_cl = (parent)
		res.index = index.value
	})
	return res
}
function doLR(s,p){
	if(s.left.arr){
		var gets = doArray(s.left,p) //gets the final array class of [][][]
		if(gets.thr) return gets
		var r = doStatement(s.right,p)
		if(r.thr) return r
		if(SEM_CHECK) typeCheckLR(gets,r,s,p)
		gets.arr_cl.array[gets.index]=r.value
		return new packSet(p.vars,null,null)
	}else{
		var call = p.vars.caller
		var l = doStatement(s.left,p)
		if(l.name==null && l.index==null) semError("cannot assign to a value: '"+s.left.value+"'",s.left.tok)
		if(l.thr) return l
		
		p.vars.caller = call
		var r = doStatement(s.right,p)
		if(r.thr) return r
		p.vars.caller = call
		
		if(SEM_CHECK) typeCheckLR(l,r,s,p) //check valid assignment types
		
		if(l.index!=null) l.arr_cl.array[l.index]=r.value
		else if(!SEM_CHECK){
			var set = setVar(new varSet((l.arr_cl || l.vars.cl),(l.arr_cl?[]:l.vars.fun),(l.arr_cl?[]:l.vars.args)),l.name,false,l.type,r.value)
			if(!set) runtimeError("unable to set variable "+l.name)
			
		}else{ //if semantic checking, dont assign value, just check exists
			var set = getVar(new varSet((l.arr_cl || l.vars.cl),(l.arr_cl?[]:l.vars.fun),(l.arr_cl?[]:l.vars.args)),l.name,false,l.type)
			if(set==null) semError("unable to set variable "+l.name,s.left.tok)
			else if(set.fin) semError("cannot change value of final constant '"+set.name+"'",s.left.tok)
		}
		if(s.pre) return new packSet(p.vars,l.value,l.type)
		
		return new packSet(p.vars,r.value,r.type)
	}
}
function doOp(s,p){
	var l = doStatement(s.left,p)
	if(l.thr) return l
	if(s.op == "!") return new packSet(p.vars,!l.value,"boolean")
	
	var r = doStatement(s.right,p)
	if(r.thr) return r
	var op = s.op
	var result;
	var type = PRIM_TYPES[Math.max(PRIM_TYPES.indexOf(l.type),PRIM_TYPES.indexOf(r.type))]
	var a = parseVal(l,p); var b = parseVal(r,p);
	if(a!=JNULL && b!=JNULL && (op=="!=" || op=="==")) {a=l.value;b=r.value;}
	if(l.type == "char" && r.type == "String") a = String.fromCharCode(l.value)
	else if(r.type == "char" && l.type == "String") b = String.fromCharCode(r.value)
	
	if(SEM_CHECK){
		if(op != "+" && op!="==" && op!="!=" && (!primType(l.type) || !primType(r.type)))
			semError("cannot perform following operator statement: '"+l.type+"' "+op+" '"+r.type+"'",s)
		//else if((primType(l.type) && !primType(r.type)) || (!primType(l.type) && primType(r.type)))
			//semError("type clash: '"+l.type+"' "+op+" '"+r.type+"'")
	}
	if(!SEM_CHECK && (l.type == "long" || r.type == "long") && primType(l.type) && primType(r.type)) return doLongOp(l,r,op,p)
	if(op=="+"){
		result = a+b
		if(l.type == "String" || r.type == "String"){
			type = "String"
			result = newString(result)
		}
	}else if(op=="-") result = a-b
	else if(op=="*") result = a*b
	else if(op=="/") { //throw divide by 0 here
		if(!SEM_CHECK && b == 0) return newException("Exception", "divide by zero exception: "+a+" / 0")
		var t1 = PRIM_TYPES.indexOf(l.type), t2 = PRIM_TYPES.indexOf(r.type)
		if(t1 > t2) type = l.type
		else type = r.type
		result = a/b
	}else if(op=="%") result = a%b
	else if(op=="|") result = a|b
	else if(op=="&") result = a&b
	else{
		type="boolean"
		if(op=="||") result = a||b
		else if(op=="&&") result = a&&b
		else if(op=="<") result = a<b
		else if(op==">") result = a>b
		else if(op=="<=") result = a<=b
		else if(op==">=") result = a>=b
		else if(op=="==") result = a==b
		else if(op=="!=") result = a!=b
	}
	return new packSet(p.vars,boundVal(new J_VALUE(type,result)),type)
}

function doLongOp(l,r,op,p){
	var a = l.value
	var b = r.value
	if(l.type!="long") a = goog.math.Long.fromString(a.toString())
	if(r.type!="long") b = goog.math.Long.fromString(b.toString())
	
	var type = "long"
	if(op=="+") result = a.add(b)
	else if(op=="-") result = a.subtract(b)
	else if(op=="*") result = a.multiply(b)
	else if(op=="/") { //throw divide by 0 here
		if(!SEM_CHECK && b.isZero()) return newException("Exception", "divide by zero exception: "+a+" / 0")
		var t1 = PRIM_TYPES.indexOf(l.type), t2 = PRIM_TYPES.indexOf(r.type)
		if(t1 > t2) type = l.type
		else type = r.type
		result = a.div(b)
	}else if(op=="%") result = a.modulo(b)
	else if(op=="|") result = a.or(b)
	else if(op=="&") result = a.and(b)
	else{
		type="boolean"
		if(op=="||") result = a.or(b)
		else if(op=="&&") result = a.and(b)
		else if(op=="<") result = a.lessThan(b)
		else if(op==">") result = a.greaterThan(b)
		else if(op=="<=") result = a.lessThanOrEqual(b)
		else if(op==">=") result = a.greaterThanOrEqual(b)
		else if(op=="==") result = a.equals(b)
		else if(op=="!=") result = a.notEquals(b)
	}printCmd(result.toString())
	return new packSet(p.vars,result,type)
}

function doNew(s,p){
	if(s.cls) {
		var c = newClass(s.cls.fun,s.cls.args,p,null,null,s.cls.gen)
		
		return new packSet(p.vars,c,c.name) //new class
	}else if(s.arr){
		var a = newArray(s.arr,p)
		return new packSet(p.vars,a,a.name) //new array
	}else {
		var en = s.str//hex_md5(s.str)
		var str = AUTO_BOX[en]
		if(str==null) {
			str = newString(s.str)
			AUTO_BOX[en] = str
		}
		return new packSet(p.vars,str,"String")
	}
}
function doReturn(s,p){
	if(!s.fun) {
		var p = new packSet(p.vars)
		p.ret = true
		return p
	}
	var r = doStatement(s.fun,p)
	r.ret = true
	if(s.thr) r.thr = true
	return r
}
function doBreak(s,p){
	var r = new packSet(p.vars)
	r.ret = true
	r.brk = s.brk
	return r
}
function doConv(s,p){
	var r = doStatement(s.stat,p)
	if(r.thr) return r
	r.type = s.type
	if(SEM_CHECK){
		var tmp = new J_VALUE(s.type)
		getSemDef(p.vars.cl,tmp)
		r.value = tmp.value
	}
	else if(s.type == "long") r.value = goog.math.Long.fromString(r.value.toString())
	return r
}
function doDef(s,p){
	p.vars.fun.each(function (i,item){
		if(item.name == s.va) getSemDef(p.vars.cl,item)
	})
	return new packSet(p.vars)
}
function doBlock(s,p){
	var tmp = new varSet(p.vars.cl,copyFunVars(s.vars).concat(p.vars.fun),p.vars.args)
	var result = runBlock(s.stats, tmp)
	return result
}
function doIf(s,p){
	var stat = doStatement(s.con,p)
	if(SEM_CHECK && !primType(stat.type)) semError("primitive type expected for if condition, got '"+stat.type+"'",s)
	var con = parseVal(stat);
	if(typeof con == "object" && con.thr) return con
	var result = new packSet(p.vars);
	var rets = true
	var typeL = ""
	
	if(s.thenf && (con || SEM_CHECK)) //if true
		result = doStatement(s.thenf,p)
	if(result) rets &= result.ret
	else rets = false
	typeL = result.type
	
	if(s.elsef && (!con || SEM_CHECK)) //if false and else block
		result = doStatement(s.elsef,p)
	if(s.elsef && result) rets &= result.ret
	else rets = false
	if(SEM_CHECK) {
		if(s.ternary && typeL != result.type) semError("Ternary operator expression types do not match", s)
		var a;
		if(s.ternary){
			var tmp = new J_VALUE(typeL)
			getSemDef(p.vars.cl, tmp)
			a = new packSet(p.vars,tmp.value,typeL)
		}else{
			a = new packSet(p.vars,null,typeL)
		}
		a.ret = rets
		return a
	}
	
	if(result && result.thr && !SEM_CHECK) return result
	if(result && result.ret && !SEM_CHECK) return result
	return new packSet(p.vars,result.value,"boolean")
}
function doFor(s,p){
	var forvars = new varSet(p.vars.cl,p.vars.fun.concat(copyFunVars(s.dof.vars)),p.vars.args)
	p = new packSet(forvars)
	var init
	s.def.each(function (i,item){init = doStatement(item,p)})
	if(init.thr) return init
	
	var stat = doStatement(s.con,p)
	if(SEM_CHECK && stat.type && !primType(stat.type)) semError("primitive type expected in for loop condition, got '"+stat.type+"'",s)
	var con = parseVal(stat)
	if(con==null) con = false
	if(con.thr) return con
	var result;
	while(con || SEM_CHECK){
		result = runBlock(s.dof.stats, p.vars)
		if(result.ret && result.brk==null && !SEM_CHECK) return result
		else if(result.ret && result.brk && !SEM_CHECK) break;
		var inc = doStatement(s.inc,p)
		
		if(SEM_CHECK) break;
		con = parseVal(doStatement(s.con,p))
		if(con.thr) return con
	}
	if(SEM_CHECK) {
		var a = new packSet(p.vars,null,SEM_FUN_RET)
		a.ret = (result.ret && !result.brk && !result.thr? true : false)
		return a
	}
	return new packSet(p.vars,con.value,"boolean")
}
function doWhile(s,p){
	var con = true;
	if(!s.skip){
		var stat = doStatement(s.con,p)
		if(SEM_CHECK && stat.type && !primType(stat.type)) semError("primitive type expected in while loop condition, got '"+stat.type+"'",s)
		con = parseVal(stat)
		if(con.thr) return con
	}
	var result=null;
	while(con){
		if(s.dof){
			result = doStatement(s.dof,p)
			if(result && result.ret && result.brk==null && !SEM_CHECK) return result
			if(result && result.ret && result.brk && !SEM_CHECK) break;	
		}
		if(SEM_CHECK) break;
		con = parseVal(doStatement(s.con,p))
		if(con.thr) return con
	}
	p = new packSet(p.vars,con.value,"boolean")
	if(SEM_CHECK && result) p.ret = (result.ret && !result.brk && !result.thr? true : false)
	return p
}
function doSwitch(s,p){
	var stat = doStatement(s.con,p)
	if(SEM_CHECK && stat.type != "int") semError("invalid switch statement of type '"+stat.type+"'",s)
	var con = parseVal(stat)
	if(con.thr) return con
	var rets = true
	
	var startindex = s.cases.length;
	var result = new packSet(p.vars);
	if(!SEM_CHECK){
		for(var i = 0; i < s.cases.length; i++){ //matches case in list to start from
			if(!s.cases[i].con) {startindex = i; break}
			var r = parseVal(doStatement(s.cases[i].con,p))
			if(con == r) {startindex = i; break}
		}
	}else startindex = 0;
	
	for(var i = startindex; i < s.cases.length; i++){ //performs cases starting from until break/return
		var result = runBlock(s.cases[i].stats,p.vars)
		if(SEM_CHECK) rets &= (result.ret && !result.brk && !result.thr? true : false)
		if(result.ret && result.brk==null && !SEM_CHECK) return result
		if(result.ret && result.brk && !SEM_CHECK) break;
	}
	p = new packSet(p.vars,result.value,"boolean")
	if(SEM_CHECK) p.ret = rets
	return p
}
function doTry(s,p){
	var oldp = p
	var forvars = new varSet(p.vars.cl, p.vars.fun.concat(s.tryf.vars), p.vars.args)
	p = new packSet(forvars)
	var result;
	
	result = runBlock(s.tryf.stats, p.vars)
	if(result.thr){
		var a = [new J_VALUE(result.value.name,result.value)]
		var fun = funSearch({funs:s.catches},"catch",true,"",a)
		if(!fun) return result
		fun.cl = p.vars.cl
		a = wrapArgs(fun.fun,a)
		result = runFunction(fun,a)
	}
	if(s.finallyf){
		forvars = new varSet(oldp.vars.cl,oldp.vars.fun,oldp.vars.args.concat(s.finallyf.vars))
		p = new packSet(forvars)
		result = runBlock(s.finallyf.stats, p.vars)
	}
	return new packSet(oldp)
}
function parseVal(v,p){
	if(v.thr) return v
	if(v.type=="String") return v.value.str_val
	if(v.value == null) return null
	if(v.value.is == IS_NULL) return JNULL
	if(!primType(v.type)){
		if(SEM_CHECK) return ""
		var fun = funSearch(v.value,"toString",true,"void",[])
		res = runFunction(fun,[],p.vars.caller)
		return res.value.str_val
	}
	return boundVal(v)
}
function boundVal(v){ //ensures numeric data types are within bounds
	switch(v.type){
		case "byte": return Math.mod(~~v.value+128,256)-128;break;
		case "char": return Math.mod(~~v.value,256);break;
		case "short": return Math.mod(~~v.value+32768,65536)-32768;break;
		case "int": return Math.mod(~~v.value+2147483648,4294967296)-2147483648;break;
	}
	return v.value
}
//Interpreter Functions
function newClass(name,args,p,s,sem,gen,by){
	var found = classSearch(name,p)
	if(found == null) {
		semError("class could not be found: "+name)
		return;
	}
	
	var cl = new J_CLASS(name,found.extends,true,copyVars(found.vars,args==null),found.funs,args==null,found.imports)
	//assign generics here and type check
	if(gen){
		var ok = true
		if(SEM_CHECK){
			if(found.generic){
				if(found.generic.length != gen.length) semError("Can't define new class '"+cl.name+"' ",found.tok)
			}else {ok=false;semError("Class '"+cl.name+"' does not have any generic types",found.tok)}
		}
		if(ok){
			var g = {}
			for(var i = 0; i < found.generic.length; i++) g[found.generic[i]] = gen[i]
			cl.generic = g	
		}
	}else if(found.generic){ // class has generics but none specified in new statement -> set all to Object
		var gen = {}
		for(var i = 0; i < found.generic.length; i++) gen[found.generic[i]] = "Object"
		cl.generic = gen
	}
	
	if(s) s.ext_cl = cl
	else cl.ext_cl = {name:cl.extends}
	if(!args && !SEM_CHECK) CLASSES[cl.name]=cl //add to list of static classes
	else if(SEM_CHECK) getSemClass(found,args==null,g)
	
	importClasses(cl,found) //import classses from import statement
	
	//create static version of class if not exists
	var assigns
	if(args) {
		staticClass(name,clsPack(cl))
		assigns = found.vardef.inst
	}else{
		staticClass(found.extends,clsPack(cl))
		assigns = found.vardef.stat
	}
	//assign class variables
	var clpack = clsPack(cl)
	if(args == null){
		for(var i = 0; i < cl.vars.length; i++){
			var stat = assigns[i]
			//printCmd(cl.name)
			if(stat){
				var r = doStatement(stat,clpack)
				//printCmd(cl.vars[i].type+r.type+r.value)
				if(r.thr) return r
				if(SEM_CHECK && cl.vars[i].emdef == null){
					typeCheckLR(cl.vars[i],r,stat,clpack)
					continue
				}
				cl.vars[i].value = r.value
			}
		}
	}
	
	//call class constructor if instantiation
	//debugger
	if(args!=null && !sem && (!by || (by && !p.vars.cl.bcode))){
		cl.vars.unshift(new J_VARIABLE("this",cl.name,false,cl))
		
		var args = doArgs(new packSet(p.vars),args)
		var fun = funSearch(cl,name,true,"void",args)
		
		if(fun==null) {semError("Class cannot be instantiated: "+name+getFunArgs(args),fun)}
		if(SEM_CHECK) return getSemClass(found,args==null,cl.generic)
		args = wrapArgs(fun.fun,args)
		runFunction(fun,args)
		if(cl.name!="Object") cl.vars.unshift(new J_VARIABLE("super",cl.extends,false,cl.ext_cl))
	}else if(SEM_CHECK) return getSemClass(found,args==null,cl.generic)
	
	return cl
}
function staticClass(name,p,isvar){
	if(SEM_CHECK && p && !classSearch(name,p) && name != p.vars.cl.name){
		if(isvar) return;
		else runtimeError(name+" not imported by class "+p.vars.cl.name)
	}
	var c = CLASSES[name]
	if(c == null) c = newClass(name,null,p) //static class not found so create new
	return c
}
function newArray(arr,p){
	var arr_cl = newClass("1_array",[],p)
	var type = arr.type
	var array = []
	if(arr.is == IS_ARR){ //int [9]...
		var r = doStatement(arr.arr[0],p)
		for(var i = 0; i < r.value; i++){
			var v = (primType(arr.type)?0:JNULL)
			if(arr.arr.length > 1){
				v = newArray(new J_ARR(arr.arr.slice(1,arr.arr.length),arr.type),p)
			}
			array.push(v)
		}
		arr_cl.name = ("[]").repeat(arr.arr.length)+arr.type
	}else{//{1,2,...}
		arr.arr.each(function (i,item){
			var r = doStatement(item,p)
			array.push(r.value)
		})
		
		if(arr.type.search(/^(\[\])+undefined$/) == 0) {
			var t = array[0].type
			if(!t) t = array[0].name
			if(!t && typeof array[0] == 'number') t = 'int' 
			arr_cl.name = arr.type.substring(0,arr.type.length-9)+t
		}else arr_cl.name = arr.type
	}
	if(arr.is == IS_ARR && arr.arr.length > 1) type = ("[]").repeat(arr.arr.length-1)+type
	if(arr.is!=IS_ARR) type = type.substring(2)
	
	var vars = new varSet(arr_cl,[],[])
	setVar(vars,"length",false,"length",array.length)
	arr_cl.array = array
	
	return arr_cl
}
function newString(str){
	var str_cl = newClass("String",[],idPack)
	str_cl.str_val = str.replace(/\\"/g,'"')
	return str_cl
}
function newException(name,msg){
	var args = []
	if(msg) args.push(new J_VALUE("String",newString(msg)))
	var p = new packSet(null,newClass(name,args,idPack),name)
	p.ret = true;
	p.thr = true;
	return p
}
function superClass(s,p){
	var cl = newClass(p.vars.cl.extends,s.args,p,p.vars.cl)
	p.vars.cl.ext_cl = cl
	
	var found = classSearch(p.vars.cl.name, p)
	var assigns = found.vardef.inst
	cl = p.vars.cl
	cl.vars = copyVars(found.vars, false).concat(cl.vars)
	
	//assign class variables
	var clpack = clsPack(cl)
	for(var i = 0; i < cl.vars.length; i++){
		var stat = assigns[i]
		if(stat){
			var r = doStatement(stat,clpack)
			if(r.thr) return r
			if(SEM_CHECK && cl.vars[i].emdef == null){
				typeCheckLR(cl.vars[i],r,stat,clpack)
				continue
			}
			cl.vars[i].value = r.value
		}
	}
	
	return new packSet(p.vars)
}
function copyVars(v,stat,sem){
	var dup = []
	v.each(function (i,item) {
		if((stat && item.static) || (!stat && !item.static)) dup.push(new J_VARIABLE(item.name,item.type,item.vis,defVal(item.type),null,item.static,item.fin,item.emdef,item.dependents))
	})
	return dup
}
function findMain(a){
	var main = PROG.each(function (m,item){
		var f = funSearch(item,"main",true,"void",[a])
		if(f!=null && f.fun.type == "void") return f
	})
	return main
}
function funSearch(cl,n,ext,t,a){//if(n == "append") debugger
	var funs = cl.funs["f_"+n]
	if(!funs) {
		if(cl.ext_cl && cl.ext_cl.is!=null) return funSearch(cl.ext_cl,n,ext,t,a)
		else return null //no functions found with name
	}
	
	var overs = getOverload(a)
	var alt = []
	var match = funs.each(function (i,item){ //search through found for arg type matching
		if(!(!ext || (ext && item.vis)) || !((!cl.static || (cl.static && item.static)))) return;
		if(item.args.length != a.length) return;
		var exact = true
		var error = item.args.each(function (j,arg){
			var ty = arg.type
			if(cl.generic && cl.generic[ty]) ty = cl.generic[ty]
			if(a[j]==null) return null;
			if(overs[j] && overs[j].indexOf(ty) == -1) return true
			else if (ty!=a[j].type) exact = false
		})
		if(!error && exact) return item
		else if(!error) alt.push(item)
	})
	if(match) return {cl:cl, fun:match}
	else if(alt.length > 0) return  {cl:cl, fun:alt[0]}
	if(cl.ext_cl && cl.ext_cl.is!=null) return funSearch(cl.ext_cl,n,ext,t,a) //searches parent class for function
	else return null //no functions found with name
}
function classSearch(name,p){
	var classScope = PROG
	if(p) classScope = classScope.concat(p.vars.cl.impcls)
	return classScope.each(function (c,item){
		if(!item) return
		if(item.name == name) return item
	})
}
function getVar(vars,n,ext,t,sem,th){
	var v
	if(!th){
		v = vars.args.each(function (i,item){if(item.name==n && (sem || (!sem && item.value!=null))) return item})
		if(v) return v
		v = vars.fun.each(function (i,item){if(item.name==n && (sem || (!sem && item.value!=null))) return item})
		if(v) return v
	}
	v = vars.cl.vars.each(function (i,item){if(item.name==n && (!ext || (ext && item.vis)) && item.value!=null) return item})
	if(v) {return v}
	//if searching an instance class for static var then search static version of class
	if(!vars.cl.static) v=getVar(new varSet(staticClass(vars.cl.name),[],[]),n,ext,t)
	if(v) return v
	if(vars.cl.ext_cl && vars.cl.ext_cl.is!=null)v=getVar(new varSet(vars.cl.ext_cl,[],[]),n,true,t)
	return v
}
function setVar(vars,n,ext,t,r,th){
	var set
	if(!th){ //if statement is this.x -> skip looking in function and arguments
		set = vars.args.each(function (i,item){
			if(item.name==n && item.value!=null) {item.value=r;return vars}
		})
		if(set) return set
		set = vars.fun.each(function (i,item){
			if(item.name==n && item.value!=null) {item.value=r;return vars}
		})
		if(set) return set
	}
	set = vars.cl.vars.each(function (i,item){
		if(item.name==n && (!ext || (ext && item.vis)) && item.value!=null) {
			var oldval = item.value;
			item.value=r;
			
			if(oldval != r){ //EM: handle updates if value has changed
				if(item.dependents){ //EM: update all class variables that depend on one updated
					for(var d = 0; d < item.dependents.length; d++){
						var vvv = item.dependents[d]
						var val = doStatement(item.dependents[d].emdef, clsPack(vars.cl)).value
						//printCmd("doing dep update for: "+val+"\n");
						setVar(clsPack(vars.cl).vars, vvv.name, false, vvv.type, val)
						//printCmd("doing dep update for: "+vvv.name+"\n");
					}
				}
				
				//EM: call the function representing changed variable value if defined: name_updated();
				var upd_fun = funSearch(vars.cl, n+"_updated", ext, "void", [])
				if(upd_fun){
					runFunction(upd_fun, [], vars.cl)
				}
			}
			return vars
		}
	})
	if(set) return set
	//setting static var in instance, go to static version if not special array class
	if(!vars.cl.static&&vars.cl.name.search(/^\[\]/)==-1) set=setVar(new varSet(staticClass(vars.cl.name),[],[]),n,ext,t,r)
	if(set) return set
	if(vars.cl.ext_cl && vars.cl.ext_cl.is!=null)set=setVar(new varSet(vars.cl.ext_cl,[],[]),n,true,t,r)
	return set
}
function getVal(vars,n,th){return getVar(vars,n,false,"",false,th).value}
function setVal(vars,n,r,th){setVar(vars,n,false,"",r,th)}
function primType(t){
	if(PRIM_TYPES.indexOf(t)!=-1) return true
	return false
}
function defVal(t){return (primType(t) ? 0 : JNULL);}
function printCmd(str,i){if(typeof str == "object") debugger
	RUN_OUTPUTS[(i!=null?i:CURRENT_FILE)] += str.toString().replace(/\\n/g,"\n")
	CMD.update()
}
function getFunArgs(args,name){ //printing function not found errors prints arg types eg (String, int)
	var fargs = ""
	args.each(function (i,a){fargs+=", "+(name?a.name:a.type)})
	return "("+fargs.substring(2)+")"
}
function getExt(cl,real){ //gets array of extension names for given class
	var list = []
	var gen
	var old = cl
	if(!cl) return list
	if(cl.generic){
		var tmp = []
		for(var x in cl.generic) {
			tmp.push(getExt(staticClass(cl.generic[x])))
		}
		gen = Array.combineN(tmp,',')
		for(var i = 0; i < gen.length; i++) gen[i] = "<"+gen[i]+">"
	}
	while(cl){
		list.push(cl.name)
		cl = cl.ext_cl
	}
	if(gen) {
		var tmp = Array.combineN([list,gen],'')
		list = list.concat(tmp)
	}
	if(real && real.implement.length > 0) {
		real.implement.each(function(i,imp){list = list.concat(getExt(staticClass(imp,clsPack(old))))})
	}
	return list
}
function getOverload(args,p){ //gets all possible overloads for each argument
	var result = []
	args.each(function (i,item){
		if(item.type == "?") result.push(null)
		var pre = CLS_EXT_LIST[item.type]
		if(pre){
			result.push(pre)
			return;
		}
		var list = []
		if(primType(item.type)) list = PRIM_TYPES.slice(PRIM_TYPES.indexOf(item.type))
		else {
			var a = item.type.match(/^(\[\])+/)
			if(a) {
				var ty = item.type.substring(a[0].length)
				var e
				if(primType(ty)) e = PRIM_TYPES.slice(PRIM_TYPES.indexOf(ty))
				else e = getExt(staticClass(ty,p))
				
				for(var j = 0; j < e.length; j++) e[j] = a[0]+e[j]
				list = e
			}
			else list = getExt(item.value,classSearch(item.value.name,p))
		}
		result.push(list)
		CLS_EXT_LIST[item.type] = list
	})
	return result
}
function getHashSub(item){
    if(item.name == "this") return "";
    if (typeof item.value== "object" && item.value.is==IS_NULL)
        return "null,"
    if (typeof item.value== "object")
        return item.name+"-("+getHash(item.value) +"),"
    else  return item.name+"-"+item.value+","
}
function getHash(c){
    var str = hex_md5(c.name)
    c.vars.each(function (i, item){
        str+=getHashSub(item)
    })
    if(c.name.search(/^\[\]/)==0){
        str+="arr-["
        c.array.each(function (i, item){
            str+=getHashSub({name:"",value:item})
        })
        str+="]"
    }
    return str 
}
function copyFunVars(vars,bcode){
	var v = new Array(vars.length)
	vars.each(function (i,item) {
		v[i] = new J_VARIABLE(item.name,item.type,true,null)
		if(bcode){
			v[i].pc_start = item.pc_start
			v[i].pc_end = item.pc_end
		}
	})
	return v;
}
//semantic validation functions
function getSemClass(c,s,g){
	var cl = SEM_CLASS_ID.each(function (i,cls){ //check if the class been created for semantics
		if(c.name == cls.name && s == cls.static) {
			if(!g) return cls
			if(!cls.generic) return;
			else{ //check if equal defined generics
				var ok = true
				for(var x in cls.generic)  if(g[x]!=cls.generic[x]){ok=false;break}
				if(ok) return cls
			}
		}
	})
	if(cl) return cl
	
	var cl = new J_CLASS(c.name,c.extends,true,copyVars(c.vars,s,true),c.funs,s,c.imports)
	cl.generic = g
	cl.implement = c.implement
	
	if(s) CLASSES[cl.name]=cl//CLASSES.push(cl)
	SEM_CLASS_ID.push(cl)
	importClasses(cl,c)
	cl.vars.each(function (i,v){
		getSemDef(cl,v)
	})
	cl.vars.unshift(new J_VARIABLE("this",cl.name,false,cl))
	
	var ext = classSearch(cl.extends,clsPack(cl))
	if(!ext){ 
		semError("Cannot find extension class '"+cl.extends+"' for class '"+cl.name+"'")
		ext = classSearch("Object") //can't find extension class so defaults down to Object
	}
	if(cl.name != "Object") cl.ext_cl = getSemClass(ext)
	if(cl.name!="Object") cl.vars.unshift(new J_VARIABLE("super",cl.extends,false,cl.ext_cl))
	return cl
} 
function getSemDef(cl,v){
	var t = v.type
	if(cl.generic && cl.generic[v.type]) t = cl.generic[v.type]//if class generics, get the defined type
	
	if(t.search(/^\[/)==0) t = "1_array"
	if(primType(v.type)) v.value = 0
	else {
		var gen
		if(t.indexOf("<")>-1){
			var g = t.substring(t.indexOf("<")+1,t.length-1).split(",")
			gen = g
			t=t.substring(0,t.indexOf("<"))
		}
		v.value = newClass(t,[],new packSet(new varSet(cl,[],[],null)),null,true,gen)
		if(t == "1_array") {
			v.value.name = v.type
			v.value.array = []
		}
	}
}
function typeCheckLR(l,r,s,p){ //validates the type of an assignment eg int x = 10.2 //invalid
	var over = getOverload([r],p)
	if(r.type && l.type && over[0].indexOf(l.type)==-1){
		if(r.value.is == IS_NULL){
			var tmp = new J_VALUE(l.type)
			getSemDef(p.vars.cl,tmp)
			r.value = tmp.value
		}else semError("invalid assignment of type '"+r.type+genToStr(r.value)+"' to type '"+l.type+"'",s)
	}
}
function genToStr(v){return (v.generic?'<'+_.values(v.generic)+'>':'')}
function nullReturn(v){return new packSet(v)}
function customReturn(v,va,ty){return new packSet(v,va,ty)}
function hiddenNewClass(name,vars){
	var found = classSearch(name,new packSet(vars))
	var cls = new J_CLASS(name,found.extends,true,copyVars(found.vars,false),found.funs,false,found.imports)
	importClasses(cls)
	return cls
}
function checkIsApplet(p){ //checks if the program has an applet class
	if(!AUTOMATE && DB_FILES) appendLocalClasses(p,CURRENT_PROJECT)
	var cls = (p ? p : PROGRAM).each(function (e,item){
		if(item.name + ".java" == CURRENT_FILENAME && classExtends(staticClass(item.name), "Applet")) return item
	})
	CLASSES = []
	return cls;
}
function classExtends(cls, name){
	if(cls.extends == name) return true;
	if(cls.extends == "Object") return name == cls.extends
	return classExtends(staticClass(cls.extends), name)
}
function appletLoader(name){ //class with main function to load an applet class
	var app = new J_CLASS("AppletLoader","Object",true,[],{},false,[])
	app.vardef = new J_ASSIGNS()
	var fun = new J_FUNCTION("main","void",true,true,[],[],[])
	app.funs.f_main = [fun]
	fun.args.push(new J_VARIABLE("args","[]String",false))
	fun.vars.push(new J_VARIABLE("a",name,false))
	fun.stats.push(new J_DEF("a",true))
	fun.stats.push(new J_LR(new J_CALL("a",null,false),
				new J_NEW(new J_CALL(name,[],false))))
	fun.stats.push(new J_CALL("APPLET_CLASS_SUB = getVal(vars,'a');function f(cls){if(cls.name != 'Applet') return f(cls.ext_cl); return cls;}APPLET_CLASS = f(getVal(vars,'a'));setVar(new varSet(APPLET_CLASS,[],[]),'a',false,'Applet',getVal(vars,'a'));return nullReturn(vars)",[],true))
	fun.stats.push(new J_CALL("a",null,false,new J_CALL("init",[],false)))
	return app
}
//Temp Objects
idPack = new packSet(new varSet({vars:[], impcls:[]},[],[]))
function clsPack(cl){return new packSet(new varSet(cl,[],[]))}
function clsVar(cl){return new varSet(cl,[],[])}
function varSet(c,f,a,ca){
	this.cl = c
	this.fun = f
	this.args = a
	this.caller = ca
}
function packSet (va,vl,ty,na) {
	this.vars = va
 	this.value = vl
 	this.type = ty
 	this.name = na
 	this.index
 	this.arr_cl
 	this.ret
 	this.brk
 	this.thr
}
function semSet(m,o){
       this.msg = m
       this.obj = o
}
