HomeForumSourceResearchGuide
Sign in to contribute to source. how it works
Component util.compiler.OpParser by barry
expand copy to clipboardexpand
uses HWI

data OpParseResultX extends OpParseResult nocycle {
	bool terminalErrorLine
	bool terminalErrorScope
	bool terminalErrorFunction
	bool terminalError
}

data Scope nocycle {
	ScopeVariable variables[]
	OpToken startInstruction
}

data ConInt {
	int value
}

data ConInt1 {
	int1 value
}

data ConInt2 {
	int2 value
}

data ConInt4 {
	int4 value
}

data ConInt8 {
	int8 value
}

data ConInt16 {
	int16 value
}

data ConInt32 {
	int32 value
}

data ConInt64 {
	int64 value
}

data ConInt128 {
	int128 value
}

data ConInt256 {
	int256 value
}

data ConInt512 {
	int512 value
}

data LocalCallOp nocycle {
	OpToken op
	LocalCallOp next
}

data OpFunctionDetail extends OpFunction nocycle {
	DanaToken tree
	LocalCallOp localCalls
}

data ApplyOpResult nocycle {
	OpToken instruction
	OpToken asparent
	DanaToken subTokens
}

data ControlFrame nocycle {
	OpToken op
	DanaToken scope
	const byte CF_IF = 1
	const byte CF_ELSE = 2
	const byte CF_WHILE = 3
	const byte CF_MUTEX = 4
	byte cfType
	BreakOp breakOps[]
}

data BreakOp nocycle {
	OpToken op
	DanaToken token
}

data Constant {
	char name[]
	DanaType type
	byte value[]
	bool deprecated
	char deprecatedBy[]
}

data TypeBoundConstants nocycle {
	char typeName[]
	Constant constants[]
}

data IFXInfo nocycle {
	OpToken jumpOp
	OpToken parentOp
	OpToken gparentOp
}

data IFXHolder nocycle {
	IFXInfo info
}

data ForParams nocycle {
	DanaToken initialiser
	DanaToken condition
	DanaToken increment
}

data AssocOption nocycle {
	char instanceName[]
	char interfaceType[]
	int functionIndex
	int rank
}

data ResetVariable nocycle {
	int index
	char type[]
	char value[]
}

data ResetVariables nocycle {
	ResetVariable vars[]
}

data ParamInfo {
	int coreParams
	String optParams[]
}

const char CMSRC_SEP[] = "#"

const bool DEBUG_BASIC = false
const bool DEBUG_INS_GEN = false
const bool DEBUG_STORE_ANALYSIS = false
const bool DEBUG_LITERAL_FRACTIONS = false
const bool DEBUG_DECIMAL_SCALING = false

const int OBJECT_EQUAL_INDEX = 1 //function index of Object.equals()

component provides OpParser requires io.Output out, data.adt.List, data.adt.Stack, data.StringUtil stringUtil, data.IntUtil iu, data.Int512Util iu512, data.ByteUtil bu, data.query.Search search, AssignGraphBuilder agrBuilder, TypeUtil typeUtil, DNCUtil dncUtil {

	//component-scope constants
	List constants

	//type-bound constants (these are parsed here)
	List typeBoundConstants

	//functions, of type OpFunction
	List functions

	int addressWidth = 8

	int getNativeAddressWidth()
		{
		ConInt c = new ConInt()
		return dana.serial(c).arrayLength
		}
	
	OpParser:OpParser(opt int aw)
		{
		constants = new List()
		functions = new List()
		typeBoundConstants = new List()

		if (isset aw)
			{
			addressWidth = aw
			}
			else
			{
			addressWidth = getNativeAddressWidth()
			}
		}
	
	void postError(OpParseResult result, int lineNumber, char file[], char message[])
		{
		result.success = false
		result.errors = new OpParseError[](result.errors, new OpParseError(file, lineNumber, message))
		}
	
	void postWarning(OpParseResult result, int lineNumber, char file[], char message[])
		{
		result.success = false
		result.warnings = new OpParseError[](result.warnings, new OpParseError(file, lineNumber, message))
		}
	
	DanaType makeFunctionDanaType(OpFunction f, DanaType types[])
		{
		DanaType result = new DanaType(DanaType.FUNCTION)
		result.fields = dana.sub(f.variables.fields, 0, f.parameters.arrayLength)
		return result
		}
	
	bool isRefType(DanaType t)
		{
		return t.class == DanaType.DATA || t.class == DanaType.INTERFACE || (t.class == DanaType.ARRAY && t.storageSize == 0)
		}
	
	void collectConstants(char file[], DanaToken tree, DanaType types[], OpParseResult status)
		{
		if (DEBUG_BASIC) out.println("COLLCT_CONSTANTS")
		Constant nc

		nc = new Constant("true", dncUtil.findType(types, "bool"))
		nc.value = parseLiteral(file, new DanaToken(type = DanaToken.LITERAL, subType = TokSubTypes.L_INT, token = "1"), new OpToken(), nc.type, types, status)
		constants.add(nc)

		nc = new Constant("false", dncUtil.findType(types, "bool"))
		nc.value = parseLiteral(file, new DanaToken(type = DanaToken.LITERAL, subType = TokSubTypes.L_INT, token = "0"), new OpToken(), nc.type, types, status)
		constants.add(nc)

		nc = new Constant("INT_MAX", dncUtil.findType(types, "int"))
		if (addressWidth == 8)
			nc.value = parseLiteral(file, new DanaToken(type = DanaToken.LITERAL, subType = TokSubTypes.L_INT, token = "18446744073709551615"), new OpToken(), nc.type, types, status)
			else if (addressWidth == 4)
			nc.value = parseLiteral(file, new DanaToken(type = DanaToken.LITERAL, subType = TokSubTypes.L_INT, token = "4294967295"), new OpToken(), nc.type, types, status)
			else
			throw new Exception("unknown address width of $addressWidth")
		constants.add(nc)

		//collect type-bound constants
		//TODO: we need to actually verify that RHS values are constants/literals
		for (int i = 0; i < types.arrayLength; i++)
			{
			TypeBoundConstants tbc = new TypeBoundConstants(types[i].name)

			for (int j = 0; j < types[i].fields.arrayLength; j++)
				{
				if (types[i].fields[j].qualifier == "constant")
					{
					DanaType qt = dncUtil.findType(types, types[i].fields[j].type)

					nc = new Constant(types[i].fields[j].name, types.findFirst(DanaType.[name], qt))
					nc.deprecated = types[i].fields[j].deprecated
					nc.deprecatedBy = types[i].fields[j].deprecatedBy

					OpToken newToken = new OpToken()
					nc.value = parseLiteral(file, types[i].fields[j].value, newToken, nc.type, types, status)
					if (qt.class == DanaType.ARRAY && qt.storageSize == 0)
						{
						//for arrays we override the declared type with the actual, precise type, which will have the fixed-array size
						nc.type = newToken.returnType
						}

					tbc.constants = new Constant[](tbc.constants, nc)
					}
				}
			
			if (tbc.constants.arrayLength != 0)
				{
				typeBoundConstants.add(tbc)
				}
			}

		//collect component-level constants
		DanaToken pw = tree.subTokens

		while (pw != null)
			{
			if (pw.type == DanaToken.OUTER_SCOPE)
				{
				//all constants should be here

				DanaToken sw = pw.subTokens

				while (sw != null)
					{
					if (sw.type == DanaToken.OPERATION && sw.subType == TokSubTypes.ASSIGN && (sw.subTokens.type == DanaToken.OPERATION && (sw.subTokens.subType == TokSubTypes.DECLARE || sw.subTokens.subType == TokSubTypes.DECLARE_ARRAY)))
						{
						//parse the RHS using the normal parse function...
						if (sw.subTokens.exStr2 == "constant")
							{
							DanaType declareType = getDeclareType(sw.subTokens, types)
							OpToken newToken = new OpToken()

							nc = new Constant(sw.subTokens.exStr1, types.findFirst(DanaType.[name], declareType))
							nc.value = parseLiteral(file, sw.subTokens.next, newToken, nc.type, types, status)
							if (declareType.class == DanaType.ARRAY && declareType.storageSize == 0)
								{
								//for arrays we override the declared type with the actual, precise type, which will have the fixed-array size
								nc.type = newToken.returnType
								}
							constants.add(nc)
							}
						}
					
					sw = sw.next
					}
				}
			
			pw = pw.next
			}
		}
	
	Constant getTypeBoundConstant(DanaType t, char name[])
		{
		for (TypeBoundConstants tbc = typeBoundConstants.getFirst(); tbc != null; tbc = typeBoundConstants.getNext())
			{
			if (tbc.typeName == t.name)
				{
				for (int i = 0; i < tbc.constants.arrayLength; i++)
					{
					if (tbc.constants[i].name == name)
						{
						return tbc.constants[i]
						}
					}
				
				return null
				}
			}

		return null
		}
	
	void makeClassInitFunction(char file[], DanaToken tree, DanaType types[], OpParseResultX status)
		{
		status.staticGlobals = new DanaType(DanaType.DATA)

		//configure the function details, including its return type and local variables
		status.classInitFunction = new OpFunction()
		status.classInitFunction.name = "_c_init"
		status.classInitFunction.returnType = dncUtil.findType(types, "void")
		status.classInitFunction.variables = new DanaType(DanaType.DATA)
		status.classInitFunction.variables.fields = new DanaTypeField(null, "void")

		//gather static variables and any associated assignments
		DanaToken pw = tree.subTokens

		while (pw != null)
			{
			if (pw.type == DanaToken.COMPONENT)
				{
				if (pw.next == null || pw.next.type != DanaToken.SCOPE)
					{
					postError(status, pw.lineNumber, file, "no component implementation found to compile (expected {})")
					return
					}
				
				pw = pw.next.subTokens
				while (pw != null)
					{
					if (pw.type == DanaToken.OPERATION)
						{
						if (pw.subType == TokSubTypes.DECLARE || pw.subType == TokSubTypes.DECLARE_ARRAY)
							{
							//add a field to our instanceGlobals type
							if (pw.exStr2 == "static")
								{
								if (globalStaticVariableExists(status, null, pw.exStr1))
									{
									postError(status, pw.lineNumber, file, "static global variable '$(pw.exStr1)' has already been declared in this component")
									}

								DanaType declareType = getDeclareType(pw, types)
								status.staticGlobals.fields = new DanaTypeField[](status.staticGlobals.fields, new DanaTypeField(pw.exStr1, declareType.name))
								}
							}
							else
							{
							//add to our class init function, but convert =(declare(a), x) to just =(a, x)
							if (pw.type == DanaToken.OPERATION && pw.subType == TokSubTypes.ASSIGN && (pw.subTokens.type == DanaToken.OPERATION && (pw.subTokens.subType == TokSubTypes.DECLARE || pw.subTokens.subType == TokSubTypes.DECLARE_ARRAY)))
								{
								//parse the RHS using the normal parse function...
								if (pw.subTokens.exStr2 == "static")
									{
									if (globalStaticVariableExists(status, null, pw.subTokens.exStr1))
										{
										postError(status, pw.subTokens.lineNumber, file, "static global variable '$(pw.subTokens.exStr1)' has already been declared in this component")
										}

									DanaType declareType = getDeclareType(pw.subTokens, types)
									status.staticGlobals.fields = new DanaTypeField[](status.staticGlobals.fields, new DanaTypeField(pw.subTokens.exStr1, declareType.name))

									int vindex = status.staticGlobals.fields.arrayLength - 1

									OpToken op = new OpToken(HWI.OP_ID_ASSIGN)
									op.returnType = declareType

									OpToken newOp = new OpToken()
									newOp.lineNumber = pw.lineNumber

									if (isRefType(declareType))
										{
										op.type = HWI.OP_ID_ASSIGN_POINTER

										newOp.type = HWI.OP_ID_GET_PTR_HND
										newOp.variableIndex = vindex
										newOp.returnType = declareType

										insertPR(status.classInitFunction, op)
										newOp.parameters = new OpToken[](new OpToken(HWI.OP_ID_GET_PR), new OpToken(HWI.OP_ID_GLOBALS))
										}
										else
										{
										newOp.type = HWI.OP_ID_GET_PTR
										newOp.variableIndex = vindex
										newOp.returnType = declareType

										newOp.parameters = new OpToken[](new OpToken(HWI.OP_ID_GET_PR), new OpToken(HWI.OP_ID_GLOBALS))
										}
									
									op.parameters = new OpToken[](op.parameters, newOp)
									
									parseOperation(file, status.classInitFunction, true, null, null, op, 1, new List(), pw.subTokens.next, null, types, new Stack(), new IFXHolder(), new ResetVariables(), status)
									if (status.terminalErrorLine || status.terminalErrorScope || status.terminalErrorFunction) return
									updateOperation(file, status.classInitFunction, null, op, null, null, 1, types, new Stack(), status)

									status.classInitFunction.instructions = new OpToken[](status.classInitFunction.instructions, op)
									}
								}
							}
						}
					
					pw = pw.next
					}
				
				break
				}
			
			pw = pw.next
			}

		//add a return instruction to finish
		status.classInitFunction.instructions = new OpToken[](status.classInitFunction.instructions, new OpToken(HWI.OP_ID_RETURN))
		}
	
	void parseObjectInitOp(char file[], ProvidedObject pObject, DanaToken pw, DanaType types[], bool staticError, OpParseResultX status)
		{
		if (pw.subType == TokSubTypes.DECLARE || pw.subType == TokSubTypes.DECLARE_ARRAY)
			{
			//add a field to our instanceGlobals type
			if (pw.exStr2 != "static")
				{
				if (globalVariableExists(status, pObject, pw.exStr1))
					{
					postError(status, pw.lineNumber, file, "global variable '$(pw.exStr1)' has already been declared in this component")
					}
				
				if (globalStaticVariableExists(status, pObject, pw.exStr1))
					{
					postError(status, pw.lineNumber, file, "global variable '$(pw.exStr1)' shadows a static global variable of the same name")
					}

				DanaType declareType = getDeclareType(pw, types)
				if (declareType == null) out.println("unknown type $(pw.token) on line $(pw.lineNumber)")
				pObject.instanceGlobals.fields = new DanaTypeField[](pObject.instanceGlobals.fields, new DanaTypeField(pw.exStr1, declareType.name))
				}
				else if (staticError)
				{
				postError(status, pw.lineNumber, file, "implementation scopes cannot contain static global variable declarations")
				return
				}
			}
			else
			{
			//add to our object init function, but convert =(declare(a), x) to just =(a, x)
			if (pw.type == DanaToken.OPERATION && pw.subType == TokSubTypes.ASSIGN && (pw.subTokens.type == DanaToken.OPERATION && (pw.subTokens.subType == TokSubTypes.DECLARE || pw.subTokens.subType == TokSubTypes.DECLARE_ARRAY)))
				{
				//parse the RHS using the normal parse function...
				if (pw.subTokens.exStr2 != "static")
					{
					if (globalVariableExists(status, pObject, pw.subTokens.exStr1))
						{
						postError(status, pw.subTokens.lineNumber, file, "global variable '$(pw.subTokens.exStr1)' has already been declared in this component")
						}
					
					if (globalStaticVariableExists(status, pObject, pw.subTokens.exStr1))
						{
						postError(status, pw.subTokens.lineNumber, file, "global variable '$(pw.subTokens.exStr1)' shadows a static global variable of the same name")
						}

					DanaType declareType = getDeclareType(pw.subTokens, types)
					pObject.instanceGlobals.fields = new DanaTypeField[](pObject.instanceGlobals.fields, new DanaTypeField(pw.subTokens.exStr1, declareType.name))

					int vindex = pObject.instanceGlobals.fields.arrayLength - 1

					OpToken op = new OpToken(HWI.OP_ID_ASSIGN)
					op.returnType = declareType

					OpToken newOp = new OpToken()
					newOp.lineNumber = pw.lineNumber

					if (isRefType(declareType))
						{
						op.type = HWI.OP_ID_ASSIGN_POINTER

						newOp.type = HWI.OP_ID_GET_PTR_HND
						newOp.variableIndex = vindex
						newOp.returnType = declareType

						insertPR(pObject.initFunction, op)
						newOp.parameters = new OpToken[](new OpToken(HWI.OP_ID_GET_PR), new OpToken(HWI.OP_ID_PRIVATE_IVS))
						}
						else
						{
						newOp.type = HWI.OP_ID_GET_PTR
						newOp.variableIndex = vindex
						newOp.returnType = declareType

						newOp.parameters = new OpToken[](new OpToken(HWI.OP_ID_GET_PR), new OpToken(HWI.OP_ID_PRIVATE_IVS))
						}
					
					op.parameters = new OpToken[](op.parameters, newOp)
					
					parseOperation(file, pObject.initFunction, true, null, pObject, op, 1, new List(), pw.subTokens.next, null, types, new Stack(), new IFXHolder(), new ResetVariables(), status)
					if (status.terminalErrorLine || status.terminalErrorScope || status.terminalErrorFunction) return
					updateOperation(file, pObject.initFunction, pObject, op, null, null, 1, types, new Stack(), status)

					pObject.initFunction.instructions = new OpToken[](pObject.initFunction.instructions, op)
					}
					else if (staticError)
					{
					postError(status, pw.lineNumber, file, "implementation scopes cannot contain static global variable declarations")
					return
					}
				}
				else
				{
				postError(status, pw.lineNumber, file, "global scope may only contain declarations and assignments")
				return
				}
			}
		}
	
	void makeObjectInitFunctions(char file[], DanaToken tree, DanaType types[], OpParseResult status)
		{
		DanaToken pw = tree.subTokens

		for (int i = 0; i < status.providedObjects.arrayLength; i++)
			{
			ProvidedObject pObject = status.providedObjects[i]

			//configure the function details, including its return type and local variables
			pObject.initFunction = new OpFunction()
			pObject.initFunction.returnType = dncUtil.findType(types, "void")
			pObject.initFunction.variables = new DanaType(DanaType.DATA)
			pObject.initFunction.variables.fields = new DanaTypeField(null, "void")

			//auto-instanced required interfaces
			for (int j = 0; j < status.requiredInterfaces.arrayLength; j++)
				{
				if (status.requiredInterfaces[j].autoInstance != null)
					{
					if (globalVariableExists(status, pObject, status.requiredInterfaces[j].autoInstance))
						{
						postError(status, pw.lineNumber, file, "global variable '$(status.requiredInterfaces[j].autoInstance)' (auto-instance variable name of required interface '$(status.requiredInterfaces[j].name)') has already been declared in this component")
						}

					DanaType declareType = types.findFirst(DanaType.[name], new DanaType(name = status.requiredInterfaces[j].name))
					pObject.instanceGlobals.fields = new DanaTypeField[](pObject.instanceGlobals.fields, new DanaTypeField(status.requiredInterfaces[j].autoInstance, declareType.name))

					//TODO: check this type has no constructor!

					int vindex = pObject.instanceGlobals.fields.arrayLength-1

					OpToken newOp = new OpToken(HWI.OP_ID_ASSIGN_POINTER)

					newOp.parameters = new OpToken[](
									new OpToken(HWI.OP_ID_GET_PR),
									new OpToken(HWI.OP_ID_GET_PTR_HND, variableIndex = vindex, returnType = declareType,
												parameters = new OpToken[](new OpToken(HWI.OP_ID_GET_PR), new OpToken(HWI.OP_ID_PRIVATE_IVS))),
									new OpToken(HWI.OP_ID_NEW_OBJECT, refType = declareType, returnType = declareType, bindportIndex = j,
												parameters = new OpToken[](new OpToken(HWI.OP_ID_GET_PR)))
									)

					pObject.initFunction.instructions = new OpToken[](pObject.initFunction.instructions, newOp)
					}
				}
			}

		while (pw != null)
			{
			if (pw.type == DanaToken.COMPONENT)
				{
				if (pw.next == null || pw.next.type != DanaToken.SCOPE)
					{
					postError(status, pw.lineNumber, file, "no component implementation found to compile (expected {})")
					return
					}
				
				//collect functions, including from implementation scopes
				ProvidedObject pObject = null

				if (status.providedObjects.arrayLength == 1)
					{
					pObject = status.providedObjects[0]
					}

				pw = pw.next.subTokens
				while (pw != null)
					{
					if (pw.type == DanaToken.OPERATION)
						{
						parseObjectInitOp(file, pObject, pw, types, false, status)
						}
						else if (pw.type == DanaToken.IMPLEMENTATION)
						{
						ProvidedObject options[] = status.providedObjects.find(ProvidedObject.[mainInterface], new ProvidedObject(pw.exStr1))

						if (options == null)
							{
							postError(status, pw.lineNumber, file, "no provided interface declared by the type name '$(pw.exStr1)'")
							return
							}

						if (pw.exStr2 != null)
							{
							pObject = options.findFirst(ProvidedObject.[semantic], new ProvidedObject(semantic = pw.exStr2))

							if (pObject == null)
								{
								postError(status, pw.lineNumber, file, "no provided interface declared by the type name '$(pw.exStr1):$(pw.exStr2)'")
								return
								}
							}
							else if (options.arrayLength != 1)
							{
							postError(status, pw.lineNumber, file, "multiple provided interfaces match the name '$(pw.exStr1)'")
							return
							}
							else
							{
							pObject = options[0]
							}

						DanaToken sw = pw.next.subTokens

						while (sw != null)
							{
							if (sw.type == DanaToken.OPERATION)
								{
								parseObjectInitOp(file, pObject, sw, types, true, status)
								}
								else if (sw.type == DanaToken.IMPLEMENTATION)
								{
								postError(status, sw.lineNumber, file, "implementation scopes cannot be nested")
								return
								}

							sw = sw.next
							}
						}
					
					pw = pw.next
					}
				
				for (int i = 0; i < status.providedObjects.arrayLength; i++)
					{
					pObject = status.providedObjects[i]

					//add a return instruction to finish
					pObject.initFunction.instructions = new OpToken[](pObject.initFunction.instructions, new OpToken(HWI.OP_ID_RETURN))
					}
				
				return
				}
			
			pw = pw.next
			}
		}
	
	void collectProvidedObjects(char file[], DanaToken tree, DanaType types[], OpParseResult status)
		{
		//collect objects from the component header, then populate with functions from implementation blocks (or a default implementation)
		DanaToken pw = tree.subTokens

		while (pw != null)
			{
			if (pw.type == DanaToken.COMPONENT)
				{
				DanaToken scope = pw.next

				pw = pw.subTokens.subTokens
				while (pw != null)
					{
					if (DEBUG_BASIC) out.println(" - provides $(pw.token):$(pw.exStr2)")
					// add the provided object
					ProvidedObject newObject = new ProvidedObject(pw.token)
					newObject.semantic = pw.exStr2
					newObject.instanceGlobals = new DanaType(DanaType.DATA)
					newObject.transferState = new DanaType(DanaType.DATA)
					// walk through sub-interfaces, if any
					DanaToken sw = pw.subTokens
					while (sw != null)
						{
						if (DEBUG_BASIC) out.println("    - sub $(sw.subTokens.token)")
						newObject.subInterfaces = new String[](newObject.subInterfaces, new String(sw.subTokens.token))
						sw = sw.next
						}
					
					DanaType mt = types.findFirst(DanaType.[name], new DanaType(name = pw.token))
					//sanity check
					if (mt.class != DanaType.INTERFACE)
						{
						postError(status, pw.lineNumber, file, "type '$(pw.token)' cannot be used for 'provides' (only interface types are permitted)")
						}

					//build transfer state
					for (int j = 0; j < mt.fields.arrayLength; j ++)
						{
						DanaType fieldType = types.findFirst(DanaType.[name], new DanaType(name = mt.fields[j].type))
						if (fieldType.class != DanaType.EVENT && fieldType.class != DanaType.FUNCTION && mt.fields[j].qualifier == "transfer")
							{
							newObject.transferState.fields = new DanaTypeField[](newObject.transferState.fields, clone mt.fields[j])
							}
						}
					
					status.providedObjects = new ProvidedObject[](status.providedObjects, newObject)

					pw = pw.next
					}
				
				return
				}
			
			pw = pw.next
			}
		
		postError(status, 0, file, "no component found to compile")
		}
	
	void collectRequires(char file[], DanaToken tree, DanaType types[], OpParseResult status)
		{
		//collect objects from the component header, then populate with functions from implementation blocks (or a default implementation)
		DanaToken pw = tree.subTokens

		while (pw != null)
			{
			if (pw.type == DanaToken.COMPONENT)
				{
				pw = pw.subTokens.next.subTokens
				while (pw != null)
					{
					if (DEBUG_BASIC) out.println(" - requires $(pw.token)")
					RequiredInterface nrq = new RequiredInterface(pw.token)
					if (pw.subTokens != null) nrq.autoInstance = pw.subTokens.token
					if (pw.exStr1 == "native") nrq.isNative = true
					nrq.semantic = pw.exStr2

					//already-declared check
					RequiredInterface match[] = status.requiredInterfaces.find(RequiredInterface.[name], nrq)
					if (match != null)
						{
						if (match.findFirst(RequiredInterface.[semantic], nrq) != null)
							{
							if (nrq.semantic != null)
								{
								postWarning(status, pw.lineNumber, file, "required interface of type $(nrq.name):$(nrq.semantic) has already been declared on this component")
								}
								else
								{
								postWarning(status, pw.lineNumber, file, "required interface of type $(nrq.name) has already been declared on this component")
								}
							}
						}

					status.requiredInterfaces = new RequiredInterface[](status.requiredInterfaces, nrq)

					//type sanity check
					DanaType dt = types.findFirst(DanaType.[name], new DanaType(name = pw.token))
					if (dt.class != DanaType.INTERFACE)
						{
						postError(status, pw.lineNumber, file, "type '$(pw.token)' cannot be used for 'requires' (only interface types are permitted)")
						}
					
					//auto-instantiable check
					if (nrq.autoInstance != null)
						{
						if (dt.fields.findFirst(DanaTypeField.[name], new DanaTypeField(name = dt.name)) != null)
							{
							postError(status, pw.lineNumber, file, "type '$(pw.token)' cannot be automatically instantiated in 'requires' due to it having a constructor")
							}
						}

					//deprecated check
					dt = types.findFirst(DanaType.[name], new DanaType(name = pw.token))
					if (dt.deprecated)
						{
						if (dt.deprecatedBy != null)
							{
							postWarning(status, pw.lineNumber, file, "interface type $(pw.token) is deprecated, use $(dt.deprecatedBy) instead")
							}
							else
							{
							postWarning(status, pw.lineNumber, file, "interface type $(pw.token) is deprecated")
							}
						}

					pw = pw.next
					}
				
				return
				}
			
			pw = pw.next
			}
		
		postError(status, 0, file, "no component found to compile")
		}
	
	bool isAutoStoreType(DanaType t, DanaType types[])
		{
		if (t.class == DanaType.INTEGER || t.class == DanaType.DECIMAL)
			{
			return true
			}
		
		if (t.class == DanaType.ARRAY)
			{
			DanaType fieldType = dncUtil.findType(types, t.fields[0].type)
			
			return isAutoStoreType(fieldType, types)
			}
		
		return false
		}
	
	OpFunctionDetail[] buildFunctionsFrom(DanaType t, DanaType types[])
		{
		OpFunctionDetail functionList[]

		//unpack our source-file information, which is packed into a single-string format after types have been flattened
		// - we need to attach source-file information to each function that we import, so that we can generate sensible file/line-number error messages
		String fromFileList[]
		int fromFileIndices[]

		if (t == null)
			{
			throw new Exception("provided interface type is null")
			}

		String parts[] = t.fromFile.explode(CMSRC_SEP)

		for (int i = 0; i < parts.arrayLength; i++)
			{
			char nm[] = parts[i].string
			int fromIndex = 0
			if (i != 0)
				{
				nm = parts[i].string.rsplit("[")[0].string
				fromIndex = parts[i].string.rsplit("[")[1].string.rsplit("]")[0].string.intFromString()
				}
			
			fromFileList = new String[](fromFileList, new String(nm))
			fromFileIndices = new int[](fromFileIndices, fromIndex)
			}
		
		int fndx = 0

		for (int j = 0; j < t.fields.arrayLength; j++)
			{
			DanaType fieldType = dncUtil.findType(types, t.fields[j].type)

			if (fieldType.class == DanaType.FUNCTION)
				{
				if ((fndx + 1 < fromFileIndices.arrayLength) && (j >= fromFileIndices[fndx+1]))
					{
					fndx ++
					}

				OpFunctionDetail newFunction = new OpFunctionDetail(t.fields[j].name)
				if (t.fields[j].flags == DanaTypeField.INHERITED) newFunction.inherited = true
				if (t.fields[j].name == t.name) newFunction.ftype = OpFunction.CONSTRUCTOR
				newFunction.fromType = t.name
				newFunction.returnType = dncUtil.findType(types, fieldType.fields[0].type)
				newFunction.fromFile = fromFileList[fndx].string

				newFunction.variables = new DanaType(DanaType.DATA)
				newFunction.variables.fields = new DanaTypeField(null, fieldType.fields[0].type)

				//add parameters
				
				for (int k = 1; k < fieldType.fields.arrayLength; k++)
					{
					DanaType ftype = dncUtil.findType(types, fieldType.fields[k].type)
					Parameter newParam = new Parameter(fieldType.fields[k].name, 0, false, ftype)
					newFunction.variables.fields = new DanaTypeField[](newFunction.variables.fields, rclone fieldType.fields[k])
					newFunction.parameters = new Parameter[](newFunction.parameters, newParam)

					if (isAutoStoreType(ftype, types))
						{
						newParam.qualifier = Parameter.Q_STORE
						}
						else if (fieldType.fields[k].qualifier == "opt,store" || fieldType.fields[k].qualifier == "store")
						{
						newParam.qualifier = Parameter.Q_STORE
						}
					}
				
				
				//take the default implementation, if any (this will be overridden by a local alternative)
				newFunction.tree = fieldType.defaultFunction

				functionList = new OpFunctionDetail[](functionList, newFunction)
				}
			}
		
		return functionList
		}
	
	void populateProvidedFunctions(char file[], DanaToken tree, DanaType types[], OpParseResult status)
		{
		for (int i = 0; i < status.providedObjects.arrayLength; i++)
			{
			status.providedObjects[i].interfaces = new InterfaceFunctions[status.providedObjects[i].subInterfaces.arrayLength+1]

			InterfaceFunctions ifunctions = new InterfaceFunctions(status.providedObjects[i].mainInterface)
			DanaType itype = types.findFirst(DanaType.[name], new DanaType(name = status.providedObjects[i].mainInterface))
			ifunctions.functions = buildFunctionsFrom(itype, types)

			status.providedObjects[i].interfaces[0] = ifunctions

			for (int j = 0; j < status.providedObjects[i].subInterfaces.arrayLength; j++)
				{
				ifunctions = new InterfaceFunctions(status.providedObjects[i].subInterfaces[j].string)
				itype = types.findFirst(DanaType.[name], new DanaType(name = status.providedObjects[i].subInterfaces[j].string))
				if (itype == null)
					{
					out.println("[error: type '$(status.providedObjects[i].subInterfaces[j].string)' not found]")
					}
				ifunctions.functions = buildFunctionsFrom(itype, types)

				status.providedObjects[i].interfaces[j+1] = ifunctions
				}
			}
		}
	
	bool hasImplementations(char file[], DanaToken tree, DanaType types[], OpParseResult status)
		{
		DanaToken pw = tree.subTokens

		while (pw != null)
			{
			if (pw.type == DanaToken.COMPONENT)
				{
				if (pw.next == null || pw.next.type != DanaToken.SCOPE)
					{
					postError(status, pw.lineNumber, file, "no component implementation found to compile (expected {})")
					return false
					}
				
				//collect functions, including from implementation scopes
				pw = pw.next.subTokens
				while (pw != null)
					{
					if (pw.type == DanaToken.IMPLEMENTATION)
						{
						return true
						}
					
					pw = pw.next
					}
				
				return false
				}
			
			pw = pw.next
			}
		
		return false
		}
	
	DanaType getDeclareType(DanaToken fnx, DanaType types[])
		{
		DanaType ftype = null
		
		if (fnx.subType == TokSubTypes.DECLARE)
			{
			ftype = dncUtil.findType(types, fnx.subTokens.token)
			}
			else if (fnx.subType == TokSubTypes.DECLARE_ARRAY)
			{
			DanaType atype
			ftype = dncUtil.findType(types, fnx.subTokens.token)

			char typeStart[] = fnx.subTokens.token
			if (fnx.subTokens.next != null)
				{
				char arraySize[] = null
				DanaToken dts = fnx.subTokens.next
				while (dts != null)
					{
					arraySize = null
					
					if (dts.subTokens != null && dts.subTokens.token != null)
						{
						arraySize = dts.subTokens.token
						}
						else if (dts.token != null)
						{
						arraySize = dts.token
						}
					

					if (arraySize == null)
						{
						atype = dncUtil.findType(types, "$(typeStart)[]")
						if (dts.next != null) typeStart = "$(typeStart)[]"
						}
						else
						{
						atype = dncUtil.findType(types, "$(typeStart)[$arraySize]")
						if (dts.next != null) typeStart = "$(typeStart)[$arraySize]"
						}
					
					dts = dts.next
					}
				
				if (arraySize == null)
					ftype = dncUtil.findType(types, "$(typeStart)[]")
					else
					ftype = dncUtil.findType(types, "$(typeStart)[$arraySize]")
				}
				else
				{
				ftype = types.findFirst(DanaType.[name], new DanaType(name = "$(typeStart)[]"))
				}
			}
		
		return ftype
		}
	
	DanaType findIntTypeBySize(DanaType types[], int size)
		{
		for (int i = 0; i < types.arrayLength; i++)
			{
			if (types[i].class == DanaType.INTEGER && types[i].storageSize == size)
				{
				return types[i]
				}
			}
		
		return null
		}
	
	bool populateParams(char file[], DanaToken pw, OpFunctionDetail newFunction, DanaType types[], bool autoStore, OpParseResult status)
		{
		if (pw.subTokens != null)
			{
			for (DanaToken xw = pw.subTokens; xw != null; xw = xw.next)
				{
				DanaToken param = xw.subTokens

				if (param.type != DanaToken.OPERATION || (param.token != "declare" && param.token != "declare_array"))
					{
					postError(status, param.lineNumber, file, "unexpected statement form in function definition (expected variable declaration, got '$(param.token)')")
					return false
					}
				
				//restrict parameters to be qualified by "store" or no qualifier
				if (param.exStr2 != "store" && param.exStr2 != "opt" && param.exStr2 != "opt,store" && param.exStr2 != null)
					{
					postError(status, param.lineNumber, file, "function parameters must be 'store', 'opt', or plain (found '$(param.exStr2)' for $(param.exStr1))")
					return false
					}

				DanaType ftype = getDeclareType(param, types)
				if (ftype == null) throw new Exception("unknown type for param $(param.token), at length $(newFunction.variables.fields.arrayLength)")
				Parameter newParam = new Parameter(param.exStr1, 0, false, ftype)
				newFunction.variables.fields = new DanaTypeField[](newFunction.variables.fields, new DanaTypeField(param.exStr1, ftype.name, param.exStr2))
				newFunction.parameters = new Parameter[](newFunction.parameters, newParam)

				if (autoStore)
					{
					if (isRefType(newParam.type))
						{
						newParam.autoStore = true
						newParam.qualifier = Parameter.Q_STORE
						}
					}
				}
			}
		
		return true
		}
	
	bool checkParams(char file[], DanaToken pw, OpFunctionDetail newFunction, DanaType types[], OpParseResult status)
		{
		int i = 0

		if (pw.subTokens != null)
			{
			for (DanaToken xw = pw.subTokens; xw != null; xw = xw.next)
				{
				if (i >= newFunction.parameters.arrayLength)
					{
					postError(status, xw.lineNumber, file, "implemented interface function has more parameters than type definition")
					return false
					}

				DanaToken param = xw.subTokens

				if (param.type != DanaToken.OPERATION || (param.token != "declare" && param.token != "declare_array"))
					{
					postError(status, param.lineNumber, file, "unexpected statement form in function definition (expected variable declaration, got '$(param.token)')")
					return false
					}
				
				//restrict parameters to be qualified by "store" or no qualifier
				if (param.exStr2 != "store" && param.exStr2 != "opt" && param.exStr2 != "opt,store" && param.exStr2 != null)
					{
					postError(status, param.lineNumber, file, "function parameters must be 'store', 'opt', or plain (found '$(param.exStr2)' for $(param.exStr1))")
					return false
					}

				DanaType ftype = getDeclareType(param, types)

				if (ftype == null)
					{
					postError(status, param.lineNumber, file, "type mismatch for parameter $(i) of function $(newFunction.name)() against interface type definition (expected $(newFunction.variables.fields[i+1].type))")
					return false
					}
				
				Parameter newParam = new Parameter(param.exStr1, 0, false, ftype)
				DanaTypeField ntf = new DanaTypeField(param.exStr1, ftype.name, param.exStr2)

				//override the parameter name, in case the implementation's name for this param is different
				newFunction.parameters[i].name = newParam.name
				newFunction.variables.fields[i+1].name = newParam.name

				if (ntf.qualifier != newFunction.variables.fields[i+1].qualifier)
					{
					postError(status, param.lineNumber, file, "qualifier mismatch for parameter $(i) of function $(newFunction.name)() against interface type definition (interface type specifies $(newFunction.variables.fields[i+1].qualifier))")
					return false
					}
				
				if (ntf.type != newFunction.variables.fields[i+1].type)
					{
					postError(status, param.lineNumber, file, "type mismatch for parameter $(i) of function $(newFunction.name)() against interface type definition (expected $(newFunction.variables.fields[i+1].type))")
					return false
					}

				i ++
				}
			}
		
		if (i < newFunction.parameters.arrayLength)
			{
			postError(status, pw.lineNumber, file, "implemented interface function has fewer parameters than type definition")
			return false
			}
		
		return true
		}
	
	bool addFunction(char file[], ProvidedObject pObject, DanaToken pw, DanaType types[], OpParseResult status)
		{
		if (pw.exStr2 != null)
			{
			InterfaceFunctions ifunctions = pObject.interfaces.findFirst(InterfaceFunctions.[type], new InterfaceFunctions(type = pw.exStr2))

			if (ifunctions == null)
				{
				postError(status, pw.lineNumber, file, "component does not provide interface of type '$(pw.exStr2)'")
				return false
				}
			
			//it's an interface member; find it in pObject and fill in its "tree" field
			OpFunctionDetail newFunction = ifunctions.functions.findFirst(OpFunction.[name], new OpFunction(name = pw.token))

			if (newFunction == null)
				{
				postError(status, pw.lineNumber, file, "interface of type '$(pw.exStr2)' has no function named '$(pw.token)'")
				return false
				}
			
			newFunction.fromFile = file
			newFunction.tree = pw.next

			//check function signature against the signature in the type definition
			DanaType actualReturnType = dncUtil.findType(types, pw.exStr1)

			if (actualReturnType !== newFunction.returnType)
				{
				postError(status, pw.lineNumber, file, "incorrect return type for interface function '$(pw.exStr2):$(pw.token)()' (expected $(newFunction.returnType.name), got $(actualReturnType.name))")
				return false
				}
			
			if (!checkParams(file, pw, newFunction, types, status))
				{
				return false
				}

			if (newFunction == null)
				{
				postError(status, pw.lineNumber, file, "functions cannot be declared outside of implementation scopes")
				return false
				}
			
			for (int i = 1; i < newFunction.variables.fields.arrayLength; i++)
				{
				if (globalVariableExists(status, pObject, newFunction.variables.fields[i].name))
					{
					postError(status, pw.lineNumber, file, "function parameter '$(newFunction.variables.fields[i].name)' shadows a global variable of the same name")
					}
				
				if (globalStaticVariableExists(status, pObject, newFunction.variables.fields[i].name))
					{
					postError(status, pw.lineNumber, file, "function parameter '$(newFunction.variables.fields[i].name)' shadows a static global variable of the same name")
					}
				}
			}
			else
			{
			//it's a local function, add it to pObject's local functions list
			OpFunctionDetail newFunction = new OpFunctionDetail(pw.token)
			newFunction.variables = new DanaType(DanaType.DATA)
			if (pw.exStr1 == "eventsink")
				{
				newFunction.returnType = dncUtil.findType(types, "void")
				newFunction.variables.fields = new DanaTypeField(null, "void")
				newFunction.ftype = OpFunction.EVENTSINK
				}
				else
				{
				newFunction.returnType = dncUtil.findType(types, pw.exStr1)
				newFunction.variables.fields = new DanaTypeField(null, pw.exStr1)
				}
			
			newFunction.fromFile = file

			populateParams(file, pw, newFunction, types, true, status)

			for (int i = 1; i < newFunction.variables.fields.arrayLength; i++)
				{
				if (globalVariableExists(status, pObject, newFunction.variables.fields[i].name))
					{
					postError(status, pw.lineNumber, file, "function parameter '$(newFunction.variables.fields[i].name)' shadows a global variable of the same name")
					}
				
				if (globalStaticVariableExists(status, pObject, newFunction.variables.fields[i].name))
					{
					postError(status, pw.lineNumber, file, "function parameter '$(newFunction.variables.fields[i].name)' shadows a static global variable of the same name")
					}
				}

			OpFunction kr = newFunction
		
			pObject.localFunctions = new OpFunction[](pObject.localFunctions, kr)

			newFunction.tree = pw.next
			}
		
		return true
		}
	
	void collectFunctions(char file[], DanaToken tree, DanaType types[], bool hasImpBlocks, OpParseResult status)
		{
		DanaToken pw = tree.subTokens

		while (pw != null)
			{
			if (pw.type == DanaToken.COMPONENT)
				{
				if (pw.next == null || pw.next.type != DanaToken.SCOPE)
					{
					postError(status, pw.lineNumber, file, "no component implementation found to compile (expected {})")
					return
					}
				
				//collect functions, including from implementation scopes
				ProvidedObject pObject = null

				if (status.providedObjects.arrayLength == 1)
					{
					pObject = status.providedObjects[0]
					}

				pw = pw.next.subTokens
				while (pw != null)
					{
					if (pw.type == DanaToken.FUNCTION_HEADER)
						{
						if (hasImpBlocks)
							{
							postError(status, pw.lineNumber, file, "functions cannot be declared outside of implementation scopes")
							return
							}
						
						addFunction(file, pObject, pw, types, status)
						}
						else if (pw.type == DanaToken.IMPLEMENTATION)
						{
						ProvidedObject options[] = status.providedObjects.find(ProvidedObject.[mainInterface], new ProvidedObject(pw.exStr1))

						if (options == null)
							{
							postError(status, pw.lineNumber, file, "no provided interface declared by the type name '$(pw.exStr1)'")
							return
							}

						if (pw.exStr2 != null)
							{
							pObject = options.findFirst(ProvidedObject.[semantic], new ProvidedObject(semantic = pw.exStr2))

							if (pObject == null)
								{
								postError(status, pw.lineNumber, file, "no provided interface declared by the type name '$(pw.exStr1):$(pw.exStr2)'")
								return
								}
							}
							else if (options.arrayLength != 1)
							{
							postError(status, pw.lineNumber, file, "multiple provided interfaces match the name '$(pw.exStr1)'")
							return
							}
							else
							{
							pObject = options[0]
							}

						DanaToken sw = pw.next.subTokens

						while (sw != null)
							{
							if (sw.type == DanaToken.FUNCTION_HEADER)
								{
								addFunction(file, pObject, sw, types, status)
								}
								else if (sw.type == DanaToken.IMPLEMENTATION)
								{
								postError(status, sw.lineNumber, file, "implementation scopes cannot be nested")
								return
								}

							sw = sw.next
							}
						}
					
					pw = pw.next
					}
				
				return
				}
			
			pw = pw.next
			}
		}
	
	void createSuperFunctions(char file[], DanaToken tree, DanaType types[], OpParseResult status)
		{
		//for any non-implemented functions of an interface's super-type, add a stub function with a super() call
		for (int i = 0; i < status.providedObjects.arrayLength; i++)
			{
			for (int j = 0; j < status.providedObjects[i].interfaces.arrayLength; j++)
				{
				for (int k = 0; k < status.providedObjects[i].interfaces[j].functions.arrayLength; k++)
					{
					OpFunctionDetail function = status.providedObjects[i].interfaces[j].functions[k]

					if (function.tree == null && function.inherited)
						{
						if (DEBUG_BASIC) out.println("[constructing inherited function implementation for function $(function.name)]")
						function.tree = new DanaToken(DanaToken.SCOPE)
						DanaToken callTok = new DanaToken(DanaToken.OPERATION, TokSubTypes.LOCAL_CALL, token = "super")
						callTok.subTokens = new DanaToken(DanaToken.EXPRESSION)
						DanaToken pTok = callTok.subTokens
						if (function.returnType.name == "void")
							{
							function.tree.subTokens = callTok
							}
							else
							{
							function.tree.subTokens = new DanaToken(DanaToken.OPERATION, TokSubTypes.RETURN, token = "return")
							function.tree.subTokens.subTokens = callTok
							}
						for (int q = 1; q < function.variables.fields.arrayLength; q++)
							{
							DanaToken ntok = new DanaToken(DanaToken.PARAM)
							ntok.subTokens = new DanaToken(DanaToken.VARIABLE, token = function.variables.fields[q].name)
							if (pTok.subTokensEnd == null)
								{
								pTok.subTokens = ntok
								}
								else
								{
								pTok.subTokensEnd.next = ntok
								}
							pTok.subTokensEnd = ntok
							}
						}
					}
				}
			}
		}
	
	byte[] fitByteArrayTo(byte ar[], int len, int lineNumber, OpParseResult status)
		{
		if (ar.arrayLength == len)
			{
			return ar
			}

		byte result[] = new byte[len]

		if (ar.arrayLength < len)
			{
			int j = len - ar.arrayLength
			for (int i = 0; i < ar.arrayLength; i++)
				{
				result[j] = ar[i]
				j ++
				}
			}
			else
			{
			return ar
			/*
			int st = ar.arrayLength - len
			int j = 0
			for (int i = 0; i < ar.arrayLength; i++)
				{
				if (i >= st)
					{
					result[j] = ar[i]
					j ++
					}
					else if (ar[i] != 0)
					{
					postError(status, lineNumber, "(core)", "precision loss in assignment")
					//throw new Exception("precision loss in assignment")
					}
				}
			*/
			}

		return result
		}
	
	int getDecimalScalingFactor(int forBitWidth)
		{
		int halfWidth = (forBitWidth/8) / 2

		if (halfWidth == 1)
			{
			ConInt1 con = new ConInt1()
			con.value --
			char str[] = con.value.makeString()
			int result = str.arrayLength - 1
			return result
			}
			else if (halfWidth == 2)
			{
			ConInt2 con = new ConInt2()
			con.value --
			char str[] = con.value.makeString()
			int result = str.arrayLength - 1
			return result
			}
			else if (halfWidth == 4)
			{
			ConInt4 con = new ConInt4()
			con.value --
			char str[] = con.value.makeString()
			int result = str.arrayLength - 1
			return result
			}
			else if (halfWidth == 8)
			{
			ConInt8 con = new ConInt8()
			con.value --
			char str[] = con.value.makeString()
			int result = str.arrayLength - 1
			return result
			}
			else if (halfWidth == 16)
			{
			ConInt16 con = new ConInt16()
			con.value --
			char str[] = con.value.makeString()
			int result = str.arrayLength - 1
			return result
			}
			else if (halfWidth == 32)
			{
			ConInt32 con = new ConInt32()
			con.value --
			char str[] = con.value.makeString()
			int result = str.arrayLength - 1
			return result
			}
			else if (halfWidth == 64)
			{
			ConInt64 con = new ConInt64()
			con.value --
			char str[] = con.value.makeString()
			int result = str.arrayLength - 1
			return result
			}
		
		return 0
		}
	
	bool canScaleFor(char integer[], char fraction[], int bitWidth)
		{
		int factor = getDecimalScalingFactor(bitWidth)
		
		if (fraction.arrayLength <= factor)
			{
			return true
			}
		
		return false
		}

	char[] scaleFor(char integer[], char fraction[], int bitWidth)
		{
		int factor = getDecimalScalingFactor(bitWidth)
		char nf[] = clone fraction
		
		while (nf.arrayLength < factor)
			{
			nf = new char[](nf, "0")
			}
		
		char result[] = new char[](integer, nf)
		if (DEBUG_LITERAL_FRACTIONS) out.println("converted fraction: $result")
		return result
		}
	
	bool isZero(byte array[])
		{
		for (int i = 0; i < array.arrayLength; i++)
			{
			if (array[i] != 0) return false
			}
		
		return true
		}

	bool fitsBitWidth(char scaledFraction[], int bitWidth)
		{
		//we put it in an integer twice the size and see if the first half of those bytes are all zero
		int512 ival = iu512.int512FromString(scaledFraction)
		ConInt512 con = new ConInt512(ival)
		byte bytes[] = dana.serial(con)
		byte sub[] = dana.sub(bytes, bytes.arrayLength - 1 - ((bitWidth*2)/8), bytes.arrayLength - 1 - (bitWidth/8))

		return isZero(sub)
		/*
		int testBytes = (bitWidth*2)/8
		byte *integer = stringToInt(scaledFraction, testBytes);
		
		if (isZero(integer, testBytes / 2))
			{
			free(integer);
			return true;
			}
		
		free(integer);
		return false;
		*/
		}
	
	void incrementInt(byte bytes[])
		{
		if (bytes.arrayLength == 512)
			{
			ConInt512 con = new ConInt512()
			dana.serial(con) =[] bytes
			con.value ++
			bytes =[] dana.serial(con)
			}
			else if (bytes.arrayLength == 256)
			{
			ConInt256 con = new ConInt256()
			dana.serial(con) =[] bytes
			con.value ++
			bytes =[] dana.serial(con)
			}
			else if (bytes.arrayLength == 128)
			{
			ConInt128 con = new ConInt128()
			dana.serial(con) =[] bytes
			con.value ++
			bytes =[] dana.serial(con)
			}
			else if (bytes.arrayLength == 64)
			{
			ConInt64 con = new ConInt64()
			dana.serial(con) =[] bytes
			con.value ++
			bytes =[] dana.serial(con)
			}
			else if (bytes.arrayLength == 32)
			{
			ConInt32 con = new ConInt32()
			dana.serial(con) =[] bytes
			con.value ++
			bytes =[] dana.serial(con)
			}
			else if (bytes.arrayLength == 16)
			{
			ConInt16 con = new ConInt16()
			dana.serial(con) =[] bytes
			con.value ++
			bytes =[] dana.serial(con)
			}
			else if (bytes.arrayLength == 8)
			{
			ConInt8 con = new ConInt8()
			dana.serial(con) =[] bytes
			con.value ++
			bytes =[] dana.serial(con)
			}
			else if (bytes.arrayLength == 4)
			{
			ConInt4 con = new ConInt4()
			dana.serial(con) =[] bytes
			con.value ++
			bytes =[] dana.serial(con)
			}
			else if (bytes.arrayLength == 2)
			{
			ConInt2 con = new ConInt2()
			dana.serial(con) =[] bytes
			con.value ++
			bytes =[] dana.serial(con)
			}
		}
	
	void complement2(byte conBytesX[])
		{
		for (int i = 0; i < conBytesX.arrayLength; i++)
			{
			conBytesX[i] = ~conBytesX[i]
			}
		
		incrementInt(conBytesX)
		}
	
	DanaType getDecTypeFor(DanaType types[], int storageSize)
		{
		for (int i = 0; i < types.arrayLength; i++)
			{
			if (types[i].class == DanaType.DECIMAL && types[i].storageSize == storageSize)
				return types[i]
			}
		
		return null
		}
	
	DanaType getIntTypeFor(DanaType types[], int storageSize)
		{
		for (int i = 0; i < types.arrayLength; i++)
			{
			if (types[i].class == DanaType.INTEGER && types[i].storageSize == storageSize)
				return types[i]
			}
		
		return null
		}
	
	byte[] serialInt(int512 value, int storageSize)
		{
		if (storageSize == 2)
			{
			ConInt2 con = new ConInt2(value)
			return dana.serial(con)
			}
			else if (storageSize == 4)
			{
			ConInt4 con = new ConInt4(value)
			return dana.serial(con)
			}
			else if (storageSize == 8)
			{
			ConInt8 con = new ConInt8(value)
			return dana.serial(con)
			}
			else if (storageSize == 16)
			{
			ConInt16 con = new ConInt16(value)
			return dana.serial(con)
			}
			else if (storageSize == 32)
			{
			ConInt32 con = new ConInt32(value)
			return dana.serial(con)
			}
			else if (storageSize == 64)
			{
			ConInt64 con = new ConInt64(value)
			return dana.serial(con)
			}
		
		return null
		}
	
	byte[] parseDec(char integer[], char fraction[], bool neg, OpToken newOp, DanaType xrType, DanaType types[])
		{
		byte result[] = null

		int bitWidth = xrType.storageSize * 8
		if (canScaleFor(integer, fraction, bitWidth))
			{
			char ik[] = scaleFor(integer, fraction, bitWidth)
			if (fitsBitWidth(ik, bitWidth))
				{
				int512 integerD = iu512.int512FromString(ik)
				newOp.returnType = xrType
				result = serialInt(integerD, xrType.storageSize)
				if (neg) complement2(result)
				}
			}
			else
			{
			//scale up to the first bitwidth that's viable
			bitWidth = bitWidth * 2
			while (true)
				{
				if (canScaleFor(integer, fraction, bitWidth))
					{
					char ik[] = scaleFor(integer, fraction, bitWidth)
					if (fitsBitWidth(ik, bitWidth))
						{
						int512 integerD = iu512.int512FromString(ik)
						newOp.returnType = getDecTypeFor(types, bitWidth/8)
						result = serialInt(integerD, bitWidth/8)
						if (neg) complement2(result)
						return result
						}
					}

				bitWidth = bitWidth * 2
				}
			}
		
		return result
		}
	
	byte[] parseLiteral(char file[], DanaToken t, OpToken newOp, DanaType xrType, DanaType types[], OpParseResult status)
		{
		byte result[] = null

		if (t.subType == TokSubTypes.L_INT)
			{
			if (xrType != null && xrType.class != DanaType.INTEGER)
				{
				xrType = null
				}
			
			//assume host-size for now...(this should be modified by the expected return type!)
			if (!t.token.startsWith("0x"))
				{
				//int value = t.token.intFromString()
				ConInt512 conMax = new ConInt512(iu512.int512FromString(t.token))

				if (xrType == null)
					{
					xrType = dncUtil.findType(types, "int")
					}
				
				//here we need to check that the integer is actually going to fit in its xrType (e.g. the number 300 won't fit into an int1)
				// - if we parse the int into an int512, we can serialise the int512 into bytes, and count the # of non-zero bytes from the right-hand-side
				byte schk[] = dana.serial(conMax)
				int sizeReq = 0
				for (int i = 0; i < schk.arrayLength; i++)
					{
					if (schk[i] != 0)
						{
						sizeReq = (schk.arrayLength - i)
						break
						}
					}
				
				if (sizeReq > xrType.storageSize)
					{
					int nextSize = xrType.storageSize
					while (sizeReq > nextSize)
						{
						nextSize = nextSize * 2
						}
					
					xrType = dncUtil.findType(types, "int$nextSize")
					}

				if (xrType.storageSize == 32)
					{
					ConInt32 con = new ConInt32(conMax.value)
					result = dana.serial(con)
					newOp.returnType = xrType
					}
					else if (xrType.storageSize == 16)
					{
					ConInt16 con = new ConInt16(conMax.value)
					result = dana.serial(con)
					newOp.returnType = xrType
					}
					else if (xrType.storageSize == 8)
					{
					ConInt8 con = new ConInt8(conMax.value)
					result = dana.serial(con)
					newOp.returnType = xrType
					}
					else if (xrType.storageSize == 4)
					{
					ConInt4 con = new ConInt4(conMax.value)
					result = dana.serial(con)
					newOp.returnType = xrType
					}
					else if (xrType.storageSize == 2)
					{
					ConInt2 con = new ConInt2(conMax.value)
					result = dana.serial(con)
					newOp.returnType = xrType
					}
					else if (xrType.storageSize == 1)
					{
					ConInt1 con = new ConInt1(conMax.value)
					result = dana.serial(con)
					newOp.returnType = xrType
					}
					else
					{
					out.println(" -- unknown XRT size $(xrType.class)/$(xrType.storageSize) on line $(t.lineNumber)")
					}
				}
				else
				{
				//hex-formatted integer literals
				byte value[] = bu.fromHexString(t.token.lsplit("x")[1].string)

				if (xrType == null)
					{
					xrType = dncUtil.findType(types, "int")
					}
				
				result = fitByteArrayTo(value, xrType.storageSize, t.lineNumber, status)
				newOp.returnType = xrType
				}
			}
			else if (t.subType == TokSubTypes.L_DEC)
			{
			char lstr[] = t.token
			bool neg = lstr[0] == "-"
			if (neg) lstr = dana.sub(lstr, 1, lstr.arrayLength - 1)
			String parts[] = lstr.lsplit(".")
			char integer[] = parts[0].string
			char fraction[] = parts[1].string

			if (xrType != null && xrType.class != DanaType.DECIMAL)
				{
				xrType = null
				}

			if (xrType == null)
				{
				if (addressWidth == 8)
					xrType = dncUtil.findType(types, "dec8")
					else if (addressWidth == 4)
					xrType = dncUtil.findType(types, "dec4")
				
				result = parseDec(integer, fraction, neg, newOp, xrType, types)
				}
				else
				{
				result = parseDec(integer, fraction, neg, newOp, xrType, types)
				}
			}
			else if (t.subType == TokSubTypes.L_STRING)
			{
			result = t.token
			if (xrType == null || xrType.class != DanaType.ARRAY || xrType.storageSize == 0 || xrType.storageSize == t.token.arrayLength)
				{
				if (t.token.arrayLength == 1)
					{
					newOp.returnType = dncUtil.findType(types, "char")
					}
					else
					{
					newOp.returnType = dncUtil.findType(types, "char[$(t.token.arrayLength)]")
					}
				}
				else
				{
				//this is mostly handling the edge-case where a fixed-size array has been declared, but the assign-into thing is shorter than it...
				if (t.token.arrayLength == 1)
					{
					newOp.returnType = dncUtil.findType(types, "char")
					}
					else
					{
					newOp.returnType = xrType
					result = new char[xrType.storageSize]
					result =[] t.token
					}
				}
			}
			else if (t.type == DanaToken.OPERATION && t.subType == TokSubTypes.NEW_ARRAY_CONSTRUCT)
			{
			DanaToken cw = t.subTokens.next.subTokens
			DanaType fieldType = types.findFirst(DanaType.[name], new DanaType(name = xrType.fields[0].type))
			char pTypeName[] = fieldType.name
			byte totalValue[] = null

			int count = 0

			while (cw != null)
				{
				OpToken newToken = new OpToken()
				byte value[] = parseLiteral(file, cw.subTokens, newToken, fieldType, types, status)

				totalValue = new byte[](totalValue, value)

				count ++
				cw = cw.next
				}
			
			newOp.returnType = types.findFirst(DanaType.[name], new DanaType(name = "$(pTypeName)[$count]"))

			return totalValue
			}
			else if (t.type == DanaToken.VARIABLE)
			{
			//this must be a simple constant, or else mate
			for (Constant cw = constants.getFirst(); cw != null; cw = constants.getNext())
				{
				if (cw.name == t.token)
					{
					newOp.returnType = cw.type
					return cw.value
					}
				}
			
			postError(status, t.lineNumber, file, "unknown constant value '$(t.token)'")
			}
		
		return result
		}
	
	//an "SR" is a generated local variable, used for the return value of an instruction (such as "equals", which produces a boolean result value)
	void addSR(OpFunction function, OpToken op, char type[], DanaType t)
		{
		op.parameters = new OpToken(type = HWI.OP_ID_GET_PTR,
											variableIndex = function.variables.fields.arrayLength,
											returnType = t,
											lineNumber = op.lineNumber,
											parameters = new OpToken[](new OpToken(type = HWI.OP_ID_GET_PR), new OpToken(type = HWI.OP_ID_LOCALS)))
		
		function.variables.fields = new DanaTypeField[](function.variables.fields, new DanaTypeField(":sr$(function.variables.fields.arrayLength)", type))
		function.variableInfo = new Variable[](function.variableInfo, new Variable())
		}
	
	int insertSR(OpFunction function, OpToken op, char type[], DanaType t)
		{
		int newIndex = function.variables.fields.arrayLength
		op.parameters = new OpToken[](new OpToken(type = HWI.OP_ID_GET_PTR,
											variableIndex = newIndex,
											returnType = t,
											lineNumber = op.lineNumber,
											parameters = new OpToken[](new OpToken(type = HWI.OP_ID_GET_PR), new OpToken(type = HWI.OP_ID_LOCALS))), op.parameters)
		
		function.variables.fields = new DanaTypeField[](function.variables.fields, new DanaTypeField(":sr$(newIndex)", type))
		function.variableInfo = new Variable[](function.variableInfo, new Variable())

		return newIndex
		}
	
	void insertVariableInit(OpFunction function, List instructions, DanaType types[], int index, char type[], char value[], OpToken after, OpParseResult status)
		{
		DanaType boolType = dncUtil.findType(types, type)
		OpToken newOp = new OpToken(HWI.OP_ID_ASSIGN, parameters = new OpToken[](new OpToken(type = HWI.OP_ID_GET_PTR,
											variableIndex = index,
											returnType = boolType,
											parameters = new OpToken[](new OpToken(type = HWI.OP_ID_GET_PR), new OpToken(type = HWI.OP_ID_LOCALS))),
											new OpToken(type = HWI.OP_ID_GET_LITERAL_PTR, refValue = parseLiteral("(core)", new DanaToken(type = DanaToken.LITERAL, subType = TokSubTypes.L_INT, token = value), new OpToken(), boolType, types, status), returnType = boolType)
											))
		
		if (after != null)
			{
			
			OpToken nxi[] = new OpToken[function.instructions.arrayLength+1]
			int j = 0
			for (int i = 0; i < function.instructions.arrayLength; i++)
				{
				nxi[j] = function.instructions[i]
				j ++
				if (function.instructions[i] === after)
					{
					nxi[j] = newOp
					j ++
					}
				}
			function.instructions = nxi
			
			//instructions.insert(newOp, after = after)
			}
			else
			{
			function.instructions = new OpToken[](newOp, function.instructions)
			//instructions.insert(newOp)
			}
		}
	
	void addVariableInit(OpFunction function, List instructions, DanaType types[], int index, char type[], char value[], OpParseResult status)
		{
		DanaType varType = dncUtil.findType(types, type)
		OpToken newOp = new OpToken(HWI.OP_ID_ASSIGN, parameters = new OpToken[](new OpToken(type = HWI.OP_ID_GET_PTR,
											variableIndex = index,
											returnType = varType,
											parameters = new OpToken[](new OpToken(type = HWI.OP_ID_GET_PR), new OpToken(type = HWI.OP_ID_LOCALS))),
											new OpToken(type = HWI.OP_ID_GET_LITERAL_PTR, refValue = parseLiteral("(core)", new DanaToken(type = DanaToken.LITERAL, subType = TokSubTypes.L_INT, token = value), new OpToken(), varType, types, status), returnType = varType)
											))
		
		function.instructions = new OpToken[](function.instructions, newOp)
		//instructions.add(newOp)
		}
	
	void insertPR(OpFunction function, OpToken op)
		{
		op.parameters = new OpToken[](new OpToken(type = HWI.OP_ID_GET_PR), op.parameters)
		}
	
	FunctionRef findFunctionRef(ProvidedObject pObject, char name[])
		{
		for (int j = 0; j < pObject.interfaces.arrayLength; j++)
			{
			for (int k = 0; k < pObject.interfaces[j].functions.arrayLength; k++)
				{
				if (pObject.interfaces[j].functions[k].name == name)
					{
					return new FunctionRef(pObject.interfaces[j].functions[k].name, pObject.interfaces[j].type)
					}
				}
			}

		for (int j = 0; j < pObject.localFunctions.arrayLength; j++)
			{
			if (pObject.localFunctions[j].name == name)
				{
				return new FunctionRef(pObject.localFunctions[j].name)
				}
			}

		return null
		}
	
	OpFunction findFunctionOp(ProvidedObject pObject, char name[], char intf[])
		{
		for (int j = 0; j < pObject.interfaces.arrayLength; j++)
			{
			for (int k = 0; k < pObject.interfaces[j].functions.arrayLength; k++)
				{
				if (pObject.interfaces[j].functions[k].name == name)
					{
					return pObject.interfaces[j].functions[k]
					}
				}
			}

		for (int j = 0; j < pObject.localFunctions.arrayLength; j++)
			{
			if (pObject.localFunctions[j].name == name)
				{
				return pObject.localFunctions[j]
				}
			}

		return null
		}
	
	bool hasConstuctor(DanaType t)
		{
		for (int i = 0; i < t.fields.arrayLength; i ++)
			{
			if (t.fields[i].name == t.name)
				{
				return true
				}
			}
		
		return false
		}
	
	int getFunctionIndex(DanaType t, char name[], DanaType types[])
		{
		int result = 0

		for (int i = 0; i < t.fields.arrayLength; i++)
			{
			DanaType fieldType = types.findFirst(DanaType.[name], new DanaType(name = t.fields[i].type))
			if (fieldType.class == DanaType.FUNCTION)
				{
				if (t.fields[i].name == name)
					return result
				
				result ++
				}
			}
		
		return INT_MAX
		}
	
	DanaTypeField getFunctionField(DanaType t, char name[], DanaType types[])
		{
		int result = 0

		for (int i = 0; i < t.fields.arrayLength; i++)
			{
			DanaType fieldType = dncUtil.findType(types, t.fields[i].type)
			if (fieldType.class == DanaType.FUNCTION)
				{
				if (t.fields[i].name == name)
					return t.fields[i]
				}
			}
		
		return null
		}
	
	int getEventIndex(DanaType t, char name[], DanaType types[])
		{
		int result = 0

		for (int i = 0; i < t.fields.arrayLength; i++)
			{
			DanaType fieldType = dncUtil.findType(types, t.fields[i].type)
			if (fieldType.class == DanaType.EVENT)
				{
				if (t.fields[i].name == name)
					return result
				
				result ++
				}
			}
		
		throw new Exception("event named $(name) not found in type $(t.name)")
		}
	
	DanaType getEventParamType(DanaType t, char name[], DanaType types[])
		{
		int result = 0

		for (int i = 0; i < t.fields.arrayLength; i++)
			{
			DanaType fieldType = dncUtil.findType(types, t.fields[i].type)
			if (fieldType.class == DanaType.EVENT)
				{
				if (t.fields[i].name == name)
					{
					if (fieldType.fields.arrayLength > 1)
						{
						return dncUtil.findType(types, fieldType.fields[1].type)
						}
						else
						{
						return null
						}
					}
				
				result ++
				}
			}
		
		throw new Exception("event named $(name) not found in type $(t.name)")
		}
	
	int getEventsinkIndex(char name[], ProvidedObject pObject)
		{
		int result = 0

		for (int i = 0; i < pObject.localFunctions.arrayLength; i++)
			{
			if (pObject.localFunctions[i].ftype == OpFunction.EVENTSINK)
				{
				if (pObject.localFunctions[i].name == name)
					return result
				
				result ++
				}
			}
		
		throw new Exception("eventsink named $(name) not found in implementation")
		}
	
	int getRequiredInterfaceIndex(char name[], char semantic[], OpParseResult status)
		{
		for (int i = 0; i < status.requiredInterfaces.arrayLength; i++)
			{
			if (status.requiredInterfaces[i].name == name && status.requiredInterfaces[i].semantic == semantic)
				{
				return i
				}
			}
		
		return INT_MAX
		}
	
	void printTree(DanaToken tokens)
		{
		for (DanaToken nxt = tokens; nxt != null; nxt = nxt.next)
			{
			out.print("<$(nxt.type)>$(nxt.token)")
			
			if (nxt.type == DanaToken.EXPRESSION) out.print("(")
			if (nxt.type == DanaToken.SQ_EXPRESSION) out.print("[")
			if (nxt.type == DanaToken.SCOPE) out.print("{")
			
			if (nxt.subTokens != null)
				{
				out.print(" <( ")
				printTree(nxt.subTokens)
				out.print(" )> ")
				if (nxt.type == DanaToken.PARAM && nxt.next != null) out.print(",")
				}
			
			if (nxt.type == DanaToken.EXPRESSION) out.print(")")
			if (nxt.type == DanaToken.SQ_EXPRESSION) out.print("]")
			if (nxt.type == DanaToken.SCOPE) out.print("}")
			}
		}
	
	DanaToken dropParams(DanaToken tok)
		{
		DanaToken lastSub = null
		DanaToken sub = null
		DanaToken sw = tok.subTokens.subTokens
		while (sw != null)
			{
			DanaToken nxts = clone sw.subTokens
			if (sub == null)
				sub = nxts
				else
				lastSub.next = nxts
			lastSub = nxts
			sw = sw.next
			}
		
		return sub
		}
	
	DanaType getType(DanaType types[], char name[])
		{
		return dncUtil.findType(types, name)
		}
	
	bool typeMayIncludeObjects(DanaType t, DanaType types[])
		{
		if (t.class == DanaType.INTERFACE)
			{
			return true
			}
			else if (t.class == DanaType.DATA)
			{
			return true
			}
			else if (t.class == DanaType.ARRAY)
			{
			DanaType fieldType = dncUtil.findType(types, t.fields[0].type)

			if (fieldType.class == DanaType.INTEGER || fieldType.class == DanaType.DECIMAL)
				{
				return false
				}
			
			return true
			}

		return false
		}
	
	ParamInfo getObjectCallParamInfo(DanaType t, char name[], DanaType types[])
		{
		for (int i = 0; i < t.fields.arrayLength; i++)
			{
			DanaType fieldType = dncUtil.findType(types, t.fields[i].type)
			if (fieldType.class == DanaType.FUNCTION)
				{
				if (t.fields[i].name == name)
					{
					ParamInfo result = new ParamInfo()

					bool optLatch = false

					for (int j = 1; j < fieldType.fields.arrayLength; j++)
						{
						if (optLatch || fieldType.fields[j].qualifier.startsWith("opt"))
							{
							result.optParams = new String[](result.optParams, new String(fieldType.fields[j].name))
							optLatch = true
							}
							else
							{
							result.coreParams ++
							}
						}

					return result
					}
				}
			}
		
		return null
		}
	
	bool isObjectCallParamStore(DanaType t, int functionIndex, int paramIndex, DanaType types[])
		{
		int j = 0
		for (int i = 0; i < t.fields.arrayLength; i++)
			{
			DanaType fieldType = dncUtil.findType(types, t.fields[i].type)
			if (fieldType.class == DanaType.FUNCTION)
				{
				if (j == functionIndex)
					{
					if (fieldType.fields[paramIndex+1].qualifier.endsWith("store"))
						{
						return true
						}
						else
						{
						return false
						}
					}
				
				j ++
				}
			}
		
		return false
		}
	
	ParamInfo getLocalCallParamInfo(ProvidedObject pObject, char name[], DanaType types[])
		{
		FunctionRef functionRef = findFunctionRef(pObject, name)

		if (functionRef == null) return null

		//resolve functionRef to its OpFunction, then construct a functionType for it, including parameters and return type, and add to prepParamStack
		OpFunction opf = findFunctionOp(pObject, functionRef.name, functionRef.intfName)
		DanaType functionType = makeFunctionDanaType(opf, types)

		ParamInfo result = new ParamInfo()

		bool optLatch = false

		for (int j = 1; j < functionType.fields.arrayLength; j++)
			{
			if (optLatch || functionType.fields[j].qualifier.startsWith("opt"))
				{
				result.optParams = new String[](result.optParams, new String(functionType.fields[j].name))
				optLatch = true
				}
				else
				{
				result.coreParams ++
				}
			}

		return result
		}
	
	bool checkOpParamCount(DanaToken list, int formal)
		{
		int actual = 0
		while (list != null)
			{
			actual ++
			list = list.next
			}
		
		if (actual != formal) return false

		return true
		}
	
	ApplyOpResult applyOperation(char file[], OpFunction function, bool global, ProvidedObject pObject, List scopes, DanaToken tok, OpToken parent, int paramIndex, DanaType types[], Stack prepParamStack, OpParseResultX status)
		{
		DanaType xrType = null
		if (parent != null)
			{
			if (parent.type == HWI.OP_ID_GET_INDEX_P || parent.type == HWI.OP_ID_GET_INDEX_R || parent.type == HWI.OP_ID_GET_INDEX_TO_ASSIGN || parent.type == HWI.OP_ID_VGET_INDEX)
				xrType = dncUtil.findType(types, "int")
				else if (parent.type == HWI.OP_ID_NEW_ARRAY)
				xrType = dncUtil.findType(types, "int")
				else if (parent.type == HWI.OP_ID_NEW_ARRAY_FROM)
				xrType = dncUtil.findType(types, "int")
				else if (parent.type == HWI.OP_ID_GET_FIELD)
				xrType = dncUtil.findType(types, "int")
				else if (parent.type == HWI.OP_ID_SUB_ARRAY)
				xrType = dncUtil.findType(types, "int")
				else if (parent.type == HWI.OP_ID_NEW_ARRAY_INIT || parent.type == HWI.OP_ID_NEW_ARRAY_APPEND)
				{
				if (parent.returnType == null)
					{
					out.println("null parent returnType line $(parent.lineNumber)")
					}
				xrType = dncUtil.findType(types, parent.returnType.fields[0].type)
				}
				else if (parent.type == HWI.OP_ID_EQUAL && paramIndex == 1)
				xrType = parent.parameters[0].returnType
				else if (parent.type == HWI.OP_ID_GREATER_THAN && paramIndex == 1)
				xrType = parent.parameters[1].returnType
				else if (parent.type == HWI.OP_ID_GREATER_THAN_EQ && paramIndex == 1)
				xrType = parent.parameters[0].returnType
				//the logical bitwise operators work most intuitively if the two things they're operating on have the same width; if they don't, they treat both values starting from their left-most-bit, which has unexpected results for two different integer sizes
				else if (parent.type == HWI.OP_ID_AND && paramIndex == 1)
				xrType = parent.parameters[0].returnType
				else if (parent.type == HWI.OP_ID_OR && paramIndex == 1)
				xrType = parent.parameters[0].returnType
				else if (parent.type == HWI.OP_ID_BNOT && paramIndex == 1)
				xrType = parent.parameters[0].returnType
				else if (parent.type == HWI.OP_ID_XOR && paramIndex == 1)
				xrType = parent.parameters[0].returnType
				else
				xrType = parent.returnType
			}

		ApplyOpResult result = new ApplyOpResult(subTokens = tok.subTokens)

		OpToken op = new OpToken()
		result.instruction = op
		result.asparent = op

		int tokType = tok.type
		int tokSubType = tok.subType
		
		//convert this token to its instruction
		if (tokType == DanaToken.OPERATION)
			{
			if (tokSubType == TokSubTypes.ASSIGN)
				{
				//(note the specific assign variant is only detectable, via updateOperation(), after the return type of the first parameter is known)
				op.type = HWI.OP_ID_ASSIGN
				}
				else if (tokSubType == TokSubTypes.DECLARE || tokSubType == TokSubTypes.DECLARE_ARRAY)
				{
				//check for an already-declared variable by this name
				if (localVariableExists(function, scopes, tok.exStr1))
					{
					postError(status, tok.lineNumber, file, "local variable '$(tok.exStr1)' has already been declared in this scope")
					}
				
				if (globalVariableExists(status, pObject, tok.exStr1))
					{
					postError(status, tok.lineNumber, file, "local variable '$(tok.exStr1)' shadows a global variable of the same name")
					}
				
				if (globalStaticVariableExists(status, pObject, tok.exStr1))
					{
					postError(status, tok.lineNumber, file, "local variable '$(tok.exStr1)' shadows a static global variable of the same name")
					}

				//add to scopes, and return as a variable-access instruction if we have a parent
				//char typeName[] = tok.subTokens.token
				DanaType declareType = getDeclareType(tok, types)
				ScopeVariable sv = new ScopeVariable(tok.exStr1, function.variables.fields.arrayLength)
				Scope cscope = scopes.getIndex(scopes.getLength()-1)
				cscope.variables = new ScopeVariable[](cscope.variables, sv)
				function.variables.fields = new DanaTypeField[](function.variables.fields, new DanaTypeField(tok.exStr1, declareType.name))
				function.variableInfo = new Variable[](function.variableInfo, new Variable())

				if (parent != null)
					{
					op.returnType = declareType
					op.variableIndex = sv.index

					if (isRefType(op.returnType))
						{
						if (paramIndex == 0 && parent.type == HWI.OP_ID_ASSIGN)
							op.type = HWI.OP_ID_GET_PTR_HND
							else
							op.type = HWI.OP_ID_GET_REF
						}
						else
						{
						op.type = HWI.OP_ID_GET_PTR
						}

					op.parameters = new OpToken[](new OpToken(type = HWI.OP_ID_GET_PR), new OpToken(type = HWI.OP_ID_LOCALS))
					}
				
				//declare has sub-tokens from the parse stage, but they're not executable ones, so we blank our subtokens
				result.subTokens = null
				}
				else if (tokSubType == TokSubTypes.NEW_ARRAY_CONSTRUCT
						|| tokSubType == TokSubTypes.NEW_ARRAY_CONSTRUCT_SE)
				{
				op.type = HWI.OP_ID_ELEMENT_FINISH

				if (tok.subTokens.next.type == DanaToken.PARAM)
					{
					op.returnType = dncUtil.findType(types, "$(tok.exStr1)[]")

					//one-dimensional array
					DanaToken sw = tok.subTokens.next.subTokens
					
					DanaToken lastSub = null
					
					//test tok.subTokens, which is the [] expression, to ensure it's empty
					if (tokSubType == TokSubTypes.NEW_ARRAY_CONSTRUCT && tok.subTokens.subTokens != null)
						{
						postError(status, tok.lineNumber, file, "value-constructed array cannot also specify an array size")
						status.terminalErrorLine = true
						return result
						}

					if (sw == null)
						{
						op.type = HWI.OP_ID_NEW_ARRAY
						op.refType = op.returnType
						insertPR(function, op)

						result.subTokens = new DanaToken(DanaToken.LITERAL, TokSubTypes.L_INT, token = "0")
						}
						else
						{
						int index = 0
						while (sw != null)
							{
							DanaToken sub = new DanaToken()
							sub.type = DanaToken.OPERATION
							sub.exStr1 = tok.exStr1
							sub.subTokens = sw.subTokens
							sub.lineNumber = sw.lineNumber

							if (index == 0)
								{
								if (tok.subType == TokSubTypes.NEW_ARRAY_CONSTRUCT)
									sub.subType = TokSubTypes.NEW_ARRAY_INIT
									else
									sub.subType = TokSubTypes.NEW_ARRAY_INIT_SE
								result.subTokens = sub
								}
								else
								{
								if (tok.subType == TokSubTypes.NEW_ARRAY_CONSTRUCT)
									sub.subType = TokSubTypes.NEW_ARRAY_APPEND
									else
									sub.subType = TokSubTypes.NEW_ARRAY_APPEND_SE
								lastSub.next = sub
								}
							
							lastSub = sub

							sw = sw.next
							index ++
							}
						}
					}
					else
					{
					//multi-dimensional array
					char returnType[] = tok.exStr1
					char prevRT[] = returnType
					DanaToken stw = tok.subTokens
					while (stw != null && stw.type != DanaToken.PARAM)
						{
						if (stw.subTokens != null)
							{
							postError(status, tok.lineNumber, file, "value-constructed array cannot also specify an array size")
							status.terminalErrorLine = true
							return result
							}
						
						prevRT = returnType
						returnType = "$(returnType)[]"
						stw = stw.next
						}

					op.returnType = dncUtil.findType(types, returnType)
					
					DanaToken sw = stw.subTokens
					
					DanaToken lastSub = null
					
					int index = 0
					while (sw != null)
						{
						DanaToken sub = new DanaToken()
						sub.type = DanaToken.OPERATION
						sub.subTokens = sw.subTokens
						sub.lineNumber = sw.lineNumber

						if (index == 0)
							{
							sub.subType = TokSubTypes.NEW_ARRAY_MD_INIT
							sub.exStr1 = returnType
							result.subTokens = sub
							}
							else
							{
							sub.subType = TokSubTypes.NEW_ARRAY_APPEND
							sub.exStr1 = prevRT
							lastSub.next = sub
							}
						
						lastSub = sub

						sw = sw.next
						index ++
						}
					}
				}
				else if (tokSubType == TokSubTypes.NEW_ARRAY_CONSTRUCT_FROM)
				{
				DanaToken lhs = tok.subTokens
				DanaToken rhs = tok.subTokens.next

				op.type = HWI.OP_ID_ELEMENT_FINISH
				op.returnType = dncUtil.findType(types, "$(lhs.exStr1)[]")

				DanaToken sw = lhs.subTokens.next.subTokens
				
				DanaToken lastSub = null
				
				//TODO: test tok.subTokens, which is the [] expression, to ensure it's empty

				int index = 0
				while (sw != null)
					{
					DanaToken sub = new DanaToken()
					sub.type = DanaToken.OPERATION
					sub.exStr1 = lhs.exStr1
					sub.subTokens = clone sw.subTokens
					sub.lineNumber = tok.lineNumber

					if (index == 0)
						{
						sub.subType = TokSubTypes.NEW_ARRAY_INIT_FROM
						sub.subTokens.next = rhs
						result.subTokens = sub
						}
						else
						{
						sub.subType = TokSubTypes.NEW_ARRAY_APPEND
						lastSub.next = sub
						}
					
					lastSub = sub

					sw = sw.next
					index ++
					}
				}
				else if (tokSubType == TokSubTypes.NEW_ARRAY_INIT)
				{
				op.type = HWI.OP_ID_NEW_ARRAY_INIT
				op.returnType = dncUtil.findType(types, "$(tok.exStr1)[]")
				op.refType = op.returnType
				insertPR(function, op)
				}
				else if (tokSubType == TokSubTypes.NEW_ARRAY_MD_INIT)
				{
				op.type = HWI.OP_ID_NEW_ARRAY_MD_INIT
				op.returnType = dncUtil.findType(types, tok.exStr1)
				op.refType = op.returnType
				insertPR(function, op)
				}
				else if (tokSubType == TokSubTypes.NEW_ARRAY_INIT_FROM)
				{
				op.type = HWI.OP_ID_NEW_ARRAY_INIT_FROM
				op.returnType = dncUtil.findType(types, "$(tok.exStr1)[]")
				op.refType = op.returnType
				insertPR(function, op)
				}
				else if (tokSubType == TokSubTypes.NEW_ARRAY_APPEND)
				{
				op.type = HWI.OP_ID_NEW_ARRAY_APPEND
				op.returnType = dncUtil.findType(types, "$(tok.exStr1)[]")
				op.refType = op.returnType
				insertPR(function, op)
				}
				else if (tokSubType == TokSubTypes.NEW_ARRAY_INIT_SE)
				{
				op.type = HWI.OP_ID_NEW_ARRAY_INIT
				op.subType = 1
				op.returnType = dncUtil.findType(types, "$(tok.exStr1)[]")
				op.refType = op.returnType
				insertPR(function, op)
				}
				else if (tokSubType == TokSubTypes.NEW_ARRAY_APPEND_SE)
				{
				op.type = HWI.OP_ID_NEW_ARRAY_APPEND
				op.subType = 1
				op.returnType = dncUtil.findType(types, "$(tok.exStr1)[]")
				op.refType = op.returnType
				insertPR(function, op)
				}
				else if (tokSubType == TokSubTypes.NEW_ARRAY)
				{
				if (tok.subTokens.next == null)
					{
					//one-dimensional array
					op.type = HWI.OP_ID_NEW_ARRAY
					op.returnType = dncUtil.findType(types, "$(tok.exStr1)[]")
					op.refType = op.returnType
					insertPR(function, op)

					result.subTokens = tok.subTokens.subTokens

					if (result.subTokens == null)
						{
						postError(status, tok.lineNumber, file, "no size value given for array initialisation by size")
						status.terminalErrorLine = true
						return result
						}
					}
					else
					{
					//multi-dimensional array
					// - create a new array initialisation, of type int[], to hold the dimensions

					DanaToken ntok = new DanaToken(DanaToken.OPERATION, TokSubTypes.NEW_ARRAY_CONSTRUCT, lineNumber = tok.lineNumber)
					ntok.exStr1 = "int"
					ntok.subTokens = new DanaToken(DanaToken.SQ_EXPRESSION, lineNumber = tok.lineNumber)
					ntok.subTokens.next = new DanaToken(DanaToken.PARAM, lineNumber = tok.lineNumber)
					ntok.lineNumber = tok.lineNumber

					char returnType[] = tok.exStr1

					DanaToken stw = tok.subTokens
					while (stw != null)
						{
						DanaToken nq = new DanaToken(DanaToken.PARAM, lineNumber = tok.lineNumber)
						nq.subTokens = stw.subTokens
						nq.lineNumber = stw.lineNumber

						if (nq.subTokens == null)
							{
							postError(status, tok.lineNumber, file, "no size value given for array initialisation by size")
							status.terminalErrorLine = true
							return result
							}

						if (ntok.subTokens.next.subTokens == null)
							{
							ntok.subTokens.next.subTokens = nq
							}
							else
							{
							ntok.subTokens.next.subTokensEnd.next = nq
							}
						
						ntok.subTokens.next.subTokensEnd = nq

						returnType = "$(returnType)[]"

						stw = stw.next
						}

					op.type = HWI.OP_ID_NEW_ARRAY_MD
					op.returnType = dncUtil.findType(types, returnType)
					op.refType = op.returnType
					insertPR(function, op)

					result.subTokens = ntok//tok.subTokens.subTokens.subTokens
					}
				}
				else if (tokSubType == TokSubTypes.NEW_ARRAY_FROM)
				{
				DanaToken lhs = tok.subTokens
				DanaToken rhs = tok.subTokens.next

				op.type = HWI.OP_ID_NEW_ARRAY_FROM
				op.returnType = dncUtil.findType(types, "$(lhs.exStr1)[]")
				op.refType = op.returnType
				insertPR(function, op)

				result.subTokens = clone lhs.subTokens.subTokens
				result.subTokens.next = rhs
				}
				else if (tokSubType == TokSubTypes.NEW_INSTANCE)
				{
				//check from the type whether it's a data or object
				DanaType qtype = dncUtil.findType(types, tok.exStr1)
				if (qtype.class == DanaType.INTERFACE)
					{
					//check if the type has a constructor (if so use an alternative instruction sequence, and handle parameter assignments)...
					if (hasConstuctor(qtype))
						{
						DanaToken sw = tok.subTokens.subTokens

						op.type = HWI.OP_ID_NEW_OBJECT_CONSTRUCT_CHK
						op.returnType = qtype
						op.refType = op.returnType

						OpToken newOp = new OpToken()
						newOp.type = HWI.OP_ID_NEW_OBJECT_CONSTRUCT
						newOp.returnType = qtype
						newOp.refType = qtype
						newOp.functionName = qtype.name
						newOp.bindportIndex = getRequiredInterfaceIndex(tok.exStr1, tok.exStr2, status)
						insertPR(function, newOp)

						if (newOp.bindportIndex == INT_MAX)
							{
							if (tok.exStr2 == null)
								postError(status, tok.lineNumber, file, "no required interface found to instantiate object $(tok.exStr1)")
								else
								postError(status, tok.lineNumber, file, "no required interface found to instantiate object $(tok.exStr1):$(tok.exStr2)")
							status.terminalErrorLine = true
							return result
							}

						DanaType objectType = qtype
						DanaTypeField functionField = objectType.fields.findFirst(DanaTypeField.[name], new DanaTypeField(name = newOp.functionName))
						newOp.functionIndex = getFunctionIndex(objectType, newOp.functionName, types)
						if (newOp.functionIndex == INT_MAX)
							{
							postError(status, tok.lineNumber, file, "function named $(newOp.functionName) not found in type $(objectType.name)")
							status.terminalErrorFunction = true
							return result
							}
						DanaType functionType = dncUtil.findType(types, functionField.type)
						prepParamStack.push(functionType)

						op.parameters = new OpToken[](newOp)						
						
						DanaToken lastSub = null
						DanaToken sub = null

						result.subTokens = null

						ParamInfo pinfo = getObjectCallParamInfo(qtype, qtype.name, types)
						int coreParams = 0
						int totalParams = 0

						if (global)
							{
							postError(status, sw.subTokens.lineNumber, file, "objects with constructors cannot be instantiated in global scope")
							return result
							}

						int pIndex = 0
						bool optLatch = false
						while (sw != null)
							{
							sub = new DanaToken()
							sub.type = DanaToken.OPERATION
							sub.subType = TokSubTypes.ASSIGN
							sub.lineNumber = tok.lineNumber

							if (sw.subTokens == null)
								{
								postError(status, tok.lineNumber, file, "empty parameter in constructor call")
								status.terminalErrorLine = true
								return result
								}

							if (sw.subTokens.type == DanaToken.OPERATION && sw.subTokens.subType == TokSubTypes.ASSIGN && sw.subTokens.subTokens.type == DanaToken.VARIABLE)
								{
								char field[] = sw.subTokens.subTokens.token
								
								sub.subTokens = new DanaToken()
								sub.subTokens.type = DanaToken.VARIABLE
								sub.subTokens.token = ":np:$field"
								sub.subTokens.lineNumber = tok.lineNumber

								sub.subTokens.next = sw.subTokens.subTokens.next

								optLatch = true
								}
								else
								{
								if (optLatch)
									{
									postError(status, sw.subTokens.lineNumber, file, "in-order parameter values cannot be specified after named parameter values")
									return result
									}

								sub.subTokens = new DanaToken()
								sub.subTokens.type = DanaToken.VARIABLE
								sub.subTokens.token = ":p:$pIndex"
								sub.subTokens.lineNumber = sw.lineNumber

								coreParams ++

								sub.subTokens.next = sw.subTokens
								}
							
							totalParams ++

							if (lastSub == null)
								result.subTokens = sub
								else
								lastSub.next = sub
							lastSub = sub

							pIndex ++
							sw = sw.next
							}
						
						if (coreParams < pinfo.coreParams)
							{
							postError(status, tok.lineNumber, file, "insufficient non-optional parameters in constructor of $(qtype.name): expected $(pinfo.coreParams)")
							return result
							}
						
						if (totalParams > (pinfo.coreParams + pinfo.optParams.arrayLength))
							{
							postError(status, tok.lineNumber, file, "too many parameters for constructor of $(qtype.name): expected maximum of $(pinfo.coreParams + pinfo.optParams.arrayLength)")
							status.terminalErrorFunction = true
							return result
							}
						}
						else
						{
						op.type = HWI.OP_ID_NEW_OBJECT
						op.returnType = qtype
						op.refType = op.returnType
						op.bindportIndex = getRequiredInterfaceIndex(tok.exStr1, tok.exStr2, status)
						insertPR(function, op)

						//"new" has sub-tokens from the parse stage, but they're not executable ones, so we blank our subtokens
						result.subTokens = null
						}
					}
					else if (qtype.class == DanaType.DATA)
					{
					// - use OP_ID_ELEMENT_FINISH (if we have any parameters), then OP_ID_ASSIGN_DATA_PRIMITIVE atc. for field assigns
					// - the first param to these assigns is getPR, which is the construct register; the second param is the RHS value to use
					// - ex1 is the data field index we're assigning into
					// - there are also named-field assignments to handle here

					DanaToken sw = tok.subTokens.subTokens

					if (sw != null)
						{
						op.type = HWI.OP_ID_ELEMENT_FINISH
						op.returnType = qtype
						op.refType = op.returnType

						OpToken newOp = new OpToken()
						newOp.type = HWI.OP_ID_NEW_DATA
						newOp.lineNumber = tok.lineNumber
						newOp.returnType = qtype
						newOp.refType = qtype
						insertPR(function, newOp)

						op.parameters = new OpToken[](newOp)

						DanaToken lastSub = null
						DanaToken sub = null

						int maxFieldCount = typeUtil.getDataFieldCount(qtype)

						int totalParams = 0
						int pIndex = 0
						bool optLatch = false
						while (sw != null)
							{
							//the task of this loop is to determine the field index, and the field type, and attach it to the sub-ops
							// - check if it's an assign operation, which means it's a specific field selection (after which you must do only field selects)
							sub = new DanaToken()
							sub.type = DanaToken.OPERATION
							sub.subType = TokSubTypes.ASSIGN_DATA_PARAM
							sub.lineNumber = sw.lineNumber

							if (sw.subTokens == null)
								{
								postError(status, tok.lineNumber, file, "empty parameter in data constructor")
								status.terminalErrorLine = true
								return result
								}

							if (sw.subTokens.type == DanaToken.OPERATION && sw.subTokens.subType == TokSubTypes.ASSIGN && sw.subTokens.subTokens.type == DanaToken.VARIABLE)
								{
								char field[] = sw.subTokens.subTokens.token
								pIndex = typeUtil.getDataFieldIndex(qtype, field)
								if (pIndex == INT_MAX)
									{
									postError(status, sw.subTokens.lineNumber, file, "field named $(field) not found in construction of data type '$(qtype.name)'")
									status.terminalErrorFunction = true
									return result
									}
								
								sub.exStr1 = typeUtil.getDataField(qtype, field).type
								sub.exStr2 = "$pIndex"
								sub.subTokens = sw.subTokens.subTokens.next
								optLatch = true
								}
								else
								{
								if (optLatch)
									{
									postError(status, sw.subTokens.lineNumber, file, "in-order field assignments cannot be specified after named field assignments, in construction of data type '$(qtype.name)'")
									return result
									}
								
								if (pIndex >= maxFieldCount)
									{
									postError(status, tok.lineNumber, file, "too many parameters for data construction of $(qtype.name): expected maximum of $(typeUtil.getDataFieldCount(qtype))")
									status.terminalErrorFunction = true
									return result
									}
								
								sub.exStr1 = typeUtil.getDataFieldAt(qtype, pIndex).type
								sub.exStr2 = "$pIndex"
								sub.subTokens = sw.subTokens
								}

							if (lastSub == null)
								result.subTokens = sub
								else
								lastSub.next = sub
							lastSub = sub

							totalParams ++

							pIndex ++
							sw = sw.next
							}
						
						if (totalParams > maxFieldCount)
							{
							postError(status, tok.lineNumber, file, "too many parameters for data construction of $(qtype.name): expected maximum of $(typeUtil.getDataFieldCount(qtype))")
							status.terminalErrorFunction = true
							return result
							}
						}
						else
						{
						op.type = HWI.OP_ID_NEW_DATA
						op.lineNumber = tok.lineNumber
						op.returnType = qtype
						op.refType = op.returnType
						insertPR(function, op)

						result.subTokens = null
						}
					}
					else
					{
					//TODO: error
					}
				}
				else if (tokSubType == TokSubTypes.NEW_INSTANCE_FROM)
				{
				//check from the type whether it's a data or object
				DanaToken lhs = tok.subTokens
				DanaToken rhs = tok.subTokens.next

				DanaType qtype = dncUtil.findType(types, lhs.exStr1)
				if (qtype.class == DanaType.INTERFACE)
					{
					//check if the type has a constructor (if so use an alternative instruction sequence, and handle parameter assignments)...
					
					//NOTE: this one is tricky to fit the paradigm: we need to parse the RHS fully, as a param of OP_ID_NEW_DYN_OBJECT_CONSTRUCT, as well as inserting the interface-name-string as a second param of OP_ID_NEW_DYN_OBJECT_CONSTRUCT
					// - we also need to pack ex1 and ex2 into a single ex1

					if (hasConstuctor(qtype))
						{
						DanaToken sw = lhs.subTokens.subTokens

						op.type = HWI.OP_ID_NEW_OBJECT_CONSTRUCT_CHK
						op.returnType = qtype
						op.refType = op.returnType

						OpToken newOp = new OpToken()
						newOp.type = HWI.OP_ID_NEW_DYN_OBJECT_CONSTRUCT
						newOp.returnType = qtype
						newOp.refType = qtype
						newOp.lineNumber = tok.lineNumber
						newOp.functionName = qtype.name
						newOp.bindportIndex = status.requiredInterfaces.findFirstIndex(RequiredInterface.[name], new RequiredInterface(name = tok.exStr1))
						insertPR(function, newOp)

						//for now we assume that the RHS is a simple variable access; in future we could perform a full sub-parse to allow expressions
						if (rhs.type != DanaToken.VARIABLE)
							{
							postError(status, sw.subTokens.lineNumber, file, "dynamic object construction must be from a variable")
							status.terminalErrorLine = true
							return result
							}
						
						ApplyOpResult subOR = applyOperation(file, function, global, pObject, scopes, rhs, newOp, 1, types, prepParamStack, status)
						if (subOR.instruction == null)
							{
							status.terminalErrorLine = true
							return result
							}
						
						newOp.parameters = new OpToken[](newOp.parameters, subOR.instruction)

						//generate the make-string thing for the final param of newOp
						DanaType ltype = dncUtil.findType(types, "char[$(qtype.name.arrayLength)]")
						OpToken msOp = new OpToken()
						msOp.type = HWI.OP_ID_NEW_ARRAY_INIT
						msOp.lineNumber = tok.lineNumber
						msOp.refType = dncUtil.findType(types, "char[]")
						msOp.returnType = msOp.refType
						insertPR(function, msOp)
						msOp.parameters = new OpToken[](msOp.parameters, new OpToken(HWI.OP_ID_GET_LITERAL_PTR, returnType = ltype, refValue = parseLiteral("(core)", new DanaToken(type = DanaToken.LITERAL, subType = TokSubTypes.L_STRING, token = qtype.name), new OpToken(), ltype, types, status)))
						
						newOp.parameters = new OpToken[](newOp.parameters, msOp)

						//THEN: sort out the "ex"-packing for newOp

						DanaType objectType = qtype
						DanaTypeField functionField = objectType.fields.findFirst(DanaTypeField.[name], new DanaTypeField(name = newOp.functionName))
						newOp.functionIndex = getFunctionIndex(objectType, newOp.functionName, types)
						DanaType functionType = dncUtil.findType(types, functionField.type)
						prepParamStack.push(functionType)

						op.parameters = new OpToken[](newOp)

						result.subTokens = null
						
						DanaToken lastSub = null
						DanaToken sub = null

						int pIndex = 0
						bool optLatch = false
						while (sw != null)
							{
							sub = new DanaToken()
							sub.type = DanaToken.OPERATION
							sub.subType = TokSubTypes.ASSIGN
							sub.lineNumber = sw.lineNumber

							if (sw.subTokens == null)
								{
								postError(status, tok.lineNumber, file, "empty parameter in constructor call")
								status.terminalErrorLine = true
								return result
								}

							if (sw.subTokens.type == DanaToken.OPERATION && sw.subTokens.subType == TokSubTypes.ASSIGN && sw.subTokens.subTokens.type == DanaToken.VARIABLE)
								{
								char field[] = sw.subTokens.subTokens.token
								
								sub.subTokens = new DanaToken()
								sub.subTokens.type = DanaToken.VARIABLE
								sub.subTokens.token = ":np:$field"
								sub.subTokens.lineNumber = sw.subTokens.lineNumber

								sub.subTokens.next = sw.subTokens.subTokens.next

								optLatch = true
								}
								else
								{
								if (optLatch)
									{
									postError(status, sw.subTokens.lineNumber, file, "in-order parameter values cannot be specified after named parameter values")
									return result
									}

								sub.subTokens = new DanaToken()
								sub.subTokens.type = DanaToken.VARIABLE
								sub.subTokens.token = ":p:$pIndex"

								sub.subTokens.next = sw.subTokens
								}

							if (lastSub == null)
								result.subTokens = sub
								else
								lastSub.next = sub
							lastSub = sub

							pIndex ++
							sw = sw.next
							}
						}
						else
						{
						op.type = HWI.OP_ID_NEW_DYN_OBJECT
						op.returnType = qtype
						op.refType = op.returnType
						insertPR(function, op)

						//"new" has sub-tokens from the parse stage, but they're not executable ones, so we blank our subtokens
						result.subTokens = clone rhs

						//create a literal string name array from the type name, as the final parameter
						DanaToken ntok = new DanaToken(DanaToken.OPERATION, TokSubTypes.NEW_ARRAY_CONSTRUCT, exStr1 = "char", lineNumber = tok.lineNumber)
						ntok.subTokens = new DanaToken(DanaToken.SQ_EXPRESSION)
						ntok.subTokens.next = new DanaToken(DanaToken.PARAM, subTokens = new DanaToken(DanaToken.PARAM, subTokens = new DanaToken(DanaToken.LITERAL, TokSubTypes.L_STRING, token = qtype.name)))

						result.subTokens.next = ntok
						}
					}
					else if (qtype.class == DanaType.DATA)
					{
					// - use OP_ID_ELEMENT_FINISH (if we have any parameters), then OP_ID_ASSIGN_DATA_PRIMITIVE atc. for field assigns
					// - the first param to these assigns is getPR, which is the construct register; the second param is the RHS value to use
					// - ex1 is the data field index we're assigning into
					// - there are also named-field assignments to handle here

					DanaToken sw = lhs.subTokens.subTokens

					if (sw != null)
						{
						postError(status, sw.subTokens.lineNumber, file, "dynamically-constructed data instances cannot have parameters")
						}
						else
						{
						op.type = HWI.OP_ID_NEW_DATA_FROM
						op.returnType = qtype
						op.refType = op.returnType
						insertPR(function, op)
						
						result.subTokens = rhs
						}
					}
					else
					{
					//TODO: error
					}
				}
				else if (tokSubType == TokSubTypes.ASSIGN_DATA_PARAM)
				{
				DanaType qtype = dncUtil.findType(types, tok.exStr1)

				if (isRefType(qtype))
					{
					op.type = HWI.OP_ID_ASSIGN_DATA_POINTER
					op.variableIndex = tok.exStr2.intFromString()
					op.returnType = qtype
					insertPR(function, op)
					}
					else if (qtype.class == DanaType.ARRAY && qtype.storageSize != 0)
					{
					op.type = HWI.OP_ID_ASSIGN_DATA_ARRAY
					op.variableIndex = tok.exStr2.intFromString()
					op.returnType = qtype
					insertPR(function, op)
					}
					else
					{
					op.type = HWI.OP_ID_ASSIGN_DATA_PRIMITIVE
					op.variableIndex = tok.exStr2.intFromString()
					op.returnType = qtype
					insertPR(function, op)
					}
				}
				else if (tokSubType == TokSubTypes.OBJECT_CALL)
				{
				// - fill in each parameter as an "assign"
				op.type = HWI.OP_ID_STACK_FRAME_LAUNCH_P
				//(note we don't know the return type here until the first param has been parsed)

				DanaToken sw = tok.subTokens.next.subTokens
				
				DanaToken lastSub = null
				DanaToken sub = new DanaToken()
				sub.type = DanaToken.OPERATION
				sub.subType = TokSubTypes.FRAME_INIT_OBJECT
				sub.exStr1 = tok.exStr1
				sub.lineNumber = tok.lineNumber
				sub.subTokens = clone tok.subTokens
				sub.subTokens.next = null

				result.subTokens = sub
				lastSub = sub

				int coreParams = 0
				int totalParams = 0

				int index = 0
				bool optLatch = false
				while (sw != null)
					{
					sub = new DanaToken()
					sub.type = DanaToken.OPERATION
					sub.subType = TokSubTypes.ASSIGN
					sub.lineNumber = sw.lineNumber

					if (sw.subTokens == null)
						{
						postError(status, tok.lineNumber, file, "empty parameter in function call")
						status.terminalErrorLine = true
						return result
						}

					if (sw.subTokens.type == DanaToken.OPERATION && sw.subTokens.subType == TokSubTypes.ASSIGN && sw.subTokens.subTokens.type == DanaToken.VARIABLE)
						{
						char field[] = sw.subTokens.subTokens.token
						
						sub.subTokens = new DanaToken()
						sub.subTokens.type = DanaToken.VARIABLE
						sub.subTokens.token = ":np:$field"
						sub.subTokens.lineNumber = sw.lineNumber

						sub.subTokens.next = sw.subTokens.subTokens.next

						optLatch = true
						}
						else
						{
						if (optLatch)
							{
							postError(status, sw.subTokens.lineNumber, file, "in-order parameter values cannot be specified after named parameter values")
							return result
							}

						sub.subTokens = new DanaToken()
						sub.subTokens.type = DanaToken.VARIABLE
						sub.subTokens.token = ":p:$index"
						sub.subTokens.lineNumber = sw.lineNumber

						sub.subTokens.next = sw.subTokens

						coreParams ++
						}

					lastSub.next = sub
					lastSub = sub

					sw = sw.next
					index ++

					totalParams ++
					}
				
				//result.subTokens.coreParams = coreParams
				//result.subTokens.totalParams = totalParams
				}
				else if (tokSubType == TokSubTypes.LOCAL_CALL || tokSubType == TokSubTypes.LOCAL_CALL_ASYNCH)
				{
				// - fill in each parameter as an "assign"

				if (tokSubType == TokSubTypes.LOCAL_CALL)
					op.type = HWI.OP_ID_STACK_FRAME_LAUNCH_P
					else
					op.type = HWI.OP_ID_STACK_FRAME_LAUNCH_ASYNCH

				DanaToken sw = tok.subTokens.subTokens

				if (tok.token != "super" && tok.exStr1 != "super")
					{
					OpFunctionDetail ofd = function
					LocalCallOp lco = new LocalCallOp(op)
					lco.next = ofd.localCalls
					ofd.localCalls = lco

					DanaToken lastSub = null
					DanaToken sub = new DanaToken()
					sub.type = DanaToken.OPERATION
					sub.subType = TokSubTypes.FRAME_INIT_LOCAL
					sub.exStr1 = tok.exStr1
					sub.lineNumber = tok.lineNumber

					ParamInfo pinfo = getLocalCallParamInfo(pObject, tok.exStr1, types)

					if (pinfo == null)
						{
						postError(status, tok.lineNumber, file, "local function '$(tok.exStr1)()' not found")
						status.terminalErrorLine = true
						return result
						}

					result.subTokens = sub
					lastSub = sub

					int coreParams = 0
					int totalParams = 0

					int index = 0
					bool optLatch = false
					while (sw != null)
						{
						sub = new DanaToken()
						sub.type = DanaToken.OPERATION
						sub.subType = TokSubTypes.ASSIGN
						sub.lineNumber = sw.lineNumber

						if (sw.subTokens == null)
							{
							postError(status, tok.lineNumber, file, "empty parameter in function call")
							status.terminalErrorLine = true
							return result
							}

						if (sw.subTokens.type == DanaToken.OPERATION && sw.subTokens.subType == TokSubTypes.ASSIGN && sw.subTokens.subTokens.type == DanaToken.VARIABLE)
							{
							char field[] = sw.subTokens.subTokens.token
							
							sub.subTokens = new DanaToken()
							sub.subTokens.type = DanaToken.VARIABLE
							sub.subTokens.token = ":np:$field"
							sub.subTokens.lineNumber = sw.subTokens.lineNumber

							sub.subTokens.next = sw.subTokens.subTokens.next

							optLatch = true
							}
							else
							{
							if (optLatch)
								{
								postError(status, sw.subTokens.lineNumber, file, "in-order parameter values cannot be specified after named parameter values")
								return result
								}

							sub.subTokens = new DanaToken()
							sub.subTokens.type = DanaToken.VARIABLE
							sub.subTokens.token = ":p:$index"
							sub.subTokens.lineNumber = sw.lineNumber

							sub.subTokens.next = sw.subTokens

							coreParams ++
							}

						lastSub.next = sub
						lastSub = sub

						sw = sw.next
						index ++

						totalParams ++
						}
					
					if (coreParams < pinfo.coreParams)
						{
						postError(status, tok.lineNumber, file, "insufficient non-optional parameters in function call $(tok.exStr1): expected $(pinfo.coreParams)")
						return result
						}
					
					if (totalParams > (pinfo.coreParams + pinfo.optParams.arrayLength))
						{
						postError(status, tok.lineNumber, file, "too many parameters for function call $(tok.exStr1): expected maximum of $(pinfo.coreParams + pinfo.optParams.arrayLength)")
						status.terminalErrorFunction = true
						return result
						}
					}
					else
					{
					//check if we're inside a constructor function; if so we need to replace super() with ":super = new_object_st()"
					// - the new-object element is done (at least for types that have a constructor); next we need to insert the wrapping assignment
					// - TODO: I have no idea how this works for sub-type objects that don't have a constructor, where their super-type also has no constructor...

					//out.println("local super call in $(function.name) of $(function.fromType)")

					if (function.ftype == OpFunction.CONSTRUCTOR)
						{
						DanaType xType = dncUtil.findType(types, function.fromType)
						DanaType qtype = dncUtil.findType(types, xType.extendsType)

						//out.println("super-constructor")

						op.type = HWI.OP_ID_ASSIGN_POINTER
						op.returnType = qtype
						op.addAbortCheck = true
						
						OpToken subOp = new OpToken()
						subOp.type = HWI.OP_ID_NEW_OBJECT_CONSTRUCT_CHK
						subOp.returnType = qtype
						subOp.refType = op.returnType

						op.parameters = new OpToken[](new OpToken(HWI.OP_ID_GET_PTR_HND), subOp)
						op.parameters[0].returnType = qtype
						op.parameters[0].variableIndex = getGlobalInstanceVariableIndex(pObject, ":super")
						op.parameters[0].parameters = new OpToken[](new OpToken(type = HWI.OP_ID_GET_PR), new OpToken(type = HWI.OP_ID_PRIVATE_IVS))

						insertPR(function, op)

						OpToken newOp = new OpToken()
						newOp.type = HWI.OP_ID_NEW_OBJECT_CONSTRUCT_ST
						newOp.returnType = qtype
						newOp.refType = qtype
						newOp.functionName = qtype.name
						newOp.bindportIndex = status.requiredInterfaces.findFirstIndex(RequiredInterface.[name], new RequiredInterface(name = qtype.name))
						insertPR(function, newOp)

						DanaType objectType = qtype
						DanaTypeField functionField = objectType.fields.findFirst(DanaTypeField.[name], new DanaTypeField(name = newOp.functionName))
						newOp.functionIndex = getFunctionIndex(objectType, newOp.functionName, types)
						DanaType functionType = dncUtil.findType(types, functionField.type)
						prepParamStack.push(functionType)

						subOp.parameters = new OpToken[](newOp)						
						
						DanaToken lastSub = null
						DanaToken sub = null

						result.subTokens = null
						result.asparent = subOp

						ParamInfo pinfo = getObjectCallParamInfo(qtype, qtype.name, types)
						int coreParams = 0
						int totalParams = 0

						int pIndex = 0
						bool optLatch = false
						while (sw != null)
							{
							sub = new DanaToken()
							sub.type = DanaToken.OPERATION
							sub.subType = TokSubTypes.ASSIGN
							sub.lineNumber = sw.lineNumber

							if (sw.subTokens == null)
								{
								postError(status, tok.lineNumber, file, "empty parameter in function call")
								status.terminalErrorLine = true
								return result
								}

							if (sw.subTokens.type == DanaToken.OPERATION && sw.subTokens.subType == TokSubTypes.ASSIGN && sw.subTokens.subTokens.type == DanaToken.VARIABLE)
								{
								char field[] = sw.subTokens.subTokens.token
								
								sub.subTokens = new DanaToken()
								sub.subTokens.type = DanaToken.VARIABLE
								sub.subTokens.token = ":np:$field"
								sub.subTokens.lineNumber = sw.lineNumber

								sub.subTokens.next = sw.subTokens.subTokens.next

								optLatch = true
								}
								else
								{
								if (optLatch)
									{
									postError(status, sw.subTokens.lineNumber, file, "in-order parameter values cannot be specified after named parameter values")
									status.terminalErrorFunction = true
									return result
									}

								sub.subTokens = new DanaToken()
								sub.subTokens.type = DanaToken.VARIABLE
								sub.subTokens.token = ":p:$pIndex"
								sub.subTokens.lineNumber = sw.lineNumber

								sub.subTokens.next = sw.subTokens

								coreParams ++
								}

							if (lastSub == null)
								result.subTokens = sub
								else
								lastSub.next = sub
							lastSub = sub

							pIndex ++
							sw = sw.next

							totalParams ++
							}
						
						if (coreParams < pinfo.coreParams)
							{
							postError(status, tok.lineNumber, file, "insufficient non-optional parameters in constructor of $(qtype.name): expected $(pinfo.coreParams)")
							status.terminalErrorFunction = true
							return result
							}
						
						if (totalParams > (pinfo.coreParams + pinfo.optParams.arrayLength))
							{
							postError(status, tok.lineNumber, file, "too many parameters for constructor of $(qtype.name): expected maximum of $(pinfo.coreParams + pinfo.optParams.arrayLength)")
							status.terminalErrorFunction = true
							return result
							}
						}
						else
						{
						DanaToken lastSub = null
						DanaToken sub = new DanaToken()
						sub.type = DanaToken.OPERATION
						sub.subType = TokSubTypes.FRAME_INIT_OBJECT
						sub.exStr1 = function.name
						sub.lineNumber = tok.lineNumber
						sub.subTokens = new DanaToken(DanaToken.VARIABLE, token = ":super")
						sub.subTokens.next = null

						//out.println("super-general")

						result.subTokens = sub
						lastSub = sub

						int index = 0
						bool optLatch = false
						while (sw != null)
							{
							sub = new DanaToken()
							sub.type = DanaToken.OPERATION
							sub.subType = TokSubTypes.ASSIGN
							sub.lineNumber = sw.lineNumber

							if (sw.subTokens == null)
								{
								postError(status, tok.lineNumber, file, "empty parameter in function call")
								status.terminalErrorLine = true
								return result
								}

							if (sw.subTokens.type == DanaToken.OPERATION && sw.subTokens.subType == TokSubTypes.ASSIGN && sw.subTokens.subTokens.type == DanaToken.VARIABLE)
								{
								char field[] = sw.subTokens.subTokens.token
								
								sub.subTokens = new DanaToken()
								sub.subTokens.type = DanaToken.VARIABLE
								sub.subTokens.token = ":np:$field"
								sub.subTokens.lineNumber = sw.subTokens.lineNumber

								sub.subTokens.next = sw.subTokens.subTokens.next

								optLatch = true
								}
								else
								{
								if (optLatch)
									{
									postError(status, sw.subTokens.lineNumber, file, "in-order parameter values cannot be specified after named parameter values")
									return result
									}

								sub.subTokens = new DanaToken()
								sub.subTokens.type = DanaToken.VARIABLE
								sub.subTokens.token = ":p:$index"
								sub.subTokens.lineNumber = sw.lineNumber

								sub.subTokens.next = sw.subTokens
								}

							lastSub.next = sub
							lastSub = sub

							sw = sw.next
							index ++
							}
						}
					}
				}
				else if (tokSubType == TokSubTypes.RETURN)
				{
				op.type = HWI.OP_ID_RETURN
				op.returnType = function.returnType

				if (function.fromType != null)
					{
					if (typeMayIncludeObjects(function.returnType, types))
						{
						//TODO: if we know the exact type, and know it's free of any object-instances created in this component, we can avoid this check
						op.type = HWI.OP_ID_RETURN_ORCHK
						}
					}
				}
				else if (tokSubType == TokSubTypes.IS_SET)
				{
				op.type = HWI.OP_ID_ISSET
				op.returnType = dncUtil.findType(types, "bool")
				op.variableIndex = function.parameters.findFirstIndex(Parameter.[name], new Parameter(name = tok.exStr1))
				addSR(function, op, "bool", op.returnType)
				}
				else if (tokSubType == TokSubTypes.HAS_TYPE)
				{
				op.type = HWI.OP_ID_HAS_TYPE
				op.returnType = dncUtil.findType(types, "bool")
				op.refType = dncUtil.findType(types, tok.exStr1)
				addSR(function, op, "bool", op.returnType)
				}
				else if (tokSubType == TokSubTypes.IMPLEMENTS)
				{
				op.type = HWI.OP_ID_IMPLEMENTS
				op.returnType = dncUtil.findType(types, "bool")
				addSR(function, op, "bool", op.returnType)
				}
				else if (tokSubType == TokSubTypes.THROW)
				{
				op.type = HWI.OP_ID_PREP_EXCEPTION_THROW
				op.returnType = dncUtil.findType(types, "void")
				}
				else if (tokSubType == TokSubTypes.CLONE)
				{
				op.type = HWI.OP_ID_CLONE_DATA
				insertPR(function, op)
				}
				else if (tokSubType == TokSubTypes.RCLONE)
				{
				op.type = HWI.OP_ID_RCLONE
				insertPR(function, op)
				}
				else if (tokSubType == TokSubTypes.DELINK)
				{
				op.type = HWI.OP_ID_DELINK
				op.returnType = dncUtil.findType(types, "void")
				}
				else if (tokSubType == TokSubTypes.SINK_EVENT)
				{
				op.type = HWI.OP_ID_SINK_EVENT
				op.functionName = tok.subTokens.exStr1
				op.functionIndex = getEventsinkIndex(op.functionName, pObject)
				result.subTokens = tok.subTokens.subTokens
				op.returnType = dncUtil.findType(types, "void")
				}
				else if (tokSubType == TokSubTypes.STOP_EVENT)
				{
				op.type = HWI.OP_ID_UNSINK_EVENT
				op.functionName = tok.subTokens.exStr1
				op.functionIndex = getEventsinkIndex(op.functionName, pObject)
				result.subTokens = tok.subTokens.subTokens
				op.returnType = dncUtil.findType(types, "void")
				}
				else if (tokSubType == TokSubTypes.EMIT_EVENT)
				{
				op.type = HWI.OP_ID_EMIT_EVENT_ST
				DanaType intfType = types.findFirst(DanaType.[name], new DanaType(name = pObject.mainInterface))
				char eventName[] = tok.subTokens.exStr1
				op.functionName = eventName
				op.functionIndex = getEventIndex(intfType, eventName, types)
				op.returnType = dncUtil.findType(types, "void")
				op.refType = getEventParamType(intfType, eventName, types)
				result.subTokens = tok.subTokens.subTokens.subTokens
				if (result.subTokens == null)
					{
					result.subTokens = new DanaToken(DanaToken.VARIABLE, token = "null")
					}
					else
					{
					result.subTokens = result.subTokens.subTokens
					}
				}
				else if (tokSubType == TokSubTypes.FRAME_INIT_OBJECT)
				{
				op.type = HWI.OP_ID_STACK_FRAME_INIT_OBJECT
				op.functionName = tok.exStr1
				}
				else if (tokSubType == TokSubTypes.FRAME_INIT_LOCAL)
				{
				op.type = HWI.OP_ID_STACK_FRAME_INIT_LOCAL
				op.functionName = tok.exStr1
				op.functionRef = findFunctionRef(pObject, tok.exStr1)

				if (op.functionRef == null) out.println("null function ref for $(tok.exStr1) at line $(tok.lineNumber)")

				//resolve functionRef to its OpFunction, then construct a functionType for it, including parameters and return type, and add to prepParamStack
				OpFunction opf = findFunctionOp(pObject, op.functionRef.name, op.functionRef.intfName)
				DanaType functionType = makeFunctionDanaType(opf, types)
				op.returnType = dncUtil.findType(types, functionType.fields[0].type)

				prepParamStack.push(functionType)
				}
				else if (tokSubType == TokSubTypes.DYNAMIC_CALL)
				{
				op.type = HWI.OP_ID_DYNAMIC_CALL
				insertPR(function, op)
				op.returnType = types.findFirst(DanaType.[name], new DanaType(name = ""))

				result.subTokens = clone tok.subTokens
				result.subTokens.next = clone tok.subTokens.next
				result.subTokens.next.next = clone tok.subTokens.next.next.subTokens.subTokens
				result.subTokens.next.next.next = null
				}
				else if (tokSubType == TokSubTypes.EMIT_EVENT_DYN)
				{
				op.type = HWI.OP_ID_EMIT_EVENT
				op.returnType = dncUtil.findType(types, "void")

				result.subTokens = clone tok.subTokens
				result.subTokens.next = clone tok.subTokens.next.subTokens.subTokens
				result.subTokens.next.next = null
				}
				else if (tokSubType == TokSubTypes.EQUAL)
				{
				op.type = HWI.OP_ID_EQUAL
				op.returnType = dncUtil.findType(types, "bool")
				addSR(function, op, "bool", op.returnType)
				}
				else if (tokSubType == TokSubTypes.EQUAL_REF)
				{
				op.type = HWI.OP_ID_EQUAL_POINTER
				op.returnType = dncUtil.findType(types, "bool")
				addSR(function, op, "bool", op.returnType)
				}
				else if (tokSubType == TokSubTypes.GREATER_THAN)
				{
				op.type = HWI.OP_ID_GREATER_THAN
				op.returnType = dncUtil.findType(types, "bool")
				addSR(function, op, "bool", op.returnType)
				}
				else if (tokSubType == TokSubTypes.LESS_THAN || tokSubType == TokSubTypes.LESS_THAN_EQ)
				{
				if (tokSubType == TokSubTypes.LESS_THAN)
					op.type = HWI.OP_ID_GREATER_THAN
					else
					op.type = HWI.OP_ID_GREATER_THAN_EQ
				op.returnType = dncUtil.findType(types, "bool")
				addSR(function, op, "bool", op.returnType)

				//operand order swap
				result.subTokens = clone tok.subTokens.next
				result.subTokens.next = clone tok.subTokens
				result.subTokens.next.next = null
				}
				else if (tokSubType == TokSubTypes.GREATER_THAN_EQ)
				{
				op.type = HWI.OP_ID_GREATER_THAN_EQ
				op.returnType = dncUtil.findType(types, "bool")
				addSR(function, op, "bool", op.returnType)
				}
				else if (tokSubType == TokSubTypes.NOT_EQUAL)
				{
				op.type = HWI.OP_ID_LNOT
				op.returnType = dncUtil.findType(types, "bool")
				addSR(function, op, "bool", op.returnType)

				op.parameters = new OpToken[](op.parameters, new OpToken(type = HWI.OP_ID_EQUAL, lineNumber = tok.lineNumber))
				op.parameters[1].returnType = dncUtil.findType(types, "bool")
				addSR(function, op.parameters[1], "bool", op.returnType)

				//we now need parameters of this tok to be added to the OP_ID_EQUAL op, not the OP_ID_LNOT
				result.asparent = op.parameters[1]
				}
				else if (tokSubType == TokSubTypes.NOT_EQUAL_REF)
				{
				op.type = HWI.OP_ID_LNOT
				op.returnType = dncUtil.findType(types, "bool")
				addSR(function, op, "bool", op.returnType)
				
				
				op.parameters = new OpToken[](op.parameters, new OpToken(type = HWI.OP_ID_EQUAL_POINTER, returnType = getType(types, "bool")))
				//op.parameters[1].returnType = getType(types, "bool")//types.findFirst(DanaType.[name], new DanaType(name = "bool"))
				addSR(function, op.parameters[1], "bool", op.returnType)
				
				//we now need parameters of this tok to be added to the OP_ID_EQUAL op, not the OP_ID_LNOT
				result.asparent = op.parameters[1]
				
				}
				else if (tokSubType == TokSubTypes.DX_EQ_OBJECT_REF)
				{
				op.type = HWI.OP_ID_EQUAL_OBJECT_REFERENCE
				op.returnType = dncUtil.findType(types, "bool")
				result.subTokens = tok.subTokens.subTokens.subTokens
				addSR(function, op, "bool", op.returnType)

				if (!checkOpParamCount(result.subTokens, 1))
					{
					postError(status, tok.lineNumber, file, "incorrect parameter count for dana.equalObjectReference(): expected 1 parameter")
					status.terminalErrorLine = true
					return result
					}
				}
				else if (tokSubType == TokSubTypes.DX_GET_SELF_ID)
				{
				op.type = HWI.OP_ID_GET_SELF_ID
				op.returnType = types.findFirst(DanaType.[name], new DanaType(name = "char[]"))
				result.subTokens = dropParams(tok)
				insertPR(function, op)

				if (!checkOpParamCount(result.subTokens, 0))
					{
					postError(status, tok.lineNumber, file, "incorrect parameter count for dana.getSelfID(): expected 0 parameters")
					status.terminalErrorLine = true
					return result
					}
				}
				else if (tokSubType == TokSubTypes.THIS_THREAD)
				{
				op.type = HWI.OP_ID_THREAD_THIS
				op.returnType = dncUtil.findType(types, "lang.Thread")
				result.subTokens = null
				insertPR(function, op)
				}
				else if (tokSubType == TokSubTypes.GET_TYPE_FIELD)
				{
				//this is just a get-literal, where the int is determined by the field index
				// - check the legacy compiler for how transfer fields and events are handled in indexing for interface types

				//"fieldName" here is also allowed to be a compound "function(type,type,type)" statement, so check for this too...
				DanaType fromType = dncUtil.findType(types, tok.subTokens.token)
				char fieldName[] = null

				if (tok.subTokens.next.subTokens == null)
					{
					postError(status, tok.lineNumber, file, "type field expression with no field identifier")
					status.terminalErrorLine = true
					return result
					}
				
				if (tok.subTokens.next.subTokens.type == DanaToken.VARIABLE)
					fieldName = tok.subTokens.next.subTokens.token
					else if (tok.subTokens.next.subTokens.type == DanaToken.OPERATION && tok.subTokens.next.subTokens.subType == TokSubTypes.LOCAL_CALL)
					fieldName = tok.subTokens.next.subTokens.exStr1

				int fieldIndex = typeUtil.getFieldIndex(fromType, fieldName, types)

				if (fieldIndex != INT_MAX)
					{
					DanaType qt = dncUtil.findType(types, "int")
					op.type = HWI.OP_ID_GET_LITERAL_PTR
					DanaToken tmp = new DanaToken(DanaToken.LITERAL, subType = TokSubTypes.L_INT, token = fieldIndex.makeString())
					op.refValue = parseLiteral(file, tmp, op, qt, types, status)
					}
					else
					{
					postError(status, tok.lineNumber, file, "type field '$(fieldName)' not found in type $(fromType.name)")
					status.terminalErrorLine = true
					}

				result.subTokens = null
				}
				else if (tokSubType == TokSubTypes.GET_VAR_FIELD)
				{
				op.type = HWI.OP_ID_GET_FIELD
				op.returnType = dncUtil.findType(types, "")
				insertPR(function, op)
				}
				else if (tokSubType == TokSubTypes.TYPEOF)
				{
				//we need to decide *now* if it's OP_ID_TYPEOF_VAR or OP_ID_TYPEOF_TYPE, since its param won't parse properly if it's a type ...
				if (tok.subTokens == null || tok.subTokens.subTokens == null)
					{
					postError(status, tok.lineNumber, file, "insufficient parameters for typeof() operator (expected 1)")
					status.terminalErrorLine = true
					return result
					}
				
				if (tok.subTokens.subTokens.subTokens.type == DanaToken.TYPE)
					{
					op.type = HWI.OP_ID_TYPEOF_TYPE
					op.returnType = types.findFirst(DanaType.[name], new DanaType(name = "")) //TODO: should be lang.Type
					op.refType = types.findFirst(DanaType.[name], new DanaType(name = tok.subTokens.subTokens.subTokens.token))
					insertPR(function, op)
					result.subTokens = null

					if (op.refType == null)
						{
						postError(status, tok.lineNumber, file, "unknown type '$(tok.subTokens.subTokens.subTokens.token)'")
						}
					}
					else
					{
					op.type = HWI.OP_ID_TYPEOF_VAR
					op.returnType = types.findFirst(DanaType.[name], new DanaType(name = "")) //TODO: should be lang.Type
					insertPR(function, op)
					result.subTokens = tok.subTokens.subTokens.subTokens
					}
				}
				else if (tokSubType == TokSubTypes.IF || tokSubType == TokSubTypes.ELSEIF)
				{
				op.type = HWI.OP_ID_IF
				op.returnType = dncUtil.findType(types, "bool")
				result.subTokens = tok.subTokens.subTokens
				}
				else if (tokSubType == TokSubTypes.ELSE)
				{
				op.type = HWI.OP_ID_NOP
				}
				else if (tokSubType == TokSubTypes.WHILE)
				{
				op.type = HWI.OP_ID_IF
				op.returnType = dncUtil.findType(types, "bool")
				result.subTokens = tok.subTokens.subTokens
				}
				else if (tokSubType == TokSubTypes.ARRAY_COPY)
				{
				op.type = HWI.OP_ID_COPY
				op.returnType = dncUtil.findType(types, "void")
				}
				else if (tokSubType == TokSubTypes.INCREMENT)
				{
				op.type = HWI.OP_ID_INTINC
				}
				else if (tokSubType == TokSubTypes.DECREMENT)
				{
				op.type = HWI.OP_ID_INTDEC
				}
				else if (tokSubType == TokSubTypes.NUM_ADD)
				{
				op.type = HWI.OP_ID_INTADD
				}
				else if (tokSubType == TokSubTypes.NUM_SUB)
				{
				op.type = HWI.OP_ID_INTSUB
				}
				else if (tokSubType == TokSubTypes.NUM_MUL)
				{
				op.type = HWI.OP_ID_INTMUL
				}
				else if (tokSubType == TokSubTypes.NUM_DIV)
				{
				op.type = HWI.OP_ID_INTDIV
				}
				else if (tokSubType == TokSubTypes.NUM_MOD)
				{
				op.type = HWI.OP_ID_INTMOD
				}
				else if (tokSubType == TokSubTypes.BIT_LSHIFT)
				{
				op.type = HWI.OP_ID_LSHIFT
				}
				else if (tokSubType == TokSubTypes.BIT_RSHIFT)
				{
				op.type = HWI.OP_ID_RSHIFT
				}
				else if (tokSubType == TokSubTypes.BIT_AND)
				{
				op.type = HWI.OP_ID_AND
				}
				else if (tokSubType == TokSubTypes.BIT_OR)
				{
				op.type = HWI.OP_ID_OR
				}
				else if (tokSubType == TokSubTypes.BIT_XOR)
				{
				op.type = HWI.OP_ID_XOR
				}
				else if (tokSubType == TokSubTypes.BIT_NOT)
				{
				op.type = HWI.OP_ID_BNOT
				}
				else if (tokSubType == TokSubTypes.IFX_AND)
				{
				op.type = HWI.OP_ID_AND
				op.subType = 1 //indicator to insert an "if" around our first parameter
				}
				else if (tokSubType == TokSubTypes.IFX_OR)
				{
				op.type = HWI.OP_ID_OR
				op.subType = 1 //indicator to insert an "if" around our first parameter
				}
				else if (tokSubType == TokSubTypes.BOOL_NOT)
				{
				op.type = HWI.OP_ID_LNOT
				op.returnType = dncUtil.findType(types, "bool")
				addSR(function, op, "bool", op.returnType)
				}
				else if (tokSubType == TokSubTypes.BREAK)
				{
				op.type = HWI.OP_ID_JMP
				op.returnType = dncUtil.findType(types, "void")
				}
				else if (tokSubType == TokSubTypes.MUTEX)
				{
				op.type = HWI.OP_ID_MUTEX_LOCK
				op.returnType = dncUtil.findType(types, "void")
				result.subTokens = tok.subTokens.subTokens
				}
				else if (tokSubType == TokSubTypes.ARRAY_LENGTH)
				{
				op.type = HWI.OP_ID_LENGTH
				op.returnType = dncUtil.findType(types, "int")
				addSR(function, op, "int", op.returnType)
				}
				else if (tokSubType == TokSubTypes.SUB_ARRAY)
				{
				op.type = HWI.OP_ID_SUB_ARRAY
				insertPR(function, op)
				result.subTokens = dropParams(tok)

				if (!checkOpParamCount(result.subTokens, 3))
					{
					postError(status, tok.lineNumber, file, "incorrect parameter count for dana.sub(): expected 3 parameters")
					status.terminalErrorLine = true
					return result
					}
				}
				else if (tokSubType == TokSubTypes.SERIAL)
				{
				op.type = HWI.OP_ID_SERIAL
				insertPR(function, op)
				op.returnType = dncUtil.findType(types, "byte[]")
				result.subTokens = dropParams(tok)

				if (result.subTokens.next != null)
					{
					op.type = HWI.OP_ID_SERIAL_FIELD

					if (!checkOpParamCount(result.subTokens, 2))
						{
						postError(status, tok.lineNumber, file, "incorrect parameter count for dana.serial(): expected between 1 and 2 parameters")
						status.terminalErrorLine = true
						return result
						}
					}
					else
					{
					if (!checkOpParamCount(result.subTokens, 1))
						{
						postError(status, tok.lineNumber, file, "incorrect parameter count for dana.serial(): expected between 1 and 2 parameters")
						status.terminalErrorLine = true
						return result
						}
					}
				}
				else if (tokSubType == TokSubTypes.GET_RUNTIME_IDC)
				{
				op.type = HWI.OP_ID_MACHINE
				insertPR(function, op)
				op.returnType = types.findFirst(DanaType.[name], new DanaType(name = "lang.IDC"))
				result.subTokens = dropParams(tok)

				if (!checkOpParamCount(result.subTokens, 0))
					{
					postError(status, tok.lineNumber, file, "incorrect parameter count for dana.getIDC(): expected 0 parameters")
					status.terminalErrorLine = true
					return result
					}
				}
				else if (tokSubType == TokSubTypes.PAUSE_OBJECT)
				{
				op.type = HWI.OP_ID_EVO_PAUSE_OBJECT
				op.returnType = types.findFirst(DanaType.[name], new DanaType(name = "bool"))
				addSR(function, op, "bool", op.returnType)
				result.subTokens = dropParams(tok)

				if (!checkOpParamCount(result.subTokens, 2))
					{
					postError(status, tok.lineNumber, file, "incorrect parameter count for dana.pauseObject(): expected 2 parameters")
					status.terminalErrorLine = true
					return result
					}
				}
				else if (tokSubType == TokSubTypes.RESUME_OBJECT)
				{
				op.type = HWI.OP_ID_EVO_RESUME_OBJECT
				op.returnType = types.findFirst(DanaType.[name], new DanaType(name = "void"))
				result.subTokens = dropParams(tok)

				if (!checkOpParamCount(result.subTokens, 1))
					{
					postError(status, tok.lineNumber, file, "incorrect parameter count for dana.resumeObject(): expected 1 parameter")
					status.terminalErrorLine = true
					return result
					}
				}
				else if (tokSubType == TokSubTypes.WAIT_FOR_OBJECT)
				{
				op.type = HWI.OP_ID_EVO_WAIT_FOR_OBJECT
				op.returnType = types.findFirst(DanaType.[name], new DanaType(name = "void"))
				result.subTokens = dropParams(tok)

				if (!checkOpParamCount(result.subTokens, 1))
					{
					postError(status, tok.lineNumber, file, "incorrect parameter count for dana.waitForObject(): expected 1 parameter")
					status.terminalErrorLine = true
					return result
					}
				}
				else if (tokSubType == TokSubTypes.WAIT_FOR_OBJECT_THREADS)
				{
				op.type = HWI.OP_ID_EVO_WAIT_FOR_OBJECT_TH
				op.returnType = types.findFirst(DanaType.[name], new DanaType(name = "void"))
				result.subTokens = dropParams(tok)

				if (!checkOpParamCount(result.subTokens, 1))
					{
					postError(status, tok.lineNumber, file, "incorrect parameter count for dana.waitForObjectThreads(): expected 1 parameter")
					status.terminalErrorLine = true
					return result
					}
				}
				else if (tokSubType == TokSubTypes.REWIRE_OBJECT)
				{
				op.type = HWI.OP_ID_EVO_REWIRE_OBJECT
				op.returnType = types.findFirst(DanaType.[name], new DanaType(name = "lang.Object"))
				result.subTokens = dropParams(tok)
				insertPR(function, op)

				if (!checkOpParamCount(result.subTokens, 2))
					{
					postError(status, tok.lineNumber, file, "incorrect parameter count for dana.waitForObjectThreads(): expected 2 parameters")
					status.terminalErrorLine = true
					return result
					}
				}
				else if (tokSubType == TokSubTypes.HALT)
				{
				op.type = HWI.OP_ID_FORCE_STOP_OBJECT
				op.returnType = types.findFirst(DanaType.[name], new DanaType(name = "void"))
				result.subTokens = dropParams(tok)

				if (!checkOpParamCount(result.subTokens, 2))
					{
					postError(status, tok.lineNumber, file, "incorrect parameter count for dana.halt(): expected 2 parameters")
					status.terminalErrorLine = true
					return result
					}
				}
				else if (tokSubType == TokSubTypes.GET_DIMENSIONS)
				{
				op.type = HWI.OP_ID_GET_ARRAY_DIMENSIONS
				insertPR(function, op)
				op.returnType = types.findFirst(DanaType.[name], new DanaType(name = "int[]"))
				result.subTokens = dropParams(tok)

				if (!checkOpParamCount(result.subTokens, 1))
					{
					postError(status, tok.lineNumber, file, "incorrect parameter count for dana.getDimensions(): expected 1 parameter")
					status.terminalErrorLine = true
					return result
					}
				}
				else if (tokSubType == TokSubTypes.SET_DIMENSIONS)
				{
				op.type = HWI.OP_ID_SET_ARRAY_DIMENSIONS
				//insertPR(function, op)
				op.returnType = types.findFirst(DanaType.[name], new DanaType(name = "void"))
				result.subTokens = dropParams(tok)

				if (!checkOpParamCount(result.subTokens, 2))
					{
					postError(status, tok.lineNumber, file, "incorrect parameter count for dana.setDimensions(): expected 2 parameters")
					status.terminalErrorLine = true
					return result
					}
				}
				else if (tokSubType == TokSubTypes.HTOD)
				{
				op.type = HWI.OP_ID_HTOD
				result.subTokens = dropParams(tok)

				if (!checkOpParamCount(result.subTokens, 1))
					{
					postError(status, tok.lineNumber, file, "incorrect parameter count for dana.htod(): expected 1 parameter")
					status.terminalErrorLine = true
					return result
					}
				}
				else if (tokSubType == TokSubTypes.DTOH)
				{
				op.type = HWI.OP_ID_DTOH
				result.subTokens = dropParams(tok)

				if (!checkOpParamCount(result.subTokens, 1))
					{
					postError(status, tok.lineNumber, file, "incorrect parameter count for dana.dtoh(): expected 1 parameter")
					status.terminalErrorLine = true
					return result
					}
				}
				else if (tokSubType == TokSubTypes.TYPE_CONST)
				{
				//make a DanaToken out of the type's value field, then re-use parseLiteral(), with the constant's type as the xrType
				// - this is assuming that the named field actually exists as a constant in the given type...
				DanaType t = dncUtil.findType(types, tok.exStr1)
				if (t != null)
					{
					DanaTypeField tf = t.fields.findFirst(DanaTypeField.[name], new DanaTypeField(name = tok.exStr2))
					if (tf != null)
						{
						if (tf.qualifier == "constant")
							{
							Constant c = getTypeBoundConstant(t, tf.name)

							if (c != null)
								{
								if (c.deprecated)
									{
									if (c.deprecatedBy != null)
										{
										postWarning(status, tok.lineNumber, file, "type-bound constant $(t.name):$(tf.name) is deprecated, use $(c.deprecatedBy) instead")
										}
										else
										{
										postWarning(status, tok.lineNumber, file, "type-bound constant $(t.name):$(tf.name) is deprecated")
										}
									}
								
								op.type = HWI.OP_ID_GET_LITERAL_PTR
								op.returnType = c.type
								op.refValue = c.value
								}
								else
								{
								postError(status, tok.lineNumber, file, "type field '$(tok.exStr2)' is not a known constant")
								status.terminalErrorLine = true
								return result
								}
							}
							else
							{
							postError(status, tok.lineNumber, file, "type field '$(tok.exStr2)' is not a constant")
							status.terminalErrorLine = true
							return result
							}
						}
						else
						{
						postError(status, tok.lineNumber, file, "unknown type field name '$(tok.exStr2)'")
						status.terminalErrorLine = true
						return result
						}
					}
				}
				else if (tokSubType == TokSubTypes.MEMBER)
				{
				//note: tok.exStr1 is the field name; we can't resolve the outer operation here (e.g. OP_ID_GET_PTR), and the variable index, until we know the type of the 1st param
				// - we therefore leave things as OP_ID_MEMBER_ACCESS until we've parsed the first param, then come back to it

				op.type = HWI.OP_ID_MEMBER_ACCESS
				op.variableName = tok.exStr2
				}
				else if (tokSubType == TokSubTypes.ARRAY_INDEX)
				{
				op.type = HWI.OP_ID_GET_INDEX_P

				//the 2nd param is packed in a PARAM token, which we need to remove
				result.subTokens = clone tok.subTokens
				result.subTokens.next = tok.subTokens.next.subTokens

				if (result.subTokens.next == null)
					{
					postError(status, tok.lineNumber, file, "expected index value in array index operation")
					status.terminalErrorLine = true
					return result
					}
				}
			}
			else if (tok.type == DanaToken.VARIABLE)
			{
			if (tok.token == "null")
				{
				op.type = HWI.OP_ID_NULL
				op.returnType = dncUtil.findType(types, "")
				insertPR(function, op)
				return result
				}

			if (tok.token.startsWith(":p"))
				{
				DanaType functionType = prepParamStack.peek()
				String parts[] = tok.token.explode(":")
				int vIndex = parts[1].string.intFromString()
				op.variableIndex = vIndex + 1
				char typeName[] = functionType.fields[op.variableIndex].type
				op.returnType = dncUtil.findType(types, typeName)

				if (isRefType(op.returnType))
					{
					op.type = HWI.OP_ID_GET_PTR_HND
					}
					else
					{
					op.type = HWI.OP_ID_GET_PTR
					}

				op.parameters = new OpToken[](new OpToken(type = HWI.OP_ID_GET_PR), new OpToken(type = HWI.OP_ID_STACK_FRAME_PARAMS, variableIndex = vIndex, lineNumber = tok.lineNumber))

				return result
				}
				else if (tok.token.startsWith(":np"))
				{
				DanaType functionType = prepParamStack.peek()
				String parts[] = tok.token.explode(":")
				int vIndex = functionType.fields.findFirstIndex(DanaTypeField.[name], new DanaTypeField(name = parts[1].string)) - 1
				op.variableIndex = vIndex + 1

				if (op.variableIndex == INT_MAX)
					{
					postError(status, tok.lineNumber, file, "unknown parameter name '$(parts[1].string)'")
					status.terminalErrorFunction = true
					return result
					}
				
				char typeName[] = functionType.fields[op.variableIndex].type
				op.returnType = dncUtil.findType(types, typeName)

				if (isRefType(op.returnType))
					{
					op.type = HWI.OP_ID_GET_PTR_HND
					}
					else
					{
					op.type = HWI.OP_ID_GET_PTR
					}

				op.parameters = new OpToken[](new OpToken(type = HWI.OP_ID_GET_PR), new OpToken(type = HWI.OP_ID_STACK_FRAME_PARAMS, variableIndex = vIndex, lineNumber = tok.lineNumber))

				return result
				}

			//determine if it's a local, constant, static global, instance global, instance transfer global
			for (Scope s = scopes.getFirst(); s != null; s = scopes.getNext())
				{
				for (int i = 0; i < s.variables.arrayLength; i++)
					{
					if (s.variables[i].name == tok.token)
						{
						//if this is the first param of an "assign", we might need to use an assignable "get" form

						char typeName[] = function.variables.fields[s.variables[i].index].type
						op.returnType = dncUtil.findType(types, typeName)
						op.variableIndex = s.variables[i].index

						if (op.returnType == null)
							{
							postError(status, tok.lineNumber, file, "unknown return type for variable '$(tok.token)' / $(typeName)")
							return result
							}
						
						if (isRefType(op.returnType))
							{
							if (paramIndex == 0 && parent != null && parent.type == HWI.OP_ID_ASSIGN)
								op.type = HWI.OP_ID_GET_PTR_HND
								else
								op.type = HWI.OP_ID_GET_REF
							}
							else
							{
							op.type = HWI.OP_ID_GET_PTR
							}

						op.parameters = new OpToken[](new OpToken(type = HWI.OP_ID_GET_PR), new OpToken(type = HWI.OP_ID_LOCALS))

						return result
						}
					}
				}
			
			if (pObject != null) //null while parsing static global variables
				{
				for (int i = 0; i < pObject.instanceGlobals.fields.arrayLength; i++)
					{
					if (pObject.instanceGlobals.fields[i].name == tok.token)
						{
						char typeName[] = pObject.instanceGlobals.fields[i].type
						op.returnType = dncUtil.findType(types, typeName)
						op.variableIndex = i

						if (isRefType(op.returnType))
							{
							if (paramIndex == 0 && parent != null && parent.type == HWI.OP_ID_ASSIGN)
								op.type = HWI.OP_ID_GET_PTR_HND
								else
								op.type = HWI.OP_ID_GET_REF
							}
							else
							{
							op.type = HWI.OP_ID_GET_PTR
							}

						op.parameters = new OpToken[](new OpToken(type = HWI.OP_ID_GET_PR), new OpToken(type = HWI.OP_ID_PRIVATE_IVS))

						return result
						}
					}
				}
			
			for (int i = 0; i < status.staticGlobals.fields.arrayLength; i++)
				{
				if (status.staticGlobals.fields[i].name == tok.token)
					{
					char typeName[] = status.staticGlobals.fields[i].type
					op.returnType = dncUtil.findType(types, typeName)
					op.variableIndex = i

					if (isRefType(op.returnType))
						{
						if (paramIndex == 0 && parent != null && parent.type == HWI.OP_ID_ASSIGN)
							op.type = HWI.OP_ID_GET_PTR_HND
							else
							op.type = HWI.OP_ID_GET_REF
						}
						else
						{
						op.type = HWI.OP_ID_GET_PTR
						}

					op.parameters = new OpToken[](new OpToken(type = HWI.OP_ID_GET_PR), new OpToken(type = HWI.OP_ID_GLOBALS))

					return result
					}
				}
			
			//check transfer variables
			if (pObject != null) //null while parsing static global variables
				{
				for (int i = 0; i < pObject.transferState.fields.arrayLength; i++)
					{
					if (pObject.transferState.fields[i].name == tok.token)
						{
						char typeName[] = pObject.transferState.fields[i].type
						op.returnType = dncUtil.findType(types, typeName)
						op.variableIndex = i

						if (isRefType(op.returnType))
							{
							if (paramIndex == 0 && parent != null && parent.type == HWI.OP_ID_ASSIGN)
								op.type = HWI.OP_ID_GET_PTR_HND
								else
								op.type = HWI.OP_ID_GET_REF
							}
							else
							{
							op.type = HWI.OP_ID_GET_PTR
							}

						op.parameters = new OpToken[](new OpToken(type = HWI.OP_ID_GET_PR), new OpToken(type = HWI.OP_ID_TRANSFERS))

						return result
						}
					}
				}

			for (Constant cw = constants.getFirst(); cw != null; cw = constants.getNext())
				{
				if (cw.name == tok.token)
					{
					op.type = HWI.OP_ID_GET_LITERAL_PTR
					op.refValue = cw.value
					op.returnType = cw.type

					return result
					}
				}
			
			postError(status, tok.lineNumber, file, "unknown variable '$(tok.token)'")
			status.terminalErrorLine = true
			}
			else if (tok.type == DanaToken.LITERAL)
			{
			//appropriately handle int, dec, string, by making a byte-array representation
			op.type = HWI.OP_ID_GET_LITERAL_PTR
			op.refValue = parseLiteral(file, tok, op, xrType, types, status)

			if (op.returnType == null)
				{
				if (DEBUG_INS_GEN) out.println("null return type for '$(tok.token)'")
				}
			
			if (DEBUG_INS_GEN) out.println("literal return type: $(op.returnType.name)")
			}
			else
			{
			postError(status, tok.lineNumber, file, "unexpected token '$(tok.token)'")
			status.terminalErrorLine = true
			return result
			}
		
		return result
		}
	
	AssocOption hasAssociativeFunction(RequiredInterface ri, char fname[], DanaType forType, char returnType[], DanaType types[])
		{
		DanaType itype = dncUtil.findType(types, ri.name)

		int index = 0
		for (int i = 0; i < itype.fields.arrayLength; i++)
			{
			DanaType fieldType = dncUtil.findType(types, itype.fields[i].type)

			if (fieldType.class == DanaType.FUNCTION)
				{
				if (itype.fields[i].name == fname)
					{
					DanaType fieldTypeA = dncUtil.findType(types, fieldType.fields[0].type)
					DanaType fieldTypeB = dncUtil.findType(types, fieldType.fields[1].type)

					if (returnType == null || fieldTypeA.name == returnType)
						{
						int distance = typeUtil.getTypeDistance(fieldTypeB, forType, types)

						if (distance != INT_MAX)
							{
							return new AssocOption(ri.autoInstance, ri.name, index, distance)
							}
						}
					}
				
				index ++
				}
			}
		
		return null
		}
	
	AssocOption[] findAssociativeFunctions(char functionName[], DanaType forType, char returnType[], DanaType types[], OpParseResult status)
		{
		AssocOption options[] = new AssocOption[2]
		options[0] = new AssocOption(rank = INT_MAX)
		options[1] = new AssocOption(rank = INT_MAX)

		for (int i = 0; i < status.requiredInterfaces.arrayLength; i++)
			{
			if (status.requiredInterfaces[i].autoInstance != null)
				{
				AssocOption option = hasAssociativeFunction(status.requiredInterfaces[i], functionName, forType, returnType, types)

				if (option != null)
					{
					if (option.rank < options[0].rank)
						{
						options[0] = option
						}
						else if (option.rank < options[1].rank)
						{
						options[1] = option
						}
					}
				}
			}
		
		if (options[0].rank != INT_MAX)
			return options
			else
			return null
		}
	
	bool arrayUpgradable(DanaType array, DanaType var, DanaType types[])
		{
		if (var.class == DanaType.NULL)
			{
			return false
			}
		
		if (var.class != DanaType.ARRAY)
			{
			DanaType fieldType = dncUtil.findType(types, array.fields[0].type)
			if (fieldType === var)
				{
				return true
				}
				else if (fieldType.class == DanaType.INTEGER && var.class == DanaType.INTEGER)
				{
				//this always works, for any sizes
				return true
				}
				else if (fieldType.class == DanaType.DECIMAL && var.class == DanaType.DECIMAL)
				{
				//this always works, for any sizes
				return true
				}
				else if (fieldType.class == DanaType.DATA && var.class == DanaType.DATA)
				{
				if (typeUtil.isSuperTypeOf(fieldType, var, types))
					{
					return true
					}
				}
				else
				{
				return false
				}
			}
			else if (var.storageSize != 0) //a fixed-length array
			{
			DanaType fieldTypeA = dncUtil.findType(types, array.fields[0].type)
			DanaType fieldTypeB = dncUtil.findType(types, var.fields[0].type)
			if (fieldTypeA === fieldTypeB)
				{
				return true
				}
				else if (fieldTypeA.class == DanaType.INTEGER && fieldTypeB.class == DanaType.INTEGER)
				{
				//this always works, for any sizes
				return true
				}
				else
				{
				return false
				}
			}

		return false
		}
	
	//this function applies updates that may be necessary once a child operation has been processed
	// - this may include deriving a return type, or changing the instruction type to reflect the child instruction
	OpToken updateOperation(char file[], OpFunction function, ProvidedObject pObject, OpToken op, OpToken parent, DanaToken parentParamList, int childIndex, DanaType types[], Stack prepParamStack, OpParseResultX status)
		{
		if (childIndex == 0)
			{
			if (op.type == HWI.OP_ID_ASSIGN)
				{
				if (op.parameters[0].type == HWI.OP_ID_GET_FIELD)
					{
					op.parameters[0].type = HWI.OP_ID_GET_FIELD_TO_ASSIGN
					op.type = HWI.OP_ID_VASSIGN
					op.parameters = new OpToken[](new OpToken(type = HWI.OP_ID_GET_PR), op.parameters)
					op.returnType = dncUtil.findType(types, "")
					}
					else if (op.parameters[0].type == HWI.OP_ID_VGET_INDEX)
					{
					op.parameters[0].type = HWI.OP_ID_GET_INDEX_TO_ASSIGN
					op.type = HWI.OP_ID_VASSIGN
					op.parameters = new OpToken[](new OpToken(type = HWI.OP_ID_GET_PR), op.parameters)
					op.returnType = dncUtil.findType(types, "")
					}
					else
					{
					op.returnType = op.parameters[0].returnType

					if (isRefType(op.returnType))
						{
						op.type = HWI.OP_ID_ASSIGN_POINTER
						op.parameters = new OpToken[](new OpToken(type = HWI.OP_ID_GET_PR), op.parameters)
						}
						else if (op.returnType.class == DanaType.ARRAY && op.returnType.storageSize != 0)
						{
						op.type = HWI.OP_ID_ASSIGN_ARRAY
						}
					}
				}
				else if (op.type == HWI.OP_ID_CLONE_DATA)
				{
				op.returnType = op.parameters[1].returnType
				
				if (op.returnType.class == DanaType.ARRAY)
					{
					op.type = HWI.OP_ID_CLONE_ARRAY
					}
				}
				else if (op.type == HWI.OP_ID_RCLONE)
				{
				op.returnType = op.parameters[1].returnType
				}
				else if (op.type == HWI.OP_ID_SUB_ARRAY)
				{
				op.returnType = op.parameters[1].returnType
				}
				else if (op.type == HWI.OP_ID_STACK_FRAME_INIT_OBJECT)
				{
				op.returnType = op.parameters[0].returnType

				DanaType objectType = op.returnType

				if (objectType.class == DanaType.INTERFACE)
					{
					//regular object call
					DanaTypeField functionField = getFunctionField(objectType, op.functionName, types)
					op.functionIndex = getFunctionIndex(objectType, op.functionName, types)

					if (op.functionIndex == INT_MAX)
						{
						postError(status, op.lineNumber, file, "function named $(op.functionName)() not found in type $(objectType.name)")
						status.terminalErrorLine = true
						return null
						}

					DanaType functionType = dncUtil.findType(types, functionField.type)
					prepParamStack.push(functionType)

					if (functionField.deprecated)
						{
						if (functionField.deprecatedBy != null)
							{
							postWarning(status, op.lineNumber, file, "interface function $(op.returnType.name):$(op.functionName)() is deprecated, use $(functionField.deprecatedBy)() instead")
							}
							else
							{
							postWarning(status, op.lineNumber, file, "interface function $(op.returnType.name):$(op.functionName)() is deprecated")
							}
						}
					
					ParamInfo pinfo = getObjectCallParamInfo(objectType, op.functionName, types)
					int coreParams = 0
					int totalParams = 0

					//now check, using :p and :np prefixes, for the core/total count match
					DanaToken w = parentParamList.next
					while (w != null)
						{
						if (w.subTokens.token.startsWith(":p"))
							{
							coreParams ++
							}
						
						totalParams ++
						
						w = w.next
						}
					
					if (coreParams < pinfo.coreParams)
						{
						postError(status, op.lineNumber, file, "insufficient non-optional parameters for function of $(objectType.name):$(op.functionName)(): expected $(pinfo.coreParams)")
						}
					
					if (totalParams > (pinfo.coreParams + pinfo.optParams.arrayLength))
						{
						postError(status, op.lineNumber, file, "too many parameters for function $(objectType.name):$(op.functionName)(): expected maximum of $(pinfo.coreParams + pinfo.optParams.arrayLength)")
						status.terminalErrorFunction = true
						}
					}
					else
					{
					//check for an associative function
					DanaType rR = op.parameters[0].returnType

					AssocOption options[] = findAssociativeFunctions(op.functionName, rR, null, types, status)

					if (options != null)
						{
						int msfIndex = options[0].functionIndex

						op.functionIndex = msfIndex

						OpToken getObj = new OpToken(HWI.OP_ID_GET_REF, variableIndex = getGlobalInstanceVariableIndex(pObject, options[0].instanceName))
						getObj.parameters = new OpToken[](new OpToken(HWI.OP_ID_GET_PR), new OpToken(HWI.OP_ID_PRIVATE_IVS))

						objectType = dncUtil.findType(types, options[0].interfaceType)
						DanaTypeField functionField = getFunctionField(objectType, op.functionName, types)

						getObj.returnType = objectType
						op.returnType = objectType

						DanaType functionType = dncUtil.findType(types, functionField.type)
						prepParamStack.push(functionType)

						OpToken assignOp = new OpToken()
						OpToken paramOp = new OpToken()

						paramOp.variableIndex = 1
						paramOp.returnType = objectType

						if (isRefType(rR))
							{
							assignOp.type = HWI.OP_ID_ASSIGN_POINTER
							assignOp.parameters = new OpToken(HWI.OP_ID_GET_PR)

							paramOp.type = HWI.OP_ID_GET_PTR_HND
							}
							else
							{
							assignOp.type = HWI.OP_ID_ASSIGN

							paramOp.type = HWI.OP_ID_GET_PTR
							}
						
						paramOp.parameters = new OpToken[](new OpToken(type = HWI.OP_ID_GET_PR), new OpToken(type = HWI.OP_ID_STACK_FRAME_PARAMS, lineNumber = op.lineNumber))

						assignOp.parameters = new OpToken[](assignOp.parameters, paramOp)
						assignOp.parameters = new OpToken[](assignOp.parameters, op.parameters[0])

						assignOp.lineNumber = op.parameters[0].lineNumber

						//parent.parameters = new OpToken[](parent.parameters, assignOp)

						op.parameters[0] = getObj

						if (functionField.deprecated)
							{
							if (functionField.deprecatedBy != null)
								{
								postWarning(status, op.lineNumber, file, "interface function $(objectType.name):$(op.functionName)() is deprecated, use $(functionField.deprecatedBy)() instead")
								}
								else
								{
								postWarning(status, op.lineNumber, file, "interface function $(objectType.name):$(op.functionName)() is deprecated")
								}
							}
						
						ParamInfo pinfo = getObjectCallParamInfo(objectType, op.functionName, types)
						int coreParams = 1
						int totalParams = 1

						//now check, using :p and :np prefixes, for the core/total count match
						DanaToken w = parentParamList.next
						while (w != null)
							{
							if (w.subTokens.token.startsWith(":p"))
								{
								coreParams ++
								}
							
							totalParams ++
							
							w = w.next
							}
						
						if (coreParams < pinfo.coreParams)
							{
							postError(status, op.lineNumber, file, "insufficient non-optional parameters for function of $(objectType.name):$(op.functionName)(): expected $(pinfo.coreParams)")
							}
						
						if (totalParams > (pinfo.coreParams + pinfo.optParams.arrayLength))
							{
							postError(status, op.lineNumber, file, "too many parameters for function $(objectType.name):$(op.functionName)(): expected maximum of $(pinfo.coreParams + pinfo.optParams.arrayLength)")
							status.terminalErrorFunction = true
							}

						return assignOp
						}
						else
						{
						postError(status, op.lineNumber, file, "no associative function named $(op.functionName)() found for type '$(rR.name)'")
						status.terminalErrorLine = true
						}
					}
				}
				else if (op.type == HWI.OP_ID_MEMBER_ACCESS)
				{
				DanaType dataType = op.parameters[0].returnType

				if (dataType.class == DanaType.DATA)
					{
					op.variableIndex = typeUtil.getDataFieldIndex(dataType, op.variableName)

					if (op.variableIndex == INT_MAX)
						{
						postError(status, op.lineNumber, file, "no field named '$(op.variableName)' found on type '$(dataType.name)'")
						status.terminalErrorLine = true
						return null
						}

					DanaTypeField field = typeUtil.getDataField(dataType, op.variableName)//dataType.fields[op.variableIndex]
					DanaType fieldType = dncUtil.findType(types, field.type)

					op.returnType = fieldType

					insertPR(function, op)
					}
					else if (dataType.class == DanaType.INTERFACE)
					{
					//TODO: check if I implement a provided object of this type; if so, does that implementation have a private global instance variable with this name?

					ProvidedObject qp = null

					for (int i = 0; i < status.providedObjects.arrayLength; i++)
						{
						if (status.providedObjects[i].mainInterface == dataType.name)
							{
							qp = status.providedObjects[i]
							break
							}
						}
					
					if (qp != null)
						{
						int index = INT_MAX
						for (int i = 0; i < qp.instanceGlobals.fields.arrayLength; i++)
							{
							if (qp.instanceGlobals.fields[i].name == op.variableName)
								{
								index = i
								break
								}
							}
						
						if (index != INT_MAX)
							{
							char typeName[] = qp.instanceGlobals.fields[index].type
							op.returnType = dncUtil.findType(types, typeName)
							op.variableIndex = index

							if (isRefType(op.returnType))
								{
								if (childIndex == 0 && parent != null && parent.type == HWI.OP_ID_ASSIGN)
									op.type = HWI.OP_ID_GET_PTR_HND
									else
									op.type = HWI.OP_ID_GET_REF
								}
								else
								{
								op.type = HWI.OP_ID_GET_PTR
								}

							op.parameters = new OpToken[](new OpToken(type = HWI.OP_ID_GET_PR), new OpToken(type = HWI.OP_ID_PRIVATE_INTER_IVS, parameters = new OpToken[](new OpToken(type = HWI.OP_ID_GET_PR), op.parameters[0])))
							}
							else
							{
							postError(status, op.lineNumber, file, "no field named '$(op.variableName)' found on type '$(dataType.name)'")
							status.terminalErrorLine = true
							return null
							}
						}
						else
						{
						postError(status, op.lineNumber, file, "no field named '$(op.variableName)' found on type '$(dataType.name)'")
						status.terminalErrorLine = true
						return null
						}
					}
					else
					{
					postError(status, op.lineNumber, file, "no field named '$(op.variableName)' found on type '$(dataType.name)'")
					status.terminalErrorLine = true
					return null
					}
				}
				else if (op.type == HWI.OP_ID_GET_INDEX_P)
				{
				DanaType arrayType = op.parameters[0].returnType

				//if (arrayType.class != DanaType.ARRAY)
				//	{
				//	out.println("[CSM-error] arrayType is not an array for get-index on line $(op.lineNumber)")
				//	}

				if (arrayType.class != DanaType.NULL && arrayType.class != DanaType.VAR)
					{
					if (arrayType.fields.arrayLength == 0)
						{
						postError(status, op.lineNumber, file, "type $(arrayType.name) is not an array and cannot be indexed")
						status.terminalErrorLine = true
						return null
						}

					DanaTypeField field = arrayType.fields[0]
				
					insertPR(function, op)

					DanaType cellType = dncUtil.findType(types, field.type)

					if (isRefType(cellType))
						{
						op.type = HWI.OP_ID_GET_INDEX_R
						}
					
					op.returnType = cellType
					}
					else
					{
					op.type = HWI.OP_ID_VGET_INDEX
					op.returnType = arrayType
					insertPR(function, op)
					}
				}
				else if (op.type == HWI.OP_ID_ASSIGN_DATA_POINTER)
				{
				//upgrade non-array RHS operands to arrays, if they would otherwise be a type-match

				if (DEBUG_INS_GEN) out.println("[check array-p upgrade, line $(op.lineNumber): $(op.parameters[1].returnType.name): $(op.parameters[1].returnType.class).$(op.parameters[1].returnType.storageSize)]")

				if (op.returnType.class == DanaType.ARRAY && op.returnType.storageSize == 0
					&& (op.parameters[1].returnType.class != DanaType.ARRAY || op.parameters[1].returnType.storageSize != 0))
					{
					if (arrayUpgradable(op.returnType, op.parameters[1].returnType, types))
						{
						OpToken newOp = new OpToken(type = HWI.OP_ID_NEW_ARRAY_INIT)
						newOp.returnType = op.returnType
						newOp.refType = op.returnType
						newOp.lineNumber = op.lineNumber
						newOp.parameters = new OpToken[](new OpToken(type = HWI.OP_ID_GET_PR), op.parameters[1])
						op.parameters[1] = newOp
						}
					}
				}
				else if (op.type == HWI.OP_ID_ASSIGN_DATA_ARRAY)
				{
				//upgrade non-array RHS operands to arrays, if they would otherwise be a type-match

				if (DEBUG_INS_GEN) out.println("[check array-pa upgrade, line $(op.lineNumber): $(op.parameters[1].returnType.name): $(op.parameters[1].returnType.class).$(op.parameters[1].returnType.storageSize)]")

				if (op.parameters[1].returnType.class != DanaType.ARRAY || op.parameters[1].returnType.storageSize != 0)
					{
					if (arrayUpgradable(op.returnType, op.parameters[1].returnType, types))
						{
						OpToken newOp = new OpToken(type = HWI.OP_ID_NEW_ARRAY_INIT)
						newOp.returnType = op.returnType
						newOp.refType = op.returnType
						newOp.lineNumber = op.lineNumber
						newOp.parameters = new OpToken[](new OpToken(type = HWI.OP_ID_GET_PR), op.parameters[1])
						op.parameters[1] = newOp
						}
					}
				}
				else if (op.type == HWI.OP_ID_INTADD)
				{
				DanaType typeA = op.parameters[0].returnType

				if (typeA.class == DanaType.DECIMAL)
					{
					op.type = HWI.OP_ID_DEC_ADD
					}
				}
				else if (op.type == HWI.OP_ID_INTSUB)
				{
				DanaType typeA = op.parameters[0].returnType

				if (typeA.class == DanaType.DECIMAL)
					{
					op.type = HWI.OP_ID_DEC_SUB
					}
				}
				else if (op.type == HWI.OP_ID_INTMUL)
				{
				DanaType typeA = op.parameters[0].returnType

				if (typeA.class == DanaType.DECIMAL)
					{
					op.type = HWI.OP_ID_DEC_MUL
					}
				}
				else if (op.type == HWI.OP_ID_INTDIV)
				{
				DanaType typeA = op.parameters[0].returnType

				if (typeA.class == DanaType.DECIMAL)
					{
					op.type = HWI.OP_ID_DEC_DIV
					}
				}
				else if (op.type == HWI.OP_ID_HTOD || op.type == HWI.OP_ID_DTOH)
				{
				DanaType typeA = op.parameters[0].returnType
				addSR(function, op, typeA.name, typeA)
				op.returnType = typeA
				}
				else if (op.type == HWI.OP_ID_RETURN || op.type == HWI.OP_ID_RETURN_ORCHK)
				{
				//upgrade non-array operands to arrays
				if (op.returnType.class == DanaType.ARRAY && op.returnType.storageSize == 0
					&& (op.parameters[0].returnType.class != DanaType.ARRAY || op.parameters[0].returnType.storageSize != 0))
					{
					if (arrayUpgradable(op.returnType, op.parameters[0].returnType, types))
						{
						OpToken newOp = new OpToken(type = HWI.OP_ID_NEW_ARRAY_INIT)
						newOp.returnType = op.returnType
						newOp.refType = op.returnType
						newOp.lineNumber = op.lineNumber
						newOp.parameters = new OpToken[](new OpToken(type = HWI.OP_ID_GET_PR), op.parameters[0])
						op.parameters[0] = newOp
						}
					}
				}
				else if (op.type == HWI.OP_ID_EMIT_EVENT_ST)
				{
				DanaType typeA = op.parameters[0].returnType

				if (typeMayIncludeObjects(typeA, types))
					{
					op.type = HWI.OP_ID_EMIT_EVENT_ST_ORCHK
					}
				}
			}
			else if (childIndex == 1)
			{
			if (op.type == HWI.OP_ID_ASSIGN_POINTER)
				{
				//upgrade non-array RHS operands to arrays

				if (DEBUG_INS_GEN) out.println("[check array upgrade ln $(op.lineNumber): $(op.returnType.name) vs $(op.parameters[2].returnType.name): $(op.parameters[2].returnType.class).$(op.parameters[2].returnType.storageSize)]")

				if (op.returnType.class == DanaType.ARRAY && op.returnType.storageSize == 0
					&& (op.parameters[2].returnType.class != DanaType.ARRAY || op.parameters[2].returnType.storageSize != 0))
					{
					if (arrayUpgradable(op.returnType, op.parameters[2].returnType, types))
						{
						OpToken newOp = new OpToken(type = HWI.OP_ID_NEW_ARRAY_INIT)
						newOp.returnType = op.returnType
						newOp.refType = op.returnType
						newOp.lineNumber = op.lineNumber
						newOp.parameters = new OpToken[](new OpToken(type = HWI.OP_ID_GET_PR), op.parameters[2])
						op.parameters[2] = newOp
						}
					}
				}
			}
		
		return null
		}
	
	int getGlobalInstanceVariableIndex(ProvidedObject pObject, char vname[])
		{
		for (int i = 0; i < pObject.instanceGlobals.fields.arrayLength; i++)
			{
			if (pObject.instanceGlobals.fields[i].name == vname)
				{
				return i
				}
			}

		return 0
		}
	
	bool isCharStringType(DanaType rR, DanaType types[])
		{
		if (rR.name == "char" || rR.name == "char[]")
			{
			return true
			}
		
		if (rR.class == DanaType.ARRAY)
			{
			DanaType fieldTypeA = dncUtil.findType(types, rR.fields[0].type)

			if (fieldTypeA.name == "char")
				{
				return true
				}
			}
		
		return false
		}
	
	int getEventCount(DanaType objectType, DanaType types[], char file[], int lineNumber, OpParseResult status)
		{
		int count = 0
		for (int i = 0; i < objectType.fields.arrayLength; i++)
			{
			DanaType fieldTypeA = dncUtil.findType(types, objectType.fields[i].type)
			if (fieldTypeA.class == DanaType.EVENT)
				{
				count ++
				}
			}
		
		if (count == 0)
			{
			postError(status, lineNumber, file, "type '$(objectType.name)' has no events to listen to")
			}
		
		return count
		}
	
	OpToken makeDecToInt(OpFunction function, OpToken forParam, DanaType types[])
		{
		DanaType rB = forParam.returnType

		OpToken newOp = new OpToken()
		newOp.type = HWI.OP_ID_INT_FROM_DEC
		DanaType intA = getIntTypeFor(types, rB.storageSize)
		DanaType intB = getIntTypeFor(types, rB.storageSize / 2)
		newOp.returnType = intB
		insertSR(function, newOp, intA.name, intA)
		insertSR(function, newOp, intB.name, intB)
		newOp.parameters = new OpToken[](newOp.parameters, forParam)

		return newOp
		}
	
	OpToken makeIntToDec(OpFunction function, OpToken forParam, DanaType types[])
		{
		DanaType rB = forParam.returnType

		OpToken newOp = new OpToken()
		newOp.type = HWI.OP_ID_DEC_FROM_INT
		DanaType decA = getDecTypeFor(types, rB.storageSize * 2)
		newOp.returnType = decA
		insertSR(function, newOp, decA.name, decA)
		newOp.parameters = new OpToken[](newOp.parameters, forParam)

		return newOp
		}
	
	OpToken makeDecRescale(OpFunction function, OpToken forParam, DanaType toScale, DanaType types[])
		{
		DanaType rB = forParam.returnType

		OpToken newOp = new OpToken()
		newOp.type = HWI.OP_ID_DEC_RESCALE
		newOp.returnType = toScale
		
		if (rB.storageSize > toScale.storageSize)
			{
			if (DEBUG_DECIMAL_SCALING) out.println(" - scaling DOWN")
			insertSR(function, newOp, rB.name, rB)
			insertSR(function, newOp, rB.name, rB)
			insertSR(function, newOp, toScale.name, toScale)
			}
			else
			{
			if (DEBUG_DECIMAL_SCALING) out.println(" - scaling UP")
			insertSR(function, newOp, toScale.name, toScale)
			insertSR(function, newOp, toScale.name, toScale)
			insertSR(function, newOp, toScale.name, toScale)
			}
		newOp.parameters = new OpToken[](newOp.parameters, forParam)

		return newOp
		}
	
	void finishOperation(char file[], OpFunction function, List instructions, ProvidedObject pObject, OpToken parent, int pIndex, ApplyOpResult aor, DanaType types[], Stack prepParamStack, List scopes, IFXHolder ifxItem, ResetVariables rvars, OpParseResultX status)
		{
		if (aor.instruction.type == HWI.OP_ID_ASSIGN)
			{
			//deal with int/dec mismatches between LHS and RHS, inserting suitable conversion instructions

			DanaType rA = aor.instruction.parameters[0].returnType
			DanaType rB = aor.instruction.parameters[1].returnType

			if (rA.class == DanaType.INTEGER && rB.class == DanaType.DECIMAL)
				{
				aor.instruction.parameters[1] = makeDecToInt(function, aor.instruction.parameters[1], types)
				}
				else if (rA.class == DanaType.DECIMAL && (rB.class == DanaType.INTEGER || rB.class == DanaType.DECIMAL))
				{
				if (DEBUG_DECIMAL_SCALING) out.println("assign on line $(aor.instruction.lineNumber) has inputs at $(rA.class).$(rA.storageSize) and $(rB.class).$(rB.storageSize)")

				if (rB.class == DanaType.INTEGER)
					{
					aor.instruction.parameters[1] = makeIntToDec(function, aor.instruction.parameters[1], types)
					rB = aor.instruction.parameters[1].returnType
					if (DEBUG_DECIMAL_SCALING) out.println(" - int to dec")
					}
				
				if (rA.storageSize != rB.storageSize)
					{
					aor.instruction.parameters[1] = makeDecRescale(function, aor.instruction.parameters[1], rA, types)
					rA = aor.instruction.parameters[1].returnType
					if (DEBUG_DECIMAL_SCALING) out.println(" - scaling param 2")
					}
				}
			}
			else if (aor.instruction.type == HWI.OP_ID_MEMBER_ACCESS)
			{
			if (isRefType(aor.instruction.returnType))
				{
				//handle LHS of assign case, where parent op is an assignment, for PTR_HND instructions
				OpToken newOp = new OpToken()
				if (parent != null && parent.type == HWI.OP_ID_ASSIGN)
					newOp.type = HWI.OP_ID_GET_PTR_HND
					else
					newOp.type = HWI.OP_ID_GET_REF
				newOp.variableIndex = aor.instruction.variableIndex
				newOp.returnType = aor.instruction.returnType
				newOp.lineNumber = aor.instruction.lineNumber
				insertPR(function, newOp)

				newOp.parameters = new OpToken[](newOp.parameters, aor.instruction)
				aor.instruction = newOp
				}
				else
				{
				OpToken newOp = new OpToken()
				newOp.type = HWI.OP_ID_GET_PTR
				newOp.variableIndex = aor.instruction.variableIndex
				newOp.returnType = aor.instruction.returnType
				insertPR(function, newOp)

				newOp.parameters = new OpToken[](newOp.parameters, aor.instruction)
				aor.instruction = newOp
				}
			}
			else if (aor.instruction.type == HWI.OP_ID_GET_INDEX_P || aor.instruction.type == HWI.OP_ID_GET_INDEX_R)
			{
			//are we the first operand of an assignment?
			if (parent != null && parent.type == HWI.OP_ID_ASSIGN && pIndex == 0)
				{
				//is our array a ref-style one? (otherwise it's a fixed-size array, which are always accessed by get_ptr-style instructions)
				if (aor.instruction.parameters[1].returnType.storageSize == 0)
					{
					aor.instruction.type = HWI.OP_ID_GET_INDEX_TO_ASSIGN
					}
				}
			
			aor.instruction.returnType = dncUtil.findType(types, aor.instruction.parameters[1].returnType.fields[0].type)
			}
			else if (aor.instruction.type == HWI.OP_ID_STACK_FRAME_LAUNCH_P)
			{
			DanaType functionType = prepParamStack.pop()
			if (functionType == null) out.println("[CSM-error] function type null on line $(aor.instruction.lineNumber)")
			aor.instruction.returnType = dncUtil.findType(types, functionType.fields[0].type)

			//check if it's an object call, and if so whether the object is a native library one
			bool nativeCall = false
			if (aor.instruction.parameters[0].type == HWI.OP_ID_STACK_FRAME_INIT_OBJECT)
				{
				DanaType objectType = aor.instruction.parameters[0].parameters[0].returnType
				RequiredInterface rq = status.requiredInterfaces.findFirst(RequiredInterface.[name], new RequiredInterface(name = objectType.name))
				if (rq != null && rq.isNative)
					{
					nativeCall = true
					}
				}

			if (isRefType(aor.instruction.returnType))
				{
				//convert to _R
				aor.instruction.type = HWI.OP_ID_STACK_FRAME_LAUNCH_R
				insertPR(function, aor.instruction)
				}
				else
				{
				insertSR(function, aor.instruction, aor.instruction.returnType.name, aor.instruction.returnType)
				}
			
			if (nativeCall)
				{
				if (aor.instruction.type == HWI.OP_ID_STACK_FRAME_LAUNCH_P)
					{
					aor.instruction.type = HWI.OP_ID_STACK_FRAME_LAUNCH_LIB_P
					}
					else if (aor.instruction.type == HWI.OP_ID_STACK_FRAME_LAUNCH_R)
					{
					aor.instruction.type = HWI.OP_ID_STACK_FRAME_LAUNCH_LIB_R
					}
				}
			}
			else if (aor.instruction.type == HWI.OP_ID_EQUAL)
			{
			DanaType rA = aor.instruction.parameters[1].returnType
			DanaType rB = aor.instruction.parameters[2].returnType
			
			if (rA.class == DanaType.NULL || rB.class == DanaType.NULL)
				{
				aor.instruction.type = HWI.OP_ID_EQUAL_POINTER
				}
				else if (aor.instruction.parameters[1].type == HWI.OP_ID_GET_FIELD || aor.instruction.parameters[2].type == HWI.OP_ID_GET_FIELD || aor.instruction.parameters[1].type == HWI.OP_ID_VGET_INDEX || aor.instruction.parameters[2].type == HWI.OP_ID_VGET_INDEX)
				{
				aor.instruction.type = HWI.OP_ID_VEQUAL
				}
				else
				{
				if (rA.class == DanaType.ARRAY || rB.class == DanaType.ARRAY)
					{
					aor.instruction.type = HWI.OP_ID_EQUAL_ARRAY
					}
					else if (rA.class == DanaType.DATA)
					{
					aor.instruction.type = HWI.OP_ID_EQUAL_RECORD
					}
					else if (rA.class == DanaType.INTERFACE)
					{
					//here we need a more intensive conversion, to "a.equals(b)"

					OpToken newOp = new OpToken(HWI.OP_ID_STACK_FRAME_LAUNCH_P)
					newOp.returnType = dncUtil.findType(types, "bool")
					newOp.lineNumber = aor.instruction.lineNumber
					newOp.parameters = new OpToken[](aor.instruction.parameters[0], new OpToken(HWI.OP_ID_STACK_FRAME_INIT_OBJECT, functionIndex = OBJECT_EQUAL_INDEX, returnType = rA))
					newOp.parameters[1].parameters = aor.instruction.parameters[1]

					//add the parameter-assignment instruction
					OpToken assignOp = new OpToken()
					OpToken paramOp = new OpToken()
					assignOp.lineNumber = aor.instruction.parameters[1].lineNumber

					paramOp.variableIndex = 1

					assignOp.type = HWI.OP_ID_ASSIGN_POINTER
					assignOp.parameters = new OpToken(HWI.OP_ID_GET_PR)

					paramOp.type = HWI.OP_ID_GET_PTR_HND
					paramOp.returnType = rA
					
					paramOp.parameters = new OpToken[](new OpToken(type = HWI.OP_ID_GET_PR), new OpToken(type = HWI.OP_ID_STACK_FRAME_PARAMS, lineNumber = aor.instruction.lineNumber))

					assignOp.parameters = new OpToken[](assignOp.parameters, paramOp)
					assignOp.parameters = new OpToken[](assignOp.parameters, aor.instruction.parameters[2])

					newOp.parameters = new OpToken[](newOp.parameters, assignOp)

					aor.instruction = newOp
					}
					else
					{
					//int/dec compatibility conversions
					if (rA.class == DanaType.DECIMAL && (rB.class == DanaType.INTEGER || rB.class == DanaType.DECIMAL))
						{
						if (DEBUG_DECIMAL_SCALING) out.println("equal on line $(aor.instruction.lineNumber) has inputs at $(rA.class).$(rA.storageSize) and $(rB.class).$(rB.storageSize)")

						if (rB.class == DanaType.INTEGER)
							{
							aor.instruction.parameters[2] = makeIntToDec(function, aor.instruction.parameters[2], types)
							rB = aor.instruction.parameters[2].returnType
							}
						
						if (rB.storageSize < rA.storageSize)
							{
							aor.instruction.parameters[2] = makeDecRescale(function, aor.instruction.parameters[2], rA, types)
							rB = aor.instruction.parameters[2].returnType
							if (DEBUG_DECIMAL_SCALING) out.println(" - scaling param 2 up")
							}
							else if (rA.storageSize < rB.storageSize)
							{
							aor.instruction.parameters[1] = makeDecRescale(function, aor.instruction.parameters[1], rB, types)
							rA = aor.instruction.parameters[1].returnType
							if (DEBUG_DECIMAL_SCALING) out.println(" - scaling param 1 up")
							}
						}
						else if (rA.class == DanaType.INTEGER && rB.class == DanaType.DECIMAL)
						{
						aor.instruction.parameters[2] = makeDecToInt(function, aor.instruction.parameters[2], types)
						}
					}
				}
			}
			else if (aor.instruction.type == HWI.OP_ID_GREATER_THAN)
			{
			if (aor.instruction.parameters[1].type == HWI.OP_ID_GET_FIELD || aor.instruction.parameters[2].type == HWI.OP_ID_GET_FIELD)
				{
				aor.instruction.type = HWI.OP_ID_VGREATER_THAN
				}
				else
				{
				//do we need dec size matching (via rescaling)?
				DanaType rA = aor.instruction.parameters[1].returnType
				DanaType rB = aor.instruction.parameters[2].returnType

				if (rA.class == DanaType.DECIMAL && (rB.class == DanaType.INTEGER || rB.class == DanaType.DECIMAL))
					{
					if (DEBUG_DECIMAL_SCALING) out.println("greater-than on line $(aor.instruction.lineNumber) has inputs at $(rA.class).$(rA.storageSize) and $(rB.class).$(rB.storageSize)")

					if (rB.class == DanaType.INTEGER)
						{
						aor.instruction.parameters[2] = makeIntToDec(function, aor.instruction.parameters[2], types)
						rB = aor.instruction.parameters[2].returnType
						}
					
					if (rB.storageSize < rA.storageSize)
						{
						aor.instruction.parameters[2] = makeDecRescale(function, aor.instruction.parameters[2], rA, types)
						rB = aor.instruction.parameters[2].returnType
						if (DEBUG_DECIMAL_SCALING) out.println(" - scaling param 2 up")
						}
					}
				}
			}
			else if (aor.instruction.type == HWI.OP_ID_GREATER_THAN_EQ)
			{
			if (aor.instruction.parameters[1].type == HWI.OP_ID_GET_FIELD || aor.instruction.parameters[2].type == HWI.OP_ID_GET_FIELD)
				{
				aor.instruction.type = HWI.OP_ID_VGREATER_THAN_EQ
				}
			}
			else if (aor.instruction.type == HWI.OP_ID_LNOT) //applyOperation() does an invisible conversion of !=, which can miss the equal variant check
			{
			if (aor.instruction.parameters[1].type == HWI.OP_ID_EQUAL)
				{
				OpToken opm = aor.instruction.parameters[1]

				DanaType rA = opm.parameters[1].returnType
				DanaType rB = opm.parameters[2].returnType
				
				if (rA.class == DanaType.NULL || rB.class == DanaType.NULL)
					{
					opm.type = HWI.OP_ID_EQUAL_POINTER
					}
					else if (aor.instruction.parameters[1].parameters[1].type == HWI.OP_ID_GET_FIELD || aor.instruction.parameters[1].parameters[2].type == HWI.OP_ID_GET_FIELD || aor.instruction.parameters[1].parameters[1].type == HWI.OP_ID_VGET_INDEX || aor.instruction.parameters[1].parameters[2].type == HWI.OP_ID_VGET_INDEX)
					{
					aor.instruction.parameters[1].type = HWI.OP_ID_VEQUAL
					}
					else
					{
					if (rA == null) out.println("null return type A on line $(opm.lineNumber)")
					if (rB == null) out.println("null return type B on line $(opm.lineNumber)")

					if (rA.class == DanaType.ARRAY || rB.class == DanaType.ARRAY)
						{
						opm.type = HWI.OP_ID_EQUAL_ARRAY
						}
						else if (rA.class == DanaType.DATA)
						{
						opm.type = HWI.OP_ID_EQUAL_RECORD
						}
						else if (rA.class == DanaType.INTERFACE)
						{
						//here we need a more intensive conversion, to "a.equals(b)"

						OpToken newOp = new OpToken(HWI.OP_ID_STACK_FRAME_LAUNCH_P)
						newOp.returnType = dncUtil.findType(types, "bool")
						newOp.parameters = new OpToken[](opm.parameters[0], new OpToken(HWI.OP_ID_STACK_FRAME_INIT_OBJECT, functionIndex = OBJECT_EQUAL_INDEX, returnType = rA))
						newOp.parameters[1].parameters = opm.parameters[1]

						//add the parameter-assignment instruction
						OpToken assignOp = new OpToken()
						OpToken paramOp = new OpToken()
						assignOp.lineNumber = opm.parameters[1].lineNumber

						paramOp.variableIndex = 1

						assignOp.type = HWI.OP_ID_ASSIGN_POINTER
						assignOp.parameters = new OpToken(HWI.OP_ID_GET_PR)

						paramOp.type = HWI.OP_ID_GET_PTR_HND
						paramOp.returnType = rA
						
						paramOp.parameters = new OpToken[](new OpToken(type = HWI.OP_ID_GET_PR), new OpToken(type = HWI.OP_ID_STACK_FRAME_PARAMS, lineNumber = aor.instruction.lineNumber))

						assignOp.parameters = new OpToken[](assignOp.parameters, paramOp)
						assignOp.parameters = new OpToken[](assignOp.parameters, opm.parameters[2])

						newOp.parameters = new OpToken[](newOp.parameters, assignOp)

						aor.instruction.parameters[1] = newOp
						}
						else
						{
						//int/dec compatibility conversions
						if (rA.class == DanaType.DECIMAL && (rB.class == DanaType.INTEGER || rB.class == DanaType.DECIMAL))
							{
							if (DEBUG_DECIMAL_SCALING) out.println("equal on line $(aor.instruction.lineNumber) has inputs at $(rA.class).$(rA.storageSize) and $(rB.class).$(rB.storageSize)")

							if (rB.class == DanaType.INTEGER)
								{
								opm.parameters[2] = makeIntToDec(function, opm.parameters[2], types)
								rB = opm.parameters[2].returnType
								}
							
							if (rB.storageSize < rA.storageSize)
								{
								opm.parameters[2] = makeDecRescale(function, opm.parameters[2], rA, types)
								rB = opm.parameters[2].returnType
								if (DEBUG_DECIMAL_SCALING) out.println(" - scaling param 2 up")
								}
								else if (rA.storageSize < rB.storageSize)
								{
								opm.parameters[1] = makeDecRescale(function, opm.parameters[1], rB, types)
								rA = opm.parameters[1].returnType
								if (DEBUG_DECIMAL_SCALING) out.println(" - scaling param 1 up")
								}
							}
							else if (rA.class == DanaType.INTEGER && rB.class == DanaType.DECIMAL)
							{
							opm.parameters[2] = makeDecToInt(function, opm.parameters[2], types)
							}
						}
					}
				}
			}
			else if (aor.instruction.type == HWI.OP_ID_STACK_FRAME_LAUNCH_ASYNCH)
			{
			DanaType functionType = prepParamStack.pop()
			aor.instruction.returnType = dncUtil.findType(types, "lang.Thread")
			insertPR(function, aor.instruction)
			}
			else if (aor.instruction.type == HWI.OP_ID_NEW_OBJECT_CONSTRUCT_CHK)
			{
			//insertPR(function, aor.instruction)
			prepParamStack.pop()
			}
			else if (aor.instruction.type == HWI.OP_ID_SINK_EVENT)
			{
			DanaType rA = aor.instruction.parameters[0].returnType
			aor.instruction.eventRangeHigh = getEventCount(rA, types, file, aor.instruction.lineNumber, status)
			}
			else if (aor.instruction.type == HWI.OP_ID_UNSINK_EVENT)
			{
			DanaType rA = aor.instruction.parameters[0].returnType
			aor.instruction.eventRangeHigh = getEventCount(rA, types, file, aor.instruction.lineNumber, status)
			}
			else if (aor.instruction.type == HWI.OP_ID_INTINC)
			{
			DanaType rA = aor.instruction.parameters[0].returnType
			aor.instruction.returnType = rA
			}
			else if (aor.instruction.type == HWI.OP_ID_INTDEC)
			{
			DanaType rA = aor.instruction.parameters[0].returnType
			aor.instruction.returnType = rA
			}
			else if (aor.instruction.type == HWI.OP_ID_INTADD)
			{
			DanaType xrType = null
			if (parent != null) xrType = parent.returnType

			//for our SR (and return type) sizing, we need to be 2x the larger of the two parameters, unless xrType is a match for the larger one, in which case we just use the larger size
			DanaType rR = null
			DanaType rA = aor.instruction.parameters[0].returnType
			DanaType rB = aor.instruction.parameters[1].returnType

			if (rB.class == DanaType.DECIMAL)
				{
				aor.instruction.parameters[1] = makeDecToInt(function, aor.instruction.parameters[1], types)
				rB = aor.instruction.parameters[1].returnType
				}

			if (rA.storageSize > rB.storageSize)
				rR = rA
				else
				rR = rB
			
			if (xrType != null && rR.storageSize == xrType.storageSize)
				{
				aor.instruction.returnType = xrType
				insertSR(function, aor.instruction, xrType.name, xrType)
				}
				else
				{
				//locate a type which is 2x larger than rR
				//DanaType nt = findIntTypeBySize(types, rR.storageSize * 2)
				DanaType nt = findIntTypeBySize(types, rR.storageSize)
				insertSR(function, aor.instruction, nt.name, nt)
				aor.instruction.returnType = nt
				}
			}
			else if (aor.instruction.type == HWI.OP_ID_INTMUL)
			{
			/*
			DanaType xrType = null
			if (parent != null) xrType = parent.returnType
			*/

			//for our SR (and return type) sizing, we need to be 2x the larger of the two parameters, unless xrType is a match for the larger one, in which case we just use the larger size
			DanaType rR = null
			DanaType rA = aor.instruction.parameters[0].returnType
			DanaType rB = aor.instruction.parameters[1].returnType

			if (rB.class == DanaType.DECIMAL)
				{
				aor.instruction.parameters[1] = makeDecToInt(function, aor.instruction.parameters[1], types)
				rB = aor.instruction.parameters[1].returnType
				}

			if (rA.storageSize > rB.storageSize)
				rR = rA
				else
				rR = rB
			
			//locate a type which is 2x larger than rR (the runtime instruction currently requires this behaviour)
			DanaType nt = findIntTypeBySize(types, rR.storageSize * 2) 
			insertSR(function, aor.instruction, nt.name, nt)
			aor.instruction.returnType = nt
			}
			else if (aor.instruction.type == HWI.OP_ID_INTSUB || aor.instruction.type == HWI.OP_ID_INTDIV || aor.instruction.type == HWI.OP_ID_INTMOD)
			{
			DanaType xrType = null
			if (parent != null) xrType = parent.returnType

			DanaType rR = null
			DanaType rA = aor.instruction.parameters[0].returnType
			DanaType rB = aor.instruction.parameters[1].returnType

			if (rB.class == DanaType.DECIMAL)
				{
				aor.instruction.parameters[1] = makeDecToInt(function, aor.instruction.parameters[1], types)
				rB = aor.instruction.parameters[1].returnType
				}

			if (rA.storageSize > rB.storageSize)
				rR = rA
				else
				rR = rB
			
			aor.instruction.returnType = rR
			insertSR(function, aor.instruction, rR.name, rR)
			}
			else if (aor.instruction.type == HWI.OP_ID_AND || aor.instruction.type == HWI.OP_ID_OR)
			{
			DanaType xrType = null
			if (parent != null) xrType = parent.returnType

			DanaType rR = null
			DanaType rA = aor.instruction.parameters[0].returnType
			DanaType rB = aor.instruction.parameters[1].returnType

			if (rA.storageSize > rB.storageSize)
				rR = rA
				else
				rR = rB
			
			aor.instruction.returnType = rR

			if (aor.instruction.subType == 1)
				{
				if (DEBUG_INS_GEN) out.println(" --> inserting X_IF_OP")
				//it's a logically exclusive variant, && or ||, so we need to add an "if" around the first parameter, and patch the jump location later
				OpToken newOp = new OpToken()
				if (aor.instruction.type == HWI.OP_ID_AND)
					newOp.type = HWI.OP_ID_IFX
					else
					newOp.type = HWI.OP_ID_IFXN
				newOp.parameters = new OpToken[](aor.instruction.parameters[0])
				newOp.lineNumber = aor.instruction.lineNumber
				newOp.returnType = dncUtil.findType(types, "bool")
				aor.instruction.parameters[0] = newOp

				ifxItem.info = new IFXInfo(newOp, aor.instruction, parent)

				//for the || operation we use an SR variable which is pre-initialised to 1, so that if we skip the "or" then its result is set to 1
				if (aor.instruction.type == HWI.OP_ID_AND)
					{
					int ndx = insertSR(function, aor.instruction, rR.name, rR)

					//these state-machine variables must be initialised to 0/1 at the last scope-start instruction, in case we're in a loop
					Scope scope = scopes.getIndex(scopes.getLength()-1)
					if (scope.startInstruction != null)
						{
						insertVariableInit(function, instructions, types, ndx, "bool", "0", scope.startInstruction, status)
						}
					
					rvars.vars = new ResetVariable[](rvars.vars, new ResetVariable(ndx, "bool", "0"))
					}
					else
					{
					int ndx = insertSR(function, aor.instruction, rR.name, rR)

					//these state-machine variables must be initialised to 0/1 at the last scope-start instruction, in case we're in a loop
					Scope scope = scopes.getIndex(scopes.getLength()-1)
					insertVariableInit(function, instructions, types, ndx, "bool", "1", scope.startInstruction, status)

					rvars.vars = new ResetVariable[](rvars.vars, new ResetVariable(ndx, "bool", "1"))
					}
				}
				else
				{
				insertSR(function, aor.instruction, rR.name, rR)
				}
			}
			else if (aor.instruction.type == HWI.OP_ID_DEC_ADD)
			{
			DanaType xrType = null
			if (parent != null) xrType = parent.returnType

			//for our SR (and return type) sizing, we need to be 2x the larger of the two parameters, unless xrType is a match for the larger one, in which case we just use the larger size
			DanaType rR = null
			DanaType rA = aor.instruction.parameters[0].returnType
			DanaType rB = aor.instruction.parameters[1].returnType

			if (rB.class == DanaType.INTEGER || rB.class == DanaType.DECIMAL)
				{
				if (rB.class == DanaType.INTEGER)
					{
					aor.instruction.parameters[1] = makeIntToDec(function, aor.instruction.parameters[1], types)
					rB = aor.instruction.parameters[1].returnType
					}
				
				if (DEBUG_DECIMAL_SCALING) out.println("dec-add params on line $(aor.instruction.lineNumber) at $(rA.storageSize), $(rB.storageSize)")
				
				if (rA.storageSize > rB.storageSize)
					{
					aor.instruction.parameters[1] = makeDecRescale(function, aor.instruction.parameters[1], rA, types)
					rB = aor.instruction.parameters[1].returnType
					if (DEBUG_DECIMAL_SCALING) out.println(" - scaling param 2 up")
					}
					else if (rB.storageSize > rA.storageSize)
					{
					aor.instruction.parameters[0] = makeDecRescale(function, aor.instruction.parameters[0], rB, types)
					rA = aor.instruction.parameters[0].returnType
					if (DEBUG_DECIMAL_SCALING) out.println(" - scaling param 1 up")
					}
				}
			
			insertSR(function, aor.instruction, rA.name, rA)

			aor.instruction.returnType = rA
			}
			else if (aor.instruction.type == HWI.OP_ID_DEC_SUB)
			{
			DanaType xrType = null
			if (parent != null) xrType = parent.returnType

			//for our SR (and return type) sizing, we need to be 2x the larger of the two parameters, unless xrType is a match for the larger one, in which case we just use the larger size
			DanaType rA = aor.instruction.parameters[0].returnType
			DanaType rB = aor.instruction.parameters[1].returnType

			if (rB.class == DanaType.INTEGER || rB.class == DanaType.DECIMAL)
				{
				if (rB.class == DanaType.INTEGER)
					{
					aor.instruction.parameters[1] = makeIntToDec(function, aor.instruction.parameters[1], types)
					rB = aor.instruction.parameters[1].returnType
					}

				if (DEBUG_DECIMAL_SCALING) out.println("dec-sub params on line $(aor.instruction.lineNumber) at $(rA.storageSize), $(rB.storageSize)")
				
				if (rA.storageSize > rB.storageSize)
					{
					aor.instruction.parameters[1] = makeDecRescale(function, aor.instruction.parameters[1], rA, types)
					rB = aor.instruction.parameters[1].returnType
					if (DEBUG_DECIMAL_SCALING) out.println(" - scaling param 2 up")
					}
					else if (rB.storageSize > rA.storageSize)
					{
					aor.instruction.parameters[0] = makeDecRescale(function, aor.instruction.parameters[0], rB, types)
					rA = aor.instruction.parameters[0].returnType
					if (DEBUG_DECIMAL_SCALING) out.println(" - scaling param 1 up")
					}
				}
			
			aor.instruction.returnType = rA
			insertSR(function, aor.instruction, rA.name, rA)
			}
			else if (aor.instruction.type == HWI.OP_ID_DEC_MUL)
			{
			DanaType xrType = null
			if (parent != null) xrType = parent.returnType

			//for our SR (and return type) sizing, we need to be 2x the larger of the two parameters, unless xrType is a match for the larger one, in which case we just use the larger size
			DanaType rA = aor.instruction.parameters[0].returnType
			DanaType rB = aor.instruction.parameters[1].returnType

			if (rB.class == DanaType.INTEGER || rB.class == DanaType.DECIMAL)
				{
				if (rB.class == DanaType.INTEGER)
					{
					aor.instruction.parameters[1] = makeIntToDec(function, aor.instruction.parameters[1], types)
					rB = aor.instruction.parameters[1].returnType
					}
				
				if (DEBUG_DECIMAL_SCALING) out.println("dec-mul params on line $(aor.instruction.lineNumber) at $(rA.storageSize), $(rB.storageSize)")
				
				if (rA.storageSize > rB.storageSize)
					{
					aor.instruction.parameters[1] = makeDecRescale(function, aor.instruction.parameters[1], rA, types)
					rB = aor.instruction.parameters[1].returnType
					if (DEBUG_DECIMAL_SCALING) out.println(" - scaling param 2 up")
					}
					else if (rB.storageSize > rA.storageSize)
					{
					aor.instruction.parameters[0] = makeDecRescale(function, aor.instruction.parameters[0], rB, types)
					rA = aor.instruction.parameters[0].returnType
					if (DEBUG_DECIMAL_SCALING) out.println(" - scaling param 1 up")
					}
				}

			aor.instruction.returnType = rA
			insertSR(function, aor.instruction, getDecTypeFor(types, rA.storageSize * 2).name, getDecTypeFor(types, rA.storageSize * 2))
			insertSR(function, aor.instruction, rA.name, rA)
			}
			else if (aor.instruction.type == HWI.OP_ID_DEC_DIV)
			{
			DanaType xrType = null
			if (parent != null) xrType = parent.returnType

			//for our SR (and return type) sizing, we need to be 2x the larger of the two parameters, unless xrType is a match for the larger one, in which case we just use the larger size
			DanaType rA = aor.instruction.parameters[0].returnType
			DanaType rB = aor.instruction.parameters[1].returnType

			if (DEBUG_DECIMAL_SCALING) out.println("dec-div params on line $(aor.instruction.lineNumber) at $(rA.storageSize), $(rB.storageSize)")

			if (rB.class == DanaType.INTEGER || rB.class == DanaType.DECIMAL)
				{
				if (rB.class == DanaType.INTEGER)
					{
					aor.instruction.parameters[1] = makeIntToDec(function, aor.instruction.parameters[1], types)
					rB = aor.instruction.parameters[1].returnType
					if (DEBUG_DECIMAL_SCALING) out.println(" - int-to-dec")
					}

				if (rA.storageSize > rB.storageSize)
					{
					aor.instruction.parameters[1] = makeDecRescale(function, aor.instruction.parameters[1], rA, types)
					rB = aor.instruction.parameters[1].returnType
					if (DEBUG_DECIMAL_SCALING) out.println(" - scaling param 2 up")
					}
					else if (rB.storageSize > rA.storageSize)
					{
					aor.instruction.parameters[0] = makeDecRescale(function, aor.instruction.parameters[0], rB, types)
					rA = aor.instruction.parameters[0].returnType
					if (DEBUG_DECIMAL_SCALING) out.println(" - scaling param 1 up")
					}
				}
			
			aor.instruction.returnType = rA
			insertSR(function, aor.instruction, getDecTypeFor(types, rA.storageSize * 2).name, getDecTypeFor(types, rA.storageSize * 2))
			insertSR(function, aor.instruction, rA.name, rA)
			}
			else if (aor.instruction.type == HWI.OP_ID_XOR)
			{
			DanaType xrType = null
			if (parent != null) xrType = parent.returnType

			DanaType rR = null
			DanaType rA = aor.instruction.parameters[0].returnType
			DanaType rB = aor.instruction.parameters[1].returnType

			if (rA.storageSize > rB.storageSize)
				rR = rA
				else
				rR = rB
			
			aor.instruction.returnType = rR
			insertSR(function, aor.instruction, rR.name, rR)
			}
			else if (aor.instruction.type == HWI.OP_ID_BNOT)
			{
			DanaType xrType = null
			if (parent != null) xrType = parent.returnType

			DanaType rA = aor.instruction.parameters[0].returnType

			aor.instruction.returnType = rA
			insertSR(function, aor.instruction, rA.name, rA)
			}
			else if (aor.instruction.type == HWI.OP_ID_LSHIFT || aor.instruction.type == HWI.OP_ID_RSHIFT)
			{
			DanaType xrType = null
			if (parent != null) xrType = parent.returnType

			DanaType rR = aor.instruction.parameters[0].returnType

			aor.instruction.returnType = rR
			insertSR(function, aor.instruction, rR.name, rR)
			}
			else if ((aor.instruction.type == HWI.OP_ID_NEW_ARRAY_INIT && aor.instruction.subType == 1)
					|| (aor.instruction.type == HWI.OP_ID_NEW_ARRAY_APPEND && aor.instruction.subType == 1))
			{
			//these are the "string expression" versions, which we check return-types of and consider wrapping in associative makeString() operations
			DanaType rR = aor.instruction.parameters[1].returnType

			if (rR == null)
				{
				out.println("return type of $(aor.instruction.parameters[1].type) is null")
				}
			
			if (!isCharStringType(rR, types))
				{
				AssocOption options[] = findAssociativeFunctions("makeString", rR, "char[]", types, status)

				if (options != null)
					{
					//create the object call instruction
					OpToken newOp = new OpToken(HWI.OP_ID_STACK_FRAME_LAUNCH_R)
					newOp.returnType = dncUtil.findType(types, "char[]")
					newOp.parameters = new OpToken[](new OpToken(HWI.OP_ID_GET_PR), new OpToken(HWI.OP_ID_STACK_FRAME_INIT_OBJECT, functionIndex = options[0].functionIndex))
					newOp.parameters[1].parameters = new OpToken(HWI.OP_ID_GET_REF, variableIndex = getGlobalInstanceVariableIndex(pObject, options[0].instanceName))
					newOp.parameters[1].parameters[0].parameters = new OpToken[](new OpToken(HWI.OP_ID_GET_PR), new OpToken(HWI.OP_ID_PRIVATE_IVS))

					//add the parameter-assignment instruction
					OpToken assignOp = new OpToken()
					OpToken paramOp = new OpToken()
					assignOp.lineNumber = aor.instruction.parameters[1].lineNumber

					paramOp.variableIndex = 1

					if (isRefType(rR))
						{
						assignOp.type = HWI.OP_ID_ASSIGN_POINTER
						assignOp.parameters = new OpToken(HWI.OP_ID_GET_PR)

						paramOp.type = HWI.OP_ID_GET_PTR_HND
						paramOp.returnType = rR
						}
						else
						{
						assignOp.type = HWI.OP_ID_ASSIGN

						paramOp.type = HWI.OP_ID_GET_PTR
						}
					
					paramOp.parameters = new OpToken[](new OpToken(type = HWI.OP_ID_GET_PR), new OpToken(type = HWI.OP_ID_STACK_FRAME_PARAMS, lineNumber = aor.instruction.lineNumber))

					assignOp.parameters = new OpToken[](assignOp.parameters, paramOp)
					assignOp.parameters = new OpToken[](assignOp.parameters, aor.instruction.parameters[1])

					newOp.parameters = new OpToken[](newOp.parameters, assignOp)

					aor.instruction.parameters[1] = newOp
					}
					else
					{
					postError(status, aor.instruction.lineNumber, file, "no makeString() function available for type '$(rR.name)'")
					status.terminalErrorFunction = true
					}
				}
			}
		}
	
	void incrementParameterIndices(DanaToken tw)
		{
		while (tw != null)
			{
			DanaToken pt = tw.subTokens
			if (pt.token.startsWith(":p"))
				{
				String parts[] = pt.token.explode(":")
				int vi = parts[1].string.intFromString() + 1
				pt.token = ":p:$vi"
				}

			tw = tw.next
			}
		}
	
	OpToken parseOperation(char file[], OpFunction function, bool global, List instructions, ProvidedObject pObject, OpToken parent, int pIndex, List scopes, DanaToken tree, DanaToken parentParamList, DanaType types[], Stack prepParamStack, IFXHolder ifxItem, ResetVariables rvars, OpParseResultX status)
		{
		DanaToken nxt = tree

		//pass through expressions, which are only used for syntactic disambiguation
		while (nxt.type == DanaToken.EXPRESSION)
			{
			//we need to pass through the PARAM that's nested here too
			nxt = nxt.subTokens.subTokens
			}

		//we need to do sub-operations one at a time, since sometimes the return-type of the first one informs the xreturn type of the others
		// - also, sometimes the operation's return type (like OP_RETURN) enforce return-types top-down, rather than bottom-up, so we need to support both
		// - applyOperation() does the top-down enforcement
		ApplyOpResult aor = applyOperation(file, function, global, pObject, scopes, nxt, parent, pIndex, types, prepParamStack, status)
		aor.instruction.lineNumber = nxt.lineNumber

		if (status.terminalErrorLine || status.terminalErrorScope || status.terminalErrorFunction) return null

		if (DEBUG_INS_GEN) out.println("tok ID $(nxt.type)/$(nxt.subType) converted to $(aor.instruction.type)")

		if (aor.instruction.type == 0)
			{
			out.println("[error: instruction $(nxt.type)/$(nxt.subType) ($(nxt.token)) converts to 0 on line $(nxt.lineNumber) of $file]")
			}

		//NOTE: this "paramAddend" feels a bit over-specialized (it's only used for associative function conversions)
		// - it means "add this thing to the end of your parent's param list, after you have been added"
		OpToken paramAddend = null
		IFXHolder ifxHolder = new IFXHolder()
		Stack ifxStack = new Stack()
		int i = 0
		for (DanaToken sub = aor.subTokens; sub != null; sub = sub.next)
			{
			if (DEBUG_INS_GEN) out.println("parse child $i of iid $(aor.instruction.type)")
			OpToken subOp = parseOperation(file, function, global, instructions, pObject, aor.asparent, i, scopes, sub, aor.subTokens, types, prepParamStack, ifxHolder, rvars, status)
			if (status.terminalErrorLine || status.terminalErrorScope || status.terminalErrorFunction) return null
			paramAddend = updateOperation(file, function, pObject, aor.instruction, parent, parentParamList, i, types, prepParamStack, status)

			if (subOp.type == HWI.OP_ID_STACK_FRAME_INIT_OBJECT && aor.instruction.parameters.arrayLength != 1)
				{
				//associative function signature: we update any parameter assignment ":p:" index values to be +1, to account for the auto-inserted 1st param
				incrementParameterIndices(sub.next)
				}
			
			IFXInfo xi = ifxStack.peek()
			while (xi != null)
				{
				if (xi.parentOp.type == HWI.OP_ID_AND && (aor.instruction.type != HWI.OP_ID_AND || aor.instruction.subType != 1))
					{
					ifxStack.pop()
					OpToken toRef = getFirstExecutedOp(subOp)
					xi.jumpOp.opRef = toRef

					if (DEBUG_INS_GEN) out.println("[patching JMP from $(xi.parentOp.type) from line $(xi.parentOp.lineNumber) to $(toRef.type) on line $(toRef.lineNumber), of $(subOp.type) on line $(subOp.lineNumber), while this op is $(aor.instruction.type)]")

					xi = ifxStack.peek()
					}
					else if (xi.parentOp.type == HWI.OP_ID_OR && (aor.instruction.type != HWI.OP_ID_OR || aor.instruction.subType != 1))
					{
					ifxStack.pop()
					OpToken toRef = getFirstExecutedOp(subOp)
					xi.jumpOp.opRef = toRef

					if (DEBUG_INS_GEN) out.println("[patching JMP from $(xi.parentOp.type) from line $(xi.parentOp.lineNumber) to $(toRef.type) on line $(toRef.lineNumber)]")

					xi = ifxStack.peek()
					}
					else
					{
					break
					}
				}
			
			if (ifxHolder.info != null)
				{
				ifxStack.push(ifxHolder.info)
				if (DEBUG_INS_GEN) out.println("[pushing $(ifxHolder.info.parentOp.type) from line $(ifxHolder.info.parentOp.lineNumber) onto IFX stack]")
				}
			ifxHolder.info = null

			i ++
			}
		
		if (status.terminalErrorLine || status.terminalErrorScope || status.terminalErrorFunction) return null
		
		IFXInfo xi = ifxStack.pop()
		while (xi != null)
			{
			xi.jumpOp.opRef = aor.asparent
			if (DEBUG_INS_GEN) out.println("[patching JMP from $(xi.parentOp.type) from line $(xi.parentOp.lineNumber) to $(aor.asparent.type) on line $(aor.asparent.lineNumber)]")
			xi = ifxStack.pop()
			}
		
		finishOperation(file, function, instructions, pObject, parent, pIndex, aor, types, prepParamStack, scopes, ifxItem, rvars, status)

		if (status.terminalErrorLine || status.terminalErrorScope || status.terminalErrorFunction) return null

		char msg[] = typeUtil.typeCheck(aor.instruction, types)
		if (msg != null)
			{
			postError(status, aor.instruction.lineNumber, file, msg)
			}

		if (aor.instruction.returnType == null && aor.instruction.type != HWI.OP_ID_NOP)
			{
			out.println("[CSM-warning] instruction $(aor.instruction.type) on line $(aor.instruction.lineNumber) has a null return type")
			}

		if (parent == null)
			{
			function.instructions = new OpToken[](function.instructions, aor.instruction)
			//instructions.add(aor.instruction)

			if (aor.instruction.addAbortCheck)
				{
				function.instructions = new OpToken[](function.instructions, new OpToken(HWI.OP_ID_CHECK_ABORT))
				//instructions.add(new OpToken(HWI.OP_ID_CHECK_ABORT))
				}
			}
			else
			{
			parent.parameters = new OpToken[](parent.parameters, aor.instruction)
			if (paramAddend != null)
				{
				//this is an associative function parameter addition
				parent.parameters = new OpToken[](parent.parameters, paramAddend)
				//NOTE: any other assign-to-parameters, with ":p:" index values, will now be wrong, and need a +1
				}
			}
		
		return aor.instruction
		}
	
	OpToken getFirstExecutedOp(OpToken op)
		{
		while (op.parameters != null)
			{
			op = op.parameters[0]
			}
		
		return op
		}
	
	ForParams parseFor(char file[], DanaToken params, OpParseResult status)
		{
		ForParams result = new ForParams()

		DanaToken tw = params.subTokens.subTokens

		if (tw.type == DanaToken.TOKEN && tw.token == ";")
			{
			//param1 is null
			tw = tw.next
			}
			else if (tw.type == DanaToken.TOKEN)
			{
			//param1 is something illegal
			postError(status, tw.lineNumber, file, "expected expression, got '$(tw.token)'")
			}
			else
			{
			//param1 is not null
			result.initialiser = tw

			tw = tw.next

			if (tw.type != DanaToken.TOKEN && tw.token != ";")
				{
				//something illegal
				postError(status, tw.lineNumber, file, "expected ';', got '$(tw.token)'")
				}
			
			tw = tw.next
			}
		
		if (tw == null)
			{
			postError(status, result.initialiser.lineNumber, file, "for-loop must have a termination condition expression, got ")
			}
			else if (tw.type == DanaToken.TOKEN && tw.token == ";")
			{
			//param2 is null (illegal)
			postError(status, tw.lineNumber, file, "for-loop must have a termination condition expression, got '$(tw.token)'")
			tw = tw.next
			}
			else if (tw.type == DanaToken.TOKEN)
			{
			//param2 is something illegal
			postError(status, tw.lineNumber, file, "expected expression, got '$(tw.token)'")
			}
			else
			{
			//param2 is not null
			result.condition = tw

			tw = tw.next

			if (tw == null)
				{
				//something illegal
				postError(status, result.condition.lineNumber, file, "expected ';', got ")
				}
				else if (tw.type != DanaToken.TOKEN && tw.token != ";")
				{
				//something illegal
				postError(status, tw.lineNumber, file, "expected ';', got '$(tw.token)'")
				}
			
			tw = tw.next
			}
		
		if (tw != null && tw.type != DanaToken.TOKEN)
			{
			//param3 is not null
			result.increment = tw
			tw = tw.next
			}
			else if (tw != null)
			{
			//param3 is something illegal
			postError(status, tw.lineNumber, file, "expected loop iterator expression, got '$(tw.token)'")
			}
		
		return result
		}
	
	bool localVariableExists(OpFunction function, List scopes, char name[])
		{
		for (Scope s = scopes.getFirst(); s != null; s = scopes.getNext())
			{
			for (int i = 0; i < s.variables.arrayLength; i++)
				{
				if (s.variables[i].name == name)
					{
					return true
					}
				}
			}
		
		return false
		}
	
	bool globalVariableExists(OpParseResult status, ProvidedObject pObject, char name[])
		{
		for (int i = 0; i < pObject.instanceGlobals.fields.arrayLength; i++)
			{
			if (pObject.instanceGlobals.fields[i].name == name)
				{
				return true
				}
			}
		
		return false
		}
	
	bool globalStaticVariableExists(OpParseResult status, ProvidedObject pObject, char name[])
		{
		for (int i = 0; i < status.staticGlobals.fields.arrayLength; i++)
				{
				if (status.staticGlobals.fields[i].name == name)
					{
					return true
					}
				}
		
		return false
		}
	
	void parseScope(char file[], OpFunction function, List instructions, ProvidedObject pObject, List scopes, DanaToken tree, DanaType types[], Stack cfStack, Stack loopStack, OpParseResultX status)
		{
		ResetVariables rvars = new ResetVariables()
		Stack prepParamStack = new Stack()

		List jumpList = new List()

		if (DEBUG_INS_GEN && tree != null) out.println("parse-scope in $(function.name) at line $(tree.lineNumber) (scope list length is $(scopes.getLength()))")

		DanaToken nxtOvr = null
		DanaToken nxt = tree
		while (nxt != null)
			{
			if (nxt.type == DanaToken.OPERATION && nxt.subType == TokSubTypes.FOR)
				{
				//do control flow
				// - we convert to a "while", with an extra scope to wrap the declare/assign portion; the increment portion moves to end of inner while scope
				// - the parser does not actually parse semi-colons as anything ...

				ForParams params = parseFor(file, nxt, status)

				DanaToken nwNxt = new DanaToken(DanaToken.SCOPE)
				DanaToken prime = null
				if (params.initialiser != null)
					{
					nwNxt.subTokens = clone params.initialiser
					nwNxt.subTokens.next = new DanaToken(DanaToken.OPERATION, TokSubTypes.WHILE, "while", nxt.lineNumber, subTokens = new DanaToken(DanaToken.PARAM, subTokens = clone params.condition))

					nwNxt.subTokens.next.subTokens.subTokens.next = null
					nwNxt.subTokensEnd = nwNxt.subTokens.next

					nwNxt.subTokens.next.next = rclone nxt.next
					nwNxt.subTokensEnd = nwNxt.subTokens.next.next
					nwNxt.subTokensEnd.next = null
					}
					else
					{
					nwNxt.subTokens = new DanaToken(DanaToken.OPERATION, TokSubTypes.WHILE, "while", nxt.lineNumber, subTokens = new DanaToken(DanaToken.PARAM, subTokens = clone params.condition))

					nwNxt.subTokens.subTokens.subTokens.next = null
					nwNxt.subTokensEnd = nwNxt.subTokens

					nwNxt.subTokens.next = rclone nxt.next
					nwNxt.subTokensEnd = nwNxt.subTokens.next
					nwNxt.subTokensEnd.next = null
					}

				nxtOvr = nwNxt
				nwNxt.next = nxt.next.next

				if (params.increment != null)
					{
					if (nwNxt.subTokensEnd.subTokensEnd != null)
						{
						//non-empty loop body
						nwNxt.subTokensEnd.subTokensEnd.next = params.increment
						nwNxt.subTokensEnd.subTokensEnd = nwNxt.subTokensEnd.subTokensEnd.next
						}
						else
						{
						//empty loop body
						nwNxt.subTokensEnd.subTokens = params.increment
						nwNxt.subTokensEnd.subTokensEnd = params.increment
						}
					}
				}
				else if (nxt.type == DanaToken.SCOPE)
				{
				if (DEBUG_INS_GEN) out.println("((parse scope at line $(nxt.lineNumber)))")

				ControlFrame cf = cfStack.peek()
				
				//if we have && or || in a while-loop header, we need to reset state machine variables every time round the loop
				// - this is done by && and || operations collecting their state machine variables in "rvars", which we then play out here
				if (cf != null && cf.scope === nxt && cf.cfType == ControlFrame.CF_WHILE && rvars.vars.arrayLength != 0)
					{
					//insert reset instructions
					for (int i = 0; i < rvars.vars.arrayLength; i++)
						{
						addVariableInit(function, instructions, types, rvars.vars[i].index, rvars.vars[i].type, rvars.vars[i].value, status)
						}

					rvars.vars = null
					}

				Scope scope = new Scope() 
				scopes.add(scope)

				if (function.instructions.arrayLength != 0)
				//if (instructions.getLength() != 0)
					{
					scope.startInstruction = function.instructions[function.instructions.arrayLength-1]
					//scope.startInstruction = instructions.getLast()
					}

				parseScope(file, function, instructions, pObject, scopes, nxt.subTokens, types, cfStack, loopStack, status)

				scopes.remove(scope)

				if (DEBUG_INS_GEN) out.println("parse-scope-end in $(function.name) from line $(nxt.lineNumber) (scope list length is $(scopes.getLength()))")

				//ControlFrame cf = cfStack.peek()
				if (cf != null && cf.scope === nxt)
					{
					cfStack.pop()

					if (cf.cfType == ControlFrame.CF_IF)
						{
						//if we're followed by an else or and else-if, insert a JMP instruction to jump over that
						if (nxt.next != null && nxt.next.type == DanaToken.OPERATION && (nxt.next.subType == TokSubTypes.ELSEIF || nxt.next.subType == TokSubTypes.ELSE))
							{
							OpToken jmptok = new OpToken(type = HWI.OP_ID_JMP)
							jmptok.returnType = dncUtil.findType(types, "void")
							function.instructions = new OpToken[](function.instructions, jmptok)
							//instructions.add(jmptok)
							jumpList.add(jmptok)
							}
							else if (jumpList.getLength() != 0)
							{
							//resolve all true-branch end-of-body jumps to the end of this else block
							OpToken noptok = new OpToken(type = HWI.OP_ID_NOP)
							function.instructions = new OpToken[](function.instructions, noptok)
							//instructions.add(noptok)

							for (OpToken t = jumpList.getFirst(); t != null; t = jumpList.getNext())
								{
								t.opRef = noptok
								}
							
							jumpList = new List()
							}

						//insert a NOP for the false-path, and complete the cross-reference for the if-header
						OpToken noptok = new OpToken(type = HWI.OP_ID_NOP)
						cf.op.opRef = noptok
						function.instructions = new OpToken[](function.instructions, noptok)
						//instructions.add(noptok)
						}
						else if (cf.cfType == ControlFrame.CF_ELSE)
						{
						if (jumpList.getLength() != 0)
							{
							//resolve all true-branch end-of-body jumps to the end of this else block
							OpToken noptok = new OpToken(type = HWI.OP_ID_NOP)
							function.instructions = new OpToken[](function.instructions, noptok)
							//instructions.add(noptok)

							for (OpToken t = jumpList.getFirst(); t != null; t = jumpList.getNext())
								{
								t.opRef = noptok
								}
							
							jumpList = new List()
							}
						}
						else if (cf.cfType == ControlFrame.CF_WHILE)
						{
						//insert a jump-to-start, then a NOP, and complete the two cross-references
						OpToken jmptok = new OpToken(type = HWI.OP_ID_JMP)
						jmptok.returnType = dncUtil.findType(types, "void")
						jmptok.opRef = getFirstExecutedOp(cf.op)
						function.instructions = new OpToken[](function.instructions, jmptok)
						//instructions.add(jmptok)

						OpToken noptok = new OpToken(type = HWI.OP_ID_NOP)
						cf.op.opRef = noptok
						function.instructions = new OpToken[](function.instructions, noptok)
						//instructions.add(noptok)

						//check for any break statements
						for (int i = 0; i < cf.breakOps.arrayLength; i++)
							{
							cf.breakOps[i].op.opRef = noptok
							}
						
						loopStack.pop()

						if (DEBUG_INS_GEN) out.println(" (wrote while end seq)")
						}
						else if (cf.cfType == ControlFrame.CF_MUTEX)
						{
						//insert a mutex-unlock instruction, with a copy of the opening instruction's parameters
						OpToken unlock = new OpToken(type = HWI.OP_ID_MUTEX_UNLOCK)
						unlock.returnType = dncUtil.findType(types, "void")
						function.instructions = new OpToken[](function.instructions, unlock)
						//instructions.add(unlock)

						unlock.parameters = clone cf.op.parameters
						unlock.parameters[0] = rclone unlock.parameters[0]
						}
					
					if (DEBUG_INS_GEN) out.println("((parse scope end for $(cf.cfType)))")
					}
				}
				else if (nxt.type == DanaToken.OPERATION && (nxt.subType == TokSubTypes.DECLARE || nxt.subType == TokSubTypes.DECLARE_ARRAY))
				{
				//check for an already-declared variable by this name
				if (localVariableExists(function, scopes, nxt.exStr1))
					{
					postError(status, nxt.lineNumber, file, "local variable '$(nxt.exStr1)' has already been declared in this scope")
					}
				
				if (globalVariableExists(status, pObject, nxt.exStr1))
					{
					postError(status, nxt.lineNumber, file, "local variable '$(nxt.exStr1)' shadows a global variable of the same name")
					}
				
				if (globalStaticVariableExists(status, pObject, nxt.exStr1))
					{
					postError(status, nxt.lineNumber, file, "local variable '$(nxt.exStr1)' shadows a static global variable of the same name")
					}

				//add variable to scope, and to flat function variable list
				DanaType declareType = getDeclareType(nxt, types)
				ScopeVariable sv = new ScopeVariable(nxt.exStr1, function.variables.fields.arrayLength)
				Scope cscope = scopes.getIndex(scopes.getLength()-1)
				cscope.variables = new ScopeVariable[](cscope.variables, sv)
				function.variables.fields = new DanaTypeField[](function.variables.fields, new DanaTypeField(nxt.exStr1, declareType.name))
				function.variableInfo = new Variable[](function.variableInfo, new Variable())
				}
				else
				{
				//parse a regular operation, converting it to execution-order, and attaching return-type information etc.
				//TODO: we could mark all SR variables as "available" after each of these top-level parseOperation()s, and re-use SR's for the next one
				status.terminalErrorLine = false

				OpToken ot = parseOperation(file, function, false, instructions, pObject, null, 0, scopes, nxt, null, types, prepParamStack, new IFXHolder(), rvars, status)

				if (status.terminalErrorScope || status.terminalErrorFunction)
					{
					return
					}
					else if (!status.terminalErrorLine)
					{
					//if the operation was an IF, or WHILE, push the operation onto a stack, along with a reference to the following scope
					// - once that scope has been parsed (via parseScope), pop it off the stack, and resolve cross-instruction references
					// - we probably do this by always inserting a "nop" after the scope, and referencing to that
					if (ot.type == HWI.OP_ID_IF)
						{
						byte cfType = 0
						if (nxt.subType == TokSubTypes.IF || nxt.subType == TokSubTypes.ELSEIF)
							{
							cfType = ControlFrame.CF_IF
							}
							else if (nxt.subType == TokSubTypes.WHILE)
							{
							cfType = ControlFrame.CF_WHILE
							}
						
						ControlFrame ncf = new ControlFrame(ot, nxt.next, cfType)

						cfStack.push(ncf)
						if (cfType == ControlFrame.CF_WHILE) loopStack.push(ncf)

						if (nxt.next == null || nxt.next.type != DanaToken.SCOPE)
							{
							out.println("illegal scope state machine at $(nxt.lineNumber)")
							}
						}
						else if (ot.type == HWI.OP_ID_NOP && nxt.subType == TokSubTypes.ELSE)
						{
						cfStack.push(new ControlFrame(ot, nxt.next, 2))
						}
						else if (ot.type == HWI.OP_ID_JMP && nxt.subType == TokSubTypes.BREAK)
						{
						ControlFrame lcf = loopStack.peek()
						if (lcf != null)
							{
							lcf.breakOps = new BreakOp[](lcf.breakOps, new BreakOp(ot, nxt))
							}
							else
							{
							postError(status, nxt.lineNumber, file, "'break' statement with no loop")
							}
						}
						else if (ot.type == HWI.OP_ID_MUTEX_LOCK)
						{
						cfStack.push(new ControlFrame(ot, nxt.next, ControlFrame.CF_MUTEX))
						}
					}
				}

			if (nxtOvr != null)
				{
				nxt = nxtOvr
				nxtOvr = null
				}
				else
				{
				nxt = nxt.next
				}
			}
		
		status.terminalErrorScope = false
		}
	
	bool checkReturns(char file[], DanaToken tree, ProvidedObject pObject, OpParseResultX status)
		{
		bool allBranchesReturn = false

		while (tree != null)
			{
			if (tree.type == DanaToken.OPERATION && tree.subType == TokSubTypes.IF)
				{
				allBranchesReturn = checkReturns(file, tree.next.subTokens, pObject, status)
				}
				else if (tree.type == DanaToken.OPERATION && tree.subType == TokSubTypes.ELSEIF)
				{
				allBranchesReturn = allBranchesReturn & checkReturns(file, tree.next.subTokens, pObject, status)
				}
				else if (tree.type == DanaToken.OPERATION && tree.subType == TokSubTypes.ELSE)
				{
				allBranchesReturn = allBranchesReturn & checkReturns(file, tree.next.subTokens, pObject, status)

				if (allBranchesReturn)
					{
					return true
					}
				}
				else if (tree.type == DanaToken.OPERATION && (tree.subType == TokSubTypes.RETURN || tree.subType == TokSubTypes.THROW))
				{
				return true
				}
				else if (tree.type == DanaToken.OPERATION && tree.subType == TokSubTypes.MUTEX)
				{
				allBranchesReturn = checkReturns(file, tree.next.subTokens, pObject, status)

				if (allBranchesReturn)
					{
					return true
					}
				}

			tree = tree.next
			}

		return false
		}
	
	void parseFunction(char file[], OpFunctionDetail function, ProvidedObject pObject, DanaType types[], OpParseResultX status)
		{
		char fileOrigin[] = file
		List scopes = new List()
		//List instructions = new List()

		Scope scope = new Scope() 
		scopes.add(scope)

		status.terminalErrorLine = false
		status.terminalErrorScope = false
		status.terminalErrorFunction = false

		//populate scope with function parameters
		for (int i = 0; i < function.parameters.arrayLength; i++)
			{
			scope.variables = new ScopeVariable[](scope.variables, new ScopeVariable(function.parameters[i].name, i + 1))
			}

		Stack cfStack = new Stack()
		Stack loopStack = new Stack()

		//begin parsing the function's root scope
		if (function.fromFile != null) file = function.fromFile
		if (function.tree == null)
			{
			//out.println("[error: function $(function.name) from $file has no scope, inherited status is $(function.inherited)]")
			if (!function.inherited)
				{
				postError(status, 0, fileOrigin, "function $(function.fromType):$(function.name) has no implementation")
				return
				}
			}
		
		//check all branches return
		if (function.returnType.name != "void" && function.name != pObject.interfaces[0].type && !checkReturns(file, function.tree.subTokens, pObject, status))
			{
			postError(status, function.tree.lineNumber, file, "missing return statement in function returning non-void")
			}

		//parse
		parseScope(file, function, null, pObject, scopes, function.tree.subTokens, types, cfStack, loopStack, status)

		//add automated return instructions, for void functions, or for constructors
		if (DEBUG_BASIC) out.println("finishing function $(function.name) of $(pObject.interfaces[0].type)")
		if (function.returnType.name == "void")
			{
			function.instructions = new OpToken[](function.instructions, new OpToken(HWI.OP_ID_RETURN))
			//instructions.add(new OpToken(HWI.OP_ID_RETURN))
			}
			else if (function.name == pObject.interfaces[0].type)
			{
			DanaType boolType = dncUtil.findType(types, "bool")
			function.instructions = new OpToken[](function.instructions, new OpToken(HWI.OP_ID_RETURN, parameters = new OpToken[](new OpToken(type = HWI.OP_ID_GET_LITERAL_PTR, refValue = parseLiteral(file, new DanaToken(type = DanaToken.LITERAL, subType = TokSubTypes.L_INT, token = "1"), new OpToken(), boolType, types, status), returnType = boolType)), returnType = boolType))
			//instructions.add(new OpToken(HWI.OP_ID_RETURN, parameters = new OpToken[](new OpToken(type = HWI.OP_ID_GET_LITERAL_PTR, refValue = parseLiteral(file, new DanaToken(type = DanaToken.LITERAL, subType = TokSubTypes.L_INT, token = "1"), new OpToken(), boolType, types, status), returnType = boolType)), returnType = boolType))
			}
		
		//function.instructions = instructions.getContents(typeof(OpToken))
		
		if (function.ftype == OpFunction.EVENTSINK)
			{
			pObject.eventSinkCount ++
			}
		}
	
	void parseFunctions(char file[], char impl[], DanaToken tree, DanaType types[], OpParseResultX status)
		{
		//we've now collected all functions scopes in the providedObjects structure -- just work through these, instead of the main tree

		if (DEBUG_BASIC) out.println("-- parsing functions --")

		for (int i = 0; i < status.providedObjects.arrayLength; i++)
			{
			for (int j = 0; j < status.providedObjects[i].interfaces.arrayLength; j++)
				{
				for (int k = 0; k < status.providedObjects[i].interfaces[j].functions.arrayLength; k++)
					{
					parseFunction(file, status.providedObjects[i].interfaces[j].functions[k], status.providedObjects[i], types, status)
					}
				}
			
			for (int j = 0; j < status.providedObjects[i].localFunctions.arrayLength; j++)
				{
				parseFunction(file, status.providedObjects[i].localFunctions[j], status.providedObjects[i], types, status)
				}
			}
		}
	
	bool refOnList(Data ref, List list)
		{
		for (Data el = list.getFirst(); el != null; el = list.getNext())
			{
			if (el === ref) return true
			}
		
		return false
		}
	
	bool hasLocalReference(AssignInstance inst[], List visitList)
		{
		for (int i = 0; i < inst.arrayLength; i++)
			{
			if (!refOnList(inst[i], visitList))
				{
				visitList.add(inst[i])

				if (inst[i].storeClass == AssignInstance.SC_LOCAL)
					{
					return true
					}
				
				//there's now a "fields" scan
				for (int j = 0; j < inst[i].fields.arrayLength; j++)
					{
					if (hasLocalReference(inst[i].fields[j].instances, visitList))
						{
						return true
						}
					}
				}
			}
		
		return false
		}
	
	bool hasLocalReachable(OpFunction function, AssignInstance inst[])
		{
		for (int i = 0; i < inst.arrayLength; i++)
			{
			if (inst[i].storeClass == AssignInstance.SC_LOCAL)
				{
				return true
				}
			
			//there's now a "fields" scan
			for (int j = 0; j < inst[i].fields.arrayLength; j++)
				{
				if (hasLocalReference(inst[i].fields[j].instances, new List()))
					{
					return true
					}
				}
			}

		return false
		}
	
	void resetLocalAccesses(OpFunction function, OpToken instructions[])
		{
		for (int i = 0; i < instructions.arrayLength; i++)
			{
			resetLocalAccesses(function, instructions[i].parameters)

			if (instructions[i].type == HWI.OP_ID_GET_REF
				|| instructions[i].type == HWI.OP_ID_GET_PTR
				|| instructions[i].type == HWI.OP_ID_GET_PTR_HND)
				{
				if (instructions[i].parameters[1].type == HWI.OP_ID_LOCALS_STORE)
					{
					int vindex = instructions[i].variableIndex - function.parameters.arrayLength - 1

					if (instructions[i].variableIndex > function.parameters.arrayLength)
						{
						function.variableInfo[vindex].storeClass = Variable.SC_LOCAL
						}

					instructions[i].parameters[1].type = HWI.OP_ID_LOCALS
					}
					else if (instructions[i].parameters[1].type == HWI.OP_ID_MEMBER_ACCESS)
					{
					instructions[i].scopeOverride = 0
					}
				}
			}
		}

	bool reviewStoreFunctionParams(char file[], ProvidedObject pObject, OpFunctionDetail function, OpToken instructions[], DanaType types[], AssignGraph agr, OpParseResult status)
		{
		bool result = false

		for (LocalCallOp lco = function.localCalls; lco != null; lco = lco.next)
			{
			OpToken ins = lco.op
			if ((ins.type == HWI.OP_ID_STACK_FRAME_LAUNCH_R || ins.type == HWI.OP_ID_STACK_FRAME_LAUNCH_P)
				&& (ins.parameters[1].type == HWI.OP_ID_STACK_FRAME_INIT_LOCAL))
				{
				//walk through each parameter assignment, to decide if we're going to force it to local based on the RHS
				FunctionRef fref = ins.parameters[1].functionRef
				if (DEBUG_STORE_ANALYSIS) out.println(" -- call to $(fref.intfName):$(fref.name) on line $(ins.lineNumber)")

				OpFunction opf = findFunctionOp(pObject, fref.name, fref.intfName)

				bool functionChange = false

				for (int j = 2; j < ins.parameters.arrayLength; j++)
					{
					if (ins.parameters[j].type == HWI.OP_ID_ASSIGN_POINTER)
						{
						Parameter prm = opf.parameters[j - 2]
						AssignInstance rhs[] = agrBuilder.getInstances(agr, ins.parameters[j].parameters[2], types)

						if (prm.qualifier == Parameter.Q_STORE && prm.autoStore && hasLocalReachable(function, rhs))
							{
							if (DEBUG_STORE_ANALYSIS) out.println("  - downgrade parameter $(j-2)")
							prm.qualifier = 0
							prm.autoStore = false
							result = true
							functionChange = true
							}
						}
					}
				
				if (functionChange)
					{
					//reset all local variable accesses to "locals"
					if (DEBUG_STORE_ANALYSIS) out.println("  - resetting local access types in $(opf.name)")
					resetLocalAccesses(opf, opf.instructions)
					}
				}
			}

		return result
		}
	
	void reviewAssignments(char file[], OpToken instructions[], DanaType types[], OpParseResult status)
		{
		for (int i = 0; i < instructions.arrayLength; i++)
			{
			if (instructions[i].type == HWI.OP_ID_ASSIGN_POINTER)
				{
				if (instructions[i].parameters[1].returnType == null)
					{
					throw new Exception("return type for op $(instructions[i].parameters[1].type) on line $(instructions[i].parameters[1].lineNumber) is null")
					}
				
				if (instructions[i].parameters[1].returnType.class == DanaType.ARRAY)
					{
					DanaType fieldType = dncUtil.findType(types, instructions[i].parameters[1].returnType.fields[0].type)

					if (fieldType.class == DanaType.INTEGER)
						{
						instructions[i].type = HWI.OP_ID_ASSIGN_POINTER_UNCHECKED
						}
					}
				}
			
			reviewAssignments(file, instructions[i].parameters, types, status)
			}
		}
	
	void reviewCheckedAssignments(char file[], OpToken instructions[], OpToken parent, DanaType types[], OpParseResult status)
		{
		for (int i = 0; i < instructions.arrayLength; i++)
			{
			if (instructions[i].type == HWI.OP_ID_ASSIGN_POINTER)
				{
				if (instructions[i].parameters[1].returnType == null)
					{
					throw new Exception("return type for op $(instructions[i].parameters[1].type) on line $(instructions[i].parameters[1].lineNumber) is null")
					}
				
				if (parent != null && (parent.type == HWI.OP_ID_STACK_FRAME_LAUNCH_P || parent.type == HWI.OP_ID_STACK_FRAME_LAUNCH_R) && instructions[1].type == HWI.OP_ID_STACK_FRAME_INIT_OBJECT)
					{
					//check if this param index is "store" ...
					DanaType objectType = instructions[1].returnType
					if (objectType == null) out.println("[CSM-error] return type null for object call on line $(instructions[1].lineNumber)/$(instructions[i].lineNumber)")

					if (isObjectCallParamStore(objectType, instructions[1].functionIndex, i-2, types))
						{
						instructions[i].type = HWI.OP_ID_ASSIGN_POINTER_ORCHK
						}
					}
				}
			
			reviewCheckedAssignments(file, instructions[i].parameters, instructions[i], types, status)
			}
		}
	
	void upgradeLocalVariables(AssignGraph agr, OpFunction function)
		{
		if (DEBUG_STORE_ANALYSIS) out.println("store::checking $(function.variables.fields.arrayLength - function.parameters.arrayLength) local variables")
		int j = 0
		for (int i = function.parameters.arrayLength + 1; i < function.variables.fields.arrayLength; i++)
			{
			if (DEBUG_STORE_ANALYSIS) out.println("store::considering local variable '$(function.variables.fields[i].name)'")
			if (!hasLocalReference(agr.vars[i].instances, new List()))
				{
				if (DEBUG_STORE_ANALYSIS) out.println("store:: - upgrading local variable '$(function.variables.fields[i].name)' to sc_store")
				function.variableInfo[j].storeClass = Variable.SC_STORE
				}
			
			j++
			}
		}
	
	void convertLocalVariableAccess(OpFunction function, OpToken instructions[], DanaType types[])
		{
		for (int i = 0; i < instructions.arrayLength; i++)
			{
			convertLocalVariableAccess(function, instructions[i].parameters, types)

			if (instructions[i].type == HWI.OP_ID_GET_REF
				|| instructions[i].type == HWI.OP_ID_GET_PTR
				|| instructions[i].type == HWI.OP_ID_GET_PTR_HND)
				{
				if (instructions[i].parameters[1].type == HWI.OP_ID_LOCALS)
					{
					//the "-1" here is the return value
					int vindex = instructions[i].variableIndex - function.parameters.arrayLength - 1
					if (instructions[i].variableIndex > function.parameters.arrayLength)
						{
						if (function.variableInfo[vindex].storeClass == Variable.SC_STORE)
							{
							instructions[i].parameters[1].type = HWI.OP_ID_LOCALS_STORE
							if (DEBUG_STORE_ANALYSIS) out.println("store:: - reframing variable access '$(function.variables.fields[function.parameters.arrayLength + 1 + vindex].name)' on line $(instructions[i].lineNumber) to sc_local_store")
							}
						}
						else
						{
						//it's a parameter, so deal with that (check the qualifier)...
						if ((function.parameters[instructions[i].variableIndex - 1].qualifier & Parameter.Q_STORE) == Parameter.Q_STORE)
							{
							instructions[i].parameters[1].type = HWI.OP_ID_LOCALS_STORE
							if (DEBUG_STORE_ANALYSIS) out.println("store:: - reframing param access '$(function.parameters[instructions[i].variableIndex - 1].name)' on line $(instructions[i].lineNumber) to sc_local_store")
							}
						}
					}
					else if (instructions[i].parameters[1].type == HWI.OP_ID_LOCALS_STORE)
					{
					//the "-1" here is the return value
					int vindex = instructions[i].variableIndex - function.parameters.arrayLength - 1
					if (instructions[i].variableIndex > function.parameters.arrayLength)
						{
						if (function.variableInfo[vindex].storeClass != Variable.SC_STORE)
							{
							instructions[i].parameters[1].type = HWI.OP_ID_LOCALS
							if (DEBUG_STORE_ANALYSIS) out.println("store:: - reframing param access '$(function.parameters[instructions[i].variableIndex - 1].name)' on line $(instructions[i].lineNumber) to sc_local")
							}
						}
						else
						{
						//it's a parameter, so deal with that (check the qualifier)...
						if ((function.parameters[instructions[i].variableIndex - 1].qualifier & Parameter.Q_STORE) != Parameter.Q_STORE)
							{
							instructions[i].parameters[1].type = HWI.OP_ID_LOCALS
							if (DEBUG_STORE_ANALYSIS) out.println("store:: - reframing param access '$(function.parameters[instructions[i].variableIndex - 1].name)' on line $(instructions[i].lineNumber) to sc_local")
							}
						}
					}
					else if (instructions[i].parameters[1].type == HWI.OP_ID_MEMBER_ACCESS)
					{
					//any member access that yields a primitive array converts the original local variable access (if it's a local) to locals_store
					// - OR, we can issue an ex2 value to GET_REF, to override its scope status
					DanaType rt = instructions[i].returnType
					if (rt.class == DanaType.ARRAY)
						{
						DanaType ft = dncUtil.findType(types, rt.fields[0].type)

						if (ft.class == DanaType.INTEGER || ft.class == DanaType.DECIMAL)
							{
							instructions[i].scopeOverride = 1
							}
						}
					}
				}
			}
		}
	
	bool markStoreFor(char file[], ProvidedObject pObject, OpFunction function, DanaType types[], OpParseResult status)
		{
		if (DEBUG_STORE_ANALYSIS) out.println("store::analysing function $(function.name)()")

		//perform store analysis for this function
		AssignGraph agr = agrBuilder.buildAssignmentGraph(function, types)

		//now look for any local function call instructions, and check for what we're assigning into those parameters, to downgrade them to "local"
		if (DEBUG_STORE_ANALYSIS) out.println("store::review function params")
		bool change = reviewStoreFunctionParams(file, pObject, function, function.instructions, types, agr, status)

		reviewAssignments(file, function.instructions, types, status)

		//...and look for any local variables of this function, to upgrade them to "local_store" where possible
		upgradeLocalVariables(agr, function)
		if (DEBUG_STORE_ANALYSIS) out.println("store::analysing local variable access")
		convertLocalVariableAccess(function, function.instructions, types)

		return change
		}
	
	void markStoreStatus(char file[], DanaType types[], OpParseResult status)
		{
		//perform store analysis, for all functions
		if (DEBUG_STORE_ANALYSIS) out.println("store::starting analysis")
		bool change = true
		while (change)
			{
			change = false

			if (DEBUG_STORE_ANALYSIS) out.println("store:: --------- iterating... ---------")

			for (int i = 0; i < status.providedObjects.arrayLength; i++)
				{
				for (int j = 0; j < status.providedObjects[i].interfaces.arrayLength; j++)
					{
					for (int k = 0; k < status.providedObjects[i].interfaces[j].functions.arrayLength; k++)
						{
						bool hasChange = markStoreFor(file, status.providedObjects[i], status.providedObjects[i].interfaces[j].functions[k], types, status)
						if (hasChange) change = true
						}
					}
				
				for (int j = 0; j < status.providedObjects[i].localFunctions.arrayLength; j++)
					{
					bool hasChange = markStoreFor(file, status.providedObjects[i], status.providedObjects[i].localFunctions[j], types, status)
					if (hasChange) change = true
					}
				}
			}
		
		//now that this is settled, we can update assign_pointer instructions that go into object call parameters, to check them for objects
		// (this is asConvertAssignPointer() from the classic compiler)
		for (int i = 0; i < status.providedObjects.arrayLength; i++)
			{
			for (int j = 0; j < status.providedObjects[i].interfaces.arrayLength; j++)
				{
				for (int k = 0; k < status.providedObjects[i].interfaces[j].functions.arrayLength; k++)
					{
					reviewCheckedAssignments(file, status.providedObjects[i].interfaces[j].functions[k].instructions, null, types, status)
					}
				}
			
			for (int j = 0; j < status.providedObjects[i].localFunctions.arrayLength; j++)
				{
				reviewCheckedAssignments(file, status.providedObjects[i].localFunctions[j].instructions, null, types, status)
				}
			}
		}
	
	void generateSuperObject(char file[], DanaToken tree, DanaType types[], OpParseResult status)
		{
		//if we have unimplemented inherited functions, and we have an available required interface
		for (int i = 0; i < status.providedObjects.arrayLength; i++)
			{
			bool needSuperObject = false

			for (int k = 0; k < status.providedObjects[i].interfaces[0].functions.arrayLength; k++)
				{
				OpFunctionDetail function = status.providedObjects[i].interfaces[0].functions[k]

				if (function.tree == null && function.inherited)
					{
					needSuperObject = true
					break
					}
				}
			
			if (needSuperObject)
				{
				ProvidedObject pObject = status.providedObjects[i]
				DanaType itype = dncUtil.findType(types, status.providedObjects[i].interfaces[0].type)
				pObject.instanceGlobals.fields = new DanaTypeField[](pObject.instanceGlobals.fields, new DanaTypeField(":super", itype.extendsType))
				}
			}
		}
	
	OpParseResult OpParser:parse(char file[], store DanaToken tree, DanaType types[])
		{
		//the core of this is going to work by parsing functions
		OpParseResultX result = new OpParseResultX()

		//first collect component-scope constants
		collectConstants(file, tree, types, result)

		//then start by extracting global-scope stuff into "init" functions (.init_component and .init_instance)
		// - except that .init_instance needs to be done per-provided-object
		makeClassInitFunction(file, tree, types, result)

		//collect list of provided objects and their interfaces
		collectProvidedObjects(file, tree, types, result)

		if (result.errors.arrayLength != 0) return result

		//collect list of required interfaces
		collectRequires(file, tree, types, result)

		if (result.errors.arrayLength != 0) return result

		bool hasImpBlocks = hasImplementations(file, tree, types, result)

		//pre-populate our list an anticipated interface-implementation functions, so we can fill in their implementations from various places
		// - this process sets each function's "tree" to the default implementation from the type definition, if any ("import" default functions)
		// - this defaults get overridden by local alternatives, if present, during collectFunctions
		populateProvidedFunctions(file, tree, types, result)

		makeObjectInitFunctions(file, tree, types, result)

		//generate a global variable of our super-type
		generateSuperObject(file, tree, types, result)

		//collection functions that actually appear in the component, overriding any default implementations imported from type definitions
		collectFunctions(file, tree, types, hasImpBlocks, result)

		//create stub functions, which call super(), for un-implemented functions of a supertype
		createSuperFunctions(file, tree, types, result)

		//then parse each function, including conversion of literals into bytes, and derivation of return types for every instruction
		parseFunctions(file, null, tree.subTokens, types, result)

		if (result.errors.arrayLength == 0)
			{
			//perform store analysis, for all functions
			markStoreStatus(file, types, result)
			}

		return result
		}
}
Revision history
To propose a new revision to this entity, use dana source put -uc your/new/version.dn -n util.compiler.OpParser -m "reason for update" -u yourUsername
Version 22 by barry
Version 21 by barry
Version 20 by barry
Version 19 by barry
Version 18 by barry
Version 17 by barry
Version 16 by barry
Version 15 by barry
Version 14 by barry
Version 13 (this version) by barry
Notes for this version: Removes a confusing internal compiler exception for some kinds of syntax error in global variable scope, adds errors for trying to instantiate constructor-bearing objects in global scope
Version 12 by barry
Version 11 by barry
Version 10 by barry
Version 9 by barry
Version 8 by barry
Version 7 by barry
Version 6 by barry
Version 5 by barry
Version 4 by barry
Version 3 by barry
Version 2 by barry
Version 1 by barry