public CrstType(string name) { m_name = name; m_level = CrstUnassigned; m_acquiredBeforeCrsts = new List <CrstType>(); m_group = null; m_defined = false; }
// Declare a SameLevelAs relationship between the two Crst types given. Groups will be assigned, created // or merged as required to maintain our guarantees (each CrstType is a member of at most one group and // all CrstTypes involved in the same transitive closure of a SameLevelAs relationship are members of one // group). public static void Join(CrstType crst1, CrstType crst2) { CrstTypeGroup group; if (crst1 == crst2) { // In this case the type refers to itself. Create a singleton group for this type if it doesn't // already exist. if (crst1.Group == null) { group = new CrstTypeGroup(); group.m_members.Add(crst1); s_groups.Add(group); crst1.Group = group; } } else if (crst1.Group == null && crst2.Group == null) { // Neither types belong to a group already. So we can create a new one and add both types to it. group = new CrstTypeGroup(); group.m_members.Add(crst1); group.m_members.Add(crst2); s_groups.Add(group); crst1.Group = group; crst2.Group = group; } else if (crst1.Group == null) { // The first type doesn't belong to a group yet but the second does. So we can simply add the // first type to the second group. group = crst2.Group; group.m_members.Add(crst1); crst1.Group = group; } else if (crst2.Group == null) { // As for the case above but the group/no-group positions are reversed. group = crst1.Group; group.m_members.Add(crst2); crst2.Group = group; } else if (crst1.Group != crst2.Group) { // Both types belong to different groups so we'll have to merge them. Add the members of group 2 // to group 1 and throw away group 2. group = crst1.Group; CrstTypeGroup absorbGroup = crst2.Group; foreach (CrstType crst in absorbGroup.m_members) { group.m_members.Add(crst); crst.Group = group; } s_groups.Remove(absorbGroup); } // The only case left is when both types are already in the same group and there's no work needed in // this case. }
// Parse a single Crst type definition. void ParseCrst() { // The next token had better be an identifier (the Crst type name). Token token = NextToken(); if (token.Id != KeywordId.Id) { throw new UnexpectedTokenError(token, KeywordId.Id); } // The Crst instance might already exist in the dictionary (forward references to a Crst type cause // these entries to auto-vivify). But in that case the entry better not be marked as 'Defined' which // would indicate a double declaration. CrstType crst; if (m_crsts.ContainsKey(token.Text)) { crst = m_crsts[token.Text]; if (crst.Defined) { throw new ParseError(String.Format("Duplicate definition for CrstType '{0}'", token.Text), token); } } else { // Otherwise this Crst type hasn't been seen thus far so we allocate a new instance and add it to // the dictionary. crst = new CrstType(token.Text); m_crsts.Add(crst.Name, crst); } // We're defining, not just referencing this type. crst.Defined = true; // Parse any attributes inside this definition (until we see an 'End' token). bool parsingCrst = true; while (parsingCrst) { // Get the next token. Either some attribute keyword or 'End'. token = NextToken(); List <CrstType> list; switch (token.Id) { case KeywordId.AcquiredBefore: // Simply parse the following list of Crst types into the current type's AcquiredBefore list. ParseList(crst.AcquiredBeforeList); break; case KeywordId.AcquiredAfter: // AcquiredAfter is trickier. To make the ranking algorithm's life easier we actually // normalize all rules to the AcquiredBefore form (see LevelCrsts() for the reasoning). So we // capture the list of Crst types that follow the AcquiredAfter keyword and then append the // current type to the AcquiredBefore list of each type found. list = new List <CrstType>(); ParseList(list); foreach (CrstType priorCrst in list) { priorCrst.AcquiredBeforeList.Add(crst); } break; case KeywordId.SameLevelAs: // Parse the following list of Crst types them let the CrstTypeGroup class handle the // resulting updates to the type groups we're currently maintaining. See the comments for the // CrstTypeGroup class for more details. list = new List <CrstType>(); ParseList(list); foreach (CrstType sameLevelCrst in list) { CrstTypeGroup.Join(crst, sameLevelCrst); } break; case KeywordId.Unordered: crst.Level = CrstType.CrstUnordered; break; case KeywordId.End: parsingCrst = false; break; default: throw new UnexpectedTokenError(token, KeywordId.AcquiredBefore, KeywordId.AcquiredAfter, KeywordId.SameLevelAs, KeywordId.Unordered); } } }
// Peform checking of the Crst type definitions we've read just read. Various forms of logic error are // scanned for including cycles in the dependency graph. Returns true if no errors are found. If false is // returned a descriptive error message will have already been written to the console. bool ValidateCrsts() { // Look at each Crst type definition in turn. foreach (CrstType crst in m_crsts.Values) { // Catch Crst types that are referenced but never defined. if (!crst.Defined) { Console.WriteLine(String.Format("Error: CrstType 'Crst{0}' is referenced without being defined", crst.Name)); return(false); } // Catch the use of the 'Unordered' attribute alongside the 'AcquiredBefore' attribute (which // indicates an ordering). if (crst.Level == CrstType.CrstUnordered && (crst.AcquiredBeforeList.Count > 0 || crst.Group != null)) { Console.WriteLine(String.Format("Error: CrstType 'Crst{0}' is declared as both unordered and acquired before 'Crst{1}'", crst.Name, crst.AcquiredBeforeList[0].Name)); return(false); } // Catch the use of the 'Unordered' attribute alongside the 'SameLevelAs' attribute (which // indicates an ordering). if (crst.Level == CrstType.CrstUnordered && crst.Group != null) { Console.WriteLine(String.Format("Error: CrstType 'Crst{0}' is declared as both unordered and in the same level as another CrstType", crst.Name)); return(false); } // Catch the simple cycle where the Crst type depends on itself. if (crst.AcquiredBeforeList.Contains(crst)) { Console.WriteLine(String.Format("Error: CrstType 'Crst{0}' is declared as being acquired before itself", crst.Name)); return(false); } // Look for deeper cycles using a recursive algorithm in 'FindCycle()'. List <CrstType> cycleList = new List <CrstType>(); if (FindCycle(crst, crst, cycleList)) { Console.WriteLine(String.Format("Error: CrstType 'Crst{0}' is involved in a dependency cycle with the following CrstTypes:", crst.Name)); foreach (CrstType cycleCrst in cycleList) { Console.WriteLine(String.Format(" Crst{0}", cycleCrst.Name)); } return(false); } } // Perform normalization of each set of Crst types that are included in the same group (i.e. have a // 'SameLevelAs' relationship). Normalization means that each Crst type in a group will have exactly // the same set of dependency rules as all the others. CrstTypeGroup.NormalizeAllRules(); // The normalization process could have introduced cycles in the dependency graph so run the cycle // detection pass again. We do separate passes like this since normalizing can lead to less intuitive // error messages if a cycle is found: so if the cycle exists before normalization takes place we want // to generate an error message then. foreach (CrstType crst in m_crsts.Values) { List <CrstType> cycleList = new List <CrstType>(); if (FindCycle(crst, crst, cycleList)) { Console.WriteLine(String.Format("Error: CrstType 'Crst{0}' is involved in a dependency cycle with the following CrstTypes:", crst.Name)); foreach (CrstType cycleCrst in cycleList) { Console.WriteLine(String.Format(" Crst{0}", cycleCrst)); } Console.WriteLine("Note that the cycle was detected only after 'SameLevelAs' processing was performed so some CrstType dependencies are implied by peer CrstTypes"); return(false); } } return(true); }
public CrstType(string name) { m_name = name; m_level = CrstUnassigned; m_acquiredBeforeCrsts = new List<CrstType>(); m_group = null; m_defined = false; }