-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmemo.py
115 lines (93 loc) · 3.4 KB
/
memo.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
def memoize(func):
"""Memoize a parsing method.
The functon must be a method on a class deriving from Parser.
The method must have either no arguments or a single argument that
is an int or str (the latter being the case for expect()).
It must return either None or an object that is not modified (at
least not while we're parsing).
We memoize positive and negative outcomes per input position.
The function is expected to move the input position iff it returns
a not-None value.
The memo is structured as a dict of dict, the outer dict indexed
by input position, the inner by function and arguments.
"""
def memoize_wrapper(self, *args):
vis = self.tokenizer.vis
pos = self.mark()
if vis is not None:
vis.show_call(pos, func.__name__, args)
memo = self.memos.get(pos)
if memo is None:
memo = self.memos[pos] = {}
key = (func, args)
if key in memo:
res, endpos = memo[key]
self.reset(endpos)
else:
res = func(self, *args)
endpos = self.mark()
if res is None:
assert endpos == pos
else:
assert endpos > pos
memo[key] = res, endpos
if vis is not None:
vis.show_return(pos, res, endpos)
return res
return memoize_wrapper
def no_memoize(func):
"""Like @memoize but doesn't use cache."""
def memoize_wrapper(self, *args):
vis = self.tokenizer.vis
pos = self.mark()
if vis is not None:
vis.show_call(pos, func.__name__, args)
res = func(self, *args)
endpos = self.mark()
if res is None:
assert endpos == pos
else:
assert endpos > pos
if vis is not None:
vis.show_return(pos, res, endpos)
return res
return memoize_wrapper
def memoize_left_rec(func):
"""Memoize a left-recursive parsing method.
This is similar to @memoize but loops until no longer parse is obtained.
Inspired by https://github.com/PhilippeSigaud/Pegged/wiki/Left-Recursion
"""
def memoize_left_rec_wrapper(self, *args):
vis = self.tokenizer.vis
pos = self.mark()
if vis is not None:
vis.show_call(pos, "*" + func.__name__, args)
memo = self.memos.get(pos)
if memo is None:
memo = self.memos[pos] = {}
key = (func, args)
if key in memo:
res, endpos = memo[key]
self.reset(endpos)
else:
# This is where we deviate from @memoize.
# Prime the cache with a failure.
memo[key] = lastres, lastpos = None, pos
if vis is not None:
vis.stuff_cache(pos, "*" + func.__name__, args, None)
# Loop until no longer parse is obtained.
while True:
self.reset(pos)
res = func(self, *args)
endpos = self.mark()
if endpos <= lastpos:
break
memo[key] = lastres, lastpos = res, endpos
if vis is not None:
vis.stuff_cache(pos, "*" + func.__name__, args, res)
res = lastres
self.reset(lastpos)
if vis is not None:
vis.show_return(pos, res, endpos)
return res
return memoize_left_rec_wrapper