static void Main(string[] args) { var fileCfg = ParseCommandLine(args, out string cfgFilename); var stackCfg = new StackCfg(); if (cfgFilename != null) { try { stackCfg = StackCfg.LoadJson(cfgFilename); } catch (Exception e) { var defaultCfgFile = "whomst-default.json"; var excMessage = $"Could not load config file {cfgFilename}."; var helpfulMessage = excMessage + $"Generating {defaultCfgFile}. Use it with /cfg={defaultCfgFile})"; Console.WriteLine(helpfulMessage); File.WriteAllText( defaultCfgFile, JsonConvert.SerializeObject(StackCfg.Default(), new JsonSerializerSettings { Formatting = Formatting.Indented, NullValueHandling = NullValueHandling.Ignore, })); throw new AggregateException(excMessage, e); } } // NOTE cfg.PrepareForUse unnecessary, will get called in WhomstJob ctor WhomstGlobalJob.Execute(fileCfg, stackCfg); }
public void RunJob(FileCfg fileCfg, StackCfg stackCfg) { var job = new WhomstJob { SharedStackCfg = stackCfg, SharedFileCfg = fileCfg, }; { job.StackCfg = StackCfg.Default().MergeOverWith(job.SharedStackCfg); job.FileCfg = job.StackCfg.GetProtoFileCfg(job.SharedFileCfg.InputFileExt) .MergeOverWith(job.SharedFileCfg); job.FileCfg.Validate(); jobStack.Push(job); jobWriteList.Add(job); if (ScriptState == null) { ScriptState = CSharpScript.RunAsync( "", ScriptOptions.Default .WithMetadataResolver(ScriptMetadataResolver.Default .WithBaseDirectory(Environment.CurrentDirectory) .WithSearchPaths(RuntimeEnvironment.GetRuntimeDirectory()) .WithSearchPaths(job.StackCfg.Includes) ).WithEmitDebugInformation(true), this, typeof(IWhomstGlobals)).Result; } else { foreach (var incl in job.StackCfg.Includes.Where(i => Includes.Add(i))) { Util.AssertWarning(false, $"Could not add include directory {incl} midway through Whomst execution"); } } StringBuilder setupCode = new StringBuilder(); foreach (var assm in job.StackCfg.Assemblies.Where(a => Assemblies.Add(a))) { setupCode.AppendLine($"#r\"{assm}\""); } foreach (var using1 in job.StackCfg.Usings.Where(u => Usings.Add(u))) { setupCode.AppendLine($"using {using1};"); } Util.AssertWarning( InputFiles.Add(job.FileCfg.InputFilePath), $"{job.FileCfg.InputFilePath} is getting preprocessed twice."); Util.AssertWarning( OutputFiles.Add(job.FileCfg.OutputFilePath), $"{job.FileCfg.OutputFilePath} is getting written twice."); ScriptState = ScriptState.ContinueWithAsync(setupCode.ToString()).Result; } var fmt = job.FileCfg.FormatCfg; { var text = File.ReadAllText(job.FileCfg.InputFilePath, fmt.Encoding); var linesIn = text.EzSplit(fmt.NewL); void AddLineGroup(int s, int e, LineGroupType t) => job.LineGroups.Add(new LineGroup { InclStart = s, ExclEnd = e, Type = t }); AddLineGroup(0, -1, LineGroupType.Content); for (int index = 0; index < linesIn.Length; index++) { var line = linesIn[index]; void Assert(bool b, string s) => Util.Assert( b, s, job.FileCfg.InputFileName, index + 1); switch (job.LineGroups.Last().Type) { case LineGroupType.Content: var startTokenInd = line.IndexOf(fmt.StartCodeToken); if (0 <= startTokenInd) { job.LineGroups.Last().ExclEnd = index; var endTokenInd = line.IndexOf(fmt.StartOutputToken); if (0 <= endTokenInd) { Assert(startTokenInd < endTokenInd, $"{fmt.StartOutputToken} came before {fmt.StartCodeToken}"); AddLineGroup(index, index + 1, LineGroupType.TwoLiner); AddLineGroup(index + 1, -1, LineGroupType.GenOutput); } else { AddLineGroup(index, index + 1, LineGroupType.StartCode); AddLineGroup(index + 1, -1, LineGroupType.WhomstCode); } } var startOneLinerInd = line.IndexOf(fmt.OneLinerStartCodeToken); if (0 <= startOneLinerInd && (startTokenInd < 0)) { var midOneLinerInd = line.IndexOf(fmt.OneLinerStartOutputToken); var endOneLinerInd = line.IndexOf(fmt.OneLinerEndOutputToken); Assert( startOneLinerInd < midOneLinerInd, $"{fmt.OneLinerStartCodeToken} must be followed with {fmt.OneLinerStartOutputToken}"); Assert( midOneLinerInd < endOneLinerInd, $"{fmt.OneLinerStartOutputToken} must be followed with {fmt.OneLinerEndOutputToken}"); job.LineGroups.Last().ExclEnd = index; AddLineGroup(index, index + 1, LineGroupType.OneLiner); AddLineGroup(index + 1, -1, LineGroupType.Content); } break; case LineGroupType.WhomstCode: if (line.Contains(fmt.StartOutputToken)) { job.LineGroups.Last().ExclEnd = index; AddLineGroup(index, index + 1, LineGroupType.StartOutput); AddLineGroup(index + 1, -1, LineGroupType.GenOutput); } break; case LineGroupType.GenOutput: if (line.Contains(fmt.EndOutputToken)) { job.LineGroups.Last().ExclEnd = index; AddLineGroup(index, index + 1, LineGroupType.EndOutput); AddLineGroup(index + 1, -1, LineGroupType.Content); } break; default: throw new ArgumentOutOfRangeException(); } } job.LineGroups.Last().ExclEnd = linesIn.Length; var endingType = job.LineGroups.Last().Type; Util.Assert( endingType == LineGroupType.Content || endingType == LineGroupType.OneLiner, $"Document ends with unclosed {endingType} (missing \"{fmt.EndOutputToken}\"??)", job.FileCfg.InputFileName, job.LineGroups.Last().InclStart + 1); List <LineGroup> remove = new List <LineGroup>(); foreach (var lg in job.LineGroups) { lg.InLines = linesIn.Slice(lg.InclStart, lg.ExclEnd); //if (lg.InLines.Count == 0) { remove.Add(lg); } } foreach (var rem in remove) { job.LineGroups.Remove(rem); } } { string indent = null; string oldHash = null; foreach (var lineGroup in job.LineGroups) { string firstLine = lineGroup.InLines.FirstOrDefault(); switch (lineGroup.Type) { case LineGroupType.Content: case LineGroupType.OneLiner: { lineGroup.Indent = ""; lineGroup.DeIndentedInLines = lineGroup.InLines; } break; case LineGroupType.StartCode: { var tokenInd = firstLine.IndexOf(fmt.StartCodeToken); Util.Assert( tokenInd + fmt.StartCodeToken.Length == firstLine.Length, $"Line cannot contain anything after {fmt.StartCodeToken}", job.FileCfg.InputFileName, lineGroup.StartingLineNumber); indent = firstLine.Substring(0, tokenInd); } break; case LineGroupType.TwoLiner: { var tokenInd = firstLine.IndexOf(fmt.StartCodeToken); indent = firstLine.Substring(0, tokenInd); } break; case LineGroupType.WhomstCode: { } break; case LineGroupType.StartOutput: { Util.Assert( firstLine == $"{indent}{fmt.StartOutputToken}", "Line with {fmt.StartOutputToken} should contain it, indent, and nothign else", job.FileCfg.InputFileName, lineGroup.StartingLineNumber); } break; case LineGroupType.GenOutput: { lineGroup.Indent = Regex.Replace(indent, @"[^\s]", " "); } break; case LineGroupType.EndOutput: { Util.Assert( firstLine.StartsWith($"{indent}{fmt.EndOutputToken}"), "Indent mismatch", job.FileCfg.InputFileName, lineGroup.StartingLineNumber); string restOfLine = firstLine.Substring(indent.Length + fmt.EndOutputToken.Length).Trim(); Util.Assert(!restOfLine.StartsWith(fmt.HashInfix) || restOfLine.Contains(oldHash), "Code preceding hash does not match hash, which means that generated code has been edited.\n" + "Undo changes or remove Hash", job.FileCfg.InputFileName, lineGroup.StartingLineNumber); } break; default: throw new ArgumentOutOfRangeException(); } lineGroup.Indent = lineGroup.Indent ?? indent; lineGroup.DeIndentedInLines = lineGroup.DeIndentedInLines ?? lineGroup.InLines.Select((l, ind) => { Util.Assert(l.StartsWith(lineGroup.Indent), "Line not indented", job.FileCfg.InputFileName, lineGroup.StartingLineNumber + ind); return(l.Substring(lineGroup.Indent.Length)); }).ToList(); if (lineGroup.Type == LineGroupType.GenOutput) { oldHash = Util.CalculateMD5Hash(lineGroup.DeIndentedInLines.AggLines(fmt.NewL)); } } } { string RunAndRtrn(string code, string file, int lineNumber) { ScriptState = Eval(code, file, lineNumber); var returnValue = ScriptState.ReturnValue; var returnValueScriptState = returnValue as OneTimeScriptState; // WhomstLoad or WhomstEval if (returnValueScriptState != null) { ScriptState = returnValueScriptState.ScriptState; } else if (returnValue != null) { return(returnValue.ToString()); } return(null); } int prevCodeStartLine = -1; foreach (var lineGroup in job.LineGroups) { switch (lineGroup.Type) { case LineGroupType.Content: { job.PrevContentLines = lineGroup.InLines; } break; case LineGroupType.OneLiner: { var firstLine = lineGroup.InLines[0]; var startInd = firstLine.IndexOf(fmt.OneLinerStartCodeToken); var midInd = firstLine.IndexOf(fmt.OneLinerStartOutputToken); var endInd = firstLine.IndexOf(fmt.OneLinerEndOutputToken); var startIndRight = startInd + fmt.OneLinerStartCodeToken.Length; var midIndRight = midInd + fmt.OneLinerStartOutputToken.Length; var endIndRight = endInd + fmt.OneLinerEndOutputToken.Length; var code = firstLine.Substring(startIndRight, midInd - startIndRight); var oldOutput = firstLine.Substring(midIndRight, endInd - midIndRight); var output = RunAndRtrn(code, job.FileCfg.InputFileName, lineGroup.StartingLineNumber) ?.Replace(fmt.NewL, ", "); // TODO this is shameful lineGroup.DeIndentedOutLines = new[] { firstLine.Substring(0, startInd), code, output, firstLine.Substring(endIndRight, firstLine.Length - endIndRight), }; // TODO this is shameful lineGroup.DeIndentedInLines = new[] { firstLine.Substring(0, startInd), code, oldOutput ?? "", firstLine.Substring(endIndRight, firstLine.Length - endIndRight), }; } break; case LineGroupType.StartCode: { lineGroup.DeIndentedOutLines = fmt.StartCodeToken.Array1(); } break; case LineGroupType.TwoLiner: { var firstLine = lineGroup.InLines[0]; var startInd = firstLine.IndexOf(fmt.StartCodeToken); var midInd = firstLine.IndexOf(fmt.StartOutputToken); var startIndRight = startInd + fmt.StartCodeToken.Length; var code = firstLine.Substring(startIndRight, midInd - startIndRight); lineGroup.DeIndentedOutLines = code.Array1(); job.PrevCodeLines = lineGroup.DeIndentedOutLines; prevCodeStartLine = lineGroup.StartingLineNumber; } break; case LineGroupType.WhomstCode: { job.PrevCodeLines = lineGroup.DeIndentedInLines; prevCodeStartLine = lineGroup.StartingLineNumber; } break; case LineGroupType.StartOutput: break; case LineGroupType.GenOutput: { // TODO past output job.WhomstOut = new StringWriter(); var codeSb = new StringBuilder(); int lineNumber = prevCodeStartLine; foreach (var ind in Enumerable.Range(0, job.PrevCodeLines.Count)) { var lineCpy = job.PrevCodeLines[ind]; if (lineCpy.Contains(fmt.ForceDirective)) { lineCpy = lineCpy.Replace(fmt.ForceDirective, ""); } else if (job.FileCfg.ShallSkipCompute) { continue; } if (lineCpy.Contains(fmt.YieldDirective) || ind == job.PrevCodeLines.Count - 1) { codeSb.AppendLine(lineCpy.Replace(fmt.YieldDirective, "")); var output = RunAndRtrn(codeSb.ToString(), fileCfg.InputFileName, lineNumber); if (output != null) { job.WhomstOut.WriteLine(output); } codeSb.Clear(); lineNumber = prevCodeStartLine + ind + 1; } else { codeSb.AppendLine(lineCpy); } } var outputUnindented = job.WhomstOut.GetStringBuilder().ToString(); if (outputUnindented.EndsWith(Environment.NewLine)) { outputUnindented = outputUnindented.Substring(0, outputUnindented.Length - Environment.NewLine.Length); } job.WhomstOut.Dispose(); job.WhomstOut = null; lineGroup.DeIndentedOutLines = outputUnindented.EzSplit(fmt.NewL).ToList(); job.PrevOutputLines = lineGroup.DeIndentedOutLines; } break; case LineGroupType.EndOutput: { var hash = Util.CalculateMD5Hash(job.PrevOutputLines.AggLines(fmt.NewL)); lineGroup.DeIndentedOutLines = $"{fmt.EndOutputToken}{fmt.HashInfix} {hash}".Array1(); } break; default: throw new ArgumentOutOfRangeException(); } lineGroup.DeIndentedOutLines = lineGroup.DeIndentedOutLines ?? lineGroup.DeIndentedInLines; OneTimeScriptState.CheckInstancesDisposed(); } } // Invalidating stuff which I know I don't want to be used again job.WhomstOut = null; job.PrevCodeLines = null; job.PrevContentLines = null; job.PrevOutputLines = null; jobStack.Pop(); }