kalenislims/lims/formula_parser.py

190 lines
5.6 KiB
Python
Raw Normal View History

2017-10-08 02:23:22 +02:00
# -*- coding: utf-8 -*-
# This file is part of lims module for Tryton.
# The COPYRIGHT file at the top level of this repository contains
# the full copyright notices and license terms.
from trytond.model import Model
2019-07-23 23:27:33 +02:00
from trytond.exceptions import UserError
from trytond.i18n import gettext
2017-10-08 02:23:22 +02:00
class FormulaParser(Model):
'Formula Parser'
2020-03-13 22:14:10 +01:00
__slots__ = ('_string', '_index', '_vars')
2017-10-08 02:23:22 +02:00
def __init__(self, string, vars={}, id=None, **kwargs):
2020-03-13 22:14:10 +01:00
self._string = string
self._index = 0
self._vars = {
2017-10-08 02:23:22 +02:00
'pi': 3.141592653589793,
'e': 2.718281828459045,
}
2019-03-04 15:41:58 +01:00
for var in list(vars.keys()):
2020-03-13 22:14:10 +01:00
if self._vars.get(var) is not None:
2019-07-23 23:27:33 +02:00
raise UserError(gettext(
'lims.msg_variable_redefine', variable=var))
2020-03-13 22:14:10 +01:00
self._vars[var] = vars[var]
2020-08-06 19:52:36 +02:00
super().__init__(id, **kwargs)
2017-10-08 02:23:22 +02:00
def getValue(self):
value = self.parseExpression()
self.skipWhitespace()
if self.hasNext():
2019-07-23 23:27:33 +02:00
raise UserError(gettext('lims.msg_unexpected_character',
2020-03-13 22:14:10 +01:00
character=self.peek(), index=str(self._index)))
2017-10-08 02:23:22 +02:00
return value
def peek(self):
2020-03-13 22:14:10 +01:00
return self._string[self._index:self._index + 1]
2017-10-08 02:23:22 +02:00
def hasNext(self):
2020-03-13 22:14:10 +01:00
return self._index < len(self._string)
2017-10-08 02:23:22 +02:00
def skipWhitespace(self):
while self.hasNext():
if self.peek() in ' \t\n\r':
2020-03-13 22:14:10 +01:00
self._index += 1
2017-10-08 02:23:22 +02:00
else:
return
def parseExpression(self):
return self.parseAddition()
def parseAddition(self):
values = [self.parseMultiplication()]
while True:
self.skipWhitespace()
char = self.peek()
if char == '+':
2020-03-13 22:14:10 +01:00
self._index += 1
2017-10-08 02:23:22 +02:00
values.append(self.parseMultiplication())
elif char == '-':
2020-03-13 22:14:10 +01:00
self._index += 1
2017-10-08 02:23:22 +02:00
values.append(-1 * self.parseMultiplication())
else:
break
return sum(values)
def parseMultiplication(self):
values = [self.parsePower()]
while True:
self.skipWhitespace()
char = self.peek()
if char == '*':
2020-03-13 22:14:10 +01:00
self._index += 1
2017-10-08 02:23:22 +02:00
values.append(self.parsePower())
elif char == '/':
2020-03-13 22:14:10 +01:00
# div_index = self._index
self._index += 1
2017-10-08 02:23:22 +02:00
denominator = self.parsePower()
if denominator == 0:
2019-07-23 23:27:33 +02:00
# raise UserError(gettext(
# 'lims.msg_division_zero', index=str(div_index)))
2017-10-08 02:23:22 +02:00
return 0.0
values.append(1.0 / denominator)
else:
break
value = 1.0
for factor in values:
value *= factor
return value
def parsePower(self):
values = [self.parseParenthesis()]
while True:
self.skipWhitespace()
char = self.peek()
if char == '^':
2020-03-13 22:14:10 +01:00
self._index += 1
2017-10-08 02:23:22 +02:00
values.append(self.parseParenthesis())
else:
break
value = values[0]
for exponent in values[1:]:
value **= exponent
return value
def parseParenthesis(self):
self.skipWhitespace()
char = self.peek()
if char == '(':
2020-03-13 22:14:10 +01:00
self._index += 1
2017-10-08 02:23:22 +02:00
value = self.parseExpression()
self.skipWhitespace()
if self.peek() != ')':
2019-07-23 23:27:33 +02:00
raise UserError(gettext(
2020-03-13 22:14:10 +01:00
'lims.msg_closing_parenthesis', index=str(self._index)))
self._index += 1
2017-10-08 02:23:22 +02:00
return value
else:
return self.parseNegative()
def parseNegative(self):
self.skipWhitespace()
char = self.peek()
if char == '-':
2020-03-13 22:14:10 +01:00
self._index += 1
2017-10-08 02:23:22 +02:00
return -1 * self.parseParenthesis()
else:
return self.parseValue()
def parseValue(self):
self.skipWhitespace()
char = self.peek()
if char in '0123456789.':
return self.parseNumber()
else:
return self.parseVariable()
def parseVariable(self):
self.skipWhitespace()
var = ''
while self.hasNext():
char = self.peek()
if char.lower() in '_abcdefghijklmnopqrstuvwxyz0123456789':
var += char
2020-03-13 22:14:10 +01:00
self._index += 1
2017-10-08 02:23:22 +02:00
else:
break
2020-03-13 22:14:10 +01:00
value = self._vars.get(var, None)
2017-10-08 02:23:22 +02:00
if value is None:
2019-07-23 23:27:33 +02:00
raise UserError(gettext(
'lims.msg_unrecognized_variable', variable=var))
2017-10-08 02:23:22 +02:00
if value == '':
return float(0)
try:
value = float(value)
except (ValueError):
return float(0)
return value
def parseNumber(self):
self.skipWhitespace()
strValue = ''
decimal_found = False
char = ''
while self.hasNext():
char = self.peek()
if char == '.':
if decimal_found:
2019-07-23 23:27:33 +02:00
raise UserError(gettext(
2020-03-13 22:14:10 +01:00
'lims.msg_extra_period', index=str(self._index)))
2017-10-08 02:23:22 +02:00
decimal_found = True
strValue += '.'
elif char in '0123456789':
strValue += char
else:
break
2020-03-13 22:14:10 +01:00
self._index += 1
2017-10-08 02:23:22 +02:00
if len(strValue) == 0:
if char == '':
2019-07-23 23:27:33 +02:00
raise UserError(gettext('lims.msg_unexpected_end'))
2017-10-08 02:23:22 +02:00
else:
2019-07-23 23:27:33 +02:00
raise UserError(gettext('lims.msg_number_expected',
2020-03-13 22:14:10 +01:00
index=str(self._index), character=char))
2017-10-08 02:23:22 +02:00
return float(strValue)