espertechinc / nesper

NEsper - Complex Event Processing and Event Series Analysis for .NET
GNU General Public License v2.0
86 stars 26 forks source link

Syntax error around enumeration method #18

Closed pa-hobe closed 1 year ago

pa-hobe commented 6 years ago

The following statement collects a sequence of positive values or a sequence of negative values using the repeat until pattern:

select
    (coalesce(a, b)).countOf() as Count
from pattern[
    every ([3:] a=MyValueEvent(Value > 0) until MyValueEvent(Value <= 0))
    or
    every ([3:] b=MyValueEvent(Value < 0) until MyValueEvent(Value >= 0))
]

The form without parentheses coalesce(a, b).countOf() is rejected with an Incorrect syntax error.

However, if the statement is constructed via the EPStatementObjectModel, then the EPL string export does not add the parentheses. The Program.cs shows the successful parsing of the EPL statement version from above. Then the statement is compiled to the model representation. Creating a statement using the EPL string from the model representation is no longer possible because of the syntax exception.

The output of the program:


Original EPL statement
----------------------
select
    (coalesce(a, b)).countOf() as Count
from pattern[
    every ([3:] a=MyValueEvent(Value > 0) until MyValueEvent(Value <= 0))
    or
    every ([3:] b=MyValueEvent(Value < 0) until MyValueEvent(Value >= 0))
]

EPL from EPStatementObjectModel
-------------------------------
select coalesce(a,b).countOf() as Count from pattern [every ([3:] a=MyValueEvent(Value>0) until MyValueEvent(Value<=0)) or every ([3:] b=MyValueEvent(Value<0) until MyValueEvent(Value>=0))]

EPL without parentheses causes exception
----------------------------------------
com.espertech.esper.client.EPStatementSyntaxException: Incorrect syntax near '.' at line 1 column 20 [select coalesce(a,b).countOf() as Count from pattern [every ([3:] a=MyValueEvent(Value>0) until MyValueEvent(Value<=0)) or every ([3:] b=MyValueEvent(Value<0) until MyValueEvent(Value>=0))]]
   at com.espertech.esper.epl.parse.ParseHelper.Parse(String expression, String eplStatementErrorMsg, Boolean addPleaseCheck, ParseRuleSelector parseRuleSelector, Boolean rewriteScript)
   at com.espertech.esper.core.service.EPAdministratorHelper.CompileEPL(String eplStatement, String eplStatementForErrorMsg, Boolean addPleaseCheck, String statementName, SelectClauseStreamSelectorEnum defaultStreamSelector, EngineImportService engineImportService, VariableService variableService, SchedulingService schedulingService, String engineURI, ConfigurationInformation configSnapshot, PatternNodeFactory patternNodeFactory, ContextManagementService contextManagementService, ExprDeclaredService exprDeclaredService, TableService tableService)
   at com.espertech.esper.core.service.EPAdministratorHelper.CompileEPL(String eplStatement, String eplStatementForErrorMsg, Boolean addPleaseCheck, String statementName, EPServicesContext services, SelectClauseStreamSelectorEnum defaultStreamSelector)
   at com.espertech.esper.core.service.EPAdministratorImpl.CreateEPLStmt(String eplStatement, String statementName, Object userobject, Nullable`1 optionalStatementId)
   at com.espertech.esper.core.service.EPAdministratorImpl.CreateEPL(String eplStatement)
   at WindowHaving.Program.Main(String[] args) in [...]Program.cs:line 60

The Program.cs file:


using System;
using System.Globalization;
using com.espertech.esper.client;
using com.espertech.esper.client.soda;

namespace WindowHaving
{
    public sealed class MyValueEvent
    {
        public double Value { get; }

        public MyValueEvent(double value)
        {
            Value = value;
        }

        public override string ToString()
        {
            return
                nameof(MyValueEvent) + "(" +
                nameof(Value) + "=" + Value.ToString(NumberFormatInfo.InvariantInfo) + ")";
        }
    }

    internal sealed class Program
    {
        private static void Main(string[] args)
        {
            try
            {
                EPServiceProvider engine = EPServiceProviderManager.GetDefaultProvider();
                EPAdministrator admin = engine.EPAdministrator;
                admin.Configuration.AddEventType<MyValueEvent>();

                const string originalEplStatement =
                    "select\n" +
                    "    (coalesce(a, b)).countOf() as Count\n" +
                    "from pattern[\n" +
                    "    every ([3:] a=MyValueEvent(Value > 0) until MyValueEvent(Value <= 0))\n" +
                    "    or\n" +
                    "    every ([3:] b=MyValueEvent(Value < 0) until MyValueEvent(Value >= 0))\n" +
                    "]";
                EPStatement statement = admin.CreateEPL(originalEplStatement);
                EPStatementObjectModel model = admin.CompileEPL(originalEplStatement);
                string eplStatementFromObjectModel = model.ToEPL();

                Console.WriteLine("Original EPL statement");
                Console.WriteLine("----------------------");
                Console.WriteLine(originalEplStatement);
                Console.WriteLine("");

                Console.WriteLine("EPL from EPStatementObjectModel");
                Console.WriteLine("-------------------------------");
                Console.WriteLine(eplStatementFromObjectModel);
                Console.WriteLine("");

                Console.WriteLine("EPL without parentheses causes exception");
                Console.WriteLine("----------------------------------------");
                admin.CreateEPL(eplStatementFromObjectModel);
            }
            catch (Exception exception)
            {
                Console.WriteLine(exception);
            }
        }
    }
}

IMHO, the additional parentheses in (coalesce(a, b)).countOf() should not be necessary. Function parameters should have a higher precedence than the dot operator and coalesce(a, b).countOf() should suffice.

ajaxx commented 6 years ago

Raising question to Esper general since it would arguably affect both projects. @bernhardttom any thoughts?