private static CsIdentifier parseExpressionIdentifier(TokenJar tok, ref int i) { var startIndex = tok[i].StartIndex; // Check if this can be parsed as a type identifier. If it can't, don't throw; if it failed because of a malformed generic type parameter, it could still be a less-than operator. CsTypeName ty; var j = i; try { ty = parseTypeName(tok, ref j, typeIdentifierFlags.AllowKeywords | typeIdentifierFlags.DontAllowDot).Item1; } catch (ParseException) { goto afterTy; } // Since we didn't allow dot and we didn't allow arrays, nullables or pointers, we should get a simple name if (!(ty is CsConcreteTypeName) || ((CsConcreteTypeName) ty).Parts.Count != 1) throw new ParseException("Unexpected internal error: Expected simple name, received '{0}'.".Fmt(ty), i); var simpleName = ((CsConcreteTypeName) ty).Parts[0]; // Special case: only accept an identifier with generic type parameters if it has one of a special set of tokens following it; otherwise, reject it so that it is parsed as a less-than operator // (this is so that a generic method group expression is correctly parsed, but two less-than/greater-than or less-than/shift-right expressions are not mistaken for a generic method group expression) if (!simpleName.EndsWithGenerics || (tok.IndexExists(j) && tok[j].Type == TokenType.Builtin && _genericMethodGroupMagicalTokens.Contains(tok[j].TokenStr))) { i = j; return simpleName; } afterTy: var ret = new CsNameIdentifier { StartIndex = startIndex, EndIndex = tok[i].EndIndex, Name = tok[i].Identifier("Identifier expected.") }; i++; return ret; }
private static Tuple<CsTypeName, bool> parseTypeName(TokenJar tok, ref int i, typeIdentifierFlags flags, bool tryNotToThrow = false) { var ty = new CsConcreteTypeName { StartIndex = tok[i].StartIndex }; if (tok[i].IsIdentifier("global") && tok[i + 1].IsBuiltin("::")) { ty.HasGlobal = true; i += 2; } var j = i; bool onShr = false; while (true) { CsIdentifier partAbstract; if (tok[j].Type == TokenType.Builtin && (flags & typeIdentifierFlags.AllowKeywords) == typeIdentifierFlags.AllowKeywords && builtinTypes.Contains(tok[j].TokenStr)) partAbstract = new CsKeywordIdentifier { StartIndex = tok[j].StartIndex, EndIndex = tok[j].EndIndex, Keyword = tok[j].TokenStr }; else if (ty.Parts.Count > 0 && (flags & typeIdentifierFlags.Lenient) != 0 && tok[j].Type != TokenType.Identifier) { ty.EndIndex = tok[j].EndIndex; return Tuple.Create((CsTypeName) ty, false); } else if (tok[j].Type != TokenType.Identifier && tryNotToThrow) return null; else partAbstract = new CsNameIdentifier { StartIndex = tok[j].StartIndex, EndIndex = tok[j].EndIndex, Name = tok[j].Identifier("Type expected.") }; j++; ty.Parts.Add(partAbstract); i = j; if (tok[j].IsBuiltin("<")) { if (!(partAbstract is CsNameIdentifier)) throw new ParseException("'{0}' cannot have generic arguments.".Fmt(partAbstract.ToString()), tok[j].StartIndex, ty); var part = (CsNameIdentifier) partAbstract; part.GenericTypeArguments = new List<CsTypeName>(); j++; if ((flags & typeIdentifierFlags.AllowEmptyGenerics) != 0 && (tok[j].IsBuiltin(",") || tok[j].IsBuiltin(">"))) { part.GenericTypeArguments.Add(new CsEmptyGenericParameter { StartIndex = tok[j].StartIndex, EndIndex = tok[j].StartIndex }); while (tok[j].IsBuiltin(",")) { j++; part.GenericTypeArguments.Add(new CsEmptyGenericParameter { StartIndex = tok[j].StartIndex, EndIndex = tok[j].StartIndex }); } if (!tok[j].IsBuiltin(">")) throw new ParseException("',' or '>' expected.", tok[j].StartIndex, ty); j++; } else { try { var result = parseTypeName(tok, ref j, (flags & typeIdentifierFlags.Lenient) | typeIdentifierFlags.AllowNullablesAndPointers | typeIdentifierFlags.AllowArrays | typeIdentifierFlags.AllowKeywords | typeIdentifierFlags.AllowShr); part.GenericTypeArguments.Add(result.Item1); while (tok[j].IsBuiltin(",")) { j++; result = parseTypeName(tok, ref j, (flags & typeIdentifierFlags.Lenient) | typeIdentifierFlags.AllowNullablesAndPointers | typeIdentifierFlags.AllowArrays | typeIdentifierFlags.AllowKeywords | typeIdentifierFlags.AllowShr); part.GenericTypeArguments.Add(result.Item1); } if (result.Item2) // We're definitely on a ">>" token j++; else { if (tok[j].IsBuiltin(">")) j++; else if (tok[j].IsBuiltin(">>") && (flags & typeIdentifierFlags.AllowShr) != 0) onShr = true; else throw new ParseException("',' or '>' expected. (3)", tok[j].StartIndex, ty); } } catch (ParseException) { if ((flags & typeIdentifierFlags.Lenient) != 0) { part.GenericTypeArguments = null; return Tuple.Create((CsTypeName) ty, false); } throw; } } } i = j; if (!onShr && (flags & typeIdentifierFlags.DontAllowDot) == 0 && tok[j].IsBuiltin(".")) j++; else break; } ty.EndIndex = tok[j - 1].EndIndex + (onShr ? 1 : 0); CsTypeName ret = ty; try { if ((flags & typeIdentifierFlags.AllowNullablesAndPointers) != 0 && !onShr) { while (tok[j].IsBuiltin("*")) { ret = new CsPointerTypeName { StartIndex = ret.StartIndex, EndIndex = tok[j].EndIndex, InnerType = ret }; j++; } if (tok[j].IsBuiltin("?")) { ret = new CsNullableTypeName { StartIndex = ret.StartIndex, EndIndex = tok[j].EndIndex, InnerType = ret }; j++; } } i = j; if ((flags & typeIdentifierFlags.AllowArrays) != 0 && !onShr) { var arrayRanks = new List<int>(); while (tok[j].IsBuiltin("[") && (tok[j + 1].IsBuiltin("]") || tok[j + 1].IsBuiltin(","))) { j++; int num = 1; while (tok[j].IsBuiltin(",")) { num++; j++; } if (!tok[j].IsBuiltin("]")) { if ((flags & typeIdentifierFlags.Lenient) == 0) throw new ParseException("',' or ']' expected.", tok[j].StartIndex, ret); if (arrayRanks.Count > 0) ret = new CsArrayTypeName { StartIndex = ret.StartIndex, EndIndex = tok[j - 1].EndIndex, ArrayRanks = arrayRanks, InnerType = ret }; return Tuple.Create(ret, false); } j++; i = j; arrayRanks.Add(num); } if (arrayRanks.Count > 0) ret = new CsArrayTypeName { StartIndex = ret.StartIndex, EndIndex = tok[j - 1].EndIndex, ArrayRanks = arrayRanks, InnerType = ret }; } } catch (ParseException e) { if ((flags & typeIdentifierFlags.Lenient) != 0) return Tuple.Create(ret, false); if (e.IncompleteResult is CsTypeName) throw; throw new ParseException(e.Message, e.Index, ret, e); } i = j; return Tuple.Create(ret, onShr); }