A class that helps you invoke MacroProcessor on on a set of source files, given a set of command-line options.
This class helps you process command-line options (see ProcessArguments(IList{string}, bool, bool, IList{string})), complete InputOutput objects based on those options (see CompleteInputOutputOptions), and add macros from Assemblies (AddMacros and AddStdMacros).
Exemple #1
0
		[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();
			}
		}
Exemple #2
0
		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;
		}
Exemple #3
0
		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;
		}
Exemple #4
0
		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;
			}
		}
Exemple #5
0
		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;
			}
		}