[STAThread] // Required by ICSharpCode.TextEditor public static void Main(string[] args) { if (!args.Contains("--nologo")) Console.WriteLine("LeMP macro compiler ({0})", typeof(Compiler).Assembly.GetName().Version.ToString()); if (args.Contains("--editor")) { Console.WriteLine("Starting editor..."); System.Windows.Forms.Application.EnableVisualStyles(); System.Windows.Forms.Application.SetCompatibleTextRenderingDefault(false); System.Windows.Forms.Application.Run(new TextEditor.LempDemoForm()); return; } KnownOptions["editor"] = Pair.Create("", "Show built-in text editor"); Severity minSeverity = Severity.Note; #if DEBUG minSeverity = Severity.Debug; #endif var filter = new SeverityMessageFilter(MessageSink.Console, minSeverity); Compiler c = new Compiler(filter, typeof(BuiltinMacros)); var argList = args.ToList(); var options = c.ProcessArguments(argList, false, true); if (argList.Count == 0) { filter.Write(Severity.Error, null, "No input files provided, stopping."); return; } if (!MaybeShowHelp(options, KnownOptions)) { WarnAboutUnknownOptions(options, filter, KnownOptions.With("nologo", Pair.Create("", ""))); using (LNode.PushPrinter(EcsNodePrinter.PrintPlainCSharp)) c.Run(); } }
public static bool ProcessArguments(Compiler c, BMultiMap<string, string> options) { string value; var filter = c.Sink as SeverityMessageFilter ?? new SeverityMessageFilter(c.Sink, Severity.Note); if (options.TryGetValue("verbose", out value)) { if (value != "false") { try { // Enum.TryParse() does not exist before .NET 4 so use Enum.Parse filter.MinSeverity = (Severity)Enum.Parse(typeof(Severity), value); } catch (Exception) { // Docs say OverflowException, but that just sounds wrong filter.MinSeverity = Severity.Verbose; } } } IMessageSink sink = c.Sink = filter; if (options.TryGetValue("max-expand", out value)) TryCatch("While parsing max-expand", sink, () => c.MaxExpansions = int.Parse(value)); foreach (var macroDll in options["macros"]) { Assembly assembly; TryCatch("While opening " + macroDll, sink, () => { if (macroDll.Contains('\\') || macroDll.Contains('/')) { // Avoid "Absolute path information is required" exception string fullPath = Path.Combine(Environment.CurrentDirectory, macroDll); assembly = Assembly.LoadFile(fullPath); } else assembly = Assembly.LoadFrom(macroDll); c.AddMacros(assembly); }); } foreach (var macroDll in options["macros-longname"]) { Assembly assembly; TryCatch("While opening " + macroDll, sink, () => { assembly = Assembly.Load(macroDll); c.AddMacros(assembly); }); } if (options.TryGetValue("noparallel", out value) && (value == null || value == "true")) c.Parallel = false; if (options.TryGetValue("outext", out c.OutExt) && c.OutExt != null && !c.OutExt.StartsWith(".")) c.OutExt = "." + c.OutExt; if (options.TryGetValue("inlang", out value)) { ApplyLanguageOption(sink, "--inlang", value, ref c.InLang); } if (options.TryGetValue("outlang", out value)) { IParsingService lang = null; ApplyLanguageOption(sink, "--outlang", value, ref lang); c.OutLang = lang.Printer ?? c.OutLang; } if (options.TryGetValue("forcelang", out value) && (value == null || value == "true")) c.ForceInLang = true; if (!options.ContainsKey("outlang") && c.OutExt != null && FileNameToLanguage(c.OutExt) == null) sink.Write(Severity.Error, "--outext", "No language was found for extension «{0}»", c.OutExt); double num; if (options.TryGetValue("timeout", out value)) { if (!double.TryParse(value, out num) || !(num >= 0)) sink.Write(Severity.Error, "--timeout", "Invalid or missing timeout value", c.OutExt); else c.AbortTimeout = TimeSpan.FromSeconds(num); } return true; }
public static Compiler ProcessArguments(BMultiMap<string, string> options, SeverityMessageFilter sink, Type prelude, List<string> inputFiles) { if (inputFiles.Count == 0) { sink.Write(Severity.Error, null, "No input provided, stopping."); return null; } Compiler c = new Compiler(sink, prelude); c.Files = new List<InputOutput>(OpenSourceFiles(sink, inputFiles)); return ProcessArguments(c, options) ? c : null; }
protected override byte[] Generate(string inputFilePath, string inputFileContents, string defaultNamespace, IVsGeneratorProgress progressCallback) { string oldCurDir = Environment.CurrentDirectory; try { string inputFolder = Path.GetDirectoryName(inputFilePath); Environment.CurrentDirectory = inputFolder; // --macros should be relative to file being processed // Originally I wrote a conversion from IVsGeneratorProgress to // IMessageSink so that errors could be reported immediately and // directly to Visual Studio. This broke in a bizarre way when I // added processing on a separate thread (in order to be able to // abort the thread if it runs too long); I got the following // InvalidCastException: "Unable to cast COM object of type 'System.__ComObject' // to interface type 'Microsoft.VisualStudio.Shell.Interop.IVsGeneratorProgress'. // This operation failed because the QueryInterface call on the COM component for // the interface with IID '{BED89B98-6EC9-43CB-B0A8-41D6E2D6669D}' failed due to // the following error: No such interface supported (Exception from HRESULT: // 0x80004002 (E_NOINTERFACE))." // // A simple solution is to store the messages rather than reporting // them immediately. I'll report the errors at the very end. MessageHolder sink = new MessageHolder(); var sourceFile = new InputOutput((UString)inputFileContents, inputFilePath); Compiler.KnownOptions["no-out-header"] = Pair.Create("", "Remove explanatory comment from output file"); Compiler.KnownOptions.Remove("parallel"); // not applicable to single file Compiler.KnownOptions.Remove("noparallel"); // not applicable to single file var c = new Compiler(sink, sourceFile) { AbortTimeout = TimeSpan.FromSeconds(10), Parallel = false // only one file, parallel doesn't help }; var argList = G.SplitCommandLineArguments(defaultNamespace); var options = c.ProcessArguments(argList, true, false); // Note: if default namespace is left blank, VS uses the namespace // from project settings. Don't show an error in that case. if (argList.Count > 1 || (argList.Count == 1 && options.Count > 0)) sink.Write(Severity.Error, "Command line", "'{0}': expected options only (try --help).", argList[0]); string _; if (options.TryGetValue("help", out _) || options.TryGetValue("?", out _)) { var ms = new MemoryStream(); LeMP.Compiler.ShowHelp(LeMP.Compiler.KnownOptions, new StreamWriter(ms), false); return ms.GetBuffer(); } LeMP.Compiler.WarnAboutUnknownOptions(options, sink, LeMP.Compiler.KnownOptions); if (options.ContainsKey("no-out-header")) c.NoOutHeader = true; if (c.InLang == LesLanguageService.Value || inputFilePath.EndsWith(".les", StringComparison.OrdinalIgnoreCase)) c.MacroProcessor.PreOpenedNamespaces.Add(GSymbol.Get("LeMP.Prelude.Les")); Configure(c); _requestedExtension = c.OutExt; c.Run(); // Report errors foreach (var msg in sink.List) ReportErrorToVS(progressCallback, msg.Severity, msg.Context, msg.Format, msg.Args); return Encoding.UTF8.GetBytes(c.Output.ToString()); } finally { Environment.CurrentDirectory = oldCurDir; } }
protected override byte[] Generate(string inputFilePath, string inputFileContents, string defaultNamespace, IVsGeneratorProgress progressCallback) { string oldCurDir = Environment.CurrentDirectory; try { string inputFolder = Path.GetDirectoryName(inputFilePath); Environment.CurrentDirectory = inputFolder; // --macros should be relative to file being processed var options = new BMultiMap<string, string>(); var argList = G.SplitCommandLineArguments(defaultNamespace); UG.ProcessCommandLineArguments(argList, options, "", LeMP.Compiler.ShortOptions, LeMP.Compiler.TwoArgOptions); string _; var KnownOptions = LeMP.Compiler.KnownOptions; if (options.TryGetValue("help", out _) || options.TryGetValue("?", out _)) LeMP.Compiler.ShowHelp(KnownOptions); // Originally I wrote a conversion from IVsGeneratorProgress to // IMessageSink so that errors could be reported immediately and // directly to Visual Studio. This broke in a bizarre way when I // added processing on a separate thread (in order to be able to // abort the thread if it runs too long); I got the following // InvalidCastException: "Unable to cast COM object of type 'System.__ComObject' // to interface type 'Microsoft.VisualStudio.Shell.Interop.IVsGeneratorProgress'. // This operation failed because the QueryInterface call on the COM component for // the interface with IID '{BED89B98-6EC9-43CB-B0A8-41D6E2D6669D}' failed due to // the following error: No such interface supported (Exception from HRESULT: // 0x80004002 (E_NOINTERFACE))." // // A simple solution is to store the messages rather than reporting // them immediately. I'll report the errors at the very end. MessageHolder sink = new MessageHolder(); var sourceFile = new InputOutput((UString)inputFileContents, Path.GetFileName(inputFilePath)); var c = new Compiler(sink, sourceFile) { AbortTimeout = TimeSpan.FromSeconds(10), Parallel = false // only one file, parallel doesn't help }; if (LeMP.Compiler.ProcessArguments(c, options)) { if (options.ContainsKey("no-out-header")) { options.Remove("no-out-header", 1); c.NoOutHeader = true; } LeMP.Compiler.WarnAboutUnknownOptions(options, sink, KnownOptions); if (c != null) { if (inputFilePath.EndsWith(".les", StringComparison.OrdinalIgnoreCase)) c.MacroProcessor.PreOpenedNamespaces.Add(GSymbol.Get("LeMP.Prelude.Les")); Configure(c); c.Run(); // Report errors foreach (var msg in sink.List) ReportErrorToVS(progressCallback, msg.Severity, msg.Context, msg.Format, msg.Args); return Encoding.UTF8.GetBytes(c.Output.ToString()); } } return null; } finally { Environment.CurrentDirectory = oldCurDir; } }