r"""Make a parsing function from a peglet grammar, defining the
    grammar's semantic actions with keyword arguments.

    The parsing function maps a string to a results tuple or raises
    Unparsable. (It can optionally take a rule name to start from, by
    default the first in the grammar.) It doesn't necessarily match
    the whole input, just a prefix.

    >>> nums = Parser(r"nums = num ,\s* nums | num   num = (\d+) int", int=int)
    >>> nums('42, 137, and 0 are magic numbers')
    (42, 137)
    >>> nums('The magic numbers are 42, 137, and 0')
    Traceback (most recent call last):
    Unparsable: ('nums', '', 'The magic numbers are 42, 137, and 0')

def Parser(grammar, **actions):
    r"""Make a parsing function from a peglet grammar, defining the
    grammar's semantic actions with keyword arguments.

    The parsing function maps a string to a results tuple or raises
    Unparsable. (It can optionally take a rule name to start from, by
    default the first in the grammar.) It doesn't necessarily match
    the whole input, just a prefix.

    >>> nums = Parser(r"nums = num ,\s* nums | num   num = (\d+) int", int=int)
    >>> nums('42, 137, and 0 are magic numbers')
    (42, 137)
    >>> nums('The magic numbers are 42, 137, and 0')
    Traceback (most recent call last):
    Unparsable: ('nums', '', 'The magic numbers are 42, 137, and 0')
    """
    parts = re.split(' ('+_identifier+') += ',
                     ' '+re.sub(r'\s', ' ', grammar))
    if len(parts) == 1 or parts[0].strip():
        raise BadGrammar("Missing left hand side", parts[0])
    if len(set(parts[1::2])) != len(parts[1::2]):
        raise BadGrammar("Multiply-defined rule(s)", grammar)
    rules = dict((lhs, [alt.split() for alt in (' '+rhs+' ').split(' | ')])
                 for lhs, rhs in zip(parts[1::2], parts[2::2]))
    return lambda text, rule=parts[1]: _parse(rules, actions, rule, text)