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)
|