Source code for julian.mjd_pyparser

##########################################################################################
# julian/mjd_pyparser.py
##########################################################################################
"""
=========================
MJD/JD pyparsing Grammars
=========================
"""

from julian.time_pyparser import req_timesys

from pyparsing import (
    CaselessLiteral,
    Combine,
    FollowedBy,
    Literal,
    OneOrMore,
    Optional,
    ParserElement,
    StringEnd,
    Suppress,
    White,
    Word,
    ZeroOrMore,
    alphanums,
    nums,
    one_of,
    srange,
)

##########################################################################################
# Begin grammar
##########################################################################################

# All whitespace is handled explicitly
ParserElement.set_default_whitespace_chars('')

# Useful definitions...
white     = Suppress(OneOrMore(White()))
opt_white = Suppress(ZeroOrMore(White()))

def _action(name, value, s, l, t):
    return [(name, value), ('~', s.upper().index(t[0].upper(), l) + len(t[0]))]

def _actions(items, s, l, t):
    item_list = []
    for k in range(0, len(items), 2):
        item_list.append((items[k], items[k+1]))
    return item_list + [('~', s.upper().index(t[0].upper(), l) + len(t[0]))]

##########################################################################################
# Numbers
##########################################################################################

nonzero = srange('[1-9]')
opt_sign = one_of(['-', '+', ''])
digits = Word(nonzero, nums) | Literal('0')

int_value = Combine(opt_sign + digits)
int_value.set_parse_action(lambda s,l,t: _action('DAY', int(t[0]), s,l,t))

float_value = Combine(opt_sign + digits + Literal('.') + Optional(Word(nums)))
float_value.set_parse_action(lambda s,l,t: _action('DAY', float(t[0]), s,l,t))

# Note: float_value must appear before int_value!
number = float_value | int_value

##########################################################################################
# Integer MJD day
##########################################################################################

mjd = CaselessLiteral('MJD')
mjd.set_parse_action(lambda s,l,t: _actions(['YEAR', 'MJD'], s,l,t))

par_mjd = CaselessLiteral('(MJD)')
par_mjd.set_parse_action(lambda s,l,t: _actions(['YEAR', 'MJD'], s,l,t))

mjd_date = (
    mjd + opt_white + int_value |
    int_value + white + mjd |
    int_value + opt_white + par_mjd
)

##########################################################################################
# Numeric date = MJD/JD etc. plus a floating-point number
##########################################################################################

JD_TYPES = {
    'JD' : ('JD' , 'UTC'),
    'MJD': ('MJD', 'UTC'),
}

jd_type = one_of(['JD', 'MJD'], caseless=True)
jd_type.set_parse_action(lambda s,l,t:
                         _actions(['YEAR', t[0].upper()], s,l,t))

paren_jd_type = one_of(['(' + k + ')' for k in JD_TYPES.keys()], caseless=True)
paren_jd_type.set_parse_action(lambda s,l,t:
                               _actions(['YEAR', t[0][1:-1].upper()], s,l,t))

numeric_date = (
    jd_type + opt_white + number |
    number + white + jd_type |
    number + opt_white + paren_jd_type
)

JXD_TYPES = {
    'JED' : ('JD' , 'TDB'),
    'JTD' : ('JD' , 'TT' ),
    'MJED': ('MJD', 'TDB'),
    'MJTD': ('MJD', 'TT' ),
}

jxd_type = one_of(JXD_TYPES.keys(), caseless=True)
jxd_type.set_parse_action(lambda s,l,t:
                          _actions(['YEAR', JXD_TYPES[t[0].upper()][0],
                                    'TIMESYS', JXD_TYPES[t[0].upper()][1]], s,l,t))

paren_jxd_type = one_of(['(' + k + ')' for k in JXD_TYPES.keys()], caseless=True)
paren_jxd_type.set_parse_action(lambda s,l,t:
                                _actions(['YEAR', JXD_TYPES[t[0][1:-1].upper()][0],
                                          'TIMESYS', JXD_TYPES[t[0][1:-1].upper()][1]],
                                         s,l,t))

numeric_timesys_date = (
    jxd_type + opt_white + number |
    number + white + jxd_type |
    number + opt_white + paren_jxd_type
)

##########################################################################################

[docs] def mjd_pyparser(*, floating=True, timesys=True, padding=True, embedded=False): """A date parser using MJD, JD, MJED, or JED. Parameters: floating (bool, optional): True to allow fractional days. If false, only an MJD integer date is permitted. timesys (str, optional): True to allow an explicit time system in the string via a values such as "MJD", "MJED", "JD", or JED". padding (bool, optional): True to ignore leading or trailing white space. embedded (bool, optional): True to allow the time to be followed by additional text. Returns: pyparsing.ParserElement: A parser for the selected syntax. Calling the `as_list()` method on the returned ParseResult object returns a list containing some but not all of these tuples, depending on what appears in the parsed string: * ("YEAR", type): Either "MJD" or "JD", indicating that the day value is not part of an actual year. * ("TIMESYS", name): Time system, either "UTC" or "TDB". * ("DAY", day); Day number as either an int or a float. * ("~", number): The last occurrence of this tuple in the list contains the number of characters matched. """ if floating: if timesys: pyparser = numeric_date + req_timesys | numeric_timesys_date | numeric_date else: pyparser = numeric_date else: pyparser = mjd_date if padding: pyparser = opt_white + pyparser if embedded: pyparser += ~FollowedBy(alphanums) elif padding: pyparser += opt_white + StringEnd() else: pyparser += StringEnd() return pyparser
##########################################################################################