/// <summary> /// This method is responsible for parsing an object property /// reference line from inside of a .con or .ai file, and converting /// the property into a C# property /// </summary> /// <remarks> /// The type of value is figured out within the method, but only certain types of /// object types can be parsed here. /// </remarks> /// <param name="token">The token for this ObjectProperty</param> /// <param name="objectLevel">Specifies the nested object level for this Property</param> public virtual void Parse(Token token, int objectLevel = 0) { // Seperate our property name and values TokenArgs tokenArgs = token.TokenArgs; string propName = tokenArgs.PropertyNames[objectLevel]; // Fetch our property that we are setting the value to KeyValuePair <string, PropertyInfo> prop = GetProperty(propName); Type propType = prop.Value.PropertyType; bool isCollection = propType.GetInterface("IObjectPropertyCollection") != null; // Get the value that is set var value = prop.Value.GetValue(this); // Is this an object method, or Object Property? if (propType.BaseType.Name == "ObjectMethod") { // Object methods are always instantiated in the object's constructor ObjectMethod method = (ObjectMethod)value; ConFileEntry item = method.Invoke(token); // Add item to the file entry list if (item != null) { token.File?.AddEntry(item, token); } } else { // If the value is null, then we create a new object property instance ObjectProperty obj = (ObjectProperty)value; if (obj == null) { // Create instance and add it to the entries list obj = ObjectProperty.Create(prop.Value, this, token); // Add entry? Property Collections add thier own properties if (!isCollection) { token.File?.AddProperty(obj); } } // Create our instance, and parse obj.SetValue(token, objectLevel); prop.Value.SetValue(this, obj); } }
internal void AddPropertyAfter(ObjectProperty property, ConFileEntry afterItem) { // Make sure this entry doesnt already exist if (Entries.IndexOf(property) > 0) throw new Exception("The specified ObjectProperty already exists in this confile."); // Find the owner object int index = Entries.IndexOf(afterItem) + 1; if (index == 0) throw new Exception("The specified \"afterObject\" does not exist in this ConFile"); // Insert item if (index == Entries.Count) Entries.Add(property); else Entries.Insert(index, property); }
/// <summary> /// Gets the Reference of an Expression, that occurs before the specified /// entry. /// </summary> /// <param name="name">The name of the variable or constant expression</param> /// <param name="beforeEntry"> /// The entry where this variable or constant expression is referenced /// </param> /// <returns> /// Returns the last reference value of the specifed expression, occuring /// before the <paramref name="beforeEntry"/> /// </returns> public Expression GetExpressionReference(string name, ConFileEntry beforeEntry) { // Check to see if this expression exists if (!Expressions.ContainsKey(name)) goto Undefined; // Grab the last reference before this confile entry int index = Entries.IndexOf(beforeEntry); if (index == -1) throw new Exception("The specified entry does not exist in this ConFile"); // Now find the last set expression reference value for (int i = index - 1; i >= 0; i--) { ConFileEntry entry = Entries[i]; TokenType kind = entry.Token.Kind; if (kind == TokenType.Constant || kind == TokenType.Variable) { // cast to Expression Expression exp = entry as Expression; if (exp.Name == name) { // This expression hasnt been assigned yet.. shame /*** * We have no way yet to determine if a variable is assigned * inside an If/While statement, so skip for now * * if (String.IsNullOrWhiteSpace(exp.Value)) throw new Exception($"Value cannot be null; Expression \"{name}\" is unassigned"); */ return exp; } } } // If we are here, the expression was not defined yet Undefined: { string err; if (name.StartsWith("c")) err = $"Undefined constant \"{name}\""; else err = $"Undefined variable \"{name}\""; throw new Exception(err); } }
/// <summary> /// Adds a new object to this confile, and returns it's reference /// </summary> /// <param name="entry"></param> /// <param name="token"></param> /// <returns></returns> public void AddEntry(ConFileEntry entry, Token token) { if (token.Kind == TokenType.RemComment) { return; } else if (token.Kind == TokenType.ObjectStart) { Objects.Add((ConFileObject)entry); } else if (token.Kind == TokenType.ActiveSwitch) { // Create a new reference and add it var reference = new ObjectReference() { Token = token, Object = (ConFileObject)entry }; References.Add(reference); // Set entry to the object reference entry = reference; } else if (entry is Expression) { Expression exp = entry as Expression; if (!Expressions.ContainsKey(exp.Name)) { // Add the expression Expressions.Add(exp.Name, new List<Expression>() { exp }); } } // Always add the entry Entries.Add(entry); }