protected virtual void CheckForIndentStyleMismatch(UString indent1, UString indent2, Token next) { int common = System.Math.Min(indent1.Length, indent2.Length); indent1 = indent1.Substring(0, common); indent2 = indent2.Substring(0, common); if (!indent1.Equals(indent2)) { ErrorSink.Write(Severity.Warning, IndexToMsgContext(next), "Indentation style changed on this line from {0} to {1}", Les2Printer.PrintLiteral(indent1.ToString()), Les2Printer.PrintLiteral(indent2.ToString())); } }
void GetPatternComponents(LNode pattern, out LNode varBinding, out bool refExistingVar, out LNode cmpExpr, out LNode isType, out LNode inRange, out VList <LNode> subPatterns, out VList <LNode> conditions) { // Here's a typical pattern (case expr): // is Shape(ShapeType.Circle, ref size, Location: p is Point<int>(x, y)): // When there is an arg list, we decode its Target and return the args. // // The caller is in charge of stripping out "Property:" prefix, if any, // so the most complex pattern that this method considers is something // like `(expr is Type in Range)(subPatterns) && conds` where `expr` is // a varName or $varName to deconstruct, or some expression to test for // equality. Assuming it's an equality test, the output will be // // varBinding = null // refExistingVar = false // cmpExpr = quote(expr); // isType = quote(Type); // inRange = quote(Range); // conds will have "conds" pushed to the front. // bool haveSubPatterns = false; subPatterns = VList <LNode> .Empty; refExistingVar = pattern.AttrNamed(S.Ref) != null; // First, look for "pattern && condition" conditions = VList <LNode> .Empty; while (pattern.Calls(S.And, 2)) { conditions.Add(pattern.Args.Last); pattern = pattern.Args[0]; } LNode cmpExprOrBinding = null; varBinding = cmpExpr = isType = inRange = null; // Now decode the expression. Use three passes, each of which decodes // an "outer" layer such as A is B, A in B, or expr(args). Since you // can combine these operators, we may need multiple passes (e.g. // "X is T in R" and "X in R is T" are equivalent), and keep in mind // that operator trees like "A in B" are nearly identical to prefix- // calls like "foo(A, B)" except for the call target and the `BaseStyle`. for (int pass = 1; pass <= 3; pass++) { LNode inRange2 = inRange, isType2 = isType; { LNode patternL; if (pattern.Calls(CodeSymbols.In, 2) && (patternL = pattern.Args[0]) != null && (inRange = pattern.Args[1]) != null || pattern.Calls((Symbol)"in", 2) && (patternL = pattern.Args[0]) != null && (inRange = pattern.Args[1]) != null) { pattern = patternL; if (inRange2 != null) { _context.Sink.Error(inRange2, "match-case does not support multiple 'in' operators"); } } else if (pattern.Calls(CodeSymbols.Is, 2) && (cmpExprOrBinding = pattern.Args[0]) != null && (isType = pattern.Args[1]) != null || pattern.Calls((Symbol)"is", 2) && (cmpExprOrBinding = pattern.Args[0]) != null && (isType = pattern.Args[1]) != null) { pattern = cmpExprOrBinding; if (isType2 != null) { _context.Sink.Error(isType2, "match-case does not support multiple 'is' operators"); } } else if (pattern.Calls(CodeSymbols.Is, 1) && (isType = pattern.Args[0]) != null || pattern.Calls((Symbol)"is", 1) && (isType = pattern.Args[0]) != null) { if (isType2 != null) { _context.Sink.Error(isType2, "match-case does not support multiple 'is' operators"); } goto doneAnalysis; } else if (pattern.Calls(CodeSymbols.DotDotDot, 2) || pattern.Calls(CodeSymbols.DotDot, 2) || pattern.Calls(CodeSymbols.DotDotDot, 1) || pattern.Calls(CodeSymbols.DotDot, 1)) { inRange = pattern; goto doneAnalysis; } else if (pattern.Calls(CodeSymbols.Tuple)) { subPatterns = pattern.Args; cmpExprOrBinding = null; } else { // It's very tempting to detect NodeStyle.PrefixNotation to distinguish, // say, A.B<C> from id(A, B, C), but I'm reluctant to do so. BaseStyle // is by convention "unsemantic" and not guaranteed to be preserved // across serializations or supported the same way by different parsers. // So instead of asking "is this in PrefixNotation?" I ask "does the // target appear to be a normal identifier?" LNode target = pattern.Target; if (!haveSubPatterns && pattern.IsCall && (!target.IsId || target.AttrNamed(S.TriviaInParens) != null || (!target.HasSpecialName && Les2Printer.IsNormalIdentifier(target.Name))) ) { haveSubPatterns = true; subPatterns = pattern.Args; pattern = pattern.Target; } else { cmpExprOrBinding = pattern; } } } } doneAnalysis: if (cmpExprOrBinding != null) { if (cmpExprOrBinding.Calls(S.Substitute, 1)) { varBinding = cmpExprOrBinding[0]; } else if (refExistingVar) { varBinding = cmpExprOrBinding; } else if ((varBinding ?? cmpExprOrBinding).IsIdNamed(__)) { cmpExprOrBinding = varBinding = null; } // Originally a plain identifier would be a binding, like $identifier //if (cmpExprOrBinding.IsId && cmpExprOrBinding.AttrNamed(S.TriviaInParens) == null) // varBinding = cmpExprOrBinding; if (varBinding != null) { if (varBinding.AttrNamed(S.Ref) != null) { refExistingVar = true; varBinding = varBinding.WithoutAttrs(); } if (!varBinding.IsId) { _context.Sink.Error(varBinding, "Invalid variable name in match-case: {0}", varBinding); varBinding = null; } } if (varBinding == null) { cmpExpr = cmpExprOrBinding; } } if (refExistingVar && varBinding == null) { refExistingVar = false; var got = cmpExprOrBinding ?? pattern; _context.Sink.Warning(got, "'ref' expected a variable name (got `{0}`)", got); } }