Ejemplo n.º 1
0
        private bool IsValidRowScopedDottedNameNode(DottedNameNode node, TexlBinding binding, OperationCapabilityMetadata metadata, out bool isRowScopedDelegationExempted)
        {
            Contracts.AssertValue(node);
            Contracts.AssertValue(binding);

            isRowScopedDelegationExempted = false;
            if (node.Left.Kind == NodeKind.FirstName &&
                binding.IsDelegationExempted(node.Left as FirstNameNode) &&
                binding.IsLambdaScoped(node.Left as FirstNameNode))
            {
                isRowScopedDelegationExempted = true;

                return(true);
            }

            if (node.Left.Kind == NodeKind.DottedName)
            {
                return(IsValidRowScopedDottedNameNode(node.Left.AsDottedName(), binding, metadata, out isRowScopedDelegationExempted));
            }

            if (node.Left.Kind == NodeKind.Call && binding.GetInfo(node.Left as CallNode).Function is AsTypeFunction)
            {
                return(IsValidCallNode(node.Left as CallNode, binding, metadata));
            }

            // We only allow dotted or firstname node on LHS for now, with exception of AsType.
            return(node.Left.Kind == NodeKind.FirstName);
        }
Ejemplo n.º 2
0
        public DottedNameInfo(DottedNameNode node, object data = null)
        {
            Contracts.AssertValue(node);
            Contracts.AssertValueOrNull(data);

            Node = node;
            Data = data;
        }
Ejemplo n.º 3
0
        public override void PostVisit(DottedNameNode node)
        {
            var argType = _txb.GetType(node);

            if (argType.Kind == DKind.ViewValue)
            {
                ContainsView = true;
            }
        }
            internal override bool TryAddSuggestionsForNodeKind(IntellisenseData.IntellisenseData intellisenseData)
            {
                Contracts.AssertValue(intellisenseData);

                TexlNode curNode   = intellisenseData.CurNode;
                int      cursorPos = intellisenseData.CursorPos;

                // Cursor position is after the dot (If it was before the dot FindNode would have returned the left node).
                Contracts.Assert(curNode.Token.IsDottedNamePunctuator);
                Contracts.Assert(curNode.Token.Span.Lim <= cursorPos);

                DottedNameNode dottedNameNode = curNode.CastDottedName();
                Identifier     ident          = dottedNameNode.Right;
                string         identName      = ident.Name;
                var            leftNode       = dottedNameNode.Left;
                DType          leftType       = intellisenseData.Binding.GetType(leftNode);

                intellisenseData.BeforeAddSuggestionsForDottedNameNode(leftNode);

                bool isOneColumnTable = leftType.IsColumn &&
                                        leftNode.Kind == NodeKind.DottedName &&
                                        leftType.Accepts(intellisenseData.Binding.GetType(((DottedNameNode)leftNode).Left));

                if (cursorPos < ident.Token.Span.Min)
                {
                    // Cursor position is before the identifier starts.
                    // i.e. "this.  |  Awards"
                    AddSuggestionsForLeftNodeScope(intellisenseData, leftNode, isOneColumnTable, leftType);
                }
                else if (cursorPos <= ident.Token.Span.Lim)
                {
                    // Cursor position is in the identifier.
                    // Suggest fields that don't need to be qualified.
                    // Identifiers can include open and close brackets and the Token.Span covers them.
                    // Get the matching string as a substring from the script so that the whitespace is preserved.
                    intellisenseData.SetMatchArea(ident.Token.Span.Min, cursorPos, ident.Token.Span.Lim - ident.Token.Span.Min);

                    if (!intellisenseData.Binding.ErrorContainer.HasErrors(dottedNameNode))
                    {
                        intellisenseData.BoundTo = identName;
                    }

                    AddSuggestionsForLeftNodeScope(intellisenseData, leftNode, isOneColumnTable, leftType);
                }
                else if (IntellisenseHelper.CanSuggestAfterValue(cursorPos, intellisenseData.Script))
                {
                    // Verify that cursor is after a space after the identifier.
                    // i.e. "this.Awards   |"
                    // Suggest binary operators.
                    IntellisenseHelper.AddSuggestionsForAfterValue(intellisenseData, intellisenseData.Binding.GetType(dottedNameNode));
                }

                return(true);
            }
Ejemplo n.º 5
0
        private bool TryGetEntityInfo(DottedNameNode dottedNameNode, TexlBinding binding, out IExpandInfo entityInfo)
        {
            Contracts.AssertValueOrNull(dottedNameNode);
            Contracts.AssertValue(binding);

            entityInfo = null;
            if (dottedNameNode == null || !binding.HasExpandInfo(dottedNameNode))
            {
                return(false);
            }

            return(binding.TryGetEntityInfo(dottedNameNode, out entityInfo));
        }
        private bool TryGetDsNode(DottedNameNode dottedNameNode, TexlBinding binding, out IList <FirstNameNode> dsNode)
        {
            Contracts.AssertValueOrNull(dottedNameNode);
            Contracts.AssertValue(binding);

            dsNode = null;
            if (dottedNameNode == null || !binding.HasExpandInfo(dottedNameNode))
            {
                return(false);
            }

            return(TryGetValidValue(dottedNameNode.Left, binding, out dsNode));
        }
Ejemplo n.º 7
0
        private static PythonNode Wrap(DottedName stmt, PythonNode parent)
        {
            var result = new DottedNameNode(stmt)
            {
                Parent = parent
            };

            if (stmt.Names != null)
            {
                result.Value = stmt.Names;
            }
            //result.AddChild(Wrap(stmt.Root));
            return(result);
        }
Ejemplo n.º 8
0
        public override bool PreVisit(DottedNameNode node)
        {
            Contracts.AssertValue(node);
            Contracts.Assert(node.Token.IsDottedNamePunctuator);

            // Cursor is before the dot.
            if (_cursorPosition <= node.Token.Span.Min)
            {
                node.Left.Accept(this);
                return(false);
            }

            // Cursor is in the dot or the right identifier.
            _result = node;
            return(false);
        }
Ejemplo n.º 9
0
        // Parse a namespace-qualified invocation, e.g. Facebook.GetFriends()
        private CallNode ParseInvocationWithNamespace(DottedNameNode dotted)
        {
            Contracts.Assert(dotted.HasPossibleNamespaceQualifier);

            DPath path = dotted.ToDPath();

            Contracts.Assert(path.IsValid);
            Contracts.Assert(!path.IsRoot);
            Contracts.Assert(!path.Parent.IsRoot);

            Identifier head = new Identifier(path.Parent, null, dotted.Right.Token);

            Contracts.Assert(_curs.TidCur == TokKind.ParenOpen);

            return(ParseInvocation(head, ParseTrivia(), dotted));
        }
        private bool TryGetDsInfo(DottedNameNode dottedNameNode, TexlBinding binding, out IExternalDataSource dsInfo)
        {
            Contracts.AssertValueOrNull(dottedNameNode);
            Contracts.AssertValue(binding);

            dsInfo = null;
            if (dottedNameNode == null || !binding.HasExpandInfo(dottedNameNode))
            {
                return(false);
            }

            IExpandInfo info;

            binding.TryGetEntityInfo(dottedNameNode, out info).Verify();
            dsInfo = info.ParentDataSource;
            return(true);
        }
Ejemplo n.º 11
0
        internal static bool TryConvertNodeToDPath(TexlBinding binding, DottedNameNode node, out DPath path)
        {
            Contracts.AssertValue(binding);
            Contracts.AssertValue(node);

            if (node.Left is DottedNameNode && TryConvertNodeToDPath(binding, node.Left as DottedNameNode, out path))
            {
                DName  rightNodeName = node.Right.Name;
                string possibleRename;
                if (binding.TryGetReplacedIdentName(node.Right, out possibleRename))
                {
                    rightNodeName = new DName(possibleRename);
                }

                path = path.Append(rightNodeName);
                return(true);
            }
            else if (node.Left is FirstNameNode firstName)
            {
                if (binding.GetInfo(firstName).Kind == BindKind.LambdaFullRecord)
                {
                    DName rightNodeName = node.Right.Name;
                    if (binding.TryGetReplacedIdentName(node.Right, out string rename))
                    {
                        rightNodeName = new DName(rename);
                    }

                    path = DPath.Root.Append(rightNodeName);
                    return(true);
                }

                // Check if the access was renamed:
                DName  leftNodeName = firstName.Ident.Name;
                string possibleRename;
                if (binding.TryGetReplacedIdentName(firstName.Ident, out possibleRename))
                {
                    leftNodeName = new DName(possibleRename);
                }

                path = DPath.Root.Append(leftNodeName).Append(node.Right.Name);
                return(true);
            }

            path = DPath.Root;
            return(false);
        }
Ejemplo n.º 12
0
        public override LazyList <string> Visit(DottedNameNode node, Precedence parentPrecedence)
        {
            Contracts.AssertValue(node);

            string separator = TexlParser.GetTokString(node.Token.Kind);

            var values = node.Left.Accept(this, Precedence.Primary);

            values = values.With(separator);
            if (node.Right.AtToken != null)
            {
                values = values.With(TexlLexer.PunctuatorAt);
            }
            values = values.With(GetRightToken(node.Left, node.Right));
            if (node.UsesBracket)
            {
                values = values.With(TexlLexer.PunctuatorBracketClose);
            }

            return(ApplyPrecedence(parentPrecedence, Precedence.Primary, values));
        }
Ejemplo n.º 13
0
        public override LazyList <string> Visit(DottedNameNode node, Precedence parentPrecedence)
        {
            Contracts.AssertValue(node);

            string separator = TexlParser.GetTokString(node.Token.Kind);

            var values = node.Left.Accept(this, Precedence.Primary);

            values = values.With(separator);
            if (node.Right.AtToken != null || node.UsesBracket)
            {
                values = values.With("#$disambiguation$#");
            }
            else
            {
                values = values.With(node.RightNode?.Accept(this, parentPrecedence) ??
                                     LazyList <string> .Of("#$righthandid$#"));
            }

            return(ApplyPrecedence(parentPrecedence, Precedence.Primary, values));
        }
Ejemplo n.º 14
0
        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));
        }
Ejemplo n.º 15
0
 public override Result Visit(DottedNameNode node, Context context)
 {
     return(Default);
 }
Ejemplo n.º 16
0
        public override LazyList <string> Visit(DottedNameNode node, Context context)
        {
            Contracts.AssertValue(node);

            return(Basic(node, context));
        }
Ejemplo n.º 17
0
 public override Result Visit(DottedNameNode node, Context context)
 {
     return(node.Left.Accept(this, context));
 }
Ejemplo n.º 18
0
 // Visit methods for non-leaf node types.
 // If PreVisit returns true, the children are visited and PostVisit is called.
 public abstract Result Visit(DottedNameNode node, Context context);
Ejemplo n.º 19
0
            public override void PostVisit(DottedNameNode node)
            {
                Contracts.AssertValue(node);

                DType                  lhsType = _txb.GetType(node.Left);
                DType                  typeRhs = DType.Invalid;
                DName                  nameRhs = node.Right.Name;
                FirstNameInfo          firstNameInfo;
                FirstNameNode          firstNameNode;
                IExternalTableMetadata tableMetadata;
                DType                  nodeType = DType.Unknown;

                if (node.Left.Kind != NodeKind.FirstName &&
                    node.Left.Kind != NodeKind.DottedName)
                {
                    SetDottedNameError(node, TexlStrings.ErrInvalidName);
                    return;
                }

                nameRhs = GetLogicalNodeNameAndUpdateDisplayNames(lhsType, node.Right);

                if (!lhsType.TryGetType(nameRhs, out typeRhs))
                {
                    SetDottedNameError(node, TexlStrings.ErrInvalidName);
                    return;
                }

                // There are two cases:
                // 1. RHS could be an option set.
                // 2. RHS could be a data entity.
                // 3. RHS could be a column name and LHS would be a datasource.
                if (typeRhs.IsOptionSet)
                {
                    nodeType = typeRhs;
                }
                else if (typeRhs.IsExpandEntity)
                {
                    var entityInfo = typeRhs.ExpandInfo;
                    Contracts.AssertValue(entityInfo);

                    string entityPath = string.Empty;
                    if (lhsType.HasExpandInfo)
                    {
                        entityPath = lhsType.ExpandInfo.ExpandPath.ToString();
                    }

                    DType expandedEntityType = GetExpandedEntityType(typeRhs, entityPath);

                    var parentDataSource = entityInfo.ParentDataSource;
                    var metadata         = new DataTableMetadata(parentDataSource.Name, parentDataSource.Name);
                    nodeType = DType.CreateMetadataType(new DataColumnMetadata(typeRhs.ExpandInfo.Name, expandedEntityType, metadata));
                }
                else if ((firstNameNode = node.Left.AsFirstName()) != null && (firstNameInfo = _txb.GetInfo(firstNameNode)) != null)
                {
                    var tabularDataSourceInfo = firstNameInfo.Data as IExternalTabularDataSource;
                    tableMetadata = tabularDataSourceInfo?.TableMetadata;
                    if (tableMetadata == null || !tableMetadata.TryGetColumn(nameRhs.Value, out var columnMetadata) || !IsColumnMultiChoice(columnMetadata))
                    {
                        SetDottedNameError(node, TexlStrings.ErrInvalidName);
                        return;
                    }

                    var metadata = new DataTableMetadata(tabularDataSourceInfo.Name, tableMetadata.DisplayName);
                    nodeType = DType.CreateMetadataType(new DataColumnMetadata(columnMetadata, metadata));
                }
                else
                {
                    SetDottedNameError(node, TexlStrings.ErrInvalidName);
                    return;
                }

                Contracts.AssertValid(nodeType);

                _txb.SetType(node, nodeType);
                _txb.SetInfo(node, new DottedNameInfo(node));
            }
Ejemplo n.º 20
0
        public bool IsValidDottedNameNode(DottedNameNode node, TexlBinding binding, OperationCapabilityMetadata metadata, IOpDelegationStrategy opDelStrategy)
        {
            Contracts.AssertValue(node);
            Contracts.AssertValue(binding);
            Contracts.AssertValueOrNull(opDelStrategy);

            var isRowScoped = binding.IsRowScope(node);

            if (!isRowScoped)
            {
                return(IsValidNode(node, binding));
            }

            bool isRowScopedDelegationExempted;

            if (!IsValidRowScopedDottedNameNode(node, binding, metadata, out isRowScopedDelegationExempted))
            {
                var telemetryMessage = string.Format("Kind:{0}, isRowScoped:{1}",
                                                     node.Kind, isRowScoped);

                SuggestDelegationHintAndAddTelemetryMessage(node, binding, telemetryMessage);
                return(false);
            }

            if (isRowScopedDelegationExempted)
            {
                binding.SetBlockScopedConstantNode(node);
                return(true);
            }

            if (binding.TryGetFullRecordRowScopeAccessInfo(node, out var firstNameInfo))
            {
                // This means that this row scoped field is from some parent scope which is non-delegatable. That should deny delegation at that point.
                // For this scope, this means that value will be provided from some other source.
                // For example, AddColumns(CDS As Left, "Column1", LookUp(CDS1, Left.Name in FirstName))
                // CDS - *[Name:s], CDS1 - *[FirstName:s]
                if (GetCapabilityMetadata(firstNameInfo) == null)
                {
                    return(true);
                }
            }

            if (!binding.GetType(node.Left).HasExpandInfo)
            {
                DPath columnPath;
                if (!BinderUtils.TryConvertNodeToDPath(binding, node, out columnPath) || !metadata.IsDelegationSupportedByColumn(columnPath, _function.FunctionDelegationCapability))
                {
                    var safeColumnName = CharacterUtils.MakeSafeForFormatString(columnPath.ToDottedSyntax());
                    var message        = string.Format(StringResources.Get(TexlStrings.OpNotSupportedByColumnSuggestionMessage_OpNotSupportedByColumn), safeColumnName);
                    SuggestDelegationHintAndAddTelemetryMessage(node, binding, message, TexlStrings.OpNotSupportedByColumnSuggestionMessage_OpNotSupportedByColumn, safeColumnName);
                    TrackingProvider.Instance.SetDelegationTrackerStatus(DelegationStatus.NoDelSupportByColumn, node, binding, _function, DelegationTelemetryInfo.CreateNoDelSupportByColumnTelemetryInfo(columnPath.ToDottedSyntax()));
                    return(false);
                }

                // If there is any operator applied on this node then check if column supports operation.
                return(opDelStrategy?.IsOpSupportedByColumn(metadata, node, columnPath, binding) ?? true);
            }

            // If there is an entity reference then we need to do additional verification.
            IExpandInfo info           = binding.GetType(node.Left).ExpandInfo.VerifyValue();
            var         dataSourceInfo = info.ParentDataSource;

            IDataEntityMetadata entityMetadata;

            if (!dataSourceInfo.DataEntityMetadataProvider.TryGetEntityMetadata(info.Identity, out entityMetadata))
            {
                var telemetryMessage = string.Format("Kind:{0}, isRowScoped:{1}, no metadata found for entity {2}",
                                                     node.Kind, isRowScoped, CharacterUtils.MakeSafeForFormatString(info.Identity));

                SuggestDelegationHintAndAddTelemetryMessage(node, binding, telemetryMessage);
                return(false);
            }

            OperationCapabilityMetadata entityCapabilityMetadata = GetScopedOperationCapabilityMetadata(entityMetadata.DelegationMetadata);
            string maybeLogicalName;
            DName  columnName = node.Right.Name;

            if (entityMetadata.DisplayNameMapping.TryGetFromSecond(node.Right.Name.Value, out maybeLogicalName))
            {
                columnName = new DName(maybeLogicalName);
            }

            var entityColumnPath = DPath.Root.Append(columnName);

            if (!entityCapabilityMetadata.IsDelegationSupportedByColumn(entityColumnPath, _function.FunctionDelegationCapability))
            {
                var safeColumnName = CharacterUtils.MakeSafeForFormatString(columnName.Value);
                var message        = string.Format(StringResources.Get(TexlStrings.OpNotSupportedByColumnSuggestionMessage_OpNotSupportedByColumn), safeColumnName);
                SuggestDelegationHintAndAddTelemetryMessage(node, binding, message, TexlStrings.OpNotSupportedByColumnSuggestionMessage_OpNotSupportedByColumn, safeColumnName);
                TrackingProvider.Instance.SetDelegationTrackerStatus(DelegationStatus.NoDelSupportByColumn, node, binding, _function, DelegationTelemetryInfo.CreateNoDelSupportByColumnTelemetryInfo(columnName));
                return(false);
            }

            // If there is any operator applied on this node then check if column supports operation.
            return(opDelStrategy?.IsOpSupportedByColumn(entityCapabilityMetadata, node, entityColumnPath, binding) ?? true);
        }
Ejemplo n.º 21
0
 public abstract void PostVisit(DottedNameNode node);
Ejemplo n.º 22
0
 // Visit methods for non-leaf node types.
 // If PreVisit returns true, the children are visited and PostVisit is called.
 public virtual bool PreVisit(DottedNameNode node)
 {
     return(true);
 }
Ejemplo n.º 23
0
 public override void PostVisit(DottedNameNode node)
 {
 }
Ejemplo n.º 24
0
        // Parses the next (maximal) expression with precedence >= precMin.
        private TexlNode ParseExpr(Precedence precMin, TexlNode node = null)
        {
            // ParseOperand may accept PrefixUnary and higher, so ParseExpr should never be called
            // with precMin > Precedence.PrefixUnary - it will not correctly handle those cases.
            Contracts.Assert(Precedence.None <= precMin && precMin <= Precedence.PrefixUnary);

            try
            {
                // The parser is recursive. Deeply nested invocations (over 200 deep) and other
                // intentionally miscrafted rules can throw it off, causing stack overflows.
                // Ensure the product doesn't crash in such situations, but instead post
                // corresponding parse errors.
                if (node == null)
                {
                    if (++_depth > _maxAllowedExpressionDepth)
                    {
                        return(CreateError(_curs.TokMove(), TexlStrings.ErrRuleNestedTooDeeply));
                    }

                    // Get the left operand.
                    node = ParseOperand();
                }

                // Process operators and right operands as long as the precedence bound is satisfied.
                for (;;)
                {
                    var leftTrivia = ParseTrivia();
                    Contracts.AssertValue(node);
                    Token       tok;
                    TexlNode    right;
                    Identifier  identifier;
                    ITexlSource rightTrivia;
                    switch (_curs.TidCur)
                    {
                    case TokKind.PercentSign:
                        Contracts.Assert(precMin <= Precedence.PostfixUnary);
                        tok  = _curs.TokMove();
                        node = new UnaryOpNode(
                            ref _idNext,
                            tok,
                            new SourceList(new NodeSource(node), new TokenSource(tok)),
                            UnaryOp.Percent,
                            node);
                        break;

                    case TokKind.Dot:
                    case TokKind.Bang:
                        Contracts.Assert(precMin <= Precedence.Primary);
                        DottedNameNode leftDotted;
                        if ((leftDotted = node as DottedNameNode) != null && leftDotted.Token.Kind != _curs.TidCur && leftDotted.Token.Kind != TokKind.BracketOpen)
                        {
                            // Can't mix and match separators. E.g. A.B!C is invalid.
                            goto case TokKind.False;
                        }
                        tok         = _curs.TokMove();
                        rightTrivia = ParseTrivia();
                        identifier  = ParseIdentifier();
                        node        = new DottedNameNode(
                            ref _idNext,
                            tok,
                            new SourceList(
                                new NodeSource(node),
                                new TokenSource(tok),
                                new SpreadSource(rightTrivia),
                                new IdentifierSource(identifier)),
                            node,
                            identifier,
                            null);
                        if (node.Depth > _maxAllowedExpressionDepth)
                        {
                            return(CreateError(node.Token, TexlStrings.ErrRuleNestedTooDeeply));
                        }
                        break;

                    case TokKind.Caret:
                        Contracts.Assert(precMin <= Precedence.Power);
                        node = ParseBinary(node, leftTrivia, BinaryOp.Power, Precedence.PrefixUnary);
                        break;

                    case TokKind.Mul:
                        if (precMin > Precedence.Mul)
                        {
                            goto default;
                        }
                        node = ParseBinary(node, leftTrivia, BinaryOp.Mul, Precedence.Mul + 1);
                        break;

                    case TokKind.Div:
                        if (precMin > Precedence.Mul)
                        {
                            goto default;
                        }
                        tok         = _curs.TokMove();
                        rightTrivia = ParseTrivia();
                        right       = ParseExpr(Precedence.Mul + 1);
                        node        = MakeBinary(BinaryOp.Div, node, leftTrivia, tok, rightTrivia, right);
                        break;

                    case TokKind.Sub:
                        if (precMin > Precedence.Add)
                        {
                            goto default;
                        }
                        tok         = _curs.TokMove();
                        rightTrivia = ParseTrivia();
                        right       = ParseExpr(Precedence.Add + 1);
                        right       = new UnaryOpNode(ref _idNext, tok, right.SourceList, UnaryOp.Minus, right);
                        node        = MakeBinary(BinaryOp.Add, node, leftTrivia, tok, rightTrivia, right);
                        break;

                    case TokKind.Add:
                        if (precMin > Precedence.Add)
                        {
                            goto default;
                        }
                        node = ParseBinary(node, leftTrivia, BinaryOp.Add, Precedence.Add + 1);
                        break;

                    case TokKind.Ampersand:
                        if (precMin > Precedence.Concat)
                        {
                            goto default;
                        }
                        node = ParseBinary(node, leftTrivia, BinaryOp.Concat, Precedence.Concat + 1);
                        break;

                    case TokKind.KeyAnd:
                    case TokKind.And:
                        if (precMin > Precedence.And)
                        {
                            goto default;
                        }
                        node = ParseBinary(node, leftTrivia, BinaryOp.And, Precedence.And + 1);
                        break;

                    case TokKind.KeyOr:
                    case TokKind.Or:
                        if (precMin > Precedence.Or)
                        {
                            goto default;
                        }
                        node = ParseBinary(node, leftTrivia, BinaryOp.Or, Precedence.Or + 1);
                        break;

                    // Comparison operators
                    // expr = expr
                    // expr <> expr
                    case TokKind.Equ:
                        if (precMin > Precedence.Compare)
                        {
                            goto default;
                        }
                        node = ParseBinary(node, leftTrivia, BinaryOp.Equal, Precedence.Compare + 1);
                        break;

                    case TokKind.LssGrt:
                        if (precMin > Precedence.Compare)
                        {
                            goto default;
                        }
                        node = ParseBinary(node, leftTrivia, BinaryOp.NotEqual, Precedence.Compare + 1);
                        break;

                    // expr < expr
                    case TokKind.Lss:
                        if (precMin > Precedence.Compare)
                        {
                            goto default;
                        }
                        node = ParseBinary(node, leftTrivia, BinaryOp.Less, Precedence.Compare + 1);
                        break;

                    // expr <= expr
                    case TokKind.LssEqu:
                        if (precMin > Precedence.Compare)
                        {
                            goto default;
                        }
                        node = ParseBinary(node, leftTrivia, BinaryOp.LessEqual, Precedence.Compare + 1);
                        break;

                    // expr > expr
                    case TokKind.Grt:
                        if (precMin > Precedence.Compare)
                        {
                            goto default;
                        }
                        node = ParseBinary(node, leftTrivia, BinaryOp.Greater, Precedence.Compare + 1);
                        break;

                    // expr >= expr
                    case TokKind.GrtEqu:
                        if (precMin > Precedence.Compare)
                        {
                            goto default;
                        }
                        node = ParseBinary(node, leftTrivia, BinaryOp.GreaterEqual, Precedence.Compare + 1);
                        break;

                    case TokKind.Ident:
                    case TokKind.NumLit:
                    case TokKind.StrLit:
                    case TokKind.True:
                    case TokKind.False:
                        PostError(_curs.TokCur, TexlStrings.ErrOperatorExpected);
                        tok         = _curs.TokMove();
                        rightTrivia = ParseTrivia();
                        right       = ParseExpr(Precedence.Error);
                        node        = MakeBinary(BinaryOp.Error, node, leftTrivia, tok, rightTrivia, right);
                        break;

                    case TokKind.ParenOpen:
                        DottedNameNode dotted;
                        if ((dotted = node as DottedNameNode) == null ||
                            !dotted.HasPossibleNamespaceQualifier)
                        {
                            goto default;
                        }
                        node = ParseInvocationWithNamespace(dotted);
                        break;

                    case TokKind.In:
                        if (precMin > Precedence.In)
                        {
                            goto default;
                        }
                        node = ParseBinary(node, leftTrivia, BinaryOp.In, Precedence.In + 1);
                        break;

                    case TokKind.Exactin:
                        if (precMin > Precedence.In)
                        {
                            goto default;
                        }
                        node = ParseBinary(node, leftTrivia, BinaryOp.Exactin, Precedence.In + 1);
                        break;


                    case TokKind.As:
                        if (precMin > Precedence.As)
                        {
                            goto default;
                        }
                        node = ParseAs(node, leftTrivia);
                        break;

                    case TokKind.Semicolon:
                        // Only allow this when expression chaining is enabled (e.g. in behavior rules).
                        if ((_flags & Flags.EnableExpressionChaining) == 0)
                        {
                            goto case TokKind.False;
                        }
                        if (precMin > Precedence.None)
                        {
                            goto default;
                        }
                        node = ParseExprChain(node, leftTrivia);
                        break;

                    case TokKind.BracketOpen:
                        // Note we explicitly forbid [@foo][@bar], and also A!B!C[@foo], since these are syntactically nonsensical at the moment.
                        FirstNameNode first;
                        if ((first = node as FirstNameNode) == null || first.Ident.AtToken != null || _curs.TidPeek() != TokKind.At)
                        {
                            goto default;
                        }
                        node = ParseScopeField(first);
                        break;

                    case TokKind.Comment:
                        Contracts.Assert(false, "A stray comment was found");
                        _curs.TokMove();
                        return(node);

                    case TokKind.Eof:
                        if (_after == null)
                        {
                            _after = new SourceList(leftTrivia);
                        }
                        else
                        {
                            _after = new SourceList(new SpreadSource(_after.Sources), new SpreadSource(leftTrivia));
                        }
                        return(node);

                    default:
                        AddExtraTrivia(leftTrivia);
                        return(node);
                    }
                }
            }
            finally
            {
                --_depth;
            }
        }