public string GenerateMcFunction(BuildEnvironment env) { if (_compiled != null) { return(_compiled); } // Compile to vanilla mcfunction var builder = new StringBuilder(); // Append header string header = env.Constants["compiled_header"]; header = header.Replace("{date}", DateTime.Now.ToString("yyyy-MM-dd HH:mm")); header = header.Replace("{file}", Id); builder.Append("# " + header + "\n"); // Compile commands foreach (Command.Command command in Commands) { string compiled = command.Compile(env); if (compiled != null) { builder.Append(compiled + "\n"); } } _compiled = builder.ToString(); return(_compiled); }
/// <summary> /// Compile MCFunction. /// </summary> /// <param name="env">Build environment</param> /// <param name="generateOutput">Should a text output be generated?</param> public void Compile(BuildEnvironment env, bool generateOutput = true) { // Apply custom commands var toRemove = new List <Command.Command>(); int i = 0; while (i < Commands.Count) { Command.Command command = Commands[i]; foreach (ICustomCommand customCommand in CustomCommands) { if (customCommand.DoesApply(env, command)) { ApplyResult result = customCommand.Apply(env, command); // Strip command from output if requested if (result.StripFromOutput) { toRemove.Add(command); } // Add replacement commands Commands.InsertRange(i + 1, result.AddCommands); } } i++; } // Remove compiler commands (they are not meant for the output) foreach (Command.Command command in toRemove) { Commands.Remove(command); } toRemove.Clear(); // Split function blocks into seperate McFunctions SplitFunctionBlocks(env); if (generateOutput) { // Compile to vanilla MCFunction file GenerateMcFunction(env); } }
/// <summary> /// Save this MCFunction and optionally it's children. /// </summary> /// <param name="env"></param> /// <param name="saveChildren"></param> public void Save(BuildEnvironment env, bool saveChildren = true) { string output = env.GetPath(Id, "mcfunction", env.OutputPath); Directory.GetParent(output).Create(); File.WriteAllText(output, GenerateMcFunction(env)); Logger.Info("Saved to: " + output); if (saveChildren) { foreach (McFunction function in ChildFunctions) { function.Save(env); } } }
/// <summary> /// Get a list of files that need to be compiled. /// </summary> private static List <string> GetFiles(BuildEnvironment env) { var paths = Directory.GetFiles(env.Path, "*.mcfunction", SearchOption.AllDirectories); var list = new List <string>(); foreach (string path in paths) { string localPath = path.Substring(env.Path.Length).Replace('\\', '/'); Match match = Regex.Match(localPath, @"^\/data\/([^\/?<>\\:*|""]+)\/functions\/([^?<>\\:*|""]+)\.mcfunction$"); if (!match.Success) { continue; } list.Add(match.Groups[1].Value + ":" + match.Groups[2].Value); } return(list); }
/// <summary> /// Search for function blocks and split them out into their own McFunctions. /// </summary> public void SplitFunctionBlocks(BuildEnvironment env) { // Iterate over commands, finding function blocks in the *root level* // We then make take those commands into their own McFunction and run this function again on the sub McFunction. var functionBlocks = new Dictionary <Argument, Range>(); int nesting = 0; int i = 0; foreach (Command.Command command in Commands) { Argument arg = command.Arguments.LastOrDefault(); if (arg == null) { continue; } if (arg.Tokens.FirstOrDefault() is OpenFunctionBlockToken) { nesting++; if (nesting > FunctionBlockNestingLimit) { throw new Exception($"Exceeded function block nesting limit of {FunctionBlockNestingLimit}!"); } if (nesting == 1) { // Root level function block. functionBlocks.Add(arg, new Range(i, -1)); } } if (arg.Tokens.FirstOrDefault() is CloseFunctionBlockToken) { nesting--; if (nesting < 0) { throw new Exception("Unexpected additional function block bracket!"); } if (nesting == 0) { // Back to root nesting, close this function block functionBlocks.Last().Value.Maximum = i; } } i++; } if (nesting > 0) { throw new Exception("Unclosed function block!"); } // Move commands into child McFunctions int functionId = 0; foreach (KeyValuePair <Argument, Range> range in functionBlocks) { string parent = Id.Substring(0, Math.Max(Id.LastIndexOf("/"), Id.LastIndexOf(":")) + 1); string name = Id.Substring(Math.Max(Id.LastIndexOf("/"), Id.LastIndexOf(":")) + 1); if (!parent.EndsWith("_subs/")) { parent += "_subs/"; } var mcFunction = new McFunction(parent + name + "_" + functionId++); ChildFunctions.Add(mcFunction); // Move commands mcFunction.Commands.AddRange(Commands.GetRange(range.Value.Minimum + 1, range.Value.Length - 1)); Commands.RemoveRange(range.Value.Minimum + 1, range.Value.Length); // Replace argument with function call range.Key.Tokens.Clear(); range.Key.Tokens.Add(new TextToken("function " + mcFunction.Id)); Logger.Debug($"Function block \"{mcFunction.Id}\" created from commands {functionBlocks.Last().Value} in {Id}!"); // Run split function blocks on child so they can do their own function blocks. mcFunction.SplitFunctionBlocks(env); } }
public static void Compile(Options.CompileDatapack options) { Logger.Info($"Compile datapack\n\t{options.SourceDirectory} -> {options.OutputDirectory}"); var env = new BuildEnvironment(options.SourceDirectory); env.OutputPath = options.OutputDirectory; // Try to add custom constants into build environment var constants = options.ToConstantsDictonary(); if (constants != null) { foreach (var constant in constants) { env.Constants.Add(constant.Key, constant.Value); } Logger.Info($"{constants.Count} command line constant{(constants.Count == 1 ? "" : "s")} provided."); } // Get file list List <string> files = GetFiles(env); Logger.Info($"{files.Count} files found to be compiled."); // Start parse & compile Logger.Info("Starting parse & compile..."); var parser = new Parser.Parser(env); Timer.Start("parse_and_compile"); var totalParseTime = new TimeSpan(); var totalCompileTime = new TimeSpan(); foreach (string file in files) { // Parse Logger.Info($"Parsing {file}..."); Timer.Start("parse"); McFunction mcFunction = parser.Parse(file); totalParseTime = totalParseTime.Add(Timer.End("parse")); Logger.Debug($"Parsed {file}. {mcFunction.Commands.Count} commands found."); // Compile Logger.Info($"Compiling {file}..."); Timer.Start("compile"); mcFunction.Compile(env); totalCompileTime = totalCompileTime.Add(Timer.End("compile")); Logger.Debug($"Compiled {file}."); // Save mcFunction.Save(env); } TimeSpan parseAndCompileTime = Timer.End("parse_and_compile"); Logger.Info($"Datapack compile finished. Took {Math.Round(parseAndCompileTime.TotalSeconds, 3)}s"); Logger.Debug($"Total parse time: {Math.Round(totalParseTime.TotalSeconds, 3)}s"); Logger.Debug($"Total compile time: {Math.Round(totalCompileTime.TotalSeconds, 3)}s"); }