Event Engine Class and Lookahead Regex

I am preparing to build an Event Engine for the imp. Think of a light bulb as the use case - it needs to support actions like turning the light on/off firing at specific times with optional repeats, randomization, etc. It also needs to support working when the imp is offline and needs to work with hardware.spiflash to ensure that things can continue to work even when power has been lost and the imp starts offline but still has a valid RTC. There would be Agent and Device code to provide a nice REST API that can create/list/delete events, ensures events are synchronized to the device, lets you know when an event is active, etc.

One solution I am considering involves the storing and parsing of ISO 8601 strings. I’m not terribly familiar but at first glance, they appear to have a nice compact syntax for most of the features I’m looking for. Unfortunately, a complete parsing solution for all the syntax sugar in ISO 8601 is not very straightforward… I did, however, stumble upon this regex based javascript solution which accomplishes validation (its not perfect, but pretty close) and when combined with the Timer and Serializer classes on the Electric Imp Github, should make a nice starting point: http://stackoverflow.com/questions/21686539/regular-expression-for-full-iso-8601-date-syntax

The first issue I’ve run into is that all but one of the regex’s throw an unfinished range error… Looking a little bit deeper and comparing https://electricimp.com/docs/squirrel/regexp/regexp/ to https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions it appears that negated lookaheads are what the squirrel regex class is missing.

`x(?!y) 
Matches 'x' only if 'x' is not followed by 'y'. This is called a negated lookahead.
For example, /\\d+(?!\\.)/ matches a number only if it is not followed by a decimal point. The regular expression /\\d+(?!\\.)/.exec("3.141") matches '141' but not '3.141'.
`

Is this feature something that could be added?

There is some squirrel code below that demonstrates the issue

`ISO8601RegexTable <- {}
ISO8601RegexTable.Duration <- regexp(@"^(R\\d*\\/)?P(?:\\d+(?:\\.\\d+)?Y)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?W)?(?:\\d+(?:\\.\\d+)?D)?(?:T(?:\\d+(?:\\.\\d+)?H)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?S)?)?$"),     


//	ERROR: unfinished range in regexp(string) 
//     if any of the regex's below are uncommented

//ISO8601RegexTable.Date <- regexp(@"^([\\+-]?\\d{4}(?!\\d{2}\\b))((-?)((0[1-9]|1[0-2])(\\3([12]\\d|0[1-9]|3[01]))?|W([0-4]\\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\\d|[12]\\d{2}|3([0-5]\\d|6[1-6])))([T\\s]((([01]\\d|2[0-3])((:?)[0-5]\\d)?|24\\:?00)([\\.,]\\d+(?!:))?)?(\\17[0-5]\\d([\\.,]\\d+)?)?([zZ]|([\\+-])([01]\\d|2[0-3]):?([0-5]\\d)?)?)?)?$"),
//ISO8601RegexTable["Range of Date/Date"] <- regexp(@"^([\\+-]?\\d{4}(?!\\d{2}\\b))((-?)((0[1-9]|1[0-2])(\\3([12]\\d|0[1-9]|3[01]))?|W([0-4]\\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\\d|[12]\\d{2}|3([0-5]\\d|6[1-6])))([T\\s]((([01]\\d|2[0-3])((:?)[0-5]\\d)?|24\\:?00)([\\.,]\\d+(?!:))?)?(\\17[0-5]\\d([\\.,]\\d+)?)?([zZ]|([\\+-])([01]\\d|2[0-3]):?([0-5]\\d)?)?)?)?(\\/)([\\+-]?\\d{4}(?!\\d{2}\\b))((-?)((0[1-9]|1[0-2])(\\3([12]\\d|0[1-9]|3[01]))?|W([0-4]\\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\\d|[12]\\d{2}|3([0-5]\\d|6[1-6])))([T\\s]((([01]\\d|2[0-3])((:?)[0-5]\\d)?|24\\:?00)([\\.,]\\d+(?!:))?)?(\\17[0-5]\\d([\\.,]\\d+)?)?([zZ]|([\\+-])([01]\\d|2[0-3]):?([0-5]\\d)?)?)?)?$"),      
//ISO8601RegexTable["Range of Date/Duration"] <- regexp(@"^(R\\d*\\/)?([\\+-]?\\d{4}(?!\\d{2}\\b))((-?)((0[1-9]|1[0-2])(\\4([12]\\d|0[1-9]|3[01]))?|W([0-4]\\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\\d|[12]\\d{2}|3([0-5]\\d|6[1-6])))([T\\s]((([01]\\d|2[0-3])((:?)[0-5]\\d)?|24\\:?00)([\\.,]\\d+(?!:))?)?(\\18[0-5]\\d([\\.,]\\d+)?)?([zZ]|([\\+-])([01]\\d|2[0-3]):?([0-5]\\d)?)?)?)?(\\/)P(?:\\d+(?:\\.\\d+)?Y)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?W)?(?:\\d+(?:\\.\\d+)?D)?(?:T(?:\\d+(?:\\.\\d+)?H)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?S)?)?$"),
//ISO8601RegexTable["Range of Duration/Date"] <- regexp(@"^(R\\d*\\/)?P(?:\\d+(?:\\.\\d+)?Y)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?W)?(?:\\d+(?:\\.\\d+)?D)?(?:T(?:\\d+(?:\\.\\d+)?H)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?S)?)?\\/([\\+-]?\\d{4}(?!\\d{2}\\b))((-?)((0[1-9]|1[0-2])(\\4([12]\\d|0[1-9]|3[01]))?|W([0-4]\\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\\d|[12]\\d{2}|3([0-5]\\d|6[1-6])))([T\\s]((([01]\\d|2[0-3])((:?)[0-5]\\d)?|24\\:?00)([\\.,]\\d+(?!:))?)?(\\18[0-5]\\d([\\.,]\\d+)?)?([zZ]|([\\+-])([01]\\d|2[0-3]):?([0-5]\\d)?)?)?)?$")



testDatesArray <- [
         "2013",        
         "2013-01-05",
         "2013-01-05T04:13:00+00:00",
         "2013-99-99T04:13:00+00:00",
         "P1Y2M10DT2H30M",
         "P1.5Y2.5M10.5DT2.5H30.5M",
         "P1W",
         "P1.5W",
         "R/P1D",         
         "R/P1.5W",
         "R5/P1.5W",        
         "R/P5.1Y3.5M3.4W2.5D",      
         "2007-03-01T13:00:00Z/2008-05-11T15:30:00Z",
         "2007-03-01T13:00:00Z/P1Y2M10DT2H30M",
         "2012-10/P1M",
         "2012-10/P1W",
         "2012-10/P1.5W",
         "R5/2007-03-01T13:00:00Z/P1Y2M10DT2H30M", 
         "R5/2012-10/P1M", 
         "R/2012-10/P1W",
         "P1Y2M10DT2H30M/2007-03-01T13:00:00Z", 
         "P1Y/2007-03-01",      
         "R2/P1Y2M10DT2H30M/2007-03-01T13:00:00Z", 
    ]


foreach(key, r in ISO8601RegexTable){
    server.log("Testing using " + key)
    
    foreach(key, val in testDatesArray){
        local results = r.capture(val)
        
        if(results != null)
            server.log("[valid]: " + val)
        else
            server.log("[invalid]: " + val)
    }
}`

Also, I’m open to ideas if someone has a better strategy on how to begin approaching this problem… :slight_smile: