Я столкнулся с подобной проблемой несколько лет назад. У меня было веб-приложение с умеренным трафиком, которое должно было разрешить уравнения, и оно нуждалось в функциях, аналогичных вашим, и должно было быть быстрым. Я прошел через несколько идей.
Первое решение включало добавление вычисляемых столбцов в нашу базу данных. Наши таблицы для приложения хранят свойства в столбцах (например, есть столбец «Сумма к оплате», «Другая скидка» и т. Д.). Если пользователь введет формулу, подобную PropertyA * 2, код изменит базовую таблицу, чтобы получить новый вычисляемый столбец. Это грязно, поскольку добавление и удаление столбцов. Тем не менее, у него есть несколько преимуществ: база данных (SQL Server) очень быстро выполняла вычисления; база данных обработала много ошибок для нас; и я мог бы притвориться, что вычисленные значения были такими же, как не вычисленные значения, а это означало, что мне не нужно было изменять какой-либо существующий код, который работал с не вычисленными значениями.
Это работало некоторое время, пока нам не потребовалась способность формулы ссылаться на другую формулу, а SQL Server этого не позволяет. Поэтому я переключился на скриптовый движок. IronPython тогда еще не был достаточно зрелым, поэтому я выбрал другой движок ... Я не могу вспомнить, какой именно сейчас. Во всяком случае, это было легко написать, но это было немного медленно. Не много, может быть, несколько миллисекунд на запрос, но для веб-приложения время действительно складывается по всем запросам.
Именно тогда я решил написать собственный синтаксический анализатор для формул. То есть у меня есть класс PlusToken для добавления двух значений, класс ItemToken, который соответствует GetValue («Discount») и т. Д. Когда пользователь вводит новую формулу, валидатор анализирует формулу, проверяет ее действительность (например, они ссылались на столбец, который не существует?), и сохраняли его в полускомпилированной форме, которую потом легко разобрать. Когда пользователь запрашивает вычисленное значение, анализатор считывает формулу, анализирует ее, вычисляет, какие данные необходимы из базы данных, и вычисляет окончательный ответ. Сначала потребовалось немало работы, но она работает хорошо и очень быстро. Вот что я узнал:
- Если пользователь вводит формулу, которая приводит к циклу в формулах, и вы пытаетесь вычислить значение формулы, вам не хватит места в стеке. Если вы запускаете это в веб-приложении, весь веб-сервер перестанет работать, пока вы не сбросите его. Поэтому важно обнаруживать циклы на этапе проверки.
- Если у вас более пары формул, объедините все вызовы базы данных в одном месте, а затем запросите все данные одновременно. Гораздо быстрее.
- Пользователи будут вводить дурацкие вещи в формулы. Синтаксический анализатор, который выдает полезные сообщения об ошибках, впоследствии избавит вас от множества головных болей.