Example #1
0
 def test_decorator(self):
     m1 = Matcher()
     m2 = Matcher()
     f = lambda: None
     m1.register(_(), f)
     decf = m2.handler(_())(f)
     self.assertEquals(decf, f)
     self.assertEquals(m1, m2)
Example #2
0
 def test_int_constant(self):
     self.assertTrue(self.parse('1'), _(1))
     self.assertTrue(self.parse('-1'), _(-1))
     try:
         self.parse('- 1')
         self.fail('bad integer')
     except parser.ParseException:
         pass
Example #3
0
 def test_int_constant(self):
     self.assertTrue(self.parse('1'), _(1))
     self.assertTrue(self.parse('-1'), _(-1))
     try:
         self.parse('- 1')
         self.fail('bad integer')
     except parser.ParseException:
         pass
Example #4
0
 def test_equality(self):
     m1 = Matcher()
     m2 = Matcher()
     self.assertEquals(m1, m2)
     f = lambda: None
     m2.register(_(), f)
     self.assertNotEquals(m1, m2)
     m1.register(_(), f)
     self.assertEquals(m1, m2)
Example #5
0
 def test_handler_priority(self):
     m = Matcher()
     m.register(_(1), lambda: 'my precious, the one')
     m.register(_(int), lambda: 'just an int')
     m.register(_(), lambda: 'just an object? whatever')
     m.register(_(str), lambda: 'i wish i could find a string')
     self.assertNotEquals(m('hi'), 'i wish i could find a string')
     self.assertEquals(m(None), 'just an object? whatever')
     self.assertEquals(m(3), 'just an int')
     self.assertEquals(m(1), 'my precious, the one')
Example #6
0
 def test_float_constant(self):
     self.assertEquals(self.parse('1.'), _(1.))
     self.assertEquals(self.parse('.5'), _(.5))
     self.assertEquals(self.parse('1.5'), _(1.5))
     self.assertEquals(self.parse('-1.'), _(-1.))
     self.assertEquals(self.parse('-.5'), _(-.5))
     self.assertEquals(self.parse('-1.5'), _(-1.5))
     try:
         self.parse('1 . 0')
         self.fail('bad float')
     except parser.ParseException:
         pass
Example #7
0
 def test_float_constant(self):
     self.assertEquals(self.parse('1.'), _(1.))
     self.assertEquals(self.parse('.5'), _(.5))
     self.assertEquals(self.parse('1.5'), _(1.5))
     self.assertEquals(self.parse('-1.'), _(-1.))
     self.assertEquals(self.parse('-.5'), _(-.5))
     self.assertEquals(self.parse('-1.5'), _(-1.5))
     try:
         self.parse('1 . 0')
         self.fail('bad float')
     except parser.ParseException:
         pass
Example #8
0
 def test_or(self):
     self.assertEquals(self.parse('x | y'), _() % 'x' | _() % 'y')
Example #9
0
 def test_typed_named_var(self):
     pattern = self.parse('x:str')
     self.assertEquals(pattern, _(str)%'x')
Example #10
0
 def test_named_var(self):
     pattern = self.parse('x')
     self.assertEquals(pattern, _()%'x')
Example #11
0
 def test_case_classes(self):
     self.assertEquals(self.parse('Case3(1, 2, 3)'), _(Case3(1, 2, 3)))
     self.assertEquals(self.parse('Case0()'), _(Case0()))
Example #12
0
 def test_or(self):
     self.assertEquals(self.parse('x | y'), _()%'x' | _()%'y')
Example #13
0
 def test_head_tail(self):
     self.assertEquals(self.parse('head :: tail'), _()%'head' + _()%'tail')
     self.assertEquals(self.parse('head :: []'), _()%'head' + _([]))
     self.assertEquals(self.parse('a :: b :: c'),
             _()%'a' + _()%'b' + _()%'c')
     self.assertEquals(self.parse('a :: b :: c :: d'),
             _()%'a' + _()%'b' + _()%'c' + _()%'d')
Example #14
0
 def get_named_var(var_name):
     try:
         get_type(var_name)
     except ParseException:
         return _()%var_name
     raise ParseException('var name clashes with type: %s' % var_name)
Example #15
0
 def get_named_var(var_name):
     try:
         get_type(var_name)
     except ParseException:
         return _() % var_name
     raise ParseException('var name clashes with type: %s' % var_name)
Example #16
0
 def test_regex_constant(self):
     self.assertEquals(self.parse('/abc/'), _(re.compile('abc')))
     self.assertEquals(self.parse(r'/\//'), _(re.compile('/')))
     self.assertEquals(self.parse(r'/\\/'), _(re.compile(r'\\')))
Example #17
0
 def test_str_constant(self):
     self.assertEquals(self.parse('"abc"'), _('abc'))
     self.assertEquals(self.parse("'abc'"), _('abc'))
Example #18
0
 def test_anon_var(self):
     pattern = self.parse('_')
     self.assertEquals(pattern, _())
Example #19
0
 def test_nested_type_var(self):
     pattern = self.parse('x:unittest.TestCase')
     self.assertEquals(pattern, _(unittest.TestCase) % 'x')
Example #20
0
 def test_typed_named_var(self):
     pattern = self.parse('x:str')
     self.assertEquals(pattern, _(str) % 'x')
Example #21
0
 def test_typed_anon_var(self):
     pattern = self.parse('_:str')
     self.assertEquals(pattern, _(str))
Example #22
0
def Parser(context=None):
    """
    Create a parser.

    :param context: optional context, defaults to the caller's
        `globals()`
    :type context: dict

    .. warning:: creating a parser is expensive!

    Usage and syntax examples:

        >>> parser = Parser()

    match anything anonymously:

        >>> parser('_') << 'whatever'
        Match({})

    match anything and bind to a name:

        >>> parser('x') << 1
        Match({'x': 1})

    match instances of a specific type:

        >>> parser('_:str') << 1
        >>> parser('_:int') << 1
        Match({})
        >>> parser('x:str') << 'abc'
        Match({'x': 'abc'})

    match int, float, str and bool constants:

        >>> parser('1') << 1
        Match({})
        >>> parser('1.618') << 1.618
        Match({})
        >>> parser('"abc"') << 'abc'
        Match({})
        >>> parser('True') << True
        Match({})

    match lists:

        >>> parser('[]') << ()
        Match({})
        >>> parser('[x:int]') << [1]
        Match({'x': 1})
        >>> parser('[a, b, _]') << [1, 2, 3]
        Match({'a': 1, 'b': 2})

    split head vs. tail:

        >>> parser('a::b') << (1, 2, 3)
        Match({'a': 1, 'b': (2, 3)})
        >>> parser('a::b::c') << (0, 1, 2, 3, 4)
        Match({'a': 0, 'c': (2, 3, 4), 'b': 1})

    match named tuples (as if they were Scala case classes)

        >>> try:
        ...     from collections import namedtuple
        ...     Case3 = namedtuple('Case3', 'a b c')
        ...     parser = Parser() # Case3 has to be in the context
        ...     parser('Case3(x, y, z)') << Case3(1, 2, 3)
        ... except ImportError:
        ...     from pyfpm.pattern import Match
        ...     Match({'y': 2, 'x': 1, 'z': 3}) # no namedtuple in python < 2.6
        Match({'y': 2, 'x': 1, 'z': 3})

    boolean or between expressions:

        >>> parser('a:int|b:str') << 1
        Match({'a': 1})
        >>> parser('a:int|b:str') << 'hello'
        Match({'b': 'hello'})

    nest expressions:

        >>> parser('[[[x:int]]]') << [[[1]]]
        Match({'x': 1})
        >>> parser('[_:int|[], 2, 3]') << (1, 2, 3)
        Match({})
        >>> parser('[_:int|[], 2, 3]') << ([], 2, 3)
        Match({})
        >>> parser('[_:int|[], 2, 3]') << ([1], 2, 3)

    """
    if context is None:
        context = _get_caller_globals()

    # parsing actions
    def get_type(type_name):
        try:
            t = eval(type_name, context)
        except NameError:
            raise ParseException('unknown type: %s' % type_name)
        if not isinstance(t, type):
            raise ParseException('not a type: %s' % type_name)
        return t

    def get_named_var(var_name):
        try:
            get_type(var_name)
        except ParseException:
            return _() % var_name
        raise ParseException('var name clashes with type: %s' % var_name)

    # begin grammar
    type_ = Word(
        alphas, alphanums +
        '._')('type_').setParseAction(lambda *args: get_type(args[-1].type_))

    anon_var = Literal("_")('anon_var').setParseAction(lambda *args: _())

    named_var = Word(alphas, alphanums + '_')('named_var').setParseAction(
        lambda *args: get_named_var(args[-1].named_var))

    untyped_var = (named_var | anon_var)('untyped_var')

    typed_var = (untyped_var + Suppress(':') +
                 type_)('typed_var').setParseAction(lambda *args: _(args[
                     -1].type_) % args[-1].untyped_var.bound_name)

    var = (typed_var | untyped_var)('var')

    int_const = Combine(Optional('-') + Word(nums))(
        'int_const').setParseAction(lambda *args: int(args[-1].int_const))

    float_const = Combine(
        Optional('-') + Word(nums) + Literal('.') + Optional(Word(nums))
        | Optional('-') + Literal('.') + Word(nums)
    )('float_const').setParseAction(lambda *args: float(args[-1].float_const))

    str_const = (quotedString
                 | dblQuotedString)('str_const').setParseAction(removeQuotes)

    regex_const = QuotedString(
        quoteChar='/', escChar='\\')('regex_const').setParseAction(
            lambda *args: re.compile(args[-1].regex_const))

    true = Keyword('True').setParseAction(lambda *args: _(True))
    false = Keyword('False').setParseAction(lambda *args: _(False))
    null = Keyword('None').setParseAction(lambda *args: _(None))

    const = (float_const | int_const | str_const | regex_const | false | true
             | null)('const').setParseAction(lambda *args: _(args[-1].const))

    scalar = Forward()

    pattern = Forward()

    head_tail = (scalar + Suppress('::') + pattern)(
        'head_tail').setParseAction(lambda *args: args[-1][0] + args[-1][1])

    list_item = (pattern | scalar)('list_item')

    list_contents = Optional(delimitedList(list_item))('list_contents')

    full_list = (Suppress('[') + list_contents + Suppress(']')
                 )('full_list').setParseAction(lambda *args: _(list(args[-1])))

    list_ = (head_tail | full_list)('list')

    case_class = Combine(
        type_ + Suppress('(') + list_contents +
        Suppress(')'))('case_class').setParseAction(
            lambda *args: _(args[-1][0].type_(*args[-1][0].list_contents)))

    scalar << (const | var | case_class
               | Suppress('(') + pattern + Suppress(')'))('scalar')

    or_clause = (list_ | scalar)('or_clause')

    or_expression = (or_clause + Suppress('|') +
                     pattern)('or_expression').setParseAction(
                         lambda *args: args[-1][0] | args[-1][1])

    def conditional_pattern_action(*args):
        try:
            pattern, condition_string = args[-1]
            code = compile(condition_string.strip(), '<pattern_condition>',
                           'eval')
            pattern.if_(_IfCondition(code, context))
            return pattern
        except ValueError:
            pass

    pattern << ((or_expression | or_clause) +
                Optional(Suppress(Keyword('if')) + restOfLine)
                )('pattern').setParseAction(conditional_pattern_action)

    # end grammar

    def parse(expression):
        (p, ) = pattern.parseString(expression, parseAll=True)
        return p

    parse.context = context
    parse.setDebug = pattern.setDebug
    return parse
Example #23
0
 def test_autoparse_context(self):
     m = Matcher([('y:TestMatcher', lambda y: self.assertEquals(self, y))])
     self.assertEquals(m.bindings[0][0], _(TestMatcher)%'y')
     m(self)
Example #24
0
 def test_case_classes(self):
     self.assertEquals(self.parse('Case3(1, 2, 3)'), _(Case3(1, 2, 3)))
     self.assertEquals(self.parse('Case0()'), _(Case0()))
Example #25
0
def Parser(context=None):
    """
    Create a parser.

    :param context: optional context, defaults to the caller's
        `globals()`
    :type context: dict

    .. warning:: creating a parser is expensive!

    Usage and syntax examples:

        >>> parser = Parser()

    match anything anonymously:

        >>> parser('_') << 'whatever'
        Match({})

    match anything and bind to a name:

        >>> parser('x') << 1
        Match({'x': 1})

    match instances of a specific type:

        >>> parser('_:str') << 1
        >>> parser('_:int') << 1
        Match({})
        >>> parser('x:str') << 'abc'
        Match({'x': 'abc'})

    match int, float, str and bool constants:

        >>> parser('1') << 1
        Match({})
        >>> parser('1.618') << 1.618
        Match({})
        >>> parser('"abc"') << 'abc'
        Match({})
        >>> parser('True') << True
        Match({})

    match lists:

        >>> parser('[]') << ()
        Match({})
        >>> parser('[x:int]') << [1]
        Match({'x': 1})
        >>> parser('[a, b, _]') << [1, 2, 3]
        Match({'a': 1, 'b': 2})

    split head vs. tail:

        >>> parser('a::b') << (1, 2, 3)
        Match({'a': 1, 'b': (2, 3)})
        >>> parser('a::b::c') << (0, 1, 2, 3, 4)
        Match({'a': 0, 'c': (2, 3, 4), 'b': 1})

    match named tuples (as if they were Scala case classes)

        >>> try:
        ...     from collections import namedtuple
        ...     Case3 = namedtuple('Case3', 'a b c')
        ...     parser = Parser() # Case3 has to be in the context
        ...     parser('Case3(x, y, z)') << Case3(1, 2, 3)
        ... except ImportError:
        ...     from pyfpm.pattern import Match
        ...     Match({'y': 2, 'x': 1, 'z': 3}) # no namedtuple in python < 2.6
        Match({'y': 2, 'x': 1, 'z': 3})

    boolean or between expressions:

        >>> parser('a:int|b:str') << 1
        Match({'a': 1})
        >>> parser('a:int|b:str') << 'hello'
        Match({'b': 'hello'})

    nest expressions:

        >>> parser('[[[x:int]]]') << [[[1]]]
        Match({'x': 1})
        >>> parser('[_:int|[], 2, 3]') << (1, 2, 3)
        Match({})
        >>> parser('[_:int|[], 2, 3]') << ([], 2, 3)
        Match({})
        >>> parser('[_:int|[], 2, 3]') << ([1], 2, 3)

    """
    if context is None:
        context = _get_caller_globals()

    # parsing actions
    def get_type(type_name):
        try:
            t = eval(type_name, context)
        except NameError:
            raise ParseException('unknown type: %s' % type_name)
        if not isinstance(t, type):
            raise ParseException('not a type: %s' % type_name)
        return t

    def get_named_var(var_name):
        try:
            get_type(var_name)
        except ParseException:
            return _()%var_name
        raise ParseException('var name clashes with type: %s' % var_name)

    # begin grammar
    type_ = Word(alphas, alphanums + '._')('type_').setParseAction(
            lambda *args: get_type(args[-1].type_))

    anon_var = Literal("_")('anon_var').setParseAction(lambda *args: _())

    named_var = Word(alphas, alphanums + '_')('named_var').setParseAction(
            lambda *args: get_named_var(args[-1].named_var))

    untyped_var = (named_var | anon_var)('untyped_var')

    typed_var = (untyped_var + Suppress(':') + type_)(
            'typed_var').setParseAction(
            lambda *args: _(args[-1].type_)%args[-1].untyped_var.bound_name)

    var = (typed_var | untyped_var)('var')

    int_const = Combine(Optional('-') + Word(nums))(
            'int_const').setParseAction(lambda *args: int(args[-1].int_const))

    float_const = Combine(Optional('-') + Word(nums) + Literal('.')
            + Optional(Word(nums)) |
            Optional('-') + Literal('.') + Word(nums))(
            'float_const').setParseAction(
                    lambda *args: float(args[-1].float_const))

    str_const = (quotedString | dblQuotedString)('str_const').setParseAction(
            removeQuotes)

    regex_const = QuotedString(quoteChar='/', escChar='\\')('regex_const'
            ).setParseAction(lambda *args: re.compile(args[-1].regex_const))

    true = Keyword('True').setParseAction(lambda *args: _(True))
    false = Keyword('False').setParseAction(lambda *args: _(False))
    null = Keyword('None').setParseAction(lambda *args: _(None))

    const = (float_const | int_const | str_const | regex_const |
            false | true | null)('const').setParseAction(
                    lambda *args: _(args[-1].const))

    scalar = Forward()

    pattern = Forward()

    head_tail = (scalar + Suppress('::') + pattern)(
            'head_tail').setParseAction(lambda *args: args[-1][0] + args[-1][1])

    list_item = (pattern | scalar)('list_item')

    list_contents = Optional(delimitedList(list_item))('list_contents')

    full_list = (Suppress('[') + list_contents + Suppress(']'))(
            'full_list').setParseAction(lambda *args: _(list(args[-1])))

    list_ = (head_tail | full_list)('list')

    case_class = Combine(type_ + Suppress('(') + list_contents +
            Suppress(')'))('case_class').setParseAction(
                lambda *args: _(args[-1][0].type_(*args[-1][0].list_contents)))

    scalar << (const | var | case_class |
            Suppress('(') + pattern + Suppress(')'))('scalar')

    or_clause = (list_ | scalar)('or_clause')

    or_expression = (or_clause + Suppress('|') + pattern)(
            'or_expression').setParseAction(
                    lambda *args: args[-1][0] | args[-1][1])

    def conditional_pattern_action(*args):
        try:
            pattern, condition_string = args[-1]
            code = compile(condition_string.strip(),
                    '<pattern_condition>', 'eval')
            pattern.if_(_IfCondition(code, context))
            return pattern
        except ValueError:
            pass

    pattern << ((or_expression | or_clause) +
            Optional(Suppress(Keyword('if')) + restOfLine))(
                    'pattern').setParseAction(conditional_pattern_action)

    # end grammar

    def parse(expression):
        (p,) = pattern.parseString(expression, parseAll=True)
        return p

    parse.context = context
    parse.setDebug = pattern.setDebug
    return parse
Example #26
0
 def test_nested_or(self):
     self.assertEquals(self.parse('[x | y]'), _([_() % 'x' | _() % 'y']))
     self.assertEquals(self.parse('[(x | y)]'), _([_() % 'x' | _() % 'y']))
Example #27
0
 def test_explicit_list(self):
     self.assertEquals(self.parse('[]'), _([]))
     self.assertEquals(self.parse('[x:int]'), _([_(int)%'x']))
     self.assertEquals(self.parse('[_, x:int]'), _(_(), _(int)%'x'))
     self.assertEquals(self.parse('[_, []]'), _(_(), _([])))
     self.assertEquals(self.parse('[[]]'), _([_([])]))
     self.assertEquals(self.parse('[[], _]'), _([_([]), _()]))
Example #28
0
 def test_simplematch(self):
     m = Matcher()
     m.register(_(), lambda: 'test')
     self.assertEquals(m(None), 'test')
Example #29
0
 def test_nested_or(self):
     self.assertEquals(self.parse('[x | y]'), _([_()%'x' | _()%'y']))
     self.assertEquals(self.parse('[(x | y)]'), _([_()%'x' | _()%'y']))
Example #30
0
 def test_str_constant(self):
     self.assertEquals(self.parse('"abc"'), _('abc'))
     self.assertEquals(self.parse("'abc'"), _('abc'))
Example #31
0
 def test_anon_var(self):
     pattern = self.parse('_')
     self.assertEquals(pattern, _())
Example #32
0
 def test_explicit_list(self):
     self.assertEquals(self.parse('[]'), _([]))
     self.assertEquals(self.parse('[x:int]'), _([_(int) % 'x']))
     self.assertEquals(self.parse('[_, x:int]'), _(_(), _(int) % 'x'))
     self.assertEquals(self.parse('[_, []]'), _(_(), _([])))
     self.assertEquals(self.parse('[[]]'), _([_([])]))
     self.assertEquals(self.parse('[[], _]'), _([_([]), _()]))
Example #33
0
 def test_typed_anon_var(self):
     pattern = self.parse('_:str')
     self.assertEquals(pattern, _(str))
Example #34
0
 def test_constructor(self):
     f = lambda: None
     m1 = Matcher([(_(), f)])
     m2 = Matcher()
     m2.register(_(), f)
     self.assertEquals(m1, m2)
Example #35
0
 def test_nested_type_var(self):
     pattern = self.parse('x:unittest.TestCase')
     self.assertEquals(pattern, _(unittest.TestCase)%'x')
Example #36
0
 def test_autoparse(self):
     m = Matcher([('1', lambda: None)])
     self.assertEquals(m.bindings[0][0], _(1))
Example #37
0
 def test_varbind(self):
     m = Matcher()
     m.register(_()%'x', lambda x: 'x=%s' % x)
     self.assertEquals(m(None), 'x=None')
     self.assertEquals(m(1), 'x=1')
Example #38
0
 def test_named_var(self):
     pattern = self.parse('x')
     self.assertEquals(pattern, _() % 'x')
Example #39
0
 def test_regex_constant(self):
     self.assertEquals(self.parse('/abc/'), _(re.compile('abc')))
     self.assertEquals(self.parse(r'/\//'), _(re.compile('/')))
     self.assertEquals(self.parse(r'/\\/'), _(re.compile(r'\\')))
Example #40
0
 def test_head_tail(self):
     self.assertEquals(self.parse('head :: tail'),
                       _() % 'head' + _() % 'tail')
     self.assertEquals(self.parse('head :: []'), _() % 'head' + _([]))
     self.assertEquals(self.parse('a :: b :: c'),
                       _() % 'a' + _() % 'b' + _() % 'c')
     self.assertEquals(self.parse('a :: b :: c :: d'),
                       _() % 'a' + _() % 'b' + _() % 'c' + _() % 'd')