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()); } } }
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 }