Всем маткульт-привет! В этой статье мы продолжаем и заканчиваем написание консольного инженерного калькулятора. Для понимания происходящего настоятельно рекомендую сначала ознакомиться с первой частью.  В прошлой части мы научились разбивать исходное математическое выражение формата (log2(18)/3.14)*sqrt(0.11^(-3)/0.02)на токены. На выходе мы получаем массив токенов, каждый их которых содержит информацию о типе (оператор, скобка, число, ...) и об ассоциативности, если он таковую имеет. Теперь мы хотим привести выражение к виду обратной польской записи (RPN), чтобы затем удобно его посчитать. Это нам позволяет сделать изобретенный Эдсгером Дейкстрой алгоритм сортировочной станции. Не мудрствуя лукаво и не изобретая велосипед, скажу, что подробное описание работы алгоритма на русском и на английском есть на Википедии. В англиской версии также наглядно показано, почему алгоритм так называется - его работа напоминает работу железнодорожной сортировочной станции.  Сложность  , что очень и очень радует. Пока не все токены обработаны: Прочитать токен. Если токен — число, то добавить его в очередь вывода. Если токен — функция, то поместить его в стек. Если токен — разделитель аргументов функции (запятая): Пока токен на вершине стека не открывающая скобка:  Переложить оператор из стека в очередь вывода. Если стек закончился до того, как был встречен токен открывающая скобка, то в выражении пропущен разделитель аргументов функции (запятая), либо пропущена открывающая скобка. Если токен — оператор op1, то: Пока присутствует на вершине стека токен оператор op2, чей приоритет выше или равен приоритету op1, и при равенстве приоритетов op1 является левоассоциативным: Переложить op2 из стека в очередь вывода; Положить op1 в стек. Если токен — открывающая скобка, то положить его в стек. Если токен — закрывающая скобка: Пока токен на вершине стека не открывающая скобка: Переложить оператор из стека в очередь вывода. Если стек закончился до того, как был встречен токен открывающая скобка, то в выражении пропущена скобка. Выкинуть открывающую скобку из стека, но не добавлять в очередь вывода. Если токен на вершине стека — функция, переложить её в очередь вывода. Если больше не осталось токенов на входе: Пока есть токены операторы в стеке: Если токен оператор на вершине стека — открывающая скобка, то в выражении пропущена скобка. Переложить оператор из стека в очередь вывода. Конец.  Приступим к реализации. Напомню, что токенизатор и класс Token - в первой части. Фунция shuntingYard принимает на вход вектор токенов; на выходе выдает тоже вектор токенов, так называемую очередь вывода, но уже в RPN,  Основа работы алгоритма - стек, в который помещаются операторы, функции, скобки и разделители, а затем переносятся в очередь вывода. Эта операция выполняется часто, поэтому для удобства используем лямбду, которая перемещает токен с вершины стека в очередь вывода. Теперь перебираем все токены. Если токен - числовой литерал, сразу переносим его в очередь вывода; если функция или закрывающая скобка - на стек. Если токен - оператор: Если токен - закрывающая скобка: Если токен - разделитель аргументов функции (он же запятая): Теперь перекладываем опереторы, оставшиеся на стеке, в очередь вывода: Отлично, мы преобразовали выражение.  Например, из(log2(18)/3.14)*sqrt(0.11^(-3)/0.02) мы получим последовательность23 log2 2 - 3.14 / 0.1 10 3 - ^ * 0.02 / sqrt * + Теперь то, зачем мы все здесь собрались - посчитать. Как подробно описано здесь, весь смысл RPN в заключается в том, что нам больше не надо заботиться о приоритете операций - все операнды и операторы уже расположены в нужном порядке. Так что нам лишь остается последовательно пройтись по операторам, записывая промежуточные вычисления на стек.  Обработка входного символа  Если на вход подан операнд, он помещается на вершину стека. Если на вход подан знак операции, то соответствующая операция  выполняется над требуемым количеством значений, извлечённых из стека,  взятых в порядке добавления. Результат выполненной операции кладётся на  вершину стека. Если входной набор символов обработан не полностью, перейти к шагу 1. После полной обработки входного набора символов результат вычисления выражения лежит на вершине стека.  Фукнция countRPN будем принимать на вход вектор токенов в RPN (да ладно!) и возвращать результат вычисления. Теперь нам понадобится стек, куда мы будем класть операнды и результаты промежуточных вычислений: Для удобства введем лямбды, которые будут брать один или два аргумента со стека: Теперь, собственно, посчитайка: Теперь операторы: И функции: Результатом будет единственный оставшийся элемент на стеке: Весь код проекта лежит на github. Надеюсь, что было интересно. Как было замечено в комментарии к первой части, существует куда более простой способ сделать то, что было описано в статье - генератор синтаксических анализаторов GNU Bison, разработанный аж в 1985 году. В официальной документации есть пример создания RPN-калькулятора.  Начинающий линуксоид и любитель железок