/// <summary> /// Parser to turn lg content into a <see cref="Templates"/>. /// </summary> /// <param name="resource">LG resource.</param> /// <param name="importResolver">Resolver to resolve LG import id to template text.</param> /// <param name="expressionParser">Expression parser for parsing expressions.</param> /// <returns>new <see cref="Templates"/> entity.</returns> public static Templates ParseResource( LGResource resource, ImportResolverDelegate importResolver = null, ExpressionParser expressionParser = null) { return(InnerParseResource(resource, importResolver, expressionParser)); }
/// <summary> /// Parser to turn lg content into a <see cref="Templates"/>. /// </summary> /// <param name="resource">LG resource.</param> /// <param name="importResolver">Resolver to resolve LG import id to template text.</param> /// <param name="expressionParser">Expression parser for parsing expressions.</param> /// <param name="cachedTemplates">Give the file path and templates to avoid parsing and to improve performance.</param> /// <param name="parentTemplates">Parent visited Templates.</param> /// <returns>new <see cref="Templates"/> entity.</returns> private static Templates InnerParseResource( LGResource resource, ImportResolverDelegate importResolver = null, ExpressionParser expressionParser = null, Dictionary <string, Templates> cachedTemplates = null, Stack <Templates> parentTemplates = null) { if (resource == null) { throw new ArgumentNullException(nameof(resource)); } cachedTemplates = cachedTemplates ?? new Dictionary <string, Templates>(); parentTemplates = parentTemplates ?? new Stack <Templates>(); if (cachedTemplates.ContainsKey(resource.Id)) { return(cachedTemplates[resource.Id]); } importResolver = importResolver ?? DefaultFileResolver; var lg = new Templates(content: resource.Content, id: resource.Id, source: resource.FullName, importResolver: importResolver, expressionParser: expressionParser); try { lg = new TemplatesTransformer(lg).Transform(AntlrParseTemplates(resource)); lg.References = GetReferences(lg, cachedTemplates, parentTemplates); new StaticChecker(lg).Check().ForEach(u => lg.Diagnostics.Add(u)); } catch (TemplateException ex) { ex.Diagnostics.ToList().ForEach(u => lg.Diagnostics.Add(u)); } return(lg); }
/// <summary> /// Parser to turn lg content into a <see cref="Templates"/> based on the original LGFile. /// </summary> /// <param name="content">Text content contains lg templates.</param> /// <param name="lg">Original LGFile.</param> /// <returns>New <see cref="Templates"/> entity.</returns> public static Templates ParseTextWithRef(string content, Templates lg) { if (lg == null) { throw new ArgumentNullException(nameof(lg)); } var newLG = new Templates(content: content, id: InlineContentId, source: InlineContentId, importResolver: lg.ImportResolver, options: lg.Options, namedReferences: lg.NamedReferences); try { var resource = new LGResource(InlineContentId, InlineContentId, content); newLG = new TemplatesTransformer(newLG).Transform(AntlrParseTemplates(resource)); newLG.References = GetReferences(newLG) .Union(lg.References) .Union(new List <Templates> { lg }) .ToList(); new StaticChecker(newLG).Check().ForEach(u => newLG.Diagnostics.Add(u)); } catch (TemplateException ex) { ex.Diagnostics.ToList().ForEach(u => newLG.Diagnostics.Add(u)); } return(newLG); }
public static Templates ParseText( string content, string id = "", ImportResolverDelegate importResolver = null, ExpressionParser expressionParser = null) { var resource = new LGResource(id, id, content); return(ParseResource(resource, importResolver, expressionParser)); }
/// <summary> /// Parser to turn lg content into a <see cref="Templates"/>. /// </summary> /// <param name="filePath">Absolut path of a LG file.</param> /// <param name="importResolver">Resolver to resolve LG import id to template text.</param> /// <param name="expressionParser">Expression parser for parsing expressions.</param> /// <returns>new <see cref="Templates"/> entity.</returns> public static Templates ParseFile( string filePath, ImportResolverDelegate importResolver = null, ExpressionParser expressionParser = null) { var fullPath = Path.GetFullPath(filePath.NormalizePath()); var content = File.ReadAllText(fullPath); var resource = new LGResource(fullPath, fullPath, content); return(ParseResource(resource, importResolver, expressionParser)); }
private static void ResolveImportResources(Templates start, HashSet <Templates> resourcesFound, Dictionary <string, Templates> cachedTemplates, Stack <Templates> parentTemplates) { resourcesFound.Add(start); parentTemplates.Push(start); foreach (var import in start.Imports) { LGResource resource; try { var originalResource = new LGResource(start.Id, start.Source, start.Content); resource = start.ImportResolver(originalResource, import.Id); } catch (Exception e) { var diagnostic = new Diagnostic(import.SourceRange.Range, e.Message, DiagnosticSeverity.Error, start.Source); throw new TemplateException(e.Message, new List <Diagnostic>() { diagnostic }); } // Cycle reference would throw exception to avoid infinite Loop. // Import self is allowed, and would ignore it. if (parentTemplates.Peek().Id != resource.Id && parentTemplates.Any(u => u.Id == resource.Id)) { var errorMsg = $"{TemplateErrors.LoopDetected} {resource.Id} => {start.Id}"; var diagnostic = new Diagnostic(import.SourceRange.Range, errorMsg, DiagnosticSeverity.Error, start.Source); throw new TemplateException(errorMsg, new List <Diagnostic>() { diagnostic }); } if (resourcesFound.All(u => u.Id != resource.Id)) { Templates childResource; if (cachedTemplates.ContainsKey(resource.Id)) { childResource = cachedTemplates[resource.Id]; } else { childResource = InnerParseResource(resource, start.ImportResolver, start.ExpressionParser, cachedTemplates, parentTemplates); cachedTemplates.Add(resource.Id, childResource); } ResolveImportResources(childResource, resourcesFound, cachedTemplates, parentTemplates); } } parentTemplates.Pop(); }
/// <summary> /// Default import resolver, using relative/absolute file path to access the file content. /// </summary> /// <param name="resource">Original Resource.</param> /// <param name="resourceId">Import path.</param> /// <returns>Target content id.</returns> private static LGResource DefaultFileResolver(LGResource resource, string resourceId) { // import paths are in resource files which can be executed on multiple OS environments // normalize to map / & \ in importPath -> OSPath var importPath = resourceId.NormalizePath(); if (!Path.IsPathRooted(importPath)) { // get full path for importPath relative to path which is doing the import. importPath = Path.GetFullPath(Path.Combine(Path.GetDirectoryName(resource.FullName), resourceId)); } return(new LGResource(importPath, importPath, File.ReadAllText(importPath))); }
/// <summary> /// Adds a new template and returns the updated Templates instance. /// </summary> /// <param name="templateName">New template name.</param> /// <param name="parameters">New params.</param> /// <param name="templateBody">New template body.</param> /// <returns>Updated LG file.</returns> public Templates AddTemplate(string templateName, List <string> parameters, string templateBody) { var template = this.FirstOrDefault(u => u.Name == templateName); if (template != null) { throw new ArgumentException(TemplateErrors.TemplateExist(templateName)); } ClearDiagnostics(); var templateNameLine = BuildTemplateNameLine(templateName, parameters); var newTemplateBody = ConvertTemplateBody(templateBody); var content = $"{templateNameLine}{_newLine}{newTemplateBody}"; var originStartLine = GetLinesOfText(Content).Length; // update content Content = $"{Content}{_newLine}{templateNameLine}{_newLine}{newTemplateBody}"; var newTemplates = new Templates(content: string.Empty, id: Id, importResolver: ImportResolver, expressionParser: ExpressionParser, namedReferences: NamedReferences); var resource = new LGResource(Id, Id, content); newTemplates = new TemplatesTransformer(newTemplates).Transform(AntlrParseTemplates(resource)); AppendDiagnosticsWithOffset(newTemplates.Diagnostics, originStartLine); var newTemplate = newTemplates.FirstOrDefault(); if (newTemplate != null) { AdjustRangeForAddTemplate(newTemplate, originStartLine); Add(newTemplate); new StaticChecker(this).Check().ForEach(u => Diagnostics.Add(u)); } return(this); }
private static void ResolveImportResources(Templates start, HashSet <Templates> resourcesFound, Dictionary <string, Templates> cachedTemplates) { resourcesFound.Add(start); foreach (var import in start.Imports) { LGResource resource; try { var originalResource = new LGResource(start.Id, start.Source, start.Content); resource = start.ImportResolver(originalResource, import.Id); } catch (Exception e) { var diagnostic = new Diagnostic(import.SourceRange.Range, e.Message, DiagnosticSeverity.Error, start.Source); throw new TemplateException(e.Message, new List <Diagnostic>() { diagnostic }); } if (resourcesFound.All(u => u.Id != resource.Id)) { Templates childResource; if (cachedTemplates.ContainsKey(resource.Id)) { childResource = cachedTemplates[resource.Id]; } else { childResource = ParseResource(resource, start.ImportResolver, start.ExpressionParser, cachedTemplates); cachedTemplates.Add(resource.Id, childResource); } ResolveImportResources(childResource, resourcesFound, cachedTemplates); } } }
/// <summary> /// Parse LG content and achieve the AST. /// </summary> /// <param name="resource">LG resource.</param> /// <returns>The abstract syntax tree of lg file.</returns> public static IParseTree AntlrParseTemplates(LGResource resource) { if (string.IsNullOrEmpty(resource.Content)) { return(null); } var input = new AntlrInputStream(resource.Content); var lexer = new LGFileLexer(input); lexer.RemoveErrorListeners(); var tokens = new CommonTokenStream(lexer); var parser = new LGFileParser(tokens); parser.RemoveErrorListeners(); var listener = new ErrorListener(resource.FullName); parser.AddErrorListener(listener); parser.BuildParseTree = true; return(parser.file()); }
/// <summary> /// Resolve imported LG resources from a start resource. /// All the imports will be visited and resolved to LGResouce list. /// </summary> /// <param name="start">The lg resource from which to start resolving imported resources.</param> /// <param name="importResolver">resolver to resolve LG import id to template text.</param> /// <param name="resourcesFound">Resources that have been found.</param> private void ResolveImportResources(LGResource start, ImportResolverDelegate importResolver, HashSet <LGResource> resourcesFound) { var resourceIds = start.Imports.Select(lg => lg.Id).ToList(); resourcesFound.Add(start); foreach (var id in resourceIds) { try { var(content, path) = importResolver(start.Id, id); var childResource = LGParser.Parse(content, path); if (!resourcesFound.Contains(childResource)) { ResolveImportResources(childResource, importResolver, resourcesFound); } } catch (Exception err) { throw new Exception($"[Error]{id}:{err.Message}", err); } } }
/// <summary> /// Default import resolver, using relative/absolute file path to access the file content. /// </summary> /// <param name="resource">Original Resource.</param> /// <param name="resourceId">Import path.</param> /// <returns>Target content id.</returns> private static LGResource DefaultFileResolver(LGResource resource, string resourceId) { // import paths are in resource files which can be executed on multiple OS environments // normalize to map / & \ in importPath -> OSPath // If the import id contains "#", we would cut it to use the left path. // for example: [import](a.b.c#d.lg), after convertion, id would be d.lg var hashIndex = resourceId.IndexOf('#'); if (hashIndex > 0) { resourceId = resourceId.Substring(hashIndex + 1); } var importPath = resourceId.NormalizePath(); if (!Path.IsPathRooted(importPath)) { // get full path for importPath relative to path which is doing the import. importPath = Path.GetFullPath(Path.Combine(Path.GetDirectoryName(resource.FullName), resourceId)); } return(new LGResource(importPath, importPath, File.ReadAllText(importPath))); }
/// <summary> /// Updates an existing template in current Templates instance. /// </summary> /// <param name="templateName">Original template name. The only id of a template.</param> /// <param name="newTemplateName">New template Name.</param> /// <param name="parameters">New params.</param> /// <param name="templateBody">New template body.</param> /// <returns>Updated LG file.</returns> public Templates UpdateTemplate(string templateName, string newTemplateName, List <string> parameters, string templateBody) { var template = this.FirstOrDefault(u => u.Name == templateName); if (template != null) { ClearDiagnostics(); var templateNameLine = BuildTemplateNameLine(newTemplateName, parameters); var newTemplateBody = ConvertTemplateBody(templateBody); var content = $"{templateNameLine}{_newLine}{newTemplateBody}"; // update content Content = ReplaceRangeContent( Content, template.SourceRange.Range.Start.Line - 1, template.SourceRange.Range.End.Line - 1, content); var updatedTemplates = new Templates(content: string.Empty, id: Id, importResolver: ImportResolver, expressionParser: ExpressionParser, namedReferences: NamedReferences); var resource = new LGResource(Id, Id, content); updatedTemplates = new TemplatesTransformer(updatedTemplates).Transform(AntlrParseTemplates(resource)); var originStartLine = template.SourceRange.Range.Start.Line - 1; AppendDiagnosticsWithOffset(updatedTemplates.Diagnostics, originStartLine); var newTemplate = updatedTemplates.FirstOrDefault(); if (newTemplate != null) { AdjustRangeForUpdateTemplate(template, newTemplate); new StaticChecker(this).Check().ForEach(u => Diagnostics.Add(u)); } } return(this); }
/// <summary> /// Parser to turn lg content into a <see cref="LanguageGeneration.Templates"/>. /// </summary> /// <param name="resource">LG resource.</param> /// <param name="importResolver">Resolver to resolve LG import id to template text.</param> /// <param name="expressionParser">Expression parser engine for parsing expressions.</param> /// <returns>new <see cref="Templates"/> entity.</returns> public static Templates ParseResource( LGResource resource, ImportResolverDelegate importResolver = null, ExpressionParser expressionParser = null) => TemplatesParser.ParseResource(resource, importResolver, expressionParser).InjectToExpressionFunction();