HomeForumSourceResearchGuide
Sign in to contribute to source. how it works
Component util.compiler.TypeShaper by barry
expand copy to clipboardexpand
const char CMSRC_SEP[] = "#"

const byte F_RECURSE_POINT = 128

data TRCheck {
	DanaType type
	DanaTypeField field
	TRCheck prev
}

component provides TypeShaper requires io.Output out, data.adt.List, data.StringUtil stringUtil, data.IntUtil iu, data.ByteUtil bu, data.query.Search search, DNCUtil dncUtil {

	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
			//out.println("typestart: $typeStart")
			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 = types.findFirst(DanaType.[name], new DanaType(name = "$(typeStart)[]"))
						if (dts.next != null) typeStart = "$(typeStart)[]"
						}
						else
						{
						atype = types.findFirst(DanaType.[name], new DanaType(name = "$(typeStart)[$arraySize]"))
						if (dts.next != null) typeStart = "$(typeStart)[$arraySize]"
						}
					
					dts = dts.next
					}
				
				//out.println("ts: $(typeStart) / as: $(arraySize)")
				
				if (arraySize == null)
					ftype = types.findFirst(DanaType.[name], new DanaType(name = "$(typeStart)[]"))
					else
					ftype = types.findFirst(DanaType.[name], new DanaType(name = "$(typeStart)[$arraySize]"))
				}
				else
				{
				ftype = types.findFirst(DanaType.[name], new DanaType(name = "$(typeStart)[]"))
				}
			}
		
		return ftype
		}
	
	void setRecursionFlags(TRCheck checker, DanaType type)
		{
		checker = checker.prev

		while (checker != null)
			{
			if (checker.type === type)
				{
				//out.println("marking TRP for $(type.name):$(checker.type.name) via field $(checker.field.name)")
				checker.field.flags |= F_RECURSE_POINT
				}
			
			checker = checker.prev
			}
		}
	
	void locateRecursionPoints(DanaType types[], DanaType type, TRCheck checker, int level)
		{
		TRCheck ntrc = new TRCheck()
		ntrc.prev = checker

		setRecursionFlags(checker, type)

		//out.println(" -> LRP @ lv $level for $(type.name)")

		if (type.class == DanaType.DATA)
			{
			for (int i = 0; i < type.fields.arrayLength; i++)
				{
				if ((type.fields[i].flags & F_RECURSE_POINT) != F_RECURSE_POINT)
					{
					DanaType fieldType = dncUtil.findType(types, type.fields[i].type)

					ntrc.field = type.fields[i]
					ntrc.type = fieldType

					locateRecursionPoints(types, fieldType, ntrc, level + 1)
					}
				}
			}
			else if (type.class == DanaType.INTERFACE || type.class == DanaType.FUNCTION)
			{
			for (int i = 0; i < type.fields.arrayLength; i++)
				{
				byte q = type.fields[i].flags & F_RECURSE_POINT
				if ((type.fields[i].flags & F_RECURSE_POINT) != F_RECURSE_POINT)
					{
					DanaType fieldType = dncUtil.findType(types, type.fields[i].type)

					ntrc.field = type.fields[i]
					ntrc.type = fieldType

					locateRecursionPoints(types, fieldType, ntrc, level + 1)
					}
				}
			}
			else if (type.class == DanaType.ARRAY)
			{
			for (int i = 0; i < type.fields.arrayLength; i++)
				{
				if ((type.fields[i].flags & F_RECURSE_POINT) != F_RECURSE_POINT)
					{
					DanaType fieldType = dncUtil.findType(types, type.fields[i].type)

					ntrc.field = type.fields[i]
					ntrc.type = fieldType

					locateRecursionPoints(types, fieldType, ntrc, level + 1)
					}
				}
			}
		}
	
	void setTypeRecursionPoints(DanaType types[])
		{
		for (int i = 0; i < types.arrayLength; i++)
			{
			//out.println("-- set recursion for type $(types[i].name) --")
			TRCheck trc = new TRCheck(types[i], new DanaTypeField())
			locateRecursionPoints(types, types[i], trc, 0)
			}
		}
	
	void markNoCycle(DanaType t, DanaType types[])
		{
		// for now we need to be *very* conservative about this, so start with only data types that have primitive / array-primitive fields
		// - later we can try to upgrade things, but we need to be aware not just of the "same type", but fields that you *could* assign the same type in to and so create a cycle...
		
		if (t.class == DanaType.DATA)
			{
			for (int i = 0; i < t.fields.arrayLength; i++)
				{
				DanaType fieldType = dncUtil.findType(types, t.fields[i].type)

				if (fieldType.class == DanaType.DATA || fieldType.class == DanaType.INTERFACE)
					{
					return
					}
				
				if (fieldType.class == DanaType.ARRAY)
					{
					fieldType = dncUtil.findType(types, fieldType.fields[0].type)
					if (fieldType.class == DanaType.DATA || fieldType.class == DanaType.INTERFACE)
						{
						return
						}
					}
				}
			}
		
		t.flags = DanaType.F_NO_CYCLE

		//out.println("type $(t.flags) marked nocycle")
		}
	
	void setTypeNoCycle(DanaType types[])
		{
		for (int i = 0; i < types.arrayLength; i++)
			{
			if (types[i].class == DanaType.DATA)
				{
				markNoCycle(types[i], types)

				if (types[i].flags == DanaType.F_NO_CYCLE)
					{
					//out.println("type $(types[i].name) set nocycle")
					}
					else
					{
					//out.println("type $(types[i].name) set may-cycle")
					}
				}
			}
		
		for (int i = 0; i < types.arrayLength; i++)
			{
			if (types[i].class == DanaType.ARRAY)
				{
				DanaType fieldType = dncUtil.findType(types, types[i].fields[0].type)

				if (fieldType.flags == DanaType.F_NO_CYCLE || fieldType.class == DanaType.INTEGER || fieldType.class == DanaType.DECIMAL)
					{
					types[i].flags = DanaType.F_NO_CYCLE
					//out.println("type $(types[i].name) set nocycle")
					}
					else
					{
					//out.println("type $(types[i].name) set may-cycle")
					}
				}
			}
		}
	
	char[] getMakeArrayType(DanaToken tok)
		{
		char result[] = null

		if (tok.subTokens.next == null)
			{
			//one-dimensional array
			result = "$(tok.exStr1)[]"
			}
			else
			{
			//multi-dimensional array
			result = tok.exStr1

			DanaToken stw = tok.subTokens
			while (stw != null)
				{
				result = "$(result)[]"
				stw = stw.next
				}
			}
		
		return result
		}
	
	char[] getMakeArrayConstructType(DanaToken tok)
		{
		char result[] = null

		if (tok.subTokens.next.type == DanaToken.PARAM)
			{
			result = "$(tok.exStr1)[]"
			}
			else
			{
			//multi-dimensional array
			result = tok.exStr1
			DanaToken stw = tok.subTokens
			while (stw != null && stw.type != DanaToken.PARAM)
				{
				result = "$(result)[]"
				stw = stw.next
				}
			}

		return result
		}
	
	int countCharacters(char str[], char c)
		{
		int result = 0
		for (int i = 0; i < str.arrayLength; i++)
			{
			if (str[i] == c) result ++
			}
		
		return result
		}
	
	DanaType[] makeNewArrayTypes(DanaType types[], DanaToken tree)
		{
		DanaType result[]
		DanaToken tw = tree
		while (tw != null)
			{
			result = new DanaType[](result, makeNewArrayTypes(types, tw.subTokens))

			if (tw.type == DanaToken.OPERATION && tw.subType == TokSubTypes.NEW_ARRAY)
				{
				char typeName[] = getMakeArrayType(tw)

				char plainType[] = typeName.lsplit("[")[0].string
				int levels = countCharacters(typeName, "[")

				while (levels > 0)
					{
					if (dncUtil.findType(types, "$(plainType)[]") == null)
						{
						DanaType nt = new DanaType(DanaType.ARRAY, "$(plainType)[]", "$(plainType)[]", null, new DanaTypeField(type = plainType))
						result = new DanaType[](result, nt)
						}
					
					plainType = "$(plainType)[]"

					levels --
					}
				}
				else if (tw.type == DanaToken.OPERATION && tw.subType == TokSubTypes.NEW_ARRAY_CONSTRUCT)
				{
				char typeName[] = getMakeArrayConstructType(tw)

				char plainType[] = typeName.lsplit("[")[0].string
				int levels = countCharacters(typeName, "[")

				while (levels > 0)
					{
					if (dncUtil.findType(types, "$(plainType)[]") == null)
						{
						DanaType nt = new DanaType(DanaType.ARRAY, "$(plainType)[]", "$(plainType)[]", null, new DanaTypeField(type = plainType))
						result = new DanaType[](result, nt)
						}
					
					plainType = "$(plainType)[]"

					levels --
					}
				}

			tw = tw.next
			}
		
		return result
		}
	
	DanaType[] TypeShaper:shapeTypes(DanaType types[], DanaToken tree)
		{
		//here we flatten all types by including their inherited members, and we ensure that "lang.Object" is the base type of all objects
		// - we also do cycle analysis and mark type fields as recursion-points
		types = clone types

		//auto-inherit from lang.Object
		for (int i = 0; i < types.arrayLength; i++)
			{
			if (types[i].class == DanaType.INTERFACE && types[i].name != "lang.Object" && types[i].extendsType == null)
				{
				types[i] = clone types[i]
				types[i].extendsType = "lang.Object"
				}
				else if (types[i].class == DanaType.DATA && types[i].name != "Data" && types[i].name != "Mutex" && types[i].extendsType == null)
				{
				types[i] = clone types[i]
				types[i].extendsType = "Data"
				}
			}
		
		//flatten types
		// - note which ones are already flattened
		DanaType flattened[] = null
		for (int i = 0; i < types.arrayLength; i++)
			{
			DanaType frq = null

			if (types[i].class == DanaType.INTERFACE && types[i].extendsType == null)
				{
				frq = clone types[i]
				flattened = new DanaType[](flattened, frq)
				}
				else if (types[i].class == DanaType.DATA && types[i].extendsType == null)
				{
				frq = clone types[i]
				flattened = new DanaType[](flattened, frq)
				}
				else if (types[i].class != DanaType.INTERFACE && types[i].class != DanaType.DATA)
				{
				frq = clone types[i]
				flattened = new DanaType[](flattened, frq)
				}
			
			if (frq != null)
				{
				frq.fields = clone frq.fields
				for (int j = 0; j < frq.fields.arrayLength; j++)
					{
					frq.fields[j] = clone frq.fields[j]
					}
				}
			}
		
		// - flatten any types which extend an already-flattened type
		bool expansion = true
		while (expansion)
			{
			expansion = false

			for (int i = 0; i < types.arrayLength; i++)
				{
				//out.println("TSHAP: considering $(types[i].name)")
				//if (flattened.findFirst(DanaType.[name], types[i]) == null)
				if (dncUtil.findType(flattened, types[i].name) == null)
					{
					//out.println(" - TSHAP: not in flat-list, class $(types[i].class) ET $(types[i].extendsType)")
					DanaType flt = null
					if (types[i].class == DanaType.INTERFACE && (flt = dncUtil.findType(flattened, types[i].extendsType)) != null)
						{
						char compositeSourceName[] = flt.fromFile

						int countFunction = 0

						DanaType nt = new DanaType(DanaType.INTERFACE, types[i].name, types[i].simpleName, extendsType = types[i].extendsType, deprecated = types[i].deprecated, deprecatedBy = types[i].deprecatedBy)

						//merge transfer fields, events, and functions
						for (int j = 0; j < flt.fields.arrayLength; j ++)
							{
							DanaType fieldType = dncUtil.findType(types, flt.fields[j].type)
							if (fieldType.class != DanaType.EVENT && fieldType.class != DanaType.FUNCTION && flt.fields[j].qualifier == "constant")
								{
								DanaTypeField flfield = clone flt.fields[j]
								flfield.flags = DanaTypeField.INHERITED
								nt.fields = new DanaTypeField[](nt.fields, flfield)
								}
							}
						
						for (int j = 0; j < types[i].fields.arrayLength; j ++)
							{
							DanaType fieldType = dncUtil.findType(types, types[i].fields[j].type)
							if (fieldType.class != DanaType.EVENT && fieldType.class != DanaType.FUNCTION && types[i].fields[j].qualifier == "constant")
								{
								nt.fields = new DanaTypeField[](nt.fields, clone types[i].fields[j])
								}
							}

						for (int j = 0; j < flt.fields.arrayLength; j ++)
							{
							DanaType fieldType = dncUtil.findType(types, flt.fields[j].type)
							if (fieldType.class != DanaType.EVENT && fieldType.class != DanaType.FUNCTION && flt.fields[j].qualifier == "transfer")
								{
								nt.fields = new DanaTypeField[](nt.fields, clone flt.fields[j])
								}
							}
						
						for (int j = 0; j < types[i].fields.arrayLength; j ++)
							{
							DanaType fieldType = dncUtil.findType(types, types[i].fields[j].type)
							if (fieldType.class != DanaType.EVENT && fieldType.class != DanaType.FUNCTION && types[i].fields[j].qualifier == "transfer")
								{
								nt.fields = new DanaTypeField[](nt.fields, clone types[i].fields[j])
								}
							}
						
						for (int j = 0; j < flt.fields.arrayLength; j ++)
							{
							DanaType fieldType = dncUtil.findType(types, flt.fields[j].type)
							if (fieldType.class == DanaType.EVENT)
								{
								DanaTypeField flfield = clone flt.fields[j]
								flfield.flags = DanaTypeField.INHERITED
								nt.fields = new DanaTypeField[](nt.fields, flfield)
								}
							}
						
						for (int j = 0; j < types[i].fields.arrayLength; j ++)
							{
							DanaType fieldType = dncUtil.findType(types, types[i].fields[j].type)
							if (fieldType.class == DanaType.EVENT)
								{
								nt.fields = new DanaTypeField[](nt.fields, clone types[i].fields[j])
								}
							}
						
						for (int j = 0; j < flt.fields.arrayLength; j ++)
							{
							DanaType fieldType = dncUtil.findType(types, flt.fields[j].type)
							if (fieldType.class == DanaType.FUNCTION)
								{
								DanaTypeField flfield = clone flt.fields[j]
								flfield.flags = DanaTypeField.INHERITED
								nt.fields = new DanaTypeField[](nt.fields, flfield)
								countFunction ++
								}
							}
						
						for (int j = 0; j < types[i].fields.arrayLength; j ++)
							{
							DanaType fieldType = dncUtil.findType(types, types[i].fields[j].type)
							if (fieldType.class == DanaType.FUNCTION)
								{
								nt.fields = new DanaTypeField[](nt.fields, clone types[i].fields[j])
								}
							}
						
						compositeSourceName = new char[](compositeSourceName, "$(CMSRC_SEP)$(types[i].fromFile)[$(countFunction)]")
						
						nt.fromFile = compositeSourceName

						flattened = new DanaType[](flattened, nt)
						expansion = true
						}
						else if (types[i].class == DanaType.DATA && (flt = dncUtil.findType(flattened, types[i].extendsType)) != null)
						{
						DanaType nt = clone types[i]
						
						nt.fields = null

						//merge regular fields (not constants)
						for (int j = 0; j < flt.fields.arrayLength; j ++)
							{
							if (flt.fields[j].qualifier.arrayLength == 0)
								{
								nt.fields = new DanaTypeField[](nt.fields, clone flt.fields[j])
								}
							}
						
						for (int j = 0; j < types[i].fields.arrayLength; j ++)
							{
							nt.fields = new DanaTypeField[](nt.fields, clone types[i].fields[j])
							}
						
						flattened = new DanaType[](flattened, nt)
						expansion = true
						}
					}
				}
			}
		
		//invent array types for every new_array instruction, since these can be present without a variable declaration of that type (reflection)
		DanaType newArrays[] = makeNewArrayTypes(flattened, tree)
		for (int i = 0; i < newArrays.arrayLength; i++)
			{
			if (dncUtil.findType(flattened, newArrays[i].name) == null)
				{
				flattened = new DanaType[](flattened, newArrays[i])
				}
			}
		
		setTypeRecursionPoints(flattened)

		setTypeNoCycle(flattened)

		//invent fixed-size char[]-array types for every object type name, in case they're needed for dynamic instantiation instructions
		
		for (int i = 0; i < types.arrayLength; i++)
			{
			if (types[i].class == DanaType.INTERFACE)
				{
				if (dncUtil.findType(flattened, "char[$(types[i].name.arrayLength)]") == null)
					{
					DanaType nt = new DanaType(DanaType.ARRAY, "char[$(types[i].name.arrayLength)]", "char[$(types[i].name.arrayLength)]", null, new DanaTypeField(type = "char"), types[i].name.arrayLength)
					flattened = new DanaType[](flattened, nt)
					}
				}
			}
		
		//invent fixed-size primitive-type arrays for every array constant
		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)

							if (declareType.class == DanaType.ARRAY && declareType.storageSize == 0 && sw.subTokens.next.subType == TokSubTypes.NEW_ARRAY_CONSTRUCT)
								{
								//count the number of cells
								DanaToken cw = sw.subTokens.next.subTokens.next.subTokens
								DanaType fieldType = dncUtil.findType(types, declareType.fields[0].type)
								char pTypeName[] = fieldType.name

								int count = 0

								while (cw != null)
									{
									count ++
									cw = cw.next
									}
								
								//out.println("found $count params of array construct for $(pTypeName)[]")

								if (flattened.findFirst(DanaType.[name], new DanaType(name = "$(pTypeName)[$count]")) == null)
									{
									DanaType nt = new DanaType(DanaType.ARRAY, "$(pTypeName)[$count]", "$(pTypeName)[$count]", null, new DanaTypeField(type = "$(pTypeName)"), count)
									flattened = new DanaType[](flattened, nt)
									}
								}
							}
						}
					
					sw = sw.next
					}
				}
			
			pw = pw.next
			}

		return flattened
		}
}
Revision history
To propose a new revision to this entity, use dana source put -uc your/new/version.dn -n util.compiler.TypeShaper -m "reason for update" -u yourUsername
Version 2 (this version) by barry
Notes for this version: Adds cycle status to arrays.
Version 1 by barry