/// <summary>
        /// Takes an array of string values, and converts it to the proper value type for
        /// this instance's Generic Type
        /// </summary>
        /// <param name="ValueParams">The string value's to convert, and set the
        /// Value of this instance to.
        /// </param>
        public override void SetValue(Token token, int objectLevel = 0)
        {
            Type PropertyType = typeof(T);

            // ===
            // DONOT use this ObjectProperties TOKEN! breaks collections!
            TokenArgs tokenArgs = token.TokenArgs;

            // ===

            // Check for ConFileObjects
            if (typeof(ConFileObject).IsAssignableFrom(PropertyType))
            {
                // define vars
                ConFileObject obj = (ConFileObject)(object)Value;

                // Load a new object if we are undefined
                if (obj == null)
                {
                    // Is this a defined object?
                    string name = tokenArgs.Arguments.Last();
                    //var type = ScriptEngine.GetTemplateType(PropertyType);

                    // Check for the object in this properties owner's Scope
                    if (Token.File.Scope.ContainsObject(name, PropertyType))
                    {
                        obj      = Token.File.Scope.GetObject(name, PropertyType, Token);
                        Argument = new ValueInfo <T>((T)(object)obj);
                    }
                    else
                    {
                        string error = $"ObjectProperty \"{Name}\" requires an existing object that is not defined!";
                        throw new Exception(error);
                    }
                }
                else
                {
                    // Let the child class parse itself
                    obj.Parse(token, ++objectLevel);
                    Argument = new ValueInfo <T>((T)(object)obj);
                }
            }
            else if (PropertyType.IsGenericType)
            {
                // Check for Generic types, we don't support these!
                throw new Exception($"Invalid Generic Type found \"{PropertyType}\"");
            }
            else
            {
                // Since we are not an array property, make sure we didnt get
                // an array passed in the con file
                if (tokenArgs.Arguments.Length > 1)
                {
                    throw new Exception($"Expecting single value, but got an Array for \"{Name}\"");
                }

                // Since we are not an array, extract our only value
                Argument = new ValueInfo <T>(ConvertValue <T>(tokenArgs.Arguments[0], PropertyType));
            }
        }
Exemple #2
0
        /// <summary>
        /// Parses the contents of a .con or .ai file, and converts the contents into C# objects
        /// </summary>
        /// <param name="fileContents">A dictionary of file contents [LineNumber => LineContents]</param>
        /// <param name="workingFile">A reference to the ConFile that contains the contents</param>
        internal static async Task ParseFileLines(
            Dictionary <int, string> fileContents,
            ConFile workingFile,
            ExecuteInstruction run)
        {
            // ============
            // First we convert our confile lines into parsed tokens
            // ============
            Token[]   fileTokens = Tokenizer.Tokenize(workingFile, ref fileContents);
            TokenArgs tokenArgs;
            Scope     currentScope = workingFile.Scope;

            // ============
            // Now we create an object reference of all objects in the Confile
            // before parsing the object properties, which **can** reference an object
            // in the same file before its defined
            // NOTE: Do not create object references for .Active and .safeActive
            // ============
            foreach (Token token in fileTokens.Where(x => x.Kind == TokenType.ObjectStart).OrderBy(x => x.Position))
            {
                // Create the object
                var           Method   = token.TokenArgs.ReferenceType.GetMethod(token.TokenArgs.PropertyName);
                ConFileObject template = Method.Invoke(token);

                // Finally, register the object with the ObjectManager
                currentScope.AddObject(template, token);
                Logger.Info($"Created {token.TokenArgs.ReferenceType} \"{template.Name}\"", workingFile, token.Position);
            }

            // ============
            // Finally, we load all of the object properties, and assign them to their
            // respective objects
            // ============

            // Create our needed objects
            RemComment    comment    = null;
            ConFileObject currentObj = null;
            ReferenceType type;
            var           builder = new StringBuilder();

            // We use a for loop here so we can skip rem blocks and statements
            for (int i = 0; i < fileTokens.Length; i++)
            {
                // Grab token value
                Token token = fileTokens[i];
                try
                {
                    switch (token.Kind)
                    {
                    case TokenType.ObjectStart:
                    case TokenType.ActiveSwitch:
                        // NOTE: the object was created before this loop!
                        currentObj = currentScope.GetObject(token);
                        currentScope.SetActiveObject(currentObj);

                        // === Objects are already added to the working file before hand === //
                        // Add object reference to file
                        workingFile.AddEntry(currentObj, token);

                        // Reset comment
                        comment = null;

                        // Log
                        Logger.Info($"Loading object properties for \"{currentObj.Name}\"",
                                    workingFile, token.Position
                                    );
                        break;

                    case TokenType.ObjectProperty:
                        // Convert args to an object
                        tokenArgs = token.TokenArgs;

                        // Get the last used object
                        type       = tokenArgs.ReferenceType;
                        currentObj = currentScope.GetActiveObject(type);

                        // Make sure we have an object to work with and the object
                        // reference matches our current working object
                        if (currentObj == null)
                        {
                            // If we are here, we have an issue...
                            string error = $"Failed to set property \"{token.TokenArgs.ReferenceType.Name}.\""
                                           + $"{token.TokenArgs.PropertyName}. No object reference set!";
                            throw new ParseException(error, token);
                        }

                        // Let the object parse its own lines...
                        try
                        {
                            currentObj.Parse(token);

                            // Ensure comment is null
                            comment = null;
                        }
                        catch (Exception e)
                        {
                            Logger.Error(e.Message, workingFile, token.Position, e);
                            throw;
                        }
                        break;

                    case TokenType.RemComment:
                        // Create a new comment if we need to
                        if (comment == null)
                        {
                            comment = new RemComment(token);
                        }

                        // Add comment to the current string
                        comment.AppendLine(token.Value);
                        break;

                    case TokenType.BeginRem:
                        RemComment rem = new RemComment(token);
                        rem.IsRemBlock = true;

                        // Skip every line until we get to the endRem
                        builder.AppendLine(token.Value);
                        i = ScopeUntil(TokenType.EndRem, fileTokens, i, builder);

                        // Set rem value
                        rem.Value = builder.ToString().TrimEnd();
                        workingFile.AddEntry(rem, rem.Token);

                        // Clear the string builder
                        builder.Clear();
                        break;

                    case TokenType.IfStart:
                        Statement statement = new Statement(token);
                        if (token.Kind == TokenType.IfStart)
                        {
                            // Skip every line until we get to the endIf
                            builder.AppendLine(token.Value);
                            i = ScopeUntil(TokenType.EndIf, fileTokens, i, builder);

                            // Clear the string builder
                            statement.Token.Value = builder.ToString().TrimEnd();
                            builder.Clear();
                        }

                        // Add entry
                        workingFile.AddEntry(statement, statement.Token);
                        break;

                    case TokenType.Run:
                    case TokenType.Include:
                        // Just add to as a string
                        RunStatement stmt = new RunStatement(token);
                        workingFile.AddEntry(stmt, stmt.Token);

                        // Do we execute the statement?
                        if (run == ExecuteInstruction.Skip)
                        {
                            continue;
                        }

                        // Create new scope for execution
                        Scope runScope = currentScope;

                        // Are we executing in a new scope?
                        if (run == ExecuteInstruction.ExecuteInNewScope)
                        {
                            // For now, we just inherit the parent scope type
                            runScope = new Scope(currentScope, currentScope.ScopeType);
                            runScope.MissingObjectHandling = MissingObjectHandling.CheckParent;
                        }

                        // Get the filepath
                        string filePath = Path.GetDirectoryName(workingFile.FilePath);
                        string fileName = Path.Combine(filePath, stmt.FileName);

                        // Define file arguments
                        runScope.SetArguments(stmt.Arguments);

                        // Load the file
                        try
                        {
                            ConFile include = await LoadFileAsync(fileName, runScope, run);

                            workingFile.ExecutedIncludes.Add(include);
                        }
                        catch (FileNotFoundException)     // Only acceptable exception
                        {
                            fileName = Path.GetFileName(fileName);
                            Logger.Warning($"Failed to run file \"{fileName}\". File Not Found",
                                           workingFile, token.Position);
                        }
                        break;

                    case TokenType.Constant:
                    case TokenType.Variable:
                        // Set the new expression reference in Scope
                        Expression exp = new Expression(token);
                        currentScope.Expressions[exp.Name] = exp;

                        // Add expression to the confile as well
                        workingFile.AddEntry(exp, exp.Token);
                        break;

                    case TokenType.None:
                        // Dont attach comment to a property if we have an empty line here
                        if (comment != null)
                        {
                            workingFile.AddEntry(comment, comment.Token);
                            comment = null;
                        }

                        // Throw error if the line is not empty
                        if (!String.IsNullOrWhiteSpace(token.Value))
                        {
                            string message = $"Unable to parse file entry \"{token.Value}\" on line {token.Position}";
                            throw new ParseException(message, token);
                        }
                        break;
                    }
                }
                catch (Exception e)
                {
                    Logger.Error(e.Message, token.File, token.Position, e);
                    throw;
                }
            }

            // Finalize this confile
            workingFile.Finish();
        }