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