예제 #1
0
        /// <summary>
        /// Stores an ERROR message to be logged
        /// </summary>
        /// <param name="error">The message text</param>
        /// <param name="file">The ConFile being processed for this message</param>
        /// <param name="line">The ConFile line number being processed for this message</param>
        public static void Error(string error, ConFile file = null, int line = 0, Exception exception = null)
        {
            if (Enabled)
            {
                LogEntry entry = new LogEntry()
                {
                    Type         = LogEntryType.Error,
                    Message      = error,
                    File         = file,
                    Line         = line,
                    ExceptionObj = exception
                };

                lock (_syncObj)
                {
                    Messages.Add(entry);
                    Errors.Add(entry);

                    if (OnMessageLogged != null)
                    {
                        OnMessageLogged(null, entry);
                    }
                }
            }
        }
예제 #2
0
        /// <summary>
        /// Invokes the specified script command
        /// </summary>
        /// <param name="input">The ConFile formated command to execute in this scope</param>
        /// <param name="file">If a file is specified, the result of this command will also be saved
        /// in the <see cref="ConFile"/> specified here, so it can be stored when saved.</param>
        /// <example>
        ///     input => "ObjectTemplate.activeSafe GenericFireArm chrif_type95"
        /// </example>
        public void Execute(string input, ConFile file = null)
        {
            // === Create Token
            Token token = Tokenizer.Tokenize(input);

            token.File = file;

            // === Execute on Scope
            ScriptEngine.ExecuteInScope(token, this);
        }
예제 #3
0
        /// <summary>
        /// Stores an INFO message to be logged
        /// </summary>
        /// <param name="Message">The message text</param>
        /// <param name="WorkingFile">The ConFile being processed for this message</param>
        /// <param name="LineNumber">The ConFile line number being processed for this message</param>
        public static void Info(string Message, ConFile WorkingFile = null, int LineNumber = 0)
        {
            if (Enabled)
            {
                LogEntry entry = new LogEntry()
                {
                    Type = LogEntryType.Info,
                    Message = Message,
                    File = WorkingFile,
                    Line = LineNumber
                };

                lock (_syncObj)
                {
                    Messages.Add(entry);

                    if (OnMessageLogged != null)
                        OnMessageLogged(null, entry);
                }
            }
        }
예제 #4
0
        /// <summary>
        /// Loads a .con or .ai file, and converts the script objects into C# objects
        /// </summary>
        /// <param name="filePath">The path to the .con / .ai file</param>
        /// <param name="scope">
        /// Sets the scope in which the objects from this <see cref="ConFile"/>
        /// will be loaded into.
        /// </param>
        /// <param name="runInstruction">
        /// Defines how the script engine should handle nested file includes
        /// </param>
        /// <exception cref="Exception">
        /// Thrown if there is a problem loading the script file in any way.
        /// </exception>
        /// <returns>Returns the parsed bf2 script file.</returns>
        public static async Task <ConFile> LoadFileAsync(string filePath, Scope scope      = null,
                                                         ExecuteInstruction runInstruction = ExecuteInstruction.Skip)
        {
            // Create new ConFile contents
            ConFile cFile = new ConFile(filePath, scope);
            Dictionary <int, string> fileContents = new Dictionary <int, string>();
            int lineNum = 1;

            // Create Log
            Logger.Info("Loading file: " + filePath);

            // Open the confile, and read its contents
            using (FileStream fStream = new FileStream(filePath, FileMode.Open, FileAccess.Read))
                using (StreamReader reader = new StreamReader(fStream))
                {
                    // While we can keep reading
                    while (!reader.EndOfStream)
                    {
                        string line = await reader.ReadLineAsync();

                        fileContents.Add(lineNum++, line);
                    }
                }

            // Parse the contents of the Con / Ai file
            try
            {
                await ParseFileLines(fileContents, cFile, runInstruction);
            }
            catch
            {
                // Pass the exception up
                throw;
            }

            // Return the file
            return(cFile);
        }
예제 #5
0
        /// <summary>
        /// Stores an INFO message to be logged
        /// </summary>
        /// <param name="Message">The message text</param>
        /// <param name="WorkingFile">The ConFile being processed for this message</param>
        /// <param name="LineNumber">The ConFile line number being processed for this message</param>
        public static void Info(string Message, ConFile WorkingFile = null, int LineNumber = 0)
        {
            if (Enabled)
            {
                LogEntry entry = new LogEntry()
                {
                    Type    = LogEntryType.Info,
                    Message = Message,
                    File    = WorkingFile,
                    Line    = LineNumber
                };

                lock (_syncObj)
                {
                    Messages.Add(entry);

                    if (OnMessageLogged != null)
                    {
                        OnMessageLogged(null, entry);
                    }
                }
            }
        }
예제 #6
0
        /// <summary>
        /// Loads a .con or .ai file, and converts the script objects into C# objects
        /// </summary>
        /// <param name="filePath">The path to the .con / .ai file</param>
        /// <param name="scope">
        /// Sets the scope in which the objects from this <see cref="ConFile"/> 
        /// will be loaded into.
        /// </param>
        /// <param name="runInstruction">
        /// Defines how the script engine should handle nested file includes
        /// </param>
        /// <exception cref="Exception">
        /// Thrown if there is a problem loading the script file in any way.
        /// </exception>
        /// <returns>Returns the parsed bf2 script file.</returns>
        public static async Task<ConFile> LoadFileAsync(string filePath, Scope scope = null, 
            ExecuteInstruction runInstruction = ExecuteInstruction.Skip)
        {
            // Create new ConFile contents
            ConFile cFile = new ConFile(filePath, scope);
            Dictionary<int, string> fileContents = new Dictionary<int, string>();
            int lineNum = 1;

            // Create Log
            Logger.Info("Loading file: " + filePath);

            // Open the confile, and read its contents
            using (FileStream fStream = new FileStream(filePath, FileMode.Open, FileAccess.Read))
            using (StreamReader reader = new StreamReader(fStream))
            {
                // While we can keep reading
                while (!reader.EndOfStream)
                {
                    string line = await reader.ReadLineAsync();
                    fileContents.Add(lineNum++, line);
                }
            }

            // Parse the contents of the Con / Ai file
            try
            {
                await ParseFileLines(fileContents, cFile, runInstruction);
            }
            catch
            {
                // Pass the exception up
                throw;
            }

            // Return the file
            return cFile;
        }
예제 #7
0
        /// <summary>
        /// Stores an ERROR message to be logged
        /// </summary>
        /// <param name="error">The message text</param>
        /// <param name="file">The ConFile being processed for this message</param>
        /// <param name="line">The ConFile line number being processed for this message</param>
        public static void Error(string error, ConFile file = null, int line = 0, Exception exception = null)
        {
            if (Enabled)
            {
                LogEntry entry = new LogEntry()
                {
                    Type = LogEntryType.Error,
                    Message = error,
                    File = file,
                    Line = line,
                    ExceptionObj = exception
                };

                lock (_syncObj)
                {
                    Messages.Add(entry);
                    Errors.Add(entry);

                    if (OnMessageLogged != null)
                        OnMessageLogged(null, entry);
                }
            }
        }
예제 #8
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();
        }
예제 #9
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();
        }