The Grammar

Timepiece is based off an EBNF grammar using the awesome parsimonious library.

The idea is that the grammar parses the specification into arbitrary sections and then we turn those arbitrary sections into objects using the sections specified in timepiece.sections. As in, the definition of the grammar and the definition of the valid sections and their arguments are separate.

The grammar can be seen below:

EBNF grammar

time_spec = grouped_funcs?

grouped_funcs = grouped_funcs_first grouped_with_joiner*
grouped_with_joiner = joiner grouped_funcs
grouped_funcs_first = wrapped_grouped_funcs / func
wrapped_grouped_funcs = start_bracket grouped_funcs end_bracket

func = func_name "(" func_sig ")"
func_sig = key_pairs?

key_pairs = key_pair commad_key_pair*
commad_key_pair = "," key_pair
key_pair = key_name ":" func_or_value

func_or_value = arbitrary_string / number / func

start_bracket = "("
end_bracket = ")"
joiner = "&" / "|"
key_name = ~r"[a-zA-Z][a-zA-Z0-9]+"
func_name = ~r"[a-zA-Z][a-zA-Z0-9_]+"
number = ~r"[0-9]+" &~r"[\),]"
arbitrary_string = ~r"[\s\.a-zA-Z0-9;_-]+" !"("

Essentially we can create groups with parenthesis, sections are combined with | and & operators and sections are like functions with name(arg1: val1, arg2: val2(arg3: val3, arg:4: val4)).

For speed concerns, the grammar contains no white space and all white space is stripped from the specification before parsing.