schyntax / js-schyntax

A simple Node.js utility for parsing schedule strings, and finding the next scheduled event time.
MIT License
4 stars 2 forks source link

Mixin Syntax #3

Open bretcope opened 10 years ago

bretcope commented 10 years ago

I'm thinking about eliminating groups in favor of reusable mixins. The syntax might look something like this:

$weekdays = days(mon-fri)
$noon = hours(12)
$weekdaysAtNoon = { $weekdays $noon }
$weekdaysAtMidnight = { $weekdays hours(0) }

Tokens beginning with $ would be treated as mixins. Attempting to reassign a mixin should throw a syntax error (they can be overridden, however - described later). Since they can't be reassigned, it means we can make them order-independent, so you could use a mixin before it is defined and it will still have the correct value.

As with the rest of the language, whitespace would be insignificant. Mixin identifiers would be case-insensitive. Comma separators would be optional inside the {} since they feel natural, but the syntax is unambiguous without them. The braces would be optional when the mixin only consists of one expression.

The compiler would simply expand the mixins (recursively, as necessary) in order to flatten out the structure. The impact on the existing compiler code should be minimal.


The question becomes, how does this play with the definition of the actual schedule you want sch to use? I see a few possibilities, but here's what I'm leaning towards:

sch should look for a schedule in the following order:

  1. Any free floating schedule (i.e. min(0%2)). This is how sch already works, and it seems like it will remain the most common use case - no reason to break that. There can only be one free-floating schedule (m(0) $midnight = { hours(0) } sec(0) should be invalid syntax).
  2. Look for a "use" statement. There can be either a free floating schedule or a use statement. If both are present, a syntax error should be thrown. Examples:
    • use min(0)
    • use $mixinName
    • use { min(0) $mixinName }
  3. If none of the above conditions are met, a syntax error is thrown.

The use statement is technically redundant, but I think it improves the readability of a schedule which may be cluttered with mixin definitions. We could consider enforcing the use of "use" if a schedule string contains mixin definitions, but that seems unnecessary.

I would like sch to have a .mixin property where global mixins could be defined. Sch could actually ship with a default value for that property with some helpful mixins, but it would still allow users to override, or add to it, with their own mixins.

The sch function itself could accept a second optional argument sch( schedule, [mixins] ). This would allow users a high degree of flexibility in defining their mixins without screwing up the global defaults for everything else.

Mixins cannot be reassigned (they're not variables), but they can be overridden based on order of precedence.

  1. Any mixins defined in the schedule string.
  2. mixins argument.
  3. sch.mixins global property.

For example, if sch.mixins defines $a=h(12) and the mixins argument redefines it as $a=h(14) then any reference to $a in the schedule string will expand to h(14), but any reference to $a in sch.mixins will still expand to the original h(12). This is to prevent unintended consequences in composite mixins. You could think of it as inheriting parent scopes - you can redefine a variable in an inner scope without it affecting the outer scope.


If anyone has any thoughts (@rossipedia in particular), let's discuss them here.

rossipedia commented 10 years ago

So far, so good. I really like the idea, and I'd like to see some more concrete examples of the proposed syntax.

One thought I had is that we shouldn't need the $ prefix when defining the mixins via the argument or property, only when defining them in the language itself.

For example:

sch('$noon', { noon: 'hours(12)' });

as opposed to:

sch('$noon', { '$noon': 'hours(12)' });
bretcope commented 10 years ago

Oh that's interesting. I honestly hadn't even been thinking along the lines of the mixin argument being an object. I just expected it would be a big string. I don't know why it didn't occur to be, but I really like that.

bretcope commented 10 years ago

And yes, I agree. If it's an object, there's no need for the $ prefix. Frankly, there's probably no need for it in the general syntax, but it will make parsing easier and remove any possible need for reserved words.

clipperhouse commented 10 years ago

I haven’t used sch yet and so don’t have an informed opinion, but I’d think about those unnecessary bits of syntax ($ and use). Very subjective, but I lean towards YAGNI.

I think your parser will be more robust if you don’t have to lean on those signals.

bretcope commented 10 years ago

Side note: not sure why I thought mixins would eliminate the need for groups, but as soon as I gave it two seconds of thought, I realized they each serve a fundamentally different purpose, so both need to remain in the language. I'm not convinced group() was the right syntax, but that's an entirely different topic.

Related to groups, I think it should be noted that mixins cannot contain groups because the semantics would become very unclear. I could go into more detail about why, I just don't feel like it this moment.

On to Matt's point... I probably need to think about the implications of $ a little more extensively. My gut says $ not only makes parsing in code easier, but it makes the human parsing easier as well. Because of shell scripts, php, perl, etc, $token is instantly recognizable to most programmers as a variable, or variable-like, token. If you see $weekends hours(12), I would argue that it's more obvious that $weekends is a user-definable "variable" rather than a constant built into the language because of the $. Therefore, I'm leaning towards keeping it, but I'm open to counterarguments.

I think use is different because the proposal is that it would be optional. Essentially it would be a way to make your schedule a little more readable, not a requirement. However, I'm thinking maybe there's still a better way to integrate mixin definitions into the existing schedule definitions.

What about forcing all mixin definitions to be in a block which follows the schedule itself? I think this is the direction I want, but I'm not sure what the right syntax is yet.

Some possibilities:

  1. Don't include any explicit separator, and the mixin block simply starts where ever the first definition is.

    $weekends hours(12) $weekends = days(sat-sun)

    You could still break it up by lines to make it read cleaner.

    $weekends hours(12)
    $weekends = days(sat-sun)

    I don't love this option because it is non-obvious where you can place definitions versus a schedule.

  2. Define the block with a keyword and colon

    $weekends hours(12) mixins: $weekends = days(sat-sun)

    or

    $weekends hours(12)
    mixins:
     $weekends = days(sat-sun)

    This looks really nice in the multi-line, but the single line strikes me as confusing and messy.

  3. Require braces around the mixin block.

    $weekends hours(12) mixins{ $weekends = days(sat-sun) }
    ---
    $weekends hours(12)
    mixins {
     $weekends = days(sat-sun)
    }

    or possibly use a colon instead of equals to be more json/css-like.

    $weekends $noon mixins{ $weekends: days(sat-sun), $noon: hours(12) }
    ---
    $weekends $noon
    mixins {
     $weekends: days(sat-sun),
     $noon: hours(12)
    }

    Not sure commas should be required inside the block. It would definitely feel familiar to people, but ultimately the syntax would be unambiguous without them so I'm thinking they could be optional and silently ignored. That would be perfectly in-line with other parts of the language.

    Another point: I suppose there's really no reason to enclose multi-expression mixins in braces. This works just fine:

    mixins {
     $weekendsAtNoon: $weekends $noon,
     $weekends: days(sat-sun),
     $noon: hours(12)
    }

    Regardless of whether we keep $, it seems acceptable to allow omitting it in the mixins definition block. This fits well with Ross's suggestion.

    $weekends $noon
    mixins {
     weekends: days(sat-sun),
     noon: hours(12)
    }

    I think this is the syntax I would prefer, but if anyone has better suggestions, I'm open to them.

Thoughts? I like this little standards-committee we've got going.

rossipedia commented 10 years ago
$weekends $noon
mixins {
  weekends: days(sat-sun),
  noon: hours(12)
}

:+1: for that syntax

giovannicuccu commented 8 years ago

I've discovered schyntax only one month ago (Sept 2015) so I'm very late on this one, but I don'like the idea of mixins. I see schyntax as a more powerful and readable cron and the actual specs fit the purpose. Having an expression of one line only it's already a good for me, mixins adds a (bit of) complexity I'd like to avoid.

bretcope commented 8 years ago

@giovannicuccu mixins, or whatever I decide to call them, will probably look a little different than described here, but will probably still happen. Instead of plain-text replacement, they will be complete schedules which you insert into your schedule as an addition or exclusion.

The example of $noon is very contrived and not something I would ever expect someone to use. The real use case is for when you want to define something complex, especially something with a bunch of dates, like $usHolidays. You could also have things like $maintenanceWindows which you could exclude from schedules which shouldn't run during maintenance windows. It may ultimately be most useful in a cron replacement where you're possibly defining lots of schedules in a single file. Reusable pieces would make that a more pleasant experience.

There's still things to flesh out, and it's not my top priority, but I expect it will happen eventually.