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 LocalCallOp nocycle {
OpToken op
LocalCallOp next
}
data OpFunctionDetail extends OpFunction nocycle {
DanaToken tree
LocalCallOp localCalls
bool implemented
}
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 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[]
}
const char CMSRC_SEP[] = "#"
const bool DEBUG_BASIC = false
const bool DEBUG_INS_GEN = false
const bool DEBUG_STORE_ANALYSIS = 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, TypeUtil typeUtil, DNCUtil dncUtil, OpUtil opUtil, StoreAnalyser storeAnalyser {
//component-scope constants
List constants
//type-bound constants (these are parsed here)
List typeBoundConstants
//functions, of type OpFunction
List functions
int addressWidth = 8
bool perfProfile = false
int getNativeAddressWidth()
{
ConInt c = new ConInt()
return dana.serial(c).arrayLength
}
OpParser:OpParser(opt int aw, opt bool pp)
{
constants = new List()
functions = new List()
typeBoundConstants = new List()
if (isset aw)
{
addressWidth = aw
}
else
{
addressWidth = getNativeAddressWidth()
}
perfProfile = pp
}
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
}
byte[] parseLiteral(char file[], DanaToken t, OpToken newOp, DanaType xrType, DanaType types[], OpParseResult status)
{
ParseLiteralResult res = opUtil.parseLiteral(file, t, xrType, types, constants, addressWidth)
newOp.returnType = res.returnType
status.errors = new OpParseError[](status.errors, res.errors)
if (res.errors.arrayLength != 0) status.success = false
return res.content
}
void collectConstants(char file[], DanaToken tree, DanaType types[], OpParseResult status)
{
if (DEBUG_BASIC) out.println("COLLECT_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 (opUtil.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 (opUtil.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 (opUtil.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
}
if (newFunction.implemented)
{
postError(status, pw.lineNumber, file, "implementation of '$(pw.exStr2).$(pw.token)()' has already been defined")
return false
}
newFunction.fromFile = file
newFunction.tree = pw.next
newFunction.implemented = true
//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)
//check global variable shadowing
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
//check for existing local function of this name
if (pObject.localFunctions.findFirst(OpFunction.[name], kr) != null)
{
postError(status, pw.lineNumber, file, "multiple local functions defined by the name '$(kr.name)'")
}
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
}
}
}
}
}
}
//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)
}
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
}
char[] getFunctionName(DanaType t, int functionIndex, 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)
{
return t.fields[i].name
}
j ++
}
}
return null
}
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
}
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)
}
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 (opUtil.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 = opUtil.getObjectCallParamInfo(qtype, qtype.name, types)
int coreParams = 0
int totalParams = 0
if (global)
{
postError(status, tok.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)
if (op.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
}
//"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
ParamInfo pinfo = opUtil.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 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
coreParams ++
}
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_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 (opUtil.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 = opUtil.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 = opUtil.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 (opUtil.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 = opUtil.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 = opUtil.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 = ""))
if (tok.subTokens.next.next.subTokens == null)
{
postError(status, tok.lineNumber, file, "too few parameters for dynamic call operator (expected 1)")
status.terminalErrorLine = true
return result
}
else if (tok.subTokens.next.next.subTokens.next != null)
{
postError(status, tok.lineNumber, file, "too many parameters for dynamic call operator (expected 1)")
status.terminalErrorLine = true
return result
}
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 (!opUtil.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 (!opUtil.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)
{
if (tok.subTokens.next.subTokens.next != null)
{
postError(status, tok.lineNumber, file, "unexpected extra tokens in field expression")
status.terminalErrorLine = true
return result
}
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")
if (tok.subTokens == null)
{
postError(status, tok.lineNumber, file, "too few parameters for if() operator (expected 1)")
status.terminalErrorLine = true
return result
}
else if (tok.subTokens.next != null)
{
postError(status, tok.lineNumber, file, "too many parameters for if() operator (expected 1)")
status.terminalErrorLine = true
return result
}
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")
if (tok.subTokens == null)
{
postError(status, tok.lineNumber, file, "too few parameters for while() operator (expected 1)")
status.terminalErrorLine = true
return result
}
else if (tok.subTokens.next != null)
{
postError(status, tok.lineNumber, file, "too many parameters for while() operator (expected 1)")
status.terminalErrorLine = true
return result
}
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")
if (tok.subTokens == null)
{
postError(status, tok.lineNumber, file, "too few parameters for mutex() operator (expected 1)")
status.terminalErrorLine = true
return result
}
else if (tok.subTokens.next != null)
{
postError(status, tok.lineNumber, file, "too many parameters for mutex() operator (expected 1)")
status.terminalErrorLine = true
return result
}
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 (!opUtil.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 (!opUtil.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 (!opUtil.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 (!opUtil.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 (!opUtil.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 (!opUtil.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 (!opUtil.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 (!opUtil.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 (!opUtil.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)
postWarning(status, tok.lineNumber, file, "dana.halt() is deprecated, use halt() function on lang.IDC instead")
if (!opUtil.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 (!opUtil.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.GET_DIMENSION)
{
op.type = HWI.OP_ID_GET_ARRAY_DIMENSION
op.returnType = types.findFirst(DanaType.[name], new DanaType(name = "int"))
result.subTokens = dropParams(tok)
if (!opUtil.checkOpParamCount(result.subTokens, 2))
{
postError(status, tok.lineNumber, file, "incorrect parameter count for dana.getDimension(): expected 2 parameters")
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 (!opUtil.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)
postWarning(status, tok.lineNumber, file, "dana.htod() is deprecated, use data.IntUtil functions instead")
if (!opUtil.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)
postWarning(status, tok.lineNumber, file, "dana.dtoh() is deprecated, use data.IntUtil functions instead")
if (!opUtil.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 (opUtil.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 (opUtil.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 (opUtil.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 (opUtil.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 (opUtil.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 (opUtil.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 (opUtil.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 = opUtil.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 (opUtil.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 = opUtil.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)' (you must declare a named instance of a suitable required interface)")
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 (opUtil.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.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)
//check which sub-class of array index to perform, converting multiple-dimensions to an offset calculation where possible
DanaType cellType = dncUtil.findType(types, field.type)
//out.println("CONSIDER $(op.type) on line $(op.lineNumber) of $(cellType.class) with $(op.parameters[0].type) -> $(op.parameters[1].type)")
/*
if (cellType.class != DanaType.ARRAY && op.parameters[1].type == HWI.OP_ID_SUB_ARRAY_DIMENSION)
{
//terminal dimension of a multi-D array; convert to an offset calculation, and also decide the sub-class of index between P and R
// - the inner-most operation is the outer-most dimension of the array, which also has the expression that yields the array reference, so we need to work inside-out
OpToken ntok = opUtil.convertTerminalMDIndex(op, types)
op.type = ntok.type
op.parameters = ntok.parameters
}
else if (cellType.class == DanaType.ARRAY)
{
//this is a non-terminal dimension, so we use an array sub-range instruction
op.type = HWI.OP_ID_SUB_ARRAY_DIMENSION
//out.println("SUBIC")
}
else if (opUtil.isRefType(cellType))
{
//it's a single-dimension array, but returns a reference type
op.type = HWI.OP_ID_GET_INDEX_R
}
*/
/*
if (opUtil.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 (opUtil.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 = opUtil.getIntTypeFor(types, rB.storageSize)
DanaType intB = opUtil.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 = opUtil.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 wrapParameterSR(OpFunction function, OpToken ins, int param)
{
DanaType rt = ins.parameters[param].returnType
OpToken newOp = new OpToken()
newOp.type = HWI.OP_ID_ASSIGN
newOp.returnType = rt
insertSR(function, newOp, rt.name, rt)
newOp.parameters = new OpToken[](newOp.parameters, ins.parameters[param])
ins.parameters[param] = 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 (opUtil.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)
{
DanaType arrayType = aor.instruction.parameters[1].returnType
DanaTypeField field = arrayType.fields[0]
DanaType cellType = dncUtil.findType(types, field.type)
aor.instruction.returnType = dncUtil.findType(types, aor.instruction.parameters[1].returnType.fields[0].type)
bool lhs = parent != null && parent.type == HWI.OP_ID_ASSIGN && pIndex == 0
//are we the first operand of an assignment?
if (cellType.class != DanaType.ARRAY && aor.instruction.parameters[1].type == HWI.OP_ID_SUB_ARRAY_DIMENSION)
{
//terminal dimension of a multi-D array; convert to an offset calculation, and also decide the sub-class of index between P and R
// - the inner-most operation is the outer-most dimension of the array, which also has the expression that yields the array reference, so we need to work inside-out
ConvertMDResult cmdr = opUtil.convertTerminalMDIndex(aor.instruction, types, function.variables, lhs, addressWidth)
aor.instruction.type = cmdr.op.type
aor.instruction.parameters = rclone cmdr.op.parameters
function.variables.fields = new DanaTypeField[](function.variables.fields, cmdr.newVars)
for (int i = 0; i < cmdr.newVars.arrayLength; i++) function.variableInfo = new Variable[](function.variableInfo, new Variable())
}
else if (cellType.class == DanaType.ARRAY)
{
//this is a non-terminal dimension, so we use an array sub-range instruction
aor.instruction.type = HWI.OP_ID_SUB_ARRAY_DIMENSION
if (lhs)
{
postError(status, aor.instruction.lineNumber, file, "non-terminal array dimensions cannot be assigned to")
}
}
else if (lhs && arrayType.storageSize == 0)
{
aor.instruction.type = HWI.OP_ID_GET_INDEX_TO_ASSIGN
}
else if (opUtil.isRefType(cellType))
{
aor.instruction.type = HWI.OP_ID_GET_INDEX_R
}
}
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 (opUtil.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 (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_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
{
//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-eq 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.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_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
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 we know we're placing the result into a wider type, use that wider type here, else use the wider of the two input types
if (xrType != null && xrType.class == rR.class && rR.storageSize <= xrType.storageSize)
{
insertSR(function, aor.instruction, xrType.name, xrType)
aor.instruction.returnType = xrType
}
else
{
insertSR(function, aor.instruction, rR.name, rR)
aor.instruction.returnType = rR
}
}
else if (aor.instruction.type == HWI.OP_ID_INTMUL)
{
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
//if we know we're placing the result into a wider type, use that wider type here, else use the wider of the two input types
if (xrType != null && xrType.class == rR.class && rR.storageSize <= xrType.storageSize)
{
insertSR(function, aor.instruction, xrType.name, xrType)
aor.instruction.returnType = xrType
}
else
{
insertSR(function, aor.instruction, rR.name, rR)
aor.instruction.returnType = rR
}
}
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, opUtil.getDecTypeFor(types, rA.storageSize * 2).name, opUtil.getDecTypeFor(types, rA.storageSize * 2))
insertSR(function, aor.instruction, rA.name, rA)
//parameters [2] and [3] now need to be wrapped in an assign-through to an SR of equivalent size, because decimal multiplication is destructive over its input values
wrapParameterSR(function, aor.instruction, 2)
wrapParameterSR(function, aor.instruction, 3)
}
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, opUtil.getDecTypeFor(types, rA.storageSize * 2).name, opUtil.getDecTypeFor(types, rA.storageSize * 2))
insertSR(function, aor.instruction, rA.name, rA)
//parameters [2] and [3] now need to be wrapped in an assign-through to an SR of equivalent size, because decimal multiplication is destructive over its input values
wrapParameterSR(function, aor.instruction, 2)
wrapParameterSR(function, aor.instruction, 3)
}
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_GET_ARRAY_DIMENSION)
{
DanaType rA = dncUtil.findType(types, "int")
aor.instruction.returnType = rA
insertSR(function, aor.instruction, rA.name, rA)
}
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 (opUtil.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
}
}
}
else if (aor.instruction.type == HWI.OP_ID_RETURN)
{
//int/dec coercion in return statements
if (aor.instruction.parameters.arrayLength == 1 && aor.instruction.parameters[0] != null)
{
DanaType rA = function.returnType
DanaType rB = aor.instruction.parameters[0].returnType
if (rA.class == DanaType.DECIMAL && (rB.class == DanaType.INTEGER || rB.class == DanaType.DECIMAL))
{
if (DEBUG_DECIMAL_SCALING) out.println("return 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[0] = makeIntToDec(function, aor.instruction.parameters[0], types)
rB = aor.instruction.parameters[0].returnType
}
if (rB.storageSize < rA.storageSize)
{
aor.instruction.parameters[0] = makeDecRescale(function, aor.instruction.parameters[0], rA, types)
rB = aor.instruction.parameters[0].returnType
if (DEBUG_DECIMAL_SCALING) out.println(" - scaling param 1 up")
}
}
else if (rA.class == DanaType.INTEGER && rB.class == DanaType.DECIMAL)
{
aor.instruction.parameters[0] = makeDecToInt(function, aor.instruction.parameters[0], types)
}
}
}
}
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
}
}
void checkCycleUse(char file[], OpToken instruction, DanaType types[], OpParseResultX status)
{
if (instruction.type == HWI.OP_ID_ASSIGN_POINTER
&& instruction.parameters[2].type == HWI.OP_ID_STACK_FRAME_LAUNCH_R
&& instruction.parameters[2].parameters[1].type == HWI.OP_ID_STACK_FRAME_INIT_OBJECT)
{
if ((instruction.parameters[2].returnType.class == DanaType.DATA || instruction.parameters[2].returnType.class == DanaType.ARRAY) && instruction.parameters[2].returnType.flags != DanaType.F_NO_CYCLE)
{
//out.println("WHOA there RET, on line $(instruction.lineNumber), for type $(instruction.parameters[2].returnType.name)")
DanaType objectType = instruction.parameters[2].parameters[1].parameters[0].returnType
int fndx = instruction.parameters[2].parameters[1].functionIndex
postWarning(status, instruction.lineNumber, file, "potential performance issue in use of return value of type $(instruction.parameters[2].returnType.name) which may contain cycles, from function $(objectType.name):$(getFunctionName(objectType, fndx, types))()")
}
}
else if (instruction.type == HWI.OP_ID_STACK_FRAME_LAUNCH_R
&& instruction.parameters[1].type == HWI.OP_ID_STACK_FRAME_INIT_OBJECT)
{
for (int i = 2; i < instruction.parameters.arrayLength; i++)
{
if (instruction.parameters[i].type == HWI.OP_ID_ASSIGN_POINTER)
{
if ((instruction.parameters[i].parameters[2].returnType.class == DanaType.DATA || instruction.parameters[i].parameters[2].returnType.class == DanaType.ARRAY) && instruction.parameters[i].parameters[2].returnType.flags != DanaType.F_NO_CYCLE)
{
DanaType objectType = instruction.parameters[1].parameters[0].returnType
int fndx = instruction.parameters[1].functionIndex
//out.println("WHOA there PAR, on line $(instruction.lineNumber), for type $(instruction.parameters[i].parameters[2].returnType.name) to parameter $(i-2) of $(objectType.name):$(getFunctionName(objectType, fndx, types))()")
postWarning(status, instruction.lineNumber, file, "potential performance issue in passing value of type $(instruction.parameters[i].parameters[2].returnType.name), which may contain cycles, to parameter $(i-2) of $(objectType.name):$(getFunctionName(objectType, fndx, types))()")
}
}
}
}
}
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)
}
//check use of may-cycle data instances, and post performance warnings if they appear (if the compiler has been asked to generate such warnings)
if (perfProfile) checkCycleUse(file, aor.instruction, types, status)
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 [$file]")
}
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))
if (nwNxt.subTokens.next.subTokens.subTokens == null)
{
postError(status, nxt.lineNumber, file, "unknown syntax in for-loop header")
return
}
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)
}
}
}
void applyStoreStatus(OpFunction function, StoreFunction fs, OpToken instructions[], OpToken parent, DanaType types[], bool assignToLocal)
{
//bool nextATL = assignToLocal
bool nextATL = false //this is conservative for now
for (int i = 0; i < instructions.arrayLength; i++)
{
//check if we're inside an assign-to-local, which may affect parameter handling for object constructors
if (parent != null && parent.type == HWI.OP_ID_ASSIGN_POINTER)
{
OpToken originLHS = storeAnalyser.getOriginLocal(instructions[1], types)
//out.println("store:: - check assign origin on line $(parent.lineNumber)")
if (originLHS != null && fs.locals[originLHS.variableIndex-1].class == StoreVar.AC_LOCAL)
{
//out.println("store:: - is local-only")
nextATL = true
}
else
{
//out.println("store:: - is unknown/store")
nextATL = false
}
}
applyStoreStatus(function, fs, instructions[i].parameters, instructions[i], types, nextATL)
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)
{
int vindex = instructions[i].variableIndex - 1
if (fs.locals[vindex].class == StoreVar.AC_STORE)
{
if (DEBUG_STORE_ANALYSIS) out.println("store:: - reframing variable access '$(function.variables.fields[vindex+1].name)' on line $(instructions[i].lineNumber) to sc_local_store")
instructions[i].parameters[1].type = HWI.OP_ID_LOCALS_STORE
}
}
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
if (DEBUG_STORE_ANALYSIS) out.println("store:: - reframing member access as store")
}
}
}
}
else 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)
{
if (parent.type == HWI.OP_ID_STACK_FRAME_LAUNCH_P || parent.type == HWI.OP_ID_STACK_FRAME_LAUNCH_R)
{
//out.println("store:: considering function call on line $(instructions[i].lineNumber), i[1] is $(instructions[1].type)")
if (instructions[1].type == HWI.OP_ID_STACK_FRAME_INIT_OBJECT)
{
OpToken originLHS = storeAnalyser.getOriginLocal(instructions[1].parameters[0], types)
//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 the object ref is local, and the formal param scope is "store", the actual param can be a scope-override assign (but with ownership check)
if (originLHS != null && fs.locals[originLHS.variableIndex-1].class == StoreVar.AC_LOCAL && opUtil.isObjectCallParamStore(objectType, instructions[1].functionIndex, i-2, types))
{
instructions[i].type = HWI.OP_ID_ASSIGN_POINTER_ORCHK
instructions[i].scopeOverride = 1
if (DEBUG_STORE_ANALYSIS) out.println("store:: - reframing parameter assignment as unchecked on line $(instructions[i].lineNumber)")
}
//otherwise we do an assign that has a scope check AND an ownership check
else if (opUtil.isObjectCallParamStore(objectType, instructions[1].functionIndex, i-2, types))
{
instructions[i].type = HWI.OP_ID_ASSIGN_POINTER_ORCHK
if (DEBUG_STORE_ANALYSIS) out.println("store:: - reframing parameter assignment as checked on line $(instructions[i].lineNumber)")
}
}
}
else if (parent.type == HWI.OP_ID_NEW_OBJECT_CONSTRUCT_CHK)
{
//check if the thing this is being assigned in to is local, and if so allow scope overrides on parameters
// -- we do this by noting if we pass through a local-only assign_pointer, setting assignToLocal "true" if so, and back to "false" if we pass through anything else
DanaType objectType = instructions[0].returnType
if (assignToLocal && opUtil.isObjectCallParamStore(objectType, instructions[0].functionIndex, i-1, types))
{
instructions[i].type = HWI.OP_ID_ASSIGN_POINTER_ORCHK
instructions[i].scopeOverride = 1
if (DEBUG_STORE_ANALYSIS) out.println("store:: - reframing parameter assignment as unchecked on line $(instructions[i].lineNumber)")
}
else if (opUtil.isObjectCallParamStore(objectType, instructions[0].functionIndex, i-1, types))
{
instructions[i].type = HWI.OP_ID_ASSIGN_POINTER_ORCHK
if (DEBUG_STORE_ANALYSIS) out.println("store:: - reframing parameter assignment as checked on line $(instructions[i].lineNumber)")
}
}
}
}
}
}
void applyStoreAnalysis(StoreAnalysis san, DanaType types[], OpParseResult status)
{
//run through each function to apply local/store, scope overrides, and assign_pointer_check instructions
for (int i = 0; i < status.providedObjects.arrayLength; i++)
{
int ndx = 0
for (int j = 0; j < status.providedObjects[i].interfaces.arrayLength; j++)
{
for (int k = 0; k < status.providedObjects[i].interfaces[j].functions.arrayLength; k++)
{
StoreFunction sfx = san.objects[i].functions[ndx]
ndx ++
applyStoreStatus(status.providedObjects[i].interfaces[j].functions[k], sfx, status.providedObjects[i].interfaces[j].functions[k].instructions, null, types, false)
}
}
for (int j = 0; j < status.providedObjects[i].localFunctions.arrayLength; j++)
{
StoreFunction sfx = san.objects[i].functions[ndx]
ndx ++
applyStoreStatus(status.providedObjects[i].localFunctions[j], sfx, status.providedObjects[i].localFunctions[j].instructions, null, types, false)
}
}
}
void markStoreStatus(char file[], DanaType types[], OpParseResult status)
{
//perform store analysis, for all functions
if (DEBUG_STORE_ANALYSIS) out.println("store::starting analysis")
StoreAnalysis analysis = storeAnalyser.analyse(file, status.providedObjects, types)
status.warnings = new OpParseError[](status.warnings, analysis.warnings)
applyStoreAnalysis(analysis, 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, store 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
}
}