Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FEAT: parse tree evaulator and listener #1169

Conversation

chenqi0805
Copy link
Collaborator

Description

This PR

  • adds ParseTreeEvaluator and ParseTreeEvaluatorListener
  • adjusts Operator to expose getNumberOfOperands
  • adjusts OperatorProvider to expose containsOperatorSymbol

Issues Resolved

Contributes to #1003

Check List

  • New functionality includes testing.
  • New functionality has been documented.
    • New functionality has javadoc added
  • Commits are signed with a real name per the DCO

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.
For more information on following Developer Certificate of Origin and signing off your commits, please check here.

Signed-off-by: Chen <[email protected]>
@chenqi0805 chenqi0805 requested a review from a team as a code owner March 10, 2022 15:18
@chenqi0805 chenqi0805 changed the title FEAT: parse tree evaulator and listener [Blocked] FEAT: parse tree evaulator and listener Mar 10, 2022
@chenqi0805
Copy link
Collaborator Author

This PR needs to receive modification after merging of #1165

@@ -35,4 +35,8 @@ public OperatorProvider(final List<Operator<?>> operators) {
}
return operator;
}

public boolean containsOperatorSymbol(final int symbol) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As we already have getOperator, should this be have a different name? Perhaps: containsOperator or supportsOperator?

* walker.walk(listener, ...);
* final Object result = listener.getResult();
*/
class ParseTreeEvaluatorListener extends DataPrepperExpressionBaseListener {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice! I like how this came out.

* walker.walk(listener, ...);
* final Object result = listener.getResult();
*/
class ParseTreeEvaluatorListener extends DataPrepperExpressionBaseListener {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice! I like how this came out.

void testEvaluateSuccess() throws ExpressionCoercionException {
try (final MockedConstruction<ParseTreeEvaluatorListener> ignored =
mockConstruction(ParseTreeEvaluatorListener.class, (mock, context) -> when(mock.getResult()).thenReturn(true))) {
when(coercionService.coerce(any(), any())).thenAnswer(invocation -> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't believe you need this when in the try-with-resources. Moving it out will be clearer.

void testEvaluateSuccess() throws ExpressionCoercionException {
try (final MockedConstruction<ParseTreeEvaluatorListener> ignored =
mockConstruction(ParseTreeEvaluatorListener.class, (mock, context) -> when(mock.getResult()).thenReturn(true))) {
when(coercionService.coerce(any(), any())).thenAnswer(invocation -> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't believe you need this when in the try-finally.

void testEvaluateFailureInWalk() {
try (final MockedConstruction<ParseTreeEvaluatorListener> ignored =
mockConstruction(ParseTreeEvaluatorListener.class)) {
doThrow(new RuntimeException()).when(parseTreeWalker).walk(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please move doThrow out of try-with-resources.

void testEvaluateFailureInCoerce() throws ExpressionCoercionException {
try (final MockedConstruction<ParseTreeEvaluatorListener> ignored =
mockConstruction(ParseTreeEvaluatorListener.class, (mock, context) -> when(mock.getResult()).thenReturn(true))) {
when(coercionService.coerce(any(), any())).thenThrow(new ExpressionCoercionException("test message"));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please move when out of try-with-resources.

void testEvaluateFailureInCoerce() throws ExpressionCoercionException {
try (final MockedConstruction<ParseTreeEvaluatorListener> ignored =
mockConstruction(ParseTreeEvaluatorListener.class, (mock, context) -> when(mock.getResult()).thenReturn(true))) {
when(coercionService.coerce(any(), any())).thenThrow(new ExpressionCoercionException("test message"));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please move when out of try-with-resources.

when(coercionService.coerce(any(), any())).thenAnswer(invocation -> {
Object[] args = invocation.getArguments();
final Object res = args[0];
final Class<Boolean> clazz = (Class<Boolean>) args[1];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that invocation has a method that will cast for you. getArgument(1) or something like that.

when(coercionService.coerce(any(), any())).thenAnswer(invocation -> {
Object[] args = invocation.getArguments();
final Object res = args[0];
final Class<Boolean> clazz = (Class<Boolean>) args[1];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that invocation has a method that will cast for you. getArgument(1) or something like that.

dlvenable
dlvenable previously approved these changes Mar 10, 2022
@@ -35,4 +35,8 @@ public OperatorProvider(final List<Operator<?>> operators) {
}
return operator;
}

public boolean containsOperatorSymbol(final int symbol) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As we already have getOperator, should this be have a different name? Perhaps: containsOperator or supportsOperator?

@chenqi0805 chenqi0805 changed the title [Blocked] FEAT: parse tree evaulator and listener FEAT: parse tree evaulator and listener Mar 10, 2022
@chenqi0805 chenqi0805 requested a review from dlvenable March 10, 2022 20:49
@chenqi0805 chenqi0805 mentioned this pull request Mar 10, 2022
4 tasks
throw new IllegalStateException("The ParseTreeEvaluatorListener has not been walked through exactly once by " +
"a ParseTreeWalker.");
}
return operandStack.peek();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might be safer to pop to prevent reuse of a listener considering each evaluate call needs a listener.

Copy link
Collaborator Author

@chenqi0805 chenqi0805 Mar 10, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reason I did not use pop is based off idempotency. It might be a little weird that the second getResult() call throws exception when called in succession.

operatorSymbolStack.push(nodeType);
} else if (nodeType == DataPrepperExpressionParser.RPAREN) {
// pop LPAREN at operatorSymbolStack top
operatorSymbolStack.pop();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider verifying the top of the stack is a LPAREN.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mean throw exception if it is not LPAREN? AFAICT, it should only happen if parentheses in the expression are unbalanced, which should already be ruled out by parser?

private void performSingleOperation(final Operator<?> operator) {
final int numOfArgs = operator.getNumberOfOperands();
final Object[] args = new Object[numOfArgs];
for (int i = numOfArgs - 1; i >= 0; i--) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Checking operandStack.size() >= numOfArgs and throwing a more informative error message may lead to a better customer experience.

Copy link
Collaborator Author

@chenqi0805 chenqi0805 Mar 10, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if it is necessary. Here is the reasoning: I suppose if the input parseTree is valid, we should never hit this error unless the evaluation algorithm is incorrect and fails to apply to certain corner cases. I do not think we should use exception or error message to guard against incorrect implementation. Also, the error message thrown here is not that helpful to cx on troubleshooting their if condition statement.

@chenqi0805 chenqi0805 merged commit 6394263 into opensearch-project:main Mar 10, 2022
@chenqi0805 chenqi0805 deleted the feat/1003-parse-tree-evaulator-and-listener branch March 10, 2022 21:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants