internal override bool TryAddSuggestionsForNodeKind(IntellisenseData.IntellisenseData intellisenseData) { Contracts.AssertValue(intellisenseData); TexlNode curNode = intellisenseData.CurNode; // Cursor is in the operation token. // Suggest binary operators. BinaryOpNode binaryOpNode = curNode.CastBinaryOp(); var tokenSpan = binaryOpNode.Token.Span; string keyword = binaryOpNode.Op == BinaryOp.Error ? tokenSpan.GetFragment(intellisenseData.Script) : TexlParser.GetTokString(binaryOpNode.Token.Kind); int replacementLength = tokenSpan.Min == intellisenseData.CursorPos ? 0 : tokenSpan.Lim - tokenSpan.Min; intellisenseData.SetMatchArea(tokenSpan.Min, intellisenseData.CursorPos, replacementLength); intellisenseData.BoundTo = binaryOpNode.Op == BinaryOp.Error ? string.Empty : keyword; AddSuggestionsForBinaryOperatorKeyWords(intellisenseData); return(true); }
public bool HasErrorsInTree(TexlNode rootNode, DocumentErrorSeverity severity = DocumentErrorSeverity.Suggestion) { Contracts.AssertValue(rootNode); if (CollectionUtils.Size(_errors) == 0) { return(false); } foreach (var err in _errors) { if (err.Node.InTree(rootNode) && err.Severity >= severity) { return(true); } } return(false); }
private static bool TryGetLocalScopeInfo(TexlNode node, TexlBinding binding, out ScopedNameLookupInfo info) { Contracts.AssertValue(node); Contracts.AssertValue(binding); if (node.Kind == NodeKind.FirstName) { FirstNameNode curNode = node.CastFirstName(); var firstNameInfo = binding.GetInfo(curNode); if (firstNameInfo.Kind == BindKind.ScopeArgument) { info = (ScopedNameLookupInfo)firstNameInfo.Data; return(true); } } info = new ScopedNameLookupInfo(); return(false); }
// 1. // <CallNode> // | // <ListNode> // | // <Error RecordNode>[Cursor position] // For example, Patch(Accounts, OldRecord, UpdateRecord, {<Cursor position> // 2. // <CallNode> // | // <ListNode> // | // <RecordNode> // | // <Error Node>[Cursor position] // For example, Patch(Accounts, OldRecord, UpdateRecord, {A:"",<cursor position>}) // 3. // <CallNode> // | // <ListNode> // | // <VariadicOpNode> // | // <RecordNode> // | // <Error Identifier Node> [Cursor position] // For example, Patch(Accounts, OldRecord, UpdateRecord, { 'Account Name': ""}); // Patch(Accounts, OldRecord, UpdateRecord,{<Cursor position>); private static bool TryGetRecordNodeWithinACallNode(TexlNode node, out RecordNode recordNode, out CallNode callNode) { Contracts.AssertValue(node); if (!TryGetParentRecordNode(node, out recordNode) || !TryGetParentListNode(recordNode, out ListNode listNode)) { callNode = null; return(false); } if (!(listNode.Parent is CallNode cNode)) { callNode = null; return(false); } callNode = cNode; return(true); }
private static bool TryGetParentListNode(RecordNode node, out ListNode listNode) { Contracts.AssertValue(node); TexlNode parentNode = node; while (parentNode != null) { if (parentNode.Kind == NodeKind.List) { listNode = parentNode.AsList(); return(true); } parentNode = parentNode.Parent; } listNode = null; return(false); }
private static bool TryGetParentRecordNode(TexlNode node, out RecordNode recordNode) { Contracts.AssertValue(node); TexlNode parentNode = node; while (parentNode != null) { if (parentNode.Kind == NodeKind.Record) { recordNode = parentNode.AsRecord(); return(true); } parentNode = parentNode.Parent; } recordNode = null; return(false); }
// Helper used to provide hints when we detect non-delegable parts of the expression due to server restrictions. protected void SuggestDelegationHint(TexlNode node, TexlBinding binding, ErrorResourceKey?suggestionKey, params object[] args) { Contracts.AssertValue(node); Contracts.AssertValue(binding); Contracts.Assert(suggestionKey == null || suggestionKey?.Key != string.Empty); if (suggestionKey == null) { suggestionKey = TexlStrings.SuggestRemoteExecutionHint; } if (args == null || args.Length == 0) { binding.ErrorContainer.EnsureError(DocumentErrorSeverity.Warning, node, (ErrorResourceKey)suggestionKey, _function.Name); } else { binding.ErrorContainer.EnsureError(DocumentErrorSeverity.Warning, node, (ErrorResourceKey)suggestionKey, args); } }
public bool TryGetValidValue(TexlNode argNode, TexlBinding binding, out IExpandInfo entityInfo) { Contracts.AssertValue(argNode); Contracts.AssertValue(binding); entityInfo = null; switch (argNode.Kind) { case NodeKind.FirstName: return(TryGetEntityInfo(argNode.AsFirstName(), binding, out entityInfo)); case NodeKind.Call: return(TryGetEntityInfo(argNode.AsCall(), binding, out entityInfo)); case NodeKind.DottedName: return(TryGetEntityInfo(argNode.AsDottedName(), binding, out entityInfo)); } return(false); }
private TexlNode ParseExprChain(TexlNode node, ITexlSource leftTrivia) { Contracts.AssertValue(node); Contracts.Assert(_curs.TidCur == TokKind.Semicolon); var delimiters = new List <Token>(1); var expressions = new List <TexlNode>(2); expressions.Add(node); var sourceList = new List <ITexlSource>(); sourceList.Add(new NodeSource(node)); sourceList.Add(leftTrivia); while (_curs.TidCur == TokKind.Semicolon) { var delimiter = _curs.TokMove(); delimiters.Add(delimiter); sourceList.Add(new TokenSource(delimiter)); sourceList.Add(ParseTrivia()); if (_curs.TidCur == TokKind.Eof || _curs.TidCur == TokKind.Comma || _curs.TidCur == TokKind.ParenClose) { break; } // SingleExpr here means we don't want chains on the RHS, but individual expressions. var expression = ParseExpr(Precedence.SingleExpr); expressions.Add(expression); sourceList.Add(new NodeSource(expression)); sourceList.Add(ParseTrivia()); } return(new VariadicOpNode( ref _idNext, VariadicOp.Chain, expressions.ToArray(), delimiters.ToArray(), new SourceList(sourceList))); }
public override bool PreVisit(BinaryOpNode node) { Contracts.AssertValue(node); // Cursor is in the left node. if (_cursorPosition <= node.Token.Span.Min) { node.Left.Accept(this); return(false); } // Cursor is inside the operation token. if (_cursorPosition <= node.Token.Span.Lim) { _result = node; return(false); } node.Right.Accept(this); return(false); }
private bool IsValidSortOrderNode(TexlNode node, SortOpMetadata metadata, TexlBinding binding, DPath columnPath) { Contracts.AssertValue(node); Contracts.AssertValue(metadata); Contracts.AssertValue(binding); Contracts.AssertValid(columnPath); if (binding.IsAsync(node)) { AddSuggestionMessageToTelemetry("Async sortorder node.", node, binding); DelegationTrackerCore.SetDelegationTrackerStatus(DelegationStatus.AsyncSortOrder, node, binding, this, DelegationTelemetryInfo.CreateEmptyDelegationTelemetryInfo()); return(false); } string sortOrder; switch (node.Kind) { case NodeKind.FirstName: case NodeKind.StrLit: return(_sortOrderValidator.TryGetValidValue(node, binding, out sortOrder) && IsSortOrderSuppportedByColumn(node, binding, sortOrder, metadata, columnPath)); case NodeKind.DottedName: case NodeKind.Call: if (_sortOrderValidator.TryGetValidValue(node, binding, out sortOrder) && IsSortOrderSuppportedByColumn(node, binding, sortOrder, metadata, columnPath)) { return(true); } // If both ascending and descending are supported then we can support this. return(IsSortOrderSuppportedByColumn(node, binding, LanguageConstants.DescendingSortOrderString, metadata, columnPath) && IsSortOrderSuppportedByColumn(node, binding, LanguageConstants.AscendingSortOrderString, metadata, columnPath)); default: AddSuggestionMessageToTelemetry("Unsupported sortorder node kind.", node, binding); return(false); } }
private bool IsValidSortOrderNode(TexlNode node, SortOpMetadata metadata, TexlBinding binding, DPath columnPath) { Contracts.AssertValue(node); Contracts.AssertValue(metadata); Contracts.AssertValue(binding); Contracts.AssertValid(columnPath); if (binding.IsAsync(node)) { var message = string.Format("Function:{0}, SortOrderNode is async", Name); AddSuggestionMessageToTelemetry(message, node, binding); return(false); } string sortOrder; switch (node.Kind) { case NodeKind.FirstName: case NodeKind.StrLit: return(_sortOrderValidator.TryGetValidValue(node, binding, out sortOrder) && IsSortOrderSuppportedByColumn(sortOrder, metadata, columnPath)); case NodeKind.DottedName: case NodeKind.Call: if (_sortOrderValidator.TryGetValidValue(node, binding, out sortOrder) && IsSortOrderSuppportedByColumn(sortOrder, metadata, columnPath)) { return(true); } // If both ascending and descending are supported then we can support this. return(IsSortOrderSuppportedByColumn(Microsoft.PowerFx.Core.Utils.LanguageConstants.DescendingSortOrderString, metadata, columnPath) && IsSortOrderSuppportedByColumn(Microsoft.PowerFx.Core.Utils.LanguageConstants.AscendingSortOrderString, metadata, columnPath)); default: AddSuggestionMessageToTelemetry("Unsupported sortorder node.", node, binding); return(false); } }
public override bool PreVisit(RecordNode node) { Contracts.AssertValue(node); Contracts.Assert(node.Token.Kind == TokKind.CurlyOpen || node.Token.Kind == TokKind.Ident); if (_cursorPosition <= node.Token.Span.Min || // If cursor position is before the open curly return the record node. node.Count == 0 || // Or if the record node is empty, return the record node. (node.CurlyClose != null && node.CurlyClose.Span.Lim <= _cursorPosition)) // Cursor is after the closed curly. { _result = node; return(false); } // Cursor is between the open and closed curly. int length = CollectionUtils.Size(node.Commas); for (int i = 0; i < length; i++) { Token tokComma = node.Commas[i]; // Cursor position is inside ith child. if (_cursorPosition <= tokComma.Span.Min) { node.Children[i].Accept(this); return(false); } } if (node.CurlyClose == null || _cursorPosition <= node.CurlyClose.Span.Min) { // Cursor is within the last child. node.Children[node.Children.Length - 1].Accept(this); return(false); } // Cursor is after the closing curly. _result = node; return(false); }
public static DType ScopeTypeForArgumentSuggestions(CallNode callNode, IntellisenseData.IntellisenseData intellisenseData) { Contracts.AssertValue(callNode); Contracts.AssertValue(intellisenseData); var info = intellisenseData.Binding.GetInfo(callNode); if (info.Function.UseParentScopeForArgumentSuggestions) { return(ClosestParentScopeTypeForSuggestions(callNode, intellisenseData)); } if (callNode.Args.Count <= info.Function.SuggestionTypeReferenceParamIndex) { return(DType.Unknown); } TexlNode referenceArg = callNode.Args.Children[info.Function.SuggestionTypeReferenceParamIndex]; return(info.Function.UsesEnumNamespace ? GetEnumType(intellisenseData, referenceArg) : intellisenseData.Binding.GetType(referenceArg)); }
internal static DType ClosestParentScopeTypeForSuggestions(CallNode callNode, IntellisenseData.IntellisenseData intellisenseData) { Contracts.AssertValue(callNode); Contracts.AssertValue(intellisenseData); TexlNode currentNode = callNode; while (currentNode.Parent != null) { currentNode = currentNode.Parent; if (currentNode is CallNode callNodeCurr) { DType parentScopeType = ScopeTypeForArgumentSuggestions(callNodeCurr, intellisenseData); if (parentScopeType != null && parentScopeType != DType.Unknown) { return(parentScopeType); } } } return(DType.Unknown); }
private LazyList <string> Basic(TexlNode node, Context context) { return(LazyList <string> .Of( node.SourceList.Sources .SelectMany(source => { var nodeSource = source as NodeSource; if (nodeSource != null) { return nodeSource.Node.Accept(this, context); } else if (source is WhitespaceSource) { return LazyList <string> .Of(" "); } else { return source.Tokens.Select(GetScriptForToken); } }))); }
private static bool TryGetNamespaceFunctions(TexlNode node, TexlBinding binding, out IEnumerable <TexlFunction> functions) { Contracts.AssertValue(node); Contracts.AssertValue(binding); FirstNameNode curNode = node.AsFirstName(); if (curNode == null) { functions = EmptyEnumerator <TexlFunction> .Instance; return(false); } FirstNameInfo firstNameInfo = binding.GetInfo(curNode).VerifyValue(); Contracts.AssertValid(firstNameInfo.Name); DPath namespacePath = new DPath().Append(firstNameInfo.Name); functions = binding.NameResolver.LookupFunctionsInNamespace(namespacePath); return(functions.Any()); }
public bool TryGetValidValue(TexlNode argNode, TexlBinding binding, out IExternalDataSource dsInfo) { Contracts.AssertValue(argNode); Contracts.AssertValue(binding); dsInfo = null; switch (argNode.Kind) { case NodeKind.FirstName: return(TryGetDsInfo(argNode.AsFirstName(), binding, out dsInfo)); case NodeKind.Call: return(TryGetDsInfo(argNode.AsCall(), binding, out dsInfo)); case NodeKind.DottedName: return(TryGetDsInfo(argNode.AsDottedName(), binding, out dsInfo)); case NodeKind.As: return(TryGetValidValue(argNode.AsAsNode().Left, binding, out dsInfo)); } return(false); }
internal override bool TryAddSuggestionsForNodeKind(IntellisenseData.IntellisenseData intellisenseData) { Contracts.AssertValue(intellisenseData); // For Error Kind, suggest top level values only in the context of a callNode and // ThisItemProperties only in the context of thisItem. TexlNode curNode = intellisenseData.CurNode; // Three methods that implement custom behavior here, one that adds suggestions before // top level suggestions are added, one after, and one to handle the case where there aren't // any top level suggestions to add. if (intellisenseData.AddSuggestionsBeforeTopLevelErrorNodeSuggestions()) { return(true); } if (!IntellisenseHelper.AddSuggestionsForTopLevel(intellisenseData, curNode)) { intellisenseData.AddAlternativeTopLevelSuggestionsForErrorNode(); } intellisenseData.AddSuggestionsAfterTopLevelErrorNodeSuggestions(); return(true); }
internal override bool TryAddSuggestionsForNodeKind(IntellisenseData.IntellisenseData intellisenseData) { Contracts.AssertValue(intellisenseData); TexlNode curNode = intellisenseData.CurNode; int cursorPos = intellisenseData.CursorPos; var tokenSpan = curNode.Token.Span; // Only suggest after record nodes if (cursorPos <= tokenSpan.Lim) { return(true); } if (IntellisenseHelper.CanSuggestAfterValue(cursorPos, intellisenseData.Script)) { // Verify that cursor is after a space after the current node's token. // Suggest binary keywords. IntellisenseHelper.AddSuggestionsForAfterValue(intellisenseData, DType.EmptyTable); } return(true); }
private bool TryGetValidSortOrderNode(DottedNameNode node, TexlBinding binding, out string sortOrder) { Contracts.AssertValue(node); Contracts.AssertValue(binding); sortOrder = ""; TexlNode lhsNode = node.Left; var orderEnum = lhsNode.AsFirstName(); if (orderEnum == null) { return(false); } // Verify order enum if (!VerifyFirstNameNodeIsValidSortOrderEnum(orderEnum, binding)) { return(false); } string order = node.Right.Name.Value; return(IsValidOrderString(order, out sortOrder)); }
private bool TryGetDSNodes(TexlBinding binding, TexlNode[] args, out IList<FirstNameNode> dsNodes) { dsNodes = new List<FirstNameNode>(); var count = args.Count(); for (int i = 2; i < count;) { TexlNode nodeArg = args[i]; IList<FirstNameNode> tmpDsNodes; if (ArgValidators.DataSourceArgNodeValidator.TryGetValidValue(nodeArg, binding, out tmpDsNodes)) { foreach (var node in tmpDsNodes) dsNodes.Add(node); } // If there are an odd number of args, the last arg also participates. i += 2; if (i == count) i--; } return dsNodes.Any(); }
public override bool CheckInvocation(TexlBinding binding, TexlNode[] args, DType[] argTypes, IErrorContainer errors, out DType returnType, out Dictionary <TexlNode, DType> nodeToCoercedTypeMap) { Contracts.AssertValue(args); Contracts.AssertAllValues(args); Contracts.AssertValue(argTypes); Contracts.Assert(args.Length == argTypes.Length); Contracts.AssertValue(errors); Contracts.Assert(MinArity <= args.Length && args.Length <= MaxArity); bool fValid = base.CheckInvocation(args, argTypes, errors, out returnType, out nodeToCoercedTypeMap); if (argTypes.Length == 2) { DType type0 = argTypes[0]; DType type1 = argTypes[1]; DType otherType = DType.Invalid; TexlNode otherArg = null; // At least one of the arguments has to be a table. if (type0.IsTable) { // Ensure we have a one-column table of numerics fValid &= CheckNumericColumnType(type0, args[0], errors, ref nodeToCoercedTypeMap); // Borrow the return type from the 1st arg returnType = type0; // Check arg1 below. otherArg = args[1]; otherType = type1; } else if (type1.IsTable) { // Ensure we have a one-column table of numerics fValid &= CheckNumericColumnType(type1, args[1], errors, ref nodeToCoercedTypeMap); // Since the 1st arg is not a table, make a new table return type *[Result:n] returnType = DType.CreateTable(new TypedName(DType.Number, OneColumnTableResultName)); // Check arg0 below. otherArg = args[0]; otherType = type0; } else { Contracts.Assert(returnType.IsTable); errors.EnsureError(DocumentErrorSeverity.Severe, args[0], TexlStrings.ErrTypeError); errors.EnsureError(DocumentErrorSeverity.Severe, args[1], TexlStrings.ErrTypeError); // Both args are invalid. No need to continue. return(false); } Contracts.Assert(otherType.IsValid); Contracts.AssertValue(otherArg); Contracts.Assert(returnType.IsTable); Contracts.Assert(!fValid || returnType.IsColumn); if (otherType.IsTable) { // Ensure we have a one-column table of numerics fValid &= CheckNumericColumnType(otherType, otherArg, errors, ref nodeToCoercedTypeMap); } else if (!DType.Number.Accepts(otherType)) { if (otherType.CoercesTo(DType.Number)) { CollectionUtils.Add(ref nodeToCoercedTypeMap, otherArg, DType.Number); } else { fValid = false; errors.EnsureError(DocumentErrorSeverity.Severe, otherArg, TexlStrings.ErrTypeError); } } } else { DType type0 = argTypes[0]; if (type0.IsTable) { // Ensure we have a one-column table of numerics fValid &= CheckNumericColumnType(type0, args[0], errors, ref nodeToCoercedTypeMap); // Borrow the return type from the 1st arg returnType = type0; } else { Contracts.Assert(returnType.IsTable); errors.EnsureError(DocumentErrorSeverity.Severe, args[0], TexlStrings.ErrTypeError); return(false); } } return(fValid); }
private bool IsArgTypeInconsequential(TexlNode arg) { Contracts.AssertValue(arg); Contracts.Assert(arg.Parent is ListNode); Contracts.Assert(arg.Parent.Parent is CallNode); Contracts.Assert(arg.Parent.Parent.AsCall().Head.Name == Name); CallNode call = arg.Parent.Parent.AsCall().VerifyValue(); // Pattern: OnSelect = If(cond, argT, argF) // Pattern: OnSelect = If(cond, arg1, cond, arg2, ..., argK, argF) // Pattern: OnSelect = If(cond, arg1, If(cond, argT, argF)) // Pattern: OnSelect = If(cond, arg1, If(cond, arg2, cond, arg3, ...)) // Pattern: OnSelect = If(cond, arg1, cond, If(cond, arg2, cond, arg3, ...), ...) // ...etc. CallNode ancestor = call; while (ancestor.Head.Name == Name) { if (ancestor.Parent == null && ancestor.Args.Children.Length > 0) { for (int i = 0; i < ancestor.Args.Children.Length; i += 2) { // If the given node is part of a condition arg of an outer If invocation, // then it's NOT inconsequential. Note that the very last arg to an If // is not a condition -- it's the "else" branch, hence the test below. if (i != ancestor.Args.Children.Length - 1 && arg.InTree(ancestor.Args.Children[i])) { return(false); } } return(true); } // Deal with the possibility that the ancestor may be contributing to a chain. // This also lets us cover the following patterns: // Pattern: OnSelect = X; If(cond, arg1, arg2); Y; Z // Pattern: OnSelect = X; If(cond, arg1;arg11;...;arg1k, arg2;arg21;...;arg2k); Y; Z // ...etc. VariadicOpNode chainNode; if ((chainNode = ancestor.Parent.AsVariadicOp()) != null && chainNode.Op == VariadicOp.Chain) { // Top-level chain in a behavior rule. if (chainNode.Parent == null) { return(true); } // A chain nested within a larger non-call structure. if (!(chainNode.Parent is ListNode) || !(chainNode.Parent.Parent is CallNode)) { return(false); } // Only the last chain segment is consequential. int numSegments = chainNode.Children.Length; if (numSegments > 0 && !arg.InTree(chainNode.Children[numSegments - 1])) { return(true); } // The node is in the last segment of a chain nested within a larger invocation. ancestor = chainNode.Parent.Parent.AsCall(); continue; } // Walk up the parent chain to the outer invocation. if (!(ancestor.Parent is ListNode) || !(ancestor.Parent.Parent is CallNode)) { return(false); } ancestor = ancestor.Parent.Parent.AsCall(); } // Exhausted all supported patterns. return(false); }
public override bool CheckInvocation(TexlBinding binding, TexlNode[] args, DType[] argTypes, IErrorContainer errors, out DType returnType, out Dictionary <TexlNode, DType> nodeToCoercedTypeMap) { Contracts.AssertValue(binding); Contracts.AssertValue(args); Contracts.AssertAllValues(args); Contracts.AssertValue(argTypes); Contracts.Assert(args.Length == argTypes.Length); Contracts.AssertValue(errors); Contracts.Assert(MinArity <= args.Length && args.Length <= MaxArity); int count = args.Length; nodeToCoercedTypeMap = null; // Check the predicates. bool fArgsValid = true; for (int i = 0; i < (count & ~1); i += 2) { bool withCoercion; fArgsValid &= CheckType(args[i], argTypes[i], DType.Boolean, errors, true, out withCoercion); if (withCoercion) { CollectionUtils.Add(ref nodeToCoercedTypeMap, args[i], DType.Boolean); } } DType type = ReturnType; // Are we on a behavior property? bool isBehavior = binding.IsBehavior; // Compute the result type by joining the types of all non-predicate args. Contracts.Assert(type == DType.Unknown); for (int i = 1; i < count;) { TexlNode nodeArg = args[i]; DType typeArg = argTypes[i]; if (typeArg.IsError) { errors.EnsureError(args[i], TexlStrings.ErrTypeError); } DType typeSuper = DType.Supertype(type, typeArg); if (!typeSuper.IsError) { type = typeSuper; } else if (type.Kind == DKind.Unknown) { type = typeSuper; fArgsValid = false; } else if (!type.IsError) { if (typeArg.CoercesTo(type)) { CollectionUtils.Add(ref nodeToCoercedTypeMap, nodeArg, type); } else if (!isBehavior || !IsArgTypeInconsequential(nodeArg)) { errors.EnsureError(DocumentErrorSeverity.Severe, nodeArg, TexlStrings.ErrBadType_ExpectedType_ProvidedType, type.GetKindString(), typeArg.GetKindString()); fArgsValid = false; } } else if (typeArg.Kind != DKind.Unknown) { type = typeArg; fArgsValid = false; } // If there are an odd number of args, the last arg also participates. i += 2; if (i == count) { i--; } } // Update the return type based on the specified invocation args. returnType = type; return(fArgsValid); }
public virtual bool IsOpSupportedByTable(OperationCapabilityMetadata metadata, TexlNode node, TexlBinding binding) { Contracts.AssertValue(metadata); Contracts.AssertValue(node); Contracts.AssertValue(binding); if (!metadata.IsBinaryOpInDelegationSupported(_binaryOp)) { SuggestDelegationHint(node, binding, TexlStrings.OpNotSupportedByClientSuggestionMessage_OpNotSupportedByClient, _binaryOp.ToString()); return(false); } if (!metadata.IsBinaryOpSupportedByTable(_binaryOp)) { SuggestDelegationHint(node, binding, TexlStrings.OpNotSupportedByServiceSuggestionMessage_OpNotSupportedByService, _binaryOp.ToString()); return(false); } return(true); }
private LazyList <string> PrettyBinary(string strOp, Precedence parentPrecedence, Precedence precLeft, Precedence precRight, TexlNode left, TexlNode right) { Contracts.AssertNonEmpty(strOp); return(ApplyPrecedence( parentPrecedence, precLeft, left.Accept(this, precLeft) .With(strOp) .With(right.Accept(this, precRight)))); }
// For left associative operators: precRight == precLeft + 1. private LazyList <string> PrettyBinary(string strOp, Precedence parentPrecedence, Precedence precLeft, TexlNode left, TexlNode right) { return(PrettyBinary(strOp, parentPrecedence, precLeft, precLeft + 1, left, right)); }
public virtual string GetRightToken(TexlNode leftNode, Identifier right) { return(right.Token.ToString()); }
// Verifies if given kind of node is supported by function delegation. private bool IsSupportedNode(TexlNode node, OperationCapabilityMetadata metadata, TexlBinding binding, IOpDelegationStrategy opDelStrategy, bool isRHSNode) { Contracts.AssertValue(node); Contracts.AssertValue(metadata); Contracts.AssertValue(binding); Contracts.AssertValue(opDelStrategy); if (!binding.IsRowScope(node)) { // Check whether this is - // 1) in operator delegation and // 2) it is verifying if RHS node is supported and // 3) it is not an async node and // 4) it is a single column table and // 5) metadata belongs to cds datasource that supports delegation of CdsIn // If this check fails, verify if it is simply a valid node.. // Eg of valid delegation functions - // Filter(Accounts, 'Account Name' in ["Foo", Bar"]) - Direct table use // Set(Names, ["Foo", Bar"]); Filter(Accounts, 'Account Name' in Names) - Using variable of type table // ClearCollect(Names, Accounts); Filter(Accounts, 'Account Name' in Names.'Account Name') - using column from collection. // This won't be delegated - Filter(Accounts, 'Account Name' in Accounts.'Account Name') as Accounts.'Account Name' is async. if ((binding.Document.Properties.EnabledFeatures.IsEnhancedDelegationEnabled && isRHSNode && (opDelStrategy as BinaryOpDelegationStrategy)?.Op == BinaryOp.In && !binding.IsAsync(node) && binding.GetType(node).IsTable&& binding.GetType(node).IsColumn&& metadata.IsDelegationSupportedByTable(DelegationCapability.CdsIn)) || IsValidNode(node, binding)) { return(true); } } switch (node.Kind) { case NodeKind.DottedName: { if (!opDelStrategy.IsOpSupportedByTable(metadata, node, binding)) { return(false); } var dottedNodeValStrategy = _function.GetDottedNameNodeDelegationStrategy(); return(dottedNodeValStrategy.IsValidDottedNameNode(node.AsDottedName(), binding, metadata, opDelStrategy)); } case NodeKind.Call: { if (!opDelStrategy.IsOpSupportedByTable(metadata, node, binding)) { return(false); } var cNodeValStrategy = _function.GetCallNodeDelegationStrategy(); return(cNodeValStrategy.IsValidCallNode(node.AsCall(), binding, metadata)); } case NodeKind.FirstName: { var firstNameNodeValStrategy = _function.GetFirstNameNodeDelegationStrategy(); return(firstNameNodeValStrategy.IsValidFirstNameNode(node.AsFirstName(), binding, opDelStrategy)); } case NodeKind.UnaryOp: { if (!opDelStrategy.IsOpSupportedByTable(metadata, node, binding)) { return(false); } var unaryopNode = node.AsUnaryOpLit(); IOpDelegationStrategy unaryOpNodeDelegationStrategy = _function.GetOpDelegationStrategy(unaryopNode.Op); return(unaryOpNodeDelegationStrategy.IsSupportedOpNode(unaryopNode, metadata, binding)); } case NodeKind.BinaryOp: { if (!opDelStrategy.IsOpSupportedByTable(metadata, node, binding)) { return(false); } var binaryOpNode = node.AsBinaryOp().VerifyValue(); opDelStrategy = _function.GetOpDelegationStrategy(binaryOpNode.Op, binaryOpNode); var binaryOpDelStrategy = (opDelStrategy as BinaryOpDelegationStrategy).VerifyValue(); Contracts.Assert(binaryOpNode.Op == binaryOpDelStrategy.Op); if (!opDelStrategy.IsSupportedOpNode(node, metadata, binding)) { SuggestDelegationHint(binaryOpNode, binding); return(false); } break; } default: { var kind = node.Kind; if (kind != NodeKind.BoolLit && kind != NodeKind.StrLit && kind != NodeKind.NumLit) { var telemetryMessage = string.Format("NodeKind {0} unsupported.", kind); SuggestDelegationHintAndAddTelemetryMessage(node, binding, telemetryMessage); return(false); } break; } } return(true); }