Module refinery.lib.thirdparty.xlm.interpreter
Expand source code Browse git
# Copyright 2020 Amirreza Niakanlahiji
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import annotations
import copy
import datetime
import math
import operator
import random
import re
import time
from .model import Boundsheet, Cell, EvalResult, EvalStatus, XlApplicationInternational
from .parser import ParseError, Token, Tree, XLMParser
def _strtobool(val):
val = str(val).strip().lower()
if val in ('y', 'yes', 't', 'true', 'on', '1'):
return 1
elif val in ('n', 'no', 'f', 'false', 'off', '0'):
return 0
raise ValueError(f'invalid truth value {val!r}')
_ROMAN_VALUES = [
('M', 1000), ('CM', 900), ('D', 500), ('CD', 400),
('C', 100), ('XC', 90), ('L', 50), ('XL', 40),
('X', 10), ('IX', 9), ('V', 5), ('IV', 4), ('I', 1),
]
def _from_roman(s):
s = s.upper()
result = 0
idx = 0
for numeral, value in _ROMAN_VALUES:
while s[idx:idx + len(numeral)] == numeral:
result += value
idx += len(numeral)
if idx != len(s):
raise ValueError(f'invalid Roman numeral: {s!r}')
return result
_WORKSPACE_DEFAULTS = {
1: 'Windows (64-bit) NT :.00', 2: '16', 3: '0', 4: 'FALSE',
5: 'TRUE', 6: 'TRUE', 7: 'TRUE', 8: 'TRUE', 9: '/',
10: '0', 11: '-5', 12: '-6', 13: '1016.25', 14: '480', 15: '3',
18: 'TRUE', 19: 'TRUE', 21: 'TRUE', 22: '0',
23: r'C:\Users\user\AppData\Roaming\Microsoft\Excel\XLSTART',
25: 'FALSE', 26: 'Windows User', 28: '1', 29: 'FALSE',
31: 'FALSE', 32: r'C:\Program Files\Microsoft Office\Office16',
33: 'Worksheet', 35: 'FALSE', 36: 'TRUE', 37: '1', 38: '1',
40: 'TRUE', 42: 'TRUE', 43: 'TRUE', 45: 'FALSE', 46: 'TRUE',
48: r'C:\Program Files\Microsoft Office\Office16\LIBRARY',
50: 'FALSE', 51: 'FALSE', 52: 'FALSE', 54: 'TRUE', 55: 'TRUE',
56: 'Calibri', 57: '11', 58: 'TRUE', 59: 'FALSE', 60: 'FALSE',
61: '4', 63: '1', 64: 'TRUE', 65: 'TRUE', 66: '1',
67: r'C:\Users\user\Documents', 68: 'TRUE', 69: 'TRUE',
70: 'FALSE', 71: 'FALSE', 72: 'TRUE',
}
_WINDOW_DEFAULTS = {
1: '[Book1]Sheet1', 2: 1, 3: 0, 4: 0, 5: 800, 6: 600,
7: 'FALSE', 8: 'TRUE', 9: 'TRUE', 10: 'TRUE', 11: 'TRUE',
12: 0, 13: 1, 14: 'FALSE', 15: 'FALSE', 16: 'FALSE', 17: 1,
18: 'FALSE', 19: 'FALSE', 20: 'TRUE', 21: 'FALSE', 22: 'FALSE',
23: 3, 24: 'FALSE', 25: 100, 26: 'TRUE', 27: 'TRUE', 28: 0,
29: 'TRUE', 30: '[Book1]Sheet1', 31: 'window.xls',
}
_CELL_DEFAULTS = {
1: '$A$1', 2: '1', 3: '1', 4: '1', 5: '0', 7: 'General', 8: '1',
9: '0', 10: '0', 11: '0', 12: '0', 13: '0', 14: 'TRUE',
15: 'FALSE', 16: '25.71', 17: '78.75', 18: 'Calibri', 19: '11',
20: 'FALSE', 21: 'FALSE', 22: 'FALSE', 23: '11', 24: '1',
25: 'FALSE', 26: 'FALSE', 27: '0', 28: '1', 29: '1',
30: 'FALSE', 31: 'FALSE', 32: '[get.cell.xls]Macro1', 33: 'FALSE',
34: '0', 35: '0', 36: '0', 37: '0', 38: '0', 39: '0',
40: 'Normal', 42: '20.5', 43: '-122.75', 44: '298', 45: '-44',
46: 'FALSE', 47: 'FALSE', 48: 'FALSE', 49: 'FALSE', 50: '3',
51: '0', 57: 'FALSE', 58: 'Regular', 59: '1', 60: 'FALSE',
62: '[get.cell.xls]Macro1', 63: '0', 64: '0', 65: 'FALSE',
66: 'get.cell.xls',
}
class XLMInterpreter:
def __init__(self, xlm_wrapper, output_level=0):
self.xlm_wrapper = xlm_wrapper
self._formula_cache = {}
self.cell_addr_regex_str = r"((?P<sheetname>[^\s]+?|'.+?')!)?\$?(?P<column>[a-zA-Z]+)\$?(?P<row>\d+)"
self.cell_addr_regex = re.compile(self.cell_addr_regex_str)
self.xlm_parser = self.get_parser()
self.defined_names = self.xlm_wrapper.get_defined_names()
self.auto_labels = None
self._branch_stack = []
self._while_stack = []
self._for_iterators = {}
self._function_call_stack = []
self._memory = []
self._files = {}
self._registered_functions = {}
self._expr_rule_names = ['expression', 'concat_expression', 'additive_expression', 'multiplicative_expression']
self._operators = {'+': operator.add, '-': operator.sub, '*': operator.mul, '/': operator.truediv,
'>': operator.gt, '<': operator.lt, '<>': operator.ne, '=': operator.eq, '>=': operator.ge,
'<=': operator.le}
self._indent_level = 0
self._indent_current_line = False
self.day_of_month = None
self.invoke_interpreter = False
self.first_unknown_cell = None
self.cell_with_unsuccessfull_set = set()
self.selected_range = None
self.active_cell = None
self.ignore_processing = False
self.next_count = 0
self.char_error_count = 0
self.output_level = output_level
self._remove_current_formula_from_cache = False
self._start_timestamp = time.time()
self._iserror_count = 0
self._iserror_loc = None
self._iserror_val = False
self._now_count = 0
self._now_step = 2
self._handlers = {
# methods
'END.IF': self.end_if_handler,
'FORMULA.FILL': self.formula_fill_handler,
'FORMULA.ARRAY': self.formula_array_handler,
'GET.CELL': self.get_cell_handler,
'GET.DOCUMENT': self.get_document_handler,
'GET.WINDOW': self.get_window_handler,
'GET.WORKSPACE': self.get_workspace_handler,
'ON.TIME': self.on_time_handler,
'SET.VALUE': self.set_value_handler,
'SET.NAME': self.set_name_handler,
'ACTIVE.CELL': self.active_cell_handler,
'APP.MAXIMIZE': self.app_maximize_handler,
# functions
'ABS': self.abs_handler,
'ABSREF': self.absref_handler,
'ADDRESS': self.address_handler,
'AND': self.and_handler,
'CALL': self.call_handler,
'CHAR': self.char_handler,
'CLOSE': self.halt_handler,
'CODE': self.code_handler,
'CONCATENATE': self.concatenate_handler,
'COUNTA': self.counta_handler,
'COUNT': self.count_handler,
'DAY': self.day_handler,
'DEFINE.NAME': self.define_name_handler,
'DIRECTORY': self.directory_handler,
'ERROR': self.error_handler,
'FILES': self.files_handler,
'FORMULA': self.formula_handler,
'FOPEN': self.fopen_handler,
'FOR.CELL': self.forcell_handler,
'FSIZE': self.fsize_handler,
'FWRITE': self.fwrite_handler,
'FWRITELN': self.fwriteln_handler,
'GOTO': self.goto_handler,
'HALT': self.halt_handler,
'INDEX': self.index_handler,
'HLOOKUP': self.hlookup_handler,
'IF': self.if_handler,
'INDIRECT': self.indirect_handler,
'INT': self.int_handler,
'ISERROR': self.iserror_handler,
'ISNUMBER': self.is_number_handler,
'LEN': self.len_handler,
'MAX': self.max_handler,
'MIN': self.min_handler,
'MOD': self.mod_handler,
'MID': self.mid_handler,
'SQRT': self.sqrt_handler,
'NEXT': self.next_handler,
'NOT': self.not_handler,
'NOW': self.now_handler,
'OR': self.or_handler,
'OFFSET': self.offset_handler,
'PRODUCT': self.product_handler,
'QUOTIENT': self.quotient_handler,
'RANDBETWEEN': self.randbetween_handler,
'REGISTER': self.register_handler,
'REGISTER.ID': self.registerid_handler,
'RETURN': self.return_handler,
'ROUND': self.round_handler,
'ROUNDUP': self.roundup_handler,
'RUN': self.run_handler,
'ROWS': self.rows_handler,
'SEARCH': self.search_handler,
'SELECT': self.select_handler,
'SUM': self.sum_handler,
'T': self.t_handler,
'TEXT': self.text_handler,
'TRUNC': self.trunc_handler,
'VALUE': self.value_handler,
'WHILE': self.while_handler,
# Windows API
'Kernel32.VirtualAlloc': self.VirtualAlloc_handler,
'Kernel32.WriteProcessMemory': self.WriteProcessMemory_handler,
'Kernel32.RtlCopyMemory': self.RtlCopyMemory_handler,
# Future fuctions
'_xlfn.ARABIC': self.arabic_hander,
}
MAX_ISERROR_LOOPCOUNT = 10
jump_functions = ('GOTO', 'RUN')
important_functions = ('CALL', 'FOPEN', 'FWRITE', 'FREAD', 'REGISTER', 'IF', 'WHILE', 'HALT', 'CLOSE', "NEXT")
important_methods = ('SET.VALUE', 'FILE.DELETE', 'WORKBOOK.HIDE')
unicode_to_latin1_map = {
8364: 128,
129: 129,
8218: 130,
402: 131,
8222: 132,
8230: 133,
8224: 134,
8225: 135,
710: 136,
8240: 137,
352: 138,
8249: 139,
338: 140,
141: 141,
381: 142,
143: 143,
144: 144,
8216: 145,
8217: 146,
8220: 147,
8221: 148,
8226: 149,
8211: 150,
8212: 151,
732: 152,
8482: 153,
353: 154,
8250: 155,
339: 156,
157: 157,
382: 158,
376: 159
}
def __copy__(self):
result = XLMInterpreter(self.xlm_wrapper)
result.auto_labels = self.auto_labels
result._formula_cache = self._formula_cache
return result
@staticmethod
def is_float(text):
try:
float(text)
return True
except (ValueError, TypeError):
return False
@staticmethod
def is_int(text):
try:
int(text)
return True
except (ValueError, TypeError):
return False
@staticmethod
def is_bool(text):
try:
_strtobool(text)
return True
except (ValueError, TypeError, AttributeError):
return False
def convert_float(self, text):
result = None
text = text.lower()
if text == 'false':
result = 0
elif text == 'true':
result = 1
else:
result = float(text)
return result
def get_parser(self):
left_bracket = self.xlm_wrapper.get_xl_international_char(
XlApplicationInternational.xlLeftBracket)
list_separator = self.xlm_wrapper.get_xl_international_char(
XlApplicationInternational.xlListSeparator)
right_bracket = self.xlm_wrapper.get_xl_international_char(
XlApplicationInternational.xlRightBracket)
return XLMParser(left_bracket, list_separator, right_bracket)
def get_formula_cell(self, macrosheet, col, row):
result_cell = None
not_found = False
row = int(row)
current_row = row
current_addr = col + str(current_row)
while current_addr not in macrosheet.cells or \
macrosheet.cells[current_addr].formula is None:
if (current_row - row) < 10000:
current_row += 1
else:
not_found = True
break
current_addr = col + str(current_row)
if not_found is False:
result_cell = macrosheet.cells[current_addr]
return result_cell
def get_range_parts(self, parse_tree):
if isinstance(parse_tree, Tree) and parse_tree.data == 'range':
return parse_tree.children[0], parse_tree.children[-1]
else:
return None, None
def get_cell_addr(self, current_cell, cell_parse_tree):
res_sheet = res_col = res_row = None
if type(cell_parse_tree) is Token:
names = self.xlm_wrapper.get_defined_names()
label = cell_parse_tree.value.lower()
if label in names:
name_val = names[label]
if isinstance(name_val, Tree):
# example: 6a8045bc617df5f2b8f9325ed291ef05ac027144f1fda84e78d5084d26847902
res_sheet, res_col, res_row = self.get_cell_addr(current_cell, name_val)
else:
res_sheet, res_col, res_row = Cell.parse_cell_addr(name_val)
elif label.strip('"') in names:
res_sheet, res_col, res_row = Cell.parse_cell_addr(names[label.strip('"')])
else:
if len(label) > 1 and label.startswith('"') and label.endswith('"'):
label = label.strip('"')
root_parse_tree = self.xlm_parser.parse('=' + label)
res_sheet, res_col, res_row = self.get_cell_addr(current_cell, root_parse_tree.children[0])
else:
if cell_parse_tree.data == 'defined_name':
label = '{}'.format(cell_parse_tree.children[2])
formula_str = self.xlm_wrapper.get_defined_name(label)
parsed_tree = self.xlm_parser.parse('=' + formula_str)
if isinstance(parsed_tree.children[0], Tree) and parsed_tree.children[0].data == 'range':
start_cell, end_cell = self.get_range_parts(parsed_tree.children[0])
cell = start_cell.children[0]
else:
cell = parsed_tree.children[0].children[0]
else:
cell = cell_parse_tree.children[0]
if cell.data == 'a1_notation_cell':
if len(cell.children) == 2:
cell_addr = "'{}'!{}".format(cell.children[0], cell.children[1])
else:
cell_addr = cell.children[0]
res_sheet, res_col, res_row = Cell.parse_cell_addr(cell_addr)
if res_sheet is None and res_col is not None:
res_sheet = current_cell.sheet.name
elif cell.data == 'r1c1_notation_cell':
current_col = Cell.convert_to_column_index(current_cell.column)
current_row = int(current_cell.row)
for current_child in cell.children:
if current_child.type == 'NAME':
res_sheet = current_child.value
elif self.is_float(current_child.value):
val = int(float(current_child.value))
if last_seen == 'r':
res_row = val
else:
res_col = val
elif current_child.value.startswith('['):
val = int(current_child.value[1:-1])
if last_seen == 'r':
res_row = current_row + val
else:
res_col = current_col + val
elif current_child.lower() == 'r':
last_seen = 'r'
res_row = current_row
elif current_child.lower() == 'c':
last_seen = 'c'
res_col = current_col
else:
raise Exception('Cell addresss, Syntax Error')
if res_sheet is None:
res_sheet = current_cell.sheet.name
res_row = str(res_row)
res_col = Cell.convert_to_column_name(res_col)
else:
raise Exception('Cell addresss, Syntax Error')
return res_sheet, res_col, res_row
def get_cell(self, sheet_name, col, row):
result = None
sheets = self.xlm_wrapper.get_macrosheets()
if sheet_name in sheets:
sheet = sheets[sheet_name]
addr = col + str(row)
if addr in sheet.cells:
result = sheet.cells[addr]
else:
sheets = self.xlm_wrapper.get_worksheets()
if sheet_name in sheets:
sheet = sheets[sheet_name]
addr = col + str(row)
if addr in sheet.cells:
result = sheet.cells[addr]
return result
def get_worksheet_cell(self, sheet_name, col, row):
result = None
sheets = self.xlm_wrapper.get_worksheets()
if sheet_name in sheets:
sheet = sheets[sheet_name]
addr = col + str(row)
if addr in sheet.cells:
result = sheet.cells[addr]
return result
def set_cell(self, sheet_name, col, row, text, set_value_only=False):
sheets = self.xlm_wrapper.get_macrosheets()
if sheet_name in sheets:
sheet = sheets[sheet_name]
addr = col + str(row)
if addr not in sheet.cells:
new_cell = Cell()
new_cell.column = col
new_cell.row = row
new_cell.sheet = sheet
sheet.cells[addr] = new_cell
cell = sheet.cells[addr]
text = EvalResult.unwrap_str_literal(text)
if not set_value_only:
if text.startswith('='):
cell.formula = text
else:
cell.formula = None
cell.value = text
@staticmethod
def convert_ptree_to_str(parse_tree_root):
if type(parse_tree_root) == Token:
return str(parse_tree_root)
else:
result = ''
for child in parse_tree_root.children:
result += XLMInterpreter.convert_ptree_to_str(child)
return result
def get_window(self, number):
return _WINDOW_DEFAULTS.get(number)
def get_workspace(self, number):
return _WORKSPACE_DEFAULTS.get(number)
def get_default_cell_info(self, number):
return _CELL_DEFAULTS.get(number)
def evaluate_formula(self, current_cell, name, arguments, interactive, destination_arg=1, set_value_only=False):
# hash: fa391403aa028fa7b42a9f3491908f6f25414c35bfd104f8cf186220fb3b4f83" --> =FORMULA()
if isinstance(arguments[0], list) and len(arguments[0]) == 0:
return EvalResult(None, EvalStatus.FullEvaluation, False, "{}()".format(name))
source, destination = (arguments[0], arguments[1]) if destination_arg == 1 else (arguments[1], arguments[0])
src_eval_result = self.evaluate_parse_tree(current_cell, source, interactive)
if isinstance(destination, Token):
# TODO: get_defined_name must return a list; currently it returns list or one item
destination = self.xlm_wrapper.get_defined_name(destination)
if isinstance(destination, list):
destination = [] if not destination else destination[0]
if(isinstance(destination, str)):
destination = self.xlm_parser.parse('=' + destination).children[0]
if isinstance(destination, Tree):
if destination.data == 'defined_name' or destination.data == 'name':
defined_name_formula = self.xlm_wrapper.get_defined_name(destination.children[2])
if isinstance(defined_name_formula, Tree):
destination = defined_name_formula
else:
destination = self.xlm_parser.parse('=' + defined_name_formula).children[0]
if destination.data == 'concat_expression' or destination.data == 'function_call':
res = self.evaluate_parse_tree(current_cell, destination, interactive)
if isinstance(res.value, tuple) and len(res.value) == 3:
destination_str = "'{}'!{}{}".format(res.value[0], res.value[1], res.value[2])
dst_start_sheet, dst_start_col, dst_start_row = res.value
else:
destination_str = res.text
dst_start_sheet, dst_start_col, dst_start_row = Cell.parse_cell_addr(destination_str)
dst_end_sheet, dst_end_col, dst_end_row = dst_start_sheet, dst_start_col, dst_start_row
else:
if destination.data == 'range':
dst_start_sheet, dst_start_col, dst_start_row = self.get_cell_addr(current_cell,
destination.children[0])
dst_end_sheet, dst_end_col, dst_end_row = self.get_cell_addr(current_cell, destination.children[2])
else:
dst_start_sheet, dst_start_col, dst_start_row = self.get_cell_addr(current_cell, destination)
dst_end_sheet, dst_end_col, dst_end_row = dst_start_sheet, dst_start_col, dst_start_row
destination_str = XLMInterpreter.convert_ptree_to_str(destination)
text = src_eval_result.get_text(unwrap=True)
if src_eval_result.status == EvalStatus.FullEvaluation:
for row in range(int(dst_start_row), int(dst_end_row) + 1):
for col in range(Cell.convert_to_column_index(dst_start_col),
Cell.convert_to_column_index(dst_end_col) + 1):
if (
dst_start_sheet,
Cell.convert_to_column_name(col) + str(row)) in self.cell_with_unsuccessfull_set:
self.cell_with_unsuccessfull_set.remove((dst_start_sheet,
Cell.convert_to_column_name(col) + str(row)))
self.set_cell(dst_start_sheet,
Cell.convert_to_column_name(col),
str(row),
str(src_eval_result.value),
set_value_only)
else:
for row in range(int(dst_start_row), int(dst_end_row) + 1):
for col in range(Cell.convert_to_column_index(dst_start_col),
Cell.convert_to_column_index(dst_end_col) + 1):
self.cell_with_unsuccessfull_set.add((dst_start_sheet,
Cell.convert_to_column_name(col) + str(row)))
if destination_arg == 1:
text = "{}({},{})".format(name,
src_eval_result.get_text(),
destination_str)
else:
text = "{}({},{})".format(name,
destination_str,
src_eval_result.get_text())
return_val = 0
return EvalResult(None, src_eval_result.status, return_val, text)
def evaluate_argument_list(self, current_cell, name, arguments):
args_str = ''
for argument in arguments:
if type(argument) is Token or type(argument) is Tree:
arg_eval_Result = self.evaluate_parse_tree(current_cell, argument, False)
args_str += arg_eval_Result.get_text() + ','
args_str = args_str.strip(',')
return_val = text = '={}({})'.format(name, args_str)
status = EvalStatus.PartialEvaluation
return EvalResult(None, status, return_val, text)
def evaluate_function(self, current_cell, parse_tree_root, interactive):
# function name can be a string literal (double quoted or unqouted), and Tree (defined name, cell, function_call)
function_name = parse_tree_root.children[0]
function_name_literal = EvalResult.unwrap_str_literal(function_name)
# OFFSET()()
if isinstance(function_name, Tree) and function_name.data == 'function_call':
func_eval_result = self.evaluate_parse_tree(current_cell, function_name, False)
if func_eval_result.status != EvalStatus.FullEvaluation:
return EvalResult(func_eval_result.next_cell, func_eval_result.status, 0,
XLMInterpreter.convert_ptree_to_str(parse_tree_root))
else:
func_eval_result.text = XLMInterpreter.convert_ptree_to_str(parse_tree_root)
return func_eval_result
# handle alias name for a function (REGISTER)
# c45ed3a0ce5df27ac29e0fab99dc4d462f61a0d0c025e9161ced3b2c913d57d8
if function_name_literal in self._registered_functions:
parse_tree_root.children[0] = parse_tree_root.children[0].update(
None, self._registered_functions[function_name_literal]['name'])
return self.evaluate_function(current_cell, parse_tree_root, interactive)
# cell_function_call
if isinstance(function_name, Tree) and function_name.data == 'cell':
self._function_call_stack.append(current_cell)
return self.goto_handler([function_name], current_cell, interactive, parse_tree_root)
# test()
if function_name_literal.lower() in self.defined_names:
try:
ref_parsed = self.xlm_parser.parse('=' + self.defined_names[function_name_literal.lower()])
if isinstance(ref_parsed.children[0], Tree) and ref_parsed.children[0].data == 'cell':
function_name = ref_parsed.children[0]
else:
raise Exception
except:
function_name = self.defined_names[function_name_literal.lower()]
# x!test()
if isinstance(function_name, Tree) and function_name.data == 'defined_name':
function_lable = function_name.children[-1].value
if function_lable.lower() in self.defined_names:
try:
ref_parsed = self.xlm_parser.parse('=' + self.defined_names[function_lable.lower()])
if isinstance(ref_parsed.children[0], Tree) and ref_parsed.children[0].data == 'cell':
function_name = ref_parsed.children[0]
else:
raise Exception
except:
function_name = self.defined_names[function_name_literal.lower()]
# cell_function_call
if isinstance(function_name, Tree) and function_name.data == 'cell':
self._function_call_stack.append(current_cell)
return self.goto_handler([function_name], current_cell, interactive, parse_tree_root)
if self.ignore_processing and function_name_literal != 'NEXT':
return EvalResult(None, EvalStatus.IGNORED, 0, '')
arguments = []
for i in parse_tree_root.children[2].children:
if type(i) is not Token:
if len(i.children) > 0:
arguments.append(i.children[0])
else:
arguments.append(i.children)
if function_name_literal in self._handlers:
eval_result = self._handlers[function_name_literal](arguments, current_cell, interactive, parse_tree_root)
else:
eval_result = self.evaluate_argument_list(current_cell, function_name_literal, arguments)
if function_name_literal in XLMInterpreter.jump_functions:
eval_result.output_level = 0
elif function_name_literal in XLMInterpreter.important_functions:
eval_result.output_level = 2
else:
eval_result.output_level = 1
return eval_result
# region Handlers
def and_handler(self, arguments, current_cell, interactive, parse_tree_root):
value = True
status = EvalStatus.FullEvaluation
for arg in arguments:
arg_eval_result = self.evaluate_parse_tree(current_cell, arg, interactive)
if arg_eval_result.status == EvalStatus.FullEvaluation:
if EvalResult.unwrap_str_literal(str(arg_eval_result.value)).lower() != "true":
value = False
break
else:
status = EvalStatus.PartialEvaluation
value = False
break
return EvalResult(None, status, value, str(value))
def or_handler(self, arguments, current_cell, interactive, parse_tree_root):
value = False
status = EvalStatus.FullEvaluation
for arg in arguments:
arg_eval_result = self.evaluate_parse_tree(current_cell, arg, interactive)
if arg_eval_result.status == EvalStatus.FullEvaluation:
if EvalResult.unwrap_str_literal(str(arg_eval_result.value)).lower() == "true":
value = True
break
else:
status = EvalStatus.PartialEvaluation
break
return EvalResult(None, status, value, str(value))
def hlookup_handler(self, arguments, current_cell, interactive, parse_tree_root):
status = EvalStatus.FullEvaluation
value = ""
arg_eval_result1 = self.evaluate_parse_tree(current_cell, arguments[0], interactive)
arg_eval_result2 = self.evaluate_parse_tree(current_cell, arguments[1], interactive)
arg_eval_result3 = self.evaluate_parse_tree(current_cell, arguments[2], interactive)
arg_eval_result4 = self.evaluate_parse_tree(current_cell, arguments[3], interactive)
regex = arg_eval_result1.text.strip('"')
if regex == '*':
regex = ".*"
if arg_eval_result4.value == "FALSE":
sheet_name, startcolumn, startrow, endcolumn, endrow = Cell.parse_range_addr(arg_eval_result2.text)
status = EvalStatus.FullEvaluation
start_col_index = Cell.convert_to_column_index(startcolumn)
end_col_index = Cell.convert_to_column_index(endcolumn)
start_row_index = int(startrow) + int(arg_eval_result3.value) - 1
end_row_index = int(endrow)
for row in range(start_row_index, end_row_index + 1):
for col in range(start_col_index, end_col_index + 1):
if (sheet_name != None):
cell = self.get_worksheet_cell(sheet_name,
Cell.convert_to_column_name(col),
str(row))
else:
cell = self.get_cell(current_cell.sheet.name,
Cell.convert_to_column_name(col),
str(row))
if cell and re.match(regex, cell.value):
return EvalResult(None, status, cell.value, str(cell.value))
else:
status = EvalStatus.PartialEvaluation
return EvalResult(None, status, value, str(value))
def not_handler(self, arguments, current_cell, interactive, parse_tree_root):
value = True
status = EvalStatus.FullEvaluation
arg_eval_result = self.evaluate_parse_tree(current_cell, arguments[0], interactive)
if arg_eval_result.status == EvalStatus.FullEvaluation:
if EvalResult.unwrap_str_literal(str(arg_eval_result.value)).lower() == "true":
value = False
else:
status = EvalStatus.PartialEvaluation
return EvalResult(None, status, value, str(value))
def code_handler(self, arguments, current_cell, interactive, parse_tree_root):
status = EvalStatus.FullEvaluation
value = 0
arg_eval_result = self.evaluate_parse_tree(current_cell, arguments[0], interactive)
if arg_eval_result.status == EvalStatus.FullEvaluation:
if arg_eval_result.text != '':
value = ord(arg_eval_result.text[0])
if value > 256 and value in XLMInterpreter.unicode_to_latin1_map:
value = XLMInterpreter.unicode_to_latin1_map[value]
else:
status = EvalStatus.PartialEvaluation
return EvalResult(None, status, value, str(value))
def sum_handler(self, arguments, current_cell, interactive, parse_tree_root):
status = EvalStatus.FullEvaluation
value = 0
it = 0
for arg in arguments:
arg_eval_result = self.evaluate_parse_tree(current_cell, arguments[it], interactive)
value = value + float(arg_eval_result.value)
status = arg_eval_result.status
it = it + 1
return EvalResult(None, status, value, str(value))
def randbetween_handler(self, arguments, current_cell, interactive, parse_tree_root):
arg_eval_result1 = self.evaluate_parse_tree(current_cell, arguments[0], interactive)
arg_eval_result2 = self.evaluate_parse_tree(current_cell, arguments[1], interactive)
value = 0
# Initial implementation for integer
if arg_eval_result1.status == EvalStatus.FullEvaluation and arg_eval_result2.status == EvalStatus.FullEvaluation:
status = EvalStatus.FullEvaluation
value = random.randint(int(float(arg_eval_result1.value)), int(float(arg_eval_result2.value)))
return EvalResult(None, status, value, str(value))
def text_handler(self, arguments, current_cell, interactive, parse_tree_root):
arg_eval_result1 = self.evaluate_parse_tree(current_cell, arguments[0], interactive)
arg_eval_result2 = self.evaluate_parse_tree(current_cell, arguments[1], interactive)
value = 0
status = EvalStatus.PartialEvaluation
# Initial implementation for integer
if arg_eval_result1.status == EvalStatus.FullEvaluation and int(arg_eval_result2.text.strip('\"')) == 0:
status = EvalStatus.FullEvaluation
value = int(arg_eval_result1.value)
return EvalResult(None, status, value, str(value))
def active_cell_handler(self, arguments, current_cell, interactive, parse_tree_root):
status = EvalStatus.PartialEvaluation
if self.active_cell:
if self.active_cell.formula:
parse_tree = self.xlm_parser.parse(self.active_cell.formula)
eval_res = self.evaluate_parse_tree(current_cell, parse_tree, interactive)
val = eval_res.value
status = eval_res.status
else:
val = self.active_cell.value
status = EvalStatus.FullEvaluation
return_val = val
text = str(return_val)
else:
text = XLMInterpreter.convert_ptree_to_str(parse_tree_root)
return_val = text
return EvalResult(None, status, return_val, text)
def get_cell_handler(self, arguments, current_cell, interactive, parse_tree_root):
if len(arguments) == 2:
arg1_eval_result = self.evaluate_parse_tree(current_cell, arguments[0], interactive)
dst_sheet, dst_col, dst_row = self.get_cell_addr(current_cell, arguments[1])
type_id = arg1_eval_result.value
if self.is_float(type_id):
type_id = int(float(type_id))
if dst_sheet is None:
dst_sheet = current_cell.sheet.name
status = EvalStatus.PartialEvaluation
if arg1_eval_result.status == EvalStatus.FullEvaluation:
data, not_exist, not_implemented = self.xlm_wrapper.get_cell_info(dst_sheet, dst_col, dst_row, type_id)
if not_exist and 1 == 2:
return_val = self.get_default_cell_info(type_id)
text = str(return_val)
status = EvalStatus.FullEvaluation
elif not_implemented:
text = XLMInterpreter.convert_ptree_to_str(parse_tree_root)
return_val = ''
else:
text = str(data) if data is not None else None
return_val = data
status = EvalStatus.FullEvaluation
else:
text = XLMInterpreter.convert_ptree_to_str(parse_tree_root)
return_val = ''
status = EvalStatus.PartialEvaluation
return EvalResult(None, status, return_val, text)
def set_name_handler(self, arguments, current_cell, interactive, parse_tree_root):
label = EvalResult.unwrap_str_literal(XLMInterpreter.convert_ptree_to_str(arguments[0])).lower()
if isinstance(arguments[1], Tree) and arguments[1].data == 'cell':
arg2_text = XLMInterpreter.convert_ptree_to_str(arguments[1])
names = self.xlm_wrapper.get_defined_names()
names[label] = arguments[1]
text = 'SET.NAME({},{})'.format(label, arg2_text)
return_val = 0
status = EvalStatus.FullEvaluation
else:
arg2_eval_result = self.evaluate_parse_tree(current_cell, arguments[1], interactive)
if arg2_eval_result.status is EvalStatus.FullEvaluation:
arg2_text = arg2_eval_result.get_text(unwrap=True)
names = self.xlm_wrapper.get_defined_names()
if isinstance(arg2_eval_result.value, Cell):
names[label] = arg2_eval_result.value
else:
names[label] = arg2_text
text = 'SET.NAME({},{})'.format(label, arg2_text)
return_val = 0
status = EvalStatus.FullEvaluation
else:
return_val = text = XLMInterpreter.convert_ptree_to_str(parse_tree_root)
status = arg2_eval_result.status
return EvalResult(None, status, return_val, text)
def end_if_handler(self, arguments, current_cell, interactive, parse_tree_root):
self._indent_level -= 1
self._indent_current_line = True
status = EvalStatus.FullEvaluation
return EvalResult(None, status, 'END.IF', 'END.IF')
def get_workspace_handler(self, arguments, current_cell, interactive, parse_tree_root):
status = EvalStatus.PartialEvaluation
if len(arguments) == 1:
arg1_eval_Result = self.evaluate_parse_tree(current_cell, arguments[0], interactive)
if arg1_eval_Result.status == EvalStatus.FullEvaluation and self.is_float(arg1_eval_Result.get_text()):
workspace_param = self.get_workspace(int(float(arg1_eval_Result.get_text())))
# current_cell.value = workspace_param
text = 'GET.WORKSPACE({})'.format(arg1_eval_Result.get_text())
return_val = workspace_param
status = EvalStatus.FullEvaluation
next_cell = None
if status == EvalStatus.PartialEvaluation:
return_val = text = XLMInterpreter.convert_ptree_to_str(parse_tree_root)
return EvalResult(None, status, return_val, text)
def get_window_handler(self, arguments, current_cell, interactive, parse_tree_root):
status = EvalStatus.Error
if len(arguments) == 1:
arg_eval_result = self.evaluate_parse_tree(current_cell, arguments[0], interactive)
if arg_eval_result.status == EvalStatus.FullEvaluation and self.is_float(arg_eval_result.get_text()):
window_param = self.get_window(int(float(arg_eval_result.get_text())))
# current_cell.value = window_param
text = window_param # XLMInterpreter.convert_ptree_to_str(parse_tree_root)
return_val = window_param
# Overwrites to take actual values from the workbook instead of default config
if int(float(arg_eval_result.get_text())) == 1 or int(float(arg_eval_result.get_text())) == 30:
return_val = "[" + self.xlm_wrapper.get_workbook_name() + "]" + current_cell.sheet.name
status = EvalStatus.FullEvaluation
status = EvalStatus.FullEvaluation
else:
return_val = text = 'GET.WINDOW({})'.format(arg_eval_result.get_text())
status = arg_eval_result.status
if status == EvalStatus.Error:
return_val = text = XLMInterpreter.convert_ptree_to_str(parse_tree_root)
return EvalResult(None, status, return_val, text)
def get_document_handler(self, arguments, current_cell, interactive, parse_tree_root):
status = EvalStatus.Error
arg_eval_result = self.evaluate_parse_tree(current_cell, arguments[0], interactive)
return_val = ""
# Static implementation
if self.is_int(arg_eval_result.value):
status = EvalStatus.PartialEvaluation
if int(arg_eval_result.value) == 76:
return_val = "[" + self.xlm_wrapper.get_workbook_name() + "]" + current_cell.sheet.name
status = EvalStatus.FullEvaluation
elif int(arg_eval_result.value) == 88:
return_val = self.xlm_wrapper.get_workbook_name()
status = EvalStatus.FullEvaluation
text = return_val
return EvalResult(None, status, return_val, text)
def on_time_handler(self, arguments, current_cell, interactive, parse_tree_root):
status = EvalStatus.Error
if len(arguments) == 2:
arg1_eval_result = self.evaluate_parse_tree(current_cell, arguments[0], interactive)
next_sheet, next_col, next_row = self.get_cell_addr(current_cell, arguments[1])
sheets = self.xlm_wrapper.get_macrosheets()
if next_sheet in sheets:
next_cell = self.get_formula_cell(sheets[next_sheet], next_col, next_row)
text = 'ON.TIME({},{})'.format(arg1_eval_result.get_text(), str(next_cell))
status = EvalStatus.FullEvaluation
return_val = 0
return_val = text = XLMInterpreter.convert_ptree_to_str(parse_tree_root)
if status == EvalStatus.Error:
next_cell = None
return EvalResult(next_cell, status, return_val, text)
def app_maximize_handler(self, arguments, current_cell, interactive, parse_tree_root):
status = EvalStatus.FullEvaluation
return_val = True
text = str(return_val)
return EvalResult(None, status, return_val, text)
def concatenate_handler(self, arguments, current_cell, interactive, parse_tree_root):
text = ''
for arg in arguments:
arg_eval_result = self.evaluate_parse_tree(current_cell, arg, interactive)
text += arg_eval_result.get_text(unwrap=True)
return_val = text
text = EvalResult.wrap_str_literal(text)
status = EvalStatus.FullEvaluation
return EvalResult(None, status, return_val, text)
def day_handler(self, arguments, current_cell, interactive, parse_tree_root):
if self.day_of_month is None:
arg1_eval_result = self.evaluate_parse_tree(current_cell, arguments[0], interactive)
if arg1_eval_result.status == EvalStatus.FullEvaluation:
if type(arg1_eval_result.value) is datetime.datetime:
#
# text = str(arg1_eval_result.value.day)
# return_val = text
# status = EvalStatus.FullEvaluation
return_val, status, text = self.guess_day()
elif self.is_float(arg1_eval_result.value):
text = 'DAY(Serial Date)'
status = EvalStatus.NotImplemented
else:
text = XLMInterpreter.convert_ptree_to_str(parse_tree_root)
status = arg1_eval_result.status
else:
text = str(self.day_of_month)
return_val = text
status = EvalStatus.FullEvaluation
return EvalResult(None, status, return_val, text)
def guess_day(self):
xlm = self
min = 1
best_day = 0
for day in range(1, 32):
xlm.char_error_count = 0
non_printable_ascii = 0
total_count = 0
xlm = copy.copy(xlm)
xlm.day_of_month = day
try:
for index, step in enumerate(xlm.deobfuscate_macro(False, silent_mode=True)):
for char in step[2]:
if not (32 <= ord(char) <= 128):
non_printable_ascii += 1
total_count += len(step[2])
if index > 10 and ((non_printable_ascii + xlm.char_error_count) / total_count) > min:
break
if total_count != 0 and ((non_printable_ascii + xlm.char_error_count) / total_count) < min:
min = ((non_printable_ascii + xlm.char_error_count) / total_count)
best_day = day
if min == 0:
break
except Exception as exp:
pass
self.day_of_month = best_day
text = str(self.day_of_month)
return_val = text
status = EvalStatus.FullEvaluation
return return_val, status, text
#https://stackoverflow.com/questions/9574793/how-to-convert-a-python-datetime-datetime-to-excel-serial-date-number
def excel_date(self, date1):
temp = datetime.datetime(1899, 12, 30) # Note, not 31st Dec but 30th!
delta = date1 - temp
return float(delta.days) + (float(delta.seconds) / 86400)
def now_handler(self, arguments, current_cell, interactive, parse_tree_root):
return_val = text = self.excel_date(datetime.datetime.now() + datetime.timedelta(seconds=self._now_count * self._now_step))
self._now_count += 1
status = EvalStatus.FullEvaluation
return EvalResult(None, status, return_val, text)
def value_handler(self, arguments, current_cell, interactive, parse_tree_root):
return_val_result = self.evaluate_parse_tree(current_cell, arguments[0], interactive)
status = EvalStatus.FullEvaluation
value = EvalResult.unwrap_str_literal(return_val_result.value)
if EvalResult.is_int(value):
return_val = int(value)
text = str(return_val)
elif EvalResult.is_float(value):
return_val = float(value)
text = str(return_val)
else:
status = EvalStatus.Error
text = self.convert_ptree_to_str(parse_tree_root)
return_val = 0
return EvalResult(None, status, return_val, text)
def if_handler(self, arguments, current_cell, interactive, parse_tree_root):
visited = False
for stack_frame in self._branch_stack:
if stack_frame[0].get_local_address() == current_cell.get_local_address():
visited = True
if visited is False:
# self._indent_level += 1
size = len(arguments)
if size == 3:
cond_eval_result = self.evaluate_parse_tree(current_cell, arguments[0], interactive)
if self.is_bool(cond_eval_result.value):
cond_eval_result.value = bool(_strtobool(cond_eval_result.value))
elif self.is_int(cond_eval_result.value):
if int(cond_eval_result.value) == 0:
cond_eval_result.value = False
else:
cond_eval_result.value = True
if cond_eval_result.status == EvalStatus.FullEvaluation:
if cond_eval_result.value:
if type(arguments[1]) is Tree or type(arguments[1]) is Token:
self._branch_stack.append(
(current_cell, arguments[1], current_cell.sheet.cells, self._indent_level, '[TRUE]'))
status = EvalStatus.Branching
else:
status = EvalStatus.FullEvaluation
else:
if type(arguments[2]) is Tree or type(arguments[2]) is Token:
self._branch_stack.append(
(current_cell, arguments[2], current_cell.sheet.cells, self._indent_level, '[FALSE]'))
status = EvalStatus.Branching
else:
status = EvalStatus.FullEvaluation
text = XLMInterpreter.convert_ptree_to_str(parse_tree_root)
else:
memory_state = copy.deepcopy(current_cell.sheet.cells)
if type(arguments[2]) is Tree or type(arguments[2]) is Token or type(arguments[2]) is list:
self._branch_stack.append(
(current_cell, arguments[2], memory_state, self._indent_level, '[FALSE]'))
if type(arguments[1]) is Tree or type(arguments[1]) is Token or type(arguments[1]) is list:
self._branch_stack.append(
(current_cell, arguments[1], current_cell.sheet.cells, self._indent_level, '[TRUE]'))
text = XLMInterpreter.convert_ptree_to_str(parse_tree_root)
status = EvalStatus.FullBranching
else:
status = EvalStatus.FullEvaluation
text = XLMInterpreter.convert_ptree_to_str(parse_tree_root)
else:
# loop detected
text = '[[LOOP]]: ' + XLMInterpreter.convert_ptree_to_str(parse_tree_root)
status = EvalStatus.End
return EvalResult(None, status, 0, text)
def mid_handler(self, arguments, current_cell, interactive, parse_tree_root):
str_eval_result = self.evaluate_parse_tree(current_cell, arguments[0], interactive)
base_eval_result = self.evaluate_parse_tree(current_cell, arguments[1], interactive)
len_eval_result = self.evaluate_parse_tree(current_cell, arguments[2], interactive)
status = EvalStatus.PartialEvaluation
return_val = ""
if str_eval_result.status == EvalStatus.FullEvaluation:
if base_eval_result.status == EvalStatus.FullEvaluation and \
len_eval_result.status == EvalStatus.FullEvaluation:
if self.is_float(base_eval_result.value) and self.is_float(len_eval_result.value):
base = int(float(base_eval_result.value)) - 1
length = int(float(len_eval_result.value))
return_val = EvalResult.unwrap_str_literal(str_eval_result.value)[base: base + length]
text = str(return_val)
status = EvalStatus.FullEvaluation
if status == EvalStatus.PartialEvaluation:
text = 'MID({},{},{})'.format(XLMInterpreter.convert_ptree_to_str(arguments[0]),
XLMInterpreter.convert_ptree_to_str(arguments[1]),
XLMInterpreter.convert_ptree_to_str(arguments[2]))
return EvalResult(None, status, return_val, text)
def min_handler(self, arguments, current_cell, interactive, parse_tree_root):
min = None
status = EvalStatus.PartialEvaluation
for argument in arguments:
arg_eval_result = self.evaluate_parse_tree(current_cell, argument, interactive)
if arg_eval_result.status == EvalStatus.FullEvaluation:
cur_val = self.convert_float(arg_eval_result.value)
if not min or cur_val < min:
min = cur_val
else:
min = None
break
if min:
return_val = min
text = str(min)
status = EvalStatus.FullEvaluation
else:
text = return_val = self.convert_ptree_to_str(parse_tree_root)
return EvalResult(None, status, return_val, text)
def max_handler(self, arguments, current_cell, interactive, parse_tree_root):
max = None
status = EvalStatus.PartialEvaluation
for argument in arguments:
arg_eval_result = self.evaluate_parse_tree(current_cell, argument, interactive)
if arg_eval_result.status == EvalStatus.FullEvaluation:
cur_val = self.convert_float(arg_eval_result.value)
if not max or cur_val > max:
max = cur_val
else:
max = None
break
if max:
return_val = max
text = str(max)
status = EvalStatus.FullEvaluation
else:
text = return_val = self.convert_ptree_to_str(parse_tree_root)
return EvalResult(None, status, return_val, text)
def product_handler(self, arguments, current_cell, interactive, parse_tree_root):
total = None
status = EvalStatus.PartialEvaluation
for argument in arguments:
arg_eval_result = self.evaluate_parse_tree(current_cell, argument, interactive)
if arg_eval_result.status == EvalStatus.FullEvaluation:
if not total:
total = self.convert_float(arg_eval_result.value)
else:
total *= self.convert_float(arg_eval_result.value)
else:
total = None
break
if total:
return_val = total
text = str(total)
status = EvalStatus.FullEvaluation
else:
text = return_val = self.convert_ptree_to_str(parse_tree_root)
return EvalResult(None, status, return_val, text)
def mod_handler(self, arguments, current_cell, interactive, parse_tree_root):
arg1_eval_res = self.evaluate_parse_tree(current_cell, arguments[0], interactive)
arg2_eval_res = self.evaluate_parse_tree(current_cell, arguments[1], interactive)
if arg1_eval_res.status == EvalStatus.FullEvaluation and arg2_eval_res.status == EvalStatus.FullEvaluation:
return_val = float(arg1_eval_res.value) % float(arg2_eval_res.value)
text = str(return_val)
status = EvalStatus.FullEvaluation
return EvalResult(None, status, return_val, text)
def sqrt_handler(self, arguments, current_cell, interactive, parse_tree_root):
arg1_eval_res = self.evaluate_parse_tree(current_cell, arguments[0], interactive)
status = EvalStatus.PartialEvaluation
if arg1_eval_res.status == EvalStatus.FullEvaluation:
return_val = math.floor(math.sqrt(float(arg1_eval_res.value)))
text = str(return_val)
status = EvalStatus.FullEvaluation
if status == EvalStatus.PartialEvaluation:
return_val = text = self.convert_ptree_to_str(parse_tree_root)
return EvalResult(None, status, return_val, text)
def goto_handler(self, arguments, current_cell, interactive, parse_tree_root):
next_sheet, next_col, next_row = self.get_cell_addr(current_cell, arguments[0])
next_cell = None
if next_sheet is not None and next_sheet in self.xlm_wrapper.get_macrosheets():
next_cell = self.get_formula_cell(self.xlm_wrapper.get_macrosheets()[next_sheet],
next_col,
next_row)
status = EvalStatus.FullEvaluation
else:
status = EvalStatus.Error
text = XLMInterpreter.convert_ptree_to_str(parse_tree_root)
return_val = 0
return EvalResult(next_cell, status, return_val, text)
def halt_handler(self, arguments, current_cell, interactive, parse_tree_root):
return_val = text = XLMInterpreter.convert_ptree_to_str(parse_tree_root)
status = EvalStatus.End
self._indent_level -= 1
return EvalResult(None, status, return_val, text)
def call_handler(self, arguments, current_cell, interactive, parse_tree_root):
argument_texts = []
status = EvalStatus.FullEvaluation
for argument in arguments:
arg_eval_result = self.evaluate_parse_tree(current_cell, argument, interactive)
if arg_eval_result.status != EvalStatus.FullEvaluation:
status = arg_eval_result.status
argument_texts.append(arg_eval_result.get_text())
list_separator = self.xlm_wrapper.get_xl_international_char(XlApplicationInternational.xlListSeparator)
text = 'CALL({})'.format(list_separator.join(argument_texts))
return_val = 0
return EvalResult(None, status, return_val, text)
def is_number_handler(self, arguments, current_cell, interactive, parse_tree_root):
eval_result = self.evaluate_parse_tree(current_cell, arguments[0], interactive)
if eval_result.status == EvalStatus.FullEvaluation:
if self.is_int(eval_result.text) or self.is_float(eval_result.text):
return_val = 1
else:
return_val = 0
text = str(return_val)
else:
text = 'ISNUMBER({})'.format(eval_result.get_text())
return_val = 1 # true
return EvalResult(None, eval_result.status, return_val, text)
def search_handler(self, arguments, current_cell, interactive, parse_tree_root):
arg1_eval_res = self.evaluate_parse_tree(current_cell, arguments[0], interactive)
arg2_eval_res = self.evaluate_parse_tree(current_cell, arguments[1], interactive)
if arg1_eval_res.status == EvalStatus.FullEvaluation and arg2_eval_res.status == EvalStatus.FullEvaluation:
try:
arg1_val = EvalResult.unwrap_str_literal(str(arg1_eval_res.value))
arg2_val = EvalResult.unwrap_str_literal(arg2_eval_res.value)
return_val = arg2_val.lower().index(arg1_val.lower())
text = str(return_val)
except ValueError:
return_val = None
text = ''
status = EvalStatus.FullEvaluation
else:
text = 'SEARCH({},{})'.format(arg1_eval_res.get_text(), arg2_eval_res.get_text())
return_val = 0
status = EvalStatus.PartialEvaluation
return EvalResult(None, status, return_val, text)
def round_handler(self, arguments, current_cell, interactive, parse_tree_root):
arg1_eval_res = self.evaluate_parse_tree(current_cell, arguments[0], interactive)
arg2_eval_res = self.evaluate_parse_tree(current_cell, arguments[1], interactive)
if arg1_eval_res.status == EvalStatus.FullEvaluation and arg2_eval_res.status == EvalStatus.FullEvaluation:
return_val = round(float(arg1_eval_res.value), int(float(arg2_eval_res.value)))
text = str(return_val)
status = EvalStatus.FullEvaluation
return EvalResult(None, status, return_val, text)
def roundup_handler(self, arguments, current_cell, interactive, parse_tree_root):
arg1_eval_res = self.evaluate_parse_tree(current_cell, arguments[0], interactive)
if arg1_eval_res.status == EvalStatus.FullEvaluation:
return_val = math.ceil(float(arg1_eval_res.value))
text = str(return_val)
status = EvalStatus.FullEvaluation
return EvalResult(None, status, return_val, text)
def directory_handler(self, arguments, current_cell, interactive, parse_tree_root):
text = r'C:\Users\user\Documents'
return_val = text
status = EvalStatus.FullEvaluation
return EvalResult(None, status, return_val, text)
def char_handler(self, arguments, current_cell, interactive, parse_tree_root):
arg_eval_result = self.evaluate_parse_tree(current_cell, arguments[0], interactive)
if arg_eval_result.status == EvalStatus.FullEvaluation:
value = arg_eval_result.text
if arg_eval_result.value in self.defined_names:
value = self.defined_names[arg_eval_result.value].value
if 0 <= float(value) <= 255:
return_val = text = chr(int(float(value)))
# cell = self.get_formula_cell(current_cell.sheet, current_cell.column, current_cell.row)
# cell.value = text
status = EvalStatus.FullEvaluation
else:
return_val = text = XLMInterpreter.convert_ptree_to_str(parse_tree_root)
self.char_error_count += 1
status = EvalStatus.Error
else:
text = 'CHAR({})'.format(arg_eval_result.text)
return_val = text
status = EvalStatus.PartialEvaluation
return EvalResult(arg_eval_result.next_cell, status, return_val, text)
def t_handler(self, arguments, current_cell, interactive, parse_tree_root):
arg_eval_result = self.evaluate_parse_tree(current_cell, arguments[0], interactive)
return_val = ''
if arg_eval_result.status == EvalStatus.FullEvaluation:
if isinstance(arg_eval_result.value, tuple) and len(arg_eval_result.value) == 3:
cell = self.get_cell(arg_eval_result.value[0], arg_eval_result.value[1], arg_eval_result.value[2])
return_val = cell.value
elif arg_eval_result.value != 'TRUE' and arg_eval_result.value != 'FALSE':
return_val = str(arg_eval_result.value)
status = EvalStatus.FullEvaluation
else:
status = EvalStatus.PartialEvaluation
return EvalResult(arg_eval_result.next_cell, status, return_val, EvalResult.wrap_str_literal(str(return_val), must_wrap=True))
def int_handler(self, arguments, current_cell, interactive, parse_tree_root):
arg_eval_result = self.evaluate_parse_tree(current_cell, arguments[0], interactive)
return_val = int(0)
if arg_eval_result.status == EvalStatus.FullEvaluation:
text = str(arg_eval_result.value).lower()
if text == "true":
return_val = int(1)
elif text == "false":
return_val = int(0)
else:
return_val = int(arg_eval_result.value)
status = EvalStatus.FullEvaluation
else:
status = EvalStatus.PartialEvaluation
return EvalResult(arg_eval_result.next_cell, status, return_val, str(return_val))
def run_handler(self, arguments, current_cell, interactive, parse_tree_root):
size = len(arguments)
next_cell = None
status = EvalStatus.PartialEvaluation
if 1 <= size <= 2:
next_sheet, next_col, next_row = self.get_cell_addr(current_cell, arguments[0])
if next_sheet is not None and next_sheet in self.xlm_wrapper.get_macrosheets():
next_cell = self.get_formula_cell(self.xlm_wrapper.get_macrosheets()[next_sheet],
next_col,
next_row)
if size == 1:
text = 'RUN({}!{}{})'.format(next_sheet, next_col, next_row)
else:
text = 'RUN({}!{}{}, {})'.format(next_sheet, next_col, next_row,
XLMInterpreter.convert_ptree_to_str(arguments[1]))
status = EvalStatus.FullEvaluation
else:
text = XLMInterpreter.convert_ptree_to_str(parse_tree_root)
status = EvalStatus.Error
return_val = 0
else:
text = XLMInterpreter.convert_ptree_to_str(parse_tree_root)
status = EvalStatus.Error
return_val = 1
return EvalResult(next_cell, status, return_val, text)
def formula_handler(self, arguments, current_cell, interactive, parse_tree_root):
return self.evaluate_formula(current_cell, 'FORMULA', arguments, interactive)
def formula_fill_handler(self, arguments, current_cell, interactive, parse_tree_root):
return self.evaluate_formula(current_cell, 'FORMULA.FILL', arguments, interactive)
def formula_array_handler(self, arguments, current_cell, interactive, parse_tree_root):
return self.evaluate_formula(current_cell, 'FORMULA.ARRAY', arguments, interactive)
def set_value_handler(self, arguments, current_cell, interactive, parse_tree_root):
return self.evaluate_formula(
current_cell, 'SET.VALUE', arguments, interactive, destination_arg=2, set_value_only=True)
def error_handler(self, arguments, current_cell, interactive, parse_tree_root):
return EvalResult(None, EvalStatus.FullEvaluation, 0, XLMInterpreter.convert_ptree_to_str(parse_tree_root))
def select_handler(self, arguments, current_cell, interactive, parse_tree_root):
status = EvalStatus.PartialEvaluation
range_eval_result = self.evaluate_parse_tree(current_cell, arguments[0], interactive)
if len(arguments) == 2:
# e.g., SELECT(B1:B100,B1) and SELECT(,"R[1]C")
if self.active_cell:
sheet, col, row = self.get_cell_addr(self.active_cell, arguments[1])
else:
sheet, col, row = self.get_cell_addr(current_cell, arguments[1])
if sheet:
self.active_cell = self.get_cell(sheet, col, row)
status = EvalStatus.FullEvaluation
elif isinstance(arguments[0], Token):
text = XLMInterpreter.convert_ptree_to_str(parse_tree_root)
return_val = 0
elif arguments[0].data == 'range':
# e.g., SELECT(D1:D10:D1)
sheet, col, row = self.selected_range[2]
if sheet:
self.active_cell = self.get_cell(sheet, col, row)
status = EvalStatus.FullEvaluation
elif arguments[0].data == 'cell':
# select(R1C1)
if self.active_cell:
sheet, col, row = self.get_cell_addr(self.active_cell, arguments[0])
else:
sheet, col, row = self.get_cell_addr(current_cell, arguments[0])
if sheet:
self.active_cell = self.get_cell(sheet, col, row)
status = EvalStatus.FullEvaluation
text = XLMInterpreter.convert_ptree_to_str(parse_tree_root)
return_val = 0
return EvalResult(None, status, return_val, text)
def iterate_range(self, name, start_cell, end_cell):
sheet_name = start_cell[0]
row_start = int(start_cell[2])
row_end = int(end_cell[2])
for row_index in range(row_start, row_end + 1):
col_start = Cell.convert_to_column_index(start_cell[1])
col_end = Cell.convert_to_column_index(end_cell[1])
for col_index in range(col_start, col_end+1):
next_cell = self.get_cell(sheet_name, Cell.convert_to_column_name(col_index), row_index)
if next_cell:
yield next_cell
def forcell_handler(self, arguments, current_cell, interactive, parse_tree_root):
var_eval_result = self.evaluate_parse_tree(current_cell, arguments[0], interactive)
start_cell_ptree, end_cell_ptree = self.get_range_parts(arguments[1])
start_cell = self.get_cell_addr(current_cell, start_cell_ptree)
end_cell = self.get_cell_addr(current_cell, end_cell_ptree)
if start_cell[0] != end_cell[0]:
end_cell = (start_cell[0], end_cell[1], end_cell[2])
skip = False
if len(arguments) >= 3:
skip_eval_result = self.evaluate_parse_tree(current_cell, arguments[2], interactive)
skip = bool(skip_eval_result.value)
variable_name = EvalResult.unwrap_str_literal(var_eval_result.value).lower()
if len(self._while_stack) > 0 and self._while_stack[-1]['start_point'] == current_cell:
iterator = self._while_stack[-1]['iterator']
else:
iterator = self.iterate_range(variable_name, start_cell, end_cell)
stack_record = {'start_point': current_cell, 'status': True, 'iterator': iterator}
self._while_stack.append(stack_record)
try:
self.defined_names[variable_name] = next(iterator)
except:
self._while_stack[-1]['status'] = False
self._indent_level += 1
return EvalResult(None, EvalStatus.FullEvaluation, 0 , self.convert_ptree_to_str(parse_tree_root))
def while_handler(self, arguments, current_cell, interactive, parse_tree_root):
status = EvalStatus.PartialEvaluation
text = ''
stack_record = {'start_point': current_cell, 'status': False}
condition_eval_result = self.evaluate_parse_tree(current_cell, arguments[0], interactive)
status = condition_eval_result.status
if condition_eval_result.status == EvalStatus.FullEvaluation:
if str(condition_eval_result.value).lower() == 'true':
stack_record['status'] = True
text = '{} -> [{}]'.format(XLMInterpreter.convert_ptree_to_str(parse_tree_root),
str(condition_eval_result.value))
if not text:
text = '{}'.format(XLMInterpreter.convert_ptree_to_str(parse_tree_root))
self._while_stack.append(stack_record)
if stack_record['status'] == False:
self.ignore_processing = True
self._indent_level += 1
return EvalResult(None, status, 0, text)
def next_handler(self, arguments, current_cell, interactive, parse_tree_root):
status = EvalStatus.FullEvaluation
next_cell = None
if self._indent_level == len(self._while_stack):
self.ignore_processing = False
next_cell = None
if len(self._while_stack) > 0:
top_record = self._while_stack.pop()
if top_record['status'] is True:
next_cell = top_record['start_point']
if 'iterator' in top_record:
self._while_stack.append(top_record)
self._indent_level = self._indent_level - 1 if self._indent_level > 0 else 0
self._indent_current_line = True
if next_cell is None:
status = EvalStatus.IGNORED
return EvalResult(next_cell, status, 0, 'NEXT')
def len_handler(self, arguments, current_cell, interactive, parse_tree_root):
arg_eval_result = self.evaluate_parse_tree(current_cell, arguments[0], interactive)
if arg_eval_result.status == EvalStatus.FullEvaluation:
return_val = len(arg_eval_result.get_text(unwrap=True))
text = str(return_val)
status = EvalStatus.FullEvaluation
else:
text = XLMInterpreter.convert_ptree_to_str(parse_tree_root)
return_val = text
status = EvalStatus.PartialEvaluation
return EvalResult(None, status, return_val, text)
def define_name_handler(self, arguments, current_cell, interactive, parse_tree_root):
arg_name_eval_result = self.evaluate_parse_tree(current_cell, arguments[0], interactive)
status = EvalStatus.PartialEvaluation
if arg_name_eval_result.status == EvalStatus.FullEvaluation:
arg_val_eval_result = self.evaluate_parse_tree(current_cell, arguments[1], interactive)
status = EvalStatus.FullEvaluation
name = EvalResult.unwrap_str_literal(arg_name_eval_result.text).lower()
if EvalResult.is_int(arg_val_eval_result.value):
self.defined_names[name] = int(arg_val_eval_result.value)
elif EvalResult.is_float(arg_val_eval_result.value):
self.defined_names[name] = float(arg_val_eval_result.value)
else:
self.defined_names[name] = arg_val_eval_result.value
return_val = self.defined_names[name]
text = "DEFINE.NAME({},{})".format(EvalResult.wrap_str_literal(name), str(return_val))
else:
return_val = text = self.convert_ptree_to_str(parse_tree_root)
return EvalResult(None, status, return_val, text)
def index_handler(self, arguments, current_cell, interactive, parse_tree_root):
array_arg_result = self.evaluate_parse_tree(current_cell, arguments[0], interactive)
status = EvalStatus.PartialEvaluation
if array_arg_result.status == EvalStatus.FullEvaluation:
index_arg_result = self.evaluate_parse_tree(current_cell, arguments[1], interactive)
if isinstance(array_arg_result.value, list):
# example: f9adf499bc16bfd096e00bc59c3233f022dec20c20440100d56e58610e4aded3
return_val = array_arg_result.value[int(float(index_arg_result.value))-1] # index starts at 1 in excel
else:
# example: 6a8045bc617df5f2b8f9325ed291ef05ac027144f1fda84e78d5084d26847902
range = EvalResult.unwrap_str_literal(array_arg_result.value)
parsed_range = Cell.parse_range_addr(range)
index = int(float(index_arg_result.value))-1
row_str = str(int(float(parsed_range[2])) + index)
if parsed_range[0]:
sheet_name = parsed_range[0]
else:
sheet_name = current_cell.sheet.name
return_val = self.get_cell(sheet_name, parsed_range[1], row_str)
text = str(return_val)
status = EvalStatus.FullEvaluation
else:
return_val = text = self.convert_ptree_to_str(parse_tree_root)
return EvalResult(None, status, return_val, text)
def rows_handler(self, arguments, current_cell, interactive, parse_tree_root):
arg_eval_result = self.evaluate_parse_tree(current_cell, arguments[0], interactive)
status = EvalStatus.PartialEvaluation
if arg_eval_result.status == EvalStatus.FullEvaluation:
if isinstance(arg_eval_result.value, list):
# example: f9adf499bc16bfd096e00bc59c3233f022dec20c20440100d56e58610e4aded3
return_val = len(arg_eval_result.value)
else:
# example: 6a8045bc617df5f2b8f9325ed291ef05ac027144f1fda84e78d5084d26847902
range = EvalResult.unwrap_str_literal(arg_eval_result.value)
parsed_range = Cell.parse_range_addr(range)
return_val = int(parsed_range[4]) - int(parsed_range[2]) + 1
text = str(return_val)
status = EvalStatus.FullEvaluation
else:
return_val = text = self.convert_ptree_to_str(parse_tree_root)
return EvalResult(None, status, return_val, text)
def counta_handler(self, arguments, current_cell, interactive, parse_tree_root):
arg_eval_result = self.evaluate_parse_tree(current_cell, arguments[0], interactive)
sheet_name, startcolumn, startrow, endcolumn, endrow = Cell.parse_range_addr(arg_eval_result.text)
count = 0
it = int(startrow)
start_col_index = Cell.convert_to_column_index(startcolumn)
end_col_index = Cell.convert_to_column_index(endcolumn)
start_row_index = int(startrow)
end_row_index = int(endrow)
val_item_count = 0
for row in range(start_row_index, end_row_index + 1):
for col in range(start_col_index, end_col_index + 1):
if (sheet_name != None):
cell = self.get_worksheet_cell(sheet_name,
Cell.convert_to_column_name(col),
str(row))
else:
cell = self.get_cell(current_cell.sheet.name,
Cell.convert_to_column_name(col),
str(row))
if cell and cell.value != '':
val_item_count += 1
return_val = val_item_count
status = EvalStatus.FullEvaluation
text = str(return_val)
return EvalResult(None, status, return_val, text)
def count_handler(self, arguments, current_cell, interactive, parse_tree_root):
return_val = len(arguments)
text = str(return_val)
status = EvalStatus.FullEvaluation
return EvalResult(None, status, return_val, text)
def trunc_handler(self, arguments, current_cell, interactive, parse_tree_root):
arg_eval_result = self.evaluate_parse_tree(current_cell, arguments[0], interactive)
if arg_eval_result.status == EvalStatus.FullEvaluation:
if arg_eval_result.value == "TRUE":
return_val = 1
elif arg_eval_result.value == "FALSE":
return_val = 0
else:
return_val = math.trunc(float(arg_eval_result.value))
text = str(return_val)
status = EvalStatus.FullEvaluation
else:
text = XLMInterpreter.convert_ptree_to_str(parse_tree_root)
return_val = text
status = EvalStatus.PartialEvaluation
return EvalResult(None, status, return_val, text)
def quotient_handler(self, arguments, current_cell, interactive, parse_tree_root):
numerator_arg_eval_result = self.evaluate_parse_tree(current_cell, arguments[0], interactive)
Denominator_arg_eval_result = self.evaluate_parse_tree(current_cell, arguments[0], interactive)
status = EvalStatus.PartialEvaluation
if numerator_arg_eval_result.status == EvalStatus.FullEvaluation and \
Denominator_arg_eval_result.status == EvalStatus.FullEvaluation:
return_val = numerator_arg_eval_result.value // Denominator_arg_eval_result.value
text = str(return_val)
status = EvalStatus.FullEvaluation
return EvalResult(None, status, return_val, text)
def abs_handler(self, arguments, current_cell, interactive, parse_tree_root):
arg_eval_result = self.evaluate_parse_tree(current_cell, arguments[0], interactive)
if arg_eval_result.status == EvalStatus.FullEvaluation:
if arg_eval_result.value == "TRUE":
return_val = 1
elif arg_eval_result.value == "FALSE":
return_val = 0
else:
return_val = abs(float(arg_eval_result.value))
text = str(return_val)
status = EvalStatus.FullEvaluation
else:
text = XLMInterpreter.convert_ptree_to_str(parse_tree_root)
return_val = text
status = EvalStatus.PartialEvaluation
return EvalResult(None, status, return_val, text)
def absref_handler(self, arguments, current_cell, interactive, parse_tree_root):
arg_ref_txt_eval_result = self.evaluate_parse_tree(current_cell, arguments[0], interactive)
status = EvalStatus.PartialEvaluation
if arg_ref_txt_eval_result.status == EvalStatus.FullEvaluation and \
(isinstance(arguments[1], Tree) and arguments[1].data == 'cell'):
offset_addr_text = arg_ref_txt_eval_result.value
base_addr_text = self.convert_ptree_to_str(arguments[1])
return_val = Cell.get_abs_addr(base_addr_text, offset_addr_text)
status = EvalStatus.FullEvaluation
else:
return_val = XLMInterpreter.convert_ptree_to_str(parse_tree_root)
return EvalResult(None, status, return_val, str(return_val))
def address_handler(self, arguments, current_cell, interactive, parse_tree_root):
arg_row_num_eval_result = self.evaluate_parse_tree(current_cell, arguments[0], interactive)
arg_col_num_eval_result = self.evaluate_parse_tree(current_cell, arguments[1], interactive)
optional_args = True
if len(arguments) >= 3:
arg_abs_num_eval_result = self.evaluate_parse_tree(current_cell, arguments[2], interactive)
if arg_abs_num_eval_result.status == EvalStatus.FullEvaluation:
abs_num = arg_abs_num_eval_result.value
else:
optional_args = False
else:
abs_num = 1
if len(arguments) >= 4:
arg_a1_eval_result = self.evaluate_parse_tree(current_cell, arguments[3], interactive)
if arg_a1_eval_result.status == EvalStatus.FullEvaluation:
a1 = arg_a1_eval_result.value
else:
optional_args = False
else:
a1 = "TRUE"
if len(arguments) >= 5:
arg_sheet_eval_result = self.evaluate_parse_tree(current_cell, arguments[4], interactive)
if arg_sheet_eval_result.status == EvalStatus.FullEvaluation:
sheet_name = arg_sheet_eval_result.text.strip('\"')
else:
optional_args = False
else:
sheet_name = current_cell.sheet.name
return_val = ''
if arg_row_num_eval_result.status == EvalStatus.FullEvaluation and \
arg_col_num_eval_result.status == EvalStatus.FullEvaluation and \
optional_args:
return_val += sheet_name + '!'
if a1 == "FALSE":
cell_addr_tmpl = 'R{}C{}'
if abs_num == 2:
cell_addr_tmpl = 'R{}C[{}]'
elif abs_num == 3:
cell_addr_tmpl = 'R[{}]C{}'
elif abs_num == 4:
cell_addr_tmpl = 'R[{}]C[{}]'
return_val += cell_addr_tmpl.format(arg_row_num_eval_result.text,
arg_col_num_eval_result.text)
else:
cell_addr_tmpl = '${}${}'
if abs_num == 2:
cell_addr_tmpl = '{}${}'
elif abs_num == 3:
cell_addr_tmpl = '${}{}'
elif abs_num == 4:
cell_addr_tmpl = '{}{}'
return_val += cell_addr_tmpl.format(Cell.convert_to_column_name(int(arg_col_num_eval_result.value)),
arg_row_num_eval_result.text)
status = EvalStatus.FullEvaluation
else:
status = EvalStatus.PartialEvaluation
return_val = self.evaluate_parse_tree(current_cell, arguments, False)
return EvalResult(None, status, return_val, str(return_val))
def indirect_handler(self, arguments, current_cell, interactive, parse_tree_root):
arg_addr_eval_result = self.evaluate_parse_tree(current_cell, arguments[0], interactive)
status = EvalStatus.PartialEvaluation
if arg_addr_eval_result.status == EvalStatus.FullEvaluation:
a1 = "TRUE"
if len(arguments) == 2:
arg_a1_eval_result = self.evaluate_parse_tree(current_cell, arguments[1], interactive)
if arg_a1_eval_result.status == EvalStatus.FullEvaluation:
a1 = arg_a1_eval_result.value
sheet_name, col, row = Cell.parse_cell_addr(arg_addr_eval_result.value)
indirect_cell = self.get_cell(sheet_name, col, row)
return_val = indirect_cell.value
status = EvalStatus.FullEvaluation
else:
return_val = self.evaluate_parse_tree(current_cell, arguments, False)
return EvalResult(None, status, return_val, str(return_val))
def register_handler(self, arguments, current_cell, interactive, parse_tree_root):
if len(arguments) >= 4:
arg_list = []
status = EvalStatus.FullEvaluation
for index, arg in enumerate(arguments):
if index > 3:
break
res_eval = self.evaluate_parse_tree(current_cell, arg, interactive)
arg_list.append(res_eval.get_text(unwrap=True))
function_name = "{}.{}".format(arg_list[0], arg_list[1])
# signature: https://support.office.com/en-us/article/using-the-call-and-register-functions-06fa83c1-2869-4a89-b665-7e63d188307f
function_signature = arg_list[2]
function_alias = arg_list[3]
# overrides previously registered function
self._registered_functions[function_alias] = {'name': function_name, 'signature': function_signature}
text = self.evaluate_argument_list(current_cell, 'REGISTER', arguments).get_text(unwrap=True)
else:
status = EvalStatus.Error
text = XLMInterpreter.convert_ptree_to_str(parse_tree_root)
return_val = 0
return EvalResult(None, status, return_val, text)
def registerid_handler(self, arguments, current_cell, interactive, parse_tree_root):
if len(arguments) >= 3:
arg_list = []
status = EvalStatus.FullEvaluation
for index, arg in enumerate(arguments):
if index > 2:
break
res_eval = self.evaluate_parse_tree(current_cell, arg, interactive)
arg_list.append(res_eval.get_text(unwrap=True))
function_name = "{}.{}".format(arg_list[0], arg_list[1])
# signature: https://support.office.com/en-us/article/using-the-call-and-register-functions-06fa83c1-2869-4a89-b665-7e63d188307f
function_signature = arg_list[2]
#function_alias = arg_list[3]
# overrides previously registered function
#self._registered_functions[function_alias] = {'name': function_name, 'signature': function_signature}
text = self.evaluate_argument_list(current_cell, 'REGISTER.ID', arguments).get_text(unwrap=True)
return_val = function_name
else:
status = EvalStatus.Error
text = XLMInterpreter.convert_ptree_to_str(parse_tree_root)
return_val = 0
return EvalResult(None, status, return_val, text)
def return_handler(self, arguments, current_cell, interactive, parse_tree_root):
arg1_eval_res = self.evaluate_parse_tree(current_cell, arguments[0], interactive)
if self._function_call_stack:
return_cell = self._function_call_stack.pop()
return_cell.value = arg1_eval_res.value
arg1_eval_res.next_cell = self.get_formula_cell(return_cell.sheet,
return_cell.column,
str(int(return_cell.row) + 1))
if arg1_eval_res.text == '':
arg1_eval_res.text = 'RETURN()'
return arg1_eval_res
def fopen_handler(self, arguments, current_cell, interactive, parse_tree_root):
arg1_eval_res = self.evaluate_parse_tree(current_cell, arguments[0], interactive)
if len(arguments) > 1:
arg2_eval_res = self.evaluate_parse_tree(current_cell, arguments[1], interactive)
access = arg2_eval_res.value
else:
access = '1'
if arg1_eval_res.status == EvalStatus.FullEvaluation:
file_name = arg1_eval_res.get_text(unwrap=True)
else:
file_name = "default_name"
if file_name not in self._files:
self._files[file_name] = {'file_access': access,
'file_content': ''}
text = 'FOPEN({},{})'.format(arg1_eval_res.get_text(unwrap=False),
access)
return EvalResult(None, arg1_eval_res.status, file_name, text)
def fsize_handler(self, arguments, current_cell, interactive, parse_tree_root, end_line=''):
arg1_eval_res = self.evaluate_parse_tree(current_cell, arguments[0], interactive)
file_name = arg1_eval_res.get_text(unwrap=True)
status = EvalStatus.PartialEvaluation
return_val = 0
if file_name in self._files:
status = EvalStatus.FullEvaluation
if self._files[file_name]['file_content'] is not None:
return_val = len(self._files[file_name]['file_content'])
text = 'FSIZE({})'.format(EvalResult.wrap_str_literal(file_name))
return EvalResult(None, status, return_val, str(return_val))
def fwrite_handler(self, arguments, current_cell, interactive, parse_tree_root, end_line=''):
arg1_eval_res = self.evaluate_parse_tree(current_cell, arguments[0], interactive)
arg2_eval_res = self.evaluate_parse_tree(current_cell, arguments[1], interactive)
file_name = arg1_eval_res.value
if file_name.strip() == "" or EvalResult.is_int(file_name) or EvalResult.is_float(file_name):
if len(self._files) > 0:
file_name = list(self._files.keys())[0]
else:
file_name = "default_filename"
file_content = arg2_eval_res.get_text(unwrap=True)
status = EvalStatus.PartialEvaluation
if file_name in self._files:
status = EvalStatus.FullEvaluation
self._files[file_name]['file_content'] += file_content + end_line
text = 'FWRITE({},{})'.format(EvalResult.wrap_str_literal(file_name), EvalResult.wrap_str_literal(file_content))
return EvalResult(None, status, '0', text)
def fwriteln_handler(self, arguments, current_cell, interactive, parse_tree_root):
return self.fwrite_handler(arguments, current_cell, interactive, parse_tree_root, end_line='\r\n')
def files_handler(self, arguments, current_cell, interactive, parse_tree_root, end_line=''):
arg1_eval_res = self.evaluate_parse_tree(current_cell, arguments[0], interactive)
dir_name = arg1_eval_res.get_text(unwrap=True)
status = EvalStatus.FullEvaluation
# if dir_name in self._files:
# return_val = dir_name
# else:
# return_val = None
return_val = dir_name
text = "FILES({})".format(EvalResult.wrap_str_literal(dir_name))
return EvalResult(None, status, return_val, text)
def iserror_handler(self, arguments, current_cell, interactive, parse_tree_root, end_line=''):
arg1_eval_res = self.evaluate_parse_tree(current_cell, arguments[0], interactive)
status = EvalStatus.FullEvaluation
if arg1_eval_res.value == None:
return_val = True
else:
return_val = False
if self._iserror_loc is None:
self._iserror_val = return_val
self._iserror_loc = current_cell
self._iserror_count = 1
elif self._iserror_loc == current_cell:
if self._iserror_val != return_val:
self._iserror_val = return_val
self._iserror_count = 1
elif self._iserror_count < XLMInterpreter.MAX_ISERROR_LOOPCOUNT:
self._iserror_count += 1
else:
return_val = not return_val
self._iserror_loc = None
text = 'ISERROR({})'.format(EvalResult.wrap_str_literal(arg1_eval_res.get_text(unwrap=True)))
return EvalResult(None, status, return_val, text)
def offset_handler(self, arguments, current_cell, interactive, parse_tree_root):
value = 0
next = None
status = EvalStatus.PartialEvaluation
cell = self.get_cell_addr(current_cell, arguments[0])
row_index = self.evaluate_parse_tree(current_cell, arguments[1], interactive)
col_index = self.evaluate_parse_tree(current_cell, arguments[2], interactive)
if isinstance(cell, tuple) and \
row_index.status == EvalStatus.FullEvaluation and \
col_index.status == EvalStatus.FullEvaluation:
row = str(int(cell[2]) + int(float(str(row_index.value))))
col = Cell.convert_to_column_name(Cell.convert_to_column_index(cell[1]) + int(float(str(col_index.value))))
ref_cell = (cell[0], col, row)
value = ref_cell
status = EvalStatus.FullEvaluation
next = self.get_formula_cell(self.xlm_wrapper.get_macrosheets()[cell[0]], col, row)
text = XLMInterpreter.convert_ptree_to_str(parse_tree_root)
return EvalResult(next, status, value, text)
def arabic_hander(self, arguments, current_cell, interactive, parse_tree_root, end_line=''):
arg1_eval_res = self.evaluate_parse_tree(current_cell, arguments[0], interactive)
if arg1_eval_res.status == EvalStatus.FullEvaluation:
roman_number = EvalResult.get_text(arg1_eval_res, unwrap=True)
return_val = _from_roman(roman_number)
status = EvalStatus.FullEvaluation
text = str(return_val)
else:
return_val = text = self.convert_ptree_to_str(parse_tree_root)
return EvalResult(None, status, return_val, text)
def VirtualAlloc_handler(self, arguments, current_cell, interactive, parse_tree_root):
base_eval_res = self.evaluate_parse_tree(current_cell, arguments[0], interactive)
size_eval_res = self.evaluate_parse_tree(current_cell, arguments[1], interactive)
if base_eval_res.status == EvalStatus.FullEvaluation and size_eval_res.status == EvalStatus.FullEvaluation:
base = int(base_eval_res.get_text(unwrap=True))
occupied_addresses = [rec['base'] + rec['size'] for rec in self._memory]
for memory_record in self._memory:
if memory_record['base'] <= base <= (memory_record['base'] + memory_record['size']):
base = map(max, occupied_addresses) + 4096
size = int(size_eval_res.get_text(unwrap=True))
self._memory.append({
'base': base,
'size': size,
'data': [0] * size
})
return_val = base
status = EvalStatus.FullEvaluation
else:
status = EvalStatus.PartialEvaluation
return_val = 0
text = XLMInterpreter.convert_ptree_to_str(parse_tree_root)
return EvalResult(None, status, return_val, text)
def WriteProcessMemory_handler(self, arguments, current_cell, interactive, parse_tree_root):
status = EvalStatus.PartialEvaluation
if len(arguments) > 4:
status = EvalStatus.FullEvaluation
args_eval_result = []
for arg in arguments:
arg_eval_res = self.evaluate_parse_tree(current_cell, arg, interactive)
if arg_eval_res.status != EvalStatus.FullEvaluation:
status = arg_eval_res.status
args_eval_result.append(arg_eval_res)
if status == EvalStatus.FullEvaluation:
base_address = int(args_eval_result[1].value)
mem_data = args_eval_result[2].value
mem_data = bytearray([ord(x) for x in mem_data])
size = int(args_eval_result[3].value)
if not self.write_memory(base_address, mem_data, size):
status = EvalStatus.Error
text = 'Kernel32.WriteProcessMemory({},{},"{}",{},{})'.format(
args_eval_result[0].get_text(),
base_address,
mem_data.hex(),
size,
args_eval_result[4].get_text())
return_val = 0
if status != EvalStatus.FullEvaluation:
text = XLMInterpreter.convert_ptree_to_str(parse_tree_root)
return_val = 0
return EvalResult(None, status, return_val, text)
def RtlCopyMemory_handler(self, arguments, current_cell, interactive, parse_tree_root):
status = EvalStatus.PartialEvaluation
if len(arguments) == 3:
destination_eval_res = self.evaluate_parse_tree(current_cell, arguments[0], interactive)
src_eval_res = self.evaluate_parse_tree(current_cell, arguments[1], interactive)
size_res = self.evaluate_parse_tree(current_cell, arguments[2], interactive)
if destination_eval_res.status == EvalStatus.FullEvaluation and \
src_eval_res.status == EvalStatus.FullEvaluation:
status = EvalStatus.FullEvaluation
mem_data = src_eval_res.value
mem_data = bytearray([ord(x) for x in mem_data])
if not self.write_memory(int(destination_eval_res.value), mem_data, len(mem_data)):
status = EvalStatus.Error
text = 'Kernel32.RtlCopyMemory({},"{}",{})'.format(
destination_eval_res.get_text(),
mem_data.hex(),
size_res.get_text())
if status == EvalStatus.PartialEvaluation:
text = XLMInterpreter.convert_ptree_to_str(parse_tree_root)
return_val = 0
return EvalResult(None, status, return_val, text)
# endregion
def write_memory(self, base_address, mem_data, size):
result = True
for mem_rec in self._memory:
if mem_rec['base'] <= base_address <= mem_rec['base'] + mem_rec['size']:
if mem_rec['base'] <= base_address + size <= mem_rec['base'] + mem_rec['size']:
offset = base_address - mem_rec['base']
for i in range(0, size):
mem_rec['data'][offset + i] = mem_data[i]
else:
result = False
break
return result
def evaluate_defined_name(self, current_cell, name, interactive):
result = None
lname = name.lower()
if lname in self.defined_names:
val = self.defined_names[lname]
if isinstance(val, Tree) and val.data == 'cell':
eval_res = self.evaluate_cell(current_cell, interactive, val)
result = eval_res.value
elif isinstance(val, list):
result = val
else:
if isinstance(val, Cell):
data = val.value
else:
# example: c7e40628fb6beb52d9d73a3b3afd1dca5d2335713593b698637e1a47b42bfc71 password: 2021
data = val
try:
formula_str = str(data) if str(data).startswith('=') else '=' + str(data)
parsed_formula = self.xlm_parser.parse(formula_str)
eval_result = self.evaluate_parse_tree(current_cell, parsed_formula, interactive)
if isinstance(eval_result.value, list):
result = eval_result.value
else:
result = str(eval_result.value)
except:
result = str(data)
return result
def evaluate_parse_tree(self, current_cell, parse_tree_root, interactive=True):
next_cell = None
status = EvalStatus.NotImplemented
text = None
return_val = None
if type(parse_tree_root) is Token:
if parse_tree_root.value.lower() in self.defined_names:
# this formula has a defined name that can be changed
# current formula must be removed from cache
self._remove_current_formula_from_cache = True
parse_tree_root.value = self.evaluate_defined_name(current_cell, parse_tree_root.value, interactive)
return_val = parse_tree_root.value
status = EvalStatus.FullEvaluation
text = str(return_val)
result = EvalResult(next_cell, status, return_val, text)
elif type(parse_tree_root) is list:
return_val = text = ''
status = EvalStatus.FullEvaluation
result = EvalResult(next_cell, status, return_val, text)
elif parse_tree_root.data == 'function_call':
result = self.evaluate_function(current_cell, parse_tree_root, interactive)
elif parse_tree_root.data == 'cell':
result = self.evaluate_cell(current_cell, interactive, parse_tree_root)
elif parse_tree_root.data == 'range':
result = self.evaluate_range(current_cell, interactive, parse_tree_root)
elif parse_tree_root.data == 'array':
result = self.evaluate_array(current_cell, interactive, parse_tree_root)
elif parse_tree_root.data in self._expr_rule_names:
text_left = None
concat_status = EvalStatus.FullEvaluation
for index, child in enumerate(parse_tree_root.children):
if type(child) is Token and child.type in ['ADDITIVEOP', 'MULTIOP', 'CMPOP', 'CONCATOP']:
op_str = str(child)
right_arg = parse_tree_root.children[index + 1]
right_arg_eval_res = self.evaluate_parse_tree(current_cell, right_arg, interactive)
if isinstance(right_arg_eval_res.value, Cell):
text_right = EvalResult.unwrap_str_literal(right_arg_eval_res.value.value)
else:
text_right = right_arg_eval_res.get_text(unwrap=True)
if op_str == '&':
if left_arg_eval_res.status == EvalStatus.FullEvaluation and right_arg_eval_res.status != EvalStatus.FullEvaluation:
text_left = '{}&{}'.format(text_left, text_right)
left_arg_eval_res.status = EvalStatus.PartialEvaluation
concat_status = EvalStatus.PartialEvaluation
elif left_arg_eval_res.status != EvalStatus.FullEvaluation and right_arg_eval_res.status == EvalStatus.FullEvaluation:
text_left = '{}&{}'.format(text_left, text_right)
left_arg_eval_res.status = EvalStatus.FullEvaluation
concat_status = EvalStatus.PartialEvaluation
elif left_arg_eval_res.status != EvalStatus.FullEvaluation and right_arg_eval_res.status != EvalStatus.FullEvaluation:
text_left = '{}&{}'.format(text_left, text_right)
left_arg_eval_res.status = EvalStatus.PartialEvaluation
concat_status = EvalStatus.PartialEvaluation
else:
text_left = text_left + text_right
elif left_arg_eval_res.status == EvalStatus.FullEvaluation and right_arg_eval_res.status == EvalStatus.FullEvaluation:
status = EvalStatus.FullEvaluation
if isinstance(right_arg_eval_res.value, Cell):
value_right = right_arg_eval_res.value.value
else:
value_right = right_arg_eval_res.value
if isinstance(value_right, str):
if value_right.lower() == 'true':
value_right = 1
elif value_right.lower() == 'false':
value_right = 0
text_left = str(text_left)
text_right = str(text_right)
if text_left == '':
text_left = '0'
value_left = 0
if text_right == '':
text_right = '0'
value_right = 0
if self.is_float(value_left) and self.is_float(value_right):
if op_str in self._operators:
op_res = self._operators[op_str](float(value_left), float(value_right))
if type(op_res) == bool:
value_left = str(op_res)
elif op_res.is_integer():
value_left = str(int(op_res))
else:
op_res = round(op_res, 10)
value_left = str(op_res)
else:
value_left = 'Operator ' + op_str
left_arg_eval_res.status = EvalStatus.NotImplemented
elif EvalResult.is_datetime(text_left.strip('\"')) and EvalResult.is_datetime(text_right.strip('\"')):
timestamp1 = datetime.datetime.strptime(text_left.strip('\"'), "%Y-%m-%d %H:%M:%S.%f")
timestamp2 = datetime.datetime.strptime(text_right.strip('\"'), "%Y-%m-%d %H:%M:%S.%f")
op_res = self._operators[op_str](float(timestamp1.timestamp()),
float(timestamp2.timestamp()))
op_res += 1000
if type(op_res) == bool:
value_left = str(op_res)
elif EvalResult.is_datetime(op_res):
value_left = str(op_res)
elif op_res.is_integer():
value_left = str(op_res)
else:
op_res = round(op_res, 10)
value_left = str(op_res)
elif EvalResult.is_datetime(text_left.strip('\"')) and EvalResult.is_time(text_right.strip('\"')):
timestamp1 = datetime.datetime.strptime(text_left.strip('\"'), "%Y-%m-%d %H:%M:%S.%f")
timestamp2 = datetime.datetime.strptime(text_right.strip('\"'), "%H:%M:%S")
t1 = float(timestamp1.timestamp())
t2 = float(
int(timestamp2.hour) * 3600 + int(timestamp2.minute) * 60 + int(timestamp2.second))
op_res = datetime.datetime.fromtimestamp(self._operators[op_str](t1, t2))
if type(op_res) == bool:
value_left = str(op_res)
elif type(op_res) == datetime.datetime:
value_left = str(op_res)
elif op_res.is_integer():
value_left = str(op_res)
else:
op_res = round(op_res, 10)
value_left = str(op_res)
else:
if op_str in self._operators:
value_left = EvalResult.unwrap_str_literal(str(value_left))
value_right = EvalResult.unwrap_str_literal(str(value_right))
op_res = self._operators[op_str](value_left, value_right)
value_left = op_res
else:
value_left = XLMInterpreter.convert_ptree_to_str(parse_tree_root)
left_arg_eval_res.status = EvalStatus.PartialEvaluation
text_left = value_left
else:
left_arg_eval_res.status = EvalStatus.PartialEvaluation
text_left = '{}{}{}'.format(text_left, op_str, text_right)
return_val = text_left
else:
if text_left is None:
left_arg = parse_tree_root.children[index]
left_arg_eval_res = self.evaluate_parse_tree(current_cell, left_arg, interactive)
text_left = left_arg_eval_res.get_text(unwrap=True)
if isinstance(left_arg_eval_res.value, Cell):
value_left = left_arg_eval_res.value.value
else:
value_left = left_arg_eval_res.value
if isinstance(value_left, str):
if value_left.lower() == 'true':
value_left = 1
elif value_left.lower() == 'false':
value_left = 0
if concat_status == EvalStatus.PartialEvaluation and left_arg_eval_res.status == EvalStatus.FullEvaluation:
left_arg_eval_res.status = concat_status
result = EvalResult(next_cell, left_arg_eval_res.status, return_val, EvalResult.wrap_str_literal(text_left))
elif parse_tree_root.data == 'final':
arg = parse_tree_root.children[1]
result = self.evaluate_parse_tree(current_cell, arg, interactive)
else:
status = EvalStatus.FullEvaluation
for child_node in parse_tree_root.children:
if child_node is not None:
child_eval_result = self.evaluate_parse_tree(current_cell, child_node, interactive)
if child_eval_result.status != EvalStatus.FullEvaluation:
status = child_eval_result.status
result = EvalResult(child_eval_result.next_cell, status, child_eval_result.value, child_eval_result.text)
result.output_level = child_eval_result.output_level
return result
def evaluate_cell(self, current_cell, interactive, parse_tree_root):
sheet_name, col, row = self.get_cell_addr(current_cell, parse_tree_root)
return_val = ''
text = ''
status = EvalStatus.PartialEvaluation
if sheet_name is not None:
cell_addr = col + str(row)
if sheet_name in self.xlm_wrapper.get_macrosheets():
sheet = self.xlm_wrapper.get_macrosheets()[sheet_name]
else:
sheet = self.xlm_wrapper.get_worksheets()[sheet_name]
if cell_addr not in sheet.cells and (sheet_name, cell_addr) in self.cell_with_unsuccessfull_set:
if interactive:
self.invoke_interpreter = True
if self.first_unknown_cell is None:
self.first_unknown_cell = cell_addr
if cell_addr in sheet.cells:
cell = sheet.cells[cell_addr]
if cell.formula is not None and cell.formula != cell.value:
try:
parse_tree = self.xlm_parser.parse(cell.formula)
eval_result = self.evaluate_parse_tree(cell, parse_tree, False)
return_val = eval_result.value
text = eval_result.get_text()
status = eval_result.status
except:
return_val = cell.formula
text = EvalResult.wrap_str_literal(cell.formula)
status = EvalStatus.FullEvaluation
elif cell.value is not None:
text = EvalResult.wrap_str_literal(cell.value, must_wrap=True)
return_val = text
status = EvalStatus.FullEvaluation
else:
text = "{}".format(cell_addr)
else:
if (sheet_name, cell_addr) in self.cell_with_unsuccessfull_set:
text = "{}".format(cell_addr)
else:
text = ''
status = EvalStatus.FullEvaluation
else:
text = XLMInterpreter.convert_ptree_to_str(parse_tree_root)
return EvalResult(None, status, return_val, text)
def evaluate_range(self, current_cell, interactive, parse_tree_root):
status = EvalStatus.PartialEvaluation
if len(parse_tree_root.children) >= 3:
start_address = self.get_cell_addr(current_cell, parse_tree_root.children[0])
end_address = self.get_cell_addr(current_cell, parse_tree_root.children[2])
selected = None
if len(parse_tree_root.children) == 5:
selected = self.get_cell_addr(current_cell, parse_tree_root.children[4])
self.selected_range = (start_address, end_address, selected)
status = EvalStatus.FullEvaluation
text = XLMInterpreter.convert_ptree_to_str(parse_tree_root)
return_val = text
return EvalResult(None, status, return_val, text)
def evaluate_array(self, current_cell, interactive, parse_tree_root):
status = EvalStatus.PartialEvaluation
array_elements = []
for index, array_elm in enumerate(parse_tree_root.children):
# skip semicolon (;)
if index % 2 == 1:
continue
if array_elm.type == 'NUMBER':
array_elements.append(float(array_elm))
else:
array_elements.append(str(array_elm))
text = str(array_elements)
return_val = array_elements
return EvalResult(None, status, return_val, text)
def has_loop(self, path, length=10):
if len(path) < length * 2:
return False
else:
result = False
start_index = len(path) - length
for j in range(0, start_index - length):
matched = True
k = j
while start_index + k - j < len(path):
if path[k] != path[start_index + k - j]:
matched = False
break
k += 1
if matched:
result = True
break
return result
regex_string = r'\"([^\"]|\"\")*\"'
detect_string = re.compile(regex_string, flags=re.MULTILINE)
def extract_strings(self, string):
result = []
matches = XLMInterpreter.detect_string.finditer(string)
for matchNum, match in enumerate(matches, start=1):
result.append(match.string[match.start(0):match.end(0)])
return result
def deobfuscate_macro(self, interactive, start_point="", timeout=0, silent_mode=False):
result = []
self._start_timestamp = time.time()
self.auto_labels = self.xlm_wrapper.get_defined_name('auto_open', full_match=False)
self.auto_labels.extend(self.xlm_wrapper.get_defined_name('auto_close', full_match=False))
if len(self.auto_labels) == 0:
if len(start_point) > 0:
self.auto_labels = [('auto_open', start_point)]
if self.auto_labels is not None and len(self.auto_labels) > 0:
macros = self.xlm_wrapper.get_macrosheets()
continue_emulation = True
for auto_open_label in self.auto_labels:
if not continue_emulation:
break
try:
sheet_name, col, row = Cell.parse_cell_addr(auto_open_label[1])
if sheet_name in macros:
current_cell = self.get_formula_cell(macros[sheet_name], col, row)
self._branch_stack = [(current_cell, current_cell.formula, macros[sheet_name].cells, 0, '')]
observed_cells = []
while len(self._branch_stack) > 0:
if not continue_emulation:
break
current_cell, formula, saved_cells, indent_level, desc = self._branch_stack.pop()
macros[current_cell.sheet.name].cells = saved_cells
self._indent_level = indent_level
stack_record = True
while current_cell is not None:
if not continue_emulation:
break
if type(formula) is str:
replace_op = getattr(self.xlm_wrapper, "replace_nonprintable_chars", None)
if callable(replace_op):
formula = replace_op(formula, '_')
if formula not in self._formula_cache:
parse_tree = self.xlm_parser.parse(formula)
self._formula_cache[formula] = parse_tree
else:
parse_tree = self._formula_cache[formula]
else:
parse_tree = formula
if stack_record:
previous_indent = self._indent_level - 1 if self._indent_level > 0 else 0
else:
previous_indent = self._indent_level
evaluation_result = self.evaluate_parse_tree(current_cell, parse_tree, interactive)
if self._remove_current_formula_from_cache:
self._remove_current_formula_from_cache = False
if formula in self._formula_cache:
del (self._formula_cache[formula])
if len(self._while_stack) == 0 and evaluation_result.text != 'NEXT':
observed_cells.append(current_cell.get_local_address())
if self.has_loop(observed_cells):
break
if self.invoke_interpreter:
self.invoke_interpreter = False
self.first_unknown_cell = None
continue
if evaluation_result.value is not None:
current_cell.value = str(evaluation_result.value)
if evaluation_result.next_cell is None and \
(evaluation_result.status == EvalStatus.FullEvaluation or
evaluation_result.status == EvalStatus.PartialEvaluation or
evaluation_result.status == EvalStatus.NotImplemented or
evaluation_result.status == EvalStatus.IGNORED):
evaluation_result.next_cell = self.get_formula_cell(current_cell.sheet,
current_cell.column,
str(int(current_cell.row) + 1))
if stack_record:
evaluation_result.text = (
desc + ' ' + evaluation_result.get_text(unwrap=False)).strip()
if self._indent_current_line:
previous_indent = self._indent_level
self._indent_current_line = False
if evaluation_result.status != EvalStatus.IGNORED:
if self.output_level >= 3 and evaluation_result.output_level == 2:
strings = self.extract_strings(evaluation_result.get_text(unwrap=True))
if strings:
yield (
current_cell, evaluation_result.status,
'\n'.join(strings),
previous_indent)
elif evaluation_result.output_level >= self.output_level:
yield (
current_cell, evaluation_result.status,
evaluation_result.get_text(unwrap=False),
previous_indent)
if timeout > 0 and time.time() - self._start_timestamp > timeout:
continue_emulation = False
if evaluation_result.next_cell is not None:
current_cell = evaluation_result.next_cell
else:
break
formula = current_cell.formula
stack_record = False
except Exception:
pass
Classes
class XLMInterpreter (xlm_wrapper, output_level=0)-
Expand source code Browse git
class XLMInterpreter: def __init__(self, xlm_wrapper, output_level=0): self.xlm_wrapper = xlm_wrapper self._formula_cache = {} self.cell_addr_regex_str = r"((?P<sheetname>[^\s]+?|'.+?')!)?\$?(?P<column>[a-zA-Z]+)\$?(?P<row>\d+)" self.cell_addr_regex = re.compile(self.cell_addr_regex_str) self.xlm_parser = self.get_parser() self.defined_names = self.xlm_wrapper.get_defined_names() self.auto_labels = None self._branch_stack = [] self._while_stack = [] self._for_iterators = {} self._function_call_stack = [] self._memory = [] self._files = {} self._registered_functions = {} self._expr_rule_names = ['expression', 'concat_expression', 'additive_expression', 'multiplicative_expression'] self._operators = {'+': operator.add, '-': operator.sub, '*': operator.mul, '/': operator.truediv, '>': operator.gt, '<': operator.lt, '<>': operator.ne, '=': operator.eq, '>=': operator.ge, '<=': operator.le} self._indent_level = 0 self._indent_current_line = False self.day_of_month = None self.invoke_interpreter = False self.first_unknown_cell = None self.cell_with_unsuccessfull_set = set() self.selected_range = None self.active_cell = None self.ignore_processing = False self.next_count = 0 self.char_error_count = 0 self.output_level = output_level self._remove_current_formula_from_cache = False self._start_timestamp = time.time() self._iserror_count = 0 self._iserror_loc = None self._iserror_val = False self._now_count = 0 self._now_step = 2 self._handlers = { # methods 'END.IF': self.end_if_handler, 'FORMULA.FILL': self.formula_fill_handler, 'FORMULA.ARRAY': self.formula_array_handler, 'GET.CELL': self.get_cell_handler, 'GET.DOCUMENT': self.get_document_handler, 'GET.WINDOW': self.get_window_handler, 'GET.WORKSPACE': self.get_workspace_handler, 'ON.TIME': self.on_time_handler, 'SET.VALUE': self.set_value_handler, 'SET.NAME': self.set_name_handler, 'ACTIVE.CELL': self.active_cell_handler, 'APP.MAXIMIZE': self.app_maximize_handler, # functions 'ABS': self.abs_handler, 'ABSREF': self.absref_handler, 'ADDRESS': self.address_handler, 'AND': self.and_handler, 'CALL': self.call_handler, 'CHAR': self.char_handler, 'CLOSE': self.halt_handler, 'CODE': self.code_handler, 'CONCATENATE': self.concatenate_handler, 'COUNTA': self.counta_handler, 'COUNT': self.count_handler, 'DAY': self.day_handler, 'DEFINE.NAME': self.define_name_handler, 'DIRECTORY': self.directory_handler, 'ERROR': self.error_handler, 'FILES': self.files_handler, 'FORMULA': self.formula_handler, 'FOPEN': self.fopen_handler, 'FOR.CELL': self.forcell_handler, 'FSIZE': self.fsize_handler, 'FWRITE': self.fwrite_handler, 'FWRITELN': self.fwriteln_handler, 'GOTO': self.goto_handler, 'HALT': self.halt_handler, 'INDEX': self.index_handler, 'HLOOKUP': self.hlookup_handler, 'IF': self.if_handler, 'INDIRECT': self.indirect_handler, 'INT': self.int_handler, 'ISERROR': self.iserror_handler, 'ISNUMBER': self.is_number_handler, 'LEN': self.len_handler, 'MAX': self.max_handler, 'MIN': self.min_handler, 'MOD': self.mod_handler, 'MID': self.mid_handler, 'SQRT': self.sqrt_handler, 'NEXT': self.next_handler, 'NOT': self.not_handler, 'NOW': self.now_handler, 'OR': self.or_handler, 'OFFSET': self.offset_handler, 'PRODUCT': self.product_handler, 'QUOTIENT': self.quotient_handler, 'RANDBETWEEN': self.randbetween_handler, 'REGISTER': self.register_handler, 'REGISTER.ID': self.registerid_handler, 'RETURN': self.return_handler, 'ROUND': self.round_handler, 'ROUNDUP': self.roundup_handler, 'RUN': self.run_handler, 'ROWS': self.rows_handler, 'SEARCH': self.search_handler, 'SELECT': self.select_handler, 'SUM': self.sum_handler, 'T': self.t_handler, 'TEXT': self.text_handler, 'TRUNC': self.trunc_handler, 'VALUE': self.value_handler, 'WHILE': self.while_handler, # Windows API 'Kernel32.VirtualAlloc': self.VirtualAlloc_handler, 'Kernel32.WriteProcessMemory': self.WriteProcessMemory_handler, 'Kernel32.RtlCopyMemory': self.RtlCopyMemory_handler, # Future fuctions '_xlfn.ARABIC': self.arabic_hander, } MAX_ISERROR_LOOPCOUNT = 10 jump_functions = ('GOTO', 'RUN') important_functions = ('CALL', 'FOPEN', 'FWRITE', 'FREAD', 'REGISTER', 'IF', 'WHILE', 'HALT', 'CLOSE', "NEXT") important_methods = ('SET.VALUE', 'FILE.DELETE', 'WORKBOOK.HIDE') unicode_to_latin1_map = { 8364: 128, 129: 129, 8218: 130, 402: 131, 8222: 132, 8230: 133, 8224: 134, 8225: 135, 710: 136, 8240: 137, 352: 138, 8249: 139, 338: 140, 141: 141, 381: 142, 143: 143, 144: 144, 8216: 145, 8217: 146, 8220: 147, 8221: 148, 8226: 149, 8211: 150, 8212: 151, 732: 152, 8482: 153, 353: 154, 8250: 155, 339: 156, 157: 157, 382: 158, 376: 159 } def __copy__(self): result = XLMInterpreter(self.xlm_wrapper) result.auto_labels = self.auto_labels result._formula_cache = self._formula_cache return result @staticmethod def is_float(text): try: float(text) return True except (ValueError, TypeError): return False @staticmethod def is_int(text): try: int(text) return True except (ValueError, TypeError): return False @staticmethod def is_bool(text): try: _strtobool(text) return True except (ValueError, TypeError, AttributeError): return False def convert_float(self, text): result = None text = text.lower() if text == 'false': result = 0 elif text == 'true': result = 1 else: result = float(text) return result def get_parser(self): left_bracket = self.xlm_wrapper.get_xl_international_char( XlApplicationInternational.xlLeftBracket) list_separator = self.xlm_wrapper.get_xl_international_char( XlApplicationInternational.xlListSeparator) right_bracket = self.xlm_wrapper.get_xl_international_char( XlApplicationInternational.xlRightBracket) return XLMParser(left_bracket, list_separator, right_bracket) def get_formula_cell(self, macrosheet, col, row): result_cell = None not_found = False row = int(row) current_row = row current_addr = col + str(current_row) while current_addr not in macrosheet.cells or \ macrosheet.cells[current_addr].formula is None: if (current_row - row) < 10000: current_row += 1 else: not_found = True break current_addr = col + str(current_row) if not_found is False: result_cell = macrosheet.cells[current_addr] return result_cell def get_range_parts(self, parse_tree): if isinstance(parse_tree, Tree) and parse_tree.data == 'range': return parse_tree.children[0], parse_tree.children[-1] else: return None, None def get_cell_addr(self, current_cell, cell_parse_tree): res_sheet = res_col = res_row = None if type(cell_parse_tree) is Token: names = self.xlm_wrapper.get_defined_names() label = cell_parse_tree.value.lower() if label in names: name_val = names[label] if isinstance(name_val, Tree): # example: 6a8045bc617df5f2b8f9325ed291ef05ac027144f1fda84e78d5084d26847902 res_sheet, res_col, res_row = self.get_cell_addr(current_cell, name_val) else: res_sheet, res_col, res_row = Cell.parse_cell_addr(name_val) elif label.strip('"') in names: res_sheet, res_col, res_row = Cell.parse_cell_addr(names[label.strip('"')]) else: if len(label) > 1 and label.startswith('"') and label.endswith('"'): label = label.strip('"') root_parse_tree = self.xlm_parser.parse('=' + label) res_sheet, res_col, res_row = self.get_cell_addr(current_cell, root_parse_tree.children[0]) else: if cell_parse_tree.data == 'defined_name': label = '{}'.format(cell_parse_tree.children[2]) formula_str = self.xlm_wrapper.get_defined_name(label) parsed_tree = self.xlm_parser.parse('=' + formula_str) if isinstance(parsed_tree.children[0], Tree) and parsed_tree.children[0].data == 'range': start_cell, end_cell = self.get_range_parts(parsed_tree.children[0]) cell = start_cell.children[0] else: cell = parsed_tree.children[0].children[0] else: cell = cell_parse_tree.children[0] if cell.data == 'a1_notation_cell': if len(cell.children) == 2: cell_addr = "'{}'!{}".format(cell.children[0], cell.children[1]) else: cell_addr = cell.children[0] res_sheet, res_col, res_row = Cell.parse_cell_addr(cell_addr) if res_sheet is None and res_col is not None: res_sheet = current_cell.sheet.name elif cell.data == 'r1c1_notation_cell': current_col = Cell.convert_to_column_index(current_cell.column) current_row = int(current_cell.row) for current_child in cell.children: if current_child.type == 'NAME': res_sheet = current_child.value elif self.is_float(current_child.value): val = int(float(current_child.value)) if last_seen == 'r': res_row = val else: res_col = val elif current_child.value.startswith('['): val = int(current_child.value[1:-1]) if last_seen == 'r': res_row = current_row + val else: res_col = current_col + val elif current_child.lower() == 'r': last_seen = 'r' res_row = current_row elif current_child.lower() == 'c': last_seen = 'c' res_col = current_col else: raise Exception('Cell addresss, Syntax Error') if res_sheet is None: res_sheet = current_cell.sheet.name res_row = str(res_row) res_col = Cell.convert_to_column_name(res_col) else: raise Exception('Cell addresss, Syntax Error') return res_sheet, res_col, res_row def get_cell(self, sheet_name, col, row): result = None sheets = self.xlm_wrapper.get_macrosheets() if sheet_name in sheets: sheet = sheets[sheet_name] addr = col + str(row) if addr in sheet.cells: result = sheet.cells[addr] else: sheets = self.xlm_wrapper.get_worksheets() if sheet_name in sheets: sheet = sheets[sheet_name] addr = col + str(row) if addr in sheet.cells: result = sheet.cells[addr] return result def get_worksheet_cell(self, sheet_name, col, row): result = None sheets = self.xlm_wrapper.get_worksheets() if sheet_name in sheets: sheet = sheets[sheet_name] addr = col + str(row) if addr in sheet.cells: result = sheet.cells[addr] return result def set_cell(self, sheet_name, col, row, text, set_value_only=False): sheets = self.xlm_wrapper.get_macrosheets() if sheet_name in sheets: sheet = sheets[sheet_name] addr = col + str(row) if addr not in sheet.cells: new_cell = Cell() new_cell.column = col new_cell.row = row new_cell.sheet = sheet sheet.cells[addr] = new_cell cell = sheet.cells[addr] text = EvalResult.unwrap_str_literal(text) if not set_value_only: if text.startswith('='): cell.formula = text else: cell.formula = None cell.value = text @staticmethod def convert_ptree_to_str(parse_tree_root): if type(parse_tree_root) == Token: return str(parse_tree_root) else: result = '' for child in parse_tree_root.children: result += XLMInterpreter.convert_ptree_to_str(child) return result def get_window(self, number): return _WINDOW_DEFAULTS.get(number) def get_workspace(self, number): return _WORKSPACE_DEFAULTS.get(number) def get_default_cell_info(self, number): return _CELL_DEFAULTS.get(number) def evaluate_formula(self, current_cell, name, arguments, interactive, destination_arg=1, set_value_only=False): # hash: fa391403aa028fa7b42a9f3491908f6f25414c35bfd104f8cf186220fb3b4f83" --> =FORMULA() if isinstance(arguments[0], list) and len(arguments[0]) == 0: return EvalResult(None, EvalStatus.FullEvaluation, False, "{}()".format(name)) source, destination = (arguments[0], arguments[1]) if destination_arg == 1 else (arguments[1], arguments[0]) src_eval_result = self.evaluate_parse_tree(current_cell, source, interactive) if isinstance(destination, Token): # TODO: get_defined_name must return a list; currently it returns list or one item destination = self.xlm_wrapper.get_defined_name(destination) if isinstance(destination, list): destination = [] if not destination else destination[0] if(isinstance(destination, str)): destination = self.xlm_parser.parse('=' + destination).children[0] if isinstance(destination, Tree): if destination.data == 'defined_name' or destination.data == 'name': defined_name_formula = self.xlm_wrapper.get_defined_name(destination.children[2]) if isinstance(defined_name_formula, Tree): destination = defined_name_formula else: destination = self.xlm_parser.parse('=' + defined_name_formula).children[0] if destination.data == 'concat_expression' or destination.data == 'function_call': res = self.evaluate_parse_tree(current_cell, destination, interactive) if isinstance(res.value, tuple) and len(res.value) == 3: destination_str = "'{}'!{}{}".format(res.value[0], res.value[1], res.value[2]) dst_start_sheet, dst_start_col, dst_start_row = res.value else: destination_str = res.text dst_start_sheet, dst_start_col, dst_start_row = Cell.parse_cell_addr(destination_str) dst_end_sheet, dst_end_col, dst_end_row = dst_start_sheet, dst_start_col, dst_start_row else: if destination.data == 'range': dst_start_sheet, dst_start_col, dst_start_row = self.get_cell_addr(current_cell, destination.children[0]) dst_end_sheet, dst_end_col, dst_end_row = self.get_cell_addr(current_cell, destination.children[2]) else: dst_start_sheet, dst_start_col, dst_start_row = self.get_cell_addr(current_cell, destination) dst_end_sheet, dst_end_col, dst_end_row = dst_start_sheet, dst_start_col, dst_start_row destination_str = XLMInterpreter.convert_ptree_to_str(destination) text = src_eval_result.get_text(unwrap=True) if src_eval_result.status == EvalStatus.FullEvaluation: for row in range(int(dst_start_row), int(dst_end_row) + 1): for col in range(Cell.convert_to_column_index(dst_start_col), Cell.convert_to_column_index(dst_end_col) + 1): if ( dst_start_sheet, Cell.convert_to_column_name(col) + str(row)) in self.cell_with_unsuccessfull_set: self.cell_with_unsuccessfull_set.remove((dst_start_sheet, Cell.convert_to_column_name(col) + str(row))) self.set_cell(dst_start_sheet, Cell.convert_to_column_name(col), str(row), str(src_eval_result.value), set_value_only) else: for row in range(int(dst_start_row), int(dst_end_row) + 1): for col in range(Cell.convert_to_column_index(dst_start_col), Cell.convert_to_column_index(dst_end_col) + 1): self.cell_with_unsuccessfull_set.add((dst_start_sheet, Cell.convert_to_column_name(col) + str(row))) if destination_arg == 1: text = "{}({},{})".format(name, src_eval_result.get_text(), destination_str) else: text = "{}({},{})".format(name, destination_str, src_eval_result.get_text()) return_val = 0 return EvalResult(None, src_eval_result.status, return_val, text) def evaluate_argument_list(self, current_cell, name, arguments): args_str = '' for argument in arguments: if type(argument) is Token or type(argument) is Tree: arg_eval_Result = self.evaluate_parse_tree(current_cell, argument, False) args_str += arg_eval_Result.get_text() + ',' args_str = args_str.strip(',') return_val = text = '={}({})'.format(name, args_str) status = EvalStatus.PartialEvaluation return EvalResult(None, status, return_val, text) def evaluate_function(self, current_cell, parse_tree_root, interactive): # function name can be a string literal (double quoted or unqouted), and Tree (defined name, cell, function_call) function_name = parse_tree_root.children[0] function_name_literal = EvalResult.unwrap_str_literal(function_name) # OFFSET()() if isinstance(function_name, Tree) and function_name.data == 'function_call': func_eval_result = self.evaluate_parse_tree(current_cell, function_name, False) if func_eval_result.status != EvalStatus.FullEvaluation: return EvalResult(func_eval_result.next_cell, func_eval_result.status, 0, XLMInterpreter.convert_ptree_to_str(parse_tree_root)) else: func_eval_result.text = XLMInterpreter.convert_ptree_to_str(parse_tree_root) return func_eval_result # handle alias name for a function (REGISTER) # c45ed3a0ce5df27ac29e0fab99dc4d462f61a0d0c025e9161ced3b2c913d57d8 if function_name_literal in self._registered_functions: parse_tree_root.children[0] = parse_tree_root.children[0].update( None, self._registered_functions[function_name_literal]['name']) return self.evaluate_function(current_cell, parse_tree_root, interactive) # cell_function_call if isinstance(function_name, Tree) and function_name.data == 'cell': self._function_call_stack.append(current_cell) return self.goto_handler([function_name], current_cell, interactive, parse_tree_root) # test() if function_name_literal.lower() in self.defined_names: try: ref_parsed = self.xlm_parser.parse('=' + self.defined_names[function_name_literal.lower()]) if isinstance(ref_parsed.children[0], Tree) and ref_parsed.children[0].data == 'cell': function_name = ref_parsed.children[0] else: raise Exception except: function_name = self.defined_names[function_name_literal.lower()] # x!test() if isinstance(function_name, Tree) and function_name.data == 'defined_name': function_lable = function_name.children[-1].value if function_lable.lower() in self.defined_names: try: ref_parsed = self.xlm_parser.parse('=' + self.defined_names[function_lable.lower()]) if isinstance(ref_parsed.children[0], Tree) and ref_parsed.children[0].data == 'cell': function_name = ref_parsed.children[0] else: raise Exception except: function_name = self.defined_names[function_name_literal.lower()] # cell_function_call if isinstance(function_name, Tree) and function_name.data == 'cell': self._function_call_stack.append(current_cell) return self.goto_handler([function_name], current_cell, interactive, parse_tree_root) if self.ignore_processing and function_name_literal != 'NEXT': return EvalResult(None, EvalStatus.IGNORED, 0, '') arguments = [] for i in parse_tree_root.children[2].children: if type(i) is not Token: if len(i.children) > 0: arguments.append(i.children[0]) else: arguments.append(i.children) if function_name_literal in self._handlers: eval_result = self._handlers[function_name_literal](arguments, current_cell, interactive, parse_tree_root) else: eval_result = self.evaluate_argument_list(current_cell, function_name_literal, arguments) if function_name_literal in XLMInterpreter.jump_functions: eval_result.output_level = 0 elif function_name_literal in XLMInterpreter.important_functions: eval_result.output_level = 2 else: eval_result.output_level = 1 return eval_result # region Handlers def and_handler(self, arguments, current_cell, interactive, parse_tree_root): value = True status = EvalStatus.FullEvaluation for arg in arguments: arg_eval_result = self.evaluate_parse_tree(current_cell, arg, interactive) if arg_eval_result.status == EvalStatus.FullEvaluation: if EvalResult.unwrap_str_literal(str(arg_eval_result.value)).lower() != "true": value = False break else: status = EvalStatus.PartialEvaluation value = False break return EvalResult(None, status, value, str(value)) def or_handler(self, arguments, current_cell, interactive, parse_tree_root): value = False status = EvalStatus.FullEvaluation for arg in arguments: arg_eval_result = self.evaluate_parse_tree(current_cell, arg, interactive) if arg_eval_result.status == EvalStatus.FullEvaluation: if EvalResult.unwrap_str_literal(str(arg_eval_result.value)).lower() == "true": value = True break else: status = EvalStatus.PartialEvaluation break return EvalResult(None, status, value, str(value)) def hlookup_handler(self, arguments, current_cell, interactive, parse_tree_root): status = EvalStatus.FullEvaluation value = "" arg_eval_result1 = self.evaluate_parse_tree(current_cell, arguments[0], interactive) arg_eval_result2 = self.evaluate_parse_tree(current_cell, arguments[1], interactive) arg_eval_result3 = self.evaluate_parse_tree(current_cell, arguments[2], interactive) arg_eval_result4 = self.evaluate_parse_tree(current_cell, arguments[3], interactive) regex = arg_eval_result1.text.strip('"') if regex == '*': regex = ".*" if arg_eval_result4.value == "FALSE": sheet_name, startcolumn, startrow, endcolumn, endrow = Cell.parse_range_addr(arg_eval_result2.text) status = EvalStatus.FullEvaluation start_col_index = Cell.convert_to_column_index(startcolumn) end_col_index = Cell.convert_to_column_index(endcolumn) start_row_index = int(startrow) + int(arg_eval_result3.value) - 1 end_row_index = int(endrow) for row in range(start_row_index, end_row_index + 1): for col in range(start_col_index, end_col_index + 1): if (sheet_name != None): cell = self.get_worksheet_cell(sheet_name, Cell.convert_to_column_name(col), str(row)) else: cell = self.get_cell(current_cell.sheet.name, Cell.convert_to_column_name(col), str(row)) if cell and re.match(regex, cell.value): return EvalResult(None, status, cell.value, str(cell.value)) else: status = EvalStatus.PartialEvaluation return EvalResult(None, status, value, str(value)) def not_handler(self, arguments, current_cell, interactive, parse_tree_root): value = True status = EvalStatus.FullEvaluation arg_eval_result = self.evaluate_parse_tree(current_cell, arguments[0], interactive) if arg_eval_result.status == EvalStatus.FullEvaluation: if EvalResult.unwrap_str_literal(str(arg_eval_result.value)).lower() == "true": value = False else: status = EvalStatus.PartialEvaluation return EvalResult(None, status, value, str(value)) def code_handler(self, arguments, current_cell, interactive, parse_tree_root): status = EvalStatus.FullEvaluation value = 0 arg_eval_result = self.evaluate_parse_tree(current_cell, arguments[0], interactive) if arg_eval_result.status == EvalStatus.FullEvaluation: if arg_eval_result.text != '': value = ord(arg_eval_result.text[0]) if value > 256 and value in XLMInterpreter.unicode_to_latin1_map: value = XLMInterpreter.unicode_to_latin1_map[value] else: status = EvalStatus.PartialEvaluation return EvalResult(None, status, value, str(value)) def sum_handler(self, arguments, current_cell, interactive, parse_tree_root): status = EvalStatus.FullEvaluation value = 0 it = 0 for arg in arguments: arg_eval_result = self.evaluate_parse_tree(current_cell, arguments[it], interactive) value = value + float(arg_eval_result.value) status = arg_eval_result.status it = it + 1 return EvalResult(None, status, value, str(value)) def randbetween_handler(self, arguments, current_cell, interactive, parse_tree_root): arg_eval_result1 = self.evaluate_parse_tree(current_cell, arguments[0], interactive) arg_eval_result2 = self.evaluate_parse_tree(current_cell, arguments[1], interactive) value = 0 # Initial implementation for integer if arg_eval_result1.status == EvalStatus.FullEvaluation and arg_eval_result2.status == EvalStatus.FullEvaluation: status = EvalStatus.FullEvaluation value = random.randint(int(float(arg_eval_result1.value)), int(float(arg_eval_result2.value))) return EvalResult(None, status, value, str(value)) def text_handler(self, arguments, current_cell, interactive, parse_tree_root): arg_eval_result1 = self.evaluate_parse_tree(current_cell, arguments[0], interactive) arg_eval_result2 = self.evaluate_parse_tree(current_cell, arguments[1], interactive) value = 0 status = EvalStatus.PartialEvaluation # Initial implementation for integer if arg_eval_result1.status == EvalStatus.FullEvaluation and int(arg_eval_result2.text.strip('\"')) == 0: status = EvalStatus.FullEvaluation value = int(arg_eval_result1.value) return EvalResult(None, status, value, str(value)) def active_cell_handler(self, arguments, current_cell, interactive, parse_tree_root): status = EvalStatus.PartialEvaluation if self.active_cell: if self.active_cell.formula: parse_tree = self.xlm_parser.parse(self.active_cell.formula) eval_res = self.evaluate_parse_tree(current_cell, parse_tree, interactive) val = eval_res.value status = eval_res.status else: val = self.active_cell.value status = EvalStatus.FullEvaluation return_val = val text = str(return_val) else: text = XLMInterpreter.convert_ptree_to_str(parse_tree_root) return_val = text return EvalResult(None, status, return_val, text) def get_cell_handler(self, arguments, current_cell, interactive, parse_tree_root): if len(arguments) == 2: arg1_eval_result = self.evaluate_parse_tree(current_cell, arguments[0], interactive) dst_sheet, dst_col, dst_row = self.get_cell_addr(current_cell, arguments[1]) type_id = arg1_eval_result.value if self.is_float(type_id): type_id = int(float(type_id)) if dst_sheet is None: dst_sheet = current_cell.sheet.name status = EvalStatus.PartialEvaluation if arg1_eval_result.status == EvalStatus.FullEvaluation: data, not_exist, not_implemented = self.xlm_wrapper.get_cell_info(dst_sheet, dst_col, dst_row, type_id) if not_exist and 1 == 2: return_val = self.get_default_cell_info(type_id) text = str(return_val) status = EvalStatus.FullEvaluation elif not_implemented: text = XLMInterpreter.convert_ptree_to_str(parse_tree_root) return_val = '' else: text = str(data) if data is not None else None return_val = data status = EvalStatus.FullEvaluation else: text = XLMInterpreter.convert_ptree_to_str(parse_tree_root) return_val = '' status = EvalStatus.PartialEvaluation return EvalResult(None, status, return_val, text) def set_name_handler(self, arguments, current_cell, interactive, parse_tree_root): label = EvalResult.unwrap_str_literal(XLMInterpreter.convert_ptree_to_str(arguments[0])).lower() if isinstance(arguments[1], Tree) and arguments[1].data == 'cell': arg2_text = XLMInterpreter.convert_ptree_to_str(arguments[1]) names = self.xlm_wrapper.get_defined_names() names[label] = arguments[1] text = 'SET.NAME({},{})'.format(label, arg2_text) return_val = 0 status = EvalStatus.FullEvaluation else: arg2_eval_result = self.evaluate_parse_tree(current_cell, arguments[1], interactive) if arg2_eval_result.status is EvalStatus.FullEvaluation: arg2_text = arg2_eval_result.get_text(unwrap=True) names = self.xlm_wrapper.get_defined_names() if isinstance(arg2_eval_result.value, Cell): names[label] = arg2_eval_result.value else: names[label] = arg2_text text = 'SET.NAME({},{})'.format(label, arg2_text) return_val = 0 status = EvalStatus.FullEvaluation else: return_val = text = XLMInterpreter.convert_ptree_to_str(parse_tree_root) status = arg2_eval_result.status return EvalResult(None, status, return_val, text) def end_if_handler(self, arguments, current_cell, interactive, parse_tree_root): self._indent_level -= 1 self._indent_current_line = True status = EvalStatus.FullEvaluation return EvalResult(None, status, 'END.IF', 'END.IF') def get_workspace_handler(self, arguments, current_cell, interactive, parse_tree_root): status = EvalStatus.PartialEvaluation if len(arguments) == 1: arg1_eval_Result = self.evaluate_parse_tree(current_cell, arguments[0], interactive) if arg1_eval_Result.status == EvalStatus.FullEvaluation and self.is_float(arg1_eval_Result.get_text()): workspace_param = self.get_workspace(int(float(arg1_eval_Result.get_text()))) # current_cell.value = workspace_param text = 'GET.WORKSPACE({})'.format(arg1_eval_Result.get_text()) return_val = workspace_param status = EvalStatus.FullEvaluation next_cell = None if status == EvalStatus.PartialEvaluation: return_val = text = XLMInterpreter.convert_ptree_to_str(parse_tree_root) return EvalResult(None, status, return_val, text) def get_window_handler(self, arguments, current_cell, interactive, parse_tree_root): status = EvalStatus.Error if len(arguments) == 1: arg_eval_result = self.evaluate_parse_tree(current_cell, arguments[0], interactive) if arg_eval_result.status == EvalStatus.FullEvaluation and self.is_float(arg_eval_result.get_text()): window_param = self.get_window(int(float(arg_eval_result.get_text()))) # current_cell.value = window_param text = window_param # XLMInterpreter.convert_ptree_to_str(parse_tree_root) return_val = window_param # Overwrites to take actual values from the workbook instead of default config if int(float(arg_eval_result.get_text())) == 1 or int(float(arg_eval_result.get_text())) == 30: return_val = "[" + self.xlm_wrapper.get_workbook_name() + "]" + current_cell.sheet.name status = EvalStatus.FullEvaluation status = EvalStatus.FullEvaluation else: return_val = text = 'GET.WINDOW({})'.format(arg_eval_result.get_text()) status = arg_eval_result.status if status == EvalStatus.Error: return_val = text = XLMInterpreter.convert_ptree_to_str(parse_tree_root) return EvalResult(None, status, return_val, text) def get_document_handler(self, arguments, current_cell, interactive, parse_tree_root): status = EvalStatus.Error arg_eval_result = self.evaluate_parse_tree(current_cell, arguments[0], interactive) return_val = "" # Static implementation if self.is_int(arg_eval_result.value): status = EvalStatus.PartialEvaluation if int(arg_eval_result.value) == 76: return_val = "[" + self.xlm_wrapper.get_workbook_name() + "]" + current_cell.sheet.name status = EvalStatus.FullEvaluation elif int(arg_eval_result.value) == 88: return_val = self.xlm_wrapper.get_workbook_name() status = EvalStatus.FullEvaluation text = return_val return EvalResult(None, status, return_val, text) def on_time_handler(self, arguments, current_cell, interactive, parse_tree_root): status = EvalStatus.Error if len(arguments) == 2: arg1_eval_result = self.evaluate_parse_tree(current_cell, arguments[0], interactive) next_sheet, next_col, next_row = self.get_cell_addr(current_cell, arguments[1]) sheets = self.xlm_wrapper.get_macrosheets() if next_sheet in sheets: next_cell = self.get_formula_cell(sheets[next_sheet], next_col, next_row) text = 'ON.TIME({},{})'.format(arg1_eval_result.get_text(), str(next_cell)) status = EvalStatus.FullEvaluation return_val = 0 return_val = text = XLMInterpreter.convert_ptree_to_str(parse_tree_root) if status == EvalStatus.Error: next_cell = None return EvalResult(next_cell, status, return_val, text) def app_maximize_handler(self, arguments, current_cell, interactive, parse_tree_root): status = EvalStatus.FullEvaluation return_val = True text = str(return_val) return EvalResult(None, status, return_val, text) def concatenate_handler(self, arguments, current_cell, interactive, parse_tree_root): text = '' for arg in arguments: arg_eval_result = self.evaluate_parse_tree(current_cell, arg, interactive) text += arg_eval_result.get_text(unwrap=True) return_val = text text = EvalResult.wrap_str_literal(text) status = EvalStatus.FullEvaluation return EvalResult(None, status, return_val, text) def day_handler(self, arguments, current_cell, interactive, parse_tree_root): if self.day_of_month is None: arg1_eval_result = self.evaluate_parse_tree(current_cell, arguments[0], interactive) if arg1_eval_result.status == EvalStatus.FullEvaluation: if type(arg1_eval_result.value) is datetime.datetime: # # text = str(arg1_eval_result.value.day) # return_val = text # status = EvalStatus.FullEvaluation return_val, status, text = self.guess_day() elif self.is_float(arg1_eval_result.value): text = 'DAY(Serial Date)' status = EvalStatus.NotImplemented else: text = XLMInterpreter.convert_ptree_to_str(parse_tree_root) status = arg1_eval_result.status else: text = str(self.day_of_month) return_val = text status = EvalStatus.FullEvaluation return EvalResult(None, status, return_val, text) def guess_day(self): xlm = self min = 1 best_day = 0 for day in range(1, 32): xlm.char_error_count = 0 non_printable_ascii = 0 total_count = 0 xlm = copy.copy(xlm) xlm.day_of_month = day try: for index, step in enumerate(xlm.deobfuscate_macro(False, silent_mode=True)): for char in step[2]: if not (32 <= ord(char) <= 128): non_printable_ascii += 1 total_count += len(step[2]) if index > 10 and ((non_printable_ascii + xlm.char_error_count) / total_count) > min: break if total_count != 0 and ((non_printable_ascii + xlm.char_error_count) / total_count) < min: min = ((non_printable_ascii + xlm.char_error_count) / total_count) best_day = day if min == 0: break except Exception as exp: pass self.day_of_month = best_day text = str(self.day_of_month) return_val = text status = EvalStatus.FullEvaluation return return_val, status, text #https://stackoverflow.com/questions/9574793/how-to-convert-a-python-datetime-datetime-to-excel-serial-date-number def excel_date(self, date1): temp = datetime.datetime(1899, 12, 30) # Note, not 31st Dec but 30th! delta = date1 - temp return float(delta.days) + (float(delta.seconds) / 86400) def now_handler(self, arguments, current_cell, interactive, parse_tree_root): return_val = text = self.excel_date(datetime.datetime.now() + datetime.timedelta(seconds=self._now_count * self._now_step)) self._now_count += 1 status = EvalStatus.FullEvaluation return EvalResult(None, status, return_val, text) def value_handler(self, arguments, current_cell, interactive, parse_tree_root): return_val_result = self.evaluate_parse_tree(current_cell, arguments[0], interactive) status = EvalStatus.FullEvaluation value = EvalResult.unwrap_str_literal(return_val_result.value) if EvalResult.is_int(value): return_val = int(value) text = str(return_val) elif EvalResult.is_float(value): return_val = float(value) text = str(return_val) else: status = EvalStatus.Error text = self.convert_ptree_to_str(parse_tree_root) return_val = 0 return EvalResult(None, status, return_val, text) def if_handler(self, arguments, current_cell, interactive, parse_tree_root): visited = False for stack_frame in self._branch_stack: if stack_frame[0].get_local_address() == current_cell.get_local_address(): visited = True if visited is False: # self._indent_level += 1 size = len(arguments) if size == 3: cond_eval_result = self.evaluate_parse_tree(current_cell, arguments[0], interactive) if self.is_bool(cond_eval_result.value): cond_eval_result.value = bool(_strtobool(cond_eval_result.value)) elif self.is_int(cond_eval_result.value): if int(cond_eval_result.value) == 0: cond_eval_result.value = False else: cond_eval_result.value = True if cond_eval_result.status == EvalStatus.FullEvaluation: if cond_eval_result.value: if type(arguments[1]) is Tree or type(arguments[1]) is Token: self._branch_stack.append( (current_cell, arguments[1], current_cell.sheet.cells, self._indent_level, '[TRUE]')) status = EvalStatus.Branching else: status = EvalStatus.FullEvaluation else: if type(arguments[2]) is Tree or type(arguments[2]) is Token: self._branch_stack.append( (current_cell, arguments[2], current_cell.sheet.cells, self._indent_level, '[FALSE]')) status = EvalStatus.Branching else: status = EvalStatus.FullEvaluation text = XLMInterpreter.convert_ptree_to_str(parse_tree_root) else: memory_state = copy.deepcopy(current_cell.sheet.cells) if type(arguments[2]) is Tree or type(arguments[2]) is Token or type(arguments[2]) is list: self._branch_stack.append( (current_cell, arguments[2], memory_state, self._indent_level, '[FALSE]')) if type(arguments[1]) is Tree or type(arguments[1]) is Token or type(arguments[1]) is list: self._branch_stack.append( (current_cell, arguments[1], current_cell.sheet.cells, self._indent_level, '[TRUE]')) text = XLMInterpreter.convert_ptree_to_str(parse_tree_root) status = EvalStatus.FullBranching else: status = EvalStatus.FullEvaluation text = XLMInterpreter.convert_ptree_to_str(parse_tree_root) else: # loop detected text = '[[LOOP]]: ' + XLMInterpreter.convert_ptree_to_str(parse_tree_root) status = EvalStatus.End return EvalResult(None, status, 0, text) def mid_handler(self, arguments, current_cell, interactive, parse_tree_root): str_eval_result = self.evaluate_parse_tree(current_cell, arguments[0], interactive) base_eval_result = self.evaluate_parse_tree(current_cell, arguments[1], interactive) len_eval_result = self.evaluate_parse_tree(current_cell, arguments[2], interactive) status = EvalStatus.PartialEvaluation return_val = "" if str_eval_result.status == EvalStatus.FullEvaluation: if base_eval_result.status == EvalStatus.FullEvaluation and \ len_eval_result.status == EvalStatus.FullEvaluation: if self.is_float(base_eval_result.value) and self.is_float(len_eval_result.value): base = int(float(base_eval_result.value)) - 1 length = int(float(len_eval_result.value)) return_val = EvalResult.unwrap_str_literal(str_eval_result.value)[base: base + length] text = str(return_val) status = EvalStatus.FullEvaluation if status == EvalStatus.PartialEvaluation: text = 'MID({},{},{})'.format(XLMInterpreter.convert_ptree_to_str(arguments[0]), XLMInterpreter.convert_ptree_to_str(arguments[1]), XLMInterpreter.convert_ptree_to_str(arguments[2])) return EvalResult(None, status, return_val, text) def min_handler(self, arguments, current_cell, interactive, parse_tree_root): min = None status = EvalStatus.PartialEvaluation for argument in arguments: arg_eval_result = self.evaluate_parse_tree(current_cell, argument, interactive) if arg_eval_result.status == EvalStatus.FullEvaluation: cur_val = self.convert_float(arg_eval_result.value) if not min or cur_val < min: min = cur_val else: min = None break if min: return_val = min text = str(min) status = EvalStatus.FullEvaluation else: text = return_val = self.convert_ptree_to_str(parse_tree_root) return EvalResult(None, status, return_val, text) def max_handler(self, arguments, current_cell, interactive, parse_tree_root): max = None status = EvalStatus.PartialEvaluation for argument in arguments: arg_eval_result = self.evaluate_parse_tree(current_cell, argument, interactive) if arg_eval_result.status == EvalStatus.FullEvaluation: cur_val = self.convert_float(arg_eval_result.value) if not max or cur_val > max: max = cur_val else: max = None break if max: return_val = max text = str(max) status = EvalStatus.FullEvaluation else: text = return_val = self.convert_ptree_to_str(parse_tree_root) return EvalResult(None, status, return_val, text) def product_handler(self, arguments, current_cell, interactive, parse_tree_root): total = None status = EvalStatus.PartialEvaluation for argument in arguments: arg_eval_result = self.evaluate_parse_tree(current_cell, argument, interactive) if arg_eval_result.status == EvalStatus.FullEvaluation: if not total: total = self.convert_float(arg_eval_result.value) else: total *= self.convert_float(arg_eval_result.value) else: total = None break if total: return_val = total text = str(total) status = EvalStatus.FullEvaluation else: text = return_val = self.convert_ptree_to_str(parse_tree_root) return EvalResult(None, status, return_val, text) def mod_handler(self, arguments, current_cell, interactive, parse_tree_root): arg1_eval_res = self.evaluate_parse_tree(current_cell, arguments[0], interactive) arg2_eval_res = self.evaluate_parse_tree(current_cell, arguments[1], interactive) if arg1_eval_res.status == EvalStatus.FullEvaluation and arg2_eval_res.status == EvalStatus.FullEvaluation: return_val = float(arg1_eval_res.value) % float(arg2_eval_res.value) text = str(return_val) status = EvalStatus.FullEvaluation return EvalResult(None, status, return_val, text) def sqrt_handler(self, arguments, current_cell, interactive, parse_tree_root): arg1_eval_res = self.evaluate_parse_tree(current_cell, arguments[0], interactive) status = EvalStatus.PartialEvaluation if arg1_eval_res.status == EvalStatus.FullEvaluation: return_val = math.floor(math.sqrt(float(arg1_eval_res.value))) text = str(return_val) status = EvalStatus.FullEvaluation if status == EvalStatus.PartialEvaluation: return_val = text = self.convert_ptree_to_str(parse_tree_root) return EvalResult(None, status, return_val, text) def goto_handler(self, arguments, current_cell, interactive, parse_tree_root): next_sheet, next_col, next_row = self.get_cell_addr(current_cell, arguments[0]) next_cell = None if next_sheet is not None and next_sheet in self.xlm_wrapper.get_macrosheets(): next_cell = self.get_formula_cell(self.xlm_wrapper.get_macrosheets()[next_sheet], next_col, next_row) status = EvalStatus.FullEvaluation else: status = EvalStatus.Error text = XLMInterpreter.convert_ptree_to_str(parse_tree_root) return_val = 0 return EvalResult(next_cell, status, return_val, text) def halt_handler(self, arguments, current_cell, interactive, parse_tree_root): return_val = text = XLMInterpreter.convert_ptree_to_str(parse_tree_root) status = EvalStatus.End self._indent_level -= 1 return EvalResult(None, status, return_val, text) def call_handler(self, arguments, current_cell, interactive, parse_tree_root): argument_texts = [] status = EvalStatus.FullEvaluation for argument in arguments: arg_eval_result = self.evaluate_parse_tree(current_cell, argument, interactive) if arg_eval_result.status != EvalStatus.FullEvaluation: status = arg_eval_result.status argument_texts.append(arg_eval_result.get_text()) list_separator = self.xlm_wrapper.get_xl_international_char(XlApplicationInternational.xlListSeparator) text = 'CALL({})'.format(list_separator.join(argument_texts)) return_val = 0 return EvalResult(None, status, return_val, text) def is_number_handler(self, arguments, current_cell, interactive, parse_tree_root): eval_result = self.evaluate_parse_tree(current_cell, arguments[0], interactive) if eval_result.status == EvalStatus.FullEvaluation: if self.is_int(eval_result.text) or self.is_float(eval_result.text): return_val = 1 else: return_val = 0 text = str(return_val) else: text = 'ISNUMBER({})'.format(eval_result.get_text()) return_val = 1 # true return EvalResult(None, eval_result.status, return_val, text) def search_handler(self, arguments, current_cell, interactive, parse_tree_root): arg1_eval_res = self.evaluate_parse_tree(current_cell, arguments[0], interactive) arg2_eval_res = self.evaluate_parse_tree(current_cell, arguments[1], interactive) if arg1_eval_res.status == EvalStatus.FullEvaluation and arg2_eval_res.status == EvalStatus.FullEvaluation: try: arg1_val = EvalResult.unwrap_str_literal(str(arg1_eval_res.value)) arg2_val = EvalResult.unwrap_str_literal(arg2_eval_res.value) return_val = arg2_val.lower().index(arg1_val.lower()) text = str(return_val) except ValueError: return_val = None text = '' status = EvalStatus.FullEvaluation else: text = 'SEARCH({},{})'.format(arg1_eval_res.get_text(), arg2_eval_res.get_text()) return_val = 0 status = EvalStatus.PartialEvaluation return EvalResult(None, status, return_val, text) def round_handler(self, arguments, current_cell, interactive, parse_tree_root): arg1_eval_res = self.evaluate_parse_tree(current_cell, arguments[0], interactive) arg2_eval_res = self.evaluate_parse_tree(current_cell, arguments[1], interactive) if arg1_eval_res.status == EvalStatus.FullEvaluation and arg2_eval_res.status == EvalStatus.FullEvaluation: return_val = round(float(arg1_eval_res.value), int(float(arg2_eval_res.value))) text = str(return_val) status = EvalStatus.FullEvaluation return EvalResult(None, status, return_val, text) def roundup_handler(self, arguments, current_cell, interactive, parse_tree_root): arg1_eval_res = self.evaluate_parse_tree(current_cell, arguments[0], interactive) if arg1_eval_res.status == EvalStatus.FullEvaluation: return_val = math.ceil(float(arg1_eval_res.value)) text = str(return_val) status = EvalStatus.FullEvaluation return EvalResult(None, status, return_val, text) def directory_handler(self, arguments, current_cell, interactive, parse_tree_root): text = r'C:\Users\user\Documents' return_val = text status = EvalStatus.FullEvaluation return EvalResult(None, status, return_val, text) def char_handler(self, arguments, current_cell, interactive, parse_tree_root): arg_eval_result = self.evaluate_parse_tree(current_cell, arguments[0], interactive) if arg_eval_result.status == EvalStatus.FullEvaluation: value = arg_eval_result.text if arg_eval_result.value in self.defined_names: value = self.defined_names[arg_eval_result.value].value if 0 <= float(value) <= 255: return_val = text = chr(int(float(value))) # cell = self.get_formula_cell(current_cell.sheet, current_cell.column, current_cell.row) # cell.value = text status = EvalStatus.FullEvaluation else: return_val = text = XLMInterpreter.convert_ptree_to_str(parse_tree_root) self.char_error_count += 1 status = EvalStatus.Error else: text = 'CHAR({})'.format(arg_eval_result.text) return_val = text status = EvalStatus.PartialEvaluation return EvalResult(arg_eval_result.next_cell, status, return_val, text) def t_handler(self, arguments, current_cell, interactive, parse_tree_root): arg_eval_result = self.evaluate_parse_tree(current_cell, arguments[0], interactive) return_val = '' if arg_eval_result.status == EvalStatus.FullEvaluation: if isinstance(arg_eval_result.value, tuple) and len(arg_eval_result.value) == 3: cell = self.get_cell(arg_eval_result.value[0], arg_eval_result.value[1], arg_eval_result.value[2]) return_val = cell.value elif arg_eval_result.value != 'TRUE' and arg_eval_result.value != 'FALSE': return_val = str(arg_eval_result.value) status = EvalStatus.FullEvaluation else: status = EvalStatus.PartialEvaluation return EvalResult(arg_eval_result.next_cell, status, return_val, EvalResult.wrap_str_literal(str(return_val), must_wrap=True)) def int_handler(self, arguments, current_cell, interactive, parse_tree_root): arg_eval_result = self.evaluate_parse_tree(current_cell, arguments[0], interactive) return_val = int(0) if arg_eval_result.status == EvalStatus.FullEvaluation: text = str(arg_eval_result.value).lower() if text == "true": return_val = int(1) elif text == "false": return_val = int(0) else: return_val = int(arg_eval_result.value) status = EvalStatus.FullEvaluation else: status = EvalStatus.PartialEvaluation return EvalResult(arg_eval_result.next_cell, status, return_val, str(return_val)) def run_handler(self, arguments, current_cell, interactive, parse_tree_root): size = len(arguments) next_cell = None status = EvalStatus.PartialEvaluation if 1 <= size <= 2: next_sheet, next_col, next_row = self.get_cell_addr(current_cell, arguments[0]) if next_sheet is not None and next_sheet in self.xlm_wrapper.get_macrosheets(): next_cell = self.get_formula_cell(self.xlm_wrapper.get_macrosheets()[next_sheet], next_col, next_row) if size == 1: text = 'RUN({}!{}{})'.format(next_sheet, next_col, next_row) else: text = 'RUN({}!{}{}, {})'.format(next_sheet, next_col, next_row, XLMInterpreter.convert_ptree_to_str(arguments[1])) status = EvalStatus.FullEvaluation else: text = XLMInterpreter.convert_ptree_to_str(parse_tree_root) status = EvalStatus.Error return_val = 0 else: text = XLMInterpreter.convert_ptree_to_str(parse_tree_root) status = EvalStatus.Error return_val = 1 return EvalResult(next_cell, status, return_val, text) def formula_handler(self, arguments, current_cell, interactive, parse_tree_root): return self.evaluate_formula(current_cell, 'FORMULA', arguments, interactive) def formula_fill_handler(self, arguments, current_cell, interactive, parse_tree_root): return self.evaluate_formula(current_cell, 'FORMULA.FILL', arguments, interactive) def formula_array_handler(self, arguments, current_cell, interactive, parse_tree_root): return self.evaluate_formula(current_cell, 'FORMULA.ARRAY', arguments, interactive) def set_value_handler(self, arguments, current_cell, interactive, parse_tree_root): return self.evaluate_formula( current_cell, 'SET.VALUE', arguments, interactive, destination_arg=2, set_value_only=True) def error_handler(self, arguments, current_cell, interactive, parse_tree_root): return EvalResult(None, EvalStatus.FullEvaluation, 0, XLMInterpreter.convert_ptree_to_str(parse_tree_root)) def select_handler(self, arguments, current_cell, interactive, parse_tree_root): status = EvalStatus.PartialEvaluation range_eval_result = self.evaluate_parse_tree(current_cell, arguments[0], interactive) if len(arguments) == 2: # e.g., SELECT(B1:B100,B1) and SELECT(,"R[1]C") if self.active_cell: sheet, col, row = self.get_cell_addr(self.active_cell, arguments[1]) else: sheet, col, row = self.get_cell_addr(current_cell, arguments[1]) if sheet: self.active_cell = self.get_cell(sheet, col, row) status = EvalStatus.FullEvaluation elif isinstance(arguments[0], Token): text = XLMInterpreter.convert_ptree_to_str(parse_tree_root) return_val = 0 elif arguments[0].data == 'range': # e.g., SELECT(D1:D10:D1) sheet, col, row = self.selected_range[2] if sheet: self.active_cell = self.get_cell(sheet, col, row) status = EvalStatus.FullEvaluation elif arguments[0].data == 'cell': # select(R1C1) if self.active_cell: sheet, col, row = self.get_cell_addr(self.active_cell, arguments[0]) else: sheet, col, row = self.get_cell_addr(current_cell, arguments[0]) if sheet: self.active_cell = self.get_cell(sheet, col, row) status = EvalStatus.FullEvaluation text = XLMInterpreter.convert_ptree_to_str(parse_tree_root) return_val = 0 return EvalResult(None, status, return_val, text) def iterate_range(self, name, start_cell, end_cell): sheet_name = start_cell[0] row_start = int(start_cell[2]) row_end = int(end_cell[2]) for row_index in range(row_start, row_end + 1): col_start = Cell.convert_to_column_index(start_cell[1]) col_end = Cell.convert_to_column_index(end_cell[1]) for col_index in range(col_start, col_end+1): next_cell = self.get_cell(sheet_name, Cell.convert_to_column_name(col_index), row_index) if next_cell: yield next_cell def forcell_handler(self, arguments, current_cell, interactive, parse_tree_root): var_eval_result = self.evaluate_parse_tree(current_cell, arguments[0], interactive) start_cell_ptree, end_cell_ptree = self.get_range_parts(arguments[1]) start_cell = self.get_cell_addr(current_cell, start_cell_ptree) end_cell = self.get_cell_addr(current_cell, end_cell_ptree) if start_cell[0] != end_cell[0]: end_cell = (start_cell[0], end_cell[1], end_cell[2]) skip = False if len(arguments) >= 3: skip_eval_result = self.evaluate_parse_tree(current_cell, arguments[2], interactive) skip = bool(skip_eval_result.value) variable_name = EvalResult.unwrap_str_literal(var_eval_result.value).lower() if len(self._while_stack) > 0 and self._while_stack[-1]['start_point'] == current_cell: iterator = self._while_stack[-1]['iterator'] else: iterator = self.iterate_range(variable_name, start_cell, end_cell) stack_record = {'start_point': current_cell, 'status': True, 'iterator': iterator} self._while_stack.append(stack_record) try: self.defined_names[variable_name] = next(iterator) except: self._while_stack[-1]['status'] = False self._indent_level += 1 return EvalResult(None, EvalStatus.FullEvaluation, 0 , self.convert_ptree_to_str(parse_tree_root)) def while_handler(self, arguments, current_cell, interactive, parse_tree_root): status = EvalStatus.PartialEvaluation text = '' stack_record = {'start_point': current_cell, 'status': False} condition_eval_result = self.evaluate_parse_tree(current_cell, arguments[0], interactive) status = condition_eval_result.status if condition_eval_result.status == EvalStatus.FullEvaluation: if str(condition_eval_result.value).lower() == 'true': stack_record['status'] = True text = '{} -> [{}]'.format(XLMInterpreter.convert_ptree_to_str(parse_tree_root), str(condition_eval_result.value)) if not text: text = '{}'.format(XLMInterpreter.convert_ptree_to_str(parse_tree_root)) self._while_stack.append(stack_record) if stack_record['status'] == False: self.ignore_processing = True self._indent_level += 1 return EvalResult(None, status, 0, text) def next_handler(self, arguments, current_cell, interactive, parse_tree_root): status = EvalStatus.FullEvaluation next_cell = None if self._indent_level == len(self._while_stack): self.ignore_processing = False next_cell = None if len(self._while_stack) > 0: top_record = self._while_stack.pop() if top_record['status'] is True: next_cell = top_record['start_point'] if 'iterator' in top_record: self._while_stack.append(top_record) self._indent_level = self._indent_level - 1 if self._indent_level > 0 else 0 self._indent_current_line = True if next_cell is None: status = EvalStatus.IGNORED return EvalResult(next_cell, status, 0, 'NEXT') def len_handler(self, arguments, current_cell, interactive, parse_tree_root): arg_eval_result = self.evaluate_parse_tree(current_cell, arguments[0], interactive) if arg_eval_result.status == EvalStatus.FullEvaluation: return_val = len(arg_eval_result.get_text(unwrap=True)) text = str(return_val) status = EvalStatus.FullEvaluation else: text = XLMInterpreter.convert_ptree_to_str(parse_tree_root) return_val = text status = EvalStatus.PartialEvaluation return EvalResult(None, status, return_val, text) def define_name_handler(self, arguments, current_cell, interactive, parse_tree_root): arg_name_eval_result = self.evaluate_parse_tree(current_cell, arguments[0], interactive) status = EvalStatus.PartialEvaluation if arg_name_eval_result.status == EvalStatus.FullEvaluation: arg_val_eval_result = self.evaluate_parse_tree(current_cell, arguments[1], interactive) status = EvalStatus.FullEvaluation name = EvalResult.unwrap_str_literal(arg_name_eval_result.text).lower() if EvalResult.is_int(arg_val_eval_result.value): self.defined_names[name] = int(arg_val_eval_result.value) elif EvalResult.is_float(arg_val_eval_result.value): self.defined_names[name] = float(arg_val_eval_result.value) else: self.defined_names[name] = arg_val_eval_result.value return_val = self.defined_names[name] text = "DEFINE.NAME({},{})".format(EvalResult.wrap_str_literal(name), str(return_val)) else: return_val = text = self.convert_ptree_to_str(parse_tree_root) return EvalResult(None, status, return_val, text) def index_handler(self, arguments, current_cell, interactive, parse_tree_root): array_arg_result = self.evaluate_parse_tree(current_cell, arguments[0], interactive) status = EvalStatus.PartialEvaluation if array_arg_result.status == EvalStatus.FullEvaluation: index_arg_result = self.evaluate_parse_tree(current_cell, arguments[1], interactive) if isinstance(array_arg_result.value, list): # example: f9adf499bc16bfd096e00bc59c3233f022dec20c20440100d56e58610e4aded3 return_val = array_arg_result.value[int(float(index_arg_result.value))-1] # index starts at 1 in excel else: # example: 6a8045bc617df5f2b8f9325ed291ef05ac027144f1fda84e78d5084d26847902 range = EvalResult.unwrap_str_literal(array_arg_result.value) parsed_range = Cell.parse_range_addr(range) index = int(float(index_arg_result.value))-1 row_str = str(int(float(parsed_range[2])) + index) if parsed_range[0]: sheet_name = parsed_range[0] else: sheet_name = current_cell.sheet.name return_val = self.get_cell(sheet_name, parsed_range[1], row_str) text = str(return_val) status = EvalStatus.FullEvaluation else: return_val = text = self.convert_ptree_to_str(parse_tree_root) return EvalResult(None, status, return_val, text) def rows_handler(self, arguments, current_cell, interactive, parse_tree_root): arg_eval_result = self.evaluate_parse_tree(current_cell, arguments[0], interactive) status = EvalStatus.PartialEvaluation if arg_eval_result.status == EvalStatus.FullEvaluation: if isinstance(arg_eval_result.value, list): # example: f9adf499bc16bfd096e00bc59c3233f022dec20c20440100d56e58610e4aded3 return_val = len(arg_eval_result.value) else: # example: 6a8045bc617df5f2b8f9325ed291ef05ac027144f1fda84e78d5084d26847902 range = EvalResult.unwrap_str_literal(arg_eval_result.value) parsed_range = Cell.parse_range_addr(range) return_val = int(parsed_range[4]) - int(parsed_range[2]) + 1 text = str(return_val) status = EvalStatus.FullEvaluation else: return_val = text = self.convert_ptree_to_str(parse_tree_root) return EvalResult(None, status, return_val, text) def counta_handler(self, arguments, current_cell, interactive, parse_tree_root): arg_eval_result = self.evaluate_parse_tree(current_cell, arguments[0], interactive) sheet_name, startcolumn, startrow, endcolumn, endrow = Cell.parse_range_addr(arg_eval_result.text) count = 0 it = int(startrow) start_col_index = Cell.convert_to_column_index(startcolumn) end_col_index = Cell.convert_to_column_index(endcolumn) start_row_index = int(startrow) end_row_index = int(endrow) val_item_count = 0 for row in range(start_row_index, end_row_index + 1): for col in range(start_col_index, end_col_index + 1): if (sheet_name != None): cell = self.get_worksheet_cell(sheet_name, Cell.convert_to_column_name(col), str(row)) else: cell = self.get_cell(current_cell.sheet.name, Cell.convert_to_column_name(col), str(row)) if cell and cell.value != '': val_item_count += 1 return_val = val_item_count status = EvalStatus.FullEvaluation text = str(return_val) return EvalResult(None, status, return_val, text) def count_handler(self, arguments, current_cell, interactive, parse_tree_root): return_val = len(arguments) text = str(return_val) status = EvalStatus.FullEvaluation return EvalResult(None, status, return_val, text) def trunc_handler(self, arguments, current_cell, interactive, parse_tree_root): arg_eval_result = self.evaluate_parse_tree(current_cell, arguments[0], interactive) if arg_eval_result.status == EvalStatus.FullEvaluation: if arg_eval_result.value == "TRUE": return_val = 1 elif arg_eval_result.value == "FALSE": return_val = 0 else: return_val = math.trunc(float(arg_eval_result.value)) text = str(return_val) status = EvalStatus.FullEvaluation else: text = XLMInterpreter.convert_ptree_to_str(parse_tree_root) return_val = text status = EvalStatus.PartialEvaluation return EvalResult(None, status, return_val, text) def quotient_handler(self, arguments, current_cell, interactive, parse_tree_root): numerator_arg_eval_result = self.evaluate_parse_tree(current_cell, arguments[0], interactive) Denominator_arg_eval_result = self.evaluate_parse_tree(current_cell, arguments[0], interactive) status = EvalStatus.PartialEvaluation if numerator_arg_eval_result.status == EvalStatus.FullEvaluation and \ Denominator_arg_eval_result.status == EvalStatus.FullEvaluation: return_val = numerator_arg_eval_result.value // Denominator_arg_eval_result.value text = str(return_val) status = EvalStatus.FullEvaluation return EvalResult(None, status, return_val, text) def abs_handler(self, arguments, current_cell, interactive, parse_tree_root): arg_eval_result = self.evaluate_parse_tree(current_cell, arguments[0], interactive) if arg_eval_result.status == EvalStatus.FullEvaluation: if arg_eval_result.value == "TRUE": return_val = 1 elif arg_eval_result.value == "FALSE": return_val = 0 else: return_val = abs(float(arg_eval_result.value)) text = str(return_val) status = EvalStatus.FullEvaluation else: text = XLMInterpreter.convert_ptree_to_str(parse_tree_root) return_val = text status = EvalStatus.PartialEvaluation return EvalResult(None, status, return_val, text) def absref_handler(self, arguments, current_cell, interactive, parse_tree_root): arg_ref_txt_eval_result = self.evaluate_parse_tree(current_cell, arguments[0], interactive) status = EvalStatus.PartialEvaluation if arg_ref_txt_eval_result.status == EvalStatus.FullEvaluation and \ (isinstance(arguments[1], Tree) and arguments[1].data == 'cell'): offset_addr_text = arg_ref_txt_eval_result.value base_addr_text = self.convert_ptree_to_str(arguments[1]) return_val = Cell.get_abs_addr(base_addr_text, offset_addr_text) status = EvalStatus.FullEvaluation else: return_val = XLMInterpreter.convert_ptree_to_str(parse_tree_root) return EvalResult(None, status, return_val, str(return_val)) def address_handler(self, arguments, current_cell, interactive, parse_tree_root): arg_row_num_eval_result = self.evaluate_parse_tree(current_cell, arguments[0], interactive) arg_col_num_eval_result = self.evaluate_parse_tree(current_cell, arguments[1], interactive) optional_args = True if len(arguments) >= 3: arg_abs_num_eval_result = self.evaluate_parse_tree(current_cell, arguments[2], interactive) if arg_abs_num_eval_result.status == EvalStatus.FullEvaluation: abs_num = arg_abs_num_eval_result.value else: optional_args = False else: abs_num = 1 if len(arguments) >= 4: arg_a1_eval_result = self.evaluate_parse_tree(current_cell, arguments[3], interactive) if arg_a1_eval_result.status == EvalStatus.FullEvaluation: a1 = arg_a1_eval_result.value else: optional_args = False else: a1 = "TRUE" if len(arguments) >= 5: arg_sheet_eval_result = self.evaluate_parse_tree(current_cell, arguments[4], interactive) if arg_sheet_eval_result.status == EvalStatus.FullEvaluation: sheet_name = arg_sheet_eval_result.text.strip('\"') else: optional_args = False else: sheet_name = current_cell.sheet.name return_val = '' if arg_row_num_eval_result.status == EvalStatus.FullEvaluation and \ arg_col_num_eval_result.status == EvalStatus.FullEvaluation and \ optional_args: return_val += sheet_name + '!' if a1 == "FALSE": cell_addr_tmpl = 'R{}C{}' if abs_num == 2: cell_addr_tmpl = 'R{}C[{}]' elif abs_num == 3: cell_addr_tmpl = 'R[{}]C{}' elif abs_num == 4: cell_addr_tmpl = 'R[{}]C[{}]' return_val += cell_addr_tmpl.format(arg_row_num_eval_result.text, arg_col_num_eval_result.text) else: cell_addr_tmpl = '${}${}' if abs_num == 2: cell_addr_tmpl = '{}${}' elif abs_num == 3: cell_addr_tmpl = '${}{}' elif abs_num == 4: cell_addr_tmpl = '{}{}' return_val += cell_addr_tmpl.format(Cell.convert_to_column_name(int(arg_col_num_eval_result.value)), arg_row_num_eval_result.text) status = EvalStatus.FullEvaluation else: status = EvalStatus.PartialEvaluation return_val = self.evaluate_parse_tree(current_cell, arguments, False) return EvalResult(None, status, return_val, str(return_val)) def indirect_handler(self, arguments, current_cell, interactive, parse_tree_root): arg_addr_eval_result = self.evaluate_parse_tree(current_cell, arguments[0], interactive) status = EvalStatus.PartialEvaluation if arg_addr_eval_result.status == EvalStatus.FullEvaluation: a1 = "TRUE" if len(arguments) == 2: arg_a1_eval_result = self.evaluate_parse_tree(current_cell, arguments[1], interactive) if arg_a1_eval_result.status == EvalStatus.FullEvaluation: a1 = arg_a1_eval_result.value sheet_name, col, row = Cell.parse_cell_addr(arg_addr_eval_result.value) indirect_cell = self.get_cell(sheet_name, col, row) return_val = indirect_cell.value status = EvalStatus.FullEvaluation else: return_val = self.evaluate_parse_tree(current_cell, arguments, False) return EvalResult(None, status, return_val, str(return_val)) def register_handler(self, arguments, current_cell, interactive, parse_tree_root): if len(arguments) >= 4: arg_list = [] status = EvalStatus.FullEvaluation for index, arg in enumerate(arguments): if index > 3: break res_eval = self.evaluate_parse_tree(current_cell, arg, interactive) arg_list.append(res_eval.get_text(unwrap=True)) function_name = "{}.{}".format(arg_list[0], arg_list[1]) # signature: https://support.office.com/en-us/article/using-the-call-and-register-functions-06fa83c1-2869-4a89-b665-7e63d188307f function_signature = arg_list[2] function_alias = arg_list[3] # overrides previously registered function self._registered_functions[function_alias] = {'name': function_name, 'signature': function_signature} text = self.evaluate_argument_list(current_cell, 'REGISTER', arguments).get_text(unwrap=True) else: status = EvalStatus.Error text = XLMInterpreter.convert_ptree_to_str(parse_tree_root) return_val = 0 return EvalResult(None, status, return_val, text) def registerid_handler(self, arguments, current_cell, interactive, parse_tree_root): if len(arguments) >= 3: arg_list = [] status = EvalStatus.FullEvaluation for index, arg in enumerate(arguments): if index > 2: break res_eval = self.evaluate_parse_tree(current_cell, arg, interactive) arg_list.append(res_eval.get_text(unwrap=True)) function_name = "{}.{}".format(arg_list[0], arg_list[1]) # signature: https://support.office.com/en-us/article/using-the-call-and-register-functions-06fa83c1-2869-4a89-b665-7e63d188307f function_signature = arg_list[2] #function_alias = arg_list[3] # overrides previously registered function #self._registered_functions[function_alias] = {'name': function_name, 'signature': function_signature} text = self.evaluate_argument_list(current_cell, 'REGISTER.ID', arguments).get_text(unwrap=True) return_val = function_name else: status = EvalStatus.Error text = XLMInterpreter.convert_ptree_to_str(parse_tree_root) return_val = 0 return EvalResult(None, status, return_val, text) def return_handler(self, arguments, current_cell, interactive, parse_tree_root): arg1_eval_res = self.evaluate_parse_tree(current_cell, arguments[0], interactive) if self._function_call_stack: return_cell = self._function_call_stack.pop() return_cell.value = arg1_eval_res.value arg1_eval_res.next_cell = self.get_formula_cell(return_cell.sheet, return_cell.column, str(int(return_cell.row) + 1)) if arg1_eval_res.text == '': arg1_eval_res.text = 'RETURN()' return arg1_eval_res def fopen_handler(self, arguments, current_cell, interactive, parse_tree_root): arg1_eval_res = self.evaluate_parse_tree(current_cell, arguments[0], interactive) if len(arguments) > 1: arg2_eval_res = self.evaluate_parse_tree(current_cell, arguments[1], interactive) access = arg2_eval_res.value else: access = '1' if arg1_eval_res.status == EvalStatus.FullEvaluation: file_name = arg1_eval_res.get_text(unwrap=True) else: file_name = "default_name" if file_name not in self._files: self._files[file_name] = {'file_access': access, 'file_content': ''} text = 'FOPEN({},{})'.format(arg1_eval_res.get_text(unwrap=False), access) return EvalResult(None, arg1_eval_res.status, file_name, text) def fsize_handler(self, arguments, current_cell, interactive, parse_tree_root, end_line=''): arg1_eval_res = self.evaluate_parse_tree(current_cell, arguments[0], interactive) file_name = arg1_eval_res.get_text(unwrap=True) status = EvalStatus.PartialEvaluation return_val = 0 if file_name in self._files: status = EvalStatus.FullEvaluation if self._files[file_name]['file_content'] is not None: return_val = len(self._files[file_name]['file_content']) text = 'FSIZE({})'.format(EvalResult.wrap_str_literal(file_name)) return EvalResult(None, status, return_val, str(return_val)) def fwrite_handler(self, arguments, current_cell, interactive, parse_tree_root, end_line=''): arg1_eval_res = self.evaluate_parse_tree(current_cell, arguments[0], interactive) arg2_eval_res = self.evaluate_parse_tree(current_cell, arguments[1], interactive) file_name = arg1_eval_res.value if file_name.strip() == "" or EvalResult.is_int(file_name) or EvalResult.is_float(file_name): if len(self._files) > 0: file_name = list(self._files.keys())[0] else: file_name = "default_filename" file_content = arg2_eval_res.get_text(unwrap=True) status = EvalStatus.PartialEvaluation if file_name in self._files: status = EvalStatus.FullEvaluation self._files[file_name]['file_content'] += file_content + end_line text = 'FWRITE({},{})'.format(EvalResult.wrap_str_literal(file_name), EvalResult.wrap_str_literal(file_content)) return EvalResult(None, status, '0', text) def fwriteln_handler(self, arguments, current_cell, interactive, parse_tree_root): return self.fwrite_handler(arguments, current_cell, interactive, parse_tree_root, end_line='\r\n') def files_handler(self, arguments, current_cell, interactive, parse_tree_root, end_line=''): arg1_eval_res = self.evaluate_parse_tree(current_cell, arguments[0], interactive) dir_name = arg1_eval_res.get_text(unwrap=True) status = EvalStatus.FullEvaluation # if dir_name in self._files: # return_val = dir_name # else: # return_val = None return_val = dir_name text = "FILES({})".format(EvalResult.wrap_str_literal(dir_name)) return EvalResult(None, status, return_val, text) def iserror_handler(self, arguments, current_cell, interactive, parse_tree_root, end_line=''): arg1_eval_res = self.evaluate_parse_tree(current_cell, arguments[0], interactive) status = EvalStatus.FullEvaluation if arg1_eval_res.value == None: return_val = True else: return_val = False if self._iserror_loc is None: self._iserror_val = return_val self._iserror_loc = current_cell self._iserror_count = 1 elif self._iserror_loc == current_cell: if self._iserror_val != return_val: self._iserror_val = return_val self._iserror_count = 1 elif self._iserror_count < XLMInterpreter.MAX_ISERROR_LOOPCOUNT: self._iserror_count += 1 else: return_val = not return_val self._iserror_loc = None text = 'ISERROR({})'.format(EvalResult.wrap_str_literal(arg1_eval_res.get_text(unwrap=True))) return EvalResult(None, status, return_val, text) def offset_handler(self, arguments, current_cell, interactive, parse_tree_root): value = 0 next = None status = EvalStatus.PartialEvaluation cell = self.get_cell_addr(current_cell, arguments[0]) row_index = self.evaluate_parse_tree(current_cell, arguments[1], interactive) col_index = self.evaluate_parse_tree(current_cell, arguments[2], interactive) if isinstance(cell, tuple) and \ row_index.status == EvalStatus.FullEvaluation and \ col_index.status == EvalStatus.FullEvaluation: row = str(int(cell[2]) + int(float(str(row_index.value)))) col = Cell.convert_to_column_name(Cell.convert_to_column_index(cell[1]) + int(float(str(col_index.value)))) ref_cell = (cell[0], col, row) value = ref_cell status = EvalStatus.FullEvaluation next = self.get_formula_cell(self.xlm_wrapper.get_macrosheets()[cell[0]], col, row) text = XLMInterpreter.convert_ptree_to_str(parse_tree_root) return EvalResult(next, status, value, text) def arabic_hander(self, arguments, current_cell, interactive, parse_tree_root, end_line=''): arg1_eval_res = self.evaluate_parse_tree(current_cell, arguments[0], interactive) if arg1_eval_res.status == EvalStatus.FullEvaluation: roman_number = EvalResult.get_text(arg1_eval_res, unwrap=True) return_val = _from_roman(roman_number) status = EvalStatus.FullEvaluation text = str(return_val) else: return_val = text = self.convert_ptree_to_str(parse_tree_root) return EvalResult(None, status, return_val, text) def VirtualAlloc_handler(self, arguments, current_cell, interactive, parse_tree_root): base_eval_res = self.evaluate_parse_tree(current_cell, arguments[0], interactive) size_eval_res = self.evaluate_parse_tree(current_cell, arguments[1], interactive) if base_eval_res.status == EvalStatus.FullEvaluation and size_eval_res.status == EvalStatus.FullEvaluation: base = int(base_eval_res.get_text(unwrap=True)) occupied_addresses = [rec['base'] + rec['size'] for rec in self._memory] for memory_record in self._memory: if memory_record['base'] <= base <= (memory_record['base'] + memory_record['size']): base = map(max, occupied_addresses) + 4096 size = int(size_eval_res.get_text(unwrap=True)) self._memory.append({ 'base': base, 'size': size, 'data': [0] * size }) return_val = base status = EvalStatus.FullEvaluation else: status = EvalStatus.PartialEvaluation return_val = 0 text = XLMInterpreter.convert_ptree_to_str(parse_tree_root) return EvalResult(None, status, return_val, text) def WriteProcessMemory_handler(self, arguments, current_cell, interactive, parse_tree_root): status = EvalStatus.PartialEvaluation if len(arguments) > 4: status = EvalStatus.FullEvaluation args_eval_result = [] for arg in arguments: arg_eval_res = self.evaluate_parse_tree(current_cell, arg, interactive) if arg_eval_res.status != EvalStatus.FullEvaluation: status = arg_eval_res.status args_eval_result.append(arg_eval_res) if status == EvalStatus.FullEvaluation: base_address = int(args_eval_result[1].value) mem_data = args_eval_result[2].value mem_data = bytearray([ord(x) for x in mem_data]) size = int(args_eval_result[3].value) if not self.write_memory(base_address, mem_data, size): status = EvalStatus.Error text = 'Kernel32.WriteProcessMemory({},{},"{}",{},{})'.format( args_eval_result[0].get_text(), base_address, mem_data.hex(), size, args_eval_result[4].get_text()) return_val = 0 if status != EvalStatus.FullEvaluation: text = XLMInterpreter.convert_ptree_to_str(parse_tree_root) return_val = 0 return EvalResult(None, status, return_val, text) def RtlCopyMemory_handler(self, arguments, current_cell, interactive, parse_tree_root): status = EvalStatus.PartialEvaluation if len(arguments) == 3: destination_eval_res = self.evaluate_parse_tree(current_cell, arguments[0], interactive) src_eval_res = self.evaluate_parse_tree(current_cell, arguments[1], interactive) size_res = self.evaluate_parse_tree(current_cell, arguments[2], interactive) if destination_eval_res.status == EvalStatus.FullEvaluation and \ src_eval_res.status == EvalStatus.FullEvaluation: status = EvalStatus.FullEvaluation mem_data = src_eval_res.value mem_data = bytearray([ord(x) for x in mem_data]) if not self.write_memory(int(destination_eval_res.value), mem_data, len(mem_data)): status = EvalStatus.Error text = 'Kernel32.RtlCopyMemory({},"{}",{})'.format( destination_eval_res.get_text(), mem_data.hex(), size_res.get_text()) if status == EvalStatus.PartialEvaluation: text = XLMInterpreter.convert_ptree_to_str(parse_tree_root) return_val = 0 return EvalResult(None, status, return_val, text) # endregion def write_memory(self, base_address, mem_data, size): result = True for mem_rec in self._memory: if mem_rec['base'] <= base_address <= mem_rec['base'] + mem_rec['size']: if mem_rec['base'] <= base_address + size <= mem_rec['base'] + mem_rec['size']: offset = base_address - mem_rec['base'] for i in range(0, size): mem_rec['data'][offset + i] = mem_data[i] else: result = False break return result def evaluate_defined_name(self, current_cell, name, interactive): result = None lname = name.lower() if lname in self.defined_names: val = self.defined_names[lname] if isinstance(val, Tree) and val.data == 'cell': eval_res = self.evaluate_cell(current_cell, interactive, val) result = eval_res.value elif isinstance(val, list): result = val else: if isinstance(val, Cell): data = val.value else: # example: c7e40628fb6beb52d9d73a3b3afd1dca5d2335713593b698637e1a47b42bfc71 password: 2021 data = val try: formula_str = str(data) if str(data).startswith('=') else '=' + str(data) parsed_formula = self.xlm_parser.parse(formula_str) eval_result = self.evaluate_parse_tree(current_cell, parsed_formula, interactive) if isinstance(eval_result.value, list): result = eval_result.value else: result = str(eval_result.value) except: result = str(data) return result def evaluate_parse_tree(self, current_cell, parse_tree_root, interactive=True): next_cell = None status = EvalStatus.NotImplemented text = None return_val = None if type(parse_tree_root) is Token: if parse_tree_root.value.lower() in self.defined_names: # this formula has a defined name that can be changed # current formula must be removed from cache self._remove_current_formula_from_cache = True parse_tree_root.value = self.evaluate_defined_name(current_cell, parse_tree_root.value, interactive) return_val = parse_tree_root.value status = EvalStatus.FullEvaluation text = str(return_val) result = EvalResult(next_cell, status, return_val, text) elif type(parse_tree_root) is list: return_val = text = '' status = EvalStatus.FullEvaluation result = EvalResult(next_cell, status, return_val, text) elif parse_tree_root.data == 'function_call': result = self.evaluate_function(current_cell, parse_tree_root, interactive) elif parse_tree_root.data == 'cell': result = self.evaluate_cell(current_cell, interactive, parse_tree_root) elif parse_tree_root.data == 'range': result = self.evaluate_range(current_cell, interactive, parse_tree_root) elif parse_tree_root.data == 'array': result = self.evaluate_array(current_cell, interactive, parse_tree_root) elif parse_tree_root.data in self._expr_rule_names: text_left = None concat_status = EvalStatus.FullEvaluation for index, child in enumerate(parse_tree_root.children): if type(child) is Token and child.type in ['ADDITIVEOP', 'MULTIOP', 'CMPOP', 'CONCATOP']: op_str = str(child) right_arg = parse_tree_root.children[index + 1] right_arg_eval_res = self.evaluate_parse_tree(current_cell, right_arg, interactive) if isinstance(right_arg_eval_res.value, Cell): text_right = EvalResult.unwrap_str_literal(right_arg_eval_res.value.value) else: text_right = right_arg_eval_res.get_text(unwrap=True) if op_str == '&': if left_arg_eval_res.status == EvalStatus.FullEvaluation and right_arg_eval_res.status != EvalStatus.FullEvaluation: text_left = '{}&{}'.format(text_left, text_right) left_arg_eval_res.status = EvalStatus.PartialEvaluation concat_status = EvalStatus.PartialEvaluation elif left_arg_eval_res.status != EvalStatus.FullEvaluation and right_arg_eval_res.status == EvalStatus.FullEvaluation: text_left = '{}&{}'.format(text_left, text_right) left_arg_eval_res.status = EvalStatus.FullEvaluation concat_status = EvalStatus.PartialEvaluation elif left_arg_eval_res.status != EvalStatus.FullEvaluation and right_arg_eval_res.status != EvalStatus.FullEvaluation: text_left = '{}&{}'.format(text_left, text_right) left_arg_eval_res.status = EvalStatus.PartialEvaluation concat_status = EvalStatus.PartialEvaluation else: text_left = text_left + text_right elif left_arg_eval_res.status == EvalStatus.FullEvaluation and right_arg_eval_res.status == EvalStatus.FullEvaluation: status = EvalStatus.FullEvaluation if isinstance(right_arg_eval_res.value, Cell): value_right = right_arg_eval_res.value.value else: value_right = right_arg_eval_res.value if isinstance(value_right, str): if value_right.lower() == 'true': value_right = 1 elif value_right.lower() == 'false': value_right = 0 text_left = str(text_left) text_right = str(text_right) if text_left == '': text_left = '0' value_left = 0 if text_right == '': text_right = '0' value_right = 0 if self.is_float(value_left) and self.is_float(value_right): if op_str in self._operators: op_res = self._operators[op_str](float(value_left), float(value_right)) if type(op_res) == bool: value_left = str(op_res) elif op_res.is_integer(): value_left = str(int(op_res)) else: op_res = round(op_res, 10) value_left = str(op_res) else: value_left = 'Operator ' + op_str left_arg_eval_res.status = EvalStatus.NotImplemented elif EvalResult.is_datetime(text_left.strip('\"')) and EvalResult.is_datetime(text_right.strip('\"')): timestamp1 = datetime.datetime.strptime(text_left.strip('\"'), "%Y-%m-%d %H:%M:%S.%f") timestamp2 = datetime.datetime.strptime(text_right.strip('\"'), "%Y-%m-%d %H:%M:%S.%f") op_res = self._operators[op_str](float(timestamp1.timestamp()), float(timestamp2.timestamp())) op_res += 1000 if type(op_res) == bool: value_left = str(op_res) elif EvalResult.is_datetime(op_res): value_left = str(op_res) elif op_res.is_integer(): value_left = str(op_res) else: op_res = round(op_res, 10) value_left = str(op_res) elif EvalResult.is_datetime(text_left.strip('\"')) and EvalResult.is_time(text_right.strip('\"')): timestamp1 = datetime.datetime.strptime(text_left.strip('\"'), "%Y-%m-%d %H:%M:%S.%f") timestamp2 = datetime.datetime.strptime(text_right.strip('\"'), "%H:%M:%S") t1 = float(timestamp1.timestamp()) t2 = float( int(timestamp2.hour) * 3600 + int(timestamp2.minute) * 60 + int(timestamp2.second)) op_res = datetime.datetime.fromtimestamp(self._operators[op_str](t1, t2)) if type(op_res) == bool: value_left = str(op_res) elif type(op_res) == datetime.datetime: value_left = str(op_res) elif op_res.is_integer(): value_left = str(op_res) else: op_res = round(op_res, 10) value_left = str(op_res) else: if op_str in self._operators: value_left = EvalResult.unwrap_str_literal(str(value_left)) value_right = EvalResult.unwrap_str_literal(str(value_right)) op_res = self._operators[op_str](value_left, value_right) value_left = op_res else: value_left = XLMInterpreter.convert_ptree_to_str(parse_tree_root) left_arg_eval_res.status = EvalStatus.PartialEvaluation text_left = value_left else: left_arg_eval_res.status = EvalStatus.PartialEvaluation text_left = '{}{}{}'.format(text_left, op_str, text_right) return_val = text_left else: if text_left is None: left_arg = parse_tree_root.children[index] left_arg_eval_res = self.evaluate_parse_tree(current_cell, left_arg, interactive) text_left = left_arg_eval_res.get_text(unwrap=True) if isinstance(left_arg_eval_res.value, Cell): value_left = left_arg_eval_res.value.value else: value_left = left_arg_eval_res.value if isinstance(value_left, str): if value_left.lower() == 'true': value_left = 1 elif value_left.lower() == 'false': value_left = 0 if concat_status == EvalStatus.PartialEvaluation and left_arg_eval_res.status == EvalStatus.FullEvaluation: left_arg_eval_res.status = concat_status result = EvalResult(next_cell, left_arg_eval_res.status, return_val, EvalResult.wrap_str_literal(text_left)) elif parse_tree_root.data == 'final': arg = parse_tree_root.children[1] result = self.evaluate_parse_tree(current_cell, arg, interactive) else: status = EvalStatus.FullEvaluation for child_node in parse_tree_root.children: if child_node is not None: child_eval_result = self.evaluate_parse_tree(current_cell, child_node, interactive) if child_eval_result.status != EvalStatus.FullEvaluation: status = child_eval_result.status result = EvalResult(child_eval_result.next_cell, status, child_eval_result.value, child_eval_result.text) result.output_level = child_eval_result.output_level return result def evaluate_cell(self, current_cell, interactive, parse_tree_root): sheet_name, col, row = self.get_cell_addr(current_cell, parse_tree_root) return_val = '' text = '' status = EvalStatus.PartialEvaluation if sheet_name is not None: cell_addr = col + str(row) if sheet_name in self.xlm_wrapper.get_macrosheets(): sheet = self.xlm_wrapper.get_macrosheets()[sheet_name] else: sheet = self.xlm_wrapper.get_worksheets()[sheet_name] if cell_addr not in sheet.cells and (sheet_name, cell_addr) in self.cell_with_unsuccessfull_set: if interactive: self.invoke_interpreter = True if self.first_unknown_cell is None: self.first_unknown_cell = cell_addr if cell_addr in sheet.cells: cell = sheet.cells[cell_addr] if cell.formula is not None and cell.formula != cell.value: try: parse_tree = self.xlm_parser.parse(cell.formula) eval_result = self.evaluate_parse_tree(cell, parse_tree, False) return_val = eval_result.value text = eval_result.get_text() status = eval_result.status except: return_val = cell.formula text = EvalResult.wrap_str_literal(cell.formula) status = EvalStatus.FullEvaluation elif cell.value is not None: text = EvalResult.wrap_str_literal(cell.value, must_wrap=True) return_val = text status = EvalStatus.FullEvaluation else: text = "{}".format(cell_addr) else: if (sheet_name, cell_addr) in self.cell_with_unsuccessfull_set: text = "{}".format(cell_addr) else: text = '' status = EvalStatus.FullEvaluation else: text = XLMInterpreter.convert_ptree_to_str(parse_tree_root) return EvalResult(None, status, return_val, text) def evaluate_range(self, current_cell, interactive, parse_tree_root): status = EvalStatus.PartialEvaluation if len(parse_tree_root.children) >= 3: start_address = self.get_cell_addr(current_cell, parse_tree_root.children[0]) end_address = self.get_cell_addr(current_cell, parse_tree_root.children[2]) selected = None if len(parse_tree_root.children) == 5: selected = self.get_cell_addr(current_cell, parse_tree_root.children[4]) self.selected_range = (start_address, end_address, selected) status = EvalStatus.FullEvaluation text = XLMInterpreter.convert_ptree_to_str(parse_tree_root) return_val = text return EvalResult(None, status, return_val, text) def evaluate_array(self, current_cell, interactive, parse_tree_root): status = EvalStatus.PartialEvaluation array_elements = [] for index, array_elm in enumerate(parse_tree_root.children): # skip semicolon (;) if index % 2 == 1: continue if array_elm.type == 'NUMBER': array_elements.append(float(array_elm)) else: array_elements.append(str(array_elm)) text = str(array_elements) return_val = array_elements return EvalResult(None, status, return_val, text) def has_loop(self, path, length=10): if len(path) < length * 2: return False else: result = False start_index = len(path) - length for j in range(0, start_index - length): matched = True k = j while start_index + k - j < len(path): if path[k] != path[start_index + k - j]: matched = False break k += 1 if matched: result = True break return result regex_string = r'\"([^\"]|\"\")*\"' detect_string = re.compile(regex_string, flags=re.MULTILINE) def extract_strings(self, string): result = [] matches = XLMInterpreter.detect_string.finditer(string) for matchNum, match in enumerate(matches, start=1): result.append(match.string[match.start(0):match.end(0)]) return result def deobfuscate_macro(self, interactive, start_point="", timeout=0, silent_mode=False): result = [] self._start_timestamp = time.time() self.auto_labels = self.xlm_wrapper.get_defined_name('auto_open', full_match=False) self.auto_labels.extend(self.xlm_wrapper.get_defined_name('auto_close', full_match=False)) if len(self.auto_labels) == 0: if len(start_point) > 0: self.auto_labels = [('auto_open', start_point)] if self.auto_labels is not None and len(self.auto_labels) > 0: macros = self.xlm_wrapper.get_macrosheets() continue_emulation = True for auto_open_label in self.auto_labels: if not continue_emulation: break try: sheet_name, col, row = Cell.parse_cell_addr(auto_open_label[1]) if sheet_name in macros: current_cell = self.get_formula_cell(macros[sheet_name], col, row) self._branch_stack = [(current_cell, current_cell.formula, macros[sheet_name].cells, 0, '')] observed_cells = [] while len(self._branch_stack) > 0: if not continue_emulation: break current_cell, formula, saved_cells, indent_level, desc = self._branch_stack.pop() macros[current_cell.sheet.name].cells = saved_cells self._indent_level = indent_level stack_record = True while current_cell is not None: if not continue_emulation: break if type(formula) is str: replace_op = getattr(self.xlm_wrapper, "replace_nonprintable_chars", None) if callable(replace_op): formula = replace_op(formula, '_') if formula not in self._formula_cache: parse_tree = self.xlm_parser.parse(formula) self._formula_cache[formula] = parse_tree else: parse_tree = self._formula_cache[formula] else: parse_tree = formula if stack_record: previous_indent = self._indent_level - 1 if self._indent_level > 0 else 0 else: previous_indent = self._indent_level evaluation_result = self.evaluate_parse_tree(current_cell, parse_tree, interactive) if self._remove_current_formula_from_cache: self._remove_current_formula_from_cache = False if formula in self._formula_cache: del (self._formula_cache[formula]) if len(self._while_stack) == 0 and evaluation_result.text != 'NEXT': observed_cells.append(current_cell.get_local_address()) if self.has_loop(observed_cells): break if self.invoke_interpreter: self.invoke_interpreter = False self.first_unknown_cell = None continue if evaluation_result.value is not None: current_cell.value = str(evaluation_result.value) if evaluation_result.next_cell is None and \ (evaluation_result.status == EvalStatus.FullEvaluation or evaluation_result.status == EvalStatus.PartialEvaluation or evaluation_result.status == EvalStatus.NotImplemented or evaluation_result.status == EvalStatus.IGNORED): evaluation_result.next_cell = self.get_formula_cell(current_cell.sheet, current_cell.column, str(int(current_cell.row) + 1)) if stack_record: evaluation_result.text = ( desc + ' ' + evaluation_result.get_text(unwrap=False)).strip() if self._indent_current_line: previous_indent = self._indent_level self._indent_current_line = False if evaluation_result.status != EvalStatus.IGNORED: if self.output_level >= 3 and evaluation_result.output_level == 2: strings = self.extract_strings(evaluation_result.get_text(unwrap=True)) if strings: yield ( current_cell, evaluation_result.status, '\n'.join(strings), previous_indent) elif evaluation_result.output_level >= self.output_level: yield ( current_cell, evaluation_result.status, evaluation_result.get_text(unwrap=False), previous_indent) if timeout > 0 and time.time() - self._start_timestamp > timeout: continue_emulation = False if evaluation_result.next_cell is not None: current_cell = evaluation_result.next_cell else: break formula = current_cell.formula stack_record = False except Exception: passClass variables
var MAX_ISERROR_LOOPCOUNT-
The type of the None singleton.
var jump_functions-
The type of the None singleton.
var important_functions-
The type of the None singleton.
var important_methods-
The type of the None singleton.
var unicode_to_latin1_map-
The type of the None singleton.
var regex_string-
The type of the None singleton.
var detect_string-
The type of the None singleton.
Static methods
def is_float(text)-
Expand source code Browse git
@staticmethod def is_float(text): try: float(text) return True except (ValueError, TypeError): return False def is_int(text)-
Expand source code Browse git
@staticmethod def is_int(text): try: int(text) return True except (ValueError, TypeError): return False def is_bool(text)-
Expand source code Browse git
@staticmethod def is_bool(text): try: _strtobool(text) return True except (ValueError, TypeError, AttributeError): return False def convert_ptree_to_str(parse_tree_root)-
Expand source code Browse git
@staticmethod def convert_ptree_to_str(parse_tree_root): if type(parse_tree_root) == Token: return str(parse_tree_root) else: result = '' for child in parse_tree_root.children: result += XLMInterpreter.convert_ptree_to_str(child) return result
Methods
def convert_float(self, text)-
Expand source code Browse git
def convert_float(self, text): result = None text = text.lower() if text == 'false': result = 0 elif text == 'true': result = 1 else: result = float(text) return result def get_parser(self)-
Expand source code Browse git
def get_parser(self): left_bracket = self.xlm_wrapper.get_xl_international_char( XlApplicationInternational.xlLeftBracket) list_separator = self.xlm_wrapper.get_xl_international_char( XlApplicationInternational.xlListSeparator) right_bracket = self.xlm_wrapper.get_xl_international_char( XlApplicationInternational.xlRightBracket) return XLMParser(left_bracket, list_separator, right_bracket) def get_formula_cell(self, macrosheet, col, row)-
Expand source code Browse git
def get_formula_cell(self, macrosheet, col, row): result_cell = None not_found = False row = int(row) current_row = row current_addr = col + str(current_row) while current_addr not in macrosheet.cells or \ macrosheet.cells[current_addr].formula is None: if (current_row - row) < 10000: current_row += 1 else: not_found = True break current_addr = col + str(current_row) if not_found is False: result_cell = macrosheet.cells[current_addr] return result_cell def get_range_parts(self, parse_tree)-
Expand source code Browse git
def get_range_parts(self, parse_tree): if isinstance(parse_tree, Tree) and parse_tree.data == 'range': return parse_tree.children[0], parse_tree.children[-1] else: return None, None def get_cell_addr(self, current_cell, cell_parse_tree)-
Expand source code Browse git
def get_cell_addr(self, current_cell, cell_parse_tree): res_sheet = res_col = res_row = None if type(cell_parse_tree) is Token: names = self.xlm_wrapper.get_defined_names() label = cell_parse_tree.value.lower() if label in names: name_val = names[label] if isinstance(name_val, Tree): # example: 6a8045bc617df5f2b8f9325ed291ef05ac027144f1fda84e78d5084d26847902 res_sheet, res_col, res_row = self.get_cell_addr(current_cell, name_val) else: res_sheet, res_col, res_row = Cell.parse_cell_addr(name_val) elif label.strip('"') in names: res_sheet, res_col, res_row = Cell.parse_cell_addr(names[label.strip('"')]) else: if len(label) > 1 and label.startswith('"') and label.endswith('"'): label = label.strip('"') root_parse_tree = self.xlm_parser.parse('=' + label) res_sheet, res_col, res_row = self.get_cell_addr(current_cell, root_parse_tree.children[0]) else: if cell_parse_tree.data == 'defined_name': label = '{}'.format(cell_parse_tree.children[2]) formula_str = self.xlm_wrapper.get_defined_name(label) parsed_tree = self.xlm_parser.parse('=' + formula_str) if isinstance(parsed_tree.children[0], Tree) and parsed_tree.children[0].data == 'range': start_cell, end_cell = self.get_range_parts(parsed_tree.children[0]) cell = start_cell.children[0] else: cell = parsed_tree.children[0].children[0] else: cell = cell_parse_tree.children[0] if cell.data == 'a1_notation_cell': if len(cell.children) == 2: cell_addr = "'{}'!{}".format(cell.children[0], cell.children[1]) else: cell_addr = cell.children[0] res_sheet, res_col, res_row = Cell.parse_cell_addr(cell_addr) if res_sheet is None and res_col is not None: res_sheet = current_cell.sheet.name elif cell.data == 'r1c1_notation_cell': current_col = Cell.convert_to_column_index(current_cell.column) current_row = int(current_cell.row) for current_child in cell.children: if current_child.type == 'NAME': res_sheet = current_child.value elif self.is_float(current_child.value): val = int(float(current_child.value)) if last_seen == 'r': res_row = val else: res_col = val elif current_child.value.startswith('['): val = int(current_child.value[1:-1]) if last_seen == 'r': res_row = current_row + val else: res_col = current_col + val elif current_child.lower() == 'r': last_seen = 'r' res_row = current_row elif current_child.lower() == 'c': last_seen = 'c' res_col = current_col else: raise Exception('Cell addresss, Syntax Error') if res_sheet is None: res_sheet = current_cell.sheet.name res_row = str(res_row) res_col = Cell.convert_to_column_name(res_col) else: raise Exception('Cell addresss, Syntax Error') return res_sheet, res_col, res_row def get_cell(self, sheet_name, col, row)-
Expand source code Browse git
def get_cell(self, sheet_name, col, row): result = None sheets = self.xlm_wrapper.get_macrosheets() if sheet_name in sheets: sheet = sheets[sheet_name] addr = col + str(row) if addr in sheet.cells: result = sheet.cells[addr] else: sheets = self.xlm_wrapper.get_worksheets() if sheet_name in sheets: sheet = sheets[sheet_name] addr = col + str(row) if addr in sheet.cells: result = sheet.cells[addr] return result def get_worksheet_cell(self, sheet_name, col, row)-
Expand source code Browse git
def get_worksheet_cell(self, sheet_name, col, row): result = None sheets = self.xlm_wrapper.get_worksheets() if sheet_name in sheets: sheet = sheets[sheet_name] addr = col + str(row) if addr in sheet.cells: result = sheet.cells[addr] return result def set_cell(self, sheet_name, col, row, text, set_value_only=False)-
Expand source code Browse git
def set_cell(self, sheet_name, col, row, text, set_value_only=False): sheets = self.xlm_wrapper.get_macrosheets() if sheet_name in sheets: sheet = sheets[sheet_name] addr = col + str(row) if addr not in sheet.cells: new_cell = Cell() new_cell.column = col new_cell.row = row new_cell.sheet = sheet sheet.cells[addr] = new_cell cell = sheet.cells[addr] text = EvalResult.unwrap_str_literal(text) if not set_value_only: if text.startswith('='): cell.formula = text else: cell.formula = None cell.value = text def get_window(self, number)-
Expand source code Browse git
def get_window(self, number): return _WINDOW_DEFAULTS.get(number) def get_workspace(self, number)-
Expand source code Browse git
def get_workspace(self, number): return _WORKSPACE_DEFAULTS.get(number) def get_default_cell_info(self, number)-
Expand source code Browse git
def get_default_cell_info(self, number): return _CELL_DEFAULTS.get(number) def evaluate_formula(self, current_cell, name, arguments, interactive, destination_arg=1, set_value_only=False)-
Expand source code Browse git
def evaluate_formula(self, current_cell, name, arguments, interactive, destination_arg=1, set_value_only=False): # hash: fa391403aa028fa7b42a9f3491908f6f25414c35bfd104f8cf186220fb3b4f83" --> =FORMULA() if isinstance(arguments[0], list) and len(arguments[0]) == 0: return EvalResult(None, EvalStatus.FullEvaluation, False, "{}()".format(name)) source, destination = (arguments[0], arguments[1]) if destination_arg == 1 else (arguments[1], arguments[0]) src_eval_result = self.evaluate_parse_tree(current_cell, source, interactive) if isinstance(destination, Token): # TODO: get_defined_name must return a list; currently it returns list or one item destination = self.xlm_wrapper.get_defined_name(destination) if isinstance(destination, list): destination = [] if not destination else destination[0] if(isinstance(destination, str)): destination = self.xlm_parser.parse('=' + destination).children[0] if isinstance(destination, Tree): if destination.data == 'defined_name' or destination.data == 'name': defined_name_formula = self.xlm_wrapper.get_defined_name(destination.children[2]) if isinstance(defined_name_formula, Tree): destination = defined_name_formula else: destination = self.xlm_parser.parse('=' + defined_name_formula).children[0] if destination.data == 'concat_expression' or destination.data == 'function_call': res = self.evaluate_parse_tree(current_cell, destination, interactive) if isinstance(res.value, tuple) and len(res.value) == 3: destination_str = "'{}'!{}{}".format(res.value[0], res.value[1], res.value[2]) dst_start_sheet, dst_start_col, dst_start_row = res.value else: destination_str = res.text dst_start_sheet, dst_start_col, dst_start_row = Cell.parse_cell_addr(destination_str) dst_end_sheet, dst_end_col, dst_end_row = dst_start_sheet, dst_start_col, dst_start_row else: if destination.data == 'range': dst_start_sheet, dst_start_col, dst_start_row = self.get_cell_addr(current_cell, destination.children[0]) dst_end_sheet, dst_end_col, dst_end_row = self.get_cell_addr(current_cell, destination.children[2]) else: dst_start_sheet, dst_start_col, dst_start_row = self.get_cell_addr(current_cell, destination) dst_end_sheet, dst_end_col, dst_end_row = dst_start_sheet, dst_start_col, dst_start_row destination_str = XLMInterpreter.convert_ptree_to_str(destination) text = src_eval_result.get_text(unwrap=True) if src_eval_result.status == EvalStatus.FullEvaluation: for row in range(int(dst_start_row), int(dst_end_row) + 1): for col in range(Cell.convert_to_column_index(dst_start_col), Cell.convert_to_column_index(dst_end_col) + 1): if ( dst_start_sheet, Cell.convert_to_column_name(col) + str(row)) in self.cell_with_unsuccessfull_set: self.cell_with_unsuccessfull_set.remove((dst_start_sheet, Cell.convert_to_column_name(col) + str(row))) self.set_cell(dst_start_sheet, Cell.convert_to_column_name(col), str(row), str(src_eval_result.value), set_value_only) else: for row in range(int(dst_start_row), int(dst_end_row) + 1): for col in range(Cell.convert_to_column_index(dst_start_col), Cell.convert_to_column_index(dst_end_col) + 1): self.cell_with_unsuccessfull_set.add((dst_start_sheet, Cell.convert_to_column_name(col) + str(row))) if destination_arg == 1: text = "{}({},{})".format(name, src_eval_result.get_text(), destination_str) else: text = "{}({},{})".format(name, destination_str, src_eval_result.get_text()) return_val = 0 return EvalResult(None, src_eval_result.status, return_val, text) def evaluate_argument_list(self, current_cell, name, arguments)-
Expand source code Browse git
def evaluate_argument_list(self, current_cell, name, arguments): args_str = '' for argument in arguments: if type(argument) is Token or type(argument) is Tree: arg_eval_Result = self.evaluate_parse_tree(current_cell, argument, False) args_str += arg_eval_Result.get_text() + ',' args_str = args_str.strip(',') return_val = text = '={}({})'.format(name, args_str) status = EvalStatus.PartialEvaluation return EvalResult(None, status, return_val, text) def evaluate_function(self, current_cell, parse_tree_root, interactive)-
Expand source code Browse git
def evaluate_function(self, current_cell, parse_tree_root, interactive): # function name can be a string literal (double quoted or unqouted), and Tree (defined name, cell, function_call) function_name = parse_tree_root.children[0] function_name_literal = EvalResult.unwrap_str_literal(function_name) # OFFSET()() if isinstance(function_name, Tree) and function_name.data == 'function_call': func_eval_result = self.evaluate_parse_tree(current_cell, function_name, False) if func_eval_result.status != EvalStatus.FullEvaluation: return EvalResult(func_eval_result.next_cell, func_eval_result.status, 0, XLMInterpreter.convert_ptree_to_str(parse_tree_root)) else: func_eval_result.text = XLMInterpreter.convert_ptree_to_str(parse_tree_root) return func_eval_result # handle alias name for a function (REGISTER) # c45ed3a0ce5df27ac29e0fab99dc4d462f61a0d0c025e9161ced3b2c913d57d8 if function_name_literal in self._registered_functions: parse_tree_root.children[0] = parse_tree_root.children[0].update( None, self._registered_functions[function_name_literal]['name']) return self.evaluate_function(current_cell, parse_tree_root, interactive) # cell_function_call if isinstance(function_name, Tree) and function_name.data == 'cell': self._function_call_stack.append(current_cell) return self.goto_handler([function_name], current_cell, interactive, parse_tree_root) # test() if function_name_literal.lower() in self.defined_names: try: ref_parsed = self.xlm_parser.parse('=' + self.defined_names[function_name_literal.lower()]) if isinstance(ref_parsed.children[0], Tree) and ref_parsed.children[0].data == 'cell': function_name = ref_parsed.children[0] else: raise Exception except: function_name = self.defined_names[function_name_literal.lower()] # x!test() if isinstance(function_name, Tree) and function_name.data == 'defined_name': function_lable = function_name.children[-1].value if function_lable.lower() in self.defined_names: try: ref_parsed = self.xlm_parser.parse('=' + self.defined_names[function_lable.lower()]) if isinstance(ref_parsed.children[0], Tree) and ref_parsed.children[0].data == 'cell': function_name = ref_parsed.children[0] else: raise Exception except: function_name = self.defined_names[function_name_literal.lower()] # cell_function_call if isinstance(function_name, Tree) and function_name.data == 'cell': self._function_call_stack.append(current_cell) return self.goto_handler([function_name], current_cell, interactive, parse_tree_root) if self.ignore_processing and function_name_literal != 'NEXT': return EvalResult(None, EvalStatus.IGNORED, 0, '') arguments = [] for i in parse_tree_root.children[2].children: if type(i) is not Token: if len(i.children) > 0: arguments.append(i.children[0]) else: arguments.append(i.children) if function_name_literal in self._handlers: eval_result = self._handlers[function_name_literal](arguments, current_cell, interactive, parse_tree_root) else: eval_result = self.evaluate_argument_list(current_cell, function_name_literal, arguments) if function_name_literal in XLMInterpreter.jump_functions: eval_result.output_level = 0 elif function_name_literal in XLMInterpreter.important_functions: eval_result.output_level = 2 else: eval_result.output_level = 1 return eval_result def and_handler(self, arguments, current_cell, interactive, parse_tree_root)-
Expand source code Browse git
def and_handler(self, arguments, current_cell, interactive, parse_tree_root): value = True status = EvalStatus.FullEvaluation for arg in arguments: arg_eval_result = self.evaluate_parse_tree(current_cell, arg, interactive) if arg_eval_result.status == EvalStatus.FullEvaluation: if EvalResult.unwrap_str_literal(str(arg_eval_result.value)).lower() != "true": value = False break else: status = EvalStatus.PartialEvaluation value = False break return EvalResult(None, status, value, str(value)) def or_handler(self, arguments, current_cell, interactive, parse_tree_root)-
Expand source code Browse git
def or_handler(self, arguments, current_cell, interactive, parse_tree_root): value = False status = EvalStatus.FullEvaluation for arg in arguments: arg_eval_result = self.evaluate_parse_tree(current_cell, arg, interactive) if arg_eval_result.status == EvalStatus.FullEvaluation: if EvalResult.unwrap_str_literal(str(arg_eval_result.value)).lower() == "true": value = True break else: status = EvalStatus.PartialEvaluation break return EvalResult(None, status, value, str(value)) def hlookup_handler(self, arguments, current_cell, interactive, parse_tree_root)-
Expand source code Browse git
def hlookup_handler(self, arguments, current_cell, interactive, parse_tree_root): status = EvalStatus.FullEvaluation value = "" arg_eval_result1 = self.evaluate_parse_tree(current_cell, arguments[0], interactive) arg_eval_result2 = self.evaluate_parse_tree(current_cell, arguments[1], interactive) arg_eval_result3 = self.evaluate_parse_tree(current_cell, arguments[2], interactive) arg_eval_result4 = self.evaluate_parse_tree(current_cell, arguments[3], interactive) regex = arg_eval_result1.text.strip('"') if regex == '*': regex = ".*" if arg_eval_result4.value == "FALSE": sheet_name, startcolumn, startrow, endcolumn, endrow = Cell.parse_range_addr(arg_eval_result2.text) status = EvalStatus.FullEvaluation start_col_index = Cell.convert_to_column_index(startcolumn) end_col_index = Cell.convert_to_column_index(endcolumn) start_row_index = int(startrow) + int(arg_eval_result3.value) - 1 end_row_index = int(endrow) for row in range(start_row_index, end_row_index + 1): for col in range(start_col_index, end_col_index + 1): if (sheet_name != None): cell = self.get_worksheet_cell(sheet_name, Cell.convert_to_column_name(col), str(row)) else: cell = self.get_cell(current_cell.sheet.name, Cell.convert_to_column_name(col), str(row)) if cell and re.match(regex, cell.value): return EvalResult(None, status, cell.value, str(cell.value)) else: status = EvalStatus.PartialEvaluation return EvalResult(None, status, value, str(value)) def not_handler(self, arguments, current_cell, interactive, parse_tree_root)-
Expand source code Browse git
def not_handler(self, arguments, current_cell, interactive, parse_tree_root): value = True status = EvalStatus.FullEvaluation arg_eval_result = self.evaluate_parse_tree(current_cell, arguments[0], interactive) if arg_eval_result.status == EvalStatus.FullEvaluation: if EvalResult.unwrap_str_literal(str(arg_eval_result.value)).lower() == "true": value = False else: status = EvalStatus.PartialEvaluation return EvalResult(None, status, value, str(value)) def code_handler(self, arguments, current_cell, interactive, parse_tree_root)-
Expand source code Browse git
def code_handler(self, arguments, current_cell, interactive, parse_tree_root): status = EvalStatus.FullEvaluation value = 0 arg_eval_result = self.evaluate_parse_tree(current_cell, arguments[0], interactive) if arg_eval_result.status == EvalStatus.FullEvaluation: if arg_eval_result.text != '': value = ord(arg_eval_result.text[0]) if value > 256 and value in XLMInterpreter.unicode_to_latin1_map: value = XLMInterpreter.unicode_to_latin1_map[value] else: status = EvalStatus.PartialEvaluation return EvalResult(None, status, value, str(value)) def sum_handler(self, arguments, current_cell, interactive, parse_tree_root)-
Expand source code Browse git
def sum_handler(self, arguments, current_cell, interactive, parse_tree_root): status = EvalStatus.FullEvaluation value = 0 it = 0 for arg in arguments: arg_eval_result = self.evaluate_parse_tree(current_cell, arguments[it], interactive) value = value + float(arg_eval_result.value) status = arg_eval_result.status it = it + 1 return EvalResult(None, status, value, str(value)) def randbetween_handler(self, arguments, current_cell, interactive, parse_tree_root)-
Expand source code Browse git
def randbetween_handler(self, arguments, current_cell, interactive, parse_tree_root): arg_eval_result1 = self.evaluate_parse_tree(current_cell, arguments[0], interactive) arg_eval_result2 = self.evaluate_parse_tree(current_cell, arguments[1], interactive) value = 0 # Initial implementation for integer if arg_eval_result1.status == EvalStatus.FullEvaluation and arg_eval_result2.status == EvalStatus.FullEvaluation: status = EvalStatus.FullEvaluation value = random.randint(int(float(arg_eval_result1.value)), int(float(arg_eval_result2.value))) return EvalResult(None, status, value, str(value)) def text_handler(self, arguments, current_cell, interactive, parse_tree_root)-
Expand source code Browse git
def text_handler(self, arguments, current_cell, interactive, parse_tree_root): arg_eval_result1 = self.evaluate_parse_tree(current_cell, arguments[0], interactive) arg_eval_result2 = self.evaluate_parse_tree(current_cell, arguments[1], interactive) value = 0 status = EvalStatus.PartialEvaluation # Initial implementation for integer if arg_eval_result1.status == EvalStatus.FullEvaluation and int(arg_eval_result2.text.strip('\"')) == 0: status = EvalStatus.FullEvaluation value = int(arg_eval_result1.value) return EvalResult(None, status, value, str(value)) def active_cell_handler(self, arguments, current_cell, interactive, parse_tree_root)-
Expand source code Browse git
def active_cell_handler(self, arguments, current_cell, interactive, parse_tree_root): status = EvalStatus.PartialEvaluation if self.active_cell: if self.active_cell.formula: parse_tree = self.xlm_parser.parse(self.active_cell.formula) eval_res = self.evaluate_parse_tree(current_cell, parse_tree, interactive) val = eval_res.value status = eval_res.status else: val = self.active_cell.value status = EvalStatus.FullEvaluation return_val = val text = str(return_val) else: text = XLMInterpreter.convert_ptree_to_str(parse_tree_root) return_val = text return EvalResult(None, status, return_val, text) def get_cell_handler(self, arguments, current_cell, interactive, parse_tree_root)-
Expand source code Browse git
def get_cell_handler(self, arguments, current_cell, interactive, parse_tree_root): if len(arguments) == 2: arg1_eval_result = self.evaluate_parse_tree(current_cell, arguments[0], interactive) dst_sheet, dst_col, dst_row = self.get_cell_addr(current_cell, arguments[1]) type_id = arg1_eval_result.value if self.is_float(type_id): type_id = int(float(type_id)) if dst_sheet is None: dst_sheet = current_cell.sheet.name status = EvalStatus.PartialEvaluation if arg1_eval_result.status == EvalStatus.FullEvaluation: data, not_exist, not_implemented = self.xlm_wrapper.get_cell_info(dst_sheet, dst_col, dst_row, type_id) if not_exist and 1 == 2: return_val = self.get_default_cell_info(type_id) text = str(return_val) status = EvalStatus.FullEvaluation elif not_implemented: text = XLMInterpreter.convert_ptree_to_str(parse_tree_root) return_val = '' else: text = str(data) if data is not None else None return_val = data status = EvalStatus.FullEvaluation else: text = XLMInterpreter.convert_ptree_to_str(parse_tree_root) return_val = '' status = EvalStatus.PartialEvaluation return EvalResult(None, status, return_val, text) def set_name_handler(self, arguments, current_cell, interactive, parse_tree_root)-
Expand source code Browse git
def set_name_handler(self, arguments, current_cell, interactive, parse_tree_root): label = EvalResult.unwrap_str_literal(XLMInterpreter.convert_ptree_to_str(arguments[0])).lower() if isinstance(arguments[1], Tree) and arguments[1].data == 'cell': arg2_text = XLMInterpreter.convert_ptree_to_str(arguments[1]) names = self.xlm_wrapper.get_defined_names() names[label] = arguments[1] text = 'SET.NAME({},{})'.format(label, arg2_text) return_val = 0 status = EvalStatus.FullEvaluation else: arg2_eval_result = self.evaluate_parse_tree(current_cell, arguments[1], interactive) if arg2_eval_result.status is EvalStatus.FullEvaluation: arg2_text = arg2_eval_result.get_text(unwrap=True) names = self.xlm_wrapper.get_defined_names() if isinstance(arg2_eval_result.value, Cell): names[label] = arg2_eval_result.value else: names[label] = arg2_text text = 'SET.NAME({},{})'.format(label, arg2_text) return_val = 0 status = EvalStatus.FullEvaluation else: return_val = text = XLMInterpreter.convert_ptree_to_str(parse_tree_root) status = arg2_eval_result.status return EvalResult(None, status, return_val, text) def end_if_handler(self, arguments, current_cell, interactive, parse_tree_root)-
Expand source code Browse git
def end_if_handler(self, arguments, current_cell, interactive, parse_tree_root): self._indent_level -= 1 self._indent_current_line = True status = EvalStatus.FullEvaluation return EvalResult(None, status, 'END.IF', 'END.IF') def get_workspace_handler(self, arguments, current_cell, interactive, parse_tree_root)-
Expand source code Browse git
def get_workspace_handler(self, arguments, current_cell, interactive, parse_tree_root): status = EvalStatus.PartialEvaluation if len(arguments) == 1: arg1_eval_Result = self.evaluate_parse_tree(current_cell, arguments[0], interactive) if arg1_eval_Result.status == EvalStatus.FullEvaluation and self.is_float(arg1_eval_Result.get_text()): workspace_param = self.get_workspace(int(float(arg1_eval_Result.get_text()))) # current_cell.value = workspace_param text = 'GET.WORKSPACE({})'.format(arg1_eval_Result.get_text()) return_val = workspace_param status = EvalStatus.FullEvaluation next_cell = None if status == EvalStatus.PartialEvaluation: return_val = text = XLMInterpreter.convert_ptree_to_str(parse_tree_root) return EvalResult(None, status, return_val, text) def get_window_handler(self, arguments, current_cell, interactive, parse_tree_root)-
Expand source code Browse git
def get_window_handler(self, arguments, current_cell, interactive, parse_tree_root): status = EvalStatus.Error if len(arguments) == 1: arg_eval_result = self.evaluate_parse_tree(current_cell, arguments[0], interactive) if arg_eval_result.status == EvalStatus.FullEvaluation and self.is_float(arg_eval_result.get_text()): window_param = self.get_window(int(float(arg_eval_result.get_text()))) # current_cell.value = window_param text = window_param # XLMInterpreter.convert_ptree_to_str(parse_tree_root) return_val = window_param # Overwrites to take actual values from the workbook instead of default config if int(float(arg_eval_result.get_text())) == 1 or int(float(arg_eval_result.get_text())) == 30: return_val = "[" + self.xlm_wrapper.get_workbook_name() + "]" + current_cell.sheet.name status = EvalStatus.FullEvaluation status = EvalStatus.FullEvaluation else: return_val = text = 'GET.WINDOW({})'.format(arg_eval_result.get_text()) status = arg_eval_result.status if status == EvalStatus.Error: return_val = text = XLMInterpreter.convert_ptree_to_str(parse_tree_root) return EvalResult(None, status, return_val, text) def get_document_handler(self, arguments, current_cell, interactive, parse_tree_root)-
Expand source code Browse git
def get_document_handler(self, arguments, current_cell, interactive, parse_tree_root): status = EvalStatus.Error arg_eval_result = self.evaluate_parse_tree(current_cell, arguments[0], interactive) return_val = "" # Static implementation if self.is_int(arg_eval_result.value): status = EvalStatus.PartialEvaluation if int(arg_eval_result.value) == 76: return_val = "[" + self.xlm_wrapper.get_workbook_name() + "]" + current_cell.sheet.name status = EvalStatus.FullEvaluation elif int(arg_eval_result.value) == 88: return_val = self.xlm_wrapper.get_workbook_name() status = EvalStatus.FullEvaluation text = return_val return EvalResult(None, status, return_val, text) def on_time_handler(self, arguments, current_cell, interactive, parse_tree_root)-
Expand source code Browse git
def on_time_handler(self, arguments, current_cell, interactive, parse_tree_root): status = EvalStatus.Error if len(arguments) == 2: arg1_eval_result = self.evaluate_parse_tree(current_cell, arguments[0], interactive) next_sheet, next_col, next_row = self.get_cell_addr(current_cell, arguments[1]) sheets = self.xlm_wrapper.get_macrosheets() if next_sheet in sheets: next_cell = self.get_formula_cell(sheets[next_sheet], next_col, next_row) text = 'ON.TIME({},{})'.format(arg1_eval_result.get_text(), str(next_cell)) status = EvalStatus.FullEvaluation return_val = 0 return_val = text = XLMInterpreter.convert_ptree_to_str(parse_tree_root) if status == EvalStatus.Error: next_cell = None return EvalResult(next_cell, status, return_val, text) def app_maximize_handler(self, arguments, current_cell, interactive, parse_tree_root)-
Expand source code Browse git
def app_maximize_handler(self, arguments, current_cell, interactive, parse_tree_root): status = EvalStatus.FullEvaluation return_val = True text = str(return_val) return EvalResult(None, status, return_val, text) def concatenate_handler(self, arguments, current_cell, interactive, parse_tree_root)-
Expand source code Browse git
def concatenate_handler(self, arguments, current_cell, interactive, parse_tree_root): text = '' for arg in arguments: arg_eval_result = self.evaluate_parse_tree(current_cell, arg, interactive) text += arg_eval_result.get_text(unwrap=True) return_val = text text = EvalResult.wrap_str_literal(text) status = EvalStatus.FullEvaluation return EvalResult(None, status, return_val, text) def day_handler(self, arguments, current_cell, interactive, parse_tree_root)-
Expand source code Browse git
def day_handler(self, arguments, current_cell, interactive, parse_tree_root): if self.day_of_month is None: arg1_eval_result = self.evaluate_parse_tree(current_cell, arguments[0], interactive) if arg1_eval_result.status == EvalStatus.FullEvaluation: if type(arg1_eval_result.value) is datetime.datetime: # # text = str(arg1_eval_result.value.day) # return_val = text # status = EvalStatus.FullEvaluation return_val, status, text = self.guess_day() elif self.is_float(arg1_eval_result.value): text = 'DAY(Serial Date)' status = EvalStatus.NotImplemented else: text = XLMInterpreter.convert_ptree_to_str(parse_tree_root) status = arg1_eval_result.status else: text = str(self.day_of_month) return_val = text status = EvalStatus.FullEvaluation return EvalResult(None, status, return_val, text) def guess_day(self)-
Expand source code Browse git
def guess_day(self): xlm = self min = 1 best_day = 0 for day in range(1, 32): xlm.char_error_count = 0 non_printable_ascii = 0 total_count = 0 xlm = copy.copy(xlm) xlm.day_of_month = day try: for index, step in enumerate(xlm.deobfuscate_macro(False, silent_mode=True)): for char in step[2]: if not (32 <= ord(char) <= 128): non_printable_ascii += 1 total_count += len(step[2]) if index > 10 and ((non_printable_ascii + xlm.char_error_count) / total_count) > min: break if total_count != 0 and ((non_printable_ascii + xlm.char_error_count) / total_count) < min: min = ((non_printable_ascii + xlm.char_error_count) / total_count) best_day = day if min == 0: break except Exception as exp: pass self.day_of_month = best_day text = str(self.day_of_month) return_val = text status = EvalStatus.FullEvaluation return return_val, status, text def excel_date(self, date1)-
Expand source code Browse git
def excel_date(self, date1): temp = datetime.datetime(1899, 12, 30) # Note, not 31st Dec but 30th! delta = date1 - temp return float(delta.days) + (float(delta.seconds) / 86400) def now_handler(self, arguments, current_cell, interactive, parse_tree_root)-
Expand source code Browse git
def now_handler(self, arguments, current_cell, interactive, parse_tree_root): return_val = text = self.excel_date(datetime.datetime.now() + datetime.timedelta(seconds=self._now_count * self._now_step)) self._now_count += 1 status = EvalStatus.FullEvaluation return EvalResult(None, status, return_val, text) def value_handler(self, arguments, current_cell, interactive, parse_tree_root)-
Expand source code Browse git
def value_handler(self, arguments, current_cell, interactive, parse_tree_root): return_val_result = self.evaluate_parse_tree(current_cell, arguments[0], interactive) status = EvalStatus.FullEvaluation value = EvalResult.unwrap_str_literal(return_val_result.value) if EvalResult.is_int(value): return_val = int(value) text = str(return_val) elif EvalResult.is_float(value): return_val = float(value) text = str(return_val) else: status = EvalStatus.Error text = self.convert_ptree_to_str(parse_tree_root) return_val = 0 return EvalResult(None, status, return_val, text) def if_handler(self, arguments, current_cell, interactive, parse_tree_root)-
Expand source code Browse git
def if_handler(self, arguments, current_cell, interactive, parse_tree_root): visited = False for stack_frame in self._branch_stack: if stack_frame[0].get_local_address() == current_cell.get_local_address(): visited = True if visited is False: # self._indent_level += 1 size = len(arguments) if size == 3: cond_eval_result = self.evaluate_parse_tree(current_cell, arguments[0], interactive) if self.is_bool(cond_eval_result.value): cond_eval_result.value = bool(_strtobool(cond_eval_result.value)) elif self.is_int(cond_eval_result.value): if int(cond_eval_result.value) == 0: cond_eval_result.value = False else: cond_eval_result.value = True if cond_eval_result.status == EvalStatus.FullEvaluation: if cond_eval_result.value: if type(arguments[1]) is Tree or type(arguments[1]) is Token: self._branch_stack.append( (current_cell, arguments[1], current_cell.sheet.cells, self._indent_level, '[TRUE]')) status = EvalStatus.Branching else: status = EvalStatus.FullEvaluation else: if type(arguments[2]) is Tree or type(arguments[2]) is Token: self._branch_stack.append( (current_cell, arguments[2], current_cell.sheet.cells, self._indent_level, '[FALSE]')) status = EvalStatus.Branching else: status = EvalStatus.FullEvaluation text = XLMInterpreter.convert_ptree_to_str(parse_tree_root) else: memory_state = copy.deepcopy(current_cell.sheet.cells) if type(arguments[2]) is Tree or type(arguments[2]) is Token or type(arguments[2]) is list: self._branch_stack.append( (current_cell, arguments[2], memory_state, self._indent_level, '[FALSE]')) if type(arguments[1]) is Tree or type(arguments[1]) is Token or type(arguments[1]) is list: self._branch_stack.append( (current_cell, arguments[1], current_cell.sheet.cells, self._indent_level, '[TRUE]')) text = XLMInterpreter.convert_ptree_to_str(parse_tree_root) status = EvalStatus.FullBranching else: status = EvalStatus.FullEvaluation text = XLMInterpreter.convert_ptree_to_str(parse_tree_root) else: # loop detected text = '[[LOOP]]: ' + XLMInterpreter.convert_ptree_to_str(parse_tree_root) status = EvalStatus.End return EvalResult(None, status, 0, text) def mid_handler(self, arguments, current_cell, interactive, parse_tree_root)-
Expand source code Browse git
def mid_handler(self, arguments, current_cell, interactive, parse_tree_root): str_eval_result = self.evaluate_parse_tree(current_cell, arguments[0], interactive) base_eval_result = self.evaluate_parse_tree(current_cell, arguments[1], interactive) len_eval_result = self.evaluate_parse_tree(current_cell, arguments[2], interactive) status = EvalStatus.PartialEvaluation return_val = "" if str_eval_result.status == EvalStatus.FullEvaluation: if base_eval_result.status == EvalStatus.FullEvaluation and \ len_eval_result.status == EvalStatus.FullEvaluation: if self.is_float(base_eval_result.value) and self.is_float(len_eval_result.value): base = int(float(base_eval_result.value)) - 1 length = int(float(len_eval_result.value)) return_val = EvalResult.unwrap_str_literal(str_eval_result.value)[base: base + length] text = str(return_val) status = EvalStatus.FullEvaluation if status == EvalStatus.PartialEvaluation: text = 'MID({},{},{})'.format(XLMInterpreter.convert_ptree_to_str(arguments[0]), XLMInterpreter.convert_ptree_to_str(arguments[1]), XLMInterpreter.convert_ptree_to_str(arguments[2])) return EvalResult(None, status, return_val, text) def min_handler(self, arguments, current_cell, interactive, parse_tree_root)-
Expand source code Browse git
def min_handler(self, arguments, current_cell, interactive, parse_tree_root): min = None status = EvalStatus.PartialEvaluation for argument in arguments: arg_eval_result = self.evaluate_parse_tree(current_cell, argument, interactive) if arg_eval_result.status == EvalStatus.FullEvaluation: cur_val = self.convert_float(arg_eval_result.value) if not min or cur_val < min: min = cur_val else: min = None break if min: return_val = min text = str(min) status = EvalStatus.FullEvaluation else: text = return_val = self.convert_ptree_to_str(parse_tree_root) return EvalResult(None, status, return_val, text) def max_handler(self, arguments, current_cell, interactive, parse_tree_root)-
Expand source code Browse git
def max_handler(self, arguments, current_cell, interactive, parse_tree_root): max = None status = EvalStatus.PartialEvaluation for argument in arguments: arg_eval_result = self.evaluate_parse_tree(current_cell, argument, interactive) if arg_eval_result.status == EvalStatus.FullEvaluation: cur_val = self.convert_float(arg_eval_result.value) if not max or cur_val > max: max = cur_val else: max = None break if max: return_val = max text = str(max) status = EvalStatus.FullEvaluation else: text = return_val = self.convert_ptree_to_str(parse_tree_root) return EvalResult(None, status, return_val, text) def product_handler(self, arguments, current_cell, interactive, parse_tree_root)-
Expand source code Browse git
def product_handler(self, arguments, current_cell, interactive, parse_tree_root): total = None status = EvalStatus.PartialEvaluation for argument in arguments: arg_eval_result = self.evaluate_parse_tree(current_cell, argument, interactive) if arg_eval_result.status == EvalStatus.FullEvaluation: if not total: total = self.convert_float(arg_eval_result.value) else: total *= self.convert_float(arg_eval_result.value) else: total = None break if total: return_val = total text = str(total) status = EvalStatus.FullEvaluation else: text = return_val = self.convert_ptree_to_str(parse_tree_root) return EvalResult(None, status, return_val, text) def mod_handler(self, arguments, current_cell, interactive, parse_tree_root)-
Expand source code Browse git
def mod_handler(self, arguments, current_cell, interactive, parse_tree_root): arg1_eval_res = self.evaluate_parse_tree(current_cell, arguments[0], interactive) arg2_eval_res = self.evaluate_parse_tree(current_cell, arguments[1], interactive) if arg1_eval_res.status == EvalStatus.FullEvaluation and arg2_eval_res.status == EvalStatus.FullEvaluation: return_val = float(arg1_eval_res.value) % float(arg2_eval_res.value) text = str(return_val) status = EvalStatus.FullEvaluation return EvalResult(None, status, return_val, text) def sqrt_handler(self, arguments, current_cell, interactive, parse_tree_root)-
Expand source code Browse git
def sqrt_handler(self, arguments, current_cell, interactive, parse_tree_root): arg1_eval_res = self.evaluate_parse_tree(current_cell, arguments[0], interactive) status = EvalStatus.PartialEvaluation if arg1_eval_res.status == EvalStatus.FullEvaluation: return_val = math.floor(math.sqrt(float(arg1_eval_res.value))) text = str(return_val) status = EvalStatus.FullEvaluation if status == EvalStatus.PartialEvaluation: return_val = text = self.convert_ptree_to_str(parse_tree_root) return EvalResult(None, status, return_val, text) def goto_handler(self, arguments, current_cell, interactive, parse_tree_root)-
Expand source code Browse git
def goto_handler(self, arguments, current_cell, interactive, parse_tree_root): next_sheet, next_col, next_row = self.get_cell_addr(current_cell, arguments[0]) next_cell = None if next_sheet is not None and next_sheet in self.xlm_wrapper.get_macrosheets(): next_cell = self.get_formula_cell(self.xlm_wrapper.get_macrosheets()[next_sheet], next_col, next_row) status = EvalStatus.FullEvaluation else: status = EvalStatus.Error text = XLMInterpreter.convert_ptree_to_str(parse_tree_root) return_val = 0 return EvalResult(next_cell, status, return_val, text) def halt_handler(self, arguments, current_cell, interactive, parse_tree_root)-
Expand source code Browse git
def halt_handler(self, arguments, current_cell, interactive, parse_tree_root): return_val = text = XLMInterpreter.convert_ptree_to_str(parse_tree_root) status = EvalStatus.End self._indent_level -= 1 return EvalResult(None, status, return_val, text) def call_handler(self, arguments, current_cell, interactive, parse_tree_root)-
Expand source code Browse git
def call_handler(self, arguments, current_cell, interactive, parse_tree_root): argument_texts = [] status = EvalStatus.FullEvaluation for argument in arguments: arg_eval_result = self.evaluate_parse_tree(current_cell, argument, interactive) if arg_eval_result.status != EvalStatus.FullEvaluation: status = arg_eval_result.status argument_texts.append(arg_eval_result.get_text()) list_separator = self.xlm_wrapper.get_xl_international_char(XlApplicationInternational.xlListSeparator) text = 'CALL({})'.format(list_separator.join(argument_texts)) return_val = 0 return EvalResult(None, status, return_val, text) def is_number_handler(self, arguments, current_cell, interactive, parse_tree_root)-
Expand source code Browse git
def is_number_handler(self, arguments, current_cell, interactive, parse_tree_root): eval_result = self.evaluate_parse_tree(current_cell, arguments[0], interactive) if eval_result.status == EvalStatus.FullEvaluation: if self.is_int(eval_result.text) or self.is_float(eval_result.text): return_val = 1 else: return_val = 0 text = str(return_val) else: text = 'ISNUMBER({})'.format(eval_result.get_text()) return_val = 1 # true return EvalResult(None, eval_result.status, return_val, text) def search_handler(self, arguments, current_cell, interactive, parse_tree_root)-
Expand source code Browse git
def search_handler(self, arguments, current_cell, interactive, parse_tree_root): arg1_eval_res = self.evaluate_parse_tree(current_cell, arguments[0], interactive) arg2_eval_res = self.evaluate_parse_tree(current_cell, arguments[1], interactive) if arg1_eval_res.status == EvalStatus.FullEvaluation and arg2_eval_res.status == EvalStatus.FullEvaluation: try: arg1_val = EvalResult.unwrap_str_literal(str(arg1_eval_res.value)) arg2_val = EvalResult.unwrap_str_literal(arg2_eval_res.value) return_val = arg2_val.lower().index(arg1_val.lower()) text = str(return_val) except ValueError: return_val = None text = '' status = EvalStatus.FullEvaluation else: text = 'SEARCH({},{})'.format(arg1_eval_res.get_text(), arg2_eval_res.get_text()) return_val = 0 status = EvalStatus.PartialEvaluation return EvalResult(None, status, return_val, text) def round_handler(self, arguments, current_cell, interactive, parse_tree_root)-
Expand source code Browse git
def round_handler(self, arguments, current_cell, interactive, parse_tree_root): arg1_eval_res = self.evaluate_parse_tree(current_cell, arguments[0], interactive) arg2_eval_res = self.evaluate_parse_tree(current_cell, arguments[1], interactive) if arg1_eval_res.status == EvalStatus.FullEvaluation and arg2_eval_res.status == EvalStatus.FullEvaluation: return_val = round(float(arg1_eval_res.value), int(float(arg2_eval_res.value))) text = str(return_val) status = EvalStatus.FullEvaluation return EvalResult(None, status, return_val, text) def roundup_handler(self, arguments, current_cell, interactive, parse_tree_root)-
Expand source code Browse git
def roundup_handler(self, arguments, current_cell, interactive, parse_tree_root): arg1_eval_res = self.evaluate_parse_tree(current_cell, arguments[0], interactive) if arg1_eval_res.status == EvalStatus.FullEvaluation: return_val = math.ceil(float(arg1_eval_res.value)) text = str(return_val) status = EvalStatus.FullEvaluation return EvalResult(None, status, return_val, text) def directory_handler(self, arguments, current_cell, interactive, parse_tree_root)-
Expand source code Browse git
def directory_handler(self, arguments, current_cell, interactive, parse_tree_root): text = r'C:\Users\user\Documents' return_val = text status = EvalStatus.FullEvaluation return EvalResult(None, status, return_val, text) def char_handler(self, arguments, current_cell, interactive, parse_tree_root)-
Expand source code Browse git
def char_handler(self, arguments, current_cell, interactive, parse_tree_root): arg_eval_result = self.evaluate_parse_tree(current_cell, arguments[0], interactive) if arg_eval_result.status == EvalStatus.FullEvaluation: value = arg_eval_result.text if arg_eval_result.value in self.defined_names: value = self.defined_names[arg_eval_result.value].value if 0 <= float(value) <= 255: return_val = text = chr(int(float(value))) # cell = self.get_formula_cell(current_cell.sheet, current_cell.column, current_cell.row) # cell.value = text status = EvalStatus.FullEvaluation else: return_val = text = XLMInterpreter.convert_ptree_to_str(parse_tree_root) self.char_error_count += 1 status = EvalStatus.Error else: text = 'CHAR({})'.format(arg_eval_result.text) return_val = text status = EvalStatus.PartialEvaluation return EvalResult(arg_eval_result.next_cell, status, return_val, text) def t_handler(self, arguments, current_cell, interactive, parse_tree_root)-
Expand source code Browse git
def t_handler(self, arguments, current_cell, interactive, parse_tree_root): arg_eval_result = self.evaluate_parse_tree(current_cell, arguments[0], interactive) return_val = '' if arg_eval_result.status == EvalStatus.FullEvaluation: if isinstance(arg_eval_result.value, tuple) and len(arg_eval_result.value) == 3: cell = self.get_cell(arg_eval_result.value[0], arg_eval_result.value[1], arg_eval_result.value[2]) return_val = cell.value elif arg_eval_result.value != 'TRUE' and arg_eval_result.value != 'FALSE': return_val = str(arg_eval_result.value) status = EvalStatus.FullEvaluation else: status = EvalStatus.PartialEvaluation return EvalResult(arg_eval_result.next_cell, status, return_val, EvalResult.wrap_str_literal(str(return_val), must_wrap=True)) def int_handler(self, arguments, current_cell, interactive, parse_tree_root)-
Expand source code Browse git
def int_handler(self, arguments, current_cell, interactive, parse_tree_root): arg_eval_result = self.evaluate_parse_tree(current_cell, arguments[0], interactive) return_val = int(0) if arg_eval_result.status == EvalStatus.FullEvaluation: text = str(arg_eval_result.value).lower() if text == "true": return_val = int(1) elif text == "false": return_val = int(0) else: return_val = int(arg_eval_result.value) status = EvalStatus.FullEvaluation else: status = EvalStatus.PartialEvaluation return EvalResult(arg_eval_result.next_cell, status, return_val, str(return_val)) def run_handler(self, arguments, current_cell, interactive, parse_tree_root)-
Expand source code Browse git
def run_handler(self, arguments, current_cell, interactive, parse_tree_root): size = len(arguments) next_cell = None status = EvalStatus.PartialEvaluation if 1 <= size <= 2: next_sheet, next_col, next_row = self.get_cell_addr(current_cell, arguments[0]) if next_sheet is not None and next_sheet in self.xlm_wrapper.get_macrosheets(): next_cell = self.get_formula_cell(self.xlm_wrapper.get_macrosheets()[next_sheet], next_col, next_row) if size == 1: text = 'RUN({}!{}{})'.format(next_sheet, next_col, next_row) else: text = 'RUN({}!{}{}, {})'.format(next_sheet, next_col, next_row, XLMInterpreter.convert_ptree_to_str(arguments[1])) status = EvalStatus.FullEvaluation else: text = XLMInterpreter.convert_ptree_to_str(parse_tree_root) status = EvalStatus.Error return_val = 0 else: text = XLMInterpreter.convert_ptree_to_str(parse_tree_root) status = EvalStatus.Error return_val = 1 return EvalResult(next_cell, status, return_val, text) def formula_handler(self, arguments, current_cell, interactive, parse_tree_root)-
Expand source code Browse git
def formula_handler(self, arguments, current_cell, interactive, parse_tree_root): return self.evaluate_formula(current_cell, 'FORMULA', arguments, interactive) def formula_fill_handler(self, arguments, current_cell, interactive, parse_tree_root)-
Expand source code Browse git
def formula_fill_handler(self, arguments, current_cell, interactive, parse_tree_root): return self.evaluate_formula(current_cell, 'FORMULA.FILL', arguments, interactive) def formula_array_handler(self, arguments, current_cell, interactive, parse_tree_root)-
Expand source code Browse git
def formula_array_handler(self, arguments, current_cell, interactive, parse_tree_root): return self.evaluate_formula(current_cell, 'FORMULA.ARRAY', arguments, interactive) def set_value_handler(self, arguments, current_cell, interactive, parse_tree_root)-
Expand source code Browse git
def set_value_handler(self, arguments, current_cell, interactive, parse_tree_root): return self.evaluate_formula( current_cell, 'SET.VALUE', arguments, interactive, destination_arg=2, set_value_only=True) def error_handler(self, arguments, current_cell, interactive, parse_tree_root)-
Expand source code Browse git
def error_handler(self, arguments, current_cell, interactive, parse_tree_root): return EvalResult(None, EvalStatus.FullEvaluation, 0, XLMInterpreter.convert_ptree_to_str(parse_tree_root)) def select_handler(self, arguments, current_cell, interactive, parse_tree_root)-
Expand source code Browse git
def select_handler(self, arguments, current_cell, interactive, parse_tree_root): status = EvalStatus.PartialEvaluation range_eval_result = self.evaluate_parse_tree(current_cell, arguments[0], interactive) if len(arguments) == 2: # e.g., SELECT(B1:B100,B1) and SELECT(,"R[1]C") if self.active_cell: sheet, col, row = self.get_cell_addr(self.active_cell, arguments[1]) else: sheet, col, row = self.get_cell_addr(current_cell, arguments[1]) if sheet: self.active_cell = self.get_cell(sheet, col, row) status = EvalStatus.FullEvaluation elif isinstance(arguments[0], Token): text = XLMInterpreter.convert_ptree_to_str(parse_tree_root) return_val = 0 elif arguments[0].data == 'range': # e.g., SELECT(D1:D10:D1) sheet, col, row = self.selected_range[2] if sheet: self.active_cell = self.get_cell(sheet, col, row) status = EvalStatus.FullEvaluation elif arguments[0].data == 'cell': # select(R1C1) if self.active_cell: sheet, col, row = self.get_cell_addr(self.active_cell, arguments[0]) else: sheet, col, row = self.get_cell_addr(current_cell, arguments[0]) if sheet: self.active_cell = self.get_cell(sheet, col, row) status = EvalStatus.FullEvaluation text = XLMInterpreter.convert_ptree_to_str(parse_tree_root) return_val = 0 return EvalResult(None, status, return_val, text) def iterate_range(self, name, start_cell, end_cell)-
Expand source code Browse git
def iterate_range(self, name, start_cell, end_cell): sheet_name = start_cell[0] row_start = int(start_cell[2]) row_end = int(end_cell[2]) for row_index in range(row_start, row_end + 1): col_start = Cell.convert_to_column_index(start_cell[1]) col_end = Cell.convert_to_column_index(end_cell[1]) for col_index in range(col_start, col_end+1): next_cell = self.get_cell(sheet_name, Cell.convert_to_column_name(col_index), row_index) if next_cell: yield next_cell def forcell_handler(self, arguments, current_cell, interactive, parse_tree_root)-
Expand source code Browse git
def forcell_handler(self, arguments, current_cell, interactive, parse_tree_root): var_eval_result = self.evaluate_parse_tree(current_cell, arguments[0], interactive) start_cell_ptree, end_cell_ptree = self.get_range_parts(arguments[1]) start_cell = self.get_cell_addr(current_cell, start_cell_ptree) end_cell = self.get_cell_addr(current_cell, end_cell_ptree) if start_cell[0] != end_cell[0]: end_cell = (start_cell[0], end_cell[1], end_cell[2]) skip = False if len(arguments) >= 3: skip_eval_result = self.evaluate_parse_tree(current_cell, arguments[2], interactive) skip = bool(skip_eval_result.value) variable_name = EvalResult.unwrap_str_literal(var_eval_result.value).lower() if len(self._while_stack) > 0 and self._while_stack[-1]['start_point'] == current_cell: iterator = self._while_stack[-1]['iterator'] else: iterator = self.iterate_range(variable_name, start_cell, end_cell) stack_record = {'start_point': current_cell, 'status': True, 'iterator': iterator} self._while_stack.append(stack_record) try: self.defined_names[variable_name] = next(iterator) except: self._while_stack[-1]['status'] = False self._indent_level += 1 return EvalResult(None, EvalStatus.FullEvaluation, 0 , self.convert_ptree_to_str(parse_tree_root)) def while_handler(self, arguments, current_cell, interactive, parse_tree_root)-
Expand source code Browse git
def while_handler(self, arguments, current_cell, interactive, parse_tree_root): status = EvalStatus.PartialEvaluation text = '' stack_record = {'start_point': current_cell, 'status': False} condition_eval_result = self.evaluate_parse_tree(current_cell, arguments[0], interactive) status = condition_eval_result.status if condition_eval_result.status == EvalStatus.FullEvaluation: if str(condition_eval_result.value).lower() == 'true': stack_record['status'] = True text = '{} -> [{}]'.format(XLMInterpreter.convert_ptree_to_str(parse_tree_root), str(condition_eval_result.value)) if not text: text = '{}'.format(XLMInterpreter.convert_ptree_to_str(parse_tree_root)) self._while_stack.append(stack_record) if stack_record['status'] == False: self.ignore_processing = True self._indent_level += 1 return EvalResult(None, status, 0, text) def next_handler(self, arguments, current_cell, interactive, parse_tree_root)-
Expand source code Browse git
def next_handler(self, arguments, current_cell, interactive, parse_tree_root): status = EvalStatus.FullEvaluation next_cell = None if self._indent_level == len(self._while_stack): self.ignore_processing = False next_cell = None if len(self._while_stack) > 0: top_record = self._while_stack.pop() if top_record['status'] is True: next_cell = top_record['start_point'] if 'iterator' in top_record: self._while_stack.append(top_record) self._indent_level = self._indent_level - 1 if self._indent_level > 0 else 0 self._indent_current_line = True if next_cell is None: status = EvalStatus.IGNORED return EvalResult(next_cell, status, 0, 'NEXT') def len_handler(self, arguments, current_cell, interactive, parse_tree_root)-
Expand source code Browse git
def len_handler(self, arguments, current_cell, interactive, parse_tree_root): arg_eval_result = self.evaluate_parse_tree(current_cell, arguments[0], interactive) if arg_eval_result.status == EvalStatus.FullEvaluation: return_val = len(arg_eval_result.get_text(unwrap=True)) text = str(return_val) status = EvalStatus.FullEvaluation else: text = XLMInterpreter.convert_ptree_to_str(parse_tree_root) return_val = text status = EvalStatus.PartialEvaluation return EvalResult(None, status, return_val, text) def define_name_handler(self, arguments, current_cell, interactive, parse_tree_root)-
Expand source code Browse git
def define_name_handler(self, arguments, current_cell, interactive, parse_tree_root): arg_name_eval_result = self.evaluate_parse_tree(current_cell, arguments[0], interactive) status = EvalStatus.PartialEvaluation if arg_name_eval_result.status == EvalStatus.FullEvaluation: arg_val_eval_result = self.evaluate_parse_tree(current_cell, arguments[1], interactive) status = EvalStatus.FullEvaluation name = EvalResult.unwrap_str_literal(arg_name_eval_result.text).lower() if EvalResult.is_int(arg_val_eval_result.value): self.defined_names[name] = int(arg_val_eval_result.value) elif EvalResult.is_float(arg_val_eval_result.value): self.defined_names[name] = float(arg_val_eval_result.value) else: self.defined_names[name] = arg_val_eval_result.value return_val = self.defined_names[name] text = "DEFINE.NAME({},{})".format(EvalResult.wrap_str_literal(name), str(return_val)) else: return_val = text = self.convert_ptree_to_str(parse_tree_root) return EvalResult(None, status, return_val, text) def index_handler(self, arguments, current_cell, interactive, parse_tree_root)-
Expand source code Browse git
def index_handler(self, arguments, current_cell, interactive, parse_tree_root): array_arg_result = self.evaluate_parse_tree(current_cell, arguments[0], interactive) status = EvalStatus.PartialEvaluation if array_arg_result.status == EvalStatus.FullEvaluation: index_arg_result = self.evaluate_parse_tree(current_cell, arguments[1], interactive) if isinstance(array_arg_result.value, list): # example: f9adf499bc16bfd096e00bc59c3233f022dec20c20440100d56e58610e4aded3 return_val = array_arg_result.value[int(float(index_arg_result.value))-1] # index starts at 1 in excel else: # example: 6a8045bc617df5f2b8f9325ed291ef05ac027144f1fda84e78d5084d26847902 range = EvalResult.unwrap_str_literal(array_arg_result.value) parsed_range = Cell.parse_range_addr(range) index = int(float(index_arg_result.value))-1 row_str = str(int(float(parsed_range[2])) + index) if parsed_range[0]: sheet_name = parsed_range[0] else: sheet_name = current_cell.sheet.name return_val = self.get_cell(sheet_name, parsed_range[1], row_str) text = str(return_val) status = EvalStatus.FullEvaluation else: return_val = text = self.convert_ptree_to_str(parse_tree_root) return EvalResult(None, status, return_val, text) def rows_handler(self, arguments, current_cell, interactive, parse_tree_root)-
Expand source code Browse git
def rows_handler(self, arguments, current_cell, interactive, parse_tree_root): arg_eval_result = self.evaluate_parse_tree(current_cell, arguments[0], interactive) status = EvalStatus.PartialEvaluation if arg_eval_result.status == EvalStatus.FullEvaluation: if isinstance(arg_eval_result.value, list): # example: f9adf499bc16bfd096e00bc59c3233f022dec20c20440100d56e58610e4aded3 return_val = len(arg_eval_result.value) else: # example: 6a8045bc617df5f2b8f9325ed291ef05ac027144f1fda84e78d5084d26847902 range = EvalResult.unwrap_str_literal(arg_eval_result.value) parsed_range = Cell.parse_range_addr(range) return_val = int(parsed_range[4]) - int(parsed_range[2]) + 1 text = str(return_val) status = EvalStatus.FullEvaluation else: return_val = text = self.convert_ptree_to_str(parse_tree_root) return EvalResult(None, status, return_val, text) def counta_handler(self, arguments, current_cell, interactive, parse_tree_root)-
Expand source code Browse git
def counta_handler(self, arguments, current_cell, interactive, parse_tree_root): arg_eval_result = self.evaluate_parse_tree(current_cell, arguments[0], interactive) sheet_name, startcolumn, startrow, endcolumn, endrow = Cell.parse_range_addr(arg_eval_result.text) count = 0 it = int(startrow) start_col_index = Cell.convert_to_column_index(startcolumn) end_col_index = Cell.convert_to_column_index(endcolumn) start_row_index = int(startrow) end_row_index = int(endrow) val_item_count = 0 for row in range(start_row_index, end_row_index + 1): for col in range(start_col_index, end_col_index + 1): if (sheet_name != None): cell = self.get_worksheet_cell(sheet_name, Cell.convert_to_column_name(col), str(row)) else: cell = self.get_cell(current_cell.sheet.name, Cell.convert_to_column_name(col), str(row)) if cell and cell.value != '': val_item_count += 1 return_val = val_item_count status = EvalStatus.FullEvaluation text = str(return_val) return EvalResult(None, status, return_val, text) def count_handler(self, arguments, current_cell, interactive, parse_tree_root)-
Expand source code Browse git
def count_handler(self, arguments, current_cell, interactive, parse_tree_root): return_val = len(arguments) text = str(return_val) status = EvalStatus.FullEvaluation return EvalResult(None, status, return_val, text) def trunc_handler(self, arguments, current_cell, interactive, parse_tree_root)-
Expand source code Browse git
def trunc_handler(self, arguments, current_cell, interactive, parse_tree_root): arg_eval_result = self.evaluate_parse_tree(current_cell, arguments[0], interactive) if arg_eval_result.status == EvalStatus.FullEvaluation: if arg_eval_result.value == "TRUE": return_val = 1 elif arg_eval_result.value == "FALSE": return_val = 0 else: return_val = math.trunc(float(arg_eval_result.value)) text = str(return_val) status = EvalStatus.FullEvaluation else: text = XLMInterpreter.convert_ptree_to_str(parse_tree_root) return_val = text status = EvalStatus.PartialEvaluation return EvalResult(None, status, return_val, text) def quotient_handler(self, arguments, current_cell, interactive, parse_tree_root)-
Expand source code Browse git
def quotient_handler(self, arguments, current_cell, interactive, parse_tree_root): numerator_arg_eval_result = self.evaluate_parse_tree(current_cell, arguments[0], interactive) Denominator_arg_eval_result = self.evaluate_parse_tree(current_cell, arguments[0], interactive) status = EvalStatus.PartialEvaluation if numerator_arg_eval_result.status == EvalStatus.FullEvaluation and \ Denominator_arg_eval_result.status == EvalStatus.FullEvaluation: return_val = numerator_arg_eval_result.value // Denominator_arg_eval_result.value text = str(return_val) status = EvalStatus.FullEvaluation return EvalResult(None, status, return_val, text) def abs_handler(self, arguments, current_cell, interactive, parse_tree_root)-
Expand source code Browse git
def abs_handler(self, arguments, current_cell, interactive, parse_tree_root): arg_eval_result = self.evaluate_parse_tree(current_cell, arguments[0], interactive) if arg_eval_result.status == EvalStatus.FullEvaluation: if arg_eval_result.value == "TRUE": return_val = 1 elif arg_eval_result.value == "FALSE": return_val = 0 else: return_val = abs(float(arg_eval_result.value)) text = str(return_val) status = EvalStatus.FullEvaluation else: text = XLMInterpreter.convert_ptree_to_str(parse_tree_root) return_val = text status = EvalStatus.PartialEvaluation return EvalResult(None, status, return_val, text) def absref_handler(self, arguments, current_cell, interactive, parse_tree_root)-
Expand source code Browse git
def absref_handler(self, arguments, current_cell, interactive, parse_tree_root): arg_ref_txt_eval_result = self.evaluate_parse_tree(current_cell, arguments[0], interactive) status = EvalStatus.PartialEvaluation if arg_ref_txt_eval_result.status == EvalStatus.FullEvaluation and \ (isinstance(arguments[1], Tree) and arguments[1].data == 'cell'): offset_addr_text = arg_ref_txt_eval_result.value base_addr_text = self.convert_ptree_to_str(arguments[1]) return_val = Cell.get_abs_addr(base_addr_text, offset_addr_text) status = EvalStatus.FullEvaluation else: return_val = XLMInterpreter.convert_ptree_to_str(parse_tree_root) return EvalResult(None, status, return_val, str(return_val)) def address_handler(self, arguments, current_cell, interactive, parse_tree_root)-
Expand source code Browse git
def address_handler(self, arguments, current_cell, interactive, parse_tree_root): arg_row_num_eval_result = self.evaluate_parse_tree(current_cell, arguments[0], interactive) arg_col_num_eval_result = self.evaluate_parse_tree(current_cell, arguments[1], interactive) optional_args = True if len(arguments) >= 3: arg_abs_num_eval_result = self.evaluate_parse_tree(current_cell, arguments[2], interactive) if arg_abs_num_eval_result.status == EvalStatus.FullEvaluation: abs_num = arg_abs_num_eval_result.value else: optional_args = False else: abs_num = 1 if len(arguments) >= 4: arg_a1_eval_result = self.evaluate_parse_tree(current_cell, arguments[3], interactive) if arg_a1_eval_result.status == EvalStatus.FullEvaluation: a1 = arg_a1_eval_result.value else: optional_args = False else: a1 = "TRUE" if len(arguments) >= 5: arg_sheet_eval_result = self.evaluate_parse_tree(current_cell, arguments[4], interactive) if arg_sheet_eval_result.status == EvalStatus.FullEvaluation: sheet_name = arg_sheet_eval_result.text.strip('\"') else: optional_args = False else: sheet_name = current_cell.sheet.name return_val = '' if arg_row_num_eval_result.status == EvalStatus.FullEvaluation and \ arg_col_num_eval_result.status == EvalStatus.FullEvaluation and \ optional_args: return_val += sheet_name + '!' if a1 == "FALSE": cell_addr_tmpl = 'R{}C{}' if abs_num == 2: cell_addr_tmpl = 'R{}C[{}]' elif abs_num == 3: cell_addr_tmpl = 'R[{}]C{}' elif abs_num == 4: cell_addr_tmpl = 'R[{}]C[{}]' return_val += cell_addr_tmpl.format(arg_row_num_eval_result.text, arg_col_num_eval_result.text) else: cell_addr_tmpl = '${}${}' if abs_num == 2: cell_addr_tmpl = '{}${}' elif abs_num == 3: cell_addr_tmpl = '${}{}' elif abs_num == 4: cell_addr_tmpl = '{}{}' return_val += cell_addr_tmpl.format(Cell.convert_to_column_name(int(arg_col_num_eval_result.value)), arg_row_num_eval_result.text) status = EvalStatus.FullEvaluation else: status = EvalStatus.PartialEvaluation return_val = self.evaluate_parse_tree(current_cell, arguments, False) return EvalResult(None, status, return_val, str(return_val)) def indirect_handler(self, arguments, current_cell, interactive, parse_tree_root)-
Expand source code Browse git
def indirect_handler(self, arguments, current_cell, interactive, parse_tree_root): arg_addr_eval_result = self.evaluate_parse_tree(current_cell, arguments[0], interactive) status = EvalStatus.PartialEvaluation if arg_addr_eval_result.status == EvalStatus.FullEvaluation: a1 = "TRUE" if len(arguments) == 2: arg_a1_eval_result = self.evaluate_parse_tree(current_cell, arguments[1], interactive) if arg_a1_eval_result.status == EvalStatus.FullEvaluation: a1 = arg_a1_eval_result.value sheet_name, col, row = Cell.parse_cell_addr(arg_addr_eval_result.value) indirect_cell = self.get_cell(sheet_name, col, row) return_val = indirect_cell.value status = EvalStatus.FullEvaluation else: return_val = self.evaluate_parse_tree(current_cell, arguments, False) return EvalResult(None, status, return_val, str(return_val)) def register_handler(self, arguments, current_cell, interactive, parse_tree_root)-
Expand source code Browse git
def register_handler(self, arguments, current_cell, interactive, parse_tree_root): if len(arguments) >= 4: arg_list = [] status = EvalStatus.FullEvaluation for index, arg in enumerate(arguments): if index > 3: break res_eval = self.evaluate_parse_tree(current_cell, arg, interactive) arg_list.append(res_eval.get_text(unwrap=True)) function_name = "{}.{}".format(arg_list[0], arg_list[1]) # signature: https://support.office.com/en-us/article/using-the-call-and-register-functions-06fa83c1-2869-4a89-b665-7e63d188307f function_signature = arg_list[2] function_alias = arg_list[3] # overrides previously registered function self._registered_functions[function_alias] = {'name': function_name, 'signature': function_signature} text = self.evaluate_argument_list(current_cell, 'REGISTER', arguments).get_text(unwrap=True) else: status = EvalStatus.Error text = XLMInterpreter.convert_ptree_to_str(parse_tree_root) return_val = 0 return EvalResult(None, status, return_val, text) def registerid_handler(self, arguments, current_cell, interactive, parse_tree_root)-
Expand source code Browse git
def registerid_handler(self, arguments, current_cell, interactive, parse_tree_root): if len(arguments) >= 3: arg_list = [] status = EvalStatus.FullEvaluation for index, arg in enumerate(arguments): if index > 2: break res_eval = self.evaluate_parse_tree(current_cell, arg, interactive) arg_list.append(res_eval.get_text(unwrap=True)) function_name = "{}.{}".format(arg_list[0], arg_list[1]) # signature: https://support.office.com/en-us/article/using-the-call-and-register-functions-06fa83c1-2869-4a89-b665-7e63d188307f function_signature = arg_list[2] #function_alias = arg_list[3] # overrides previously registered function #self._registered_functions[function_alias] = {'name': function_name, 'signature': function_signature} text = self.evaluate_argument_list(current_cell, 'REGISTER.ID', arguments).get_text(unwrap=True) return_val = function_name else: status = EvalStatus.Error text = XLMInterpreter.convert_ptree_to_str(parse_tree_root) return_val = 0 return EvalResult(None, status, return_val, text) def return_handler(self, arguments, current_cell, interactive, parse_tree_root)-
Expand source code Browse git
def return_handler(self, arguments, current_cell, interactive, parse_tree_root): arg1_eval_res = self.evaluate_parse_tree(current_cell, arguments[0], interactive) if self._function_call_stack: return_cell = self._function_call_stack.pop() return_cell.value = arg1_eval_res.value arg1_eval_res.next_cell = self.get_formula_cell(return_cell.sheet, return_cell.column, str(int(return_cell.row) + 1)) if arg1_eval_res.text == '': arg1_eval_res.text = 'RETURN()' return arg1_eval_res def fopen_handler(self, arguments, current_cell, interactive, parse_tree_root)-
Expand source code Browse git
def fopen_handler(self, arguments, current_cell, interactive, parse_tree_root): arg1_eval_res = self.evaluate_parse_tree(current_cell, arguments[0], interactive) if len(arguments) > 1: arg2_eval_res = self.evaluate_parse_tree(current_cell, arguments[1], interactive) access = arg2_eval_res.value else: access = '1' if arg1_eval_res.status == EvalStatus.FullEvaluation: file_name = arg1_eval_res.get_text(unwrap=True) else: file_name = "default_name" if file_name not in self._files: self._files[file_name] = {'file_access': access, 'file_content': ''} text = 'FOPEN({},{})'.format(arg1_eval_res.get_text(unwrap=False), access) return EvalResult(None, arg1_eval_res.status, file_name, text) def fsize_handler(self, arguments, current_cell, interactive, parse_tree_root, end_line='')-
Expand source code Browse git
def fsize_handler(self, arguments, current_cell, interactive, parse_tree_root, end_line=''): arg1_eval_res = self.evaluate_parse_tree(current_cell, arguments[0], interactive) file_name = arg1_eval_res.get_text(unwrap=True) status = EvalStatus.PartialEvaluation return_val = 0 if file_name in self._files: status = EvalStatus.FullEvaluation if self._files[file_name]['file_content'] is not None: return_val = len(self._files[file_name]['file_content']) text = 'FSIZE({})'.format(EvalResult.wrap_str_literal(file_name)) return EvalResult(None, status, return_val, str(return_val)) def fwrite_handler(self, arguments, current_cell, interactive, parse_tree_root, end_line='')-
Expand source code Browse git
def fwrite_handler(self, arguments, current_cell, interactive, parse_tree_root, end_line=''): arg1_eval_res = self.evaluate_parse_tree(current_cell, arguments[0], interactive) arg2_eval_res = self.evaluate_parse_tree(current_cell, arguments[1], interactive) file_name = arg1_eval_res.value if file_name.strip() == "" or EvalResult.is_int(file_name) or EvalResult.is_float(file_name): if len(self._files) > 0: file_name = list(self._files.keys())[0] else: file_name = "default_filename" file_content = arg2_eval_res.get_text(unwrap=True) status = EvalStatus.PartialEvaluation if file_name in self._files: status = EvalStatus.FullEvaluation self._files[file_name]['file_content'] += file_content + end_line text = 'FWRITE({},{})'.format(EvalResult.wrap_str_literal(file_name), EvalResult.wrap_str_literal(file_content)) return EvalResult(None, status, '0', text) def fwriteln_handler(self, arguments, current_cell, interactive, parse_tree_root)-
Expand source code Browse git
def fwriteln_handler(self, arguments, current_cell, interactive, parse_tree_root): return self.fwrite_handler(arguments, current_cell, interactive, parse_tree_root, end_line='\r\n') def files_handler(self, arguments, current_cell, interactive, parse_tree_root, end_line='')-
Expand source code Browse git
def files_handler(self, arguments, current_cell, interactive, parse_tree_root, end_line=''): arg1_eval_res = self.evaluate_parse_tree(current_cell, arguments[0], interactive) dir_name = arg1_eval_res.get_text(unwrap=True) status = EvalStatus.FullEvaluation # if dir_name in self._files: # return_val = dir_name # else: # return_val = None return_val = dir_name text = "FILES({})".format(EvalResult.wrap_str_literal(dir_name)) return EvalResult(None, status, return_val, text) def iserror_handler(self, arguments, current_cell, interactive, parse_tree_root, end_line='')-
Expand source code Browse git
def iserror_handler(self, arguments, current_cell, interactive, parse_tree_root, end_line=''): arg1_eval_res = self.evaluate_parse_tree(current_cell, arguments[0], interactive) status = EvalStatus.FullEvaluation if arg1_eval_res.value == None: return_val = True else: return_val = False if self._iserror_loc is None: self._iserror_val = return_val self._iserror_loc = current_cell self._iserror_count = 1 elif self._iserror_loc == current_cell: if self._iserror_val != return_val: self._iserror_val = return_val self._iserror_count = 1 elif self._iserror_count < XLMInterpreter.MAX_ISERROR_LOOPCOUNT: self._iserror_count += 1 else: return_val = not return_val self._iserror_loc = None text = 'ISERROR({})'.format(EvalResult.wrap_str_literal(arg1_eval_res.get_text(unwrap=True))) return EvalResult(None, status, return_val, text) def offset_handler(self, arguments, current_cell, interactive, parse_tree_root)-
Expand source code Browse git
def offset_handler(self, arguments, current_cell, interactive, parse_tree_root): value = 0 next = None status = EvalStatus.PartialEvaluation cell = self.get_cell_addr(current_cell, arguments[0]) row_index = self.evaluate_parse_tree(current_cell, arguments[1], interactive) col_index = self.evaluate_parse_tree(current_cell, arguments[2], interactive) if isinstance(cell, tuple) and \ row_index.status == EvalStatus.FullEvaluation and \ col_index.status == EvalStatus.FullEvaluation: row = str(int(cell[2]) + int(float(str(row_index.value)))) col = Cell.convert_to_column_name(Cell.convert_to_column_index(cell[1]) + int(float(str(col_index.value)))) ref_cell = (cell[0], col, row) value = ref_cell status = EvalStatus.FullEvaluation next = self.get_formula_cell(self.xlm_wrapper.get_macrosheets()[cell[0]], col, row) text = XLMInterpreter.convert_ptree_to_str(parse_tree_root) return EvalResult(next, status, value, text) def arabic_hander(self, arguments, current_cell, interactive, parse_tree_root, end_line='')-
Expand source code Browse git
def arabic_hander(self, arguments, current_cell, interactive, parse_tree_root, end_line=''): arg1_eval_res = self.evaluate_parse_tree(current_cell, arguments[0], interactive) if arg1_eval_res.status == EvalStatus.FullEvaluation: roman_number = EvalResult.get_text(arg1_eval_res, unwrap=True) return_val = _from_roman(roman_number) status = EvalStatus.FullEvaluation text = str(return_val) else: return_val = text = self.convert_ptree_to_str(parse_tree_root) return EvalResult(None, status, return_val, text) def VirtualAlloc_handler(self, arguments, current_cell, interactive, parse_tree_root)-
Expand source code Browse git
def VirtualAlloc_handler(self, arguments, current_cell, interactive, parse_tree_root): base_eval_res = self.evaluate_parse_tree(current_cell, arguments[0], interactive) size_eval_res = self.evaluate_parse_tree(current_cell, arguments[1], interactive) if base_eval_res.status == EvalStatus.FullEvaluation and size_eval_res.status == EvalStatus.FullEvaluation: base = int(base_eval_res.get_text(unwrap=True)) occupied_addresses = [rec['base'] + rec['size'] for rec in self._memory] for memory_record in self._memory: if memory_record['base'] <= base <= (memory_record['base'] + memory_record['size']): base = map(max, occupied_addresses) + 4096 size = int(size_eval_res.get_text(unwrap=True)) self._memory.append({ 'base': base, 'size': size, 'data': [0] * size }) return_val = base status = EvalStatus.FullEvaluation else: status = EvalStatus.PartialEvaluation return_val = 0 text = XLMInterpreter.convert_ptree_to_str(parse_tree_root) return EvalResult(None, status, return_val, text) def WriteProcessMemory_handler(self, arguments, current_cell, interactive, parse_tree_root)-
Expand source code Browse git
def WriteProcessMemory_handler(self, arguments, current_cell, interactive, parse_tree_root): status = EvalStatus.PartialEvaluation if len(arguments) > 4: status = EvalStatus.FullEvaluation args_eval_result = [] for arg in arguments: arg_eval_res = self.evaluate_parse_tree(current_cell, arg, interactive) if arg_eval_res.status != EvalStatus.FullEvaluation: status = arg_eval_res.status args_eval_result.append(arg_eval_res) if status == EvalStatus.FullEvaluation: base_address = int(args_eval_result[1].value) mem_data = args_eval_result[2].value mem_data = bytearray([ord(x) for x in mem_data]) size = int(args_eval_result[3].value) if not self.write_memory(base_address, mem_data, size): status = EvalStatus.Error text = 'Kernel32.WriteProcessMemory({},{},"{}",{},{})'.format( args_eval_result[0].get_text(), base_address, mem_data.hex(), size, args_eval_result[4].get_text()) return_val = 0 if status != EvalStatus.FullEvaluation: text = XLMInterpreter.convert_ptree_to_str(parse_tree_root) return_val = 0 return EvalResult(None, status, return_val, text) def RtlCopyMemory_handler(self, arguments, current_cell, interactive, parse_tree_root)-
Expand source code Browse git
def RtlCopyMemory_handler(self, arguments, current_cell, interactive, parse_tree_root): status = EvalStatus.PartialEvaluation if len(arguments) == 3: destination_eval_res = self.evaluate_parse_tree(current_cell, arguments[0], interactive) src_eval_res = self.evaluate_parse_tree(current_cell, arguments[1], interactive) size_res = self.evaluate_parse_tree(current_cell, arguments[2], interactive) if destination_eval_res.status == EvalStatus.FullEvaluation and \ src_eval_res.status == EvalStatus.FullEvaluation: status = EvalStatus.FullEvaluation mem_data = src_eval_res.value mem_data = bytearray([ord(x) for x in mem_data]) if not self.write_memory(int(destination_eval_res.value), mem_data, len(mem_data)): status = EvalStatus.Error text = 'Kernel32.RtlCopyMemory({},"{}",{})'.format( destination_eval_res.get_text(), mem_data.hex(), size_res.get_text()) if status == EvalStatus.PartialEvaluation: text = XLMInterpreter.convert_ptree_to_str(parse_tree_root) return_val = 0 return EvalResult(None, status, return_val, text) def write_memory(self, base_address, mem_data, size)-
Expand source code Browse git
def write_memory(self, base_address, mem_data, size): result = True for mem_rec in self._memory: if mem_rec['base'] <= base_address <= mem_rec['base'] + mem_rec['size']: if mem_rec['base'] <= base_address + size <= mem_rec['base'] + mem_rec['size']: offset = base_address - mem_rec['base'] for i in range(0, size): mem_rec['data'][offset + i] = mem_data[i] else: result = False break return result def evaluate_defined_name(self, current_cell, name, interactive)-
Expand source code Browse git
def evaluate_defined_name(self, current_cell, name, interactive): result = None lname = name.lower() if lname in self.defined_names: val = self.defined_names[lname] if isinstance(val, Tree) and val.data == 'cell': eval_res = self.evaluate_cell(current_cell, interactive, val) result = eval_res.value elif isinstance(val, list): result = val else: if isinstance(val, Cell): data = val.value else: # example: c7e40628fb6beb52d9d73a3b3afd1dca5d2335713593b698637e1a47b42bfc71 password: 2021 data = val try: formula_str = str(data) if str(data).startswith('=') else '=' + str(data) parsed_formula = self.xlm_parser.parse(formula_str) eval_result = self.evaluate_parse_tree(current_cell, parsed_formula, interactive) if isinstance(eval_result.value, list): result = eval_result.value else: result = str(eval_result.value) except: result = str(data) return result def evaluate_parse_tree(self, current_cell, parse_tree_root, interactive=True)-
Expand source code Browse git
def evaluate_parse_tree(self, current_cell, parse_tree_root, interactive=True): next_cell = None status = EvalStatus.NotImplemented text = None return_val = None if type(parse_tree_root) is Token: if parse_tree_root.value.lower() in self.defined_names: # this formula has a defined name that can be changed # current formula must be removed from cache self._remove_current_formula_from_cache = True parse_tree_root.value = self.evaluate_defined_name(current_cell, parse_tree_root.value, interactive) return_val = parse_tree_root.value status = EvalStatus.FullEvaluation text = str(return_val) result = EvalResult(next_cell, status, return_val, text) elif type(parse_tree_root) is list: return_val = text = '' status = EvalStatus.FullEvaluation result = EvalResult(next_cell, status, return_val, text) elif parse_tree_root.data == 'function_call': result = self.evaluate_function(current_cell, parse_tree_root, interactive) elif parse_tree_root.data == 'cell': result = self.evaluate_cell(current_cell, interactive, parse_tree_root) elif parse_tree_root.data == 'range': result = self.evaluate_range(current_cell, interactive, parse_tree_root) elif parse_tree_root.data == 'array': result = self.evaluate_array(current_cell, interactive, parse_tree_root) elif parse_tree_root.data in self._expr_rule_names: text_left = None concat_status = EvalStatus.FullEvaluation for index, child in enumerate(parse_tree_root.children): if type(child) is Token and child.type in ['ADDITIVEOP', 'MULTIOP', 'CMPOP', 'CONCATOP']: op_str = str(child) right_arg = parse_tree_root.children[index + 1] right_arg_eval_res = self.evaluate_parse_tree(current_cell, right_arg, interactive) if isinstance(right_arg_eval_res.value, Cell): text_right = EvalResult.unwrap_str_literal(right_arg_eval_res.value.value) else: text_right = right_arg_eval_res.get_text(unwrap=True) if op_str == '&': if left_arg_eval_res.status == EvalStatus.FullEvaluation and right_arg_eval_res.status != EvalStatus.FullEvaluation: text_left = '{}&{}'.format(text_left, text_right) left_arg_eval_res.status = EvalStatus.PartialEvaluation concat_status = EvalStatus.PartialEvaluation elif left_arg_eval_res.status != EvalStatus.FullEvaluation and right_arg_eval_res.status == EvalStatus.FullEvaluation: text_left = '{}&{}'.format(text_left, text_right) left_arg_eval_res.status = EvalStatus.FullEvaluation concat_status = EvalStatus.PartialEvaluation elif left_arg_eval_res.status != EvalStatus.FullEvaluation and right_arg_eval_res.status != EvalStatus.FullEvaluation: text_left = '{}&{}'.format(text_left, text_right) left_arg_eval_res.status = EvalStatus.PartialEvaluation concat_status = EvalStatus.PartialEvaluation else: text_left = text_left + text_right elif left_arg_eval_res.status == EvalStatus.FullEvaluation and right_arg_eval_res.status == EvalStatus.FullEvaluation: status = EvalStatus.FullEvaluation if isinstance(right_arg_eval_res.value, Cell): value_right = right_arg_eval_res.value.value else: value_right = right_arg_eval_res.value if isinstance(value_right, str): if value_right.lower() == 'true': value_right = 1 elif value_right.lower() == 'false': value_right = 0 text_left = str(text_left) text_right = str(text_right) if text_left == '': text_left = '0' value_left = 0 if text_right == '': text_right = '0' value_right = 0 if self.is_float(value_left) and self.is_float(value_right): if op_str in self._operators: op_res = self._operators[op_str](float(value_left), float(value_right)) if type(op_res) == bool: value_left = str(op_res) elif op_res.is_integer(): value_left = str(int(op_res)) else: op_res = round(op_res, 10) value_left = str(op_res) else: value_left = 'Operator ' + op_str left_arg_eval_res.status = EvalStatus.NotImplemented elif EvalResult.is_datetime(text_left.strip('\"')) and EvalResult.is_datetime(text_right.strip('\"')): timestamp1 = datetime.datetime.strptime(text_left.strip('\"'), "%Y-%m-%d %H:%M:%S.%f") timestamp2 = datetime.datetime.strptime(text_right.strip('\"'), "%Y-%m-%d %H:%M:%S.%f") op_res = self._operators[op_str](float(timestamp1.timestamp()), float(timestamp2.timestamp())) op_res += 1000 if type(op_res) == bool: value_left = str(op_res) elif EvalResult.is_datetime(op_res): value_left = str(op_res) elif op_res.is_integer(): value_left = str(op_res) else: op_res = round(op_res, 10) value_left = str(op_res) elif EvalResult.is_datetime(text_left.strip('\"')) and EvalResult.is_time(text_right.strip('\"')): timestamp1 = datetime.datetime.strptime(text_left.strip('\"'), "%Y-%m-%d %H:%M:%S.%f") timestamp2 = datetime.datetime.strptime(text_right.strip('\"'), "%H:%M:%S") t1 = float(timestamp1.timestamp()) t2 = float( int(timestamp2.hour) * 3600 + int(timestamp2.minute) * 60 + int(timestamp2.second)) op_res = datetime.datetime.fromtimestamp(self._operators[op_str](t1, t2)) if type(op_res) == bool: value_left = str(op_res) elif type(op_res) == datetime.datetime: value_left = str(op_res) elif op_res.is_integer(): value_left = str(op_res) else: op_res = round(op_res, 10) value_left = str(op_res) else: if op_str in self._operators: value_left = EvalResult.unwrap_str_literal(str(value_left)) value_right = EvalResult.unwrap_str_literal(str(value_right)) op_res = self._operators[op_str](value_left, value_right) value_left = op_res else: value_left = XLMInterpreter.convert_ptree_to_str(parse_tree_root) left_arg_eval_res.status = EvalStatus.PartialEvaluation text_left = value_left else: left_arg_eval_res.status = EvalStatus.PartialEvaluation text_left = '{}{}{}'.format(text_left, op_str, text_right) return_val = text_left else: if text_left is None: left_arg = parse_tree_root.children[index] left_arg_eval_res = self.evaluate_parse_tree(current_cell, left_arg, interactive) text_left = left_arg_eval_res.get_text(unwrap=True) if isinstance(left_arg_eval_res.value, Cell): value_left = left_arg_eval_res.value.value else: value_left = left_arg_eval_res.value if isinstance(value_left, str): if value_left.lower() == 'true': value_left = 1 elif value_left.lower() == 'false': value_left = 0 if concat_status == EvalStatus.PartialEvaluation and left_arg_eval_res.status == EvalStatus.FullEvaluation: left_arg_eval_res.status = concat_status result = EvalResult(next_cell, left_arg_eval_res.status, return_val, EvalResult.wrap_str_literal(text_left)) elif parse_tree_root.data == 'final': arg = parse_tree_root.children[1] result = self.evaluate_parse_tree(current_cell, arg, interactive) else: status = EvalStatus.FullEvaluation for child_node in parse_tree_root.children: if child_node is not None: child_eval_result = self.evaluate_parse_tree(current_cell, child_node, interactive) if child_eval_result.status != EvalStatus.FullEvaluation: status = child_eval_result.status result = EvalResult(child_eval_result.next_cell, status, child_eval_result.value, child_eval_result.text) result.output_level = child_eval_result.output_level return result def evaluate_cell(self, current_cell, interactive, parse_tree_root)-
Expand source code Browse git
def evaluate_cell(self, current_cell, interactive, parse_tree_root): sheet_name, col, row = self.get_cell_addr(current_cell, parse_tree_root) return_val = '' text = '' status = EvalStatus.PartialEvaluation if sheet_name is not None: cell_addr = col + str(row) if sheet_name in self.xlm_wrapper.get_macrosheets(): sheet = self.xlm_wrapper.get_macrosheets()[sheet_name] else: sheet = self.xlm_wrapper.get_worksheets()[sheet_name] if cell_addr not in sheet.cells and (sheet_name, cell_addr) in self.cell_with_unsuccessfull_set: if interactive: self.invoke_interpreter = True if self.first_unknown_cell is None: self.first_unknown_cell = cell_addr if cell_addr in sheet.cells: cell = sheet.cells[cell_addr] if cell.formula is not None and cell.formula != cell.value: try: parse_tree = self.xlm_parser.parse(cell.formula) eval_result = self.evaluate_parse_tree(cell, parse_tree, False) return_val = eval_result.value text = eval_result.get_text() status = eval_result.status except: return_val = cell.formula text = EvalResult.wrap_str_literal(cell.formula) status = EvalStatus.FullEvaluation elif cell.value is not None: text = EvalResult.wrap_str_literal(cell.value, must_wrap=True) return_val = text status = EvalStatus.FullEvaluation else: text = "{}".format(cell_addr) else: if (sheet_name, cell_addr) in self.cell_with_unsuccessfull_set: text = "{}".format(cell_addr) else: text = '' status = EvalStatus.FullEvaluation else: text = XLMInterpreter.convert_ptree_to_str(parse_tree_root) return EvalResult(None, status, return_val, text) def evaluate_range(self, current_cell, interactive, parse_tree_root)-
Expand source code Browse git
def evaluate_range(self, current_cell, interactive, parse_tree_root): status = EvalStatus.PartialEvaluation if len(parse_tree_root.children) >= 3: start_address = self.get_cell_addr(current_cell, parse_tree_root.children[0]) end_address = self.get_cell_addr(current_cell, parse_tree_root.children[2]) selected = None if len(parse_tree_root.children) == 5: selected = self.get_cell_addr(current_cell, parse_tree_root.children[4]) self.selected_range = (start_address, end_address, selected) status = EvalStatus.FullEvaluation text = XLMInterpreter.convert_ptree_to_str(parse_tree_root) return_val = text return EvalResult(None, status, return_val, text) def evaluate_array(self, current_cell, interactive, parse_tree_root)-
Expand source code Browse git
def evaluate_array(self, current_cell, interactive, parse_tree_root): status = EvalStatus.PartialEvaluation array_elements = [] for index, array_elm in enumerate(parse_tree_root.children): # skip semicolon (;) if index % 2 == 1: continue if array_elm.type == 'NUMBER': array_elements.append(float(array_elm)) else: array_elements.append(str(array_elm)) text = str(array_elements) return_val = array_elements return EvalResult(None, status, return_val, text) def has_loop(self, path, length=10)-
Expand source code Browse git
def has_loop(self, path, length=10): if len(path) < length * 2: return False else: result = False start_index = len(path) - length for j in range(0, start_index - length): matched = True k = j while start_index + k - j < len(path): if path[k] != path[start_index + k - j]: matched = False break k += 1 if matched: result = True break return result def extract_strings(self, string)-
Expand source code Browse git
def extract_strings(self, string): result = [] matches = XLMInterpreter.detect_string.finditer(string) for matchNum, match in enumerate(matches, start=1): result.append(match.string[match.start(0):match.end(0)]) return result def deobfuscate_macro(self, interactive, start_point='', timeout=0, silent_mode=False)-
Expand source code Browse git
def deobfuscate_macro(self, interactive, start_point="", timeout=0, silent_mode=False): result = [] self._start_timestamp = time.time() self.auto_labels = self.xlm_wrapper.get_defined_name('auto_open', full_match=False) self.auto_labels.extend(self.xlm_wrapper.get_defined_name('auto_close', full_match=False)) if len(self.auto_labels) == 0: if len(start_point) > 0: self.auto_labels = [('auto_open', start_point)] if self.auto_labels is not None and len(self.auto_labels) > 0: macros = self.xlm_wrapper.get_macrosheets() continue_emulation = True for auto_open_label in self.auto_labels: if not continue_emulation: break try: sheet_name, col, row = Cell.parse_cell_addr(auto_open_label[1]) if sheet_name in macros: current_cell = self.get_formula_cell(macros[sheet_name], col, row) self._branch_stack = [(current_cell, current_cell.formula, macros[sheet_name].cells, 0, '')] observed_cells = [] while len(self._branch_stack) > 0: if not continue_emulation: break current_cell, formula, saved_cells, indent_level, desc = self._branch_stack.pop() macros[current_cell.sheet.name].cells = saved_cells self._indent_level = indent_level stack_record = True while current_cell is not None: if not continue_emulation: break if type(formula) is str: replace_op = getattr(self.xlm_wrapper, "replace_nonprintable_chars", None) if callable(replace_op): formula = replace_op(formula, '_') if formula not in self._formula_cache: parse_tree = self.xlm_parser.parse(formula) self._formula_cache[formula] = parse_tree else: parse_tree = self._formula_cache[formula] else: parse_tree = formula if stack_record: previous_indent = self._indent_level - 1 if self._indent_level > 0 else 0 else: previous_indent = self._indent_level evaluation_result = self.evaluate_parse_tree(current_cell, parse_tree, interactive) if self._remove_current_formula_from_cache: self._remove_current_formula_from_cache = False if formula in self._formula_cache: del (self._formula_cache[formula]) if len(self._while_stack) == 0 and evaluation_result.text != 'NEXT': observed_cells.append(current_cell.get_local_address()) if self.has_loop(observed_cells): break if self.invoke_interpreter: self.invoke_interpreter = False self.first_unknown_cell = None continue if evaluation_result.value is not None: current_cell.value = str(evaluation_result.value) if evaluation_result.next_cell is None and \ (evaluation_result.status == EvalStatus.FullEvaluation or evaluation_result.status == EvalStatus.PartialEvaluation or evaluation_result.status == EvalStatus.NotImplemented or evaluation_result.status == EvalStatus.IGNORED): evaluation_result.next_cell = self.get_formula_cell(current_cell.sheet, current_cell.column, str(int(current_cell.row) + 1)) if stack_record: evaluation_result.text = ( desc + ' ' + evaluation_result.get_text(unwrap=False)).strip() if self._indent_current_line: previous_indent = self._indent_level self._indent_current_line = False if evaluation_result.status != EvalStatus.IGNORED: if self.output_level >= 3 and evaluation_result.output_level == 2: strings = self.extract_strings(evaluation_result.get_text(unwrap=True)) if strings: yield ( current_cell, evaluation_result.status, '\n'.join(strings), previous_indent) elif evaluation_result.output_level >= self.output_level: yield ( current_cell, evaluation_result.status, evaluation_result.get_text(unwrap=False), previous_indent) if timeout > 0 and time.time() - self._start_timestamp > timeout: continue_emulation = False if evaluation_result.next_cell is not None: current_cell = evaluation_result.next_cell else: break formula = current_cell.formula stack_record = False except Exception: pass