Ejemplo n.º 1
0
        /// <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();
        }
Ejemplo n.º 2
0
        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;
        }
Ejemplo n.º 3
0
        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;
        }
Ejemplo n.º 4
0
 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;
 }