Ejemplo n.º 1
0
        private static IOperation parseSingleStatement(TokenQueue tokenQueue, int precedenceAtLeast)
        {
            Queue<IOperation> queue = new Queue<IOperation>();
            Stack<QToken> stack = new Stack<QToken>();
            QToken token = null;
            bool argumentExpected = false;
            bool dotIsProperty = false;
            bool exitLoop = false;
            while (!exitLoop)
            {
                token = tokenQueue.Pop();
                if (token == null)
                    break;

                switch (token.TokenType)
                {
                    case QType.ParenthesisOpen:
                        // C# has typecasts, such as ((IInterface)x) so when seeing an opening (, we must try to read type.
                        // This however does not work if there is an identifier before the (, for example func(Enum.Value,(Enum.Value)).
                        if (stack.Count == 0 || (stack.Peek().TokenType != QType.Id && !stack.Peek().IsOperator(Operators.New)))
                        {
                            string sType = tokenQueue.GetTypeNameFollowedByClosingParenthesis();
                            if (sType != null)
                            {
                                var q = tokenQueue.Peek();
                                if (q==null || (q.TokenType!=QType.Id && q.TokenType!=QType.Value && q.TokenType!=QType.Subexpr && !q.IsOpenBrace))
                                {
                                    token = new QToken(QType.Id, sType);
                                    goto case QType.Id;
                                }
                                stack.Push(new QToken(Operators.TypeCast) {Param = sType});
                                argumentExpected = dotIsProperty = false;
                                break;
                            }
                        }
                        goto case QType.SquareOpen;
                    case QType.SquareOpen:
                    case QType.BlockOpen:
                        token.ExtraInt = -(queue.Count + stack.Count + 1);
                        stack.Push(token);
                        argumentExpected = false;
                        dotIsProperty = false;
                        break;

                    case QType.Value:
                        queue.Enqueue(operationFromToken(token));
                        argumentExpected = false;
                        dotIsProperty = true;
                        break;
                    case QType.Subexpr:
                        queue.Enqueue(operationFromToken(token));
                        argumentExpected = false;
                        dotIsProperty = true;
                        break;
                    case QType.Typeof:
                        if (tokenQueue.Peek() == null || tokenQueue.Peek().TokenType != QType.ParenthesisOpen)
                            tokenQueue.ThrowParsingException("Expected (");
                        tokenQueue.Pop();
                        token.Param = tokenQueue.ReadTypeName();
                        if (tokenQueue.Peek() == null || tokenQueue.Peek().TokenType != QType.ParenthesisClose)
                            tokenQueue.ThrowParsingException("Expected )");
                        tokenQueue.Pop();
                        queue.Enqueue(operationFromToken(token));
                        argumentExpected = false;
                        dotIsProperty = true;
                        break;

                    case QType.Id:
                        string curId = (string)token.Param;
                        bool startsWithDot = curId.StartsWith(".", StringComparison.Ordinal);
                        if (dotIsProperty)
                        {
                            if (!startsWithDot)
                                tokenQueue.ThrowParsingException("Expected .");
                            token.Param = curId.Substring(1);
                            token.ExtraInt = 1;
                        }
                        else if (token.ExtraInt == 0)
                        {
                            var n = tokenQueue.Peek();
                            if (n != null && (n.TokenType != QType.ParenthesisOpen) && (n.TokenType != QType.SquareOpen))
                                goto case QType.Value;
                        }
                        stack.Push(token);
                        argumentExpected = false;
                        dotIsProperty = true;
                        break;
                    case QType.Colon:
                        tokenQueue.Push(token);
                        exitLoop = true;
                        break;
                    case QType.Operator:
                        Operators op = token.Operator;
                        if (argumentExpected || !dotIsProperty)
                        {
                            if (op == Operators.Minus) token = new QToken(op = Operators.UnaryMinus);
                            if (op == Operators.Plus) token = new QToken(op = Operators.UnaryPlus);
                        }

                        cleanStack(queue, stack, op);

                        if (precedenceAtLeast >= 0 && s_precedence[op] < precedenceAtLeast && stack.Count == 0)
                        {
                            tokenQueue.Push(token);
                            exitLoop = true;
                            argumentExpected = false;
                            break;
                        }

                        switch (op)
                        {
                            case Operators.Or:
                            case Operators.And:
                                {
                                    // Or and AND short-circuit, so a || b || c => a?true:(b?true:c)
                                    // and a && b &&c => a?(b?c:false):false
                                    Stack<IOperation> terms = new Stack<IOperation>();
                                    bool isOr = (op == Operators.Or);
                                    do
                                    {
                                        var e1 = parseSingleStatement(tokenQueue, s_precedence[op] + 1);
                                        if (e1 == null)
                                            tokenQueue.ThrowParsingException("Expression expected after " + (isOr ? "||" : "&&"));
                                        terms.Push(e1);
                                        var n = tokenQueue.Peek();
                                        if (n == null || !n.IsOperator(op))
                                            break;
                                        tokenQueue.Pop();
                                    } while (true);

                                    IOperation o = null;
                                    while (terms.Count > 0)
                                    {
                                        if (o == null)
                                            o = terms.Pop();
                                        else
                                            o = new Operations.OperationExpression(terms.Pop(),
                                                                                    (isOr) ? new Operations.OperationConditional(createConstant(true), o) :
                                                                                          new Operations.OperationConditional(o, createConstant(false)));
                                    }
                                    o = (isOr) ? new Operations.OperationConditional(createConstant(true), o) :
                                                new Operations.OperationConditional(o, createConstant(false));

                                    queue.Enqueue(o);
                                    argumentExpected = dotIsProperty = false;
                                    break;
                                }
                            case Operators.Conditional:
                                {
                                    var e1 = parseSingleStatement(tokenQueue, -1);
                                    if (e1 == null)
                                        tokenQueue.ThrowParsingException("Failed to parse the first part of the conditional expression");
                                    var n = tokenQueue.Pop();
                                    if (n == null || n.TokenType != QType.Colon)
                                        tokenQueue.ThrowParsingException("Expected :");

                                    var e2 = parseSingleStatement(tokenQueue, -1);
                                    if (e2 == null)
                                        tokenQueue.ThrowParsingException("Failed to parse the second part of the conditional expression");
                                    queue.Enqueue(new Operations.OperationConditional(e1, e2));
                                    argumentExpected = dotIsProperty = false;
                                }
                                break;
                            case Operators.Coalesce:
                                {
                                    var e2 = parseSingleStatement(tokenQueue, -1);
                                    if (e2 == null)
                                        tokenQueue.ThrowParsingException("Expression expected after ??");

                                    queue.Enqueue(new Operations.OperationCoalesce(e2));
                                    argumentExpected = dotIsProperty = false;
                                }
                                break;

                            case Operators.Comma:
                                while (stack.Count > 0 && !stack.Peek().IsOpenBrace)
                                    queue.Enqueue(operationFromToken(stack.Pop()));
                                if (stack.Count > 0 && stack.Peek().IsOpenBrace)
                                {
                                    var p = stack.Pop();
                                    if (p.ExtraInt <= 0)
                                        p.ExtraInt = 1;
                                    p.ExtraInt++; // Count the number of comma-separated entities in braces
                                    stack.Push(p);
                                }
                                argumentExpected = dotIsProperty = false;
                                break;
                            case Operators.New:
                            case Operators.Is:
                            case Operators.As:
                                // After this there is a typename, which in case of typeof() MUST be in parentheses (unlike C++)
                                token.Param = tokenQueue.ReadTypeName();
                                if (token.Param==null && op!=Operators.New)
                                    tokenQueue.ThrowParsingException("Expected type");
                                stack.Push(token);
                                argumentExpected = false;
                                dotIsProperty = true;
                                break;

                            default:
                                argumentExpected = true;
                                dotIsProperty = false;
                                stack.Push(token);
                                break;
                        }
                        break;

                    case QType.ParenthesisClose:
                    case QType.BlockClose:
                    case QType.SquareClose:
                        if (argumentExpected)
                            tokenQueue.ThrowParsingException("Argument expected");
                        QType match = QType.ParenthesisOpen;
                        if (token.TokenType == QType.SquareClose) match = QType.SquareOpen;
                        if (token.TokenType == QType.BlockClose) match = QType.BlockOpen;

                        while (stack.Count > 0 && stack.Peek().TokenType != match)
                        {
                            var sp = stack.Pop();
                            if (sp.IsOpenBrace)
                                tokenQueue.ThrowParsingException("Parentheses or square brackets do not match");
                            queue.Enqueue(operationFromToken(sp));

                        }

                        if (stack.Count == 0)
                        {
                            tokenQueue.Push(token);
                            exitLoop = true;
                            break;
                        }

                        var args = stack.Pop().ExtraInt;
                        if (args < 0)
                            args = (args == -(queue.Count + stack.Count + 1)) ? 0 : 1;

                        var nextToken = tokenQueue.Peek();

                        if (stack.Count > 0 && stack.Peek().IsOperator(Operators.New))
                        {
                            var newOp = stack.Pop();
                            switch (token.TokenType)
                            {
                                case QType.SquareClose:
                                    if (nextToken == null || nextToken.TokenType != QType.BlockOpen)
                                        queue.Enqueue(new Operations.OperationNewObject((string)newOp.Param, args, false));
                                    else
                                    {
                                        newOp.ExtraInt = args;
                                        stack.Push(newOp);
                                    }
                                    break;
                                case QType.BlockClose:
                                    queue.Enqueue(new Operations.OperationCreateBlock(args));
                                    queue.Enqueue(new Operations.OperationNewObject((string)newOp.Param, newOp.ExtraInt + 1, (token.TokenType == QType.BlockClose)));
                                    break;
                                default:
                                    queue.Enqueue(new Operations.OperationNewObject((string)newOp.Param, args, (token.TokenType == QType.BlockClose)));
                                    break;
                            }
                        }
                        else if (token.TokenType == QType.ParenthesisClose || token.TokenType == QType.SquareClose)
                        {
                            if (stack.Count > 0 && (stack.Peek().TokenType == QType.Id))
                            {
                                var pop = stack.Pop();
                                string id = (string)pop.Param;
                                bool isProperty = (token.TokenType == QType.SquareClose);
                                queue.Enqueue(new Operations.OperationCall(id, pop.ExtraInt != 0, isProperty, args));
                            }
                            else if (token.TokenType == QType.SquareClose)
                                queue.Enqueue(new Operations.OperationCall(string.Empty, true, true, args));
                        }
                        else // if (token.Type == QType.BlockClose)
                        {
                            queue.Enqueue(new Operations.OperationCreateBlock(args));
                        }
                        argumentExpected = false;
                        dotIsProperty = true;
                        break;

                }

            }

            if (argumentExpected)
                tokenQueue.ThrowParsingException("Argument expected");

            while (stack.Count > 0)
                queue.Enqueue(operationFromToken(stack.Pop()));

            IOperation ex;
            if (queue.Count == 0)
                return null;
            if (queue.Count == 1)
                ex = queue.Peek();
            else
                ex = new Operations.OperationExpression(queue.ToArray());
            if (ex.StackBalance != 1)
                tokenQueue.ThrowParsingException("Invalid expression syntax");
            return ex;
        }
Ejemplo n.º 2
0
        private static IOperation parseSingleStatement(TokenQueue tokenQueue, int precedenceAtLeast)
        {
            Queue <IOperation> queue = new Queue <IOperation>();
            Stack <QToken>     stack = new Stack <QToken>();
            QToken             token = null;
            bool argumentExpected    = false;
            bool dotIsProperty       = false;
            bool exitLoop            = false;

            while (!exitLoop)
            {
                token = tokenQueue.Pop();
                if (token == null)
                {
                    break;
                }

                switch (token.TokenType)
                {
                case QType.ParenthesisOpen:
                    // C# has typecasts, such as ((IInterface)x) so when seeing an opening (, we must try to read type.
                    // This however does not work if there is an identifier before the (, for example func(Enum.Value,(Enum.Value)).
                    if (stack.Count == 0 || (stack.Peek().TokenType != QType.Id && !stack.Peek().IsOperator(Operators.New)))
                    {
                        string sType = tokenQueue.GetTypeNameFollowedByClosingParenthesis();
                        if (sType != null)
                        {
                            var q = tokenQueue.Peek();
                            if (q == null || (q.TokenType != QType.Id && q.TokenType != QType.Value && q.TokenType != QType.Subexpr && !q.IsOpenBrace))
                            {
                                token = new QToken(QType.Id, sType);
                                goto case QType.Id;
                            }
                            stack.Push(new QToken(Operators.TypeCast)
                            {
                                Param = sType
                            });
                            argumentExpected = dotIsProperty = false;
                            break;
                        }
                    }
                    goto case QType.SquareOpen;

                case QType.SquareOpen:
                case QType.BlockOpen:
                    token.ExtraInt = -(queue.Count + stack.Count + 1);
                    stack.Push(token);
                    argumentExpected = false;
                    dotIsProperty    = false;
                    break;

                case QType.Value:
                    queue.Enqueue(operationFromToken(token));
                    argumentExpected = false;
                    dotIsProperty    = true;
                    break;

                case QType.Subexpr:
                    queue.Enqueue(operationFromToken(token));
                    argumentExpected = false;
                    dotIsProperty    = true;
                    break;

                case QType.Typeof:
                    if (tokenQueue.Peek() == null || tokenQueue.Peek().TokenType != QType.ParenthesisOpen)
                    {
                        tokenQueue.ThrowParsingException("Expected (");
                    }
                    tokenQueue.Pop();
                    token.Param = tokenQueue.ReadTypeName();
                    if (tokenQueue.Peek() == null || tokenQueue.Peek().TokenType != QType.ParenthesisClose)
                    {
                        tokenQueue.ThrowParsingException("Expected )");
                    }
                    tokenQueue.Pop();
                    queue.Enqueue(operationFromToken(token));
                    argumentExpected = false;
                    dotIsProperty    = true;
                    break;

                case QType.Id:
                    string curId         = (string)token.Param;
                    bool   startsWithDot = curId.StartsWith(".", StringComparison.Ordinal);
                    if (dotIsProperty)
                    {
                        if (!startsWithDot)
                        {
                            tokenQueue.ThrowParsingException("Expected .");
                        }
                        token.Param    = curId.Substring(1);
                        token.ExtraInt = 1;
                    }
                    else if (token.ExtraInt == 0)
                    {
                        var n = tokenQueue.Peek();
                        if (n != null && (n.TokenType != QType.ParenthesisOpen) && (n.TokenType != QType.SquareOpen))
                        {
                            goto case QType.Value;
                        }
                    }
                    stack.Push(token);
                    argumentExpected = false;
                    dotIsProperty    = true;
                    break;

                case QType.Colon:
                    tokenQueue.Push(token);
                    exitLoop = true;
                    break;

                case QType.Operator:
                    Operators op = token.Operator;
                    if (argumentExpected || !dotIsProperty)
                    {
                        if (op == Operators.Minus)
                        {
                            token = new QToken(op = Operators.UnaryMinus);
                        }
                        if (op == Operators.Plus)
                        {
                            token = new QToken(op = Operators.UnaryPlus);
                        }
                    }

                    cleanStack(queue, stack, op);

                    if (precedenceAtLeast >= 0 && s_precedence[op] < precedenceAtLeast && stack.Count == 0)
                    {
                        tokenQueue.Push(token);
                        exitLoop         = true;
                        argumentExpected = false;
                        break;
                    }

                    switch (op)
                    {
                    case Operators.Or:
                    case Operators.And:
                    {
                        // Or and AND short-circuit, so a || b || c => a?true:(b?true:c)
                        // and a && b &&c => a?(b?c:false):false
                        Stack <IOperation> terms = new Stack <IOperation>();
                        bool isOr = (op == Operators.Or);
                        do
                        {
                            var e1 = parseSingleStatement(tokenQueue, s_precedence[op] + 1);
                            if (e1 == null)
                            {
                                tokenQueue.ThrowParsingException("Expression expected after " + (isOr ? "||" : "&&"));
                            }
                            terms.Push(e1);
                            var n = tokenQueue.Peek();
                            if (n == null || !n.IsOperator(op))
                            {
                                break;
                            }
                            tokenQueue.Pop();
                        } while (true);

                        IOperation o = null;
                        while (terms.Count > 0)
                        {
                            if (o == null)
                            {
                                o = terms.Pop();
                            }
                            else
                            {
                                o = new Operations.OperationExpression(terms.Pop(),
                                                                       (isOr) ? new Operations.OperationConditional(createConstant(true), o) :
                                                                       new Operations.OperationConditional(o, createConstant(false)));
                            }
                        }
                        o = (isOr) ? new Operations.OperationConditional(createConstant(true), o) :
                            new Operations.OperationConditional(o, createConstant(false));

                        queue.Enqueue(o);
                        argumentExpected = dotIsProperty = false;
                        break;
                    }

                    case Operators.Conditional:
                    {
                        var e1 = parseSingleStatement(tokenQueue, -1);
                        if (e1 == null)
                        {
                            tokenQueue.ThrowParsingException("Failed to parse the first part of the conditional expression");
                        }
                        var n = tokenQueue.Pop();
                        if (n == null || n.TokenType != QType.Colon)
                        {
                            tokenQueue.ThrowParsingException("Expected :");
                        }

                        var e2 = parseSingleStatement(tokenQueue, -1);
                        if (e2 == null)
                        {
                            tokenQueue.ThrowParsingException("Failed to parse the second part of the conditional expression");
                        }
                        queue.Enqueue(new Operations.OperationConditional(e1, e2));
                        argumentExpected = dotIsProperty = false;
                    }
                    break;

                    case Operators.Coalesce:
                    {
                        var e2 = parseSingleStatement(tokenQueue, -1);
                        if (e2 == null)
                        {
                            tokenQueue.ThrowParsingException("Expression expected after ??");
                        }

                        queue.Enqueue(new Operations.OperationCoalesce(e2));
                        argumentExpected = dotIsProperty = false;
                    }
                    break;

                    case Operators.Comma:
                        while (stack.Count > 0 && !stack.Peek().IsOpenBrace)
                        {
                            queue.Enqueue(operationFromToken(stack.Pop()));
                        }
                        if (stack.Count > 0 && stack.Peek().IsOpenBrace)
                        {
                            var p = stack.Pop();
                            if (p.ExtraInt <= 0)
                            {
                                p.ExtraInt = 1;
                            }
                            p.ExtraInt++;         // Count the number of comma-separated entities in braces
                            stack.Push(p);
                        }
                        argumentExpected = dotIsProperty = false;
                        break;

                    case Operators.New:
                    case Operators.Is:
                    case Operators.As:
                        // After this there is a typename, which in case of typeof() MUST be in parentheses (unlike C++)
                        token.Param = tokenQueue.ReadTypeName();
                        if (token.Param == null && op != Operators.New)
                        {
                            tokenQueue.ThrowParsingException("Expected type");
                        }
                        stack.Push(token);
                        argumentExpected = false;
                        dotIsProperty    = true;
                        break;

                    default:
                        argumentExpected = true;
                        dotIsProperty    = false;
                        stack.Push(token);
                        break;
                    }
                    break;

                case QType.ParenthesisClose:
                case QType.BlockClose:
                case QType.SquareClose:
                    if (argumentExpected)
                    {
                        tokenQueue.ThrowParsingException("Argument expected");
                    }
                    QType match = QType.ParenthesisOpen;
                    if (token.TokenType == QType.SquareClose)
                    {
                        match = QType.SquareOpen;
                    }
                    if (token.TokenType == QType.BlockClose)
                    {
                        match = QType.BlockOpen;
                    }

                    while (stack.Count > 0 && stack.Peek().TokenType != match)
                    {
                        var sp = stack.Pop();
                        if (sp.IsOpenBrace)
                        {
                            tokenQueue.ThrowParsingException("Parentheses or square brackets do not match");
                        }
                        queue.Enqueue(operationFromToken(sp));
                    }

                    if (stack.Count == 0)
                    {
                        tokenQueue.Push(token);
                        exitLoop = true;
                        break;
                    }

                    var args = stack.Pop().ExtraInt;
                    if (args < 0)
                    {
                        args = (args == -(queue.Count + stack.Count + 1)) ? 0 : 1;
                    }

                    var nextToken = tokenQueue.Peek();

                    if (stack.Count > 0 && stack.Peek().IsOperator(Operators.New))
                    {
                        var newOp = stack.Pop();
                        switch (token.TokenType)
                        {
                        case QType.SquareClose:
                            if (nextToken == null || nextToken.TokenType != QType.BlockOpen)
                            {
                                queue.Enqueue(new Operations.OperationNewObject((string)newOp.Param, args, false));
                            }
                            else
                            {
                                newOp.ExtraInt = args;
                                stack.Push(newOp);
                            }
                            break;

                        case QType.BlockClose:
                            queue.Enqueue(new Operations.OperationCreateBlock(args));
                            queue.Enqueue(new Operations.OperationNewObject((string)newOp.Param, newOp.ExtraInt + 1, (token.TokenType == QType.BlockClose)));
                            break;

                        default:
                            queue.Enqueue(new Operations.OperationNewObject((string)newOp.Param, args, (token.TokenType == QType.BlockClose)));
                            break;
                        }
                    }
                    else if (token.TokenType == QType.ParenthesisClose || token.TokenType == QType.SquareClose)
                    {
                        if (stack.Count > 0 && (stack.Peek().TokenType == QType.Id))
                        {
                            var    pop        = stack.Pop();
                            string id         = (string)pop.Param;
                            bool   isProperty = (token.TokenType == QType.SquareClose);
                            queue.Enqueue(new Operations.OperationCall(id, pop.ExtraInt != 0, isProperty, args));
                        }
                        else if (token.TokenType == QType.SquareClose)
                        {
                            queue.Enqueue(new Operations.OperationCall(string.Empty, true, true, args));
                        }
                    }
                    else     // if (token.Type == QType.BlockClose)
                    {
                        queue.Enqueue(new Operations.OperationCreateBlock(args));
                    }
                    argumentExpected = false;
                    dotIsProperty    = true;
                    break;
                }
            }

            if (argumentExpected)
            {
                tokenQueue.ThrowParsingException("Argument expected");
            }

            while (stack.Count > 0)
            {
                queue.Enqueue(operationFromToken(stack.Pop()));
            }

            IOperation ex;

            if (queue.Count == 0)
            {
                return(null);
            }
            if (queue.Count == 1)
            {
                ex = queue.Peek();
            }
            else
            {
                ex = new Operations.OperationExpression(queue.ToArray());
            }
            if (ex.StackBalance != 1)
            {
                tokenQueue.ThrowParsingException("Invalid expression syntax");
            }
            return(ex);
        }