Пример #1
0
        private AbstractStatementNode ParseStatement()
        {
            for ( ; ;)
            {
                switch (_scanner.Token().Value)
                {
                case '{':
                    return(ParseBlockStatement());

                case 'A':
                    if (_scanner.HaveIdentifier(KeywordIf))
                    {
                        return(ParseIfStatement());
                    }
                    else if (_scanner.HaveIdentifier(KeywordFor))
                    {
                        return(ParseForStatement());
                    }
                    else if (_scanner.HaveIdentifier(KeywordReturn))
                    {
                        return(ParseReturnStatement());
                    }
                    else if (_scanner.HaveIdentifier(KeywordBreak))
                    {
                        return(ParseBreakStatement());
                    }
                    else if (_scanner.HaveIdentifier(KeywordContinue))
                    {
                        return(ParseContinueStatement());
                    }
                    else if (_scanner.HaveIdentifier(KeywordGoto))
                    {
                        return(ParseGotoStatement());
                    }

                    // look for labeled statement as in label:
                    //
                    var ans = _expressionParser.Parse(terminatingCharacter: new CodePoint((byte)':'));
                    // this a pattern that admits
                    //		Label:
                    //	if the pattern doesn't fit, then the ExpectToken (';') just below will issue an error
                    if (ans is VariableTreeNode label && _scanner.IfToken(':'))
                    {
                        if (_scanner.IfTokenNoAdvance('}'))
                        {
                            // the following it is not legal or really acceptable:
                            //		{ if ( cond ) L1: }
                            //	so some grammars disallow
                            //		L1 : }
                            //	requiring input like this instead to resolve issues
                            //		L1: ; }
                            // however, when a label is used at the end of the block that contains it, e.g.
                            //		abc; ... L1 : }
                            //	this seems like an acceptable construct, and indeed some languages/grammars allow this.
                            return(new LabelStatement(new UserLabel(label.Value), null));
                        }
                        var stmt = ParseStatement();
                        return(new LabelStatement(new UserLabel(label.Value), stmt));
                    }

                    _scanner.ExpectToken(';');
                    return(new ExpressionStatement(ans));

                case ';':
                    _scanner.Advance();
                    return(new EmptyStatement());

                case CodePoint.EofValue:
                    _scanner.Error("expected statement");
                    break;

                default:
                    return(ParseExpressionStatement());
                }
            }
        }
Пример #2
0
        private void ParseExpressionToStack(CodePoint terminatingCharacter)
        {
            for ( ; ;)                  // Alternate between the States
            {
                // initial State: Unary State
                for ( ; ;)                  // Unary State
                {
                    var token = _scanner.Token();

                    switch (token.Value)
                    {
                    case '(':
                        _scanner.Advance();
                        _operatorStack.Push(Operator
                                            .PrecedenceGroupingParenthesis);              // nothing to reduce, as '(' has highest precedence
                        continue;

                    case ')':
                        _scanner.Advance();
                        if (_operatorStack.Peek() != Operator.FunctionCall)
                        {
                            _scanner.ErrorAtMark("unexpected ) in unary state");
                        }
                        _operatorStack.Pop();
                        // NB: We have a function call with no argument list, and we can either:
                        //	provide a unary function call operator
                        //		but our automated operator generators don't have that concept, or,
                        //  push an empty operand onto the stack so there's two operands for it to work
                        _operandStack.Push(null);
                        BuildAndPushOperator(Operator.FunctionCall);
                        break;

                    case '!':
                        _scanner.Advance();
                        _operatorStack.Push(Operator.LogicalNot);                            // prefix unary operators have implicit highest precedence
                        continue;

                    case '&':
                        _scanner.Advance();
                        _operatorStack.Push(Operator.AddressOf);
                        continue;

                    case '*':
                        _scanner.Advance();
                        _operatorStack.Push(Operator.Indirection);
                        continue;

                    case '+':
                        _scanner.Advance();
                        ReduceAndPushByChoice('+', Operator.PrefixIncrement, Operator.FixPoint);
                        continue;

                    case '-':
                        _scanner.Advance();
                        ReduceAndPushByChoice('-', Operator.PrefixDecrement, Operator.Negation);
                        continue;

                    case '~':
                        _scanner.Advance();
                        _operatorStack.Push(Operator.BitwiseComplement);
                        continue;

                    case 'A':
                        var id = _scanner.GetIdentifier();
                        // we can look up the variable in a symbol table here
                        _operandStack.Push(_symbolTable.LookupSymbol(id));
                        break;

                    case '0':
                        var num = _scanner.GetNonNegativeIntegralLiteral();
                        _operandStack.Push(new LongIntegerTreeNode(num));
                        break;

                    case '\"':
                        var str = _scanner.GetStringLiteral();
                        _operandStack.Push(new StringTreeNode(str));
                        break;

                    case '\'':
                        var chStr = _scanner.GetStringLiteral();
                        var cps   = new CodePointStream(chStr);
                        var cp1   = cps.Read();
                        if (cp1.AtEOF())
                        {
                            _scanner.ErrorAtMark("not enough characters in character literal");
                        }
                        var cp2 = cps.Read();
                        if (!cp2.AtEOF())
                        {
                            _scanner.ErrorAtMark("too many characters in character literal");
                        }
                        _operandStack.Push(new LongIntegerTreeNode(cp1.Value));
                        break;

                    default:
                        _scanner.ErrorAtMark("unrecognized token: " + (char)token.Value);
                        return;
                    }

                    break;        // switch to Binary State
                }                 // for (;;) { end of Unary State

                for ( ; ;)        // Binary State
                {
                    var cp = _scanner.Token();
                    if (cp.AtEOF() || cp == terminatingCharacter && AtTopLevelAndUnblocked())
                    {
                        return;
                    }

                    switch (cp.Value)
                    {
                    case '(':
                        _scanner.Advance();
                        ReduceThenPushOperator(Operator.FunctionCall);
                        break;

                    case ',':
                        _scanner.Advance();
                        AcceptComma();
                        break;

                    case ')':
                        _scanner.Advance();
                        AcceptCloseParen();
                        continue;                         // stay in Binary State

                    case '[':
                        _scanner.Advance();
                        ReduceThenPushOperator(Operator.Subscript);
                        break;

                    case ']':
                        _scanner.Advance();
                        // we have a [ b ], so we reduce until we reach the '['
                        ReduceUntilMatch(Operator.Subscript);
                        // reduction pops the operator, and, leaves us with a & b on the operand stack
                        BuildAndPushOperator(Operator.Subscript);
                        continue;                         // stay in Binary State

                    case '!':
                        _scanner.Advance();
                        if (!_scanner.IfCharacter('='))
                        {
                            _scanner.ErrorAtMark("expected = for !=");
                        }
                        ReduceThenPushOperator(Operator.NotEqual);
                        break;

                    case '*':
                        _scanner.Advance();
                        ReduceAndPushByChoice('=', Operator.AssignmentMultiplication, Operator.Multiplication);
                        break;

                    case '/':
                        _scanner.Advance();
                        ReduceAndPushByChoice('=', Operator.AssignmentDivision, Operator.Division);
                        break;

                    case '%':
                        _scanner.Advance();
                        ReduceAndPushByChoice('=', Operator.AssignmentModulo, Operator.Modulo);
                        break;

                    case '=':
                        _scanner.Advance();
                        ReduceAndPushByChoice('=', Operator.EqualEqual, Operator.Assignment);
                        break;

                    case '^':
                        _scanner.Advance();
                        ReduceAndPushByChoice('=', Operator.AssignmentBitwiseXor, Operator.BitwiseXor);
                        break;

                    case '+':
                        _scanner.Advance();
                        if (_scanner.IfCharacter('+'))
                        {
                            // C/C++ postfix ++
                            ReduceThenPushOperator(Operator.PostfixIncrement);
                            continue;                             // stay in Binary State
                        }

                        ReduceAndPushByChoice('=', Operator.AssignmentAddition, Operator.Addition);
                        break;

                    case '-':
                        _scanner.Advance();
                        if (_scanner.IfCharacter('>'))
                        {
                            // at least ->
                            if (_scanner.IfCharacter('*'))
                            {
                                // C++: member pointer: ->*
                                // ToDo: parse rhs of ->* (which is a member name) and generate an binary node to push on the operand stack
                                throw new System.NotImplementedException("->*");
                            }
                            else
                            {
                                // C/C++ indirect selection: ->
                                var memberName = _scanner.ExpectIdentifier("member identifier");
                                // ToDo: parse rhs of -> (which is a member name) and generate an operand to push on
                                throw new System.NotImplementedException("->");
                            }
                        }
                        else
                        {
                            ReduceAndPushByChoice('-', Operator.PostfixDecrement, '=', Operator.AssignmentSubtraction,
                                                  Operator.Subtraction);
                        }

                        break;

                    case '&':
                        _scanner.Advance();
                        ReduceAndPushByChoice('&', Operator.ShortCircutAnd, '=', Operator.AssignmentBitwiseAnd, Operator.BitwiseAnd);
                        break;

                    case '|':
                        _scanner.Advance();
                        ReduceAndPushByChoice('|', Operator.ShortCircutOr, '=', Operator.AssignmentBitwiseOr, Operator.BitwiseOr);
                        break;

                    case '.':
                        throw new System.NotImplementedException(". operator");

                    case '?':
                        _scanner.Advance();
                        // Operator.TernaryTest is used for grouping of the expressions during parsing,
                        //	This operator won't appear in the final tree (similar to Operator.GroupingParen which doesn't appear in the abstract tree either)
                        ReduceThenPushOperator(Operator.TernaryTest);
                        // Test 1003: a?b,c:d, which should parse ok as:
                        ////			a?(b,c):d
                        ////			   -and not-
                        ////			(a?b),(c:d)
                        ////			   as this makes no sense and thus is not helpful
                        break;

                    case ':':
                        _scanner.Advance();
                        if (_scanner.IfCharacter(':'))
                        {
                            throw new System.NotImplementedException(":: operator");
                        }
                        else
                        {
                            ReduceUntilMatch(Operator.TernaryTest);
                            // this will leave two operands on the stack, so for
                            //		a ? b :			-- which is what we have so far
                            //	a and b will be on the operand stack.
                            // now we push the true ternary operator:
                            _operatorStack.Push(Operator.TernaryChoice);
                            // when it gets reduced later, so for
                            //		a ? b : c;
                            // reducing this operator will consume a, b, and c.
                        }
                        break;

                    case '<':
                        _scanner.Advance();
                        ReduceAndPushByChoice('<', '=', Operator.AssignmentBitwiseLeftShift, Operator.BitwiseLeftShift, '=',
                                              Operator.LessOrEqual, Operator.LessThan);
                        break;

                    case '>':
                        _scanner.Advance();
                        ReduceAndPushByChoice('>', '=', Operator.AssignmentBitwiseRightShift, Operator.BitwiseRightShift, '=',
                                              Operator.GreaterOrEqual, Operator.GreaterThan);
                        break;

                    default:
                        return;                         // let caller deal with the "unexpected" input, like ;
                    }

                    break;  // switch to Unary State
                }           // end of for (;;) { Binary State
            }               // end of for(;;) { Alternating between the states
        }