public override bool Match(Token first, CompilerApplication context)
        {
            TokenQueue tokens = context.context.Tokens;

            if (tokens.Peek().Text.Equals(":"))
            {
                tokens.Peek().Type = TokenType.LABEL;
                return(true);
            }

            return(false);
        }
Пример #2
0
    private Ast.Stmt ParseExecuteStmt(TokenQueue q)
    {
        var stmt = new Ast.ExecuteStmt {
            SourceToken = q.SourceToken
        };

        q.Take("exec", "execute");

        if (q.Peek(1) == "=")
        {
            stmt.ReturnVariableName = ParseVariableName(q);
            q.Take("=");
        }

        if (q.PeekToken().Type == TokenType.String || q.PeekToken().Type == TokenType.Id)
        {
            stmt.ScriptName = q.Take().GetUnescapedText();
        }
        else
        {
            throw new SyntaxException(new[] { "string", "identifier" }, q);
        }

        if (IsVariableName(q.PeekToken()?.GetUnescapedText() ?? "") && q.Peek(1) == "=")
        {
            while (true)
            {
                var arg = new Ast.ArgumentPair();
                arg.Name = ParseVariableName(q);
                q.Take("=");
                if (q.Peek() == "default")
                {
                    q.Take();
                }
                else
                {
                    arg.Value = ParseExpr(q);
                }
                stmt.Arguments.Add(arg);
                if (!q.TakeMaybe(","))
                {
                    break;
                }
            }
        }

        ConsumeSemicolon(q);
        return(stmt);
    }
Пример #3
0
 private void ParseAssignmentStmtCore(TokenQueue q, Ast.AssignmentStmt stmt)
 {
     stmt.VariableName = ParseVariableName(q);
     if (q.Peek() == "=")
     {
         q.Take();
         stmt.InitialValue = ParseExpr(q);
     }
     ConsumeSemicolon(q);
 }
Пример #4
0
    private Ast.Stmt ParseTryCatchStmt(TokenQueue q)
    {
        var stmt = new Ast.TryCatchStmt {
            SourceToken = q.SourceToken
        };

        q.Take("begin");
        q.Take("try");
        stmt.TryBlock = new Ast.Block {
            SourceToken = q.SourceToken
        };
        while (q.Peek() != "end")
        {
            var tryStmt = ParseStmt(q);
            if (tryStmt != null)
            {
                stmt.TryBlock.Statements.Add(tryStmt);
            }
        }
        q.Take("end");
        q.Take("try");

        q.Take("begin");
        q.Take("catch");
        stmt.CatchBlock = new Ast.Block {
            SourceToken = q.SourceToken
        };
        while (q.Peek() != "end")
        {
            var catchStmt = ParseStmt(q);
            if (catchStmt != null)
            {
                stmt.CatchBlock.Statements.Add(catchStmt);
            }
        }
        q.Take("end");
        q.Take("catch");
        return(stmt);
    }
Пример #5
0
    private Ast.Stmt ParseStmt(TokenQueue q)   // or null
    {
        switch (q.Peek(0))
        {
        case ";": q.Take(";"); return(null);

        case "declare": return(ParseDeclareStmt(q));

        case "while": return(ParseWhileStmt(q));

        case "break": return(ParseBreakStmt(q));

        case "continue": return(ParseContinueStmt(q));

        case "print": return(ParsePrintStmt(q));

        case "exec":
        case "execute": return(ParseExecuteStmt(q));

        case "return": return(ParseReturnStmt(q));

        case "throw": return(ParseThrowStmt(q));

        case "set": return(ParseSetStmt(q));

        case "if": return(ParseIfStmt(q));

        case "begin": return(q.Peek(1) == "try" ? ParseTryCatchStmt(q) : ParseSqlStmt(q));

        case "import": return(ParseImportStmt(q));

        case "export": return(ParseExportStmt(q));

        case "for": return(ParseForStmt(q));

        default: return(ParseSqlStmt(q));
        }
    }
Пример #6
0
    private Ast.DeclareStmt ParseDeclareStmt(TokenQueue q)
    {
        var stmt = new Ast.DeclareStmt {
            SourceToken = q.SourceToken
        };

        q.Take("declare");
        if (q.Peek() == "parameter")
        {
            q.Take();
            stmt.IsParameter = true;
        }
        ParseAssignmentStmtCore(q, stmt);
        return(stmt);
    }
Пример #7
0
    private Ast.Stmt ParseIfStmt(TokenQueue q)
    {
        var stmt = new Ast.IfStmt {
            SourceToken = q.SourceToken
        };

        q.Take("if");
        stmt.Condition = ParseExpr(q);
        stmt.Block     = ParseBlock(q);
        if (q.Peek() == "else")
        {
            q.Take("else");
            stmt.ElseBlock = ParseBlock(q);
        }
        return(stmt);
    }
Пример #8
0
        // Read a JKLSequence, checking that it starts and terminates correctly.
        // Named read_list to follow the ref, but has been genericized to handle vectors as well.
        static public JKLSeqBase read_list(TokenQueue TQ, JKLSeqBase sequence, char start, char end)
        {
            // Check that we are in fact at the start of a list.
            string token = TQ.Next();

            if (token[0] != start)
            {
                // Parse error - probably internal if the list code is correct.
                throw new JKLInternalError("Sequence expected '" + start + "' but got: " + token);
            }

            // Use read_form to get the list's contents, accumulating them into the list.
            while (true)
            {
                token = TQ.Peek();

                if (token != null)
                {
                    // We are in the list or at the end.
                    if (token[0] == end)
                    {
                        // Reached valid end of list. Consume the end char.
                        TQ.Next();
                        // And we are done.
                        break;
                    }
                    // Mutually recurse to read the next list element.
                    JKLVal newVal = read_form(TQ);
                    sequence.Add(newVal);
                }
                else
                {
                    // The input has finished but the list hasn't. Try to get more input.
                    TQ.LoadMoreTokens(start, end);
                }
            }

            return(sequence);
        }
Пример #9
0
        // Read a JKLVal form - which is either an atom or a sequence.
        static public JKLVal read_form(TokenQueue TQ)
        {
            if (TQ.Peek() == null)
            {
                // Reader is empty - caused by a comment line in the input.
                return(null);
            }
            else if (TQ.Peek().StartsWith('('))
            {
                // Create a new List and read it's body.
                return(read_list(TQ, new JKLList(), '(', ')'));
            }
            else if (TQ.Peek().StartsWith('['))
            {
                // Create a new Vector and read it's body.
                return(read_list(TQ, new JKLVector(), '[', ']'));
            }
            else if (TQ.Peek().StartsWith('{'))
            {
                // Create a new HashMap and read it's body. EVAL checks it has valid key val pairs.
                return(read_list(TQ, new JKLHashMap(), '{', '}'));
            }
            else if (TQ.Peek().StartsWith(')') || TQ.Peek().StartsWith(']') || TQ.Peek().StartsWith('}'))
            {
                // A sequence close character that doesn't match a start.
                // This correctly handles a case like [1 ( 2 ] 3).
                throw new JKLParseError("Expecting sequence or atom but got '" + TQ.Peek() + "'");
            }

            else if (TQ.Peek().StartsWith('&'))
            {
                // Reader macro. We have '&atomName'. Convert this into (deref atomName);
                string varArgAtom = TQ.Peek();
                if (varArgAtom.Length == 1)
                {
                    // Treat a solo '&' as a varargs symbol,
                    TQ.Next();
                    return(jklVarArgsChar);
                }
                else
                {
                    throw new JKLParseError("'&' can't start a symbol name: '" + varArgAtom.ToString() + "'");
                }
            }
            else if (TQ.Peek().StartsWith('@'))
            {
                TQ.Next();
                // Build a deref form.
                JKLList derefForm = new JKLList();
                derefForm.Add(new JKLSym("deref"));
                derefForm.Add(read_form(TQ));
                return(derefForm);
            }
            else if (TQ.Peek().StartsWith('\''))
            {
                // Return a list containing a quote symbol and the quoted form.
                TQ.Next();
                JKLList quoteForm = new JKLList();
                quoteForm.Add(new JKLSym("quote"));
                quoteForm.Add(read_form(TQ));
                return(quoteForm);
            }
            else if (TQ.Peek().StartsWith('`'))
            {
                // Return a list containing a quasiquote symbol and the quasiquoted form.
                TQ.Next();
                JKLList quasiquoteForm = new JKLList();
                quasiquoteForm.Add(new JKLSym("quasiquote"));
                quasiquoteForm.Add(read_form(TQ));
                return(quasiquoteForm);
            }
            else if (TQ.Peek().StartsWith("~@"))
            {
                // Return a list containing a splice-unquote symbol and the next form.
                // Dammit! I'd missed the '~' here and spent several days wondering why (or ...) didn't work.
                TQ.Next();
                JKLList quasiquoteForm = new JKLList();
                quasiquoteForm.Add(new JKLSym("splice-unquote"));
                quasiquoteForm.Add(read_form(TQ));
                return(quasiquoteForm);
            }
            else if (TQ.Peek().StartsWith('~'))
            {
                // Return a list containing an unquote symbol and the next form.
                TQ.Next();
                JKLList quasiquoteForm = new JKLList();
                quasiquoteForm.Add(new JKLSym("unquote"));
                quasiquoteForm.Add(read_form(TQ));
                return(quasiquoteForm);
            }
            else if (TQ.Peek().StartsWith('^'))
            {
                // Return a new list that contains the symbol "with-meta" and the result of reading the
                // next next form (2nd argument) (read_form) and the next form (1st argument) in that order
                TQ.Next();
                JKLList withMetaForm = new JKLList();
                withMetaForm.Add(new JKLSym("with-meta"));
                JKLVal firstArg  = read_form(TQ);
                JKLVal secondArg = read_form(TQ);
                withMetaForm.Add(secondArg);
                withMetaForm.Add(firstArg);
                return(withMetaForm);
            }
            else
            {
                // This isn't a list so parse it as an atom.
                return(read_token(TQ));
            }
        }
Пример #10
0
 private Ast.Stmt ParseImportStmt(TokenQueue q) =>
 q.Peek(1) switch
 {
     "csv" => Check(q, ParseImportCsvStmt(q)),
Пример #11
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;
        }
Пример #12
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);
        }