コード例 #1
0
ファイル: Parser.cs プロジェクト: Timwi/ParseCs
 private static List<CsArgument> parseArgumentList(TokenJar tok, ref int i, out bool isIndexer)
 {
     isIndexer = tok[i].IsBuiltin("[");
     if (!tok[i].IsBuiltin("(") && !tok[i].IsBuiltin("["))
         throw new ParseException("'(' or '[' expected.", tok[i].StartIndex);
     i++;
     if (isIndexer && tok[i].IsBuiltin("]"))
         throw new ParseException("Empty indexing expressions are not allowed.", tok[i].StartIndex);
     var arguments = new List<CsArgument>();
     if (!tok[i].IsBuiltin(isIndexer ? "]" : ")"))
     {
         ArgumentMode inoutref = ArgumentMode.In;
         string argName = null;
         try
         {
             while (true)
             {
                 var argStartIndex = tok[i].StartIndex;
                 argName = null;
                 if (tok[i].Type == TokenType.Identifier && tok.IndexExists(i + 1) && tok[i + 1].IsBuiltin(":"))
                 {
                     argName = tok[i].Identifier();
                     i += 2;
                 }
                 inoutref = ArgumentMode.In;
                 if (tok[i].IsBuiltin("ref"))
                 {
                     inoutref = ArgumentMode.Ref;
                     i++;
                 }
                 else if (tok[i].IsBuiltin("out"))
                 {
                     inoutref = ArgumentMode.Out;
                     i++;
                 }
                 var expr = parseExpression(tok, ref i);
                 arguments.Add(new CsArgument { StartIndex = argStartIndex, EndIndex = expr.EndIndex, Name = argName, Mode = inoutref, Expression = expr });
                 if (tok[i].IsBuiltin(isIndexer ? "]" : ")"))
                     break;
                 else if (!tok[i].IsBuiltin(","))
                     throw new ParseException(isIndexer ? "',' or ']' expected." : "',' or ')' expected.", tok[i].StartIndex);
                 i++;
             }
         }
         catch (ParseException e)
         {
             if (e.IncompleteResult is CsExpression)
                 arguments.Add(new CsArgument { StartIndex = tok[i].StartIndex, Name = argName, Mode = inoutref, Expression = (CsExpression) e.IncompleteResult });
             throw new ParseException(e.Message, e.Index, arguments, e);
         }
     }
     i++;
     return arguments;
 }
コード例 #2
0
ファイル: Parser.cs プロジェクト: Timwi/ParseCs
        private static void parseModifiers(CsMember mem, TokenJar tok, ref int i)
        {
            while (true)
            {
                // All members
                if (tok[i].IsBuiltin("public"))
                    mem.IsPublic = true;
                else if (tok[i].IsBuiltin("protected"))
                    mem.IsProtected = true;
                else if (tok[i].IsBuiltin("private"))
                    mem.IsPrivate = true;
                else if (tok[i].IsBuiltin("internal"))
                    mem.IsInternal = true;
                else if (tok[i].IsBuiltin("new"))
                    mem.IsNew = true;
                else if (tok[i].IsBuiltin("unsafe"))
                    mem.IsUnsafe = true;

                // CsMemberLevel2 (CsProperty, CsMethod)
                else if (tok[i].IsBuiltin("static") && mem is CsMethodOrProperty)
                    ((CsMethodOrProperty) mem).IsStatic = true;
                else if (tok[i].IsBuiltin("abstract") && mem is CsMethodOrProperty)
                    ((CsMethodOrProperty) mem).IsAbstract = true;
                else if (tok[i].IsBuiltin("sealed") && mem is CsMethodOrProperty)
                    ((CsMethodOrProperty) mem).IsSealed = true;
                else if (tok[i].IsBuiltin("virtual") && mem is CsMethodOrProperty)
                    ((CsMethodOrProperty) mem).IsVirtual = true;
                else if (tok[i].IsBuiltin("override") && mem is CsMethodOrProperty)
                    ((CsMethodOrProperty) mem).IsOverride = true;
                else if (tok[i].IsBuiltin("extern") && mem is CsMethodOrProperty)
                    ((CsMethodOrProperty) mem).IsExtern = true;
                else if (tok[i].IsIdentifier("partial") && mem is CsMethod)
                    ((CsMethod) mem).IsPartial = true;

                // CsMultiMember (CsEvent, CsField)
                else if (tok[i].IsBuiltin("static") && mem is CsEventOrField)
                    ((CsEventOrField) mem).IsStatic = true;
                else if (tok[i].IsBuiltin("abstract") && mem is CsEvent)
                    ((CsEvent) mem).IsAbstract = true;
                else if (tok[i].IsBuiltin("sealed") && mem is CsEvent)
                    ((CsEvent) mem).IsSealed = true;
                else if (tok[i].IsBuiltin("virtual") && mem is CsEvent)
                    ((CsEvent) mem).IsVirtual = true;
                else if (tok[i].IsBuiltin("override") && mem is CsEvent)
                    ((CsEvent) mem).IsOverride = true;
                else if (tok[i].IsBuiltin("readonly") && mem is CsField)
                    ((CsField) mem).IsReadonly = true;
                else if (tok[i].IsBuiltin("const") && mem is CsField)
                    ((CsField) mem).IsConst = true;
                else if (tok[i].IsBuiltin("volatile") && mem is CsField)
                    ((CsField) mem).IsVolatile = true;

                // CsTypeLevel2 (class, struct, interface)
                else if (tok[i].IsIdentifier("partial") && mem is CsTypeLevel2)
                    ((CsTypeLevel2) mem).IsPartial = true;
                else if (tok[i].IsBuiltin("static") && mem is CsClass)
                    ((CsClass) mem).IsStatic = true;
                else if (tok[i].IsBuiltin("sealed") && mem is CsClass)
                    ((CsClass) mem).IsSealed = true;
                else if (tok[i].IsBuiltin("abstract") && mem is CsClass)
                    ((CsClass) mem).IsAbstract = true;

                // Other special
                else if (tok[i].IsBuiltin("static") && mem is CsOperatorOverload)
                    ((CsOperatorOverload) mem).IsStatic = true;
                else if (tok[i].IsBuiltin("static") && mem is CsConstructor)
                    ((CsConstructor) mem).IsStatic = true;

                else
                    break;
                i++;
            }
        }
コード例 #3
0
ファイル: Parser.cs プロジェクト: Timwi/ParseCs
        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;
        }
コード例 #4
0
ファイル: Parser.cs プロジェクト: Timwi/ParseCs
        private static void parseEventBody(CsEvent ev, TokenJar tok, ref int i)
        {
            tok[i].Assert("{");
            i++;
            ev.Methods = new List<CsSimpleMethod>();

            while (!tok[i].IsBuiltin("}"))
            {
                var startIndex = tok[i].StartIndex;
                var cAttribs = new List<CsCustomAttributeGroup>();
                while (tok[i].IsBuiltin("["))
                    cAttribs.Add(parseCustomAttributeGroup(tok, ref i));
                var m = new CsSimpleMethod { StartIndex = startIndex, CustomAttributes = cAttribs };
                parseModifiers(m, tok, ref i);
                if (!tok[i].IsIdentifier("add") && !tok[i].IsIdentifier("remove"))
                    throw new ParseException("'add' or 'remove' expected.", tok[i].StartIndex, ev);
                m.Type = tok[i].TokenStr == "add" ? MethodType.Add : MethodType.Remove;
                if (ev.Methods.Any(me => me.Type == m.Type))
                {
                    if (m.Type == MethodType.Add)
                        throw new ParseException("An 'add' method has already been defined for this event.", tok[i].StartIndex, ev);
                    else
                        throw new ParseException("A 'remove' method has already been defined for this event.", tok[i].StartIndex, ev);
                }
                ev.Methods.Add(m);
                i++;
                if (tok[i].IsBuiltin("{"))
                {
                    try { m.Body = parseBlock(tok, ref i); }
                    catch (ParseException e)
                    {
                        if (e.IncompleteResult is CsBlock)
                            m.Body = (CsBlock) e.IncompleteResult;
                        throw new ParseException(e.Message, e.Index, ev, e);
                    }
                }
                else if (tok[i].IsBuiltin(";"))
                    i++;
                else
                    throw new ParseException("'{' or ';' expected.", tok[i].StartIndex, ev);
                m.EndIndex = tok[i - 1].EndIndex;
            }
            i++;
        }
コード例 #5
0
ファイル: Parser.cs プロジェクト: Timwi/ParseCs
 private static CsExpression parseExpressionCoalesce(TokenJar tok, ref int i, bool tryNotToThrow = false)
 {
     var left = parseExpressionBoolOr(tok, ref i, tryNotToThrow);
     if (left == null)
         return null;
     if (tok[i].IsBuiltin("??"))
     {
         i++;
         try
         {
             var right = parseExpressionCoalesce(tok, ref i);
             left = new CsBinaryOperatorExpression { StartIndex = left.StartIndex, EndIndex = right.EndIndex, Left = left, Right = right, Operator = BinaryOperator.Coalesce };
         }
         catch (ParseException e)
         {
             if (e.IncompleteResult is CsExpression)
                 throw new ParseException(e.Message, e.Index, new CsBinaryOperatorExpression { StartIndex = tok[i].StartIndex, Left = left, Right = (CsExpression) e.IncompleteResult, Operator = BinaryOperator.Coalesce }, e);
             throw new ParseException(e.Message, e.Index, left, e);
         }
     }
     return left;
 }
コード例 #6
0
ファイル: Parser.cs プロジェクト: Timwi/ParseCs
        private static CsDelegate parseDelegateDeclaration(TokenJar tok, ref int i, List<CsCustomAttributeGroup> customAttribs, int j)
        {
            var deleg = new CsDelegate { StartIndex = tok[i].StartIndex, CustomAttributes = customAttribs };
            parseModifiers(deleg, tok, ref i);
            if (i != j)
                throw new ParseException("The modifier '{0}' is not valid for delegates.".Fmt(tok[i].TokenStr), tok[i].StartIndex);
            i = j + 1;
            deleg.ReturnType = parseTypeName(tok, ref i, typeIdentifierFlags.AllowKeywords | typeIdentifierFlags.AllowNullablesAndPointers | typeIdentifierFlags.AllowArrays).Item1;
            deleg.Name = tok[i].Identifier();
            i++;
            try
            {
                if (tok[i].IsBuiltin("<"))
                    deleg.GenericTypeParameters = parseGenericTypeParameterList(tok, ref i);

                deleg.Parameters = parseParameterList(tok, ref i);

                if (tok[i].IsIdentifier("where"))
                    deleg.GenericTypeConstraints = parseGenericTypeConstraints(tok, ref i);
            }
            catch (ParseException e)
            {
                if (e.IncompleteResult is List<CsGenericTypeParameter>)
                    deleg.GenericTypeParameters = (List<CsGenericTypeParameter>) e.IncompleteResult;
                else if (e.IncompleteResult is List<CsParameter>)
                    deleg.Parameters = (List<CsParameter>) e.IncompleteResult;
                else if (e.IncompleteResult is Dictionary<string, List<CsGenericTypeConstraint>>)
                    deleg.GenericTypeConstraints = (Dictionary<string, List<CsGenericTypeConstraint>>) e.IncompleteResult;
                throw new ParseException(e.Message, e.Index, deleg, e);
            }

            if (!tok[i].IsBuiltin(";"))
                throw new ParseException("';' expected.", tok[i].StartIndex, deleg);
            deleg.EndIndex = tok[i].EndIndex;
            i++;
            return deleg;
        }
コード例 #7
0
ファイル: Parser.cs プロジェクト: Timwi/ParseCs
 private static CsDocument parseDocument(TokenJar tok, ref int i)
 {
     var doc = new CsDocument { StartIndex = tok[i].StartIndex };
     try
     {
         while (tok.IndexExists(i))
         {
             var j = i;
             object result = parseMemberDeclaration(tok, ref i, true);
             if (result is CsUsingAlias)
                 doc.UsingAliases.Add((CsUsingAlias) result);
             else if (result is CsUsingNamespace)
                 doc.UsingNamespaces.Add((CsUsingNamespace) result);
             else if (result is CsNamespace)
                 doc.Namespaces.Add((CsNamespace) result);
             else if (result is CsType)
                 doc.Types.Add((CsType) result);
             else if (result is CsCustomAttributeGroup)
                 doc.CustomAttributes.Add((CsCustomAttributeGroup) result);
             else
                 throw new ParseException("Members cannot be declared directly at the top level. Expected 'using', 'namespace', or a type declaration.", tok[j].StartIndex, doc);
         }
     }
     catch (LexException e)
     {
         throw new ParseException(e.Message, e.Index, doc, e);
     }
     catch (ParseException e)
     {
         if (e.IncompleteResult is CsUsingAlias)
             doc.UsingAliases.Add((CsUsingAlias) e.IncompleteResult);
         else if (e.IncompleteResult is CsUsingNamespace)
             doc.UsingNamespaces.Add((CsUsingNamespace) e.IncompleteResult);
         else if (e.IncompleteResult is CsNamespace)
             doc.Namespaces.Add((CsNamespace) e.IncompleteResult);
         else if (e.IncompleteResult is CsType)
             doc.Types.Add((CsType) e.IncompleteResult);
         throw new ParseException(e.Message, e.Index, doc, e);
     }
     doc.EndIndex = tok[i - 1].EndIndex;
     return doc;
 }
コード例 #8
0
ファイル: Parser.cs プロジェクト: Timwi/ParseCs
        private static CsOptionalExpressionStatement parseReturnOrThrowStatement(TokenJar tok, ref int i)
        {
            if (!tok[i].IsBuiltin("return") && !tok[i].IsBuiltin("throw"))
                tok[i].Assert("false");

            var ret = tok[i].IsBuiltin("return") ? (CsOptionalExpressionStatement) new CsReturnStatement() : new CsThrowStatement();
            ret.StartIndex = tok[i].StartIndex;
            i++;
            if (tok[i].IsBuiltin(";"))
            {
                ret.EndIndex = tok[i].EndIndex;
                i++;
                return ret;
            }
            try { ret.Expression = parseExpression(tok, ref i); }
            catch (ParseException e)
            {
                if (e.IncompleteResult is CsExpression)
                    ret.Expression = (CsExpression) e.IncompleteResult;
                throw new ParseException(e.Message, e.Index, ret, e);
            }
            if (!tok[i].IsBuiltin(";"))
                throw new ParseException("';' expected. (3)", tok[i].StartIndex, ret);
            ret.EndIndex = tok[i].EndIndex;
            i++;
            return ret;
        }
コード例 #9
0
ファイル: Parser.cs プロジェクト: Timwi/ParseCs
        private static CsStatement parseStatement(TokenJar tok, ref int i)
        {
            if (tok[i].IsBuiltin("{"))
                return parseBlock(tok, ref i);

            if (tok[i].IsBuiltin("if"))
                return parseIfStatement(tok, ref i);

            if (tok[i].IsBuiltin("while") || tok[i].IsBuiltin("lock"))
                return parseWhileOrLockStatement(tok, ref i);

            if (tok[i].IsBuiltin("return") || tok[i].IsBuiltin("throw"))
                return parseReturnOrThrowStatement(tok, ref i);

            if (tok[i].IsBuiltin("foreach"))
                return parseForeachStatement(tok, ref i);

            if (tok[i].IsBuiltin("try"))
                return parseTryStatement(tok, ref i);

            if (tok[i].IsBuiltin("using"))
                return parseUsingStatement(tok, ref i);

            if (tok[i].IsBuiltin("switch"))
                return parseSwitchStatement(tok, ref i);

            if (tok[i].IsBuiltin("for"))
                return parseForStatement(tok, ref i);

            if (tok[i].IsBuiltin("do"))
            {
                var dow = new CsDoWhileStatement { StartIndex = tok[i].StartIndex };
                i++;
                dow.Body = parseStatement(tok, ref i);
                if (!tok[i].IsBuiltin("while") || !tok[i + 1].IsBuiltin("("))
                    throw new ParseException("'while' followed by '(' expected.", tok[i].StartIndex);
                i += 2;
                dow.WhileExpression = parseExpression(tok, ref i);
                if (!tok[i].IsBuiltin(")") || !tok[i + 1].IsBuiltin(";"))
                    throw new ParseException("')' followed by ';' expected.", tok[i].StartIndex, dow);
                i += 2;
                dow.EndIndex = tok[i - 1].EndIndex;
                return dow;
            }

            if (tok[i].IsBuiltin("continue") || tok[i].IsBuiltin("break"))
            {
                var stat = tok[i].IsBuiltin("continue") ? (CsStatement) new CsContinueStatement() : new CsBreakStatement();
                stat.StartIndex = tok[i].StartIndex;
                i++;
                if (!tok[i].IsBuiltin(";"))
                    throw new ParseException("';' expected.", tok[i].StartIndex, stat);
                stat.EndIndex = tok[i].EndIndex;
                i++;
                return stat;
            }

            if (tok[i].IsIdentifier("yield") && tok[i + 1].IsBuiltin("return"))
            {
                i += 2;
                CsExpression expr;
                try { expr = parseExpression(tok, ref i); }
                catch (ParseException e)
                {
                    if (e.IncompleteResult is CsExpression)
                        throw new ParseException(e.Message, e.Index, new CsYieldReturnStatement { StartIndex = tok[i].StartIndex, Expression = (CsExpression) e.IncompleteResult }, e);
                    throw;
                }
                var yieldreturn = new CsYieldReturnStatement { StartIndex = tok[i].StartIndex, Expression = expr };
                if (!tok[i].IsBuiltin(";"))
                    throw new ParseException("';' expected.", tok[i].StartIndex, yieldreturn);
                yieldreturn.EndIndex = tok[i].EndIndex;
                i++;
                return yieldreturn;
            }

            if (tok[i].IsIdentifier("yield") && tok[i + 1].IsBuiltin("break"))
            {
                var yieldbreak = new CsYieldBreakStatement { StartIndex = tok[i].StartIndex };
                i += 2;
                if (!tok[i].IsBuiltin(";"))
                    throw new ParseException("';' expected.", tok[i].StartIndex, yieldbreak);
                yieldbreak.EndIndex = tok[i].EndIndex;
                i++;
                return yieldbreak;
            }

            if (tok[i].IsBuiltin("goto"))
            {
                i++;
                if (tok[i].IsBuiltin("default"))
                {
                    var stat = new CsGotoDefaultStatement { StartIndex = tok[i].StartIndex };
                    i++;
                    if (!tok[i].IsBuiltin(";"))
                        throw new ParseException("';' expected.", tok[i].StartIndex, stat);
                    stat.EndIndex = tok[i].EndIndex;
                    i++;
                    return stat;
                }
                if (tok[i].IsBuiltin("case"))
                {
                    var stat = new CsGotoCaseStatement { StartIndex = tok[i].StartIndex };
                    i++;
                    try { stat.Expression = parseExpression(tok, ref i); }
                    catch (ParseException e)
                    {
                        if (e.IncompleteResult is CsExpression)
                            stat.Expression = (CsExpression) e.IncompleteResult;
                        throw new ParseException(e.Message, e.Index, stat, e);
                    }
                    if (!tok[i].IsBuiltin(";"))
                        throw new ParseException("';' expected.", tok[i].StartIndex, stat);
                    stat.EndIndex = tok[i].EndIndex;
                    i++;
                    return stat;
                }
                var name = tok[i].Identifier();
                i++;
                var got = new CsGotoStatement { StartIndex = tok[i].StartIndex, Label = name };
                if (!tok[i].IsBuiltin(";"))
                    throw new ParseException("';' expected.", tok[i].StartIndex, got);
                got.EndIndex = tok[i].EndIndex;
                i++;
                return got;
            }

            if (tok[i].Type == TokenType.Identifier && tok[i + 1].IsBuiltin(":"))
            {
                string name = tok[i].TokenStr;
                i += 2;
                CsStatement inner = parseStatement(tok, ref i);
                if (inner.GotoLabels == null)
                    inner.GotoLabels = new List<string> { name };
                else
                    inner.GotoLabels.Insert(0, name);
                return inner;
            }

            if (tok[i].IsBuiltin(";"))
            {
                i++;
                return new CsEmptyStatement { StartIndex = tok[i - 1].StartIndex, EndIndex = tok[i - 1].EndIndex };
            }

            if (tok[i].IsBuiltin("checked") || tok[i].IsBuiltin("unchecked") || tok[i].IsBuiltin("unsafe"))
            {
                CsBlockStatement blockStat = tok[i].IsBuiltin("checked") ? new CsCheckedStatement() : tok[i].IsBuiltin("unchecked") ? (CsBlockStatement) new CsUncheckedStatement() : new CsUnsafeStatement();
                blockStat.StartIndex = tok[i].StartIndex;
                i++;
                blockStat.Block = parseBlock(tok, ref i);
                blockStat.EndIndex = tok[i - 1].EndIndex;
                return blockStat;
            }

            if (tok[i].IsBuiltin("fixed"))
            {
                var fixd = new CsFixedStatement { StartIndex = tok[i].StartIndex };
                i++;
                if (!tok[i].IsBuiltin("("))
                    throw new ParseException("'(' expected.", tok[i].StartIndex);
                i++;
                var k = i;
                fixd.InitializationStatement = parseVariableDeclarationOrExpressionStatement(tok, ref i);
                if (!tok[i].IsBuiltin(")"))
                    throw new ParseException("')' expected.", tok[i].StartIndex);
                i++;

                if (!(fixd.InitializationStatement is CsVariableDeclarationStatement) || !(((CsVariableDeclarationStatement) fixd.InitializationStatement).Type is CsPointerTypeName))
                    throw new ParseException("'fixed' statement requires a variable declaration for a pointer-typed variable.", tok[k].StartIndex);

                try { fixd.Body = parseStatement(tok, ref i); }
                catch (ParseException e)
                {
                    if (e.IncompleteResult is CsStatement)
                    {
                        fixd.Body = (CsStatement) e.IncompleteResult;
                        throw new ParseException(e.Message, e.Index, fixd, e);
                    }
                    throw;
                }
                fixd.EndIndex = tok[i - 1].EndIndex;
                return fixd;
            }

            // Slight hack: parseVariableDeclarationOrExpressionStatement() does not consume the trailing ';' of the statement.
            // This is so that the same method can be used in parsing 'using' and 'for' statements.
            var stmt = parseVariableDeclarationOrExpressionStatement(tok, ref i);
            if (!tok[i].IsBuiltin(";"))
                throw new ParseException("';' expected.", tok[i].StartIndex, stmt);
            stmt.EndIndex = tok[i].EndIndex;
            i++;
            return stmt;
        }
コード例 #10
0
ファイル: Parser.cs プロジェクト: Timwi/ParseCs
        private static List<CsParameter> parseParameterList(TokenJar tok, ref int i, bool tryNotToThrow = false)
        {
            bool square = tok[i].IsBuiltin("[");

            if (!square && !tok[i].IsBuiltin("("))
            {
                if (tryNotToThrow)
                    return null;
                else
                    throw new ParseException("'(' + parameter list + ')' expected.", tok[i].StartIndex);
            }

            List<CsParameter> ret = new List<CsParameter>();
            if (tok[i + 1].IsBuiltin(square ? "]" : ")"))
            {
                i += 2;
                return ret;
            }

            do
            {
                i++;
                try
                {
                    var parsed = parseParameter(tok, ref i, tryNotToThrow);
                    if (parsed == null)
                        return null;
                    ret.Add(parsed);
                }
                catch (ParseException e)
                {
                    if (e.IncompleteResult is CsParameter)
                        ret.Add((CsParameter) e.IncompleteResult);
                    throw new ParseException(e.Message, e.Index, ret, e);
                }
            }
            while (tok[i].IsBuiltin(","));

            if (!tok[i].IsBuiltin(square ? "]" : ")"))
                throw new ParseException(square ? "']' or ',' expected. (2)" : "')' or ',' expected.", tok[i].StartIndex, ret);
            i++;

            return ret;
        }
コード例 #11
0
ファイル: Parser.cs プロジェクト: Timwi/ParseCs
        private static void parsePropertyBody(CsProperty prop, TokenJar tok, ref int i)
        {
            if (!tok[i].IsBuiltin("{"))
                throw new ParseException(@"'{' expected.", tok[i].StartIndex);
            i++;

            while (!tok[i].IsBuiltin("}"))
            {
                var startIndex = tok[i].StartIndex;
                var cAttribs = new List<CsCustomAttributeGroup>();
                while (tok[i].IsBuiltin("["))
                    cAttribs.Add(parseCustomAttributeGroup(tok, ref i));
                var m = new CsSimpleMethod { StartIndex = startIndex, CustomAttributes = cAttribs };
                parseModifiers(m, tok, ref i);
                if (!tok[i].IsIdentifier("get") && !tok[i].IsIdentifier("set"))
                    throw new ParseException("'get' or 'set' expected.", tok[i].StartIndex, prop);
                m.Type = tok[i].TokenStr == "get" ? MethodType.Get : MethodType.Set;
                if (prop.Methods.Any(me => me.Type == m.Type))
                    throw new ParseException("A '{0}' method has already been defined for this property.".Fmt(m.Type), tok[i].StartIndex, prop);
                prop.Methods.Add(m);
                i++;
                if (tok[i].IsBuiltin("{"))
                {
                    try { m.Body = parseBlock(tok, ref i); }
                    catch (ParseException e)
                    {
                        if (e.IncompleteResult is CsBlock)
                            m.Body = (CsBlock) e.IncompleteResult;
                        throw new ParseException(e.Message, e.Index, prop, e);
                    }
                }
                else if (tok[i].IsBuiltin(";"))
                    i++;
                else
                    throw new ParseException("'{' or ';' expected.", tok[i].StartIndex, prop);
                m.EndIndex = tok[i - 1].EndIndex;
            }
            i++;
        }
コード例 #12
0
ファイル: Parser.cs プロジェクト: Timwi/ParseCs
        private static CsParameter parseParameter(TokenJar tok, ref int i, bool tryNotToThrow = false)
        {
            var startIndex = tok[i].StartIndex;
            var customAttribs = new List<CsCustomAttributeGroup>();
            while (tok[i].IsBuiltin("["))
                customAttribs.Add(parseCustomAttributeGroup(tok, ref i));

            bool isThis = false, isOut = false, isRef = false, isParams = false;
            if (tok[i].IsBuiltin("this"))
            {
                isThis = true;
                i++;
            }
            if (tok[i].IsBuiltin("out"))
            {
                isOut = true;
                i++;
            }
            if (tok[i].IsBuiltin("ref"))
            {
                isRef = true;
                i++;
            }
            if (tok[i].IsBuiltin("params"))
            {
                isParams = true;
                i++;
            }
            var result = parseTypeName(tok, ref i, typeIdentifierFlags.AllowKeywords | typeIdentifierFlags.AllowNullablesAndPointers | typeIdentifierFlags.AllowArrays, tryNotToThrow);
            if (result == null || (tryNotToThrow && tok[i].Type != TokenType.Identifier))
                return null;
            var name = tok[i].Identifier();
            i++;
            CsExpression defaultValue = null;
            if (tok[i].IsBuiltin("="))
            {
                i++;
                defaultValue = parseExpression(tok, ref i, tryNotToThrow);
                if (defaultValue == null)
                    return null;
            }
            return new CsParameter
            {
                StartIndex = startIndex,
                EndIndex = tok[i - 1].EndIndex,
                Type = result.Item1,
                Name = name,
                IsThis = isThis,
                IsOut = isOut,
                IsRef = isRef,
                IsParams = isParams,
                CustomAttributes = customAttribs,
                DefaultValue = defaultValue
            };
        }
コード例 #13
0
ファイル: Parser.cs プロジェクト: Timwi/ParseCs
        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;
        }
コード例 #14
0
ファイル: Parser.cs プロジェクト: Timwi/ParseCs
        private static CsNamespace parseNamespace(TokenJar tok, ref int i)
        {
            var startIndex = tok[i].StartIndex;
            tok[i].Assert("namespace");
            i++;

            var sb = new StringBuilder(tok[i].Identifier());
            i++;
            while (tok[i].IsBuiltin("."))
            {
                i++;
                sb.Append('.');
                sb.Append(tok[i].Identifier());
                i++;
            }
            if (!tok[i].IsBuiltin("{"))
                throw new ParseException("'.' or '{' expected.", tok[i].StartIndex);
            i++;

            var ns = new CsNamespace { StartIndex = startIndex, Name = sb.ToString() };
            try
            {
                while (!tok[i].IsBuiltin("}"))
                {
                    var j = i;
                    object result = parseMemberDeclaration(tok, ref i, false);
                    if (result is CsUsingAlias)
                        ns.UsingAliases.Add((CsUsingAlias) result);
                    else if (result is CsUsingNamespace)
                        ns.UsingNamespaces.Add((CsUsingNamespace) result);
                    else if (result is CsNamespace)
                        ns.Namespaces.Add((CsNamespace) result);
                    else if (result is CsType)
                        ns.Types.Add((CsType) result);
                    else
                        throw new ParseException("Members cannot be declared directly in a namespace. Expected 'using', 'namespace', or a type declaration.", tok[j].StartIndex);
                }
                i++;
            }
            catch (LexException e)
            {
                throw new ParseException(e.Message, e.Index, ns, e);
            }
            catch (ParseException e)
            {
                if (e.IncompleteResult is CsUsingAlias)
                    ns.UsingAliases.Add((CsUsingAlias) e.IncompleteResult);
                else if (e.IncompleteResult is CsUsingNamespace)
                    ns.UsingNamespaces.Add((CsUsingNamespace) e.IncompleteResult);
                else if (e.IncompleteResult is CsNamespace)
                    ns.Namespaces.Add((CsNamespace) e.IncompleteResult);
                else if (e.IncompleteResult is CsType)
                    ns.Types.Add((CsType) e.IncompleteResult);
                throw new ParseException(e.Message, e.Index, ns, e);
            }
            ns.EndIndex = tok[i - 1].EndIndex;
            return ns;
        }
コード例 #15
0
ファイル: Parser.cs プロジェクト: Timwi/ParseCs
        private static CsExpressionBlockStatement parseWhileOrLockStatement(TokenJar tok, ref int i)
        {
            if (!tok[i].IsBuiltin("while") && !tok[i].IsBuiltin("lock"))
                tok[i].Assert("false");

            var stat = tok[i].IsBuiltin("while") ? (CsExpressionBlockStatement) new CsWhileStatement() : new CsLockStatement();
            stat.StartIndex = tok[i].StartIndex;
            i++;
            if (!tok[i].IsBuiltin("("))
                throw new ParseException("'(' expected.", tok[i].StartIndex);
            i++;
            stat.Expression = parseExpression(tok, ref i);
            if (!tok[i].IsBuiltin(")"))
                throw new ParseException("')' expected.", tok[i].StartIndex);
            i++;
            try { stat.Body = parseStatement(tok, ref i); }
            catch (ParseException e)
            {
                if (e.IncompleteResult is CsStatement)
                {
                    stat.Body = (CsStatement) e.IncompleteResult;
                    throw new ParseException(e.Message, e.Index, stat, e);
                }
                throw;
            }
            stat.EndIndex = tok[i - 1].EndIndex;
            return stat;
        }
コード例 #16
0
ファイル: Parser.cs プロジェクト: Timwi/ParseCs
        private static CsSwitchStatement parseSwitchStatement(TokenJar tok, ref int i)
        {
            tok[i].Assert("switch");

            var sw = new CsSwitchStatement { StartIndex = tok[i].StartIndex };
            i++;
            if (!tok[i].IsBuiltin("("))
                throw new ParseException("'(' expected.", tok[i].StartIndex, sw);
            i++;
            try { sw.SwitchOn = parseExpression(tok, ref i); }
            catch (ParseException e)
            {
                if (e.IncompleteResult is CsExpression)
                    sw.SwitchOn = (CsExpression) e.IncompleteResult;
                throw new ParseException(e.Message, e.Index, sw, e);
            }
            if (!tok[i].IsBuiltin(")"))
                throw new ParseException("')' expected.", tok[i].StartIndex, sw);
            i++;
            if (!tok[i].IsBuiltin("{"))
                throw new ParseException("'{' expected.", tok[i].StartIndex, sw);
            i++;
            CsSwitchCase cas = null;
            while (!tok[i].IsBuiltin("}"))
            {
                if (tok[i].IsBuiltin("case") || (tok[i].IsBuiltin("default") && !tok[i + 1].IsBuiltin("(")))
                {
                    bool isDef = tok[i].IsBuiltin("default");
                    if (cas == null || cas.Statements != null)
                    {
                        cas = new CsSwitchCase { StartIndex = tok[i].StartIndex };
                        sw.Cases.Add(cas);
                    }
                    i++;
                    cas.CaseValues.Add(isDef ? null : parseExpression(tok, ref i));
                    if (!tok[i].IsBuiltin(":"))
                        throw new ParseException("':' expected.", tok[i].StartIndex, sw);
                    cas.EndIndex = tok[i].EndIndex;
                    i++;
                }
                else if (cas == null)
                    throw new ParseException("'case' <expression> or 'default:' expected.", tok[i].StartIndex, sw);
                else
                {
                    try
                    {
                        if (cas.Statements == null)
                            cas.Statements = new List<CsStatement>();
                        var stat = parseStatement(tok, ref i);
                        cas.Statements.Add(stat);
                        cas.EndIndex = stat.EndIndex;
                    }
                    catch (ParseException e)
                    {
                        if (e.IncompleteResult is CsStatement)
                            cas.Statements.Add((CsStatement) e.IncompleteResult);
                        throw new ParseException(e.Message, e.Index, sw, e);
                    }
                }
            }
            sw.EndIndex = tok[i].EndIndex;
            i++;
            return sw;
        }
コード例 #17
0
ファイル: Parser.cs プロジェクト: Timwi/ParseCs
        private static CsCustomAttributeGroup parseCustomAttributeGroup(TokenJar tok, ref int i)
        {
            var startIndex = tok[i].StartIndex;
            tok[i].Assert("[");
            i++;

            var loc = CustomAttributeLocation.None;
            if ((tok[i].Type == TokenType.Identifier || tok[i].Type == TokenType.Builtin) && tok[i + 1].IsBuiltin(":"))
            {
                switch (tok[i].TokenStr)
                {
                    case "assembly": loc = CustomAttributeLocation.Assembly; break;
                    case "event": loc = CustomAttributeLocation.Event; break;
                    case "field": loc = CustomAttributeLocation.Field; break;
                    case "method": loc = CustomAttributeLocation.Method; break;
                    case "module": loc = CustomAttributeLocation.Module; break;
                    case "param": loc = CustomAttributeLocation.Param; break;
                    case "property": loc = CustomAttributeLocation.Property; break;
                    case "return": loc = CustomAttributeLocation.Return; break;
                    case "type": loc = CustomAttributeLocation.Type; break;
                    case "typevar": loc = CustomAttributeLocation.Typevar; break;
                    default:
                        throw new ParseException("Unrecognized custom attribute location. Valid locations are: 'assembly', 'event', 'field', 'method', 'module', 'param', 'property', 'return', 'type', 'typevar'.", tok[i].StartIndex);
                }
                i += 2;
            }

            List<CsCustomAttribute> group = new List<CsCustomAttribute>();
            while (true)
            {
                var attributeStartIndex = tok[i].StartIndex;
                var type = parseTypeName(tok, ref i, 0).Item1;
                var attr = new CsCustomAttribute { StartIndex = attributeStartIndex, EndIndex = tok[i - 1].EndIndex, Type = type };
                group.Add(attr);
                if (tok[i].IsBuiltin("]"))
                {
                    i++;
                    break;
                }
                else if (tok[i].IsBuiltin(","))
                {
                    i++;
                    continue;
                }
                else if (!tok[i].IsBuiltin("("))
                    throw new ParseException("'(', ',' or ']' expected.", tok[i].StartIndex);
                i++;
                bool acceptPositionalArgs = true;
                bool acceptOptionalArgs = true;
                bool expectComma = false;
                while (!tok[i].IsBuiltin(")"))
                {
                    if (expectComma)
                    {
                        if (!tok[i].IsBuiltin(","))
                            throw new ParseException("'',' or ')' expected. (1)", tok[i].StartIndex);
                        i++;
                    }
                    expectComma = true;
                    var argStartIndex = tok[i].StartIndex;
                    if (tok[i].Type == TokenType.Identifier && tok[i + 1].IsBuiltin("="))
                    {
                        acceptPositionalArgs = false;
                        acceptOptionalArgs = false;
                        var posName = tok[i].TokenStr;
                        i += 2;
                        var expr = parseExpression(tok, ref i);
                        attr.PropertySetters.Add(new CsNameAndExpression { StartIndex = argStartIndex, EndIndex = tok[i - 1].EndIndex, Name = posName, Expression = expr });
                    }
                    else if (acceptOptionalArgs && tok[i].Type == TokenType.Identifier && tok[i + 1].IsBuiltin(":"))
                    {
                        acceptPositionalArgs = false;
                        var posName = tok[i].TokenStr;
                        i += 2;
                        var expr = parseExpression(tok, ref i);
                        attr.Arguments.Add(new CsArgument { StartIndex = argStartIndex, EndIndex = tok[i - 1].EndIndex, Name = posName, Expression = expr });
                    }
                    else if (acceptPositionalArgs)
                    {
                        var expr = parseExpression(tok, ref i);
                        attr.Arguments.Add(new CsArgument { StartIndex = argStartIndex, EndIndex = tok[i - 1].EndIndex, Expression = expr });
                    }
                    else
                        throw new ParseException("Identifier '=' <expression>, or ')' expected.", tok[i].StartIndex);
                }
                i++;
                if (tok[i].IsBuiltin("]"))
                {
                    i++;
                    break;
                }
                else if (tok[i].IsBuiltin(","))
                {
                    i++;
                    continue;
                }
                else
                    throw new ParseException("']' or ',' expected. (1)", tok[i].StartIndex);
            }
            return new CsCustomAttributeGroup { StartIndex = startIndex, EndIndex = tok[i - 1].EndIndex, CustomAttributes = group, Location = loc };
        }
コード例 #18
0
ファイル: Parser.cs プロジェクト: Timwi/ParseCs
        private static CsTryStatement parseTryStatement(TokenJar tok, ref int i)
        {
            tok[i].Assert("try");
            var tr = new CsTryStatement { StartIndex = tok[i].StartIndex };

            i++;
            if (!tok[i].IsBuiltin("{"))
                throw new ParseException("'{' expected after 'try'.", tok[i].StartIndex);
            tr.TryBody = parseBlock(tok, ref i);
            if (!tok[i].IsBuiltin("catch") && !tok[i].IsBuiltin("finally"))
                throw new ParseException("'catch' or 'finally' expected after 'try' block.", tok[i].StartIndex);
            while (tok[i].IsBuiltin("catch"))
            {
                var ctch = new CsCatchClause { StartIndex = tok[i].StartIndex };
                i++;
                tr.Catches.Add(ctch);
                if (tok[i].IsBuiltin("("))
                {
                    i++;
                    ctch.Type = parseTypeName(tok, ref i, typeIdentifierFlags.AllowArrays | typeIdentifierFlags.AllowKeywords | typeIdentifierFlags.AllowNullablesAndPointers).Item1;
                    if (tok[i].Type == TokenType.Identifier)
                    {
                        ctch.Name = tok[i].TokenStr;
                        i++;
                    }
                    if (!tok[i].IsBuiltin(")"))
                        throw new ParseException("')' expected.", tok[i].StartIndex);
                    i++;
                }
                if (!tok[i].IsBuiltin("{"))
                    throw new ParseException("'{' expected.", tok[i].StartIndex);
                try { ctch.Body = parseBlock(tok, ref i); }
                catch (ParseException e)
                {
                    if (e.IncompleteResult is CsBlock)
                        ctch.Body = (CsBlock) e.IncompleteResult;
                    throw new ParseException(e.Message, e.Index, tr, e);
                }
                ctch.EndIndex = tok[i - 1].EndIndex;
            }
            if (tok[i].IsBuiltin("finally"))
            {
                i++;
                if (!tok[i].IsBuiltin("{"))
                    throw new ParseException("'{' expected after 'finally'.", tok[i].StartIndex);
                try { tr.Finally = parseBlock(tok, ref i); }
                catch (ParseException e)
                {
                    if (e.IncompleteResult is CsBlock)
                        tr.Finally = (CsBlock) e.IncompleteResult;
                    throw new ParseException(e.Message, e.Index, tr, e);
                }
            }
            tr.EndIndex = tok[i - 1].EndIndex;
            return tr;
        }
コード例 #19
0
ファイル: Parser.cs プロジェクト: Timwi/ParseCs
 private static CsDestructor parseDestructorDeclaration(TokenJar tok, ref int i, List<CsCustomAttributeGroup> customAttribs, int j)
 {
     CsDestructor des = new CsDestructor { StartIndex = tok[i].StartIndex, Name = tok[j + 1].TokenStr, CustomAttributes = customAttribs };
     parseModifiers(des, tok, ref i);
     if (i != j)
         throw new ParseException("The modifier '{0}' is not valid for destructors.".Fmt(tok[i].TokenStr), tok[i].StartIndex);
     i = j + 3;
     if (!tok[i].IsBuiltin(")"))
         throw new ParseException(@"Destructors cannot have any parameters.", tok[i].StartIndex);
     i++;
     if (!tok[i].IsBuiltin("{"))
         throw new ParseException("'{' expected.", tok[i].StartIndex);
     try { des.MethodBody = parseBlock(tok, ref i); }
     catch (ParseException e)
     {
         if (e.IncompleteResult is CsBlock)
         {
             des.MethodBody = (CsBlock) e.IncompleteResult;
             throw new ParseException(e.Message, e.Index, des, e);
         }
         throw;
     }
     return des;
 }
コード例 #20
0
ファイル: Parser.cs プロジェクト: Timwi/ParseCs
        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);
        }
コード例 #21
0
ファイル: Parser.cs プロジェクト: Timwi/ParseCs
        private static CsEnum parseEnumDeclaration(TokenJar tok, ref int i, List<CsCustomAttributeGroup> customAttribs, int j)
        {
            var en = new CsEnum { StartIndex = tok[i].StartIndex, CustomAttributes = customAttribs };
            parseModifiers(en, tok, ref i);
            if (i != j)
                throw new ParseException("The modifier '{0}' is not valid for enums.".Fmt(tok[i].TokenStr), tok[i].StartIndex);
            i = j + 1;
            en.Name = tok[i].Identifier();
            i++;

            if (tok[i].IsBuiltin(":"))
            {
                i++;
                en.BaseType = parseTypeName(tok, ref i, typeIdentifierFlags.AllowKeywords).Item1;
            }

            if (!tok[i].IsBuiltin("{"))
                throw new ParseException("'{' expected.", tok[i].StartIndex, en);
            i++;

            while (true)
            {
                var startIndex = tok[i].StartIndex;
                string ident;
                var cAttribs = new List<CsCustomAttributeGroup>();
                try
                {
                    while (tok[i].IsBuiltin("["))
                        cAttribs.Add(parseCustomAttributeGroup(tok, ref i));
                    ident = tok[i].Identifier("Enum value expected.");
                }
                catch (ParseException e)
                {
                    throw new ParseException(e.Message, e.Index, en, e);
                }
                var val = new CsEnumValue { StartIndex = startIndex, Name = ident, CustomAttributes = cAttribs };
                en.EnumValues.Add(val);
                i++;

                if (tok[i].IsBuiltin("="))
                {
                    i++;
                    try { val.LiteralValue = parseExpression(tok, ref i); }
                    catch (ParseException e)
                    {
                        if (e.IncompleteResult is CsExpression)
                            val.LiteralValue = (CsExpression) e.IncompleteResult;
                        throw new ParseException(e.Message, e.Index, en, e);
                    }
                }
                if (!tok[i].IsBuiltin(",") && !tok[i].IsBuiltin("}"))
                    throw new ParseException(val.LiteralValue == null ? "'=', ',' or '}' expected." : "',' or '}' expected.", tok[i].StartIndex, en);
                val.EndIndex = tok[i - 1].EndIndex;
                if (tok[i].IsBuiltin(","))
                    i++;

                if (tok[i].IsBuiltin("}"))
                {
                    en.EndIndex = tok[i].EndIndex;
                    i++;
                    // Skip optional ';' after type declaration
                    if (tok[i].IsBuiltin(";"))
                        i++;
                    return en;
                }
            }
        }
コード例 #22
0
ファイル: Parser.cs プロジェクト: Timwi/ParseCs
 private static CsConstructor parseConstructorDeclaration(TokenJar tok, ref int i, List<CsCustomAttributeGroup> customAttribs, int j)
 {
     CsConstructor con = new CsConstructor { StartIndex = tok[i].StartIndex, Name = tok[j].TokenStr, CustomAttributes = customAttribs };
     parseModifiers(con, tok, ref i);
     if (i != j)
         throw new ParseException("The modifier '{0}' is not valid for constructors.".Fmt(tok[i].TokenStr), tok[i].StartIndex);
     i = j + 1;
     con.Parameters = parseParameterList(tok, ref i);
     bool canHaveColon = true;
     if (tok[i].IsBuiltin(":"))
     {
         canHaveColon = false;
         i++;
         if (tok[i].IsBuiltin("this"))
             con.CallType = ConstructorCallType.This;
         else if (tok[i].IsBuiltin("base"))
             con.CallType = ConstructorCallType.Base;
         else
             throw new ParseException("'this' or 'base' expected.", tok[i].StartIndex);
         i++;
         if (!tok[i].IsBuiltin("("))
             throw new ParseException("'(' expected.", tok[i].StartIndex);
         con.MethodBody = new CsBlock { StartIndex = tok[i].StartIndex };  // temporary; just so we can throw a valid constructor as an incomplete result
         try
         {
             bool dummy;
             con.CallArguments = parseArgumentList(tok, ref i, out dummy);
         }
         catch (ParseException e)
         {
             if (e.IncompleteResult is List<CsArgument>)
                 con.CallArguments = (List<CsArgument>) e.IncompleteResult;
             throw new ParseException(e.Message, e.Index, con, e);
         }
     }
     if (!tok[i].IsBuiltin("{"))
         throw new ParseException(canHaveColon ? "':' or '{' expected." : "'{' expected.", tok[i].StartIndex);
     try { con.MethodBody = parseBlock(tok, ref i); }
     catch (ParseException e)
     {
         if (e.IncompleteResult is CsBlock)
         {
             con.MethodBody = (CsBlock) e.IncompleteResult;
             throw new ParseException(e.Message, e.Index, con, e);
         }
         throw;
     }
     con.EndIndex = tok[i - 1].EndIndex;
     return con;
 }
コード例 #23
0
ファイル: Parser.cs プロジェクト: Timwi/ParseCs
 private static CsExpression parseExpression(TokenJar tok, ref int i, bool tryNotToThrow = false)
 {
     var left = parseExpressionConditional(tok, ref i, tryNotToThrow);
     if (left == null)
         return null;
     if (tok[i].Type == TokenType.Builtin && assignmentOperators.Contains(tok[i].TokenStr))
     {
         AssignmentOperator type;
         switch (tok[i].TokenStr)
         {
             case "=": type = AssignmentOperator.Eq; break;
             case "*=": type = AssignmentOperator.TimesEq; break;
             case "/=": type = AssignmentOperator.DivEq; break;
             case "%=": type = AssignmentOperator.ModEq; break;
             case "+=": type = AssignmentOperator.PlusEq; break;
             case "-=": type = AssignmentOperator.MinusEq; break;
             case "<<=": type = AssignmentOperator.ShlEq; break;
             case ">>=": type = AssignmentOperator.ShrEq; break;
             case "&=": type = AssignmentOperator.AndEq; break;
             case "^=": type = AssignmentOperator.XorEq; break;
             case "|=": type = AssignmentOperator.OrEq; break;
             default: throw new ParseException("Unknown assigment operator.", tok[i].StartIndex, left);
         }
         i++;
         var right = parseExpression(tok, ref i);
         return new CsAssignmentExpression { StartIndex = left.StartIndex, EndIndex = right.EndIndex, Left = left, Right = right, Operator = type };
     }
     return left;
 }
コード例 #24
0
ファイル: Parser.cs プロジェクト: Timwi/ParseCs
        private static CsUsing parseUsingDeclaration(TokenJar tok, ref int i)
        {
            var startIndex = tok[i].StartIndex;
            tok[i].Assert("using");
            i++;

            var firstIdent = tok[i].Identifier();
            i++;
            if (tok[i].IsBuiltin("="))
            {
                i++;
                var typeIdent = parseTypeName(tok, ref i, 0).Item1;
                if (!tok[i].IsBuiltin(";"))
                    throw new ParseException("';' expected. (1)", tok[i].StartIndex);
                i++;
                return new CsUsingAlias { StartIndex = startIndex, EndIndex = tok[i - 1].EndIndex, Alias = firstIdent, Original = typeIdent };
            }
            else if (tok[i].IsBuiltin("."))
            {
                i++;
                var sb = new StringBuilder(firstIdent);
                sb.Append('.');
                sb.Append(tok[i].Identifier());
                i++;
                while (tok[i].IsBuiltin("."))
                {
                    sb.Append('.');
                    i++;
                    sb.Append(tok[i].Identifier());
                    i++;
                }
                if (!tok[i].IsBuiltin(";"))
                    throw new ParseException("'.' or ';' expected.", tok[i].StartIndex);
                i++;
                return new CsUsingNamespace { StartIndex = startIndex, EndIndex = tok[i - 1].EndIndex, Namespace = sb.ToString() };
            }
            else if (tok[i].IsBuiltin(";"))
            {
                i++;
                return new CsUsingNamespace { StartIndex = startIndex, EndIndex = tok[i - 1].EndIndex, Namespace = firstIdent };
            }

            throw new ParseException("'=', '.' or ';' expected.", tok[i].StartIndex);
        }
コード例 #25
0
ファイル: Parser.cs プロジェクト: Timwi/ParseCs
        private static CsExpression parseExpressionConditional(TokenJar tok, ref int i, bool tryNotToThrow = false)
        {
            var left = parseExpressionCoalesce(tok, ref i, tryNotToThrow);
            if (left == null)
                return null;
            bool haveQ = false;
            if (tok[i].IsBuiltin("?"))
            {
                haveQ = true;
                i++;
            }
            else
            {
                // This is very hacky, but I couldn't find a better way. We need to handle the following cases:
                //     myObj is int ? 5 : 1
                //     myObj is int? ? 5 : 1
                // We are fine for the second case, but in order to get that second case to work, we had to parse 'int?' as a type.
                // Therefore, if the input is actually the first case (which is more common), we have to track down that type and remove the question mark from it.
                haveQ = removeNullable(left);
            }

            if (haveQ)
            {
                CsExpression truePart;
                try { truePart = parseExpression(tok, ref i); }
                catch (ParseException e) { throw new ParseException(e.Message, e.Index, left, e); }
                if (!tok[i].IsBuiltin(":"))
                    throw new ParseException("Unterminated conditional operator. ':' expected.", tok[i].StartIndex, left);
                i++;
                try
                {
                    var falsePart = parseExpression(tok, ref i);
                    return new CsConditionalExpression { StartIndex = left.StartIndex, EndIndex = falsePart.EndIndex, Condition = left, TruePart = truePart, FalsePart = falsePart };
                }
                catch (ParseException e)
                {
                    if (e.IncompleteResult is CsExpression)
                        throw new ParseException(e.Message, e.Index, new CsConditionalExpression { StartIndex = tok[i].StartIndex, Condition = left, TruePart = truePart, FalsePart = (CsExpression) e.IncompleteResult }, e);
                    throw new ParseException(e.Message, e.Index, left, e);
                }
            }
            return left;
        }
コード例 #26
0
ファイル: Parser.cs プロジェクト: Timwi/ParseCs
        private static CsUsingStatement parseUsingStatement(TokenJar tok, ref int i)
        {
            tok[i].Assert("using");
            var startIndex = tok[i].StartIndex;

            i++;
            if (!tok[i].IsBuiltin("("))
                throw new ParseException("'(' expected.", tok[i].StartIndex);
            i++;
            var usin = new CsUsingStatement { StartIndex = startIndex, InitializationStatement = parseVariableDeclarationOrExpressionStatement(tok, ref i) };
            if (!tok[i].IsBuiltin(")"))
                throw new ParseException("')' expected.", tok[i].StartIndex);
            i++;

            try { usin.Body = parseStatement(tok, ref i); }
            catch (ParseException e)
            {
                if (e.IncompleteResult is CsStatement)
                {
                    usin.Body = (CsStatement) e.IncompleteResult;
                    throw new ParseException(e.Message, e.Index, usin, e);
                }
                throw;
            }
            usin.EndIndex = tok[i - 1].EndIndex;
            return usin;
        }
コード例 #27
0
ファイル: Parser.cs プロジェクト: Timwi/ParseCs
 private static CsExpression parseExpressionEquality(TokenJar tok, ref int i, bool tryNotToThrow = false)
 {
     var left = parseExpressionRelational(tok, ref i, tryNotToThrow);
     if (left == null)
         return null;
     while (tok[i].IsBuiltin("==") || tok[i].IsBuiltin("!="))
     {
         BinaryOperator op = tok[i].IsBuiltin("==") ? BinaryOperator.Eq : BinaryOperator.NotEq;
         i++;
         try
         {
             var right = parseExpressionRelational(tok, ref i);
             left = new CsBinaryOperatorExpression { StartIndex = left.StartIndex, EndIndex = right.EndIndex, Left = left, Right = right, Operator = op };
         }
         catch (ParseException e)
         {
             if (e.IncompleteResult is CsExpression)
                 throw new ParseException(e.Message, e.Index, new CsBinaryOperatorExpression { StartIndex = tok[i].StartIndex, Left = left, Right = (CsExpression) e.IncompleteResult, Operator = op }, e);
             throw new ParseException(e.Message, e.Index, left, e);
         }
     }
     return left;
 }
コード例 #28
0
ファイル: Parser.cs プロジェクト: Timwi/ParseCs
        private static CsStatement parseVariableDeclarationOrExpressionStatement(TokenJar tok, ref int i)
        {
            var startIndex = tok[i].StartIndex;
            bool isConst = false;

            if (tok[i].IsBuiltin("const"))
            {
                isConst = true;
                i++;
            }

            // See if the beginning of this statement is a type identifier followed by a variable name, in which case parse it as a variable declaration.
            CsTypeName declType = null;
            var j = i;
            try
            {
                var ty = parseTypeName(tok, ref j, typeIdentifierFlags.AllowArrays | typeIdentifierFlags.AllowKeywords | typeIdentifierFlags.AllowNullablesAndPointers).Item1;
                if (tok[j].Type == TokenType.Identifier)
                    declType = ty;
            }
            catch (ParseException) { }

            // If this looks like a valid variable declaration, continue parsing it.
            if (declType != null)
            {
                i = j - 1;
                var decl = new CsVariableDeclarationStatement { StartIndex = startIndex, Type = declType, IsConst = isConst };
                string name = null;
                try
                {
                    do
                    {
                        i++;
                        var nameStartIndex = tok[i].StartIndex;
                        name = tok[i].Identifier();
                        i++;
                        CsExpression expr = null;
                        if (tok[i].IsBuiltin("="))
                        {
                            i++;
                            if (tok[i].IsBuiltin("stackalloc"))
                            {
                                var stackAllocStartIndex = tok[i].StartIndex;
                                i++;
                                var stackAllocType = parseTypeName(tok, ref i, typeIdentifierFlags.AllowKeywords | typeIdentifierFlags.Lenient).Item1;
                                if (!tok[i].IsBuiltin("["))
                                    throw new ParseException("'[' expected.", tok[i].StartIndex);
                                i++;
                                var stackAllocExpr = parseExpression(tok, ref i);
                                if (!tok[i].IsBuiltin("]"))
                                    throw new ParseException("']' expected.", tok[i].StartIndex);
                                expr = new CsStackAllocExpression { StartIndex = stackAllocStartIndex, EndIndex = tok[i].EndIndex, Type = stackAllocType, SizeExpression = stackAllocExpr };
                                i++;
                            }
                            else
                                expr = parseExpression(tok, ref i);
                        }
                        decl.NamesAndInitializers.Add(new CsNameAndExpression { StartIndex = nameStartIndex, EndIndex = tok[i - 1].EndIndex, Name = name, Expression = expr });
                    }
                    while (tok[i].IsBuiltin(","));
                }
                catch (ParseException e)
                {
                    if (e.IncompleteResult is CsExpression && name != null)
                        decl.NamesAndInitializers.Add(new CsNameAndExpression { StartIndex = tok[i].StartIndex, Name = name, Expression = (CsExpression) e.IncompleteResult });
                    throw new ParseException(e.Message, e.Index, decl, e);
                }

                // This function does not consume the trailing ‘;’ of the statement, so that ‘using’ and ‘for’ can use it (where the statement can end with a ‘)’ or ‘,’ instead).
                decl.EndIndex = tok[i - 1].EndIndex;
                return decl;
            }

            if (isConst)
                throw new ParseException("Expected type + variable declaration after 'const'.", tok[i].StartIndex);

            // Finally, the only remaining possible way for it to be a valid statement is by being an expression.
            var exprStat = new CsExpressionStatement { StartIndex = tok[i].StartIndex };
            try { exprStat.Expression = parseExpression(tok, ref i); }
            catch (ParseException e)
            {
                var msg = exprStat.StartIndex == e.Index ? @"Invalid statement." : e.Message;
                if (e.IncompleteResult is CsExpression)
                {
                    exprStat.Expression = (CsExpression) e.IncompleteResult;
                    throw new ParseException(msg, e.Index, exprStat, e);
                }
                throw new ParseException(msg, e.Index, e.IncompleteResult, e);
            }

            // This function does not consume the trailing ‘;’ of the statement, so that ‘using’ and ‘for’ can use it (where the statement can end with a ‘)’ or ‘,’ instead).
            return exprStat;
        }
コード例 #29
0
ファイル: Parser.cs プロジェクト: Timwi/ParseCs
        private static CsExpression parseExpressionIdentifierOrKeyword(TokenJar tok, ref int i)
        {
            var startIndex = tok[i].StartIndex;

            if (tok[i].IsIdentifier("from") && tok[i + 1].Type == TokenType.Identifier)
            {
                var linq = new CsLinqExpression { StartIndex = startIndex };
                parseLinqQueryExpression(linq, tok, ref i);
                linq.EndIndex = tok[i - 1].EndIndex;
                return linq;
            }

            if (tok[i].IsBuiltin("{"))
            {
                try
                {
                    var exprs = parseArrayLiteral(tok, ref i);
                    return new CsArrayLiteralExpression { StartIndex = startIndex, EndIndex = tok[i - 1].EndIndex, Expressions = exprs };
                }
                catch (ParseException e)
                {
                    if (e.IncompleteResult is List<CsExpression>)
                        throw new ParseException(e.Message, e.Index, new CsArrayLiteralExpression { StartIndex = startIndex, Expressions = (List<CsExpression>) e.IncompleteResult }, e);
                    throw;
                }
            }

            if (tok[i].Type == TokenType.CharacterLiteral)
                return new CsCharacterLiteralExpression { StartIndex = startIndex, EndIndex = tok[i].EndIndex, Literal = tok[i++].TokenStr[0] };

            if (tok[i].Type == TokenType.StringLiteral)
                return new CsStringLiteralExpression { StartIndex = startIndex, EndIndex = tok[i].EndIndex, Literal = tok[i++].TokenStr };

            if (tok[i].Type == TokenType.NumberLiteral)
                return new CsNumberLiteralExpression { StartIndex = startIndex, EndIndex = tok[i].EndIndex, Literal = tok[i++].TokenStr };

            if (tok[i].IsBuiltin("true")) { i++; return new CsBooleanLiteralExpression { StartIndex = tok[i - 1].StartIndex, EndIndex = tok[i - 1].EndIndex, Literal = true }; }
            if (tok[i].IsBuiltin("false")) { i++; return new CsBooleanLiteralExpression { StartIndex = tok[i - 1].StartIndex, EndIndex = tok[i - 1].EndIndex, Literal = false }; }
            if (tok[i].IsBuiltin("null")) { i++; return new CsNullExpression { StartIndex = tok[i - 1].StartIndex, EndIndex = tok[i - 1].EndIndex }; }
            if (tok[i].IsBuiltin("this")) { i++; return new CsThisExpression { StartIndex = tok[i - 1].StartIndex, EndIndex = tok[i - 1].EndIndex }; }
            if (tok[i].IsBuiltin("base")) { i++; return new CsBaseExpression { StartIndex = tok[i - 1].StartIndex, EndIndex = tok[i - 1].EndIndex }; }

            if (tok[i].IsBuiltin("typeof") || tok[i].IsBuiltin("default") || tok[i].IsBuiltin("sizeof"))
            {
                var typof = tok[i].IsBuiltin("typeof");
                var expr = typof ? new CsTypeofExpression() : tok[i].IsBuiltin("default") ? (CsTypeOperatorExpression) new CsDefaultExpression() : new CsSizeofExpression();
                expr.StartIndex = startIndex;
                i++;
                if (!tok[i].IsBuiltin("("))
                    throw new ParseException("'(' expected after 'typeof' or 'default'.", tok[i].StartIndex);
                i++;
                CsTypeName ty;
                var tif = typeIdentifierFlags.AllowKeywords | typeIdentifierFlags.AllowNullablesAndPointers | typeIdentifierFlags.AllowArrays;
                try { ty = parseTypeName(tok, ref i, typof ? tif | typeIdentifierFlags.AllowEmptyGenerics : tif).Item1; }
                catch (ParseException e)
                {
                    if (e.IncompleteResult is CsTypeName)
                        expr.Type = (CsTypeName) e.IncompleteResult;
                    throw new ParseException(e.Message, e.Index, expr, e);
                }
                expr.Type = ty;
                if (!tok[i].IsBuiltin(")"))
                    throw new ParseException("')' expected.", tok[i].StartIndex, expr);
                expr.EndIndex = tok[i].EndIndex;
                i++;
                return expr;
            }

            if (tok[i].IsBuiltin("checked") || tok[i].IsBuiltin("unchecked"))
            {
                var expr = tok[i].IsBuiltin("checked") ? (CsCheckedUncheckedExpression) new CsCheckedExpression() : new CsUncheckedExpression();
                expr.StartIndex = startIndex;
                i++;
                if (!tok[i].IsBuiltin("("))
                    throw new ParseException("'(' expected after 'checked' or 'unchecked'.", tok[i].StartIndex);
                i++;
                expr.Subexpression = parseExpression(tok, ref i);
                if (!tok[i].IsBuiltin(")"))
                    throw new ParseException("')' expected.", tok[i].StartIndex, expr);
                expr.EndIndex = tok[i].EndIndex;
                i++;
                return expr;
            }

            if (tok[i].IsBuiltin("new"))
            {
                i++;
                if (tok[i].IsBuiltin("{"))
                {
                    var anon = new CsNewAnonymousTypeExpression { StartIndex = startIndex };
                    try { anon.Initializers = parseArrayLiteral(tok, ref i); }
                    catch (ParseException e)
                    {
                        if (e.IncompleteResult is List<CsExpression>)
                            anon.Initializers = (List<CsExpression>) e.IncompleteResult;
                        throw new ParseException(e.Message, e.Index, anon, e);
                    }
                    anon.EndIndex = tok[i - 1].EndIndex;
                    return anon;
                }
                else if (tok[i].IsBuiltin("[") && tok[i + 1].IsBuiltin("]"))
                {
                    i += 2;
                    // Implicitly-typed array
                    if (!tok[i].IsBuiltin("{"))
                        throw new ParseException("'{' expected.", tok[i].StartIndex);
                    try
                    {
                        var items = parseArrayLiteral(tok, ref i);
                        return new CsNewImplicitlyTypedArrayExpression { StartIndex = startIndex, EndIndex = tok[i - 1].EndIndex, Items = items };
                    }
                    catch (ParseException e)
                    {
                        if (e.IncompleteResult is List<CsExpression>)
                            throw new ParseException(e.Message, e.Index, new CsNewImplicitlyTypedArrayExpression { StartIndex = startIndex, Items = (List<CsExpression>) e.IncompleteResult }, e);
                        throw;
                    }
                }
                else if (tok[i].Type != TokenType.Identifier && tok[i].Type != TokenType.Builtin)
                    throw new ParseException("'{', '[' or type expected.", tok[i].StartIndex);
                else
                {
                    var ty = parseTypeName(tok, ref i, typeIdentifierFlags.AllowKeywords | typeIdentifierFlags.AllowNullablesAndPointers | typeIdentifierFlags.AllowArrays | typeIdentifierFlags.Lenient).Item1;
                    if (tok[i].IsBuiltin("("))
                    {
                        // Constructor call with parameters
                        var constructor = new CsNewConstructorExpression { StartIndex = startIndex, Type = ty };
                        try
                        {
                            bool dummy;
                            constructor.Arguments = parseArgumentList(tok, ref i, out dummy);
                            if (tok[i].IsBuiltin("{"))
                                constructor.Initializers = parseArrayLiteral(tok, ref i);
                        }
                        catch (ParseException e)
                        {
                            if (e.IncompleteResult is List<CsArgument>)
                                constructor.Arguments = (List<CsArgument>) e.IncompleteResult;
                            else if (e.IncompleteResult is List<CsExpression>)
                                constructor.Initializers = (List<CsExpression>) e.IncompleteResult;
                            throw new ParseException(e.Message, e.Index, constructor, e);
                        }
                        constructor.EndIndex = tok[i - 1].EndIndex;
                        return constructor;
                    }
                    else if (tok[i].IsBuiltin("{"))
                    {
                        // Constructor call without parameters
                        var constructor = new CsNewConstructorExpression { StartIndex = startIndex, Type = ty };
                        try { constructor.Initializers = parseArrayLiteral(tok, ref i); }
                        catch (ParseException e)
                        {
                            if (e.IncompleteResult is List<CsExpression>)
                                constructor.Initializers = (List<CsExpression>) e.IncompleteResult;
                            throw new ParseException(e.Message, e.Index, constructor, e);
                        }
                        constructor.EndIndex = tok[i - 1].EndIndex;
                        return constructor;
                    }
                    else if (tok[i].IsBuiltin("["))
                    {
                        // Array construction
                        bool dummy;
                        var k = i;
                        var ret = new CsNewArrayExpression { StartIndex = startIndex, Type = ty };
                        ret.SizeExpressions.AddRange(parseArgumentList(tok, ref i, out dummy).Select(p =>
                        {
                            if (p.Mode != ArgumentMode.In)
                                throw new ParseException("'out' and 'ref' parameters are not allowed in an array constructor.", tok[k].StartIndex);
                            return p.Expression;
                        }));
                        while (tok[i].IsBuiltin("["))
                        {
                            i++;
                            int num = 1;
                            while (tok[i].IsBuiltin(","))
                            {
                                num++;
                                i++;
                            }
                            if (!tok[i].IsBuiltin("]"))
                                throw new ParseException("',' or ']' expected.", tok[i].StartIndex, ret);
                            i++;
                            ret.AdditionalRanks.Add(num);
                        }
                        if (tok[i].IsBuiltin("{"))
                        {
                            try { ret.Items = parseArrayLiteral(tok, ref i); }
                            catch (ParseException e)
                            {
                                if (e.IncompleteResult is List<CsExpression>)
                                    ret.Items = (List<CsExpression>) e.IncompleteResult;
                                throw new ParseException(e.Message, e.Index, ret, e);
                            }
                        }
                        ret.EndIndex = tok[i - 1].EndIndex;
                        return ret;
                    }
                    else
                        throw new ParseException("'(', '[' or '{' expected.", tok[i].StartIndex);
                }
            }

            if (tok[i].IsBuiltin("delegate"))
            {
                i++;
                var delegateParams = tok[i].IsBuiltin("(") ? parseParameterList(tok, ref i) : null;
                var block = parseBlock(tok, ref i);
                return new CsAnonymousMethodExpression { StartIndex = startIndex, EndIndex = tok[i - 1].EndIndex, Body = block, Parameters = delegateParams };
            }

            // See if this could be a lambda expression. If it is, set 'parameters' to something non-null and move 'i' to behind the '=>'. Otherwise, keep 'i' unchanged.
            List<CsParameter> parameters = null;
            if (tok[i].Type == TokenType.Identifier && tok[i + 1].IsBuiltin("=>"))
            {
                // p => ...
                parameters = new List<CsParameter> { new CsParameter { StartIndex = startIndex, EndIndex = tok[i].EndIndex, Name = tok[i].TokenStr } };
                i += 2;
            }
            else if (tok[i].IsBuiltin("("))
            {
                // () => ...
                if (tok[i + 1].IsBuiltin(")") && tok[i + 2].IsBuiltin("=>"))
                {
                    parameters = new List<CsParameter>();
                    i += 3;
                }
                else
                {
                    // Try (Type p1, Type p2) => ...
                    try
                    {
                        var j = i;
                        var tried = parseParameterList(tok, ref j, tryNotToThrow: true);
                        if (tried != null && tok[j].IsBuiltin("=>"))
                        {
                            i = j + 1;
                            parameters = tried;
                        }
                    }
                    catch { }
                }

                // If (Type p1, Type p2) => ... still didn't work, try (p1, p2) => ...
                if (parameters == null && tok[i + 1].Type == TokenType.Identifier)
                {
                    var ps = new List<CsParameter> { new CsParameter { StartIndex = tok[i + 1].StartIndex, EndIndex = tok[i + 1].EndIndex, Name = tok[i + 1].TokenStr } };
                    var j = i + 2;
                    while (tok[j].IsBuiltin(",") && tok[j + 1].Type == TokenType.Identifier)
                    {
                        ps.Add(new CsParameter { StartIndex = tok[j + 1].StartIndex, EndIndex = tok[j + 1].EndIndex, Name = tok[j + 1].TokenStr });
                        j += 2;
                    }
                    if (tok[j].IsBuiltin(")") && tok[j + 1].IsBuiltin("=>"))
                    {
                        parameters = ps;
                        i = j + 2;
                    }
                }
            }

            // If 'parameters' is non-null, we found a lambda expression and 'i' is pointing behind the '=>'.
            if (parameters != null)
            {
                if (tok[i].IsBuiltin("{"))
                {
                    var lambda = new CsBlockLambdaExpression { StartIndex = startIndex, Parameters = parameters };
                    try { lambda.Body = parseBlock(tok, ref i); }
                    catch (ParseException e)
                    {
                        if (e.IncompleteResult is CsBlock)
                        {
                            lambda.Body = (CsBlock) e.IncompleteResult;
                            throw new ParseException(e.Message, e.Index, lambda, e);
                        }
                        throw;
                    }
                    lambda.EndIndex = lambda.Body.EndIndex;
                    return lambda;
                }
                else
                {
                    var lambda = new CsSimpleLambdaExpression { StartIndex = startIndex, Parameters = parameters };
                    try { lambda.Body = parseExpression(tok, ref i); }
                    catch (ParseException e)
                    {
                        if (e.IncompleteResult is CsExpression)
                        {
                            lambda.Body = (CsExpression) e.IncompleteResult;
                            throw new ParseException(e.Message, e.Index, lambda, e);
                        }
                        throw;
                    }
                    lambda.EndIndex = lambda.Body.EndIndex;
                    return lambda;
                }
            }

            // If we get to here, then there is an open-parenthesis that is not the beginning of a lambda expression parameter list.
            if (tok[i].IsBuiltin("("))
            {
                i++;

                // Every time we encounter an open-parenthesis, we don't know in advance whether it's a cast, a sub-expression, or a lambda expression.
                // We've already checked for lambda expression above, so we can rule that one out, but to check whether it's a cast, we need to tentatively
                // parse it, see if it is followed by ')', and then follow a heuristic specified in the C# standard.

                CsTypeName typeName = null;
                CsExpression expression = null;
                int afterType = i;
                int afterExpression = i;

                // Does it parse as a type?
                try
                {
                    var result = parseTypeName(tok, ref afterType, typeIdentifierFlags.AllowKeywords | typeIdentifierFlags.AllowNullablesAndPointers | typeIdentifierFlags.AllowArrays, true);
                    if (result != null && tok[afterType].IsBuiltin(")"))
                    {
                        typeName = result.Item1;
                        afterType++;
                    }
                }
                catch { }

                // Does it parse as a parenthesised expression?
                try
                {
                    expression = parseExpression(tok, ref afterExpression, true);
                    if (!tok[afterExpression].IsBuiltin(")"))
                        throw new ParseException("')' expected after parenthesised expression.", tok[afterExpression].StartIndex);
                    afterExpression++;
                }
                catch (ParseException)
                {
                    // It’s neither an expression nor a valid type name.
                    if (typeName == null)
                        throw;
                }

                // According to the Standard, we are supposed to interpret this as a cast if:
                // • it definitely doesn’t parse as an expression; OR
                // • the next token is one of a special set

                if (expression == null || (typeName != null && (
                    tok[afterType].IsBuiltin("~") || tok[afterType].IsBuiltin("!") || tok[afterType].IsBuiltin("(") ||
                    tok[afterType].Type == TokenType.Identifier || tok[afterType].Type == TokenType.CharacterLiteral ||
                    tok[afterType].Type == TokenType.NumberLiteral || tok[afterType].Type == TokenType.StringLiteral ||
                    (tok[afterType].Type == TokenType.Builtin && tok[afterType].TokenStr != "is" && tok[afterType].TokenStr != "as" && Lexer.Keywords.Contains(tok[afterType].TokenStr)))))
                {
                    // It’s a cast!
                    try
                    {
                        i = afterType;
                        var operand = parseExpressionUnary(tok, ref i);
                        return new CsCastExpression { StartIndex = startIndex, EndIndex = operand.EndIndex, Operand = operand, Type = typeName };
                    }
                    catch (ParseException e)
                    {
                        if (e.IncompleteResult is CsExpression)
                            throw new ParseException(e.Message, e.Index, new CsCastExpression { StartIndex = startIndex, Operand = (CsExpression) e.IncompleteResult, Type = typeName }, e);
                        throw;
                    }
                }

                // It’s a subexpression!
                i = afterExpression;
                return new CsParenthesizedExpression { StartIndex = startIndex, EndIndex = tok[i - 1].EndIndex, Subexpression = expression };
            }

            var simpleName = parseExpressionIdentifier(tok, ref i);
            return new CsIdentifierExpression { StartIndex = startIndex, EndIndex = tok[i - 1].EndIndex, Identifier = simpleName };
        }
コード例 #30
0
ファイル: Parser.cs プロジェクト: Timwi/ParseCs
        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;
        }