-
Notifications
You must be signed in to change notification settings - Fork 51
/
Copy pathformula_interpreter.py
executable file
·135 lines (107 loc) · 3.78 KB
/
formula_interpreter.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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
# Copyright (c) 2010 Resolver Systems Ltd, PythonAnywhere LLP
# See LICENSE.md
#
import re
from .parser import FormulaError
from .parser.tokens import t_LITTLEARROW
from .parser.parse_node import ParseNode
from .parser.parse_node_constructors import (
Atom, ArgList, Name, Number, SubscriptList, Subscript, Trailer,
)
LITTLE_ARROW_RE = re.compile(t_LITTLEARROW)
def transform_arrow(child):
if isinstance(child, basestring) and LITTLE_ARROW_RE.match(child):
return ':'
else:
return child
def rewrite_cell_reference_as_tuple(cell_reference_node):
if cell_reference_node.type == ParseNode.FL_INVALID_REFERENCE:
raise FormulaError("#Invalid! cell reference in formula")
if cell_reference_node.type == ParseNode.FL_DELETED_REFERENCE:
raise FormulaError("#Deleted! cell reference in formula")
column, row = cell_reference_node.coords
return Atom([
'(',
Number(str(column)),
',',
Number(str(row)),
')'
])
def rewrite_cell_reference(cell_reference_node):
return Atom([
Name(["worksheet"]),
Trailer([
"[",
SubscriptList([
Subscript([ rewrite_cell_reference_as_tuple(cell_reference_node) ])
]),
"]"
]),
Trailer([
".value",
]),
Name([" "])
])
def rewrite_cell_range(node):
return Atom([
Name(["CellRange"]),
Trailer([
"(",
ArgList([
Name(["worksheet"]),
",",
rewrite_cell_reference_as_tuple(node.first_cell_reference),
",",
rewrite_cell_reference_as_tuple(node.second_cell_reference),
]),
")",
]),
Name([" "])
])
CELL_REFERENCES = (
ParseNode.FL_CELL_REFERENCE, ParseNode.FL_INVALID_REFERENCE,
ParseNode.FL_DELETED_REFERENCE
)
CONTAIN_COLONS = (
ParseNode.LAMBDEF, ParseNode.DICT_MAKER, ParseNode.SUBSCRIPT,
ParseNode.SLICE_OP
)
def rewrite(parse_node):
if isinstance(parse_node, ParseNode):
if parse_node.type == ParseNode.FL_CELL_RANGE:
return rewrite_cell_range(parse_node)
elif parse_node.type in CELL_REFERENCES:
return rewrite_cell_reference(parse_node)
elif parse_node.type in CONTAIN_COLONS:
parse_node.children = map(
rewrite,
[transform_arrow(child) for child in parse_node.children]
)
else:
parse_node.children = map(rewrite, parse_node.children)
return parse_node
def get_python_formula_from_parse_tree(parse_node):
return rewrite(parse_node).flatten()[1:]
def get_dependencies_from_parse_tree(parse_node):
if not isinstance(parse_node, ParseNode):
return []
if parse_node.type == ParseNode.FL_CELL_REFERENCE:
return [parse_node.coords]
elif parse_node.type == ParseNode.FL_CELL_RANGE:
if parse_node.first_cell_reference.type in (ParseNode.FL_INVALID_REFERENCE, ParseNode.FL_DELETED_REFERENCE):
return []
if parse_node.second_cell_reference.type in (ParseNode.FL_INVALID_REFERENCE, ParseNode.FL_DELETED_REFERENCE):
return []
topleft = parse_node.first_cell_reference.coords
bottomright = parse_node.second_cell_reference.coords
left, right = sorted([topleft[0], bottomright[0]])
top, bottom = sorted([topleft[1], bottomright[1]])
return [
(c,r)
for c in xrange(left, right + 1)
for r in xrange(top, bottom + 1)
]
elif parse_node.children:
return sum((get_dependencies_from_parse_tree(child) for child in parse_node.children), [])
else:
return []