/// <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"); }