/// <summary> /// Returns the type corresponding to the specified parsed type name.</summary> /// <param name="typeName"> /// The parse tree node representing the type identifier.</param> /// <returns> /// The resolved type.</returns> public Type ResolveType(CsTypeName typeName) { if (typeName is CsEmptyGenericParameter) throw new InvalidOperationException("CsEmptyGenericParameter not allowed here."); if (typeName is CsConcreteTypeName) { var concrete = (CsConcreteTypeName) typeName; var elem = ResolveSimpleName(concrete.Parts[0], concrete.HasGlobal ? new ResolveContextGlobal() : null); foreach (var part in concrete.Parts.Skip(1)) elem = ResolveSimpleName(part, elem); if (!(elem is ResolveContextType)) throw new InvalidOperationException("“{0}” is not a type.".Fmt(typeName.ToString())); return ((ResolveContextType) elem).Type; } if (typeName is CsArrayTypeName) return ((CsArrayTypeName) typeName).ArrayRanks.Aggregate(ResolveType(((CsArrayTypeName) typeName).InnerType), (type, rank) => rank == 1 ? type.MakeArrayType() : type.MakeArrayType(rank)); if (typeName is CsPointerTypeName) return ResolveType(((CsPointerTypeName) typeName).InnerType).MakePointerType(); if (typeName is CsNullableTypeName) return typeof(Nullable<>).MakeGenericType(ResolveType(((CsNullableTypeName) typeName).InnerType)); throw new NotImplementedException(); }
private static CsMember parseMemberDeclarationComplexCase(TokenJar tok, ref int i, List<CsCustomAttributeGroup> customAttribs, int j, int afterModifiers, bool isEvent, CsTypeName type, string name, int startIndex) { // If it's "(", it's a method. // If it's "<", it may be a generic method, or it may be a property, event or (possibly generic) method that explicitly implements an interface member from a generic interface. // If it's ".", it is a property, event or (possibly generic) method that explicitly implements an interface member. List<CsGenericTypeParameter> genericTypeParameters = null; CsConcreteTypeName implementsFrom = null; if (tok[j].IsBuiltin("<") || tok[j].IsBuiltin(".")) { // In the case that this might be an explicit interface implementation, try to parse the interface type. j--; var ty = (CsConcreteTypeName) parseTypeName(tok, ref j, typeIdentifierFlags.Lenient).Item1; if (tok[j].IsBuiltin(".") && tok[j + 1].IsBuiltin("this")) { // It's an explicit implementation for an indexed property var prop = new CsIndexedProperty { StartIndex = startIndex, Type = type, CustomAttributes = customAttribs, ImplementsFrom = ty }; parseModifiers(prop, tok, ref i); if (i != afterModifiers) throw new ParseException("The modifier '{0}' is not valid for indexed properties.".Fmt(tok[i].TokenStr), tok[i].StartIndex); i = j + 2; if (!tok[i].IsBuiltin("[")) throw new ParseException("'[' expected.", tok[j].StartIndex); prop.Parameters = parseParameterList(tok, ref i); parsePropertyBody(prop, tok, ref i); prop.EndIndex = tok[i - 1].EndIndex; return prop; } // Apart from 'this', no builtins (keywords) are allowed if (!(ty.Parts[ty.Parts.Count - 1] is CsNameIdentifier)) throw new ParseException("Identifier expected instead of '{0}'.".Fmt(ty.Parts[ty.Parts.Count - 1]), tok[j - 1].StartIndex); // The following cases are still possible: // ① a generic method declaration without custom attributes, e.g. // void MyMethod<T>() { } // In this case, ‘ty’ contains ‘MyMethod<T>’ as if T were a generic type argument instead of a generic type parameter declaration, and j points at ‘(’. // ② a generic method declaration with custom attributes, e.g. // void MyMethod<[Foo] T>() { } // In this case, ‘ty’ contains just ‘MyMethod’ and j points at ‘<’. // ③ an explicit interface implementation, e.g. // a. void IMyInterface.MyMethod() { } // non-generic method // b. void IMyInterface.MyMethod<T>() { } // generic method without custom attributes // c. void IMyInterface.MyMethod<[Foo] T>() { } // generic method with custom attributes // d. int IMyInterface.MyProperty { get { ... } } // property // e. event EventHandler IMyInterface.MyEvent { add { ... } remove { ... } } // event // The interface could be anything, including namespaces, nested types and generic type arguments // (e.g. MyNamespace.IMyInterface<List<int>.Enumerator>). // In all cases, ‘ty’ contains not just the interface name, but also *at least* the member name and *at most* the member name plus its generic parameters. // Therefore, either way, remove it from ‘ty’ to get the genuine interface name. var lastPart = (CsNameIdentifier) ty.Parts[ty.Parts.Count - 1]; ty.Parts.RemoveAt(ty.Parts.Count - 1); if (lastPart.GenericTypeArguments != null) { // If the last part has generic type “arguments”, then we are in case ① or ③b. // In that case, we need to convert those “arguments” into generic parameters. genericTypeParameters = new List<CsGenericTypeParameter>(); foreach (var g in lastPart.GenericTypeArguments) { var single = g.GetSingleIdentifier(); if (single == null) throw new ParseException("Invalid generic type parameter declaration.", tok[j].StartIndex); genericTypeParameters.Add(new CsGenericTypeParameter { StartIndex = g.StartIndex, EndIndex = g.EndIndex, Name = single }); } } else if (tok[j].IsBuiltin("<")) { // The last part didn’t have generic type arguments, and we are on a “<” token, so we are in case ② or ③c. genericTypeParameters = parseGenericTypeParameterList(tok, ref j); } // The “name” that was passed in was the first token after the return type, so it would have been // set to the first identifier in the interface name, which is of course wrong. Fix it now that we know the real name. name = lastPart.Name; // If “ty” has any parts left, then that’s the interface name, otherwise this wasn’t an explicit interface declaration in the first place. implementsFrom = ty.Parts.Count == 0 ? null : ty; if (tok[j].IsBuiltin("{")) { // Case ③d and ③e: an explicitly-implemented property or an event. // It must be an explicit implementation because normal properties/events are already handled by parseMemberDeclaration(). // Explicitly-implemented events must have a body with add/remove methods; they can’t be events with the fields syntax, e.g. // event EventHandler IMyInterface.MyEvent = null; // ✗ not allowed if (genericTypeParameters != null) throw new ParseException("Properties and events cannot be generic.", tok[j].StartIndex); if (isEvent) { var nameExpr = new CsNameAndExpression { // We are assuming that after the modifiers comes the “event” keyword and after that, the name StartIndex = tok[afterModifiers + 1].StartIndex, EndIndex = tok[afterModifiers + 1].EndIndex, Name = name }; var evnt = new CsEvent { StartIndex = startIndex, Type = type, NamesAndInitializers = new List<CsNameAndExpression> { nameExpr }, CustomAttributes = customAttribs, ImplementsFrom = implementsFrom }; parseModifiers(evnt, tok, ref i); if (i != afterModifiers) throw new ParseException("The modifier '{0}' is not valid for events.".Fmt(tok[i].TokenStr), tok[i].StartIndex); i = j; parseEventBody(evnt, tok, ref i); evnt.EndIndex = tok[i - 1].EndIndex; return evnt; } else { var prop = new CsProperty { StartIndex = startIndex, Type = type, Name = name, CustomAttributes = customAttribs, ImplementsFrom = implementsFrom }; parseModifiers(prop, tok, ref i); if (i != afterModifiers) throw new ParseException("The modifier '{0}' is not valid for properties.".Fmt(tok[i].TokenStr), tok[i].StartIndex); i = j; parsePropertyBody(prop, tok, ref i); prop.EndIndex = tok[i - 1].EndIndex; return prop; } } else if (isEvent && (tok[j].IsBuiltin(";") || tok[j].IsBuiltin("="))) { // It’s an event without a body — not allowed for explicit interface implementations throw new ParseException("Events that explicitly implement an interface event must have add/remove methods.", tok[i].StartIndex); } } // We’ve taken care of explicitly-implemented properties, indexers and events. The only case left is that it must be a method. // implementsFrom and genericTypeParameters are fully populated. CsMethod meth = new CsMethod { StartIndex = startIndex, Type = type, Name = name, CustomAttributes = customAttribs, ImplementsFrom = implementsFrom, GenericTypeParameters = genericTypeParameters }; parseModifiers(meth, tok, ref i); if (i != afterModifiers) throw new ParseException("The modifier '{0}' is not valid for methods.".Fmt(tok[i].TokenStr), tok[i].StartIndex); i = j; if (!tok[i].IsBuiltin("(")) throw new ParseException("'(' expected.", tok[i].StartIndex, meth); try { meth.Parameters = parseParameterList(tok, ref i); } catch (ParseException e) { if (e.IncompleteResult is List<CsParameter>) meth.Parameters = (List<CsParameter>) e.IncompleteResult; throw new ParseException(e.Message, e.Index, meth, e); } if (tok[i].IsIdentifier("where")) { try { meth.GenericTypeConstraints = parseGenericTypeConstraints(tok, ref i); } catch (ParseException e) { if (e.IncompleteResult is Dictionary<string, List<CsGenericTypeConstraint>>) meth.GenericTypeConstraints = (Dictionary<string, List<CsGenericTypeConstraint>>) e.IncompleteResult; throw new ParseException(e.Message, e.Index, meth, e); } } if (tok[i].IsBuiltin(";")) i++; else if (tok[i].IsBuiltin("{")) { try { meth.MethodBody = parseBlock(tok, ref i); } catch (ParseException e) { if (e.IncompleteResult is CsBlock) meth.MethodBody = (CsBlock) e.IncompleteResult; throw new ParseException(e.Message, e.Index, meth, e); } } else throw new ParseException(@"';', '{' or 'where' expected.", tok[i].StartIndex, meth); meth.EndIndex = tok[i - 1].EndIndex; return meth; }
private static CsOperatorOverload parseOperatorOverloadDeclaration(TokenJar tok, ref int i, List<CsCustomAttributeGroup> customAttribs, int atOperator, int afterModifiers, CsTypeName type) { var j = atOperator + 1; var overloadableOperators = new[] { "+", "-", "!", "~", "++", "--", "true", "false", "*", "/", "%", "&", "|", "^", "<<", ">>", "==", "!=", "<", ">", "<=", ">=" }; if (tok[j].Type != TokenType.Builtin || !overloadableOperators.Contains(tok[j].TokenStr)) throw new ParseException("Overloadable operator ({0}) expected.".Fmt(overloadableOperators.Select(o => "'" + o + "'").JoinString(", ")), tok[j].StartIndex); string opStr = tok[j].TokenStr; j++; var parameters = parseParameterList(tok, ref j); CsOperatorOverload op; switch (parameters.Count) { case 1: UnaryOperator unop; switch (opStr) { case "+": unop = UnaryOperator.Plus; break; case "-": unop = UnaryOperator.Minus; break; case "!": unop = UnaryOperator.Not; break; case "~": unop = UnaryOperator.Neg; break; case "++": unop = UnaryOperator.PrefixInc; break; case "--": unop = UnaryOperator.PrefixDec; break; case "true": unop = UnaryOperator.True; break; case "false": unop = UnaryOperator.False; break; default: throw new ParseException("Binary operator must have two parameters. Overloadable unary operators are '+', '-', '!', '~', '++', '--', 'true' and 'false'.", tok[j].StartIndex); } op = new CsUnaryOperatorOverload { StartIndex = tok[i].StartIndex, CustomAttributes = customAttribs, Parameter = parameters[0], ReturnType = type, Operator = unop }; break; case 2: BinaryOperator binop; switch (opStr) { case "+": binop = BinaryOperator.Plus; break; case "-": binop = BinaryOperator.Minus; break; case "*": binop = BinaryOperator.Times; break; case "/": binop = BinaryOperator.Div; break; case "%": binop = BinaryOperator.Mod; break; case "&": binop = BinaryOperator.And; break; case "|": binop = BinaryOperator.Or; break; case "^": binop = BinaryOperator.Xor; break; case "<<": binop = BinaryOperator.Shl; break; case ">>": binop = BinaryOperator.Shr; break; case "==": binop = BinaryOperator.Eq; break; case "!=": binop = BinaryOperator.NotEq; break; case "<": binop = BinaryOperator.Less; break; case ">": binop = BinaryOperator.Greater; break; case "<=": binop = BinaryOperator.LessEq; break; case ">=": binop = BinaryOperator.GreaterEq; break; default: throw new ParseException("Unary operator must have only one parameter. Overloadable binary operators are '+', '-', '*', '/', '%', '&', '|', '^', '<<', '>>', '==', '!=', '<', '>', '<=', and '>='.", tok[j].StartIndex); } op = new CsBinaryOperatorOverload { StartIndex = tok[i].StartIndex, CustomAttributes = customAttribs, Parameter = parameters[0], SecondParameter = parameters[1], ReturnType = type, Operator = binop }; break; default: throw new ParseException("Overloadable operators must have exactly one or two parameters. Use one parameter for unary operators, two for binary operators.", tok[j].StartIndex); } parseModifiers(op, tok, ref i); if (i != afterModifiers) throw new ParseException("The modifier '{0}' is not valid for operator overloads.".Fmt(tok[i].TokenStr), tok[i].StartIndex); i = j; try { op.MethodBody = parseBlock(tok, ref i); } catch (ParseException e) { if (e.IncompleteResult is CsBlock) op.MethodBody = (CsBlock) e.IncompleteResult; throw new ParseException(e.Message, e.Index, op, e); } return op; }
private static CsEventOrField parseFieldOrEventDeclaration(bool isEvent, TokenJar tok, ref int i, List<CsCustomAttributeGroup> customAttribs, int afterModifiers, int afterName, CsTypeName type, CsTypeName implementsFrom, string name) { var startIndex = tok[i].StartIndex; CsEventOrField ret; if (isEvent) ret = new CsEvent { StartIndex = startIndex, Type = type, CustomAttributes = customAttribs, ImplementsFrom = implementsFrom }; else ret = new CsField { StartIndex = startIndex, Type = type, CustomAttributes = customAttribs }; parseModifiers(ret, tok, ref i); if (i != afterModifiers) throw new ParseException("The modifier '{0}' is not valid for {1}.".Fmt(tok[i].TokenStr, isEvent ? "events" : "fields"), tok[i].StartIndex); i = afterName; CsExpression initializer = null; try { if (tok[i].IsBuiltin("=")) { i++; initializer = parseExpression(tok, ref i); } ret.NamesAndInitializers.Add(new CsNameAndExpression { StartIndex = tok[afterName - 1].StartIndex, EndIndex = tok[i - 1].EndIndex, Name = name, Expression = initializer }); while (tok[i].IsBuiltin(",")) { i++; var nameStartIndex = tok[i].StartIndex; name = tok[i].Identifier(); initializer = null; i++; if (tok[i].IsBuiltin("=")) { i++; initializer = parseExpression(tok, ref i); } ret.NamesAndInitializers.Add(new CsNameAndExpression { StartIndex = nameStartIndex, EndIndex = tok[i - 1].EndIndex, Name = name, Expression = initializer }); } } catch (ParseException e) { if (e.IncompleteResult is CsExpression) { ret.NamesAndInitializers.Add(new CsNameAndExpression { StartIndex = tok[i].StartIndex, Name = name, Expression = (CsExpression) e.IncompleteResult }); throw new ParseException(e.Message, e.Index, ret, e); } throw; } if (!tok[i].IsBuiltin(";")) throw new ParseException("'=', ',' or ';' expected.", tok[i].StartIndex, ret); ret.EndIndex = tok[i].EndIndex; i++; return ret; }