Exemple #1
0
 def auto_memoize(self, conservative=None, full=True, d=0):
     '''
     This configuration attempts to detect which memoizer is most effective
     for each matcher.  As such it is a general "fix" for left-recursive 
     grammars and is suggested in the warning shown when the right-only 
     memoizer detects left recursion.
     
     Lepl does not guarantee that all left-recursive grammars are handled 
     correctly.  The corrections applied may be incomplete and can be
     inefficient.  It is always better to re-write a grammar to avoid
     left-recursion.  One way to improve efficiency, at the cost of less
     accurate matching, is to specify a non-zero ``d`` parameter - this 
     is the maximum iteration depth that will be used (by default, when
     ``d`` is zero, it is the length of the remaining input, which can
     be very large).
     
     '''
     from lepl.core.rewriters import AutoMemoize
     from lepl.matchers.memo import LMemo, RMemo
     self.no_memoize()
     return self.add_rewriter(
         AutoMemoize(conservative=conservative,
                     left=LMemo,
                     right=RMemo if full else None,
                     d=d))
Exemple #2
0
 def auto_memoize(self, conservative=False, full=False):
     '''
     LEPL can add memoization so that (1) complex matching is more 
     efficient and (2) left recursive grammars do not loop indefinitely.  
     However, in many common cases memoization decreases performance.
     This matcher therefore, by default, only adds memoization if it
     appears necessary for stability (case 2 above, when left recursion
     is possible).
     
     The ``conservative`` parameter controls how left-recursive loops are
     detected.  If False (default) then matchers are assumed to "consume"
     input.  This may be incorrect, in which case some left-recursive loops
     may be missed.  If True then all loops are considered left-recursive.
     This is safer, but results in much more expensive (slower) memoisation. 
     
     The `full` parameter can be used (set to True) to add memoization
     for case (1) above, in addition to case (2).  In other words, when
     True, all nodes are memozied; when False (the default) only 
     left-recursive nodes are memoized.
     
     This is part of the default configuration.
     
     See also `no_memoize()`.
     '''
     from lepl.core.rewriters import AutoMemoize
     from lepl.matchers.memo import LMemo, RMemo
     self.no_memoize()
     return self.add_rewriter(
         AutoMemoize(conservative, LMemo, RMemo if full else None))
Exemple #3
0
    def test_describe(self):
        '''
        Use a description of the graph to check against changes.
        '''
        #basicConfig(level=DEBUG)

        name = Word(Letter()) > 'name'

        expression = Delayed()
        variable = Delayed()

        function = (expression / '()') > 'function'
        expression += (variable | function) > 'expression'
        variable += (name | expression / '.' / name)

        dotted_name = function & Eos()
        base = dotted_name.tree()
        #        print(base)
        desc0 = NodeStats(dotted_name)
        print(desc0)
        assert desc0.total == 18, desc0
        self.assert_count(desc0, And, 5)
        self.assert_count(desc0, Or, 2)
        self.assert_count(desc0, Delayed, 2)

        clone0 = clone_matcher(dotted_name)
        #        print(clone0.tree())
        diff = Differ()
        diff_text = '\n'.join(
            diff.compare(base.split('\n'),
                         clone0.tree().split('\n')))
        #print(diff_text)
        descx = NodeStats(clone0)
        print(descx)
        assert descx == desc0

        clone1 = Flatten()(dotted_name)
        print(clone1.tree())
        desc1 = NodeStats(clone1)
        print(desc1)
        # flattened And (Or no longer flattened as Delayed intervenes)
        assert desc1.total == 17, desc1
        self.assert_count(desc1, And, 4)
        self.assert_count(desc1, Or, 2)
        self.assert_count(desc1, Delayed, 2)
        self.assert_count(desc1, Transform, 7)
        self.assert_count(desc1, TransformationWrapper, 7)

        clone2 = ComposeTransforms()(clone1)
        desc2 = NodeStats(clone2)
        #print(desc2)
        # compressed a transform
        assert desc2.total == 17, desc2
        self.assert_count(desc2, And, 4)
        self.assert_count(desc2, Or, 2)
        self.assert_count(desc2, Delayed, 2)
        self.assert_count(desc2, Transform, 6)
        self.assert_count(desc2, TransformationWrapper, 6)

        clone3 = RightMemoize()(clone2)
        desc3 = NodeStats(clone3)
        #print(desc3)
        assert desc3.total == 17, desc3
        self.assert_count(desc3, _RMemo, 17)
        self.assert_count(desc3, Delayed, 2)

        clone4 = LeftMemoize()(clone2)
        desc4 = NodeStats(clone4)
        #print(desc4)
        assert desc4.total == 17, desc4
        self.assert_count(desc4, _LMemo, 20)
        # left memo duplicates delayed
        self.assert_count(desc4, Delayed, 3)

        clone5 = AutoMemoize(left=LMemo, right=RMemo)(clone2)
        desc5 = NodeStats(clone5)
        #print(desc5)
        assert desc5.total == 17, desc5
        self.assert_count(desc5, _RMemo, 5)
        self.assert_count(desc5, _LMemo, 15)
        # left memo duplicates delayed
        self.assert_count(desc5, Delayed, 3)

        try:
            clone3.config.clear()
            clone3.parse_string('1join()')
            assert False, 'Expected error'
        except MemoException as error:
            assert 'Left recursion was detected' in str(error), str(error)

        clone4.config.clear()
        clone4.parse_string('1join()')
        clone5.config.clear()
        clone5.parse_string('1join()')
Exemple #4
0
    def test_describe(self):
        '''
        Use a description of the graph to check against changes.
        '''
        #basicConfig(level=DEBUG)

        name = Word(Letter()) > 'name'

        expression = Delayed()
        variable = Delayed()

        function = (expression / '()') > 'function'
        expression += (variable | function) > 'expression'
        variable += (name | expression / '.' / name)

        dotted_name = function & Eos()
        desc0 = NodeStats(dotted_name)
        #print(desc0)
        assert desc0.total == 18, desc0
        self.assert_count(desc0, And, 5)
        self.assert_count(desc0, Or, 2)
        self.assert_count(desc0, Delayed, 2)
        
        clone1 = Flatten()(dotted_name)
        desc1 = NodeStats(clone1)
        #print(desc1)
        # flattened two matchers - one each of And and Or
        assert desc1.total == 16, desc1
        self.assert_count(desc1, And, 4)
        self.assert_count(desc1, Or, 1)
        self.assert_count(desc1, Delayed, 2)
        self.assert_count(desc1, Transform, 7)
        self.assert_count(desc1, TransformationWrapper, 7)
        
        clone2 = ComposeTransforms()(clone1)
        desc2 = NodeStats(clone2)
        #print(desc2)
        # compressed two transforms
        assert desc2.total == 16, desc2
        self.assert_count(desc2, And, 4)
        self.assert_count(desc2, Or, 1)
        self.assert_count(desc2, Delayed, 2)
        self.assert_count(desc2, Transform, 2)
        self.assert_count(desc2, TransformationWrapper, 2)
        
        clone3 = Memoize(RMemo)(clone2)
        desc3 = NodeStats(clone3) 
        #print(desc3)
        assert desc3.total == 16, desc3
        self.assert_count(desc3, _RMemo, 18)
        self.assert_count(desc3, Delayed, 2)

        clone4 = Memoize(LMemo)(clone2)
        desc4 = NodeStats(clone4) 
        #print(desc4)
        assert desc4.total == 16, desc4
        self.assert_count(desc4, _LMemo, 18)
        self.assert_count(desc4, Delayed, 2)
        
        clone5 = AutoMemoize(left=LMemo, right=RMemo)(clone2)
        desc5 = NodeStats(clone5) 
        #print(desc5)
        assert desc5.total == 16, desc5
        self.assert_count(desc5, _RMemo, 13)
        self.assert_count(desc5, _LMemo, 5)
        self.assert_count(desc5, Delayed, 2)
        
        try:
            clone3.config.clear()
            clone3.parse_string('1join()')
            assert False, 'Expected error'
        except MemoException as error:
            assert 'Left recursion with RMemo?' in str(error), str(error)
        
        clone4.config.clear()
        clone4.parse_string('1join()')
        clone5.config.clear()
        clone5.parse_string('1join()')
Exemple #5
0
    def test_describe(self):
        '''
        Use a description of the graph to check against changes.
        '''
        #basicConfig(level=DEBUG)

        name = Word(Letter()) > 'name'

        expression = Delayed()
        variable = Delayed()

        function = (expression / '()') > 'function'
        expression += (variable | function) > 'expression'
        variable += (name | expression / '.' / name)

        dotted_name = function & Eos()
        base = dotted_name.tree()
#        print(base)
        desc0 = NodeStats(dotted_name)
        print(desc0)
        assert desc0.total == 18, desc0
        self.assert_count(desc0, And, 5)
        self.assert_count(desc0, Or, 2)
        self.assert_count(desc0, Delayed, 2)
        
        clone0 = clone_matcher(dotted_name)
#        print(clone0.tree())
        diff = Differ()
        diff_text = '\n'.join(diff.compare(base.split('\n'), clone0.tree().split('\n')))
        #print(diff_text)
        descx = NodeStats(clone0)
        print(descx)
        assert descx == desc0
        
        clone1 = Flatten()(dotted_name)
        print(clone1.tree())
        desc1 = NodeStats(clone1)
        print(desc1)
        # flattened And (Or no longer flattened as Delayed intervenes)
        assert desc1.total == 17, desc1
        self.assert_count(desc1, And, 4)
        self.assert_count(desc1, Or, 2)
        self.assert_count(desc1, Delayed, 2)
        self.assert_count(desc1, Transform, 7)
        self.assert_count(desc1, TransformationWrapper, 7)
        
        clone2 = ComposeTransforms()(clone1)
        desc2 = NodeStats(clone2)
        #print(desc2)
        # compressed a transform
        assert desc2.total == 17, desc2
        self.assert_count(desc2, And, 4)
        self.assert_count(desc2, Or, 2)
        self.assert_count(desc2, Delayed, 2)
        self.assert_count(desc2, Transform, 6)
        self.assert_count(desc2, TransformationWrapper, 6)
        
        clone3 = RightMemoize()(clone2)
        desc3 = NodeStats(clone3) 
        #print(desc3)
        assert desc3.total == 17, desc3
        self.assert_count(desc3, _RMemo, 17)
        self.assert_count(desc3, Delayed, 2)

        clone4 = LeftMemoize()(clone2)
        desc4 = NodeStats(clone4) 
        #print(desc4)
        assert desc4.total == 17, desc4
        self.assert_count(desc4, _LMemo, 20)
        # left memo duplicates delayed
        self.assert_count(desc4, Delayed, 3)
        
        clone5 = AutoMemoize(left=LMemo, right=RMemo)(clone2)
        desc5 = NodeStats(clone5) 
        #print(desc5)
        assert desc5.total == 17, desc5
        self.assert_count(desc5, _RMemo, 5)
        self.assert_count(desc5, _LMemo, 15)
        # left memo duplicates delayed
        self.assert_count(desc5, Delayed, 3)
        
        try:
            clone3.config.clear()
            clone3.parse_string('1join()')
            assert False, 'Expected error'
        except MemoException as error:
            assert 'Left recursion was detected' in str(error), str(error)
        
        clone4.config.clear()
        clone4.parse_string('1join()')
        clone5.config.clear()
        clone5.parse_string('1join()')
Exemple #6
0
    def test_describe(self):
        '''
        Use a description of the graph to check against changes.
        '''
        #basicConfig(level=DEBUG)

        name = Word(Letter()) > 'name'

        expression = Delayed()
        variable = Delayed()

        function = (expression / '()') > 'function'
        expression += (variable | function) > 'expression'
        variable += (name | expression / '.' / name)

        dotted_name = function & Eos()
        desc0 = NodeStats(dotted_name)
        #print(desc0)
        assert desc0.total == 18, desc0
        self.assert_count(desc0, And, 5)
        self.assert_count(desc0, Or, 2)
        self.assert_count(desc0, Delayed, 2)

        clone1 = Flatten()(dotted_name)
        desc1 = NodeStats(clone1)
        #print(desc1)
        # flattened two matchers - one each of And and Or
        assert desc1.total == 16, desc1
        self.assert_count(desc1, And, 4)
        self.assert_count(desc1, Or, 1)
        self.assert_count(desc1, Delayed, 2)
        self.assert_count(desc1, Transform, 7)
        self.assert_count(desc1, TransformationWrapper, 7)

        clone2 = ComposeTransforms()(clone1)
        desc2 = NodeStats(clone2)
        #print(desc2)
        # compressed two transforms
        assert desc2.total == 16, desc2
        self.assert_count(desc2, And, 4)
        self.assert_count(desc2, Or, 1)
        self.assert_count(desc2, Delayed, 2)
        self.assert_count(desc2, Transform, 5)
        self.assert_count(desc2, TransformationWrapper, 5)

        clone3 = Memoize(RMemo)(clone2)
        desc3 = NodeStats(clone3)
        #print(desc3)
        assert desc3.total == 16, desc3
        self.assert_count(desc3, _RMemo, 14)
        self.assert_count(desc3, Delayed, 2)

        clone4 = Memoize(LMemo)(clone2)
        desc4 = NodeStats(clone4)
        #print(desc4)
        assert desc4.total == 16, desc4
        self.assert_count(desc4, _LMemo, 14)
        self.assert_count(desc4, Delayed, 2)

        clone5 = AutoMemoize(left=LMemo, right=RMemo)(clone2)
        desc5 = NodeStats(clone5)
        #print(desc5)
        assert desc5.total == 16, desc5
        self.assert_count(desc5, _RMemo, 9)
        self.assert_count(desc5, _LMemo, 5)
        self.assert_count(desc5, Delayed, 2)

        try:
            clone3.config.clear()
            clone3.parse_string('1join()')
            assert False, 'Expected error'
        except MemoException as error:
            assert 'Left recursion with RMemo?' in str(error), str(error)

        clone4.config.clear()
        clone4.parse_string('1join()')
        clone5.config.clear()
        clone5.parse_string('1join()')