[LG] Robust Template CRUD with Two-phase parsing of LG (#3731)
* init * init * init * init * init * init * init * fix error * remove unused code * remove unused file * remove unused code * fix build error * fix error * refine * only allow define option/import at the top of lg file. * refine import recognization * add more comments and remove unused code * remove unused antlr file * remove antlr related file * fix build warning * refine * fix error * fix error * fix error * add context for Template and TemplateImport * fix comments * fix comments * fix comments * rename templatebody to body to avoid the same context name
This commit is contained in:
Родитель
ef46dd1815
Коммит
abc14c6a52
|
@ -77,6 +77,8 @@
|
|||
<Source>.*\\ExpressionAntlrParser.cs</Source>
|
||||
<Source>.*\\LGFileLexer.cs</Source>
|
||||
<Source>.*\\LGFileParser.cs</Source>
|
||||
<Source>.*\\LGTemplateLexer.cs</Source>
|
||||
<Source>.*\\LGTemplateParser.cs</Source>
|
||||
</Exclude>
|
||||
</Sources>
|
||||
</CodeCoverage>
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
lexer grammar ExpressionAntlrLexer;
|
||||
|
||||
@parser::header {#pragma warning disable 3021} // Disable StyleCop warning CS3021 re CLSCompliant attribute in generated files.
|
||||
@lexer::header {#pragma warning disable 3021} // Disable StyleCop warning CS3021 re CLSCompliant attribute in generated files.
|
||||
|
||||
@lexer::members {
|
||||
bool ignoreWS = true; // usually we ignore whitespace, but inside stringInterpolation, whitespace is significant
|
||||
}
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
parser grammar ExpressionAntlrParser;
|
||||
|
||||
@parser::header {#pragma warning disable 3021} // Disable StyleCop warning CS3021 re CLSCompliant attribute in generated files.
|
||||
@lexer::header {#pragma warning disable 3021} // Disable StyleCop warning CS3021 re CLSCompliant attribute in generated files.
|
||||
|
||||
options { tokenVocab=ExpressionAntlrLexer; }
|
||||
|
||||
file: expression EOF;
|
||||
|
|
|
@ -13,8 +13,6 @@ using Antlr4.Runtime.Misc;
|
|||
using Antlr4.Runtime.Tree;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
[assembly: CLSCompliant(false)]
|
||||
|
||||
namespace AdaptiveExpressions
|
||||
{
|
||||
/// <summary>
|
||||
|
|
|
@ -12,7 +12,7 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
|
|||
/// <summary>
|
||||
/// LG template analyzer.
|
||||
/// </summary>
|
||||
public class Analyzer : LGFileParserBaseVisitor<AnalyzerResult>
|
||||
public class Analyzer : LGTemplateParserBaseVisitor<AnalyzerResult>
|
||||
{
|
||||
private readonly Dictionary<string, Template> templateMap;
|
||||
|
||||
|
@ -23,8 +23,8 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
|
|||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Analyzer"/> class.
|
||||
/// </summary>
|
||||
/// <param name="templates">template list.</param>
|
||||
/// <param name="expressionParser">expression parser.</param>
|
||||
/// <param name="templates">Template list.</param>
|
||||
/// <param name="expressionParser">Expression parser.</param>
|
||||
public Analyzer(List<Template> templates, ExpressionParser expressionParser)
|
||||
{
|
||||
Templates = templates;
|
||||
|
@ -47,7 +47,7 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
|
|||
/// Analyzer a template to get the static analyzer results.
|
||||
/// </summary>
|
||||
/// <param name="templateName">Template name.</param>
|
||||
/// <returns>analyze result including variables and template references.</returns>
|
||||
/// <returns>Analyze result including variables and template references.</returns>
|
||||
public AnalyzerResult AnalyzeTemplate(string templateName)
|
||||
{
|
||||
if (!templateMap.ContainsKey(templateName))
|
||||
|
@ -67,29 +67,15 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
|
|||
// because given we don't track down for templates have parameters
|
||||
// the only scenario that we are still analyzing an parameterized template is
|
||||
// this template is root template to analyze, in this we also don't have exclude parameters
|
||||
var dependencies = Visit(templateMap[templateName].ParseTree);
|
||||
var dependencies = Visit(templateMap[templateName].TemplateBodyParseTree);
|
||||
evaluationTargetStack.Pop();
|
||||
|
||||
return dependencies;
|
||||
}
|
||||
|
||||
public override AnalyzerResult VisitTemplateDefinition([NotNull] LGFileParser.TemplateDefinitionContext context)
|
||||
{
|
||||
var templateNameContext = context.templateNameLine();
|
||||
if (templateNameContext.templateName().GetText().Equals(CurrentTarget().TemplateName))
|
||||
{
|
||||
if (context.templateBody() != null)
|
||||
{
|
||||
return Visit(context.templateBody());
|
||||
}
|
||||
}
|
||||
public override AnalyzerResult VisitNormalBody([NotNull] LGTemplateParser.NormalBodyContext context) => Visit(context.normalTemplateBody());
|
||||
|
||||
return new AnalyzerResult();
|
||||
}
|
||||
|
||||
public override AnalyzerResult VisitNormalBody([NotNull] LGFileParser.NormalBodyContext context) => Visit(context.normalTemplateBody());
|
||||
|
||||
public override AnalyzerResult VisitNormalTemplateBody([NotNull] LGFileParser.NormalTemplateBodyContext context)
|
||||
public override AnalyzerResult VisitNormalTemplateBody([NotNull] LGTemplateParser.NormalTemplateBodyContext context)
|
||||
{
|
||||
var result = new AnalyzerResult();
|
||||
|
||||
|
@ -102,7 +88,7 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
|
|||
return result;
|
||||
}
|
||||
|
||||
public override AnalyzerResult VisitStructuredTemplateBody([NotNull] LGFileParser.StructuredTemplateBodyContext context)
|
||||
public override AnalyzerResult VisitStructuredTemplateBody([NotNull] LGTemplateParser.StructuredTemplateBodyContext context)
|
||||
{
|
||||
var result = new AnalyzerResult();
|
||||
|
||||
|
@ -123,7 +109,7 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
|
|||
return result;
|
||||
}
|
||||
|
||||
public override AnalyzerResult VisitIfElseBody([NotNull] LGFileParser.IfElseBodyContext context)
|
||||
public override AnalyzerResult VisitIfElseBody([NotNull] LGTemplateParser.IfElseBodyContext context)
|
||||
{
|
||||
var result = new AnalyzerResult();
|
||||
|
||||
|
@ -145,7 +131,7 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
|
|||
return result;
|
||||
}
|
||||
|
||||
public override AnalyzerResult VisitSwitchCaseBody([NotNull] LGFileParser.SwitchCaseBodyContext context)
|
||||
public override AnalyzerResult VisitSwitchCaseBody([NotNull] LGTemplateParser.SwitchCaseBodyContext context)
|
||||
{
|
||||
var result = new AnalyzerResult();
|
||||
var switchCaseNodes = context.switchCaseTemplateBody().switchCaseRule();
|
||||
|
@ -166,7 +152,7 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
|
|||
return result;
|
||||
}
|
||||
|
||||
public override AnalyzerResult VisitNormalTemplateString([NotNull] LGFileParser.NormalTemplateStringContext context)
|
||||
public override AnalyzerResult VisitNormalTemplateString([NotNull] LGTemplateParser.NormalTemplateStringContext context)
|
||||
{
|
||||
var result = new AnalyzerResult();
|
||||
foreach (var expression in context.EXPRESSION())
|
||||
|
@ -177,7 +163,7 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
|
|||
return result;
|
||||
}
|
||||
|
||||
private AnalyzerResult VisitStructureValue(LGFileParser.KeyValueStructureLineContext context)
|
||||
private AnalyzerResult VisitStructureValue(LGTemplateParser.KeyValueStructureLineContext context)
|
||||
{
|
||||
var values = context.keyValueStructureValue();
|
||||
|
||||
|
@ -211,7 +197,7 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
|
|||
/// return only those without parameters.
|
||||
/// </summary>
|
||||
/// <param name="exp">Expression.</param>
|
||||
/// <returns>template refs.</returns>
|
||||
/// <returns>Template refs.</returns>
|
||||
private AnalyzerResult AnalyzeExpressionDirectly(Expression exp)
|
||||
{
|
||||
var result = new AnalyzerResult();
|
||||
|
|
|
@ -35,7 +35,7 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
|
|||
/// Gets or sets template references that this template contains.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// template references that this template contains.
|
||||
/// Template references that this template contains.
|
||||
/// </value>
|
||||
public List<string> TemplateReferences { get; set; }
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
|
|||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CustomizedMemory"/> class.
|
||||
/// </summary>
|
||||
/// <param name="scope">scope.</param>
|
||||
/// <param name="scope">Scope.</param>
|
||||
public CustomizedMemory(object scope)
|
||||
{
|
||||
this.GlobalMemory = scope == null ? null : MemoryFactory.Create(scope);
|
||||
|
@ -27,8 +27,8 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
|
|||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CustomizedMemory"/> class.
|
||||
/// </summary>
|
||||
/// <param name="globalMemory">global memory.</param>
|
||||
/// <param name="localMemory">local memory.</param>
|
||||
/// <param name="globalMemory">Global memory.</param>
|
||||
/// <param name="localMemory">Local memory.</param>
|
||||
public CustomizedMemory(IMemory globalMemory, IMemory localMemory = null)
|
||||
{
|
||||
this.GlobalMemory = globalMemory;
|
||||
|
@ -60,9 +60,9 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
|
|||
/// Try to get the value from a given path. Firstly, get result from global memory,
|
||||
/// if global memory does not contain, get from local memory.
|
||||
/// </summary>
|
||||
/// <param name="path">memory path.</param>
|
||||
/// <param name="value">resolved value.</param>
|
||||
/// <returns> true if the memory contains an element with the specified key; otherwise, false.</returns>
|
||||
/// <param name="path">Memory path.</param>
|
||||
/// <param name="value">Resolved value.</param>
|
||||
/// <returns>True if the memory contains an element with the specified key; otherwise, false.</returns>
|
||||
public bool TryGetValue(string path, out object value)
|
||||
{
|
||||
value = null;
|
||||
|
|
|
@ -14,16 +14,18 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
|
|||
public class ErrorListener : BaseErrorListener
|
||||
{
|
||||
private readonly string source;
|
||||
private readonly int lineOffset;
|
||||
|
||||
public ErrorListener(string errorSource)
|
||||
public ErrorListener(string errorSource, int lineOffset = 0)
|
||||
{
|
||||
source = errorSource;
|
||||
this.lineOffset = lineOffset;
|
||||
}
|
||||
|
||||
public override void SyntaxError([NotNull] IRecognizer recognizer, [Nullable] IToken offendingSymbol, int line, int charPositionInLine, [NotNull] string msg, [Nullable] RecognitionException e)
|
||||
{
|
||||
var startPosition = new Position(line, charPositionInLine);
|
||||
var stopPosition = new Position(line, charPositionInLine + offendingSymbol.StopIndex - offendingSymbol.StartIndex + 1);
|
||||
var startPosition = new Position(lineOffset + line, charPositionInLine);
|
||||
var stopPosition = new Position(lineOffset + line, charPositionInLine + offendingSymbol.StopIndex - offendingSymbol.StartIndex + 1);
|
||||
var range = new Range(startPosition, stopPosition);
|
||||
var diagnostic = new Diagnostic(range, TemplateErrors.SyntaxError, DiagnosticSeverity.Error, source);
|
||||
throw new TemplateException(diagnostic.ToString(), new List<Diagnostic>() { diagnostic });
|
||||
|
|
|
@ -13,8 +13,8 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
|
|||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="EvaluationTarget"/> class.
|
||||
/// </summary>
|
||||
/// <param name="templateName">template name.</param>
|
||||
/// <param name="scope">template scope.</param>
|
||||
/// <param name="templateName">Template name.</param>
|
||||
/// <param name="scope">Template scope.</param>
|
||||
public EvaluationTarget(string templateName, object scope)
|
||||
{
|
||||
TemplateName = templateName;
|
||||
|
@ -33,7 +33,7 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
|
|||
/// Gets or sets template name.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// template name.
|
||||
/// Template name.
|
||||
/// </value>
|
||||
public string TemplateName { get; set; }
|
||||
|
||||
|
@ -49,7 +49,7 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
|
|||
/// Get current instance id. If two target has the same Id,
|
||||
/// we can say they have the same template evaluation.
|
||||
/// </summary>
|
||||
/// <returns>id.</returns>
|
||||
/// <returns>Id.</returns>
|
||||
public string GetId()
|
||||
{
|
||||
var memory = (CustomizedMemory)Scope;
|
||||
|
|
|
@ -19,7 +19,7 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
|
|||
/// <summary>
|
||||
/// LG template Evaluator.
|
||||
/// </summary>
|
||||
public class Evaluator : LGFileParserBaseVisitor<object>
|
||||
public class Evaluator : LGTemplateParserBaseVisitor<object>
|
||||
{
|
||||
public const string LGType = "lgType";
|
||||
|
||||
|
@ -34,8 +34,8 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
|
|||
/// Initializes a new instance of the <see cref="Evaluator"/> class.
|
||||
/// </summary>
|
||||
/// <param name="templates">Template list.</param>
|
||||
/// <param name="expressionParser">expression parser.</param>
|
||||
/// <param name="strictMode">strict mode. If strictMode == true, exception in expression would throw outside.</param>
|
||||
/// <param name="expressionParser">Expression parser.</param>
|
||||
/// <param name="strictMode">Strict mode. If strictMode == true, exception in expression would throw outside.</param>
|
||||
public Evaluator(List<Template> templates, ExpressionParser expressionParser, bool strictMode = false)
|
||||
{
|
||||
Templates = templates;
|
||||
|
@ -73,8 +73,8 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
|
|||
/// <summary>
|
||||
/// Evaluate a template with given name and scope.
|
||||
/// </summary>
|
||||
/// <param name="inputTemplateName">template name.</param>
|
||||
/// <param name="scope">scope.</param>
|
||||
/// <param name="inputTemplateName">Template name.</param>
|
||||
/// <param name="scope">Scope.</param>
|
||||
/// <returns>Evaluate result.</returns>
|
||||
public object EvaluateTemplate(string inputTemplateName, object scope)
|
||||
{
|
||||
|
@ -112,7 +112,7 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
|
|||
|
||||
// Using a stack to track the evaluation trace
|
||||
evaluationTargetStack.Push(templateTarget);
|
||||
var result = Visit(TemplateMap[templateName].ParseTree);
|
||||
var result = Visit(TemplateMap[templateName].TemplateBodyParseTree);
|
||||
if (previousEvaluateTarget != null)
|
||||
{
|
||||
previousEvaluateTarget.EvaluatedChildren[currentEvaluateId] = result;
|
||||
|
@ -123,18 +123,7 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
|
|||
return result;
|
||||
}
|
||||
|
||||
public override object VisitTemplateDefinition([NotNull] LGFileParser.TemplateDefinitionContext context)
|
||||
{
|
||||
var templateNameContext = context.templateNameLine();
|
||||
if (templateNameContext.templateName().GetText().Equals(CurrentTarget().TemplateName))
|
||||
{
|
||||
return Visit(context.templateBody());
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public override object VisitStructuredTemplateBody([NotNull] LGFileParser.StructuredTemplateBodyContext context)
|
||||
public override object VisitStructuredTemplateBody([NotNull] LGTemplateParser.StructuredTemplateBodyContext context)
|
||||
{
|
||||
var result = new JObject();
|
||||
var typeName = context.structuredBodyNameLine().STRUCTURE_NAME().GetText();
|
||||
|
@ -173,16 +162,16 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
|
|||
return result;
|
||||
}
|
||||
|
||||
public override object VisitNormalBody([NotNull] LGFileParser.NormalBodyContext context) => Visit(context.normalTemplateBody());
|
||||
public override object VisitNormalBody([NotNull] LGTemplateParser.NormalBodyContext context) => Visit(context.normalTemplateBody());
|
||||
|
||||
public override object VisitNormalTemplateBody([NotNull] LGFileParser.NormalTemplateBodyContext context)
|
||||
public override object VisitNormalTemplateBody([NotNull] LGTemplateParser.NormalTemplateBodyContext context)
|
||||
{
|
||||
var normalTemplateStrs = context.templateString();
|
||||
var rd = new Random();
|
||||
return Visit(normalTemplateStrs[rd.Next(normalTemplateStrs.Length)].normalTemplateString());
|
||||
}
|
||||
|
||||
public override object VisitIfElseBody([NotNull] LGFileParser.IfElseBodyContext context)
|
||||
public override object VisitIfElseBody([NotNull] LGTemplateParser.IfElseBodyContext context)
|
||||
{
|
||||
var ifRules = context.ifElseTemplateBody().ifConditionRule();
|
||||
foreach (var ifRule in ifRules)
|
||||
|
@ -196,7 +185,7 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
|
|||
return null;
|
||||
}
|
||||
|
||||
public override object VisitSwitchCaseBody([NotNull] LGFileParser.SwitchCaseBodyContext context)
|
||||
public override object VisitSwitchCaseBody([NotNull] LGTemplateParser.SwitchCaseBodyContext context)
|
||||
{
|
||||
var switchCaseNodes = context.switchCaseTemplateBody().switchCaseRule();
|
||||
var length = switchCaseNodes.Length;
|
||||
|
@ -240,7 +229,7 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
|
|||
return null;
|
||||
}
|
||||
|
||||
public override object VisitNormalTemplateString([NotNull] LGFileParser.NormalTemplateStringContext context)
|
||||
public override object VisitNormalTemplateString([NotNull] LGTemplateParser.NormalTemplateStringContext context)
|
||||
{
|
||||
var prefixErrorMsg = context.GetPrefixErrorMessage();
|
||||
|
||||
|
@ -249,14 +238,14 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
|
|||
{
|
||||
switch (node.Symbol.Type)
|
||||
{
|
||||
case LGFileParser.DASH:
|
||||
case LGFileParser.MULTILINE_PREFIX:
|
||||
case LGFileParser.MULTILINE_SUFFIX:
|
||||
case LGTemplateParser.DASH:
|
||||
case LGTemplateParser.MULTILINE_PREFIX:
|
||||
case LGTemplateParser.MULTILINE_SUFFIX:
|
||||
break;
|
||||
case LGFileParser.ESCAPE_CHARACTER:
|
||||
case LGTemplateParser.ESCAPE_CHARACTER:
|
||||
result.Add(node.GetText().Escape());
|
||||
break;
|
||||
case LGFileParser.EXPRESSION:
|
||||
case LGTemplateParser.EXPRESSION:
|
||||
result.Add(EvalExpression(node.GetText(), context, prefixErrorMsg));
|
||||
break;
|
||||
default:
|
||||
|
@ -345,7 +334,7 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
|
|||
throw new Exception(ConcatErrorMsg(childErrorMsg, errorMsg));
|
||||
}
|
||||
|
||||
private object VisitStructureValue(LGFileParser.KeyValueStructureLineContext context)
|
||||
private object VisitStructureValue(LGTemplateParser.KeyValueStructureLineContext context)
|
||||
{
|
||||
var values = context.keyValueStructureValue();
|
||||
|
||||
|
@ -363,10 +352,10 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
|
|||
{
|
||||
switch (node.Symbol.Type)
|
||||
{
|
||||
case LGFileParser.ESCAPE_CHARACTER_IN_STRUCTURE_BODY:
|
||||
case LGTemplateParser.ESCAPE_CHARACTER_IN_STRUCTURE_BODY:
|
||||
itemStringResult.Append(node.GetText().Replace(@"\|", "|").Escape());
|
||||
break;
|
||||
case LGFileParser.EXPRESSION_IN_STRUCTURE_BODY:
|
||||
case LGTemplateParser.EXPRESSION_IN_STRUCTURE_BODY:
|
||||
var errorPrefix = "Property '" + context.STRUCTURE_IDENTIFIER().GetText() + "':";
|
||||
itemStringResult.Append(EvalExpression(node.GetText(), context, errorPrefix));
|
||||
break;
|
||||
|
@ -383,7 +372,7 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
|
|||
return result.Count == 1 ? result[0] : result;
|
||||
}
|
||||
|
||||
private bool EvalCondition(LGFileParser.IfConditionContext condition)
|
||||
private bool EvalCondition(LGTemplateParser.IfConditionContext condition)
|
||||
{
|
||||
var expression = condition.EXPRESSION(0);
|
||||
if (expression == null || // no expression means it's else
|
||||
|
@ -452,7 +441,8 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
|
|||
private (object value, string error) EvalByAdaptiveExpression(string exp, object scope)
|
||||
{
|
||||
var parse = this.ExpressionParser.Parse(exp);
|
||||
return parse.TryEvaluate(scope);
|
||||
var result = parse.TryEvaluate(scope);
|
||||
return result;
|
||||
}
|
||||
|
||||
// Generate a new lookup function based on one lookup function
|
||||
|
@ -555,7 +545,7 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
|
|||
else
|
||||
{
|
||||
var template = TemplateMap[CurrentTarget().TemplateName];
|
||||
var sourcePath = template.Source.NormalizePath();
|
||||
var sourcePath = template.SourceRange.Source.NormalizePath();
|
||||
var baseFolder = Environment.CurrentDirectory;
|
||||
if (Path.IsPathRooted(sourcePath))
|
||||
{
|
||||
|
@ -587,7 +577,7 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
|
|||
|
||||
if ((children0.ReturnType & ReturnType.Object) == 0 && (children0.ReturnType & ReturnType.String) == 0)
|
||||
{
|
||||
throw new Exception(TemplateErrors.ErrorTemplateNameformat(children0.ToString()));
|
||||
throw new Exception(TemplateErrors.InvalidTemplateName);
|
||||
}
|
||||
|
||||
// Validate more if the name is string constant
|
||||
|
|
|
@ -19,7 +19,7 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
|
|||
/// <summary>
|
||||
/// LG template expander.
|
||||
/// </summary>
|
||||
public class Expander : LGFileParserBaseVisitor<List<object>>
|
||||
public class Expander : LGTemplateParserBaseVisitor<List<object>>
|
||||
{
|
||||
public const string LGType = "lgType";
|
||||
public static readonly string RegexString = @"(?<!\\)\${(('(\\('|\\)|[^'])*?')|(""(\\(""|\\)|[^""])*?"")|(`(\\(`|\\)|[^`])*?`)|([^\r\n{}'""`])|({\s*}))+}?";
|
||||
|
@ -30,9 +30,9 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
|
|||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Expander"/> class.
|
||||
/// </summary>
|
||||
/// <param name="templates">template list.</param>
|
||||
/// <param name="templates">Template list.</param>
|
||||
/// <param name="expressionParser">Given expression parser.</param>
|
||||
/// <param name="strictMode">strict mode. If strictMode == true, exception in expression would throw outside.</param>
|
||||
/// <param name="strictMode">Strict mode. If strictMode == true, exception in expression would throw outside.</param>
|
||||
public Expander(List<Template> templates, ExpressionParser expressionParser, bool strictMode = false)
|
||||
{
|
||||
Templates = templates;
|
||||
|
@ -101,26 +101,15 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
|
|||
|
||||
// Using a stack to track the evaluation trace
|
||||
evaluationTargetStack.Push(new EvaluationTarget(templateName, scope));
|
||||
var result = Visit(TemplateMap[templateName].ParseTree);
|
||||
var result = Visit(TemplateMap[templateName].TemplateBodyParseTree);
|
||||
evaluationTargetStack.Pop();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public override List<object> VisitTemplateDefinition([NotNull] LGFileParser.TemplateDefinitionContext context)
|
||||
{
|
||||
var templateNameContext = context.templateNameLine();
|
||||
if (templateNameContext.templateName().GetText().Equals(CurrentTarget().TemplateName))
|
||||
{
|
||||
return Visit(context.templateBody());
|
||||
}
|
||||
public override List<object> VisitNormalBody([NotNull] LGTemplateParser.NormalBodyContext context) => Visit(context.normalTemplateBody());
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public override List<object> VisitNormalBody([NotNull] LGFileParser.NormalBodyContext context) => Visit(context.normalTemplateBody());
|
||||
|
||||
public override List<object> VisitNormalTemplateBody([NotNull] LGFileParser.NormalTemplateBodyContext context)
|
||||
public override List<object> VisitNormalTemplateBody([NotNull] LGTemplateParser.NormalTemplateBodyContext context)
|
||||
{
|
||||
var normalTemplateStrs = context.templateString();
|
||||
var result = new List<object>();
|
||||
|
@ -133,7 +122,7 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
|
|||
return result;
|
||||
}
|
||||
|
||||
public override List<object> VisitIfElseBody([NotNull] LGFileParser.IfElseBodyContext context)
|
||||
public override List<object> VisitIfElseBody([NotNull] LGTemplateParser.IfElseBodyContext context)
|
||||
{
|
||||
var ifRules = context.ifElseTemplateBody().ifConditionRule();
|
||||
foreach (var ifRule in ifRules)
|
||||
|
@ -147,7 +136,7 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
|
|||
return null;
|
||||
}
|
||||
|
||||
public override List<object> VisitSwitchCaseBody([NotNull] LGFileParser.SwitchCaseBodyContext context)
|
||||
public override List<object> VisitSwitchCaseBody([NotNull] LGTemplateParser.SwitchCaseBodyContext context)
|
||||
{
|
||||
var switchCaseNodes = context.switchCaseTemplateBody().switchCaseRule();
|
||||
var length = switchCaseNodes.Length;
|
||||
|
@ -190,7 +179,7 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
|
|||
return null;
|
||||
}
|
||||
|
||||
public override List<object> VisitStructuredBody([NotNull] LGFileParser.StructuredBodyContext context)
|
||||
public override List<object> VisitStructuredBody([NotNull] LGTemplateParser.StructuredBodyContext context)
|
||||
{
|
||||
var templateRefValues = new Dictionary<string, List<object>>();
|
||||
var stb = context.structuredTemplateBody();
|
||||
|
@ -278,7 +267,7 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
|
|||
return finalResult;
|
||||
}
|
||||
|
||||
public override List<object> VisitNormalTemplateString([NotNull] LGFileParser.NormalTemplateStringContext context)
|
||||
public override List<object> VisitNormalTemplateString([NotNull] LGTemplateParser.NormalTemplateStringContext context)
|
||||
{
|
||||
var prefixErrorMsg = context.GetPrefixErrorMessage();
|
||||
var result = new List<string>() { string.Empty };
|
||||
|
@ -286,14 +275,14 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
|
|||
{
|
||||
switch (node.Symbol.Type)
|
||||
{
|
||||
case LGFileParser.DASH:
|
||||
case LGFileParser.MULTILINE_PREFIX:
|
||||
case LGFileParser.MULTILINE_SUFFIX:
|
||||
case LGTemplateParser.DASH:
|
||||
case LGTemplateParser.MULTILINE_PREFIX:
|
||||
case LGTemplateParser.MULTILINE_SUFFIX:
|
||||
break;
|
||||
case LGFileParser.ESCAPE_CHARACTER:
|
||||
case LGTemplateParser.ESCAPE_CHARACTER:
|
||||
result = StringListConcat(result, new List<string>() { node.GetText().Escape() });
|
||||
break;
|
||||
case LGFileParser.EXPRESSION:
|
||||
case LGTemplateParser.EXPRESSION:
|
||||
result = StringListConcat(result, EvalExpression(node.GetText(), context, prefixErrorMsg));
|
||||
break;
|
||||
default:
|
||||
|
@ -320,7 +309,7 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
|
|||
return newScope;
|
||||
}
|
||||
|
||||
private bool EvalCondition(LGFileParser.IfConditionContext condition)
|
||||
private bool EvalCondition(LGTemplateParser.IfConditionContext condition)
|
||||
{
|
||||
var expression = condition.EXPRESSION(0);
|
||||
if (expression == null || // no expression means it's else
|
||||
|
@ -332,7 +321,7 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
|
|||
return false;
|
||||
}
|
||||
|
||||
private List<List<object>> VisitStructureValue(LGFileParser.KeyValueStructureLineContext context)
|
||||
private List<List<object>> VisitStructureValue(LGTemplateParser.KeyValueStructureLineContext context)
|
||||
{
|
||||
var values = context.keyValueStructureValue();
|
||||
|
||||
|
@ -350,10 +339,10 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
|
|||
{
|
||||
switch (node.Symbol.Type)
|
||||
{
|
||||
case LGFileParser.ESCAPE_CHARACTER_IN_STRUCTURE_BODY:
|
||||
case LGTemplateParser.ESCAPE_CHARACTER_IN_STRUCTURE_BODY:
|
||||
itemStringResult = StringListConcat(itemStringResult, new List<string>() { node.GetText().Replace(@"\|", "|").Escape() });
|
||||
break;
|
||||
case LGFileParser.EXPRESSION_IN_STRUCTURE_BODY:
|
||||
case LGTemplateParser.EXPRESSION_IN_STRUCTURE_BODY:
|
||||
var errorPrefix = "Property '" + context.STRUCTURE_IDENTIFIER().GetText() + "':";
|
||||
itemStringResult = StringListConcat(itemStringResult, EvalExpression(node.GetText(), item, errorPrefix));
|
||||
break;
|
||||
|
@ -556,7 +545,7 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
|
|||
|
||||
if ((children0.ReturnType & ReturnType.Object) == 0 && (children0.ReturnType & ReturnType.String) == 0)
|
||||
{
|
||||
throw new Exception(TemplateErrors.ErrorTemplateNameformat(children0.ToString()));
|
||||
throw new Exception(TemplateErrors.InvalidTemplateName);
|
||||
}
|
||||
|
||||
// Validate more if the name is string constant
|
||||
|
@ -658,7 +647,7 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
|
|||
else
|
||||
{
|
||||
var template = TemplateMap[CurrentTarget().TemplateName];
|
||||
var sourcePath = template.Source.NormalizePath();
|
||||
var sourcePath = template.SourceRange.Source.NormalizePath();
|
||||
var baseFolder = Environment.CurrentDirectory;
|
||||
if (Path.IsPathRooted(sourcePath))
|
||||
{
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text.RegularExpressions;
|
||||
using Antlr4.Runtime;
|
||||
using Antlr4.Runtime.Tree;
|
||||
|
||||
namespace Microsoft.Bot.Builder.LanguageGeneration
|
||||
|
@ -19,9 +20,9 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
|
|||
/// If a value is pure Expression.
|
||||
/// </summary>
|
||||
/// <param name="context">Key value structure value context.</param>
|
||||
/// <param name="expression">string expression.</param>
|
||||
/// <returns>is pure expression or not.</returns>
|
||||
public static bool IsPureExpression(this LGFileParser.KeyValueStructureValueContext context, out string expression)
|
||||
/// <param name="expression">String expression.</param>
|
||||
/// <returns>Is pure expression or not.</returns>
|
||||
public static bool IsPureExpression(this LGTemplateParser.KeyValueStructureValueContext context, out string expression)
|
||||
{
|
||||
expression = context.GetText();
|
||||
|
||||
|
@ -30,9 +31,9 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
|
|||
{
|
||||
switch (node.Symbol.Type)
|
||||
{
|
||||
case LGFileParser.ESCAPE_CHARACTER_IN_STRUCTURE_BODY:
|
||||
case LGTemplateParser.ESCAPE_CHARACTER_IN_STRUCTURE_BODY:
|
||||
return false;
|
||||
case LGFileParser.EXPRESSION_IN_STRUCTURE_BODY:
|
||||
case LGTemplateParser.EXPRESSION_IN_STRUCTURE_BODY:
|
||||
if (hasExpression)
|
||||
{
|
||||
return false;
|
||||
|
@ -57,8 +58,8 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
|
|||
/// <summary>
|
||||
/// Escape \ from text.
|
||||
/// </summary>
|
||||
/// <param name="text">input text.</param>
|
||||
/// <returns>escaped text.</returns>
|
||||
/// <param name="text">Input text.</param>
|
||||
/// <returns>Escaped text.</returns>
|
||||
public static string Escape(this string text)
|
||||
{
|
||||
if (text == null)
|
||||
|
@ -88,8 +89,8 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
|
|||
/// <summary>
|
||||
/// trim expression. ${abc} => abc, ${a == {}} => a == {}.
|
||||
/// </summary>
|
||||
/// <param name="expression">input expression string.</param>
|
||||
/// <returns>pure expression string.</returns>
|
||||
/// <param name="expression">Input expression string.</param>
|
||||
/// <returns>Pure expression string.</returns>
|
||||
public static string TrimExpression(this string expression)
|
||||
{
|
||||
var result = expression.Trim().TrimStart('$').Trim();
|
||||
|
@ -110,8 +111,8 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
|
|||
/// This method treats / and \ both as separators regardless of OS, for Windows that means / -> \ and for Linux/Mac \ -> /.
|
||||
/// This allows author to use ../foo.lg or ..\foo.lg as equivalents for importing.
|
||||
/// </remarks>
|
||||
/// <param name="ambigiousPath">authoredPath.</param>
|
||||
/// <returns>path expressed as OS path.</returns>
|
||||
/// <param name="ambigiousPath">Authored path.</param>
|
||||
/// <returns>Path expressed as OS path.</returns>
|
||||
public static string NormalizePath(this string ambigiousPath)
|
||||
{
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
|
@ -129,18 +130,18 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
|
|||
/// <summary>
|
||||
/// Get prefix error message from normal template sting context.
|
||||
/// </summary>
|
||||
/// <param name="context">normal template sting context.</param>
|
||||
/// <returns>prefix error message.</returns>
|
||||
public static string GetPrefixErrorMessage(this LGFileParser.NormalTemplateStringContext context)
|
||||
/// <param name="context">Normal template sting context.</param>
|
||||
/// <returns>Prefix error message.</returns>
|
||||
public static string GetPrefixErrorMessage(this LGTemplateParser.NormalTemplateStringContext context)
|
||||
{
|
||||
var errorPrefix = string.Empty;
|
||||
if (context.Parent?.Parent?.Parent is LGFileParser.IfConditionRuleContext conditionContext)
|
||||
if (context.Parent?.Parent?.Parent is LGTemplateParser.IfConditionRuleContext conditionContext)
|
||||
{
|
||||
errorPrefix = "Condition '" + conditionContext.ifCondition()?.EXPRESSION(0)?.GetText() + "': ";
|
||||
}
|
||||
else
|
||||
{
|
||||
if (context.Parent?.Parent?.Parent is LGFileParser.SwitchCaseRuleContext switchCaseContext)
|
||||
if (context.Parent?.Parent?.Parent is LGTemplateParser.SwitchCaseRuleContext switchCaseContext)
|
||||
{
|
||||
var state = switchCaseContext.switchCaseStat();
|
||||
if (state?.DEFAULT() != null)
|
||||
|
@ -160,5 +161,23 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
|
|||
|
||||
return errorPrefix;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert antlr parser into Range.
|
||||
/// </summary>
|
||||
/// <param name="context">Antlr parse context.</param>
|
||||
/// <param name="lineOffset">Line offset.</param>
|
||||
/// <returns>Range object.</returns>
|
||||
public static Range ConvertToRange(this ParserRuleContext context, int lineOffset = 0)
|
||||
{
|
||||
if (context == null)
|
||||
{
|
||||
return Range.DefaultRange;
|
||||
}
|
||||
|
||||
var startPosition = new Position(lineOffset + context.Start.Line, context.Start.Column);
|
||||
var stopPosition = new Position(lineOffset + context.Stop.Line, context.Stop.Column + context.Stop.Text.Length);
|
||||
return new Range(startPosition, stopPosition);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,259 +1,24 @@
|
|||
lexer grammar LGFileLexer;
|
||||
|
||||
// From a multiple-lanague perpective, it's not recommended to use members, predicates and actions to
|
||||
// put target-language-specific code in lexer rules
|
||||
|
||||
// the reason we use it here is that
|
||||
// 1. it greatly simplify the lexer rules, can avoid unnecessary lexer modes
|
||||
// 2. it helps us to output token more precisely
|
||||
// (for example, 'CASE:' not followed right after '-' will not be treated as a CASE token)
|
||||
// 3. we only use very basic boolen variables, and basic predidates
|
||||
// so it would be very little effort to translate to other languages
|
||||
|
||||
@parser::header {#pragma warning disable 3021} // Disable StyleCop warning CS3021 re CLSCompliant attribute in generated files.
|
||||
@lexer::header {#pragma warning disable 3021} // Disable StyleCop warning CS3021 re CLSCompliant attribute in generated files.
|
||||
|
||||
@lexer::members {
|
||||
bool ignoreWS = true; // usually we ignore whitespace, but inside template, whitespace is significant
|
||||
bool inTemplate = false; // whether we are in the template
|
||||
bool beginOfTemplateBody = false; // whether we are at the begining of template body
|
||||
bool inMultiline = false; // whether we are in multiline
|
||||
bool beginOfTemplateLine = false;// weather we are at the begining of template string
|
||||
bool inStructuredValue = false; // weather we are in the structure value
|
||||
bool beginOfStructureProperty = false; // weather we are at the begining of structure property
|
||||
bool startTemplate = false;
|
||||
}
|
||||
|
||||
// fragments
|
||||
fragment A: 'a' | 'A';
|
||||
fragment C: 'c' | 'C';
|
||||
fragment D: 'd' | 'D';
|
||||
fragment E: 'e' | 'E';
|
||||
fragment F: 'f' | 'F';
|
||||
fragment H: 'h' | 'H';
|
||||
fragment I: 'i' | 'I';
|
||||
fragment L: 'l' | 'L';
|
||||
fragment S: 's' | 'S';
|
||||
fragment T: 't' | 'T';
|
||||
fragment U: 'u' | 'U';
|
||||
fragment W: 'w' | 'W';
|
||||
|
||||
fragment LETTER: 'a'..'z' | 'A'..'Z';
|
||||
|
||||
fragment NUMBER: '0'..'9';
|
||||
|
||||
fragment WHITESPACE : ' '|'\t'|'\ufeff'|'\u00a0';
|
||||
|
||||
fragment STRING_LITERAL : ('\'' (('\\'('\''|'\\'))|(~'\''))*? '\'') | ('"' (('\\'('"'|'\\'))|(~'"'))*? '"');
|
||||
NEWLINE : '\r'? '\n';
|
||||
|
||||
fragment STRING_INTERPOLATION : '`' (('\\'('`'|'\\'))|(~'`'))*? '`';
|
||||
OPTION : WHITESPACE* '>' WHITESPACE* '!#' ~('\r'|'\n')+ { !startTemplate }?;
|
||||
|
||||
fragment ESCAPE_CHARACTER_FRAGMENT : '\\' ~[\r\n]?;
|
||||
COMMENT : WHITESPACE* '>' ~('\r'|'\n')* { !startTemplate }?;
|
||||
|
||||
IMPORT : WHITESPACE* '[' ~[\r\n[\]]*? ']' '(' ~[\r\n()]*? ')' WHITESPACE* { !startTemplate }?;
|
||||
|
||||
// top level elements
|
||||
OPTIONS
|
||||
: '>' WHITESPACE* '!#' ~('\r'|'\n')+
|
||||
;
|
||||
TEMPLATE_NAME_LINE : WHITESPACE* '#' ~('\r'|'\n')* { startTemplate = true; };
|
||||
|
||||
COMMENTS
|
||||
: '>' ~('\r'|'\n')* -> skip
|
||||
;
|
||||
|
||||
WS
|
||||
: WHITESPACE+ -> skip
|
||||
;
|
||||
|
||||
NEWLINE
|
||||
: '\r'? '\n' -> skip
|
||||
;
|
||||
|
||||
HASH
|
||||
: '#' { inTemplate = true; beginOfTemplateBody = false; } -> pushMode(TEMPLATE_NAME_MODE)
|
||||
;
|
||||
|
||||
DASH
|
||||
: '-' { inTemplate }? { beginOfTemplateLine = true; beginOfTemplateBody = false; } -> pushMode(TEMPLATE_BODY_MODE)
|
||||
;
|
||||
|
||||
OBJECT_DEFINITION
|
||||
: '{' ((WHITESPACE) | ((IDENTIFIER | STRING_LITERAL) ':' ( STRING_LITERAL | ~[{}\r\n'"`] | OBJECT_DEFINITION)+))* '}'
|
||||
;
|
||||
|
||||
EXPRESSION_FRAGMENT
|
||||
: '$' '{' (STRING_LITERAL | STRING_INTERPOLATION | OBJECT_DEFINITION | ~[}'"`])+ '}'?
|
||||
;
|
||||
|
||||
LEFT_SQUARE_BRACKET
|
||||
: '[' { inTemplate && beginOfTemplateBody }? -> pushMode(STRUCTURE_NAME_MODE)
|
||||
;
|
||||
|
||||
IMPORT
|
||||
: '[' ~[\r\n[\]]*? ']' '(' ~[\r\n()]*? ')' { inTemplate = false;}
|
||||
;
|
||||
|
||||
INVALID_TOKEN
|
||||
: . { inTemplate = false; beginOfTemplateBody = false; }
|
||||
;
|
||||
|
||||
mode TEMPLATE_NAME_MODE;
|
||||
|
||||
WS_IN_NAME
|
||||
: WHITESPACE+ -> skip
|
||||
;
|
||||
|
||||
NEWLINE_IN_NAME
|
||||
: '\r'? '\n' { beginOfTemplateBody = true;}-> skip, popMode
|
||||
;
|
||||
|
||||
IDENTIFIER
|
||||
: (LETTER | NUMBER | '_') (LETTER | NUMBER | '_')*
|
||||
;
|
||||
|
||||
DOT
|
||||
: '.'
|
||||
;
|
||||
|
||||
OPEN_PARENTHESIS
|
||||
: '('
|
||||
;
|
||||
|
||||
CLOSE_PARENTHESIS
|
||||
: ')'
|
||||
;
|
||||
|
||||
COMMA
|
||||
: ','
|
||||
;
|
||||
|
||||
TEXT_IN_NAME
|
||||
: ~[\r\n]+?
|
||||
;
|
||||
|
||||
mode TEMPLATE_BODY_MODE;
|
||||
|
||||
WS_IN_BODY
|
||||
: WHITESPACE+ {ignoreWS}? -> skip
|
||||
;
|
||||
|
||||
MULTILINE_PREFIX
|
||||
: '```' { !inMultiline && beginOfTemplateLine }? { inMultiline = true; beginOfTemplateLine = false;}-> pushMode(MULTILINE_MODE)
|
||||
;
|
||||
|
||||
NEWLINE_IN_BODY
|
||||
: '\r'? '\n' { ignoreWS = true;} -> skip, popMode
|
||||
;
|
||||
|
||||
IF
|
||||
: I F WHITESPACE* ':' {beginOfTemplateLine}? { ignoreWS = true; beginOfTemplateLine = false;}
|
||||
;
|
||||
|
||||
ELSEIF
|
||||
: E L S E WHITESPACE* I F WHITESPACE* ':' {beginOfTemplateLine}? { ignoreWS = true; beginOfTemplateLine = false;}
|
||||
;
|
||||
|
||||
ELSE
|
||||
: E L S E WHITESPACE* ':' {beginOfTemplateLine}? { ignoreWS = true; beginOfTemplateLine = false;}
|
||||
;
|
||||
|
||||
SWITCH
|
||||
: S W I T C H WHITESPACE* ':' {beginOfTemplateLine}? { ignoreWS = true; beginOfTemplateLine = false;}
|
||||
;
|
||||
|
||||
CASE
|
||||
: C A S E WHITESPACE* ':' {beginOfTemplateLine}? { ignoreWS = true; beginOfTemplateLine = false;}
|
||||
;
|
||||
|
||||
DEFAULT
|
||||
: D E F A U L T WHITESPACE* ':' {beginOfTemplateLine}? { ignoreWS = true; beginOfTemplateLine = false;}
|
||||
;
|
||||
|
||||
ESCAPE_CHARACTER
|
||||
: ESCAPE_CHARACTER_FRAGMENT { ignoreWS = false; beginOfTemplateLine = false;}
|
||||
;
|
||||
|
||||
EXPRESSION
|
||||
: EXPRESSION_FRAGMENT { ignoreWS = false; beginOfTemplateLine = false;}
|
||||
;
|
||||
|
||||
TEXT
|
||||
: ~[\r\n]+? { ignoreWS = false; beginOfTemplateLine = false;}
|
||||
;
|
||||
|
||||
mode MULTILINE_MODE;
|
||||
|
||||
MULTILINE_SUFFIX
|
||||
: '```' { inMultiline = false; } -> popMode
|
||||
;
|
||||
|
||||
MULTILINE_ESCAPE_CHARACTER
|
||||
: ESCAPE_CHARACTER_FRAGMENT -> type(ESCAPE_CHARACTER)
|
||||
;
|
||||
|
||||
MULTILINE_EXPRESSION
|
||||
: EXPRESSION_FRAGMENT -> type(EXPRESSION)
|
||||
;
|
||||
|
||||
MULTILINE_TEXT
|
||||
: (('\r'? '\n') | ~[\r\n])+? -> type(TEXT)
|
||||
;
|
||||
|
||||
mode STRUCTURE_NAME_MODE;
|
||||
|
||||
WS_IN_STRUCTURE_NAME
|
||||
: WHITESPACE+ -> skip
|
||||
;
|
||||
|
||||
NEWLINE_IN_STRUCTURE_NAME
|
||||
: '\r'? '\n' { ignoreWS = true;} {beginOfStructureProperty = true;}-> skip, pushMode(STRUCTURE_BODY_MODE)
|
||||
;
|
||||
|
||||
STRUCTURE_NAME
|
||||
: (LETTER | NUMBER | '_') (LETTER | NUMBER | '-' | '_' | '.')*
|
||||
;
|
||||
|
||||
TEXT_IN_STRUCTURE_NAME
|
||||
: ~[\r\n]+?
|
||||
;
|
||||
|
||||
mode STRUCTURE_BODY_MODE;
|
||||
|
||||
STRUCTURED_COMMENTS
|
||||
: '>' ~[\r\n]* '\r'?'\n' { !inStructuredValue && beginOfStructureProperty}? -> skip
|
||||
;
|
||||
|
||||
WS_IN_STRUCTURE_BODY
|
||||
: WHITESPACE+ {ignoreWS}? -> skip
|
||||
;
|
||||
|
||||
STRUCTURED_NEWLINE
|
||||
: '\r'? '\n' { ignoreWS = true; inStructuredValue = false; beginOfStructureProperty = true;}
|
||||
;
|
||||
|
||||
STRUCTURED_BODY_END
|
||||
: ']' {!inStructuredValue}? { inTemplate = false; beginOfTemplateBody = false;} -> popMode, popMode
|
||||
;
|
||||
|
||||
STRUCTURE_IDENTIFIER
|
||||
: (LETTER | NUMBER | '_') (LETTER | NUMBER | '-' | '_' | '.')* { !inStructuredValue && beginOfStructureProperty}? {beginOfStructureProperty = false;}
|
||||
;
|
||||
|
||||
STRUCTURE_EQUALS
|
||||
: '=' {!inStructuredValue}? {inStructuredValue = true;}
|
||||
;
|
||||
|
||||
STRUCTURE_OR_MARK
|
||||
: '|' { ignoreWS = true; }
|
||||
;
|
||||
|
||||
ESCAPE_CHARACTER_IN_STRUCTURE_BODY
|
||||
: ESCAPE_CHARACTER_FRAGMENT { ignoreWS = false; }
|
||||
;
|
||||
|
||||
EXPRESSION_IN_STRUCTURE_BODY
|
||||
: EXPRESSION_FRAGMENT { ignoreWS = false; }
|
||||
;
|
||||
|
||||
TEXT_IN_STRUCTURE_BODY
|
||||
: ~[\r\n]+? { ignoreWS = false; beginOfStructureProperty = false;}
|
||||
;
|
||||
TEMPLATE_BODY_LINE : ~('\r'|'\n')+ { startTemplate }?;
|
||||
|
||||
INVALID_LINE : ~('\r'|'\n')+ { !startTemplate }?;
|
|
@ -6,130 +6,47 @@ parser grammar LGFileParser;
|
|||
options { tokenVocab=LGFileLexer; }
|
||||
|
||||
file
|
||||
: paragraph+? EOF
|
||||
;
|
||||
: paragraph+? EOF
|
||||
;
|
||||
|
||||
paragraph
|
||||
: templateDefinition
|
||||
| importDefinition
|
||||
| optionsDefinition
|
||||
| optionDefinition
|
||||
| errorDefinition
|
||||
| commentDefinition
|
||||
| NEWLINE
|
||||
| EOF
|
||||
| errorTemplate
|
||||
;
|
||||
|
||||
errorTemplate
|
||||
: INVALID_TOKEN+
|
||||
;
|
||||
|
||||
templateDefinition
|
||||
: templateNameLine templateBody?
|
||||
;
|
||||
|
||||
templateNameLine
|
||||
: HASH ((templateName parameters?) | errorTemplateName)
|
||||
;
|
||||
|
||||
errorTemplateName
|
||||
: (IDENTIFIER|TEXT_IN_NAME|OPEN_PARENTHESIS|COMMA|CLOSE_PARENTHESIS|DOT)*
|
||||
;
|
||||
|
||||
templateName
|
||||
: IDENTIFIER (DOT IDENTIFIER)*
|
||||
;
|
||||
|
||||
parameters
|
||||
: OPEN_PARENTHESIS (IDENTIFIER (COMMA IDENTIFIER)*)? CLOSE_PARENTHESIS
|
||||
;
|
||||
|
||||
templateBody
|
||||
: normalTemplateBody #normalBody
|
||||
| ifElseTemplateBody #ifElseBody
|
||||
| switchCaseTemplateBody #switchCaseBody
|
||||
| structuredTemplateBody #structuredBody
|
||||
;
|
||||
|
||||
structuredTemplateBody
|
||||
: structuredBodyNameLine (((structuredBodyContentLine? STRUCTURED_NEWLINE) | errorStructureLine)+)? structuredBodyEndLine?
|
||||
;
|
||||
|
||||
structuredBodyNameLine
|
||||
: LEFT_SQUARE_BRACKET (STRUCTURE_NAME | errorStructuredName)
|
||||
;
|
||||
|
||||
errorStructuredName
|
||||
: (STRUCTURE_NAME|TEXT_IN_STRUCTURE_NAME)*
|
||||
;
|
||||
|
||||
structuredBodyContentLine
|
||||
: keyValueStructureLine
|
||||
| objectStructureLine
|
||||
;
|
||||
|
||||
errorStructureLine
|
||||
: (STRUCTURE_IDENTIFIER|STRUCTURE_EQUALS|STRUCTURE_OR_MARK|TEXT_IN_STRUCTURE_BODY|EXPRESSION_IN_STRUCTURE_BODY|ESCAPE_CHARACTER_IN_STRUCTURE_BODY)+
|
||||
;
|
||||
|
||||
keyValueStructureLine
|
||||
: STRUCTURE_IDENTIFIER STRUCTURE_EQUALS keyValueStructureValue (STRUCTURE_OR_MARK keyValueStructureValue)*
|
||||
;
|
||||
|
||||
keyValueStructureValue
|
||||
: (TEXT_IN_STRUCTURE_BODY|EXPRESSION_IN_STRUCTURE_BODY|ESCAPE_CHARACTER_IN_STRUCTURE_BODY)+
|
||||
;
|
||||
|
||||
objectStructureLine
|
||||
: EXPRESSION_IN_STRUCTURE_BODY
|
||||
;
|
||||
|
||||
structuredBodyEndLine
|
||||
: STRUCTURED_BODY_END
|
||||
;
|
||||
|
||||
normalTemplateBody
|
||||
: templateString+
|
||||
;
|
||||
|
||||
templateString
|
||||
: normalTemplateString
|
||||
| errorTemplateString
|
||||
;
|
||||
|
||||
normalTemplateString
|
||||
: DASH MULTILINE_PREFIX? (TEXT|EXPRESSION|ESCAPE_CHARACTER)* MULTILINE_SUFFIX?
|
||||
;
|
||||
|
||||
errorTemplateString
|
||||
: INVALID_TOKEN+
|
||||
;
|
||||
|
||||
ifElseTemplateBody
|
||||
: ifConditionRule+
|
||||
;
|
||||
|
||||
ifConditionRule
|
||||
: ifCondition normalTemplateBody?
|
||||
;
|
||||
|
||||
ifCondition
|
||||
: DASH (IF|ELSE|ELSEIF) (WS|TEXT|EXPRESSION)*
|
||||
;
|
||||
|
||||
switchCaseTemplateBody
|
||||
: switchCaseRule+
|
||||
;
|
||||
|
||||
switchCaseRule
|
||||
: switchCaseStat normalTemplateBody?
|
||||
;
|
||||
|
||||
switchCaseStat
|
||||
: DASH (SWITCH|CASE|DEFAULT) (WS|TEXT|EXPRESSION)*
|
||||
commentDefinition
|
||||
: COMMENT NEWLINE?
|
||||
;
|
||||
|
||||
importDefinition
|
||||
: IMPORT
|
||||
: IMPORT NEWLINE?
|
||||
;
|
||||
|
||||
optionsDefinition
|
||||
: OPTIONS
|
||||
optionDefinition
|
||||
: OPTION NEWLINE?
|
||||
;
|
||||
|
||||
errorDefinition
|
||||
: INVALID_LINE NEWLINE?
|
||||
;
|
||||
|
||||
templateDefinition
|
||||
: templateNameLine templateBody
|
||||
;
|
||||
|
||||
templateNameLine
|
||||
: TEMPLATE_NAME_LINE NEWLINE?
|
||||
;
|
||||
|
||||
templateBody
|
||||
: templateBodyLine*
|
||||
;
|
||||
|
||||
templateBodyLine
|
||||
: (TEMPLATE_BODY_LINE NEWLINE?) | NEWLINE
|
||||
;
|
|
@ -0,0 +1,202 @@
|
|||
lexer grammar LGTemplateLexer;
|
||||
|
||||
@parser::header {#pragma warning disable 3021} // Disable StyleCop warning CS3021 re CLSCompliant attribute in generated files.
|
||||
@lexer::header {#pragma warning disable 3021} // Disable StyleCop warning CS3021 re CLSCompliant attribute in generated files.
|
||||
|
||||
@lexer::members {
|
||||
bool ignoreWS = true; // usually we ignore whitespace, but inside template, whitespace is significant
|
||||
bool beginOfTemplateBody = true; // whether we are at the begining of template body
|
||||
bool inMultiline = false; // whether we are in multiline
|
||||
bool beginOfTemplateLine = false;// weather we are at the begining of template string
|
||||
bool inStructuredValue = false; // weather we are in the structure value
|
||||
bool beginOfStructureProperty = false; // weather we are at the begining of structure property
|
||||
}
|
||||
|
||||
// fragments
|
||||
fragment A: 'a' | 'A';
|
||||
fragment C: 'c' | 'C';
|
||||
fragment D: 'd' | 'D';
|
||||
fragment E: 'e' | 'E';
|
||||
fragment F: 'f' | 'F';
|
||||
fragment H: 'h' | 'H';
|
||||
fragment I: 'i' | 'I';
|
||||
fragment L: 'l' | 'L';
|
||||
fragment S: 's' | 'S';
|
||||
fragment T: 't' | 'T';
|
||||
fragment U: 'u' | 'U';
|
||||
fragment W: 'w' | 'W';
|
||||
|
||||
fragment LETTER: 'a'..'z' | 'A'..'Z';
|
||||
|
||||
fragment NUMBER: '0'..'9';
|
||||
|
||||
fragment WHITESPACE : ' '|'\t'|'\ufeff'|'\u00a0';
|
||||
|
||||
fragment STRING_LITERAL : ('\'' (('\\'('\''|'\\'))|(~'\''))*? '\'') | ('"' (('\\'('"'|'\\'))|(~'"'))*? '"');
|
||||
|
||||
fragment STRING_INTERPOLATION : '`' (('\\'('`'|'\\'))|(~'`'))*? '`';
|
||||
|
||||
fragment ESCAPE_CHARACTER_FRAGMENT : '\\' ~[\r\n]?;
|
||||
|
||||
fragment IDENTIFIER : (LETTER | NUMBER | '_') (LETTER | NUMBER | '_')*;
|
||||
|
||||
WS
|
||||
: WHITESPACE+ -> skip
|
||||
;
|
||||
|
||||
NEWLINE
|
||||
: '\r'? '\n' -> skip
|
||||
;
|
||||
|
||||
COMMENTS
|
||||
: '>' ~('\r'|'\n')* -> skip
|
||||
;
|
||||
|
||||
DASH
|
||||
: '-' { beginOfTemplateLine = true; beginOfTemplateBody = false; } -> pushMode(NORMAL_TEMPLATE_BODY_MODE)
|
||||
;
|
||||
|
||||
OBJECT_DEFINITION
|
||||
: '{' ((WHITESPACE) | ((IDENTIFIER | STRING_LITERAL) ':' ( STRING_LITERAL | ~[{}\r\n'"`] | OBJECT_DEFINITION)+))* '}'
|
||||
;
|
||||
|
||||
EXPRESSION_FRAGMENT
|
||||
: '$' '{' (STRING_LITERAL | STRING_INTERPOLATION | OBJECT_DEFINITION | ~[}'"`])+ '}'?
|
||||
;
|
||||
|
||||
LEFT_SQUARE_BRACKET
|
||||
: '[' { beginOfTemplateBody }? {beginOfTemplateBody = false;} -> pushMode(STRUCTURE_NAME_MODE)
|
||||
;
|
||||
|
||||
INVALID_TOKEN
|
||||
: . { beginOfTemplateBody = false; }
|
||||
;
|
||||
|
||||
mode NORMAL_TEMPLATE_BODY_MODE;
|
||||
|
||||
WS_IN_BODY
|
||||
: WHITESPACE+ {ignoreWS}? -> skip
|
||||
;
|
||||
|
||||
MULTILINE_PREFIX
|
||||
: '```' { !inMultiline && beginOfTemplateLine }? { inMultiline = true; beginOfTemplateLine = false;}-> pushMode(MULTILINE_MODE)
|
||||
;
|
||||
|
||||
NEWLINE_IN_BODY
|
||||
: '\r'? '\n' { ignoreWS = true;} -> skip, popMode
|
||||
;
|
||||
|
||||
IF
|
||||
: I F WHITESPACE* ':' {beginOfTemplateLine}? { ignoreWS = true; beginOfTemplateLine = false;}
|
||||
;
|
||||
|
||||
ELSEIF
|
||||
: E L S E WHITESPACE* I F WHITESPACE* ':' {beginOfTemplateLine}? { ignoreWS = true; beginOfTemplateLine = false;}
|
||||
;
|
||||
|
||||
ELSE
|
||||
: E L S E WHITESPACE* ':' {beginOfTemplateLine}? { ignoreWS = true; beginOfTemplateLine = false;}
|
||||
;
|
||||
|
||||
SWITCH
|
||||
: S W I T C H WHITESPACE* ':' {beginOfTemplateLine}? { ignoreWS = true; beginOfTemplateLine = false;}
|
||||
;
|
||||
|
||||
CASE
|
||||
: C A S E WHITESPACE* ':' {beginOfTemplateLine}? { ignoreWS = true; beginOfTemplateLine = false;}
|
||||
;
|
||||
|
||||
DEFAULT
|
||||
: D E F A U L T WHITESPACE* ':' {beginOfTemplateLine}? { ignoreWS = true; beginOfTemplateLine = false;}
|
||||
;
|
||||
|
||||
ESCAPE_CHARACTER
|
||||
: ESCAPE_CHARACTER_FRAGMENT { ignoreWS = false; beginOfTemplateLine = false;}
|
||||
;
|
||||
|
||||
EXPRESSION
|
||||
: EXPRESSION_FRAGMENT { ignoreWS = false; beginOfTemplateLine = false;}
|
||||
;
|
||||
|
||||
TEXT
|
||||
: ~[\r\n]+? { ignoreWS = false; beginOfTemplateLine = false;}
|
||||
;
|
||||
|
||||
mode MULTILINE_MODE;
|
||||
|
||||
MULTILINE_SUFFIX
|
||||
: '```' { inMultiline = false; } -> popMode
|
||||
;
|
||||
|
||||
MULTILINE_ESCAPE_CHARACTER
|
||||
: ESCAPE_CHARACTER_FRAGMENT -> type(ESCAPE_CHARACTER)
|
||||
;
|
||||
|
||||
MULTILINE_EXPRESSION
|
||||
: EXPRESSION_FRAGMENT -> type(EXPRESSION)
|
||||
;
|
||||
|
||||
MULTILINE_TEXT
|
||||
: (('\r'? '\n') | ~[\r\n])+? -> type(TEXT)
|
||||
;
|
||||
|
||||
mode STRUCTURE_NAME_MODE;
|
||||
|
||||
WS_IN_STRUCTURE_NAME
|
||||
: WHITESPACE+ -> skip
|
||||
;
|
||||
|
||||
NEWLINE_IN_STRUCTURE_NAME
|
||||
: '\r'? '\n' { ignoreWS = true;} {beginOfStructureProperty = true;}-> skip, pushMode(STRUCTURE_BODY_MODE)
|
||||
;
|
||||
|
||||
STRUCTURE_NAME
|
||||
: (LETTER | NUMBER | '_') (LETTER | NUMBER | '-' | '_' | '.')*
|
||||
;
|
||||
|
||||
TEXT_IN_STRUCTURE_NAME
|
||||
: ~[\r\n]+?
|
||||
;
|
||||
|
||||
mode STRUCTURE_BODY_MODE;
|
||||
|
||||
STRUCTURED_COMMENTS
|
||||
: '>' ~[\r\n]* '\r'?'\n' { !inStructuredValue && beginOfStructureProperty}? -> skip
|
||||
;
|
||||
|
||||
WS_IN_STRUCTURE_BODY
|
||||
: WHITESPACE+ {ignoreWS}? -> skip
|
||||
;
|
||||
|
||||
STRUCTURED_NEWLINE
|
||||
: '\r'? '\n' { ignoreWS = true; inStructuredValue = false; beginOfStructureProperty = true;}
|
||||
;
|
||||
|
||||
STRUCTURED_BODY_END
|
||||
: ']' {!inStructuredValue}? -> popMode, popMode
|
||||
;
|
||||
|
||||
STRUCTURE_IDENTIFIER
|
||||
: (LETTER | NUMBER | '_') (LETTER | NUMBER | '-' | '_' | '.')* { !inStructuredValue && beginOfStructureProperty}? {beginOfStructureProperty = false;}
|
||||
;
|
||||
|
||||
STRUCTURE_EQUALS
|
||||
: '=' {!inStructuredValue}? {inStructuredValue = true;}
|
||||
;
|
||||
|
||||
STRUCTURE_OR_MARK
|
||||
: '|' { ignoreWS = true; }
|
||||
;
|
||||
|
||||
ESCAPE_CHARACTER_IN_STRUCTURE_BODY
|
||||
: ESCAPE_CHARACTER_FRAGMENT { ignoreWS = false; }
|
||||
;
|
||||
|
||||
EXPRESSION_IN_STRUCTURE_BODY
|
||||
: EXPRESSION_FRAGMENT { ignoreWS = false; }
|
||||
;
|
||||
|
||||
TEXT_IN_STRUCTURE_BODY
|
||||
: ~[\r\n]+? { ignoreWS = false; beginOfStructureProperty = false;}
|
||||
;
|
||||
|
|
@ -0,0 +1,93 @@
|
|||
parser grammar LGTemplateParser;
|
||||
|
||||
@parser::header {#pragma warning disable 3021} // Disable StyleCop warning CS3021 re CLSCompliant attribute in generated files.
|
||||
@lexer::header {#pragma warning disable 3021} // Disable StyleCop warning CS3021 re CLSCompliant attribute in generated files.
|
||||
|
||||
options { tokenVocab=LGTemplateLexer; }
|
||||
|
||||
context: body EOF;
|
||||
|
||||
body
|
||||
: normalTemplateBody #normalBody
|
||||
| ifElseTemplateBody #ifElseBody
|
||||
| switchCaseTemplateBody #switchCaseBody
|
||||
| structuredTemplateBody #structuredBody
|
||||
;
|
||||
|
||||
structuredTemplateBody
|
||||
: structuredBodyNameLine (((structuredBodyContentLine? STRUCTURED_NEWLINE) | errorStructureLine)+)? structuredBodyEndLine?
|
||||
;
|
||||
|
||||
structuredBodyNameLine
|
||||
: LEFT_SQUARE_BRACKET (STRUCTURE_NAME | errorStructuredName)
|
||||
;
|
||||
|
||||
errorStructuredName
|
||||
: (STRUCTURE_NAME|TEXT_IN_STRUCTURE_NAME)*
|
||||
;
|
||||
|
||||
structuredBodyContentLine
|
||||
: keyValueStructureLine
|
||||
| objectStructureLine
|
||||
;
|
||||
|
||||
errorStructureLine
|
||||
: (STRUCTURE_IDENTIFIER|STRUCTURE_EQUALS|STRUCTURE_OR_MARK|TEXT_IN_STRUCTURE_BODY|EXPRESSION_IN_STRUCTURE_BODY|ESCAPE_CHARACTER_IN_STRUCTURE_BODY)+
|
||||
;
|
||||
|
||||
keyValueStructureLine
|
||||
: STRUCTURE_IDENTIFIER STRUCTURE_EQUALS keyValueStructureValue (STRUCTURE_OR_MARK keyValueStructureValue)*
|
||||
;
|
||||
|
||||
keyValueStructureValue
|
||||
: (TEXT_IN_STRUCTURE_BODY|EXPRESSION_IN_STRUCTURE_BODY|ESCAPE_CHARACTER_IN_STRUCTURE_BODY)+
|
||||
;
|
||||
|
||||
objectStructureLine
|
||||
: EXPRESSION_IN_STRUCTURE_BODY
|
||||
;
|
||||
|
||||
structuredBodyEndLine
|
||||
: STRUCTURED_BODY_END
|
||||
;
|
||||
|
||||
normalTemplateBody
|
||||
: templateString+
|
||||
;
|
||||
|
||||
templateString
|
||||
: normalTemplateString
|
||||
| errorTemplateString
|
||||
;
|
||||
|
||||
normalTemplateString
|
||||
: DASH MULTILINE_PREFIX? (TEXT|EXPRESSION|ESCAPE_CHARACTER)* MULTILINE_SUFFIX?
|
||||
;
|
||||
|
||||
errorTemplateString
|
||||
: INVALID_TOKEN+
|
||||
;
|
||||
|
||||
ifElseTemplateBody
|
||||
: ifConditionRule+
|
||||
;
|
||||
|
||||
ifConditionRule
|
||||
: ifCondition normalTemplateBody?
|
||||
;
|
||||
|
||||
ifCondition
|
||||
: DASH (IF|ELSE|ELSEIF) (WS|TEXT|EXPRESSION)*
|
||||
;
|
||||
|
||||
switchCaseTemplateBody
|
||||
: switchCaseRule+
|
||||
;
|
||||
|
||||
switchCaseRule
|
||||
: switchCaseStat normalTemplateBody?
|
||||
;
|
||||
|
||||
switchCaseStat
|
||||
: DASH (SWITCH|CASE|DEFAULT) (WS|TEXT|EXPRESSION)*
|
||||
;
|
|
@ -8,12 +8,20 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
|
|||
/// </summary>
|
||||
public class Range
|
||||
{
|
||||
public static readonly Range DefaultRange = new Range(1, 0, 1, 0);
|
||||
|
||||
public Range(Position start, Position end)
|
||||
{
|
||||
Start = start;
|
||||
End = end;
|
||||
}
|
||||
|
||||
public Range(int startLine, int startChar, int endLine, int endChar)
|
||||
{
|
||||
Start = new Position(startLine, startChar);
|
||||
End = new Position(endLine, endChar);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the start position. It is before or equal to <see cref="End"/>.
|
||||
/// </summary>
|
||||
|
@ -35,8 +43,7 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
|
|||
var result = Start.ToString();
|
||||
if (Start.Line <= End.Line && Start.Character < End.Character)
|
||||
{
|
||||
result += " - ";
|
||||
result += End.ToString();
|
||||
result += $" - {End}";
|
||||
}
|
||||
|
||||
return result;
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using Antlr4.Runtime;
|
||||
|
||||
namespace Microsoft.Bot.Builder.LanguageGeneration
|
||||
{
|
||||
/// <summary>
|
||||
/// Source range of the context. Including parse tree, source id and the context range.
|
||||
/// </summary>
|
||||
public class SourceRange
|
||||
{
|
||||
public SourceRange(ParserRuleContext parseTree, string source = "", int offset = 0)
|
||||
{
|
||||
this.Source = source ?? string.Empty;
|
||||
this.ParseTree = parseTree;
|
||||
this.Range = parseTree.ConvertToRange(offset);
|
||||
}
|
||||
|
||||
public SourceRange(Range range, string source = "")
|
||||
{
|
||||
this.Range = range;
|
||||
this.Source = source ?? string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets range of the block.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// range of the block.
|
||||
/// </value>
|
||||
public Range Range { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets code source, used as the lg file path.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// Code source, used as the lg file path.
|
||||
/// </value>
|
||||
public string Source { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets content parse tree form LGFileParser.g4.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// Content parse tree form LGFileParser.g4.
|
||||
/// </value>
|
||||
public ParserRuleContext ParseTree { get; set; }
|
||||
}
|
||||
}
|
|
@ -13,22 +13,22 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
|
|||
/// <summary>
|
||||
/// LG managed code checker.
|
||||
/// </summary>
|
||||
internal class StaticChecker : LGFileParserBaseVisitor<List<Diagnostic>>
|
||||
internal class StaticChecker : LGTemplateParserBaseVisitor<List<Diagnostic>>
|
||||
{
|
||||
private readonly ExpressionParser baseExpressionParser;
|
||||
private readonly Templates templates;
|
||||
private IList<string> visitedTemplateNames;
|
||||
private Template currentTemplate;
|
||||
|
||||
private IExpressionParser _expressionParser;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="StaticChecker"/> class.
|
||||
/// </summary>
|
||||
/// <param name="lg">the lg wihch would be checked.</param>
|
||||
public StaticChecker(Templates lg)
|
||||
/// <param name="templates">The templates wihch would be checked.</param>
|
||||
public StaticChecker(Templates templates)
|
||||
{
|
||||
this.templates = lg;
|
||||
baseExpressionParser = lg.ExpressionParser;
|
||||
this.templates = templates;
|
||||
baseExpressionParser = templates.ExpressionParser;
|
||||
}
|
||||
|
||||
// Create a property because we want this to be lazy loaded
|
||||
|
@ -50,81 +50,48 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
|
|||
/// <summary>
|
||||
/// Return error messages list.
|
||||
/// </summary>
|
||||
/// <returns>report result.</returns>
|
||||
/// <returns>Report result.</returns>
|
||||
public List<Diagnostic> Check()
|
||||
{
|
||||
visitedTemplateNames = new List<string>();
|
||||
var result = new List<Diagnostic>();
|
||||
|
||||
if (templates.AllTemplates.Count == 0)
|
||||
{
|
||||
result.Add(BuildLGDiagnostic(
|
||||
TemplateErrors.NoTemplate,
|
||||
DiagnosticSeverity.Warning,
|
||||
includeTemplateNameInfo: false));
|
||||
|
||||
var diagnostic = new Diagnostic(Range.DefaultRange, TemplateErrors.NoTemplate, DiagnosticSeverity.Warning, templates.Id);
|
||||
result.Add(diagnostic);
|
||||
return result;
|
||||
}
|
||||
|
||||
templates.ToList().ForEach(t =>
|
||||
foreach (var template in templates)
|
||||
{
|
||||
result.AddRange(Visit(t.ParseTree));
|
||||
});
|
||||
currentTemplate = template;
|
||||
var templateDiagnostics = new List<Diagnostic>();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public override List<Diagnostic> VisitTemplateDefinition([NotNull] LGFileParser.TemplateDefinitionContext context)
|
||||
{
|
||||
var result = new List<Diagnostic>();
|
||||
var templateNameLine = context.templateNameLine();
|
||||
var errorTemplateName = templateNameLine.errorTemplateName();
|
||||
if (errorTemplateName != null)
|
||||
{
|
||||
result.Add(BuildLGDiagnostic(TemplateErrors.InvalidTemplateName, context: errorTemplateName, includeTemplateNameInfo: false));
|
||||
}
|
||||
else
|
||||
{
|
||||
var templateName = context.templateNameLine().templateName().GetText();
|
||||
|
||||
if (visitedTemplateNames.Contains(templateName))
|
||||
// checker duplicated in different files
|
||||
foreach (var reference in templates.References)
|
||||
{
|
||||
result.Add(BuildLGDiagnostic(TemplateErrors.DuplicatedTemplateInSameTemplate(templateName), context: templateNameLine));
|
||||
}
|
||||
else
|
||||
{
|
||||
visitedTemplateNames.Add(templateName);
|
||||
foreach (var reference in templates.References)
|
||||
var sameTemplates = reference.Where(u => u.Name == template.Name);
|
||||
foreach (var sameTemplate in sameTemplates)
|
||||
{
|
||||
var sameTemplates = reference.Where(u => u.Name == templateName);
|
||||
foreach (var sameTemplate in sameTemplates)
|
||||
{
|
||||
result.Add(BuildLGDiagnostic(TemplateErrors.DuplicatedTemplateInDiffTemplate(sameTemplate.Name, sameTemplate.Source), context: templateNameLine));
|
||||
}
|
||||
}
|
||||
|
||||
if (result.Count > 0)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (context.templateBody() == null)
|
||||
{
|
||||
result.Add(BuildLGDiagnostic(TemplateErrors.NoTemplateBody(templateName), DiagnosticSeverity.Warning, context.templateNameLine()));
|
||||
}
|
||||
else
|
||||
{
|
||||
result.AddRange(Visit(context.templateBody()));
|
||||
}
|
||||
var startLine = template.SourceRange.Range.Start.Line;
|
||||
var range = new Range(startLine, 0, startLine, template.Name.Length + 1);
|
||||
var diagnostic = new Diagnostic(range, TemplateErrors.DuplicatedTemplateInDiffTemplate(sameTemplate.Name, sameTemplate.SourceRange.Source), source: templates.Id);
|
||||
templateDiagnostics.Add(diagnostic);
|
||||
}
|
||||
}
|
||||
|
||||
if (templateDiagnostics.Count == 0 && template.TemplateBodyParseTree != null)
|
||||
{
|
||||
templateDiagnostics.AddRange(Visit(template.TemplateBodyParseTree));
|
||||
}
|
||||
|
||||
result.AddRange(templateDiagnostics);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public override List<Diagnostic> VisitNormalTemplateBody([NotNull] LGFileParser.NormalTemplateBodyContext context)
|
||||
public override List<Diagnostic> VisitNormalTemplateBody([NotNull] LGTemplateParser.NormalTemplateBodyContext context)
|
||||
{
|
||||
var result = new List<Diagnostic>();
|
||||
|
||||
|
@ -144,7 +111,7 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
|
|||
return result;
|
||||
}
|
||||
|
||||
public override List<Diagnostic> VisitStructuredTemplateBody([NotNull] LGFileParser.StructuredTemplateBodyContext context)
|
||||
public override List<Diagnostic> VisitStructuredTemplateBody([NotNull] LGTemplateParser.StructuredTemplateBodyContext context)
|
||||
{
|
||||
var result = new List<Diagnostic>();
|
||||
|
||||
|
@ -202,7 +169,7 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
|
|||
return result;
|
||||
}
|
||||
|
||||
public override List<Diagnostic> VisitIfElseBody([NotNull] LGFileParser.IfElseBodyContext context)
|
||||
public override List<Diagnostic> VisitIfElseBody([NotNull] LGTemplateParser.IfElseBodyContext context)
|
||||
{
|
||||
var result = new List<Diagnostic>();
|
||||
|
||||
|
@ -277,7 +244,7 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
|
|||
return result;
|
||||
}
|
||||
|
||||
public override List<Diagnostic> VisitSwitchCaseBody([NotNull] LGFileParser.SwitchCaseBodyContext context)
|
||||
public override List<Diagnostic> VisitSwitchCaseBody([NotNull] LGTemplateParser.SwitchCaseBodyContext context)
|
||||
{
|
||||
var result = new List<Diagnostic>();
|
||||
var switchCaseRules = context.switchCaseTemplateBody().switchCaseRule();
|
||||
|
@ -364,7 +331,7 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
|
|||
return result;
|
||||
}
|
||||
|
||||
public override List<Diagnostic> VisitNormalTemplateString([NotNull] LGFileParser.NormalTemplateStringContext context)
|
||||
public override List<Diagnostic> VisitNormalTemplateString([NotNull] LGTemplateParser.NormalTemplateStringContext context)
|
||||
{
|
||||
var prefixErrorMsg = context.GetPrefixErrorMessage();
|
||||
var result = new List<Diagnostic>();
|
||||
|
@ -413,23 +380,14 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
|
|||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Build LG diagnostic with ANTLR tree node context.
|
||||
/// </summary>
|
||||
/// <param name="message">error/warning message. <see cref="Diagnostic.Message"/>.</param>
|
||||
/// <param name="severity">diagnostic Severity <see cref="DiagnosticSeverity"/> to get more info.</param>
|
||||
/// <param name="context">the parsed tree node context of the diagnostic.</param>
|
||||
/// <returns>new Diagnostic object.</returns>
|
||||
private Diagnostic BuildLGDiagnostic(
|
||||
string message,
|
||||
DiagnosticSeverity severity = DiagnosticSeverity.Error,
|
||||
ParserRuleContext context = null,
|
||||
bool includeTemplateNameInfo = true)
|
||||
ParserRuleContext context = null)
|
||||
{
|
||||
message = visitedTemplateNames.Count > 0 && includeTemplateNameInfo ? $"[{visitedTemplateNames.LastOrDefault()}]" + message : message;
|
||||
var startPosition = context == null ? new Position(0, 0) : new Position(context.Start.Line, context.Start.Column);
|
||||
var stopPosition = context == null ? new Position(0, 0) : new Position(context.Stop.Line, context.Stop.Column + context.Stop.Text.Length);
|
||||
var range = new Range(startPosition, stopPosition);
|
||||
var lineOffset = this.currentTemplate != null ? this.currentTemplate.SourceRange.Range.Start.Line : 0;
|
||||
message = this.currentTemplate != null ? $"[{this.currentTemplate.Name}]" + message : message;
|
||||
var range = context == null ? new Range(1 + lineOffset, 0, 1 + lineOffset, 0) : context.ConvertToRange(lineOffset);
|
||||
return new Diagnostic(range, message, severity, templates.Id);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,7 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace Microsoft.Bot.Builder.LanguageGeneration
|
||||
{
|
||||
|
@ -20,34 +17,45 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
|
|||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Template"/> class.
|
||||
/// </summary>
|
||||
/// <param name="parseTree">The parse tree of this template.</param>
|
||||
/// <param name="lgfileContent">lg file content.</param>
|
||||
/// <param name="source">Source of this template.</param>
|
||||
internal Template(LGFileParser.TemplateDefinitionContext parseTree, string lgfileContent, string source = "")
|
||||
/// <param name="templateName">Template name without parameters.</param>
|
||||
/// <param name="parameters">Parameter list.</param>
|
||||
/// <param name="templateBody">Template content.</param>
|
||||
/// <param name="sourceRange">Source range of template.</param>
|
||||
internal Template(
|
||||
string templateName,
|
||||
List<string> parameters,
|
||||
string templateBody,
|
||||
SourceRange sourceRange)
|
||||
{
|
||||
ParseTree = parseTree;
|
||||
Source = source;
|
||||
|
||||
Name = ExtractName();
|
||||
Parameters = ExtractParameters();
|
||||
Body = ExtractBody(lgfileContent);
|
||||
this.Name = templateName ?? string.Empty;
|
||||
this.Parameters = parameters ?? new List<string>();
|
||||
this.Body = templateBody ?? string.Empty;
|
||||
this.SourceRange = sourceRange;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets name of the template, what's followed by '#' in a LG file.
|
||||
/// Gets or sets source range.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// Name of the template, what's followed by '#' in a LG file.
|
||||
/// Start line of the template in LG file.
|
||||
/// </value>
|
||||
public string Name { get; }
|
||||
public SourceRange SourceRange { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets parameter list of this template.
|
||||
/// Gets or sets name of the template, which follows '#' in a LG file.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// Name of the template, which follows '#' in a LG file.
|
||||
/// </value>
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets parameter list of this template.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// Parameter list of this template.
|
||||
/// </value>
|
||||
public List<string> Parameters { get; }
|
||||
public List<string> Parameters { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets text format of Body of this template. All content except Name and Parameters.
|
||||
|
@ -58,102 +66,13 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
|
|||
public string Body { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets source of this template, source file path if it's from a certain file.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// Source of this template, source file path if it's from a certain file.
|
||||
/// </value>
|
||||
public string Source { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the parse tree of this template.
|
||||
/// Gets or sets the parse tree of this template.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The parse tree of this template.
|
||||
/// </value>
|
||||
public LGFileParser.TemplateDefinitionContext ParseTree { get; }
|
||||
public LGTemplateParser.BodyContext TemplateBodyParseTree { get; set; }
|
||||
|
||||
public override string ToString() => $"[{Name}({string.Join(", ", Parameters)})]\"{Body}\"";
|
||||
|
||||
/// <summary>
|
||||
/// Get the startLine and stopLine of template.
|
||||
/// </summary>
|
||||
/// <returns>template content range.</returns>
|
||||
public (int startLine, int stopLine) GetTemplateRange()
|
||||
{
|
||||
var startLine = ParseTree.Start.Line - 1;
|
||||
var stopLine = ParseTree.Stop.Line - 1;
|
||||
if (ParseTree?.Parent?.Parent is LGFileParser.FileContext fileContext)
|
||||
{
|
||||
var templateDefinitions = fileContext
|
||||
.paragraph()
|
||||
.Select(u => u.templateDefinition())
|
||||
.Where(u => u != null)
|
||||
.ToList();
|
||||
var currentIndex = -1;
|
||||
for (var i = 0; i < templateDefinitions.Count; i++)
|
||||
{
|
||||
if (templateDefinitions[i] == ParseTree)
|
||||
{
|
||||
currentIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (currentIndex >= 0 && currentIndex < templateDefinitions.Count - 1)
|
||||
{
|
||||
// in the middle of templates
|
||||
stopLine = templateDefinitions[currentIndex + 1].Start.Line - 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
// last item
|
||||
stopLine = fileContext.Stop.Line - 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (stopLine <= startLine)
|
||||
{
|
||||
stopLine = startLine;
|
||||
}
|
||||
|
||||
return (startLine, stopLine);
|
||||
}
|
||||
|
||||
private string ExtractBody(string lgfileContent)
|
||||
{
|
||||
var (startLine, stopLine) = GetTemplateRange();
|
||||
return startLine >= stopLine ? string.Empty : GetRangeContent(lgfileContent, startLine + 1, stopLine);
|
||||
}
|
||||
|
||||
private string ExtractName()
|
||||
{
|
||||
var name = ParseTree.templateNameLine().templateName()?.GetText();
|
||||
return name ?? string.Empty;
|
||||
}
|
||||
|
||||
private List<string> ExtractParameters()
|
||||
{
|
||||
var parameters = ParseTree.templateNameLine().parameters();
|
||||
if (parameters != null)
|
||||
{
|
||||
return parameters.IDENTIFIER().Select(param => param.GetText()).ToList();
|
||||
}
|
||||
|
||||
return new List<string>();
|
||||
}
|
||||
|
||||
private string GetRangeContent(string originString, int startLine, int stopLine)
|
||||
{
|
||||
var originList = originString.Split(new[] { "\r\n", "\n", "\r" }, StringSplitOptions.None);
|
||||
if (startLine < 0 || startLine > stopLine || stopLine >= originList.Length)
|
||||
{
|
||||
throw new Exception("index out of range.");
|
||||
}
|
||||
|
||||
var destList = originList.Skip(startLine).Take(stopLine - startLine + 1);
|
||||
|
||||
return string.Join("\r\n", destList);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -62,7 +62,7 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
|
|||
|
||||
public const string LoopDetected = "Loop detected:";
|
||||
|
||||
public const string SyntaxError = "Unexpected content. Expecting either a comment or a template definition or an import statement.";
|
||||
public const string SyntaxError = "Unexpected content. Expecting a comment, template definition, import statement or option definition.";
|
||||
|
||||
public const string InvalidMemory = "Scope is not a LG customized memory.";
|
||||
|
||||
|
@ -82,8 +82,6 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
|
|||
|
||||
public static string ArgumentMismatch(string templateName, int expectedCount, int actualCount) => $"arguments mismatch for template '{templateName}'. Expecting '{expectedCount}' arguments, actual '{actualCount}'.";
|
||||
|
||||
public static string ErrorTemplateNameformat(string templateName) => $"'{templateName}' cannot be used as a template name. Template names must be avalid string.";
|
||||
|
||||
public static string TemplateExist(string templateName) => $"template '{templateName}' already exists.";
|
||||
|
||||
public static string ExpressionParseError(string exp) => $"Error occurred when parsing expression '{exp}'.";
|
||||
|
|
|
@ -13,63 +13,43 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
|
|||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TemplateImport"/> class.
|
||||
/// </summary>
|
||||
/// <param name="parseTree">The parse tree of this template.</param>
|
||||
/// <param name="source">Source of this import.</param>
|
||||
internal TemplateImport(LGFileParser.ImportDefinitionContext parseTree, string source = "")
|
||||
/// <param name="description">Import description, which is in [].</param>
|
||||
/// <param name="id">Import id, which is a path, in ().</param>
|
||||
/// <param name="sourceRange">Source range of template.</param>
|
||||
internal TemplateImport(string description, string id, SourceRange sourceRange)
|
||||
{
|
||||
ParseTree = parseTree;
|
||||
Source = source;
|
||||
|
||||
Description = ExtractDescription(parseTree);
|
||||
Id = ExtractId(parseTree);
|
||||
this.SourceRange = sourceRange;
|
||||
this.Description = description;
|
||||
this.Id = id;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets description of the import, what's included by '[]' in a lg file.
|
||||
/// Gets or sets description of the import, included by '[]' in a lg file.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// Description of the import, what's included by '[]' in a lg file.
|
||||
/// Description of the import, included by '[]' in a lg file.
|
||||
/// </value>
|
||||
public string Description { get; }
|
||||
public string Description { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets id of this import, what's included by '()' in a lg file.
|
||||
/// Gets or sets id of this import, included by '()' in a lg file.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// Id of this import.
|
||||
/// </value>
|
||||
public string Id { get; }
|
||||
public string Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets origin root source of the import.
|
||||
/// Gets or sets original root source of the import.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// origin root source of the import.
|
||||
/// Original root source of the import.
|
||||
/// </value>
|
||||
public string Source { get; }
|
||||
public SourceRange SourceRange { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the parse tree of this lg file.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The parse tree of this lg file.
|
||||
/// </value>
|
||||
public LGFileParser.ImportDefinitionContext ParseTree { get; }
|
||||
|
||||
private string ExtractDescription(LGFileParser.ImportDefinitionContext parseTree)
|
||||
public override string ToString()
|
||||
{
|
||||
// content: [xxx](yyy)
|
||||
var content = parseTree.GetText();
|
||||
var closeSquareBracketIndex = content.IndexOf(']');
|
||||
return content.Substring(1, closeSquareBracketIndex - 1);
|
||||
}
|
||||
|
||||
private string ExtractId(LGFileParser.ImportDefinitionContext parseTree)
|
||||
{
|
||||
// content: [xxx](yyy)
|
||||
var content = parseTree.GetText();
|
||||
var lastOpenBracketIndex = content.LastIndexOf('(');
|
||||
return content.Substring(lastOpenBracketIndex + 1, content.Length - lastOpenBracketIndex - 2);
|
||||
return $"[{Description}]({Id})";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -77,7 +77,7 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
|
|||
/// Gets or sets expression parser.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// expression parser.
|
||||
/// Expression parser.
|
||||
/// </value>
|
||||
public ExpressionParser ExpressionParser { get; set; }
|
||||
|
||||
|
@ -85,7 +85,7 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
|
|||
/// Gets or sets import elements that this LG file contains directly.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// import elements that this LG file contains directly.
|
||||
/// Import elements that this LG file contains directly.
|
||||
/// </value>
|
||||
public IList<TemplateImport> Imports { get; set; }
|
||||
|
||||
|
@ -96,7 +96,7 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
|
|||
/// so, reference count may >= imports count.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// all references that this LG file has from <see cref="Imports"/>.
|
||||
/// All references that this LG file has from <see cref="Imports"/>.
|
||||
/// </value>
|
||||
public IList<Templates> References { get; set; }
|
||||
|
||||
|
@ -104,7 +104,7 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
|
|||
/// Gets or sets diagnostics.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// diagnostics.
|
||||
/// Diagnostics.
|
||||
/// </value>
|
||||
public IList<Diagnostic> Diagnostics { get; set; }
|
||||
|
||||
|
@ -120,7 +120,7 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
|
|||
/// Gets or sets id of this LG file.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// id of this lg source. For file, is full path.
|
||||
/// Id of this lg source. For file, is full path.
|
||||
/// </value>
|
||||
public string Id { get; set; }
|
||||
|
||||
|
@ -147,7 +147,7 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
|
|||
/// <summary>
|
||||
/// Parser to turn lg content into a <see cref="LanguageGeneration.Templates"/>.
|
||||
/// </summary>
|
||||
/// <param name="filePath"> absolut path of a LG file.</param>
|
||||
/// <param name="filePath">Absolute path of a LG file.</param>
|
||||
/// <param name="importResolver">resolver to resolve LG import id to template text.</param>
|
||||
/// <param name="expressionParser">expressionEngine Expression engine for evaluating expressions.</param>
|
||||
/// <returns>new <see cref="LanguageGeneration.Templates"/> entity.</returns>
|
||||
|
@ -160,10 +160,10 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
|
|||
/// Parser to turn lg content into a <see cref="LanguageGeneration.Templates"/>.
|
||||
/// </summary>
|
||||
/// <param name="content">Text content contains lg templates.</param>
|
||||
/// <param name="id">id is the identifier of content. If importResolver is null, id must be a full path string. </param>
|
||||
/// <param name="importResolver">resolver to resolve LG import id to template text.</param>
|
||||
/// <param name="expressionParser">expressionEngine parser engine for parsing expressions.</param>
|
||||
/// <returns>new <see cref="LanguageGeneration.Templates"/> entity.</returns>
|
||||
/// <param name="id">Id is the identifier of content. If importResolver is null, id must be a full path string. </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 ParseText(
|
||||
string content,
|
||||
string id = "",
|
||||
|
@ -187,8 +187,8 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
|
|||
/// <summary>
|
||||
/// Use to evaluate an inline template str.
|
||||
/// </summary>
|
||||
/// <param name="text">inline string which will be evaluated.</param>
|
||||
/// <param name="scope">scope object or JToken.</param>
|
||||
/// <param name="text">Inline string which will be evaluated.</param>
|
||||
/// <param name="scope">Scope object or JToken.</param>
|
||||
/// <returns>Evaluate result.</returns>
|
||||
public object EvaluateText(string text, object scope = null)
|
||||
{
|
||||
|
@ -229,10 +229,10 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
|
|||
|
||||
/// <summary>
|
||||
/// (experimental)
|
||||
/// Analyzer a template to get the static analyzer results including variables and template references.
|
||||
/// Analyze a template to get the static analyzer results including variables and template references.
|
||||
/// </summary>
|
||||
/// <param name="templateName">Template name to be evaluated.</param>
|
||||
/// <returns>analyzer result.</returns>
|
||||
/// <returns>Analyzer result.</returns>
|
||||
public AnalyzerResult AnalyzeTemplate(string templateName)
|
||||
{
|
||||
CheckErrors();
|
||||
|
@ -241,13 +241,13 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// update an exist template.
|
||||
/// Update an existing template.
|
||||
/// </summary>
|
||||
/// <param name="templateName">origin 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>
|
||||
/// <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);
|
||||
|
@ -256,7 +256,9 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
|
|||
var templateNameLine = BuildTemplateNameLine(newTemplateName, parameters);
|
||||
var newTemplateBody = ConvertTemplateBody(templateBody);
|
||||
var content = $"{templateNameLine}{newLine}{newTemplateBody}";
|
||||
var (startLine, stopLine) = template.GetTemplateRange();
|
||||
|
||||
var startLine = template.SourceRange.Range.Start.Line - 1;
|
||||
var stopLine = template.SourceRange.Range.End.Line - 1;
|
||||
|
||||
var newContent = ReplaceRangeContent(Content, startLine, stopLine, content);
|
||||
Initialize(ParseText(newContent, Id, ImportResolver));
|
||||
|
@ -268,10 +270,10 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
|
|||
/// <summary>
|
||||
/// Add a new template and return LG File.
|
||||
/// </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>
|
||||
/// <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);
|
||||
|
@ -291,15 +293,15 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
|
|||
/// <summary>
|
||||
/// Delete an exist template.
|
||||
/// </summary>
|
||||
/// <param name="templateName">which template should delete.</param>
|
||||
/// <returns>updated LG file.</returns>
|
||||
/// <param name="templateName">Which template should delete.</param>
|
||||
/// <returns>Updated LG file.</returns>
|
||||
public Templates DeleteTemplate(string templateName)
|
||||
{
|
||||
var template = this.FirstOrDefault(u => u.Name == templateName);
|
||||
if (template != null)
|
||||
{
|
||||
var (startLine, stopLine) = template.GetTemplateRange();
|
||||
|
||||
var startLine = template.SourceRange.Range.Start.Line - 1;
|
||||
var stopLine = template.SourceRange.Range.End.Line - 1;
|
||||
var newContent = ReplaceRangeContent(Content, startLine, stopLine, null);
|
||||
Initialize(ParseText(newContent, Id, ImportResolver));
|
||||
}
|
||||
|
@ -363,7 +365,7 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// use an existing LG file to override current object.
|
||||
/// Use an existing LG file to override current object.
|
||||
/// </summary>
|
||||
/// <param name="templates">Existing LG file.</param>
|
||||
private void Initialize(Templates templates)
|
||||
|
|
|
@ -8,6 +8,8 @@ using System.Linq;
|
|||
using System.Text.RegularExpressions;
|
||||
using AdaptiveExpressions;
|
||||
using Antlr4.Runtime;
|
||||
using Antlr4.Runtime.Misc;
|
||||
using Antlr4.Runtime.Tree;
|
||||
|
||||
namespace Microsoft.Bot.Builder.LanguageGeneration
|
||||
{
|
||||
|
@ -27,14 +29,19 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
|
|||
/// <summary>
|
||||
/// option regex.
|
||||
/// </summary>
|
||||
private static readonly Regex OptionRegex = new Regex(@"^> *!#(.*)$");
|
||||
public static readonly Regex OptionRegex = new Regex(@">\s*!#(.*)");
|
||||
|
||||
/// <summary>
|
||||
/// Import regex.
|
||||
/// </summary>
|
||||
public static readonly Regex ImportRegex = new Regex(@"\[([^]]*)\]\(([^)]*)\)");
|
||||
|
||||
/// <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">expressionEngine Expression engine for evaluating expressions.</param>
|
||||
/// <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,
|
||||
|
@ -51,10 +58,10 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
|
|||
/// Parser to turn lg content into a <see cref="Templates"/>.
|
||||
/// </summary>
|
||||
/// <param name="content">Text content contains lg templates.</param>
|
||||
/// <param name="id">id is the identifier of content. If importResolver is null, id must be a full path string. </param>
|
||||
/// <param name="importResolver">resolver to resolve LG import id to template text.</param>
|
||||
/// <param name="expressionParser">expressionEngine parser engine for parsing expressions.</param>
|
||||
/// <returns>new <see cref="Templates"/> entity.</returns>
|
||||
/// <param name="id">Id is the identifier of content. If importResolver is null, id must be a full path string. </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 ParseText(
|
||||
string content,
|
||||
string id = "",
|
||||
|
@ -68,8 +75,8 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
|
|||
/// 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>
|
||||
/// <param name="lg">Original LGFile.</param>
|
||||
/// <returns>New <see cref="Templates"/> entity.</returns>
|
||||
public static Templates ParseTextWithRef(string content, Templates lg)
|
||||
{
|
||||
if (lg == null)
|
||||
|
@ -79,33 +86,20 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
|
|||
|
||||
var id = "inline content";
|
||||
var newLG = new Templates(content: content, id: id, importResolver: lg.ImportResolver, options: lg.Options);
|
||||
var diagnostics = new List<Diagnostic>();
|
||||
try
|
||||
{
|
||||
var (templates, imports, invalidTemplateErrors, options) = AntlrParse(content, id);
|
||||
newLG.AddRange(templates);
|
||||
newLG.Imports = imports;
|
||||
newLG.Options = options;
|
||||
diagnostics.AddRange(invalidTemplateErrors);
|
||||
|
||||
newLG = new TemplatesTransformer(newLG).Transform(AntlrParseTemplates(content, id));
|
||||
newLG.References = GetReferences(newLG)
|
||||
.Union(lg.References)
|
||||
.Union(new List<Templates> { lg })
|
||||
.ToList();
|
||||
|
||||
var semanticErrors = new StaticChecker(newLG).Check();
|
||||
diagnostics.AddRange(semanticErrors);
|
||||
new StaticChecker(newLG).Check().ForEach(u => newLG.Diagnostics.Add(u));
|
||||
}
|
||||
catch (TemplateException ex)
|
||||
{
|
||||
diagnostics.AddRange(ex.Diagnostics);
|
||||
ex.Diagnostics.ToList().ForEach(u => newLG.Diagnostics.Add(u));
|
||||
}
|
||||
catch (Exception err)
|
||||
{
|
||||
diagnostics.Add(BuildDiagnostic(err.Message, source: id));
|
||||
}
|
||||
|
||||
newLG.Diagnostics = diagnostics;
|
||||
|
||||
return newLG;
|
||||
}
|
||||
|
@ -114,10 +108,10 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
|
|||
/// Parser to turn lg content into a <see cref="Templates"/>.
|
||||
/// </summary>
|
||||
/// <param name="content">Text content contains lg templates.</param>
|
||||
/// <param name="id">id is the identifier of content. If importResolver is null, id must be a full path string. </param>
|
||||
/// <param name="importResolver">resolver to resolve LG import id to template text.</param>
|
||||
/// <param name="expressionParser">expressionEngine parser engine for parsing expressions.</param>
|
||||
/// <param name="cachedTemplates">give the file path and templates to avoid parsing and to improve performance.</param>
|
||||
/// <param name="id">Id is the identifier of content. If importResolver is null, id must be a full path string. </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>
|
||||
/// <returns>new <see cref="Templates"/> entity.</returns>
|
||||
private static Templates InnerParseText(
|
||||
string content,
|
||||
|
@ -135,29 +129,16 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
|
|||
importResolver = importResolver ?? DefaultFileResolver;
|
||||
var lg = new Templates(content: content, id: id, importResolver: importResolver, expressionParser: expressionParser);
|
||||
|
||||
var diagnostics = new List<Diagnostic>();
|
||||
try
|
||||
{
|
||||
var (templates, imports, invalidTemplateErrors, options) = AntlrParse(content, id);
|
||||
lg.AddRange(templates);
|
||||
lg.Imports = imports;
|
||||
lg.Options = options;
|
||||
diagnostics.AddRange(invalidTemplateErrors);
|
||||
|
||||
lg = new TemplatesTransformer(lg).Transform(AntlrParseTemplates(content, id));
|
||||
lg.References = GetReferences(lg, cachedTemplates);
|
||||
var semanticErrors = new StaticChecker(lg).Check();
|
||||
diagnostics.AddRange(semanticErrors);
|
||||
new StaticChecker(lg).Check().ForEach(u => lg.Diagnostics.Add(u));
|
||||
}
|
||||
catch (TemplateException ex)
|
||||
{
|
||||
diagnostics.AddRange(ex.Diagnostics);
|
||||
ex.Diagnostics.ToList().ForEach(u => lg.Diagnostics.Add(u));
|
||||
}
|
||||
catch (Exception err)
|
||||
{
|
||||
diagnostics.Add(BuildDiagnostic(err.Message, source: id));
|
||||
}
|
||||
|
||||
lg.Diagnostics = diagnostics;
|
||||
|
||||
return lg;
|
||||
}
|
||||
|
@ -165,9 +146,9 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
|
|||
/// <summary>
|
||||
/// Default import resolver, using relative/absolute file path to access the file content.
|
||||
/// </summary>
|
||||
/// <param name="sourceId">default is file path.</param>
|
||||
/// <param name="resourceId">import path.</param>
|
||||
/// <returns>target content id.</returns>
|
||||
/// <param name="sourceId">Default is file path.</param>
|
||||
/// <param name="resourceId">Import path.</param>
|
||||
/// <returns>Target content id.</returns>
|
||||
private static (string content, string id) DefaultFileResolver(string sourceId, string resourceId)
|
||||
{
|
||||
// import paths are in resource files which can be executed on multiple OS environments
|
||||
|
@ -183,41 +164,7 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
|
|||
return (File.ReadAllText(importPath), importPath);
|
||||
}
|
||||
|
||||
private static (IList<Template> templates, IList<TemplateImport> imports, IList<Diagnostic> diagnostics, IList<string> options) AntlrParse(string content, string id = "")
|
||||
{
|
||||
var fileContext = GetFileContentContext(content, id);
|
||||
var templates = ExtractLGTemplates(fileContext, content, id);
|
||||
var imports = ExtractLGImports(fileContext, id);
|
||||
var options = ExtractLGOptions(fileContext);
|
||||
var diagnostics = GetInvalidTemplateErrors(fileContext, id);
|
||||
|
||||
return (templates, imports, diagnostics, options);
|
||||
}
|
||||
|
||||
private static IList<Diagnostic> GetInvalidTemplateErrors(LGFileParser.FileContext fileContext, string id)
|
||||
{
|
||||
var errorTemplates = fileContext == null ? new List<LGFileParser.ErrorTemplateContext>() :
|
||||
fileContext.paragraph()
|
||||
.Select(x => x.errorTemplate())
|
||||
.Where(x => x != null);
|
||||
|
||||
return errorTemplates.Select(u => BuildDiagnostic("error context.", u, id)).ToList();
|
||||
}
|
||||
|
||||
private static Diagnostic BuildDiagnostic(string errorMessage, ParserRuleContext context = null, string source = null)
|
||||
{
|
||||
errorMessage = TemplateErrors.StaticFailure + "- " + errorMessage;
|
||||
var startPosition = context == null ? new Position(0, 0) : new Position(context.Start.Line, context.Start.Column);
|
||||
var stopPosition = context == null ? new Position(0, 0) : new Position(context.Stop.Line, context.Stop.Column + context.Stop.Text.Length);
|
||||
return new Diagnostic(new Range(startPosition, stopPosition), errorMessage, source: source);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get parsed tree nodes from text by antlr4 engine.
|
||||
/// </summary>
|
||||
/// <param name="text">Original text which will be parsed.</param>
|
||||
/// <returns>Parsed tree nodes.</returns>
|
||||
private static LGFileParser.FileContext GetFileContentContext(string text, string id)
|
||||
private static IParseTree AntlrParseTemplates(string text, string id)
|
||||
{
|
||||
if (string.IsNullOrEmpty(text))
|
||||
{
|
||||
|
@ -239,72 +186,6 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
|
|||
return parser.file();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extract LG templates from the parse tree of a file.
|
||||
/// </summary>
|
||||
/// <param name="file">LG file context from ANTLR parser.</param>
|
||||
/// <param name="lgfileContent">LG file content.</param>
|
||||
/// <param name="source">text source.</param>
|
||||
/// <returns>LG template list.</returns>
|
||||
private static IList<Template> ExtractLGTemplates(LGFileParser.FileContext file, string lgfileContent, string source = "")
|
||||
{
|
||||
return file == null ? new List<Template>() :
|
||||
file.paragraph()
|
||||
.Select(x => x.templateDefinition())
|
||||
.Where(x => x != null)
|
||||
.Select(t => new Template(t, lgfileContent, source))
|
||||
.ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extract LG options from the parse tree of a file.
|
||||
/// </summary>
|
||||
/// <param name="file">LG file context from ANTLR parser.</param>
|
||||
/// <returns>Option list.</returns>
|
||||
private static IList<string> ExtractLGOptions(LGFileParser.FileContext file)
|
||||
{
|
||||
return file == null ? new List<string>() :
|
||||
file.paragraph()
|
||||
.Select(x => x.optionsDefinition())
|
||||
.Where(x => x != null)
|
||||
.Select(t => ExtractOption(t.GetText()))
|
||||
.Where(t => !string.IsNullOrEmpty(t))
|
||||
.ToList();
|
||||
}
|
||||
|
||||
private static string ExtractOption(string originalText)
|
||||
{
|
||||
var result = string.Empty;
|
||||
if (string.IsNullOrWhiteSpace(originalText))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
var matchResult = OptionRegex.Match(originalText);
|
||||
if (matchResult.Success && matchResult.Groups.Count == 2)
|
||||
{
|
||||
result = matchResult.Groups[1].Value?.Trim();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extract LG imports from a file parse tree.
|
||||
/// </summary>
|
||||
/// <param name="file">LG file context from ANTLR parser.</param>
|
||||
/// <param name="source">text source.</param>
|
||||
/// <returns>lg template list.</returns>
|
||||
private static IList<TemplateImport> ExtractLGImports(LGFileParser.FileContext file, string source = "")
|
||||
{
|
||||
return file == null ? new List<TemplateImport>() :
|
||||
file.paragraph()
|
||||
.Select(x => x.importDefinition())
|
||||
.Where(x => x != null)
|
||||
.Select(t => new TemplateImport(t, source))
|
||||
.ToList();
|
||||
}
|
||||
|
||||
private static IList<Templates> GetReferences(Templates file, Dictionary<string, Templates> cachedTemplates = null)
|
||||
{
|
||||
var resourcesFound = new HashSet<Templates>();
|
||||
|
@ -316,38 +197,248 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
|
|||
|
||||
private static void ResolveImportResources(Templates start, HashSet<Templates> resourcesFound, Dictionary<string, Templates> cachedTemplates)
|
||||
{
|
||||
var resourceIds = start.Imports.Select(lg => lg.Id);
|
||||
resourcesFound.Add(start);
|
||||
|
||||
foreach (var id in resourceIds)
|
||||
foreach (var import in start.Imports)
|
||||
{
|
||||
string content;
|
||||
string path;
|
||||
try
|
||||
{
|
||||
var (content, path) = start.ImportResolver(start.Id, id);
|
||||
if (resourcesFound.All(u => u.Id != path))
|
||||
{
|
||||
Templates childResource;
|
||||
if (cachedTemplates.ContainsKey(path))
|
||||
{
|
||||
childResource = cachedTemplates[path];
|
||||
}
|
||||
else
|
||||
{
|
||||
childResource = InnerParseText(content, path, start.ImportResolver, start.ExpressionParser, cachedTemplates);
|
||||
cachedTemplates.Add(path, childResource);
|
||||
}
|
||||
(content, path) = start.ImportResolver(start.Id, import.Id);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
var diagnostic = new Diagnostic(import.SourceRange.ParseTree.ConvertToRange(), e.Message, DiagnosticSeverity.Error, start.Id);
|
||||
throw new TemplateException(e.Message, new List<Diagnostic>() { diagnostic });
|
||||
}
|
||||
|
||||
ResolveImportResources(childResource, resourcesFound, cachedTemplates);
|
||||
if (resourcesFound.All(u => u.Id != path))
|
||||
{
|
||||
Templates childResource;
|
||||
if (cachedTemplates.ContainsKey(path))
|
||||
{
|
||||
childResource = cachedTemplates[path];
|
||||
}
|
||||
else
|
||||
{
|
||||
childResource = InnerParseText(content, path, start.ImportResolver, start.ExpressionParser, cachedTemplates);
|
||||
cachedTemplates.Add(path, childResource);
|
||||
}
|
||||
|
||||
ResolveImportResources(childResource, resourcesFound, cachedTemplates);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class TemplatesTransformer : LGFileParserBaseVisitor<object>
|
||||
{
|
||||
private static readonly Regex IdentifierRegex = new Regex(@"^[0-9a-zA-Z_]+$");
|
||||
private readonly Templates templates;
|
||||
|
||||
public TemplatesTransformer(Templates templates)
|
||||
{
|
||||
this.templates = templates;
|
||||
}
|
||||
|
||||
public Templates Transform(IParseTree parseTree)
|
||||
{
|
||||
Visit(parseTree);
|
||||
return this.templates;
|
||||
}
|
||||
|
||||
public override object VisitErrorDefinition([NotNull] LGFileParser.ErrorDefinitionContext context)
|
||||
{
|
||||
var lineContent = context.INVALID_LINE().GetText();
|
||||
if (!string.IsNullOrWhiteSpace(lineContent))
|
||||
{
|
||||
this.templates.Diagnostics.Add(BuildTemplatesDiagnostic(TemplateErrors.SyntaxError, context));
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public override object VisitImportDefinition([NotNull] LGFileParser.ImportDefinitionContext context)
|
||||
{
|
||||
var importStr = context.IMPORT().GetText();
|
||||
|
||||
var matchResult = ImportRegex.Match(importStr);
|
||||
if (matchResult.Success && matchResult.Groups.Count == 3)
|
||||
{
|
||||
var description = matchResult.Groups[1].Value?.Trim();
|
||||
var id = matchResult.Groups[2].Value?.Trim();
|
||||
|
||||
var sourceRange = new SourceRange(context, this.templates.Id);
|
||||
var import = new TemplateImport(description, id, sourceRange);
|
||||
this.templates.Imports.Add(import);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public override object VisitOptionDefinition([NotNull] LGFileParser.OptionDefinitionContext context)
|
||||
{
|
||||
var originalText = context.OPTION().GetText();
|
||||
var result = string.Empty;
|
||||
if (!string.IsNullOrWhiteSpace(originalText))
|
||||
{
|
||||
var matchResult = OptionRegex.Match(originalText);
|
||||
if (matchResult.Success && matchResult.Groups.Count == 2)
|
||||
{
|
||||
result = matchResult.Groups[1].Value?.Trim();
|
||||
}
|
||||
}
|
||||
catch (TemplateException err)
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(result))
|
||||
{
|
||||
throw err;
|
||||
this.templates.Options.Add(result);
|
||||
}
|
||||
catch (Exception err)
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public override object VisitTemplateDefinition([NotNull] LGFileParser.TemplateDefinitionContext context)
|
||||
{
|
||||
var startLine = context.Start.Line;
|
||||
var stopLine = context.Stop.Line;
|
||||
|
||||
var templateNameLine = context.templateNameLine().TEMPLATE_NAME_LINE().GetText();
|
||||
var (templateName, parameters) = ExtractTemplateNameLine(templateNameLine);
|
||||
|
||||
if (this.templates.Any(u => u.Name == templateName))
|
||||
{
|
||||
throw new TemplateException(err.Message, new List<Diagnostic> { BuildDiagnostic(err.Message, source: start.Id) });
|
||||
var diagnostic = BuildTemplatesDiagnostic(TemplateErrors.DuplicatedTemplateInSameTemplate(templateName), context.templateNameLine());
|
||||
this.templates.Diagnostics.Add(diagnostic);
|
||||
}
|
||||
else
|
||||
{
|
||||
var templateBody = context.templateBody().GetText();
|
||||
var file = context.Parent.Parent as LGFileParser.FileContext;
|
||||
var isLastTemplate = file.paragraph().Select(u => u.templateDefinition()).Where(u => u != null).Last() == context;
|
||||
if (!isLastTemplate)
|
||||
{
|
||||
templateBody = RemoveTrailingNewline(templateBody);
|
||||
}
|
||||
|
||||
var sourceRange = new SourceRange(context, this.templates.Id);
|
||||
var template = new Template(templateName, parameters, templateBody, sourceRange);
|
||||
|
||||
CheckTemplateName(templateName, context.templateNameLine());
|
||||
CheckTemplateParameters(parameters, context.templateNameLine());
|
||||
template.TemplateBodyParseTree = CheckTemplateBody(templateName, templateBody, context.templateBody(), startLine);
|
||||
|
||||
this.templates.Add(template);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private LGTemplateParser.BodyContext CheckTemplateBody(string templateName, string templateBody, LGFileParser.TemplateBodyContext context, int startLine)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(templateBody))
|
||||
{
|
||||
var diagnostic = BuildTemplatesDiagnostic(TemplateErrors.NoTemplateBody(templateName), context, DiagnosticSeverity.Warning);
|
||||
this.templates.Diagnostics.Add(diagnostic);
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
return AntlrParseTemplate(templateBody, startLine);
|
||||
}
|
||||
catch (TemplateException e)
|
||||
{
|
||||
e.Diagnostics.ToList().ForEach(u => this.templates.Diagnostics.Add(u));
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void CheckTemplateParameters(List<string> parameters, LGFileParser.TemplateNameLineContext context)
|
||||
{
|
||||
foreach (var parameter in parameters)
|
||||
{
|
||||
if (!IdentifierRegex.IsMatch(parameter))
|
||||
{
|
||||
var diagnostic = BuildTemplatesDiagnostic(TemplateErrors.InvalidTemplateName, context);
|
||||
this.templates.Diagnostics.Add(diagnostic);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void CheckTemplateName(string templateName, ParserRuleContext context)
|
||||
{
|
||||
var functionNameSplitDot = templateName.Split('.');
|
||||
foreach (var id in functionNameSplitDot)
|
||||
{
|
||||
if (!IdentifierRegex.IsMatch(id))
|
||||
{
|
||||
var diagnostic = BuildTemplatesDiagnostic(TemplateErrors.InvalidTemplateName, context);
|
||||
this.templates.Diagnostics.Add(diagnostic);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private (string templateName, List<string> parameters) ExtractTemplateNameLine(string templateNameLine)
|
||||
{
|
||||
var hashIndex = templateNameLine.IndexOf('#');
|
||||
|
||||
templateNameLine = templateNameLine.Substring(hashIndex + 1).Trim();
|
||||
|
||||
var templateName = templateNameLine;
|
||||
var parameters = new List<string>();
|
||||
var leftBracketIndex = templateNameLine.IndexOf("(");
|
||||
if (leftBracketIndex >= 0 && templateNameLine.EndsWith(")"))
|
||||
{
|
||||
templateName = templateNameLine.Substring(0, leftBracketIndex).Trim();
|
||||
var paramString = templateNameLine.Substring(leftBracketIndex + 1, templateNameLine.Length - leftBracketIndex - 2);
|
||||
if (!string.IsNullOrWhiteSpace(paramString))
|
||||
{
|
||||
parameters = paramString.Split(',').Select(u => u.Trim()).ToList();
|
||||
}
|
||||
}
|
||||
|
||||
return (templateName, parameters);
|
||||
}
|
||||
|
||||
private string RemoveTrailingNewline(string line)
|
||||
{
|
||||
// remove the end newline of middle template.
|
||||
var result = line;
|
||||
|
||||
if (result.EndsWith("\n", StringComparison.Ordinal))
|
||||
{
|
||||
result = result.Substring(0, result.Length - 1);
|
||||
if (result.EndsWith("\r", StringComparison.Ordinal))
|
||||
{
|
||||
result = result.Substring(0, result.Length - 1);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private LGTemplateParser.BodyContext AntlrParseTemplate(string templateBody, int lineOffset)
|
||||
{
|
||||
var input = new AntlrInputStream(templateBody);
|
||||
var lexer = new LGTemplateLexer(input);
|
||||
lexer.RemoveErrorListeners();
|
||||
|
||||
var tokens = new CommonTokenStream(lexer);
|
||||
var parser = new LGTemplateParser(tokens);
|
||||
parser.RemoveErrorListeners();
|
||||
var listener = new ErrorListener(this.templates.Id, lineOffset);
|
||||
|
||||
parser.AddErrorListener(listener);
|
||||
parser.BuildParseTree = true;
|
||||
|
||||
return parser.context().body();
|
||||
}
|
||||
|
||||
private Diagnostic BuildTemplatesDiagnostic(string errorMessage, ParserRuleContext context, DiagnosticSeverity severity = DiagnosticSeverity.Error)
|
||||
{
|
||||
return new Diagnostic(context.ConvertToRange(), errorMessage, severity, this.templates.Id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
|
||||
[sandwichTest-BreadEntity.lg](sandwichTest-BreadEntity.lg)
|
||||
[sandwichTest-library.lg](sandwichTest-library.lg)
|
||||
[sandwichTest-library-Missing.lg](sandwichTest-library-Missing.lg)
|
||||
|
||||
# AskBread
|
||||
- ${askEnum('Bread')}
|
||||
|
@ -10,6 +12,3 @@
|
|||
|
||||
# Bread(val)
|
||||
- ${BreadEntity(val)}
|
||||
[sandwichTest-BreadEntity.lg](sandwichTest-BreadEntity.lg)
|
||||
[sandwichTest-library.lg](sandwichTest-library.lg)
|
||||
[sandwichTest-library-Missing.lg](sandwichTest-library-Missing.lg)
|
|
@ -1,4 +1,5 @@
|
|||
|
||||
[sandwichTest-library.lg](sandwichTest-library.lg)
|
||||
[sandwichTest-library-Choose.lg](sandwichTest-library-Choose.lg)
|
||||
|
||||
# BreadEntity(value)
|
||||
- SWITCH: ${value}
|
||||
|
@ -16,5 +17,3 @@
|
|||
|
||||
# chooseBreadEntity
|
||||
- ${chooseEnumEntity('Bread')}
|
||||
[sandwichTest-library.lg](sandwichTest-library.lg)
|
||||
[sandwichTest-library-Choose.lg](sandwichTest-library-Choose.lg)
|
|
@ -1,4 +1,6 @@
|
|||
|
||||
[sandwichTest-CheeseEntity.lg](sandwichTest-CheeseEntity.lg)
|
||||
[sandwichTest-library.lg](sandwichTest-library.lg)
|
||||
[sandwichTest-library-Missing.lg](sandwichTest-library-Missing.lg)
|
||||
|
||||
# AskCheese
|
||||
- ${askEnum('Cheese')}
|
||||
|
@ -10,6 +12,3 @@
|
|||
|
||||
# Cheese(val)
|
||||
- ${CheeseEntity(val)}
|
||||
[sandwichTest-CheeseEntity.lg](sandwichTest-CheeseEntity.lg)
|
||||
[sandwichTest-library.lg](sandwichTest-library.lg)
|
||||
[sandwichTest-library-Missing.lg](sandwichTest-library-Missing.lg)
|
|
@ -1,4 +1,5 @@
|
|||
|
||||
[sandwichTest-library.lg](sandwichTest-library.lg)
|
||||
[sandwichTest-library-Choose.lg](sandwichTest-library-Choose.lg)
|
||||
|
||||
# CheeseEntity(value)
|
||||
- SWITCH: ${value}
|
||||
|
@ -24,5 +25,3 @@
|
|||
|
||||
# chooseCheeseEntity
|
||||
- ${chooseEnumEntity('Cheese')}
|
||||
[sandwichTest-library.lg](sandwichTest-library.lg)
|
||||
[sandwichTest-library-Choose.lg](sandwichTest-library-Choose.lg)
|
|
@ -1,4 +1,5 @@
|
|||
|
||||
[sandwichTest-library.lg](sandwichTest-library.lg)
|
||||
[sandwichTest-library-Missing.lg](sandwichTest-library-Missing.lg)
|
||||
|
||||
# AskLength
|
||||
- ${askString('Length')}
|
||||
|
@ -6,5 +7,3 @@
|
|||
|
||||
# LengthName
|
||||
- length
|
||||
[sandwichTest-library.lg](sandwichTest-library.lg)
|
||||
[sandwichTest-library-Missing.lg](sandwichTest-library-Missing.lg)
|
|
@ -1,4 +1,6 @@
|
|||
|
||||
[sandwichTest-MeatEntity.lg](sandwichTest-MeatEntity.lg)
|
||||
[sandwichTest-library.lg](sandwichTest-library.lg)
|
||||
[sandwichTest-library-Missing.lg](sandwichTest-library-Missing.lg)
|
||||
|
||||
# AskMeat
|
||||
- ${askEnum('Meat')}
|
||||
|
@ -10,6 +12,3 @@
|
|||
|
||||
# Meat(val)
|
||||
- ${MeatEntity(val)}
|
||||
[sandwichTest-MeatEntity.lg](sandwichTest-MeatEntity.lg)
|
||||
[sandwichTest-library.lg](sandwichTest-library.lg)
|
||||
[sandwichTest-library-Missing.lg](sandwichTest-library-Missing.lg)
|
|
@ -1,4 +1,5 @@
|
|||
|
||||
[sandwichTest-library.lg](sandwichTest-library.lg)
|
||||
[sandwichTest-library-Choose.lg](sandwichTest-library-Choose.lg)
|
||||
|
||||
# MeatEntity(value)
|
||||
- SWITCH: ${value}
|
||||
|
@ -24,5 +25,3 @@
|
|||
|
||||
# chooseMeatEntity
|
||||
- ${chooseEnumEntity('Meat')}
|
||||
[sandwichTest-library.lg](sandwichTest-library.lg)
|
||||
[sandwichTest-library-Choose.lg](sandwichTest-library-Choose.lg)
|
|
@ -1,4 +1,6 @@
|
|||
|
||||
[sandwichTest-NameEntity.lg](sandwichTest-NameEntity.lg)
|
||||
[sandwichTest-library.lg](sandwichTest-library.lg)
|
||||
[sandwichTest-library-Missing.lg](sandwichTest-library-Missing.lg)
|
||||
|
||||
# AskName
|
||||
- ${askString('Name')}
|
||||
|
@ -10,6 +12,3 @@
|
|||
|
||||
# Name(val)
|
||||
- ${NameEntity(val)}
|
||||
[sandwichTest-NameEntity.lg](sandwichTest-NameEntity.lg)
|
||||
[sandwichTest-library.lg](sandwichTest-library.lg)
|
||||
[sandwichTest-library-Missing.lg](sandwichTest-library-Missing.lg)
|
|
@ -1,4 +1,5 @@
|
|||
|
||||
[sandwichTest-library.lg](sandwichTest-library.lg)
|
||||
[sandwichTest-library-Missing.lg](sandwichTest-library-Missing.lg)
|
||||
|
||||
# AskPrice
|
||||
- ${askString('Price')}
|
||||
|
@ -6,5 +7,3 @@
|
|||
|
||||
# PriceName
|
||||
- price
|
||||
[sandwichTest-library.lg](sandwichTest-library.lg)
|
||||
[sandwichTest-library-Missing.lg](sandwichTest-library-Missing.lg)
|
|
@ -1,4 +1,6 @@
|
|||
|
||||
[sandwichTest-QuantityEntity.lg](sandwichTest-QuantityEntity.lg)
|
||||
[sandwichTest-library.lg](sandwichTest-library.lg)
|
||||
[sandwichTest-library-Missing.lg](sandwichTest-library-Missing.lg)
|
||||
|
||||
# AskQuantity
|
||||
- ${askNumber('Quantity')}
|
||||
|
@ -10,6 +12,3 @@
|
|||
|
||||
# Quantity(val)
|
||||
- ${QuantityEntity(val)}
|
||||
[sandwichTest-QuantityEntity.lg](sandwichTest-QuantityEntity.lg)
|
||||
[sandwichTest-library.lg](sandwichTest-library.lg)
|
||||
[sandwichTest-library-Missing.lg](sandwichTest-library-Missing.lg)
|
|
@ -1,3 +1,6 @@
|
|||
[sandwichTest-library.lg](sandwichTest-library.lg)
|
||||
[sandwichTest-library-ReadForm.lg](sandwichTest-library-ReadForm.lg)
|
||||
|
||||
# numberValidation(property, number)
|
||||
- IF: ${less(number, dialogClass.schema.properties[property].minimum)}
|
||||
- ${number} is less than the minimum value ${dialogClass.schema.properties[property].minimum}.
|
||||
|
@ -35,5 +38,3 @@
|
|||
|
||||
# unexpectedPropertyChange(property, val, oldVal)
|
||||
- ${name(property)} is changed from ${value(property, oldVal)} to ${value(property, val)}.
|
||||
[sandwichTest-library.lg](sandwichTest-library.lg)
|
||||
[sandwichTest-library-ReadForm.lg](sandwichTest-library-ReadForm.lg)
|
|
@ -1,3 +1,6 @@
|
|||
[sandwichTest-library.lg](sandwichTest-library.lg)
|
||||
[sandwichTest-library-Help.lg](sandwichTest-library-Help.lg)
|
||||
|
||||
# chooseEnumEntity(property)
|
||||
- ```
|
||||
${askHelp()}
|
||||
|
@ -9,5 +12,3 @@ Please choose a value for ${name(property)} from [${join(foreach(turn.dialogEven
|
|||
|
||||
# choosePropertyEntity(property)
|
||||
- "${property.entity.text}" as ${name(property.property)}
|
||||
[sandwichTest-library.lg](sandwichTest-library.lg)
|
||||
[sandwichTest-library-Help.lg](sandwichTest-library-Help.lg)
|
|
@ -1,3 +1,5 @@
|
|||
[sandwichTest-library.lg](sandwichTest-library.lg)
|
||||
|
||||
# askHelp
|
||||
- IF: ${$retries > 0 && $lastIntent != 'Help'}
|
||||
- ${join(foreach($expectedProperties, expected, help1(expected)), '\n')}
|
||||
|
@ -25,4 +27,3 @@ You can find out about a specific property by doing 'help <property>'.
|
|||
- Enter any string for ${name(property)}
|
||||
- ELSE:
|
||||
- No help available.
|
||||
[sandwichTest-library.lg](sandwichTest-library.lg)
|
|
@ -1,3 +1,6 @@
|
|||
[sandwichTest-library.lg](sandwichTest-library.lg)
|
||||
[sandwichTest-library-Help.lg](sandwichTest-library-Help.lg)
|
||||
|
||||
# askEnum(property)
|
||||
- ```
|
||||
${askHelp()}
|
||||
|
@ -25,5 +28,3 @@ Enter a value for ${name(property)}
|
|||
${askHelp()}
|
||||
Enter a value for ${name(property)}: true or false
|
||||
```
|
||||
[sandwichTest-library.lg](sandwichTest-library.lg)
|
||||
[sandwichTest-library-Help.lg](sandwichTest-library-Help.lg)
|
|
@ -1,3 +1,5 @@
|
|||
[sandwichTest-library.lg](sandwichTest-library.lg)
|
||||
|
||||
# readForm(name)
|
||||
[Activity
|
||||
Attachments=${json(formCard(name))}
|
||||
|
@ -49,4 +51,3 @@
|
|||
|
||||
# subFact(property, subproperty)
|
||||
- ${dialog[property][subproperty]}
|
||||
[sandwichTest-library.lg](sandwichTest-library.lg)
|
|
@ -1,3 +1,5 @@
|
|||
[sandwichTest-library-PROPERTYName.lg](sandwichTest-library-PROPERTYName.lg)
|
||||
|
||||
> This file contains the generic templates to use for properties of particular types.
|
||||
|
||||
# welcome
|
||||
|
@ -46,4 +48,3 @@
|
|||
|
||||
# notUnderstood
|
||||
- Sorry, I do not understand ${join(foreach(turn.unrecognizedtext, chunk, concat("'", chunk, "'")), ' or ')}
|
||||
[sandwichTest-library-PROPERTYName.lg](sandwichTest-library-PROPERTYName.lg)
|
|
@ -1,4 +1,5 @@
|
|||
|
||||
[unittests-library.lg](unittests-library.lg)
|
||||
[unittests-library-Missing.lg](unittests-library-Missing.lg)
|
||||
|
||||
# AskAge
|
||||
- ${askString('Age')}
|
||||
|
@ -6,5 +7,3 @@
|
|||
|
||||
# AgeName
|
||||
- age
|
||||
[unittests-library.lg](unittests-library.lg)
|
||||
[unittests-library-Missing.lg](unittests-library-Missing.lg)
|
|
@ -1,4 +1,5 @@
|
|||
|
||||
[unittests-library.lg](unittests-library.lg)
|
||||
[unittests-library-Missing.lg](unittests-library-Missing.lg)
|
||||
|
||||
# AskDatetimev2
|
||||
- ${askString('Datetimev2')}
|
||||
|
@ -6,5 +7,3 @@
|
|||
|
||||
# Datetimev2Name
|
||||
- datetimev2
|
||||
[unittests-library.lg](unittests-library.lg)
|
||||
[unittests-library-Missing.lg](unittests-library-Missing.lg)
|
|
@ -1,4 +1,5 @@
|
|||
|
||||
[unittests-library.lg](unittests-library.lg)
|
||||
[unittests-library-Missing.lg](unittests-library-Missing.lg)
|
||||
|
||||
# AskDimension
|
||||
- ${askString('Dimension')}
|
||||
|
@ -6,5 +7,3 @@
|
|||
|
||||
# DimensionName
|
||||
- dimension
|
||||
[unittests-library.lg](unittests-library.lg)
|
||||
[unittests-library-Missing.lg](unittests-library-Missing.lg)
|
|
@ -1,4 +1,6 @@
|
|||
|
||||
[unittests-EmailEntity.lg](unittests-EmailEntity.lg)
|
||||
[unittests-library.lg](unittests-library.lg)
|
||||
[unittests-library-Missing.lg](unittests-library-Missing.lg)
|
||||
|
||||
# AskEmail
|
||||
- ${askString('Email')}
|
||||
|
@ -10,6 +12,3 @@
|
|||
|
||||
# Email(val)
|
||||
- ${EmailEntity(val)}
|
||||
[unittests-EmailEntity.lg](unittests-EmailEntity.lg)
|
||||
[unittests-library.lg](unittests-library.lg)
|
||||
[unittests-library-Missing.lg](unittests-library-Missing.lg)
|
|
@ -1,4 +1,6 @@
|
|||
|
||||
[unittests-Enum1Entity.lg](unittests-Enum1Entity.lg)
|
||||
[unittests-library.lg](unittests-library.lg)
|
||||
[unittests-library-Missing.lg](unittests-library-Missing.lg)
|
||||
|
||||
# AskEnum1
|
||||
- ${askEnum('Enum1')}
|
||||
|
@ -10,6 +12,3 @@
|
|||
|
||||
# Enum1(val)
|
||||
- ${Enum1Entity(val)}
|
||||
[unittests-Enum1Entity.lg](unittests-Enum1Entity.lg)
|
||||
[unittests-library.lg](unittests-library.lg)
|
||||
[unittests-library-Missing.lg](unittests-library-Missing.lg)
|
|
@ -1,4 +1,5 @@
|
|||
|
||||
[unittests-library.lg](unittests-library.lg)
|
||||
[unittests-library-Choose.lg](unittests-library-Choose.lg)
|
||||
|
||||
# Enum1Entity(value)
|
||||
- SWITCH: ${value}
|
||||
|
@ -16,5 +17,3 @@
|
|||
|
||||
# chooseEnum1Entity
|
||||
- ${chooseEnumEntity('Enum1')}
|
||||
[unittests-library.lg](unittests-library.lg)
|
||||
[unittests-library-Choose.lg](unittests-library-Choose.lg)
|
|
@ -1,4 +1,6 @@
|
|||
|
||||
[unittests-Enum2Entity.lg](unittests-Enum2Entity.lg)
|
||||
[unittests-library.lg](unittests-library.lg)
|
||||
[unittests-library-Missing.lg](unittests-library-Missing.lg)
|
||||
|
||||
# AskEnum2
|
||||
- ${askEnum('Enum2')}
|
||||
|
@ -10,6 +12,3 @@
|
|||
|
||||
# Enum2(val)
|
||||
- ${Enum2Entity(val)}
|
||||
[unittests-Enum2Entity.lg](unittests-Enum2Entity.lg)
|
||||
[unittests-library.lg](unittests-library.lg)
|
||||
[unittests-library-Missing.lg](unittests-library-Missing.lg)
|
|
@ -1,4 +1,5 @@
|
|||
|
||||
[unittests-library.lg](unittests-library.lg)
|
||||
[unittests-library-Choose.lg](unittests-library-Choose.lg)
|
||||
|
||||
# Enum2Entity(value)
|
||||
- SWITCH: ${value}
|
||||
|
@ -24,5 +25,3 @@
|
|||
|
||||
# chooseEnum2Entity
|
||||
- ${chooseEnumEntity('Enum2')}
|
||||
[unittests-library.lg](unittests-library.lg)
|
||||
[unittests-library-Choose.lg](unittests-library-Choose.lg)
|
|
@ -1,4 +1,5 @@
|
|||
|
||||
[unittests-library.lg](unittests-library.lg)
|
||||
[unittests-library-Missing.lg](unittests-library-Missing.lg)
|
||||
|
||||
# AskGeographyv2
|
||||
- ${askString('Geographyv2')}
|
||||
|
@ -6,5 +7,3 @@
|
|||
|
||||
# Geographyv2Name
|
||||
- geographyv2
|
||||
[unittests-library.lg](unittests-library.lg)
|
||||
[unittests-library-Missing.lg](unittests-library-Missing.lg)
|
|
@ -1,4 +1,6 @@
|
|||
|
||||
[unittests-KeyphrasepropertyEntity.lg](unittests-KeyphrasepropertyEntity.lg)
|
||||
[unittests-library.lg](unittests-library.lg)
|
||||
[unittests-library-Missing.lg](unittests-library-Missing.lg)
|
||||
|
||||
# AskKeyphraseproperty
|
||||
- ${askString('Keyphraseproperty')}
|
||||
|
@ -10,6 +12,3 @@
|
|||
|
||||
# Keyphraseproperty(val)
|
||||
- ${KeyphrasepropertyEntity(val)}
|
||||
[unittests-KeyphrasepropertyEntity.lg](unittests-KeyphrasepropertyEntity.lg)
|
||||
[unittests-library.lg](unittests-library.lg)
|
||||
[unittests-library-Missing.lg](unittests-library-Missing.lg)
|
|
@ -1,4 +1,5 @@
|
|||
|
||||
[unittests-library.lg](unittests-library.lg)
|
||||
[unittests-library-Missing.lg](unittests-library-Missing.lg)
|
||||
|
||||
# AskMoney
|
||||
- ${askString('Money')}
|
||||
|
@ -6,5 +7,3 @@
|
|||
|
||||
# MoneyName
|
||||
- money
|
||||
[unittests-library.lg](unittests-library.lg)
|
||||
[unittests-library-Missing.lg](unittests-library-Missing.lg)
|
|
@ -1,4 +1,5 @@
|
|||
|
||||
[unittests-library.lg](unittests-library.lg)
|
||||
[unittests-library-Missing.lg](unittests-library-Missing.lg)
|
||||
|
||||
# AskOrdinalv2
|
||||
- ${askString('Ordinalv2')}
|
||||
|
@ -6,5 +7,3 @@
|
|||
|
||||
# Ordinalv2Name
|
||||
- ordinalv2
|
||||
[unittests-library.lg](unittests-library.lg)
|
||||
[unittests-library-Missing.lg](unittests-library-Missing.lg)
|
|
@ -1,4 +1,6 @@
|
|||
|
||||
[unittests-PercentageEntity.lg](unittests-PercentageEntity.lg)
|
||||
[unittests-library.lg](unittests-library.lg)
|
||||
[unittests-library-Missing.lg](unittests-library-Missing.lg)
|
||||
|
||||
# AskPercentage
|
||||
- ${askString('Percentage')}
|
||||
|
@ -10,6 +12,3 @@
|
|||
|
||||
# Percentage(val)
|
||||
- ${PercentageEntity(val)}
|
||||
[unittests-PercentageEntity.lg](unittests-PercentageEntity.lg)
|
||||
[unittests-library.lg](unittests-library.lg)
|
||||
[unittests-library-Missing.lg](unittests-library-Missing.lg)
|
|
@ -1,4 +1,6 @@
|
|||
|
||||
[unittests-PersonnameEntity.lg](unittests-PersonnameEntity.lg)
|
||||
[unittests-library.lg](unittests-library.lg)
|
||||
[unittests-library-Missing.lg](unittests-library-Missing.lg)
|
||||
|
||||
# AskPersonname
|
||||
- ${askString('Personname')}
|
||||
|
@ -10,6 +12,3 @@
|
|||
|
||||
# Personname(val)
|
||||
- ${PersonnameEntity(val)}
|
||||
[unittests-PersonnameEntity.lg](unittests-PersonnameEntity.lg)
|
||||
[unittests-library.lg](unittests-library.lg)
|
||||
[unittests-library-Missing.lg](unittests-library-Missing.lg)
|
|
@ -1,4 +1,6 @@
|
|||
|
||||
[unittests-PhonenumberEntity.lg](unittests-PhonenumberEntity.lg)
|
||||
[unittests-library.lg](unittests-library.lg)
|
||||
[unittests-library-Missing.lg](unittests-library-Missing.lg)
|
||||
|
||||
# AskPhonenumber
|
||||
- ${askString('Phonenumber')}
|
||||
|
@ -10,6 +12,3 @@
|
|||
|
||||
# Phonenumber(val)
|
||||
- ${PhonenumberEntity(val)}
|
||||
[unittests-PhonenumberEntity.lg](unittests-PhonenumberEntity.lg)
|
||||
[unittests-library.lg](unittests-library.lg)
|
||||
[unittests-library-Missing.lg](unittests-library-Missing.lg)
|
|
@ -1,4 +1,6 @@
|
|||
|
||||
[unittests-QuantityEntity.lg](unittests-QuantityEntity.lg)
|
||||
[unittests-library.lg](unittests-library.lg)
|
||||
[unittests-library-Missing.lg](unittests-library-Missing.lg)
|
||||
|
||||
# AskQuantity
|
||||
- ${askNumber('Quantity')}
|
||||
|
@ -10,6 +12,3 @@
|
|||
|
||||
# Quantity(val)
|
||||
- ${QuantityEntity(val)}
|
||||
[unittests-QuantityEntity.lg](unittests-QuantityEntity.lg)
|
||||
[unittests-library.lg](unittests-library.lg)
|
||||
[unittests-library-Missing.lg](unittests-library-Missing.lg)
|
|
@ -1,4 +1,5 @@
|
|||
|
||||
[unittests-library.lg](unittests-library.lg)
|
||||
[unittests-library-Missing.lg](unittests-library-Missing.lg)
|
||||
|
||||
# AskTemperature
|
||||
- ${askString('Temperature')}
|
||||
|
@ -6,5 +7,3 @@
|
|||
|
||||
# TemperatureName
|
||||
- temperature
|
||||
[unittests-library.lg](unittests-library.lg)
|
||||
[unittests-library-Missing.lg](unittests-library-Missing.lg)
|
|
@ -1,4 +1,6 @@
|
|||
|
||||
[unittests-UrlEntity.lg](unittests-UrlEntity.lg)
|
||||
[unittests-library.lg](unittests-library.lg)
|
||||
[unittests-library-Missing.lg](unittests-library-Missing.lg)
|
||||
|
||||
# AskUrl
|
||||
- ${askString('Url')}
|
||||
|
@ -10,6 +12,3 @@
|
|||
|
||||
# Url(val)
|
||||
- ${UrlEntity(val)}
|
||||
[unittests-UrlEntity.lg](unittests-UrlEntity.lg)
|
||||
[unittests-library.lg](unittests-library.lg)
|
||||
[unittests-library-Missing.lg](unittests-library-Missing.lg)
|
|
@ -1,4 +1,6 @@
|
|||
|
||||
[unittests-UtterancepropertyEntity.lg](unittests-UtterancepropertyEntity.lg)
|
||||
[unittests-library.lg](unittests-library.lg)
|
||||
[unittests-library-Missing.lg](unittests-library-Missing.lg)
|
||||
|
||||
# AskUtteranceproperty
|
||||
- ${askString('Utteranceproperty')}
|
||||
|
@ -10,6 +12,3 @@
|
|||
|
||||
# Utteranceproperty(val)
|
||||
- ${UtterancepropertyEntity(val)}
|
||||
[unittests-UtterancepropertyEntity.lg](unittests-UtterancepropertyEntity.lg)
|
||||
[unittests-library.lg](unittests-library.lg)
|
||||
[unittests-library-Missing.lg](unittests-library-Missing.lg)
|
|
@ -1,4 +1,6 @@
|
|||
|
||||
[unittests-ZipcodepatternEntity.lg](unittests-ZipcodepatternEntity.lg)
|
||||
[unittests-library.lg](unittests-library.lg)
|
||||
[unittests-library-Missing.lg](unittests-library-Missing.lg)
|
||||
|
||||
# AskZipcodepattern
|
||||
- ${askString('Zipcodepattern')}
|
||||
|
@ -10,6 +12,3 @@
|
|||
|
||||
# Zipcodepattern(val)
|
||||
- ${ZipcodepatternEntity(val)}
|
||||
[unittests-ZipcodepatternEntity.lg](unittests-ZipcodepatternEntity.lg)
|
||||
[unittests-library.lg](unittests-library.lg)
|
||||
[unittests-library-Missing.lg](unittests-library-Missing.lg)
|
|
@ -1,3 +1,6 @@
|
|||
[unittests-library.lg](unittests-library.lg)
|
||||
[unittests-library-ReadForm.lg](unittests-library-ReadForm.lg)
|
||||
|
||||
# numberValidation(property, number)
|
||||
- IF: ${less(number, dialogClass.schema.properties[property].minimum)}
|
||||
- ${number} is less than the minimum value ${dialogClass.schema.properties[property].minimum}.
|
||||
|
@ -35,5 +38,3 @@
|
|||
|
||||
# unexpectedPropertyChange(property, val, oldVal)
|
||||
- ${name(property)} is changed from ${value(property, oldVal)} to ${value(property, val)}.
|
||||
[unittests-library.lg](unittests-library.lg)
|
||||
[unittests-library-ReadForm.lg](unittests-library-ReadForm.lg)
|
|
@ -1,3 +1,6 @@
|
|||
[unittests-library.lg](unittests-library.lg)
|
||||
[unittests-library-Help.lg](unittests-library-Help.lg)
|
||||
|
||||
# chooseEnumEntity(property)
|
||||
- ```
|
||||
${askHelp()}
|
||||
|
@ -9,5 +12,3 @@ Please choose a value for ${name(property)} from [${join(foreach(turn.dialogEven
|
|||
|
||||
# choosePropertyEntity(property)
|
||||
- "${property.entity.text}" as ${name(property.property)}
|
||||
[unittests-library.lg](unittests-library.lg)
|
||||
[unittests-library-Help.lg](unittests-library-Help.lg)
|
|
@ -1,3 +1,5 @@
|
|||
[unittests-library.lg](unittests-library.lg)
|
||||
|
||||
# askHelp
|
||||
- IF: ${$retries > 0 && $lastIntent != 'Help'}
|
||||
- ${join(foreach($expectedProperties, expected, help1(expected)), '\n')}
|
||||
|
@ -25,4 +27,3 @@ You can find out about a specific property by doing 'help <property>'.
|
|||
- Enter any string for ${name(property)}
|
||||
- ELSE:
|
||||
- No help available.
|
||||
[unittests-library.lg](unittests-library.lg)
|
|
@ -1,3 +1,6 @@
|
|||
[unittests-library.lg](unittests-library.lg)
|
||||
[unittests-library-Help.lg](unittests-library-Help.lg)
|
||||
|
||||
# askEnum(property)
|
||||
- ```
|
||||
${askHelp()}
|
||||
|
@ -25,5 +28,3 @@ Enter a value for ${name(property)}
|
|||
${askHelp()}
|
||||
Enter a value for ${name(property)}: true or false
|
||||
```
|
||||
[unittests-library.lg](unittests-library.lg)
|
||||
[unittests-library-Help.lg](unittests-library-Help.lg)
|
|
@ -1,3 +1,5 @@
|
|||
[unittests-library.lg](unittests-library.lg)
|
||||
|
||||
# readForm(name)
|
||||
[Activity
|
||||
Attachments=${json(formCard(name))}
|
||||
|
@ -49,4 +51,3 @@
|
|||
|
||||
# subFact(property, subproperty)
|
||||
- ${dialog[property][subproperty]}
|
||||
[unittests-library.lg](unittests-library.lg)
|
|
@ -1,3 +1,5 @@
|
|||
[unittests-library-PROPERTYName.lg](unittests-library-PROPERTYName.lg)
|
||||
|
||||
> This file contains the generic templates to use for properties of particular types.
|
||||
|
||||
# welcome
|
||||
|
@ -46,4 +48,3 @@
|
|||
|
||||
# notUnderstood
|
||||
- Sorry, I do not understand ${join(foreach(turn.unrecognizedtext, chunk, concat("'", chunk, "'")), ' or ')}
|
||||
[unittests-library-PROPERTYName.lg](unittests-library-PROPERTYName.lg)
|
|
@ -1,7 +1,6 @@
|
|||
# template
|
||||
[import](./CustomFunctionSub.lg)
|
||||
# template
|
||||
- ${custom(1, 2)}
|
||||
|
||||
# callRef
|
||||
- ${refSub()}
|
||||
|
||||
[import](./CustomFunctionSub.lg)
|
|
@ -1,7 +1,3 @@
|
|||
# basicTemplate
|
||||
- Hi
|
||||
- Hello
|
||||
|
||||
[import](./6.lg)
|
||||
[import](6.lg)
|
||||
[import](././6.lg)
|
||||
|
@ -9,6 +5,11 @@
|
|||
[import](import.lg)
|
||||
[import](import/import3.lg)
|
||||
|
||||
# basicTemplate
|
||||
- Hi
|
||||
- Hello
|
||||
|
||||
|
||||
# basicTemplate2
|
||||
- Hi 2
|
||||
- Hello 2
|
|
@ -1,12 +1,12 @@
|
|||
# basicTemplate3
|
||||
- Hi
|
||||
- Hello
|
||||
|
||||
[import](./6.lg)
|
||||
[import](6.lg)
|
||||
[import](././6.lg)
|
||||
[import](8.lg)
|
||||
|
||||
# basicTemplate3
|
||||
- Hi
|
||||
- Hello
|
||||
|
||||
# basicTemplate4
|
||||
- Hi 2
|
||||
- Hello 2
|
|
@ -10,7 +10,7 @@
|
|||
- IF: ${false}
|
||||
- hi
|
||||
|
||||
> else should not follewed by any expressions
|
||||
> else should not be followed by any expressions
|
||||
# template3
|
||||
- IF: ${true}
|
||||
- hello
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
# basicTemplate
|
||||
[import](ImportFile.lg)
|
||||
# basicTemplate
|
||||
- Hi
|
||||
- Hello
|
||||
|
||||
[import](ImportFile.lg)
|
||||
|
||||
# basicTemplate2
|
||||
- Hi 2
|
||||
- Hello 2
|
|
@ -0,0 +1,9 @@
|
|||
# hi
|
||||
[]
|
||||
- hi
|
||||
|
||||
# foo.bar
|
||||
[
|
||||
]
|
||||
-hi
|
||||
[import]
|
|
@ -1,4 +1,3 @@
|
|||
# template
|
||||
[No such file Non.lg](./Non.lg)
|
||||
# template
|
||||
- Hello
|
||||
|
||||
[No such file Non.lg](./Non.lg)
|
|
@ -1,19 +1,4 @@
|
|||
#Demo
|
||||
- ${createArray(1,2
|
||||
2,3)
|
||||
[import](5.lg)
|
||||
|
||||
#Demo2
|
||||
- ${createArray(1,
|
||||
2,3)
|
||||
|
||||
#Demo3
|
||||
- IF ${32.5 > 14.1 ||
|
||||
userName == 'doskey' ||
|
||||
day = 'Monday'
|
||||
- good day
|
||||
|
||||
#Demo4
|
||||
- ${createArray(1,
|
||||
2,3)
|
||||
> this is a comment
|
|
@ -63,6 +63,7 @@
|
|||
<None Remove="ExceptionExamples\DuplicatedTemplatesInImportFiles.lg" />
|
||||
<None Remove="ExceptionExamples\RunTimeErrors.lg" />
|
||||
<None Remove="ExceptionExamples\ExpressionFormatError.lg" />
|
||||
<None Remove="ExceptionExamples\ErrorLine.lg" />
|
||||
<None Remove="MultiLanguage\a.lg" />
|
||||
<None Remove="MultiLanguage\a.en.lg" />
|
||||
</ItemGroup>
|
||||
|
@ -227,6 +228,9 @@
|
|||
<Content Include="ExceptionExamples\ExpressionFormatError.lg">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="ExceptionExamples\ErrorLine.lg">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="MultiLanguage\a.lg">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
|
|
|
@ -137,11 +137,6 @@ namespace Microsoft.Bot.Builder.AI.LanguageGeneration.Tests
|
|||
Assert.AreEqual(1, diagnostics.Count);
|
||||
Assert.AreEqual(DiagnosticSeverity.Error, diagnostics[0].Severity);
|
||||
Assert.IsTrue(diagnostics[0].Message.Contains("Close } is missing in Expression"));
|
||||
|
||||
diagnostics = Templates.ParseText("#Demo4\r\n- ${createArray(1,\r\n2,3)\r\n#AnotherTemplate").Diagnostics;
|
||||
Assert.AreEqual(1, diagnostics.Count);
|
||||
Assert.AreEqual(DiagnosticSeverity.Error, diagnostics[0].Severity);
|
||||
Assert.IsTrue(diagnostics[0].Message.Contains("Close } is missing in Expression"));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
|
@ -317,6 +312,23 @@ namespace Microsoft.Bot.Builder.AI.LanguageGeneration.Tests
|
|||
Assert.AreEqual("'dialog.abc' evaluated to null. [switchcase2] Case 'Default': Error occurred when evaluating '-I want ${dialog.abc}'.", exception.Message);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestErrorLine()
|
||||
{
|
||||
var diagnostics = GetDiagnostics("ErrorLine.lg");
|
||||
|
||||
Assert.AreEqual(4, diagnostics.Count);
|
||||
|
||||
Assert.AreEqual(DiagnosticSeverity.Error, diagnostics[0].Severity);
|
||||
Assert.IsTrue(diagnostics[0].Message.Contains(TemplateErrors.SyntaxError));
|
||||
Assert.AreEqual(DiagnosticSeverity.Error, diagnostics[1].Severity);
|
||||
Assert.IsTrue(diagnostics[1].Message.Contains(TemplateErrors.InvalidStrucName));
|
||||
Assert.AreEqual(DiagnosticSeverity.Error, diagnostics[2].Severity);
|
||||
Assert.IsTrue(diagnostics[2].Message.Contains(TemplateErrors.MissingStrucEnd));
|
||||
Assert.AreEqual(DiagnosticSeverity.Error, diagnostics[3].Severity);
|
||||
Assert.IsTrue(diagnostics[3].Message.Contains(TemplateErrors.InvalidStrucBody));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestExpressionFormatError()
|
||||
{
|
||||
|
|
|
@ -425,7 +425,7 @@ namespace Microsoft.Bot.Builder.AI.LanguageGeneration.Tests
|
|||
Assert.IsTrue(evaled == "Hi 2" || evaled == "Hello 2");
|
||||
|
||||
// Assert 6.lg of relative path is imported from text.
|
||||
templates = Templates.ParseText("# basicTemplate\r\n- Hi\r\n- Hello\r\n[import](./6.lg)", GetExampleFilePath("xx.lg"));
|
||||
templates = Templates.ParseText("[import](./6.lg)\r\n# basicTemplate\r\n- Hi\r\n- Hello\r\n", GetExampleFilePath("xx.lg"));
|
||||
|
||||
Assert.AreEqual(8, templates.AllTemplates.Count());
|
||||
evaled = templates.Evaluate("basicTemplate", null).ToString();
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
|
||||
[sandwich-BreadEntity.lg](sandwich-BreadEntity.lg)
|
||||
[sandwich-library.lg](sandwich-library.lg)
|
||||
[sandwich-library-Missing.lg](sandwich-library-Missing.lg)
|
||||
|
||||
# AskBread
|
||||
- ${askEnum('Bread')}
|
||||
|
@ -10,6 +12,3 @@
|
|||
|
||||
# Bread(val)
|
||||
- ${BreadEntity(val)}
|
||||
[sandwich-BreadEntity.lg](sandwich-BreadEntity.lg)
|
||||
[sandwich-library.lg](sandwich-library.lg)
|
||||
[sandwich-library-Missing.lg](sandwich-library-Missing.lg)
|
|
@ -1,4 +1,5 @@
|
|||
|
||||
[sandwich-library.lg](sandwich-library.lg)
|
||||
[sandwich-library-Choose.lg](sandwich-library-Choose.lg)
|
||||
|
||||
# BreadEntity(value)
|
||||
- SWITCH: ${value}
|
||||
|
@ -16,5 +17,3 @@
|
|||
|
||||
# chooseBreadEntity
|
||||
- ${chooseEnumEntity('Bread')}
|
||||
[sandwich-library.lg](sandwich-library.lg)
|
||||
[sandwich-library-Choose.lg](sandwich-library-Choose.lg)
|
|
@ -1,4 +1,6 @@
|
|||
|
||||
[sandwich-CheeseEntity.lg](sandwich-CheeseEntity.lg)
|
||||
[sandwich-library.lg](sandwich-library.lg)
|
||||
[sandwich-library-Missing.lg](sandwich-library-Missing.lg)
|
||||
|
||||
# AskCheese
|
||||
- ${askEnum('Cheese')}
|
||||
|
@ -10,6 +12,3 @@
|
|||
|
||||
# Cheese(val)
|
||||
- ${CheeseEntity(val)}
|
||||
[sandwich-CheeseEntity.lg](sandwich-CheeseEntity.lg)
|
||||
[sandwich-library.lg](sandwich-library.lg)
|
||||
[sandwich-library-Missing.lg](sandwich-library-Missing.lg)
|
|
@ -1,4 +1,5 @@
|
|||
|
||||
[sandwich-library.lg](sandwich-library.lg)
|
||||
[sandwich-library-Choose.lg](sandwich-library-Choose.lg)
|
||||
|
||||
# CheeseEntity(value)
|
||||
- SWITCH: ${value}
|
||||
|
@ -24,5 +25,3 @@
|
|||
|
||||
# chooseCheeseEntity
|
||||
- ${chooseEnumEntity('Cheese')}
|
||||
[sandwich-library.lg](sandwich-library.lg)
|
||||
[sandwich-library-Choose.lg](sandwich-library-Choose.lg)
|
|
@ -1,4 +1,5 @@
|
|||
|
||||
[sandwich-library.lg](sandwich-library.lg)
|
||||
[sandwich-library-Missing.lg](sandwich-library-Missing.lg)
|
||||
|
||||
# AskLength
|
||||
- ${askString('Length')}
|
||||
|
@ -6,5 +7,3 @@
|
|||
|
||||
# LengthName
|
||||
- length
|
||||
[sandwich-library.lg](sandwich-library.lg)
|
||||
[sandwich-library-Missing.lg](sandwich-library-Missing.lg)
|
|
@ -1,4 +1,6 @@
|
|||
|
||||
[sandwich-MeatEntity.lg](sandwich-MeatEntity.lg)
|
||||
[sandwich-library.lg](sandwich-library.lg)
|
||||
[sandwich-library-Missing.lg](sandwich-library-Missing.lg)
|
||||
|
||||
# AskMeat
|
||||
- ${askEnum('Meat')}
|
||||
|
@ -10,6 +12,3 @@
|
|||
|
||||
# Meat(val)
|
||||
- ${MeatEntity(val)}
|
||||
[sandwich-MeatEntity.lg](sandwich-MeatEntity.lg)
|
||||
[sandwich-library.lg](sandwich-library.lg)
|
||||
[sandwich-library-Missing.lg](sandwich-library-Missing.lg)
|
|
@ -1,4 +1,5 @@
|
|||
|
||||
[sandwich-library.lg](sandwich-library.lg)
|
||||
[sandwich-library-Choose.lg](sandwich-library-Choose.lg)
|
||||
|
||||
# MeatEntity(value)
|
||||
- SWITCH: ${value}
|
||||
|
@ -24,5 +25,3 @@
|
|||
|
||||
# chooseMeatEntity
|
||||
- ${chooseEnumEntity('Meat')}
|
||||
[sandwich-library.lg](sandwich-library.lg)
|
||||
[sandwich-library-Choose.lg](sandwich-library-Choose.lg)
|
|
@ -1,4 +1,6 @@
|
|||
|
||||
[sandwich-NameEntity.lg](sandwich-NameEntity.lg)
|
||||
[sandwich-library.lg](sandwich-library.lg)
|
||||
[sandwich-library-Missing.lg](sandwich-library-Missing.lg)
|
||||
|
||||
# AskName
|
||||
- ${askString('Name')}
|
||||
|
@ -10,6 +12,3 @@
|
|||
|
||||
# Name(val)
|
||||
- ${NameEntity(val)}
|
||||
[sandwich-NameEntity.lg](sandwich-NameEntity.lg)
|
||||
[sandwich-library.lg](sandwich-library.lg)
|
||||
[sandwich-library-Missing.lg](sandwich-library-Missing.lg)
|
|
@ -1,4 +1,5 @@
|
|||
|
||||
[sandwich-library.lg](sandwich-library.lg)
|
||||
[sandwich-library-Missing.lg](sandwich-library-Missing.lg)
|
||||
|
||||
# AskPrice
|
||||
- ${askString('Price')}
|
||||
|
@ -6,5 +7,3 @@
|
|||
|
||||
# PriceName
|
||||
- price
|
||||
[sandwich-library.lg](sandwich-library.lg)
|
||||
[sandwich-library-Missing.lg](sandwich-library-Missing.lg)
|
|
@ -1,4 +1,6 @@
|
|||
|
||||
[sandwich-QuantityEntity.lg](sandwich-QuantityEntity.lg)
|
||||
[sandwich-library.lg](sandwich-library.lg)
|
||||
[sandwich-library-Missing.lg](sandwich-library-Missing.lg)
|
||||
|
||||
# AskQuantity
|
||||
- ${askNumber('Quantity')}
|
||||
|
@ -10,6 +12,3 @@
|
|||
|
||||
# Quantity(val)
|
||||
- ${QuantityEntity(val)}
|
||||
[sandwich-QuantityEntity.lg](sandwich-QuantityEntity.lg)
|
||||
[sandwich-library.lg](sandwich-library.lg)
|
||||
[sandwich-library-Missing.lg](sandwich-library-Missing.lg)
|
|
@ -1,3 +1,6 @@
|
|||
[sandwich-library.lg](sandwich-library.lg)
|
||||
[sandwich-library-ReadForm.lg](sandwich-library-ReadForm.lg)
|
||||
|
||||
# numberValidation(property, number)
|
||||
- IF: ${less(number, dialogClass.schema.properties[property].minimum)}
|
||||
- ${number} is less than the minimum value ${dialogClass.schema.properties[property].minimum}.
|
||||
|
@ -35,5 +38,3 @@
|
|||
|
||||
# unexpectedPropertyChange(property, val, oldVal)
|
||||
- ${name(property)} is changed from ${value(property, oldVal)} to ${value(property, val)}.
|
||||
[sandwich-library.lg](sandwich-library.lg)
|
||||
[sandwich-library-ReadForm.lg](sandwich-library-ReadForm.lg)
|
|
@ -1,3 +1,6 @@
|
|||
[sandwich-library.lg](sandwich-library.lg)
|
||||
[sandwich-library-Help.lg](sandwich-library-Help.lg)
|
||||
|
||||
# chooseEnumEntity(property)
|
||||
- ```
|
||||
${askHelp()}
|
||||
|
@ -9,5 +12,3 @@ Please choose a value for ${name(property)} from [${join(foreach(turn.dialogEven
|
|||
|
||||
# choosePropertyEntity(property)
|
||||
- "${property.entity.text}" as ${name(property.property)}
|
||||
[sandwich-library.lg](sandwich-library.lg)
|
||||
[sandwich-library-Help.lg](sandwich-library-Help.lg)
|
|
@ -1,3 +1,5 @@
|
|||
[sandwich-library.lg](sandwich-library.lg)
|
||||
|
||||
# askHelp
|
||||
- IF: ${$retries > 0 && $lastIntent != 'Help'}
|
||||
- ${join(foreach($expectedProperties, expected, help1(expected)), '\n')}
|
||||
|
@ -25,4 +27,3 @@ You can find out about a specific property by doing 'help <property>'.
|
|||
- Enter any string for ${name(property)}
|
||||
- ELSE:
|
||||
- No help available.
|
||||
[sandwich-library.lg](sandwich-library.lg)
|
|
@ -1,3 +1,6 @@
|
|||
[sandwich-library.lg](sandwich-library.lg)
|
||||
[sandwich-library-Help.lg](sandwich-library-Help.lg)
|
||||
|
||||
# askEnum(property)
|
||||
- ```
|
||||
${askHelp()}
|
||||
|
@ -25,5 +28,3 @@ Enter a value for ${name(property)}
|
|||
${askHelp()}
|
||||
Enter a value for ${name(property)}: true or false
|
||||
```
|
||||
[sandwich-library.lg](sandwich-library.lg)
|
||||
[sandwich-library-Help.lg](sandwich-library-Help.lg)
|
|
@ -1,3 +1,5 @@
|
|||
[sandwich-library.lg](sandwich-library.lg)
|
||||
|
||||
# readForm(name)
|
||||
[Activity
|
||||
Attachments=${json(formCard(name))}
|
||||
|
@ -49,4 +51,3 @@
|
|||
|
||||
# subFact(property, subproperty)
|
||||
- ${dialog[property][subproperty]}
|
||||
[sandwich-library.lg](sandwich-library.lg)
|
|
@ -1,4 +1,5 @@
|
|||
> This file contains the generic templates to use for properties of particular types.
|
||||
[sandwich-library-PROPERTYName.lg](sandwich-library-PROPERTYName.lg)
|
||||
|
||||
# welcome
|
||||
- Welcome!
|
||||
|
@ -46,4 +47,3 @@
|
|||
|
||||
# notUnderstood
|
||||
- Sorry, I do not understand ${join(foreach(turn.unrecognizedtext, chunk, concat("'", chunk, "'")), ' or ')}
|
||||
[sandwich-library-PROPERTYName.lg](sandwich-library-PROPERTYName.lg)
|
Загрузка…
Ссылка в новой задаче