public static System.Reflection.Assembly GenerateCode(ITextTemplatingEngineHost host, ParsedTemplate pt, TemplateSettings settings, CodeCompileUnit ccu) { CompilerParameters pars = new CompilerParameters (); pars.GenerateExecutable = false; if (settings.Debug) { pars.GenerateInMemory = false; pars.IncludeDebugInformation = true; pars.TempFiles.KeepFiles = true; } else { pars.GenerateInMemory = true; pars.IncludeDebugInformation = false; } //resolve and add assembly references HashSet<string> assemblies = new HashSet<string> (); assemblies.UnionWith (settings.Assemblies); assemblies.UnionWith (host.StandardAssemblyReferences); foreach (string assem in assemblies) { string resolvedAssem = host.ResolveAssemblyReference (assem); if (!String.IsNullOrEmpty (resolvedAssem)) { pars.ReferencedAssemblies.Add (resolvedAssem); } else { pt.LogError ("Could not resolve assembly reference '" + assem + "'"); return null; } } CompilerResults results = settings.Provider.CompileAssemblyFromDom (pars, ccu); pt.Errors.AddRange (results.Errors); if (pt.Errors.HasErrors) return null; return results.CompiledAssembly; }
public CompiledTemplate (ParsedTemplate parsedTemplate, ITextTemplatingEngineHost host, Assembly assembly, TemplateSettings settings) { this.host = host; this.assembly = assembly; this.settings = settings; this.parsedTemplate = parsedTemplate; }
public static CodeCompileUnit GenerateCompileUnit(ITextTemplatingEngineHost host, ParsedTemplate pt, TemplateSettings settings) { //prep the compile unit var ccu = new CodeCompileUnit (); var namespac = new CodeNamespace (settings.Namespace); ccu.Namespaces.Add (namespac); var imports = new HashSet<string> (); imports.UnionWith (settings.Imports); imports.UnionWith (host.StandardImports); foreach (string ns in imports) namespac.Imports.Add (new CodeNamespaceImport (ns)); //prep the type var type = new CodeTypeDeclaration (settings.Name); type.IsPartial = true; if (!String.IsNullOrEmpty (settings.Inherits)) type.BaseTypes.Add (new CodeTypeReference (settings.Inherits)); else type.BaseTypes.Add (new CodeTypeReference (typeof (TextTransformation))); namespac.Types.Add (type); //prep the transform method var transformMeth = new CodeMemberMethod () { Name = "TransformText", ReturnType = new CodeTypeReference (typeof (String)), Attributes = MemberAttributes.Public | MemberAttributes.Override }; //method references that will need to be used multiple times var writeMeth = new CodeMethodReferenceExpression (new CodeThisReferenceExpression (), "Write"); var toStringMeth = new CodeMethodReferenceExpression (new CodeTypeReferenceExpression (typeof (ToStringHelper)), "ToStringWithCulture"); bool helperMode = false; //build the code from the segments foreach (TemplateSegment seg in pt.Content) { CodeStatement st = null; var location = new CodeLinePragma (seg.StartLocation.FileName ?? host.TemplateFile, seg.StartLocation.Line); switch (seg.Type) { case SegmentType.Block: if (helperMode) //TODO: are blocks permitted after helpers? throw new ParserException ("Blocks are not permitted after helpers", seg.StartLocation); st = new CodeSnippetStatement (seg.Text); break; case SegmentType.Expression: st = new CodeExpressionStatement ( new CodeMethodInvokeExpression (writeMeth, new CodeMethodInvokeExpression (toStringMeth, new CodeSnippetExpression (seg.Text)))); break; case SegmentType.Content: st = new CodeExpressionStatement (new CodeMethodInvokeExpression (writeMeth, new CodePrimitiveExpression (seg.Text))); break; case SegmentType.Helper: type.Members.Add (new CodeSnippetTypeMember (seg.Text) { LinePragma = location }); helperMode = true; break; default: throw new InvalidOperationException (); } if (st != null) { if (helperMode) { //convert the statement into a snippet member and attach it to the top level type //TODO: is there a way to do this for languages that use indentation for blocks, e.g. python? using (var writer = new StringWriter ()) { settings.Provider.GenerateCodeFromStatement (st, writer, null); type.Members.Add (new CodeSnippetTypeMember (writer.ToString ()) { LinePragma = location }); } } else { st.LinePragma = location; transformMeth.Statements.Add (st); continue; } } } //complete the transform method transformMeth.Statements.Add (new CodeMethodReturnStatement ( new CodeMethodInvokeExpression ( new CodePropertyReferenceExpression ( new CodeThisReferenceExpression (), "GenerationEnvironment"), "ToString"))); type.Members.Add (transformMeth); //generate the Host property if needed if (settings.HostSpecific) { var hostField = new CodeMemberField (new CodeTypeReference (typeof (ITextTemplatingEngineHost)), "hostValue"); hostField.Attributes = (hostField.Attributes & ~MemberAttributes.AccessMask) | MemberAttributes.Private; type.Members.Add (hostField); var hostProp = new CodeMemberProperty () { Name = "Host", Attributes = MemberAttributes.Public, HasGet = true, HasSet = true, Type = hostField.Type }; var hostFieldRef = new CodeFieldReferenceExpression (new CodeThisReferenceExpression (), "hostValue"); hostProp.SetStatements.Add (new CodeAssignStatement (hostFieldRef, new CodePropertySetValueReferenceExpression ())); hostProp.GetStatements.Add (new CodeMethodReturnStatement (hostFieldRef)); type.Members.Add (hostProp); } return ccu; }
public static TemplateSettings GetSettings(ITextTemplatingEngineHost host, ParsedTemplate pt) { string language = null; TemplateSettings settings = new TemplateSettings (); foreach (Directive dt in pt.Directives) { switch (dt.Name) { case "template": string val = dt.Extract ("language"); if (val != null) language = val; val = dt.Extract ("debug"); if (val != null) settings.Debug = string.Compare (val, "true", StringComparison.OrdinalIgnoreCase) == 0; val = dt.Extract ("inherits"); if (val != null) settings.Inherits = val; val = dt.Extract ("culture"); if (val != null) { System.Globalization.CultureInfo culture = System.Globalization.CultureInfo.GetCultureInfo (val); if (culture == null) pt.LogWarning ("Could not find culture '" + val + "'", dt.StartLocation); else settings.Culture = culture; } val = dt.Extract ("hostspecific"); if (val != null) { settings.HostSpecific = string.Compare (val, "true", StringComparison.OrdinalIgnoreCase) == 0; } break; case "assembly": string name = dt.Extract ("name"); if (name == null) pt.LogError ("Missing name attribute in assembly directive", dt.StartLocation); else settings.Assemblies.Add (name); break; case "import": string namespac = dt.Extract ("namespace"); if (namespac == null) pt.LogError ("Missing namespace attribute in import directive", dt.StartLocation); else settings.Imports.Add (namespac); break; case "output": settings.Extension = dt.Extract ("extension"); string encoding = dt.Extract ("encoding"); if (encoding != null) settings.Encoding = Encoding.GetEncoding ("encoding"); break; case "include": throw new InvalidOperationException ("Include is handled in the parser"); default: throw new NotImplementedException ("Custom directives are not supported yet"); } ComplainExcessAttributes (dt, pt); } if (settings.Name == null) settings.Name = string.Format ("GeneratedTextTransformation{0:x}", new System.Random ().Next ()); if (settings.Namespace == null) settings.Namespace = typeof (TextTransformation).Namespace; //resolve the CodeDOM provider if (String.IsNullOrEmpty (language)) { pt.LogError ("No language was specified for the template"); return settings; } if (language == "C#v3.5") { Dictionary<string, string> providerOptions = new Dictionary<string, string> (); providerOptions.Add ("CompilerVersion", "v3.5"); settings.Provider = new CSharpCodeProvider (providerOptions); } else { settings.Provider = CodeDomProvider.CreateProvider (language); } if (settings.Provider == null) { pt.LogError ("A provider could not be found for the language '" + language + "'"); return settings; } return settings; }
return false; } public static CodeCompileUnit GenerateCompileUnit (ITextTemplatingEngineHost host, ParsedTemplate pt, TemplateSettings settings) { //prep the compile unit CodeCompileUnit ccu = new CodeCompileUnit (); CodeNamespace namespac = new CodeNamespace (settings.Namespace); ccu.Namespaces.Add (namespac); HashSet<string> imports = new HashSet<string> (); imports.UnionWith (settings.Imports); imports.UnionWith (host.StandardImports); foreach (string ns in imports) namespac.Imports.Add (new CodeNamespaceImport (ns)); //prep the type CodeTypeDeclaration type = new CodeTypeDeclaration (settings.Name); if (!String.IsNullOrEmpty (settings.Inherits)) type.BaseTypes.Add (new CodeTypeReference (settings.Inherits)); else type.BaseTypes.Add (new CodeTypeReference (typeof (TextTransformation))); namespac.Types.Add (type); //prep the transform method CodeMemberMethod transformMeth = new CodeMemberMethod (); transformMeth.Name = "TransformText"; transformMeth.ReturnType = new CodeTypeReference (typeof (String)); transformMeth.Attributes = MemberAttributes.Public | MemberAttributes.Override; //method references that will need to be used multiple times CodeMethodReferenceExpression writeMeth = new CodeMethodReferenceExpression (new CodeThisReferenceExpression (), "Write"); CodeMethodReferenceExpression toStringMeth = new CodeMethodReferenceExpression (new CodeTypeReferenceExpression (typeof (ToStringHelper)), "ToStringWithCulture"); //build the code from the segments foreach (TemplateSegment seg in pt.Content) { CodeStatement st = null; switch (seg.Type) { case SegmentType.Block: st = new CodeSnippetStatement (seg.Text); break; case SegmentType.Expression: st = new CodeExpressionStatement ( new CodeMethodInvokeExpression (writeMeth, new CodeMethodInvokeExpression (toStringMeth, new CodeSnippetExpression (seg.Text)))); break; case SegmentType.Content: st = new CodeExpressionStatement (new CodeMethodInvokeExpression (writeMeth, new CodePrimitiveExpression (seg.Text))); break; case SegmentType.Helper: CodeTypeMember mem = new CodeSnippetTypeMember (seg.Text); mem.LinePragma = new CodeLinePragma (host.TemplateFile, seg.StartLocation.Line); type.Members.Add (mem); break; default: throw new InvalidOperationException (); } if (st != null) { st.LinePragma = new CodeLinePragma (host.TemplateFile, seg.StartLocation.Line); transformMeth.Statements.Add (st); } } //complete the transform method transformMeth.Statements.Add (new CodeMethodReturnStatement ( new CodeMethodInvokeExpression ( new CodePropertyReferenceExpression ( new CodeThisReferenceExpression (), "GenerationEnvironment"), "ToString"))); type.Members.Add (transformMeth); //generate the Host property if needed if (settings.HostSpecific) { CodeMemberField hostField = new CodeMemberField (new CodeTypeReference (typeof (ITextTemplatingEngineHost)), "hostValue"); hostField.Attributes = (hostField.Attributes & ~MemberAttributes.AccessMask) | MemberAttributes.Private; type.Members.Add (hostField); CodeMemberProperty hostProp = new CodeMemberProperty (); hostProp.Name = "Host"; hostProp.Attributes = MemberAttributes.Public; hostProp.HasGet = hostProp.HasGet = true; hostProp.Type = hostField.Type; CodeFieldReferenceExpression hostFieldRef = new CodeFieldReferenceExpression (new CodeThisReferenceExpression (), "hostValue"); hostProp.SetStatements.Add (new CodeAssignStatement (hostFieldRef, new CodePropertySetValueReferenceExpression ())); hostProp.GetStatements.Add (new CodeMethodReturnStatement (hostFieldRef)); type.Members.Add (hostProp); }
static int MainInternal(string [] args) { if (args.Length == 0 && !Console.IsInputRedirected) { ShowHelp(true); } var generator = new ToolTemplateGenerator(); string outputFile = null, inputFile = null; var directives = new List <string> (); var parameters = new List <string> (); var properties = new Dictionary <string, string> (); string preprocessClassName = null; bool debug = false; bool verbose = false; optionSet = new OptionSet { { "o=|out=", "Name or path of the output {<file>}. Defaults to the input filename with its " + "extension changed to `.txt'. Use `-' to output to stdout.", s => outputFile = s }, { "r=", "Name or path of an {<assembly>} reference. Assemblies will be resolved from the " + "framework and the include folders", s => generator.Refs.Add(s) }, { "u=|using=", "Import a {<namespace>}' statement with a `using", s => generator.Imports.Add(s) }, { "I=", "Search {<directory>} when resolving file includes", s => generator.IncludePaths.Add(s) }, { "P=", "Search {<directory>} when resolving assembly references", s => generator.ReferencePaths.Add(s) }, { "c=|class=", "Preprocess the template into class {<name>}", (s) => preprocessClassName = s }, { "p:=", "Add a {<name>}={<value>} key-value pair to the template's `Session' " + "dictionary. These can also be accessed using strongly typed " + "properties declared with `<#@ parameter name=\"<name>\" type=\"<type>\" #> " + "directives.", (k, v) => properties[k] = v }, { "debug", "Generate debug symbols and keep temp files", s => debug = true }, { "v|verbose", "Generate debug symbols and keep temp files", s => verbose = true }, { "h|?|help", "Show help", s => ShowHelp(false) } }; compatOptionSet = new OptionSet { { "dp=", "Directive processor (name!class!assembly)", s => directives.Add(s) }, { "a=", "Parameters (name=value) or ([processorName!][directiveName!]name!value)", s => parameters.Add(s) }, }; var remainingArgs = optionSet.Parse(args); remainingArgs = compatOptionSet.Parse(remainingArgs); string inputContent = null; bool inputIsFromStdin = false; if (remainingArgs.Count != 1) { if (Console.IsInputRedirected) { inputContent = Console.In.ReadToEnd(); inputIsFromStdin = true; } else { Console.Error.WriteLine("No input file specified."); return(1); } } else { inputFile = remainingArgs [0]; if (!File.Exists(inputFile)) { Console.Error.WriteLine("Input file '{0}' does not exist.", inputFile); return(1); } } bool writeToStdout = outputFile == "-" || (inputIsFromStdin && string.IsNullOrEmpty(outputFile)); if (!writeToStdout && string.IsNullOrEmpty(outputFile)) { outputFile = inputFile; if (Path.HasExtension(outputFile)) { var dir = Path.GetDirectoryName(outputFile); var fn = Path.GetFileNameWithoutExtension(outputFile); outputFile = Path.Combine(dir, fn + ".txt"); } else { outputFile = outputFile + ".txt"; } } if (inputFile != null) { try { inputContent = File.ReadAllText(inputFile); } catch (IOException ex) { Console.Error.WriteLine("Could not read input file '" + inputFile + "':\n" + ex); return(1); } } if (inputContent.Length == 0) { Console.Error.WriteLine("Input is empty"); return(1); } foreach (var par in parameters) { if (!generator.TryAddParameter(par)) { Console.Error.WriteLine("Parameter has incorrect format: {0}", par); return(1); } } if (!AddDirectiveProcessors(generator, directives)) { return(1); } var pt = ParsedTemplate.FromText(inputContent, generator); TemplateSettings settings = TemplatingEngine.GetSettings(generator, pt); if (debug) { settings.Debug = true; } if (verbose) { settings.Log = Console.Out; } if (pt.Errors.Count > 0) { generator.Errors.AddRange(pt.Errors); } string outputContent = null; if (!generator.Errors.HasErrors) { AddCoercedSessionParameters(generator, pt, properties); } if (!generator.Errors.HasErrors) { if (preprocessClassName == null) { outputContent = generator.ProcessTemplate(pt, inputFile, inputContent, ref outputFile, settings); } else { outputContent = generator.PreprocessTemplate(pt, inputFile, inputContent, preprocessClassName, settings); } } if (generator.Errors.HasErrors) { Console.Error.WriteLine(inputFile == null ? "Processing failed." : $"Processing '{inputFile}' failed."); } try { if (!generator.Errors.HasErrors) { if (writeToStdout) { Console.WriteLine(outputContent); } else { File.WriteAllText(outputFile, outputContent, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false)); } } } catch (IOException ex) { Console.Error.WriteLine("Could not write output file '" + outputFile + "':\n" + ex); return(1); } LogErrors(generator); return(generator.Errors.HasErrors ? 1 : 0); }