protected void ParseStatement(bool allowEmpty = false)
        {
            FilePosition statementStart = _lexer.TokenStartPosition;

            MethodGenerator.BeginSourceLine(statementStart);
            if (Accept(Token.KwFor))
            {
                Symbol       iterator;
                FilePosition fp = _lexer.TokenStartPosition;
                if (!Accept(Token.Identifier))
                {
                    AddError(fp, "Expecting integer identifier.");
                    SkipStatement();
                    return;
                }

                // Initial assignment
                iterator = LookupSymbol(fp, _lexeme);
                VerifyExpressionType(fp, DerefType(iterator.Type), IrisType.Integer);
                bool byRef = iterator.Type.IsByRef;
                if (byRef)
                {
                    EmitLoadSymbol(iterator, SymbolLoadMode.Raw);
                }

                Expect(Token.ChrAssign);
                fp = _lexer.TokenStartPosition;
                IrisType rhs = ParseExpression();
                VerifyExpressionType(fp, rhs, IrisType.Integer);

                if (byRef)
                {
                    MethodGenerator.Store(rhs);
                }
                else
                {
                    EmitStoreSymbol(iterator);
                }

                Expect(Token.KwTo);

                // Loop start and condition
                int loopLabel = GetNextLabel();
                MethodGenerator.Label(loopLabel);
                EmitLoadSymbol(iterator, SymbolLoadMode.Dereference);
                fp  = _lexer.TokenStartPosition;
                rhs = ParseExpression();
                VerifyExpressionType(fp, rhs, IrisType.Integer);
                int exitLabel = GetNextLabel();
                MethodGenerator.BranchCondition(Operator.GreaterThan, exitLabel);

                // Loop body
                Expect(Token.KwDo);
                FilePosition forEndPosition = _lastParsedPosition;
                MethodGenerator.EndSourceLine(forEndPosition);
                ParseStatement();

                // Loop end
                MethodGenerator.BeginSourceLine(statementStart); // Source position is the same as the loop start.
                Increment(iterator);
                MethodGenerator.Goto(loopLabel);
                MethodGenerator.Label(exitLabel);
                MethodGenerator.EndSourceLine(forEndPosition);
            }
            else if (Accept(Token.KwWhile))
            {
                int loopLabel = GetNextLabel();
                MethodGenerator.Label(loopLabel);

                FilePosition fp   = _lexer.TokenStartPosition;
                IrisType     type = ParseExpression();
                VerifyExpressionType(fp, type, IrisType.Boolean);
                int exitLabel = GetNextLabel();
                MethodGenerator.BranchFalse(exitLabel);

                Expect(Token.KwDo);

                MethodGenerator.EndSourceLine(_lastParsedPosition);

                ParseStatement();
                MethodGenerator.Goto(loopLabel);
                MethodGenerator.Label(exitLabel);
            }
            else if (Accept(Token.KwRepeat))
            {
                int loopLabel = GetNextLabel();
                MethodGenerator.Label(loopLabel);
                MethodGenerator.EmitNonCodeLineInfo(new SourceRange(statementStart, _lastParsedPosition));

                ParseStatements(Token.KwUntil);

                FilePosition fp   = _lexer.TokenStartPosition;
                IrisType     type = ParseExpression();
                VerifyExpressionType(fp, type, IrisType.Boolean);
                MethodGenerator.BranchFalse(loopLabel);

                MethodGenerator.EndSourceLine(_lastParsedPosition);
            }
            else if (Accept(Token.KwIf))
            {
                ParseIf();
            }
            else if (Accept(Token.KwBegin))
            {
                MethodGenerator.EmitNonCodeLineInfo(new SourceRange(statementStart, _lastParsedPosition));
                ParseStatements(Token.KwEnd);
            }
            else if (Accept(Token.Identifier))
            {
                FilePosition fp         = _lexer.TokenStartPosition;
                string       symbolName = _lexeme;
                Symbol       symbol     = LookupSymbol(fp, symbolName);
                IrisType     lhs        = symbol.Type;
                bool         assign     = false;
                bool         isArray    = false;
                if (Accept(Token.ChrOpenBracket))
                {
                    // Assignment to an array element.
                    isArray = true;
                    lhs     = ProcessArrayAccess(fp, symbol, SymbolLoadMode.Raw);
                }
                if (Accept(Token.ChrAssign))
                {
                    assign = true;
                    bool indirectAssign = false;

                    if (lhs.IsByRef)
                    {
                        lhs = lhs.GetElementType();
                        EmitLoadSymbol(symbol, SymbolLoadMode.Raw);
                        indirectAssign = true;
                    }

                    FilePosition exprPosition = _lexer.TokenStartPosition;
                    IrisType     rhs          = ParseExpression();

                    if (lhs.IsMethod)
                    {
                        AddError(fp, "Cannot assign to result of function or procedure call.");
                    }
                    else if (lhs != IrisType.Invalid)
                    {
                        if (rhs == IrisType.Void)
                        {
                            AddError(fp, "Cannot use procedure in assignment statement.");
                        }
                        else if (rhs != IrisType.Invalid && rhs != lhs)
                        {
                            AddError(exprPosition, string.Format("Cannot assign to '{0}' (type mismatch error).", symbolName));
                        }

                        if (isArray)
                        {
                            MethodGenerator.StoreElement(lhs);
                        }
                        else if (indirectAssign)
                        {
                            MethodGenerator.Store(lhs);
                        }
                        else
                        {
                            EmitStoreSymbol(symbol);
                        }
                    }
                }
                else if (isArray)
                {
                    // This is an array subscript.  Assignment is the only kind of statement that
                    // starts with an array subscript.
                    AddErrorAtTokenStart("Expecting ':='.");
                    SkipStatement();
                }

                if (!assign && !isArray)
                {
                    bool skipArgList = !Accept(Token.ChrOpenParen);
                    ProcessCall(fp, symbol, skipArgList);

                    if (symbol.Type.IsFunction)
                    {
                        MethodGenerator.Pop();
                    }
                }

                MethodGenerator.EndSourceLine(_lastParsedPosition);
            }
            else if (Accept(Token.KwElse))
            {
                AddErrorAtTokenStart("Cannot start statement with 'else' or unexpected ';' after if statement.");
                SkipStatement();
            }
            else if (!allowEmpty && !Accept(Token.ChrSemicolon))
            {
                AddErrorAtLastParsedPosition("Expecting statement.");
                SkipStatement();
            }
        }